From 714c3c9207fa26ed5a872cb1d4996dee1e1c4229 Mon Sep 17 00:00:00 2001 From: Hahaha!! Date: Mon, 8 Jun 2020 15:30:53 +0800 Subject: [PATCH 1/3] add Typescript --- README.md | 35 +- app.js | 46 +- app.ts | 9 + components/wxParse/utils/showdown.js | 2522 --- components/wxParse/wxParse.js | 38 +- components/wxParse/wxParse.wxml | 4 +- components/wxParse/wxParse.wxss | 4 - pages/index/index.js | 4 +- pages/index/index.ts | 1 + pages/richTextPage/richTextPage.js | 64 +- pages/richTextPage/richTextPage.wxml | 12 +- project.config.json | 2 +- static/md.png | Bin 61990 -> 0 bytes tsconfig.json | 29 + typings/index.d.ts | 12 + typings/lib.wa.es6.d.ts | 5881 +++++++ typings/wx/index.d.ts | 29 + typings/wx/lib.wx.api.d.ts | 12756 ++++++++++++++++ typings/wx/lib.wx.app.d.ts | 249 + typings/wx/lib.wx.cloud.d.ts | 687 + typings/wx/lib.wx.component.d.ts | 213 + typings/wx/lib.wx.page.d.ts | 212 + .../wxParse/utils => utils}/html2json.js | 0 .../wxParse/utils => utils}/htmlparser.js | 0 {components/wxParse/utils => utils}/util.js | 0 .../wxParse/utils => utils}/wxDiscode.js | 0 26 files changed, 20111 insertions(+), 2698 deletions(-) create mode 100644 app.ts delete mode 100644 components/wxParse/utils/showdown.js create mode 100644 pages/index/index.ts delete mode 100644 static/md.png create mode 100644 tsconfig.json create mode 100644 typings/index.d.ts create mode 100644 typings/lib.wa.es6.d.ts create mode 100644 typings/wx/index.d.ts create mode 100644 typings/wx/lib.wx.api.d.ts create mode 100644 typings/wx/lib.wx.app.d.ts create mode 100644 typings/wx/lib.wx.cloud.d.ts create mode 100644 typings/wx/lib.wx.component.d.ts create mode 100644 typings/wx/lib.wx.page.d.ts rename {components/wxParse/utils => utils}/html2json.js (100%) rename {components/wxParse/utils => utils}/htmlparser.js (100%) rename {components/wxParse/utils => utils}/util.js (100%) rename {components/wxParse/utils => utils}/wxDiscode.js (100%) diff --git a/README.md b/README.md index 4bc947a..76635a0 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,6 @@ - [x] video标签 - [x] br标签 - [x] button标签 - - [x] h1, h2, h3, h4标签 - [x] 文本节点 - [x] 其余块级标签 - [x] 其余行级标签 @@ -46,7 +45,7 @@ npm install --save wx-minicomponent 1. **原生组件使用方法** -- 克隆 [项目](https://github.com/csonchen/wxParse) 代码,把 **components目录** 拷贝到你的小程序根目录下面; +- 克隆 [项目](https://github.com/csonchen/wxParse) 代码,把 **components目录** 和 **utils目录** 拷贝到你的小程序根目录下面; - 在你的 **page页面** 对应的 `json` 文件引入 `wxParse` 组件 @@ -92,7 +91,7 @@ npm install --save wx-minicomponent **提示:详细步骤可以参考小程序的[npm使用文档](https://developers.weixin.qq.com/miniprogram/dev/devtools/npm.html)** -3. **补充组件:代码高亮展示组件使用** +3. **补充组件**:代码高亮展示组件使用(目前只支持 `javascript` 语法,等我有时间再扩展别的语法) - 在 `page`的 `json` 文件里面引入 `highLight` 组件 @@ -124,12 +123,11 @@ npm install --save wx-minicomponent ### 参数文档 -- **wxParse:富文本解析组件** +- **wxParse** 参数|说明|类型|例子 :--|:--|:--|:-- nodes|富文本字符|String|"\test\" -language|语言|String| 可选:"html" \| "markdown" ("md") **受信任的节点** @@ -145,13 +143,9 @@ ul| ol| img| button| -h1| -h2| -h3| -h4| ....| -- **highLight:代码高亮解析组件** +- **highLight** 参数|说明|类型|例子 :--|:--|:--|:-- @@ -161,7 +155,7 @@ language|代码语言类型|String|可选值:"javascript/typescript/css/xml/sq **提示:如果是html语言,language的值为xml** -- **wxAudio:仿微信公众号文章音频播放组件** +- **wxAudio** 参数|说明|类型|例子 :--|:--|:--|:-- @@ -171,34 +165,19 @@ src|音频地址|String| ### 示例展示 -1. **富文本解析** - -- **html文本解析实例** +- 富文本解析 ![示例](/static/wxParse.gif) -- **markdown文本解析实例** - -![示例](/static/md.png) - -2. **代码高亮** +- 代码高亮 ![示例](/static/wxHigh.gif) ### 更新历史 -- 2020-5-31 - - 1. 迁移utils目录到wxParse目录下; - - 2. 富文本增加markdown文本解析支持; - - 2020-5-21: 富文本组件image标签添加loading过渡态,优化图片加载体验 - - 2020-5-17: 完善组件参数文档,增加wxParse对audio标签标题,副标题的解析功能 - - 2020-5-13: 增加css,html,ts,sql,markdown代码高亮提示支持 - - 2020-5-6:增加图片预览功能 ### TODO diff --git a/app.js b/app.js index b545190..302c464 100644 --- a/app.js +++ b/app.js @@ -1,39 +1,9 @@ -//app.js +"use strict"; App({ - onLaunch: function () { - // 展示本地存储能力 - var logs = wx.getStorageSync('logs') || [] - logs.unshift(Date.now()) - wx.setStorageSync('logs', logs) - - // 登录 - wx.login({ - success: res => { - // 发送 res.code 到后台换取 openId, sessionKey, unionId - } - }) - // 获取用户信息 - wx.getSetting({ - success: res => { - if (res.authSetting['scope.userInfo']) { - // 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框 - wx.getUserInfo({ - success: res => { - // 可以将 res 发送给后台解码出 unionId - this.globalData.userInfo = res.userInfo - - // 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回 - // 所以此处加入 callback 以防止这种情况 - if (this.userInfoReadyCallback) { - this.userInfoReadyCallback(res) - } - } - }) - } - } - }) - }, - globalData: { - userInfo: null - } -}) \ No newline at end of file + onLaunch: function () { + }, + globalData: { + userInfo: null + } +}); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiYXBwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiYXBwLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7QUFDQSxHQUFHLENBQUM7SUFDRixRQUFRLEVBQUU7SUFFVixDQUFDO0lBQ0QsVUFBVSxFQUFFO1FBQ1YsUUFBUSxFQUFFLElBQUk7S0FDZjtDQUNGLENBQUMsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbIi8vYXBwLmpzXG5BcHAoe1xuICBvbkxhdW5jaDogZnVuY3Rpb24gKCkge1xuXG4gIH0sXG4gIGdsb2JhbERhdGE6IHtcbiAgICB1c2VySW5mbzogbnVsbFxuICB9XG59KSJdfQ== \ No newline at end of file diff --git a/app.ts b/app.ts new file mode 100644 index 0000000..84c5169 --- /dev/null +++ b/app.ts @@ -0,0 +1,9 @@ +//app.js +App({ + onLaunch: function () { + + }, + globalData: { + userInfo: null + } +}) \ No newline at end of file diff --git a/components/wxParse/utils/showdown.js b/components/wxParse/utils/showdown.js deleted file mode 100644 index 502ba7b..0000000 --- a/components/wxParse/utils/showdown.js +++ /dev/null @@ -1,2522 +0,0 @@ -/** - * showdown: https://github.com/showdownjs/showdown - */ - -function getDefaultOpts(simple) { - 'use strict'; - - var defaultOptions = { - omitExtraWLInCodeBlocks: { - defaultValue: false, - describe: 'Omit the default extra whiteline added to code blocks', - type: 'boolean' - }, - noHeaderId: { - defaultValue: false, - describe: 'Turn on/off generated header id', - type: 'boolean' - }, - prefixHeaderId: { - defaultValue: false, - describe: 'Specify a prefix to generated header ids', - type: 'string' - }, - headerLevelStart: { - defaultValue: false, - describe: 'The header blocks level start', - type: 'integer' - }, - parseImgDimensions: { - defaultValue: false, - describe: 'Turn on/off image dimension parsing', - type: 'boolean' - }, - simplifiedAutoLink: { - defaultValue: false, - describe: 'Turn on/off GFM autolink style', - type: 'boolean' - }, - literalMidWordUnderscores: { - defaultValue: false, - describe: 'Parse midword underscores as literal underscores', - type: 'boolean' - }, - strikethrough: { - defaultValue: false, - describe: 'Turn on/off strikethrough support', - type: 'boolean' - }, - tables: { - defaultValue: false, - describe: 'Turn on/off tables support', - type: 'boolean' - }, - tablesHeaderId: { - defaultValue: false, - describe: 'Add an id to table headers', - type: 'boolean' - }, - ghCodeBlocks: { - defaultValue: true, - describe: 'Turn on/off GFM fenced code blocks support', - type: 'boolean' - }, - tasklists: { - defaultValue: false, - describe: 'Turn on/off GFM tasklist support', - type: 'boolean' - }, - smoothLivePreview: { - defaultValue: false, - describe: 'Prevents weird effects in live previews due to incomplete input', - type: 'boolean' - }, - smartIndentationFix: { - defaultValue: false, - description: 'Tries to smartly fix identation in es6 strings', - type: 'boolean' - } - }; - if (simple === false) { - return JSON.parse(JSON.stringify(defaultOptions)); - } - var ret = {}; - for (var opt in defaultOptions) { - if (defaultOptions.hasOwnProperty(opt)) { - ret[opt] = defaultOptions[opt].defaultValue; - } - } - return ret; -} - -/** - * Created by Tivie on 06-01-2015. - */ - -// Private properties -var showdown = {}, - parsers = {}, - extensions = {}, - globalOptions = getDefaultOpts(true), - flavor = { - github: { - omitExtraWLInCodeBlocks: true, - prefixHeaderId: 'user-content-', - simplifiedAutoLink: true, - literalMidWordUnderscores: true, - strikethrough: true, - tables: true, - tablesHeaderId: true, - ghCodeBlocks: true, - tasklists: true - }, - vanilla: getDefaultOpts(true) - }; - -/** - * helper namespace - * @type {{}} - */ -showdown.helper = {}; - -/** - * TODO LEGACY SUPPORT CODE - * @type {{}} - */ -showdown.extensions = {}; - -/** - * Set a global option - * @static - * @param {string} key - * @param {*} value - * @returns {showdown} - */ -showdown.setOption = function (key, value) { - 'use strict'; - globalOptions[key] = value; - return this; -}; - -/** - * Get a global option - * @static - * @param {string} key - * @returns {*} - */ -showdown.getOption = function (key) { - 'use strict'; - return globalOptions[key]; -}; - -/** - * Get the global options - * @static - * @returns {{}} - */ -showdown.getOptions = function () { - 'use strict'; - return globalOptions; -}; - -/** - * Reset global options to the default values - * @static - */ -showdown.resetOptions = function () { - 'use strict'; - globalOptions = getDefaultOpts(true); -}; - -/** - * Set the flavor showdown should use as default - * @param {string} name - */ -showdown.setFlavor = function (name) { - 'use strict'; - if (flavor.hasOwnProperty(name)) { - var preset = flavor[name]; - for (var option in preset) { - if (preset.hasOwnProperty(option)) { - globalOptions[option] = preset[option]; - } - } - } -}; - -/** - * Get the default options - * @static - * @param {boolean} [simple=true] - * @returns {{}} - */ -showdown.getDefaultOptions = function (simple) { - 'use strict'; - return getDefaultOpts(simple); -}; - -/** - * Get or set a subParser - * - * subParser(name) - Get a registered subParser - * subParser(name, func) - Register a subParser - * @static - * @param {string} name - * @param {function} [func] - * @returns {*} - */ -showdown.subParser = function (name, func) { - 'use strict'; - if (showdown.helper.isString(name)) { - if (typeof func !== 'undefined') { - parsers[name] = func; - } else { - if (parsers.hasOwnProperty(name)) { - return parsers[name]; - } else { - throw Error('SubParser named ' + name + ' not registered!'); - } - } - } -}; - -/** - * Gets or registers an extension - * @static - * @param {string} name - * @param {object|function=} ext - * @returns {*} - */ -showdown.extension = function (name, ext) { - 'use strict'; - - if (!showdown.helper.isString(name)) { - throw Error('Extension \'name\' must be a string'); - } - - name = showdown.helper.stdExtName(name); - - // Getter - if (showdown.helper.isUndefined(ext)) { - if (!extensions.hasOwnProperty(name)) { - throw Error('Extension named ' + name + ' is not registered!'); - } - return extensions[name]; - - // Setter - } else { - // Expand extension if it's wrapped in a function - if (typeof ext === 'function') { - ext = ext(); - } - - // Ensure extension is an array - if (!showdown.helper.isArray(ext)) { - ext = [ext]; - } - - var validExtension = validate(ext, name); - - if (validExtension.valid) { - extensions[name] = ext; - } else { - throw Error(validExtension.error); - } - } -}; - -/** - * Gets all extensions registered - * @returns {{}} - */ -showdown.getAllExtensions = function () { - 'use strict'; - return extensions; -}; - -/** - * Remove an extension - * @param {string} name - */ -showdown.removeExtension = function (name) { - 'use strict'; - delete extensions[name]; -}; - -/** - * Removes all extensions - */ -showdown.resetExtensions = function () { - 'use strict'; - extensions = {}; -}; - -/** - * Validate extension - * @param {array} extension - * @param {string} name - * @returns {{valid: boolean, error: string}} - */ -function validate(extension, name) { - 'use strict'; - - var errMsg = (name) ? 'Error in ' + name + ' extension->' : 'Error in unnamed extension', - ret = { - valid: true, - error: '' - }; - - if (!showdown.helper.isArray(extension)) { - extension = [extension]; - } - - for (var i = 0; i < extension.length; ++i) { - var baseMsg = errMsg + ' sub-extension ' + i + ': ', - ext = extension[i]; - if (typeof ext !== 'object') { - ret.valid = false; - ret.error = baseMsg + 'must be an object, but ' + typeof ext + ' given'; - return ret; - } - - if (!showdown.helper.isString(ext.type)) { - ret.valid = false; - ret.error = baseMsg + 'property "type" must be a string, but ' + typeof ext.type + ' given'; - return ret; - } - - var type = ext.type = ext.type.toLowerCase(); - - // normalize extension type - if (type === 'language') { - type = ext.type = 'lang'; - } - - if (type === 'html') { - type = ext.type = 'output'; - } - - if (type !== 'lang' && type !== 'output' && type !== 'listener') { - ret.valid = false; - ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang/language", "output/html" or "listener"'; - return ret; - } - - if (type === 'listener') { - if (showdown.helper.isUndefined(ext.listeners)) { - ret.valid = false; - ret.error = baseMsg + '. Extensions of type "listener" must have a property called "listeners"'; - return ret; - } - } else { - if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) { - ret.valid = false; - ret.error = baseMsg + type + ' extensions must define either a "regex" property or a "filter" method'; - return ret; - } - } - - if (ext.listeners) { - if (typeof ext.listeners !== 'object') { - ret.valid = false; - ret.error = baseMsg + '"listeners" property must be an object but ' + typeof ext.listeners + ' given'; - return ret; - } - for (var ln in ext.listeners) { - if (ext.listeners.hasOwnProperty(ln)) { - if (typeof ext.listeners[ln] !== 'function') { - ret.valid = false; - ret.error = baseMsg + '"listeners" property must be an hash of [event name]: [callback]. listeners.' + ln + - ' must be a function but ' + typeof ext.listeners[ln] + ' given'; - return ret; - } - } - } - } - - if (ext.filter) { - if (typeof ext.filter !== 'function') { - ret.valid = false; - ret.error = baseMsg + '"filter" must be a function, but ' + typeof ext.filter + ' given'; - return ret; - } - } else if (ext.regex) { - if (showdown.helper.isString(ext.regex)) { - ext.regex = new RegExp(ext.regex, 'g'); - } - if (!ext.regex instanceof RegExp) { - ret.valid = false; - ret.error = baseMsg + '"regex" property must either be a string or a RegExp object, but ' + typeof ext.regex + ' given'; - return ret; - } - if (showdown.helper.isUndefined(ext.replace)) { - ret.valid = false; - ret.error = baseMsg + '"regex" extensions must implement a replace string or function'; - return ret; - } - } - } - return ret; -} - -/** - * Validate extension - * @param {object} ext - * @returns {boolean} - */ -showdown.validateExtension = function (ext) { - 'use strict'; - - var validateExtension = validate(ext, null); - if (!validateExtension.valid) { - console.warn(validateExtension.error); - return false; - } - return true; -}; - -/** - * showdownjs helper functions - */ - -if (!showdown.hasOwnProperty('helper')) { - showdown.helper = {}; -} - -/** - * Check if var is string - * @static - * @param {string} a - * @returns {boolean} - */ -showdown.helper.isString = function isString(a) { - 'use strict'; - return (typeof a === 'string' || a instanceof String); -}; - -/** - * Check if var is a function - * @static - * @param {string} a - * @returns {boolean} - */ -showdown.helper.isFunction = function isFunction(a) { - 'use strict'; - var getType = {}; - return a && getType.toString.call(a) === '[object Function]'; -}; - -/** - * ForEach helper function - * @static - * @param {*} obj - * @param {function} callback - */ -showdown.helper.forEach = function forEach(obj, callback) { - 'use strict'; - if (typeof obj.forEach === 'function') { - obj.forEach(callback); - } else { - for (var i = 0; i < obj.length; i++) { - callback(obj[i], i, obj); - } - } -}; - -/** - * isArray helper function - * @static - * @param {*} a - * @returns {boolean} - */ -showdown.helper.isArray = function isArray(a) { - 'use strict'; - return a.constructor === Array; -}; - -/** - * Check if value is undefined - * @static - * @param {*} value The value to check. - * @returns {boolean} Returns `true` if `value` is `undefined`, else `false`. - */ -showdown.helper.isUndefined = function isUndefined(value) { - 'use strict'; - return typeof value === 'undefined'; -}; - -/** - * Standardidize extension name - * @static - * @param {string} s extension name - * @returns {string} - */ -showdown.helper.stdExtName = function (s) { - 'use strict'; - return s.replace(/[_-]||\s/g, '').toLowerCase(); -}; - -function escapeCharactersCallback(wholeMatch, m1) { - 'use strict'; - var charCodeToEscape = m1.charCodeAt(0); - return '~E' + charCodeToEscape + 'E'; -} - -/** - * Callback used to escape characters when passing through String.replace - * @static - * @param {string} wholeMatch - * @param {string} m1 - * @returns {string} - */ -showdown.helper.escapeCharactersCallback = escapeCharactersCallback; - -/** - * Escape characters in a string - * @static - * @param {string} text - * @param {string} charsToEscape - * @param {boolean} afterBackslash - * @returns {XML|string|void|*} - */ -showdown.helper.escapeCharacters = function escapeCharacters(text, charsToEscape, afterBackslash) { - 'use strict'; - // First we have to escape the escape characters so that - // we can build a character class out of them - var regexString = '([' + charsToEscape.replace(/([\[\]\\])/g, '\\$1') + '])'; - - if (afterBackslash) { - regexString = '\\\\' + regexString; - } - - var regex = new RegExp(regexString, 'g'); - text = text.replace(regex, escapeCharactersCallback); - - return text; -}; - -var rgxFindMatchPos = function (str, left, right, flags) { - 'use strict'; - var f = flags || '', - g = f.indexOf('g') > -1, - x = new RegExp(left + '|' + right, 'g' + f.replace(/g/g, '')), - l = new RegExp(left, f.replace(/g/g, '')), - pos = [], - t, s, m, start, end; - - do { - t = 0; - while ((m = x.exec(str))) { - if (l.test(m[0])) { - if (!(t++)) { - s = x.lastIndex; - start = s - m[0].length; - } - } else if (t) { - if (!--t) { - end = m.index + m[0].length; - var obj = { - left: {start: start, end: s}, - match: {start: s, end: m.index}, - right: {start: m.index, end: end}, - wholeMatch: {start: start, end: end} - }; - pos.push(obj); - if (!g) { - return pos; - } - } - } - } - } while (t && (x.lastIndex = s)); - - return pos; -}; - -/** - * matchRecursiveRegExp - * - * (c) 2007 Steven Levithan - * MIT License - * - * Accepts a string to search, a left and right format delimiter - * as regex patterns, and optional regex flags. Returns an array - * of matches, allowing nested instances of left/right delimiters. - * Use the "g" flag to return all matches, otherwise only the - * first is returned. Be careful to ensure that the left and - * right format delimiters produce mutually exclusive matches. - * Backreferences are not supported within the right delimiter - * due to how it is internally combined with the left delimiter. - * When matching strings whose format delimiters are unbalanced - * to the left or right, the output is intentionally as a - * conventional regex library with recursion support would - * produce, e.g. "<" and ">" both produce ["x"] when using - * "<" and ">" as the delimiters (both strings contain a single, - * balanced instance of ""). - * - * examples: - * matchRecursiveRegExp("test", "\\(", "\\)") - * returns: [] - * matchRecursiveRegExp(">>t<>", "<", ">", "g") - * returns: ["t<>", ""] - * matchRecursiveRegExp("
test
", "]*>", "", "gi") - * returns: ["test"] - */ -showdown.helper.matchRecursiveRegExp = function (str, left, right, flags) { - 'use strict'; - - var matchPos = rgxFindMatchPos (str, left, right, flags), - results = []; - - for (var i = 0; i < matchPos.length; ++i) { - results.push([ - str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), - str.slice(matchPos[i].match.start, matchPos[i].match.end), - str.slice(matchPos[i].left.start, matchPos[i].left.end), - str.slice(matchPos[i].right.start, matchPos[i].right.end) - ]); - } - return results; -}; - -/** - * - * @param {string} str - * @param {string|function} replacement - * @param {string} left - * @param {string} right - * @param {string} flags - * @returns {string} - */ -showdown.helper.replaceRecursiveRegExp = function (str, replacement, left, right, flags) { - 'use strict'; - - if (!showdown.helper.isFunction(replacement)) { - var repStr = replacement; - replacement = function () { - return repStr; - }; - } - - var matchPos = rgxFindMatchPos(str, left, right, flags), - finalStr = str, - lng = matchPos.length; - - if (lng > 0) { - var bits = []; - if (matchPos[0].wholeMatch.start !== 0) { - bits.push(str.slice(0, matchPos[0].wholeMatch.start)); - } - for (var i = 0; i < lng; ++i) { - bits.push( - replacement( - str.slice(matchPos[i].wholeMatch.start, matchPos[i].wholeMatch.end), - str.slice(matchPos[i].match.start, matchPos[i].match.end), - str.slice(matchPos[i].left.start, matchPos[i].left.end), - str.slice(matchPos[i].right.start, matchPos[i].right.end) - ) - ); - if (i < lng - 1) { - bits.push(str.slice(matchPos[i].wholeMatch.end, matchPos[i + 1].wholeMatch.start)); - } - } - if (matchPos[lng - 1].wholeMatch.end < str.length) { - bits.push(str.slice(matchPos[lng - 1].wholeMatch.end)); - } - finalStr = bits.join(''); - } - return finalStr; -}; - -/** - * POLYFILLS - */ -if (showdown.helper.isUndefined(console)) { - console = { - warn: function (msg) { - 'use strict'; - alert(msg); - }, - log: function (msg) { - 'use strict'; - alert(msg); - }, - error: function (msg) { - 'use strict'; - throw msg; - } - }; -} - -/** - * Created by Estevao on 31-05-2015. - */ - -/** - * Showdown Converter class - * @class - * @param {object} [converterOptions] - * @returns {Converter} - */ -showdown.Converter = function (converterOptions) { - 'use strict'; - - var - /** - * Options used by this converter - * @private - * @type {{}} - */ - options = {}, - - /** - * Language extensions used by this converter - * @private - * @type {Array} - */ - langExtensions = [], - - /** - * Output modifiers extensions used by this converter - * @private - * @type {Array} - */ - outputModifiers = [], - - /** - * Event listeners - * @private - * @type {{}} - */ - listeners = {}; - - _constructor(); - - /** - * Converter constructor - * @private - */ - function _constructor() { - converterOptions = converterOptions || {}; - - for (var gOpt in globalOptions) { - if (globalOptions.hasOwnProperty(gOpt)) { - options[gOpt] = globalOptions[gOpt]; - } - } - - // Merge options - if (typeof converterOptions === 'object') { - for (var opt in converterOptions) { - if (converterOptions.hasOwnProperty(opt)) { - options[opt] = converterOptions[opt]; - } - } - } else { - throw Error('Converter expects the passed parameter to be an object, but ' + typeof converterOptions + - ' was passed instead.'); - } - - if (options.extensions) { - showdown.helper.forEach(options.extensions, _parseExtension); - } - } - - /** - * Parse extension - * @param {*} ext - * @param {string} [name=''] - * @private - */ - function _parseExtension(ext, name) { - - name = name || null; - // If it's a string, the extension was previously loaded - if (showdown.helper.isString(ext)) { - ext = showdown.helper.stdExtName(ext); - name = ext; - - // LEGACY_SUPPORT CODE - if (showdown.extensions[ext]) { - console.warn('DEPRECATION WARNING: ' + ext + ' is an old extension that uses a deprecated loading method.' + - 'Please inform the developer that the extension should be updated!'); - legacyExtensionLoading(showdown.extensions[ext], ext); - return; - // END LEGACY SUPPORT CODE - - } else if (!showdown.helper.isUndefined(extensions[ext])) { - ext = extensions[ext]; - - } else { - throw Error('Extension "' + ext + '" could not be loaded. It was either not found or is not a valid extension.'); - } - } - - if (typeof ext === 'function') { - ext = ext(); - } - - if (!showdown.helper.isArray(ext)) { - ext = [ext]; - } - - var validExt = validate(ext, name); - if (!validExt.valid) { - throw Error(validExt.error); - } - - for (var i = 0; i < ext.length; ++i) { - switch (ext[i].type) { - - case 'lang': - langExtensions.push(ext[i]); - break; - - case 'output': - outputModifiers.push(ext[i]); - break; - } - if (ext[i].hasOwnProperty(listeners)) { - for (var ln in ext[i].listeners) { - if (ext[i].listeners.hasOwnProperty(ln)) { - listen(ln, ext[i].listeners[ln]); - } - } - } - } - - } - - /** - * LEGACY_SUPPORT - * @param {*} ext - * @param {string} name - */ - function legacyExtensionLoading(ext, name) { - if (typeof ext === 'function') { - ext = ext(new showdown.Converter()); - } - if (!showdown.helper.isArray(ext)) { - ext = [ext]; - } - var valid = validate(ext, name); - - if (!valid.valid) { - throw Error(valid.error); - } - - for (var i = 0; i < ext.length; ++i) { - switch (ext[i].type) { - case 'lang': - langExtensions.push(ext[i]); - break; - case 'output': - outputModifiers.push(ext[i]); - break; - default:// should never reach here - throw Error('Extension loader error: Type unrecognized!!!'); - } - } - } - - /** - * Listen to an event - * @param {string} name - * @param {function} callback - */ - function listen(name, callback) { - if (!showdown.helper.isString(name)) { - throw Error('Invalid argument in converter.listen() method: name must be a string, but ' + typeof name + ' given'); - } - - if (typeof callback !== 'function') { - throw Error('Invalid argument in converter.listen() method: callback must be a function, but ' + typeof callback + ' given'); - } - - if (!listeners.hasOwnProperty(name)) { - listeners[name] = []; - } - listeners[name].push(callback); - } - - function rTrimInputText(text) { - var rsp = text.match(/^\s*/)[0].length, - rgx = new RegExp('^\\s{0,' + rsp + '}', 'gm'); - return text.replace(rgx, ''); - } - - /** - * Dispatch an event - * @private - * @param {string} evtName Event name - * @param {string} text Text - * @param {{}} options Converter Options - * @param {{}} globals - * @returns {string} - */ - this._dispatch = function dispatch (evtName, text, options, globals) { - if (listeners.hasOwnProperty(evtName)) { - for (var ei = 0; ei < listeners[evtName].length; ++ei) { - var nText = listeners[evtName][ei](evtName, text, this, options, globals); - if (nText && typeof nText !== 'undefined') { - text = nText; - } - } - } - return text; - }; - - /** - * Listen to an event - * @param {string} name - * @param {function} callback - * @returns {showdown.Converter} - */ - this.listen = function (name, callback) { - listen(name, callback); - return this; - }; - - /** - * Converts a markdown string into HTML - * @param {string} text - * @returns {*} - */ - this.makeHtml = function (text) { - //check if text is not falsy - if (!text) { - return text; - } - - var globals = { - gHtmlBlocks: [], - gHtmlMdBlocks: [], - gHtmlSpans: [], - gUrls: {}, - gTitles: {}, - gDimensions: {}, - gListLevel: 0, - hashLinkCounts: {}, - langExtensions: langExtensions, - outputModifiers: outputModifiers, - converter: this, - ghCodeBlocks: [] - }; - - // attacklab: Replace ~ with ~T - // This lets us use tilde as an escape char to avoid md5 hashes - // The choice of character is arbitrary; anything that isn't - // magic in Markdown will work. - text = text.replace(/~/g, '~T'); - - // attacklab: Replace $ with ~D - // RegExp interprets $ as a special character - // when it's in a replacement string - text = text.replace(/\$/g, '~D'); - - // Standardize line endings - text = text.replace(/\r\n/g, '\n'); // DOS to Unix - text = text.replace(/\r/g, '\n'); // Mac to Unix - - if (options.smartIndentationFix) { - text = rTrimInputText(text); - } - - // Make sure text begins and ends with a couple of newlines: - //text = '\n\n' + text + '\n\n'; - text = text; - // detab - text = showdown.subParser('detab')(text, options, globals); - - // stripBlankLines - text = showdown.subParser('stripBlankLines')(text, options, globals); - - //run languageExtensions - showdown.helper.forEach(langExtensions, function (ext) { - text = showdown.subParser('runExtension')(ext, text, options, globals); - }); - - // run the sub parsers - text = showdown.subParser('hashPreCodeTags')(text, options, globals); - text = showdown.subParser('githubCodeBlocks')(text, options, globals); - text = showdown.subParser('hashHTMLBlocks')(text, options, globals); - text = showdown.subParser('hashHTMLSpans')(text, options, globals); - text = showdown.subParser('stripLinkDefinitions')(text, options, globals); - text = showdown.subParser('blockGamut')(text, options, globals); - text = showdown.subParser('unhashHTMLSpans')(text, options, globals); - text = showdown.subParser('unescapeSpecialChars')(text, options, globals); - - // attacklab: Restore dollar signs - text = text.replace(/~D/g, '$$'); - - // attacklab: Restore tildes - text = text.replace(/~T/g, '~'); - - // Run output modifiers - showdown.helper.forEach(outputModifiers, function (ext) { - text = showdown.subParser('runExtension')(ext, text, options, globals); - }); - return text; - }; - - /** - * Set an option of this Converter instance - * @param {string} key - * @param {*} value - */ - this.setOption = function (key, value) { - options[key] = value; - }; - - /** - * Get the option of this Converter instance - * @param {string} key - * @returns {*} - */ - this.getOption = function (key) { - return options[key]; - }; - - /** - * Get the options of this Converter instance - * @returns {{}} - */ - this.getOptions = function () { - return options; - }; - - /** - * Add extension to THIS converter - * @param {{}} extension - * @param {string} [name=null] - */ - this.addExtension = function (extension, name) { - name = name || null; - _parseExtension(extension, name); - }; - - /** - * Use a global registered extension with THIS converter - * @param {string} extensionName Name of the previously registered extension - */ - this.useExtension = function (extensionName) { - _parseExtension(extensionName); - }; - - /** - * Set the flavor THIS converter should use - * @param {string} name - */ - this.setFlavor = function (name) { - if (flavor.hasOwnProperty(name)) { - var preset = flavor[name]; - for (var option in preset) { - if (preset.hasOwnProperty(option)) { - options[option] = preset[option]; - } - } - } - }; - - /** - * Remove an extension from THIS converter. - * Note: This is a costly operation. It's better to initialize a new converter - * and specify the extensions you wish to use - * @param {Array} extension - */ - this.removeExtension = function (extension) { - if (!showdown.helper.isArray(extension)) { - extension = [extension]; - } - for (var a = 0; a < extension.length; ++a) { - var ext = extension[a]; - for (var i = 0; i < langExtensions.length; ++i) { - if (langExtensions[i] === ext) { - langExtensions[i].splice(i, 1); - } - } - for (var ii = 0; ii < outputModifiers.length; ++i) { - if (outputModifiers[ii] === ext) { - outputModifiers[ii].splice(i, 1); - } - } - } - }; - - /** - * Get all extension of THIS converter - * @returns {{language: Array, output: Array}} - */ - this.getAllExtensions = function () { - return { - language: langExtensions, - output: outputModifiers - }; - }; -}; - -/** - * Turn Markdown link shortcuts into XHTML tags. - */ -showdown.subParser('anchors', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('anchors.before', text, options, globals); - - var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) { - if (showdown.helper.isUndefined(m7)) { - m7 = ''; - } - wholeMatch = m1; - var linkText = m2, - linkId = m3.toLowerCase(), - url = m4, - title = m7; - - if (!url) { - if (!linkId) { - // lower-case and turn embedded newlines into spaces - linkId = linkText.toLowerCase().replace(/ ?\n/g, ' '); - } - url = '#' + linkId; - - if (!showdown.helper.isUndefined(globals.gUrls[linkId])) { - url = globals.gUrls[linkId]; - if (!showdown.helper.isUndefined(globals.gTitles[linkId])) { - title = globals.gTitles[linkId]; - } - } else { - if (wholeMatch.search(/\(\s*\)$/m) > -1) { - // Special case for explicit empty url - url = ''; - } else { - return wholeMatch; - } - } - } - - url = showdown.helper.escapeCharacters(url, '*_', false); - var result = ''; - - return result; - }; - - // First, handle reference-style links: [link text] [id] - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[] // or anything else - )* - ) - \] - - [ ]? // one optional space - (?:\n[ ]*)? // one optional newline followed by spaces - - \[ - (.*?) // id = $3 - \] - )()()()() // pad remaining backreferences - /g,_DoAnchors_callback); - */ - text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)][ ]?(?:\n[ ]*)?\[(.*?)])()()()()/g, writeAnchorTag); - - // - // Next, inline-style links: [link text](url "optional title") - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ( - (?: - \[[^\]]*\] // allow brackets nested one level - | - [^\[\]] // or anything else - ) - ) - \] - \( // literal paren - [ \t]* - () // no id, so leave $3 empty - ? // href = $4 - [ \t]* - ( // $5 - (['"]) // quote char = $6 - (.*?) // Title = $7 - \6 // matching quote - [ \t]* // ignore any spaces/tabs between closing quote and ) - )? // title is optional - \) - ) - /g,writeAnchorTag); - */ - text = text.replace(/(\[((?:\[[^\]]*]|[^\[\]])*)]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, - writeAnchorTag); - - // - // Last, handle reference-style shortcuts: [link text] - // These must come last in case you've also got [link test][1] - // or [link test](/foo) - // - - /* - text = text.replace(/ - ( // wrap whole match in $1 - \[ - ([^\[\]]+) // link text = $2; can't contain '[' or ']' - \] - )()()()()() // pad rest of backreferences - /g, writeAnchorTag); - */ - text = text.replace(/(\[([^\[\]]+)])()()()()()/g, writeAnchorTag); - - text = globals.converter._dispatch('anchors.after', text, options, globals); - return text; -}); - -showdown.subParser('autoLinks', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('autoLinks.before', text, options, globals); - - var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi, - delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi, - simpleMailRegex = /(?:^|[ \n\t])([A-Za-z0-9!#$%&'*+-/=?^_`\{|}~\.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?:$|[ \n\t])/gi, - delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi; - - text = text.replace(delimUrlRegex, replaceLink); - text = text.replace(delimMailRegex, replaceMail); - // simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi, - // Email addresses: - - if (options.simplifiedAutoLink) { - text = text.replace(simpleURLRegex, replaceLink); - text = text.replace(simpleMailRegex, replaceMail); - } - - function replaceLink(wm, link) { - var lnkTxt = link; - if (/^www\./i.test(link)) { - link = link.replace(/^www\./i, 'http://www.'); - } - return '' + lnkTxt + ''; - } - - function replaceMail(wholeMatch, m1) { - var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1); - return showdown.subParser('encodeEmailAddress')(unescapedStr); - } - - text = globals.converter._dispatch('autoLinks.after', text, options, globals); - - return text; -}); - -/** - * These are all the transformations that form block-level - * tags like paragraphs, headers, and list items. - */ -showdown.subParser('blockGamut', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('blockGamut.before', text, options, globals); - - // we parse blockquotes first so that we can have headings and hrs - // inside blockquotes - text = showdown.subParser('blockQuotes')(text, options, globals); - text = showdown.subParser('headers')(text, options, globals); - - // Do Horizontal Rules: - var key = showdown.subParser('hashBlock')('
', options, globals); - text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key); - text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key); - text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, key); - - text = showdown.subParser('lists')(text, options, globals); - text = showdown.subParser('codeBlocks')(text, options, globals); - text = showdown.subParser('tables')(text, options, globals); - - // We already ran _HashHTMLBlocks() before, in Markdown(), but that - // was to escape raw HTML in the original Markdown source. This time, - // we're escaping the markup we've just created, so that we don't wrap - //

tags around block-level tags. - text = showdown.subParser('hashHTMLBlocks')(text, options, globals); - text = showdown.subParser('paragraphs')(text, options, globals); - - text = globals.converter._dispatch('blockGamut.after', text, options, globals); - - return text; -}); - -showdown.subParser('blockQuotes', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('blockQuotes.before', text, options, globals); - /* - text = text.replace(/ - ( // Wrap whole match in $1 - ( - ^[ \t]*>[ \t]? // '>' at the start of a line - .+\n // rest of the first line - (.+\n)* // subsequent consecutive lines - \n* // blanks - )+ - ) - /gm, function(){...}); - */ - - text = text.replace(/((^[ \t]{0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) { - var bq = m1; - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - bq = bq.replace(/^[ \t]*>[ \t]?/gm, '~0'); // trim one level of quoting - - // attacklab: clean up hack - bq = bq.replace(/~0/g, ''); - - bq = bq.replace(/^[ \t]+$/gm, ''); // trim whitespace-only lines - bq = showdown.subParser('githubCodeBlocks')(bq, options, globals); - bq = showdown.subParser('blockGamut')(bq, options, globals); // recurse - - bq = bq.replace(/(^|\n)/g, '$1 '); - // These leading spaces screw with

 content, so we need to fix that:
-    bq = bq.replace(/(\s*
[^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
-      var pre = m1;
-      // attacklab: hack around Konqueror 3.5.4 bug:
-      pre = pre.replace(/^  /mg, '~0');
-      pre = pre.replace(/~0/g, '');
-      return pre;
-    });
-
-    return showdown.subParser('hashBlock')('
\n' + bq + '\n
', options, globals); - }); - - text = globals.converter._dispatch('blockQuotes.after', text, options, globals); - return text; -}); - -/** - * Process Markdown `
` blocks.
- */
-showdown.subParser('codeBlocks', function (text, options, globals) {
-  'use strict';
-
-  text = globals.converter._dispatch('codeBlocks.before', text, options, globals);
-  /*
-   text = text.replace(text,
-   /(?:\n\n|^)
-   (								// $1 = the code block -- one or more lines, starting with a space/tab
-   (?:
-   (?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
-   .*\n+
-   )+
-   )
-   (\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
-   /g,function(){...});
-   */
-
-  // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
-  text += '~0';
-
-  var pattern = /(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g;
-  text = text.replace(pattern, function (wholeMatch, m1, m2) {
-    var codeblock = m1,
-        nextChar = m2,
-        end = '\n';
-
-    codeblock = showdown.subParser('outdent')(codeblock);
-    codeblock = showdown.subParser('encodeCode')(codeblock);
-    codeblock = showdown.subParser('detab')(codeblock);
-    codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines
-    codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing newlines
-
-    if (options.omitExtraWLInCodeBlocks) {
-      end = '';
-    }
-
-    codeblock = '
' + codeblock + end + '
'; - - return showdown.subParser('hashBlock')(codeblock, options, globals) + nextChar; - }); - - // attacklab: strip sentinel - text = text.replace(/~0/, ''); - - text = globals.converter._dispatch('codeBlocks.after', text, options, globals); - return text; -}); - -/** - * - * * Backtick quotes are used for spans. - * - * * You can use multiple backticks as the delimiters if you want to - * include literal backticks in the code span. So, this input: - * - * Just type ``foo `bar` baz`` at the prompt. - * - * Will translate to: - * - *

Just type foo `bar` baz at the prompt.

- * - * There's no arbitrary limit to the number of backticks you - * can use as delimters. If you need three consecutive backticks - * in your code, use four for delimiters, etc. - * - * * You can use spaces to get literal backticks at the edges: - * - * ... type `` `bar` `` ... - * - * Turns to: - * - * ... type `bar` ... - */ -showdown.subParser('codeSpans', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('codeSpans.before', text, options, globals); - - /* - text = text.replace(/ - (^|[^\\]) // Character before opening ` can't be a backslash - (`+) // $2 = Opening run of ` - ( // $3 = The code block - [^\r]*? - [^`] // attacklab: work around lack of lookbehind - ) - \2 // Matching closer - (?!`) - /gm, function(){...}); - */ - - if (typeof(text) === 'undefined') { - text = ''; - } - text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, - function (wholeMatch, m1, m2, m3) { - var c = m3; - c = c.replace(/^([ \t]*)/g, ''); // leading whitespace - c = c.replace(/[ \t]*$/g, ''); // trailing whitespace - c = showdown.subParser('encodeCode')(c); - return m1 + '' + c + ''; - } - ); - - text = globals.converter._dispatch('codeSpans.after', text, options, globals); - return text; -}); - -/** - * Convert all tabs to spaces - */ -showdown.subParser('detab', function (text) { - 'use strict'; - - // expand first n-1 tabs - text = text.replace(/\t(?=\t)/g, ' '); // g_tab_width - - // replace the nth with two sentinels - text = text.replace(/\t/g, '~A~B'); - - // use the sentinel to anchor our regex so it doesn't explode - text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1) { - var leadingText = m1, - numSpaces = 4 - leadingText.length % 4; // g_tab_width - - // there *must* be a better way to do this: - for (var i = 0; i < numSpaces; i++) { - leadingText += ' '; - } - - return leadingText; - }); - - // clean up sentinels - text = text.replace(/~A/g, ' '); // g_tab_width - text = text.replace(/~B/g, ''); - - return text; - -}); - -/** - * Smart processing for ampersands and angle brackets that need to be encoded. - */ -showdown.subParser('encodeAmpsAndAngles', function (text) { - 'use strict'; - // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: - // http://bumppo.net/projects/amputator/ - text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, '&'); - - // Encode naked <'s - text = text.replace(/<(?![a-z\/?\$!])/gi, '<'); - - return text; -}); - -/** - * Returns the string, with after processing the following backslash escape sequences. - * - * attacklab: The polite way to do this is with the new escapeCharacters() function: - * - * text = escapeCharacters(text,"\\",true); - * text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); - * - * ...but we're sidestepping its use of the (slow) RegExp constructor - * as an optimization for Firefox. This function gets called a LOT. - */ -showdown.subParser('encodeBackslashEscapes', function (text) { - 'use strict'; - text = text.replace(/\\(\\)/g, showdown.helper.escapeCharactersCallback); - text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, showdown.helper.escapeCharactersCallback); - return text; -}); - -/** - * Encode/escape certain characters inside Markdown code runs. - * The point is that in code, these characters are literals, - * and lose their special Markdown meanings. - */ -showdown.subParser('encodeCode', function (text) { - 'use strict'; - - // Encode all ampersands; HTML entities are not - // entities within a Markdown code span. - text = text.replace(/&/g, '&'); - - // Do the angle bracket song and dance: - text = text.replace(//g, '>'); - - // Now, escape characters that are magic in Markdown: - text = showdown.helper.escapeCharacters(text, '*_{}[]\\', false); - - // jj the line above breaks this: - //--- - //* Item - // 1. Subitem - // special char: * - // --- - - return text; -}); - -/** - * Input: an email address, e.g. "foo@example.com" - * - * Output: the email address as a mailto link, with each character - * of the address encoded as either a decimal or hex entity, in - * the hopes of foiling most address harvesting spam bots. E.g.: - * - * foo - * @example.com - * - * Based on a filter by Matthew Wickline, posted to the BBEdit-Talk - * mailing list: - * - */ -showdown.subParser('encodeEmailAddress', function (addr) { - 'use strict'; - - var encode = [ - function (ch) { - return '&#' + ch.charCodeAt(0) + ';'; - }, - function (ch) { - return '&#x' + ch.charCodeAt(0).toString(16) + ';'; - }, - function (ch) { - return ch; - } - ]; - - addr = 'mailto:' + addr; - - addr = addr.replace(/./g, function (ch) { - if (ch === '@') { - // this *must* be encoded. I insist. - ch = encode[Math.floor(Math.random() * 2)](ch); - } else if (ch !== ':') { - // leave ':' alone (to spot mailto: later) - var r = Math.random(); - // roughly 10% raw, 45% hex, 45% dec - ch = ( - r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch) - ); - } - return ch; - }); - - addr = '' + addr + ''; - addr = addr.replace(/">.+:/g, '">'); // strip the mailto: from the visible part - - return addr; -}); - -/** - * Within tags -- meaning between < and > -- encode [\ ` * _] so they - * don't conflict with their use in Markdown for code, italics and strong. - */ -showdown.subParser('escapeSpecialCharsWithinTagAttributes', function (text) { - 'use strict'; - - // Build a regex to find HTML tags and comments. See Friedl's - // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. - var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; - - text = text.replace(regex, function (wholeMatch) { - var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, '$1`'); - tag = showdown.helper.escapeCharacters(tag, '\\`*_', false); - return tag; - }); - - return text; -}); - -/** - * Handle github codeblocks prior to running HashHTML so that - * HTML contained within the codeblock gets escaped properly - * Example: - * ```ruby - * def hello_world(x) - * puts "Hello, #{x}" - * end - * ``` - */ -showdown.subParser('githubCodeBlocks', function (text, options, globals) { - 'use strict'; - - // early exit if option is not enabled - if (!options.ghCodeBlocks) { - return text; - } - - text = globals.converter._dispatch('githubCodeBlocks.before', text, options, globals); - - text += '~0'; - - text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) { - var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n'; - - // First parse the github code block - codeblock = showdown.subParser('encodeCode')(codeblock); - codeblock = showdown.subParser('detab')(codeblock); - codeblock = codeblock.replace(/^\n+/g, ''); // trim leading newlines - codeblock = codeblock.replace(/\n+$/g, ''); // trim trailing whitespace - - codeblock = '
' + codeblock + end + '
'; - - codeblock = showdown.subParser('hashBlock')(codeblock, options, globals); - - // Since GHCodeblocks can be false positives, we need to - // store the primitive text and the parsed text in a global var, - // and then return a token - return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n'; - }); - - // attacklab: strip sentinel - text = text.replace(/~0/, ''); - - return globals.converter._dispatch('githubCodeBlocks.after', text, options, globals); -}); - -showdown.subParser('hashBlock', function (text, options, globals) { - 'use strict'; - text = text.replace(/(^\n+|\n+$)/g, ''); - return '\n\n~K' + (globals.gHtmlBlocks.push(text) - 1) + 'K\n\n'; -}); - -showdown.subParser('hashElement', function (text, options, globals) { - 'use strict'; - - return function (wholeMatch, m1) { - var blockText = m1; - - // Undo double lines - blockText = blockText.replace(/\n\n/g, '\n'); - blockText = blockText.replace(/^\n/, ''); - - // strip trailing blank lines - blockText = blockText.replace(/\n+$/g, ''); - - // Replace the element text with a marker ("~KxK" where x is its key) - blockText = '\n\n~K' + (globals.gHtmlBlocks.push(blockText) - 1) + 'K\n\n'; - - return blockText; - }; -}); - -showdown.subParser('hashHTMLBlocks', function (text, options, globals) { - 'use strict'; - - var blockTags = [ - 'pre', - 'div', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'blockquote', - 'table', - 'dl', - 'ol', - 'ul', - 'script', - 'noscript', - 'form', - 'fieldset', - 'iframe', - 'math', - 'style', - 'section', - 'header', - 'footer', - 'nav', - 'article', - 'aside', - 'address', - 'audio', - 'canvas', - 'figure', - 'hgroup', - 'output', - 'video', - 'p' - ], - repFunc = function (wholeMatch, match, left, right) { - var txt = wholeMatch; - // check if this html element is marked as markdown - // if so, it's contents should be parsed as markdown - if (left.search(/\bmarkdown\b/) !== -1) { - txt = left + globals.converter.makeHtml(match) + right; - } - return '\n\n~K' + (globals.gHtmlBlocks.push(txt) - 1) + 'K\n\n'; - }; - - for (var i = 0; i < blockTags.length; ++i) { - text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}<' + blockTags[i] + '\\b[^>]*>', '', 'gim'); - } - - // HR SPECIAL CASE - text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, - showdown.subParser('hashElement')(text, options, globals)); - - // Special case for standalone HTML comments: - text = text.replace(/()/g, - showdown.subParser('hashElement')(text, options, globals)); - - // PHP and ASP-style processor instructions ( and <%...%>) - text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, - showdown.subParser('hashElement')(text, options, globals)); - return text; -}); - -/** - * Hash span elements that should not be parsed as markdown - */ -showdown.subParser('hashHTMLSpans', function (text, config, globals) { - 'use strict'; - - var matches = showdown.helper.matchRecursiveRegExp(text, ']*>', '', 'gi'); - - for (var i = 0; i < matches.length; ++i) { - text = text.replace(matches[i][0], '~L' + (globals.gHtmlSpans.push(matches[i][0]) - 1) + 'L'); - } - return text; -}); - -/** - * Unhash HTML spans - */ -showdown.subParser('unhashHTMLSpans', function (text, config, globals) { - 'use strict'; - - for (var i = 0; i < globals.gHtmlSpans.length; ++i) { - text = text.replace('~L' + i + 'L', globals.gHtmlSpans[i]); - } - - return text; -}); - -/** - * Hash span elements that should not be parsed as markdown - */ -showdown.subParser('hashPreCodeTags', function (text, config, globals) { - 'use strict'; - - var repFunc = function (wholeMatch, match, left, right) { - // encode html entities - var codeblock = left + showdown.subParser('encodeCode')(match) + right; - return '\n\n~G' + (globals.ghCodeBlocks.push({text: wholeMatch, codeblock: codeblock}) - 1) + 'G\n\n'; - }; - - text = showdown.helper.replaceRecursiveRegExp(text, repFunc, '^(?: |\\t){0,3}]*>\\s*]*>', '^(?: |\\t){0,3}\\s*
', 'gim'); - return text; -}); - -showdown.subParser('headers', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('headers.before', text, options, globals); - - var prefixHeader = options.prefixHeaderId, - headerLevelStart = (isNaN(parseInt(options.headerLevelStart))) ? 1 : parseInt(options.headerLevelStart), - - // Set text-style headers: - // Header 1 - // ======== - // - // Header 2 - // -------- - // - setextRegexH1 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n={2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n=+[ \t]*\n+/gm, - setextRegexH2 = (options.smoothLivePreview) ? /^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm : /^(.+)[ \t]*\n-+[ \t]*\n+/gm; - - text = text.replace(setextRegexH1, function (wholeMatch, m1) { - - var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), - hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', - hLevel = headerLevelStart, - hashBlock = '' + spanGamut + ''; - return showdown.subParser('hashBlock')(hashBlock, options, globals); - }); - - text = text.replace(setextRegexH2, function (matchFound, m1) { - var spanGamut = showdown.subParser('spanGamut')(m1, options, globals), - hID = (options.noHeaderId) ? '' : ' id="' + headerId(m1) + '"', - hLevel = headerLevelStart + 1, - hashBlock = '' + spanGamut + ''; - return showdown.subParser('hashBlock')(hashBlock, options, globals); - }); - - // atx-style headers: - // # Header 1 - // ## Header 2 - // ## Header 2 with closing hashes ## - // ... - // ###### Header 6 - // - text = text.replace(/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm, function (wholeMatch, m1, m2) { - var span = showdown.subParser('spanGamut')(m2, options, globals), - hID = (options.noHeaderId) ? '' : ' id="' + headerId(m2) + '"', - hLevel = headerLevelStart - 1 + m1.length, - header = '' + span + ''; - - return showdown.subParser('hashBlock')(header, options, globals); - }); - - function headerId(m) { - var title, escapedId = m.replace(/[^\w]/g, '').toLowerCase(); - - if (globals.hashLinkCounts[escapedId]) { - title = escapedId + '-' + (globals.hashLinkCounts[escapedId]++); - } else { - title = escapedId; - globals.hashLinkCounts[escapedId] = 1; - } - - // Prefix id to prevent causing inadvertent pre-existing style matches. - if (prefixHeader === true) { - prefixHeader = 'section'; - } - - if (showdown.helper.isString(prefixHeader)) { - return prefixHeader + title; - } - return title; - } - - text = globals.converter._dispatch('headers.after', text, options, globals); - return text; -}); - -/** - * Turn Markdown image shortcuts into tags. - */ -showdown.subParser('images', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('images.before', text, options, globals); - - var inlineRegExp = /!\[(.*?)]\s?\([ \t]*()?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(['"])(.*?)\6[ \t]*)?\)/g, - referenceRegExp = /!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g; - - function writeImageTag (wholeMatch, altText, linkId, url, width, height, m5, title) { - - var gUrls = globals.gUrls, - gTitles = globals.gTitles, - gDims = globals.gDimensions; - - linkId = linkId.toLowerCase(); - - if (!title) { - title = ''; - } - - if (url === '' || url === null) { - if (linkId === '' || linkId === null) { - // lower-case and turn embedded newlines into spaces - linkId = altText.toLowerCase().replace(/ ?\n/g, ' '); - } - url = '#' + linkId; - - if (!showdown.helper.isUndefined(gUrls[linkId])) { - url = gUrls[linkId]; - if (!showdown.helper.isUndefined(gTitles[linkId])) { - title = gTitles[linkId]; - } - if (!showdown.helper.isUndefined(gDims[linkId])) { - width = gDims[linkId].width; - height = gDims[linkId].height; - } - } else { - return wholeMatch; - } - } - - altText = altText.replace(/"/g, '"'); - altText = showdown.helper.escapeCharacters(altText, '*_', false); - url = showdown.helper.escapeCharacters(url, '*_', false); - var result = '' + altText + 'x "optional title") - text = text.replace(inlineRegExp, writeImageTag); - - text = globals.converter._dispatch('images.after', text, options, globals); - return text; -}); - -showdown.subParser('italicsAndBold', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('italicsAndBold.before', text, options, globals); - - if (options.literalMidWordUnderscores) { - //underscores - // Since we are consuming a \s character, we need to add it - text = text.replace(/(^|\s|>|\b)__(?=\S)([\s\S]+?)__(?=\b|<|\s|$)/gm, '$1$2'); - text = text.replace(/(^|\s|>|\b)_(?=\S)([\s\S]+?)_(?=\b|<|\s|$)/gm, '$1$2'); - //asterisks - text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, '$2'); - text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, '$2'); - - } else { - // must go first: - text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '$2'); - text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '$2'); - } - - text = globals.converter._dispatch('italicsAndBold.after', text, options, globals); - return text; -}); - -/** - * Form HTML ordered (numbered) and unordered (bulleted) lists. - */ -showdown.subParser('lists', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('lists.before', text, options, globals); - /** - * Process the contents of a single ordered or unordered list, splitting it - * into individual list items. - * @param {string} listStr - * @param {boolean} trimTrailing - * @returns {string} - */ - function processListItems (listStr, trimTrailing) { - // The $g_list_level global keeps track of when we're inside a list. - // Each time we enter a list, we increment it; when we leave a list, - // we decrement. If it's zero, we're not in a list anymore. - // - // We do this because when we're not inside a list, we want to treat - // something like this: - // - // I recommend upgrading to version - // 8. Oops, now this line is treated - // as a sub-list. - // - // As a single paragraph, despite the fact that the second line starts - // with a digit-period-space sequence. - // - // Whereas when we're inside a list (or sub-list), that line will be - // treated as the start of a sub-list. What a kludge, huh? This is - // an aspect of Markdown's syntax that's hard to parse perfectly - // without resorting to mind-reading. Perhaps the solution is to - // change the syntax rules such that sub-lists must start with a - // starting cardinal number; e.g. "1." or "a.". - globals.gListLevel++; - - // trim trailing blank lines: - listStr = listStr.replace(/\n{2,}$/, '\n'); - - // attacklab: add sentinel to emulate \z - listStr += '~0'; - - var rgx = /(\n)?(^[ \t]*)([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+-]|\d+[.])[ \t]+))/gm, - isParagraphed = (/\n[ \t]*\n(?!~0)/.test(listStr)); - - listStr = listStr.replace(rgx, function (wholeMatch, m1, m2, m3, m4, taskbtn, checked) { - checked = (checked && checked.trim() !== ''); - var item = showdown.subParser('outdent')(m4, options, globals), - bulletStyle = ''; - - // Support for github tasklists - if (taskbtn && options.tasklists) { - bulletStyle = ' class="task-list-item" style="list-style-type: none;"'; - item = item.replace(/^[ \t]*\[(x|X| )?]/m, function () { - var otp = ' -1)) { - item = showdown.subParser('githubCodeBlocks')(item, options, globals); - item = showdown.subParser('blockGamut')(item, options, globals); - } else { - // Recursion for sub-lists: - item = showdown.subParser('lists')(item, options, globals); - item = item.replace(/\n$/, ''); // chomp(item) - if (isParagraphed) { - item = showdown.subParser('paragraphs')(item, options, globals); - } else { - item = showdown.subParser('spanGamut')(item, options, globals); - } - } - item = '\n' + item + '\n'; - return item; - }); - - // attacklab: strip sentinel - listStr = listStr.replace(/~0/g, ''); - - globals.gListLevel--; - - if (trimTrailing) { - listStr = listStr.replace(/\s+$/, ''); - } - - return listStr; - } - - /** - * Check and parse consecutive lists (better fix for issue #142) - * @param {string} list - * @param {string} listType - * @param {boolean} trimTrailing - * @returns {string} - */ - function parseConsecutiveLists(list, listType, trimTrailing) { - // check if we caught 2 or more consecutive lists by mistake - // we use the counterRgx, meaning if listType is UL we look for UL and vice versa - var counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm, - subLists = [], - result = ''; - - if (list.search(counterRxg) !== -1) { - (function parseCL(txt) { - var pos = txt.search(counterRxg); - if (pos !== -1) { - // slice - result += '\n\n<' + listType + '>' + processListItems(txt.slice(0, pos), !!trimTrailing) + '\n\n'; - - // invert counterType and listType - listType = (listType === 'ul') ? 'ol' : 'ul'; - counterRxg = (listType === 'ul') ? /^ {0,2}\d+\.[ \t]/gm : /^ {0,2}[*+-][ \t]/gm; - - //recurse - parseCL(txt.slice(pos)); - } else { - result += '\n\n<' + listType + '>' + processListItems(txt, !!trimTrailing) + '\n\n'; - } - })(list); - for (var i = 0; i < subLists.length; ++i) { - - } - } else { - result = '\n\n<' + listType + '>' + processListItems(list, !!trimTrailing) + '\n\n'; - } - - return result; - } - - // attacklab: add sentinel to hack around khtml/safari bug: - // http://bugs.webkit.org/show_bug.cgi?id=11231 - text += '~0'; - - // Re-usable pattern to match any entire ul or ol list: - var wholeList = /^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; - - if (globals.gListLevel) { - text = text.replace(wholeList, function (wholeMatch, list, m2) { - var listType = (m2.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; - return parseConsecutiveLists(list, listType, true); - }); - } else { - wholeList = /(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm; - //wholeList = /(\n\n|^\n?)( {0,3}([*+-]|\d+\.)[ \t]+[\s\S]+?)(?=(~0)|(\n\n(?!\t| {2,}| {0,3}([*+-]|\d+\.)[ \t])))/g; - text = text.replace(wholeList, function (wholeMatch, m1, list, m3) { - - var listType = (m3.search(/[*+-]/g) > -1) ? 'ul' : 'ol'; - return parseConsecutiveLists(list, listType); - }); - } - - // attacklab: strip sentinel - text = text.replace(/~0/, ''); - - text = globals.converter._dispatch('lists.after', text, options, globals); - return text; -}); - -/** - * Remove one level of line-leading tabs or spaces - */ -showdown.subParser('outdent', function (text) { - 'use strict'; - - // attacklab: hack around Konqueror 3.5.4 bug: - // "----------bug".replace(/^-/g,"") == "bug" - text = text.replace(/^(\t|[ ]{1,4})/gm, '~0'); // attacklab: g_tab_width - - // attacklab: clean up hack - text = text.replace(/~0/g, ''); - - return text; -}); - -/** - * - */ -showdown.subParser('paragraphs', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('paragraphs.before', text, options, globals); - // Strip leading and trailing lines: - text = text.replace(/^\n+/g, ''); - text = text.replace(/\n+$/g, ''); - - var grafs = text.split(/\n{2,}/g), - grafsOut = [], - end = grafs.length; // Wrap

tags - - for (var i = 0; i < end; i++) { - var str = grafs[i]; - // if this is an HTML marker, copy it - if (str.search(/~(K|G)(\d+)\1/g) >= 0) { - grafsOut.push(str); - } else { - str = showdown.subParser('spanGamut')(str, options, globals); - str = str.replace(/^([ \t]*)/g, '

'); - str += '

'; - grafsOut.push(str); - } - } - - /** Unhashify HTML blocks */ - end = grafsOut.length; - for (i = 0; i < end; i++) { - var blockText = '', - grafsOutIt = grafsOut[i], - codeFlag = false; - // if this is a marker for an html block... - while (grafsOutIt.search(/~(K|G)(\d+)\1/) >= 0) { - var delim = RegExp.$1, - num = RegExp.$2; - - if (delim === 'K') { - blockText = globals.gHtmlBlocks[num]; - } else { - // we need to check if ghBlock is a false positive - if (codeFlag) { - // use encoded version of all text - blockText = showdown.subParser('encodeCode')(globals.ghCodeBlocks[num].text); - } else { - blockText = globals.ghCodeBlocks[num].codeblock; - } - } - blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs - - grafsOutIt = grafsOutIt.replace(/(\n\n)?~(K|G)\d+\2(\n\n)?/, blockText); - // Check if grafsOutIt is a pre->code - if (/^]*>\s*]*>/.test(grafsOutIt)) { - codeFlag = true; - } - } - grafsOut[i] = grafsOutIt; - } - text = grafsOut.join('\n\n'); - // Strip leading and trailing lines: - text = text.replace(/^\n+/g, ''); - text = text.replace(/\n+$/g, ''); - return globals.converter._dispatch('paragraphs.after', text, options, globals); -}); - -/** - * Run extension - */ -showdown.subParser('runExtension', function (ext, text, options, globals) { - 'use strict'; - - if (ext.filter) { - text = ext.filter(text, globals.converter, options); - - } else if (ext.regex) { - // TODO remove this when old extension loading mechanism is deprecated - var re = ext.regex; - if (!re instanceof RegExp) { - re = new RegExp(re, 'g'); - } - text = text.replace(re, ext.replace); - } - - return text; -}); - -/** - * These are all the transformations that occur *within* block-level - * tags like paragraphs, headers, and list items. - */ -showdown.subParser('spanGamut', function (text, options, globals) { - 'use strict'; - - text = globals.converter._dispatch('spanGamut.before', text, options, globals); - text = showdown.subParser('codeSpans')(text, options, globals); - text = showdown.subParser('escapeSpecialCharsWithinTagAttributes')(text, options, globals); - text = showdown.subParser('encodeBackslashEscapes')(text, options, globals); - - // Process anchor and image tags. Images must come first, - // because ![foo][f] looks like an anchor. - text = showdown.subParser('images')(text, options, globals); - text = showdown.subParser('anchors')(text, options, globals); - - // Make links out of things like `` - // Must come after _DoAnchors(), because you can use < and > - // delimiters in inline links like [this](). - text = showdown.subParser('autoLinks')(text, options, globals); - text = showdown.subParser('encodeAmpsAndAngles')(text, options, globals); - text = showdown.subParser('italicsAndBold')(text, options, globals); - text = showdown.subParser('strikethrough')(text, options, globals); - - // Do hard breaks: - text = text.replace(/ +\n/g, '
\n'); - - text = globals.converter._dispatch('spanGamut.after', text, options, globals); - return text; -}); - -showdown.subParser('strikethrough', function (text, options, globals) { - 'use strict'; - - if (options.strikethrough) { - text = globals.converter._dispatch('strikethrough.before', text, options, globals); - text = text.replace(/(?:~T){2}([\s\S]+?)(?:~T){2}/g, '$1'); - text = globals.converter._dispatch('strikethrough.after', text, options, globals); - } - - return text; -}); - -/** - * Strip any lines consisting only of spaces and tabs. - * This makes subsequent regexs easier to write, because we can - * match consecutive blank lines with /\n+/ instead of something - * contorted like /[ \t]*\n+/ - */ -showdown.subParser('stripBlankLines', function (text) { - 'use strict'; - return text.replace(/^[ \t]+$/mg, ''); -}); - -/** - * Strips link definitions from text, stores the URLs and titles in - * hash references. - * Link defs are in the form: ^[id]: url "optional title" - * - * ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 - * [ \t]* - * \n? // maybe *one* newline - * [ \t]* - * ? // url = $2 - * [ \t]* - * \n? // maybe one newline - * [ \t]* - * (?: - * (\n*) // any lines skipped = $3 attacklab: lookbehind removed - * ["(] - * (.+?) // title = $4 - * [")] - * [ \t]* - * )? // title is optional - * (?:\n+|$) - * /gm, - * function(){...}); - * - */ -showdown.subParser('stripLinkDefinitions', function (text, options, globals) { - 'use strict'; - - var regex = /^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=~0))/gm; - - // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug - text += '~0'; - - text = text.replace(regex, function (wholeMatch, linkId, url, width, height, blankLines, title) { - linkId = linkId.toLowerCase(); - globals.gUrls[linkId] = showdown.subParser('encodeAmpsAndAngles')(url); // Link IDs are case-insensitive - - if (blankLines) { - // Oops, found blank lines, so it's not a title. - // Put back the parenthetical statement we stole. - return blankLines + title; - - } else { - if (title) { - globals.gTitles[linkId] = title.replace(/"|'/g, '"'); - } - if (options.parseImgDimensions && width && height) { - globals.gDimensions[linkId] = { - width: width, - height: height - }; - } - } - // Completely remove the definition from the text - return ''; - }); - - // attacklab: strip sentinel - text = text.replace(/~0/, ''); - - return text; -}); - -showdown.subParser('tables', function (text, options, globals) { - 'use strict'; - - if (!options.tables) { - return text; - } - - var tableRgx = /^[ \t]{0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|~0)/gm; - - function parseStyles(sLine) { - if (/^:[ \t]*--*$/.test(sLine)) { - return ' style="text-align:left;"'; - } else if (/^--*[ \t]*:[ \t]*$/.test(sLine)) { - return ' style="text-align:right;"'; - } else if (/^:[ \t]*--*[ \t]*:$/.test(sLine)) { - return ' style="text-align:center;"'; - } else { - return ''; - } - } - - function parseHeaders(header, style) { - var id = ''; - header = header.trim(); - if (options.tableHeaderId) { - id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; - } - header = showdown.subParser('spanGamut')(header, options, globals); - - return '' + header + '\n'; - } - - function parseCells(cell, style) { - var subText = showdown.subParser('spanGamut')(cell, options, globals); - return '' + subText + '\n'; - } - - function buildTable(headers, cells) { - var tb = '\n\n\n', - tblLgn = headers.length; - - for (var i = 0; i < tblLgn; ++i) { - tb += headers[i]; - } - tb += '\n\n\n'; - - for (i = 0; i < cells.length; ++i) { - tb += '\n'; - for (var ii = 0; ii < tblLgn; ++ii) { - tb += cells[i][ii]; - } - tb += '\n'; - } - tb += '\n
\n'; - return tb; - } - - text = globals.converter._dispatch('tables.before', text, options, globals); - - text = text.replace(tableRgx, function (rawTable) { - - var i, tableLines = rawTable.split('\n'); - - // strip wrong first and last column if wrapped tables are used - for (i = 0; i < tableLines.length; ++i) { - if (/^[ \t]{0,3}\|/.test(tableLines[i])) { - tableLines[i] = tableLines[i].replace(/^[ \t]{0,3}\|/, ''); - } - if (/\|[ \t]*$/.test(tableLines[i])) { - tableLines[i] = tableLines[i].replace(/\|[ \t]*$/, ''); - } - } - - var rawHeaders = tableLines[0].split('|').map(function (s) { return s.trim();}), - rawStyles = tableLines[1].split('|').map(function (s) { return s.trim();}), - rawCells = [], - headers = [], - styles = [], - cells = []; - - tableLines.shift(); - tableLines.shift(); - - for (i = 0; i < tableLines.length; ++i) { - if (tableLines[i].trim() === '') { - continue; - } - rawCells.push( - tableLines[i] - .split('|') - .map(function (s) { - return s.trim(); - }) - ); - } - - if (rawHeaders.length < rawStyles.length) { - return rawTable; - } - - for (i = 0; i < rawStyles.length; ++i) { - styles.push(parseStyles(rawStyles[i])); - } - - for (i = 0; i < rawHeaders.length; ++i) { - if (showdown.helper.isUndefined(styles[i])) { - styles[i] = ''; - } - headers.push(parseHeaders(rawHeaders[i], styles[i])); - } - - for (i = 0; i < rawCells.length; ++i) { - var row = []; - for (var ii = 0; ii < headers.length; ++ii) { - if (showdown.helper.isUndefined(rawCells[i][ii])) { - - } - row.push(parseCells(rawCells[i][ii], styles[ii])); - } - cells.push(row); - } - - return buildTable(headers, cells); - }); - - text = globals.converter._dispatch('tables.after', text, options, globals); - - return text; -}); - -/** - * Swap back in all the special characters we've hidden. - */ -showdown.subParser('unescapeSpecialChars', function (text) { - 'use strict'; - - text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) { - var charCodeToReplace = parseInt(m1); - return String.fromCharCode(charCodeToReplace); - }); - return text; -}); -module.exports = showdown; diff --git a/components/wxParse/wxParse.js b/components/wxParse/wxParse.js index 4820cdc..ebd1f17 100644 --- a/components/wxParse/wxParse.js +++ b/components/wxParse/wxParse.js @@ -1,31 +1,23 @@ -import HtmlToJson from './utils/html2json'; -import showdown from './utils/showdown.js'; -import { getSystemInfo, bindData } from './utils/util'; +import HtmlToJson from '../../utils/html2json'; +import { getSystemInfo, bindData } from '../../utils/util'; Component({ properties: { nodes: { type: null, observer(val) { - const { language } = this.properties if (val) { - if (language === 'html') { - this._parseNodes(val) - } - - if (language === 'markdown' || language === 'md') { - const converter = new showdown.Converter(); - const parseNodes = converter.makeHtml(val); - this._parseNodes(parseNodes) + if (typeof val === 'string') { // 初始为html富文本字符串 + this._parseHtml(val) + } else if (Array.isArray(val)) { // html 富文本解析成节点数组 + this.setData({ nodesData: val }) + } else { // 其余为单个节点对象 + const nodesData = [ val ] + this.setData({ nodesData }) } } } }, - - language: { - type: String, - value: 'html' // 可选:html | markdown (md) - } }, data: { @@ -42,6 +34,7 @@ Component({ transData.view = {} transData.view.imagePadding = 0 + this.setData({ nodesData: transData.nodes, bindData: { @@ -52,17 +45,6 @@ Component({ console.log(this.data) }, - _parseNodes(nodes) { - if (typeof nodes === 'string') { // 初始为html富文本字符串 - this._parseHtml(nodes) - } else if (Array.isArray(nodes)) { // html 富文本解析成节点数组 - this.setData({ nodesData: nodes }) - } else { // 其余为单个节点对象 - const nodesData = [ nodes ] - this.setData({ nodesData }) - } - }, - /** * 图片视觉宽高计算函数区 * @param {*} e diff --git a/components/wxParse/wxParse.wxml b/components/wxParse/wxParse.wxml index 72acb28..f700d3a 100644 --- a/components/wxParse/wxParse.wxml +++ b/components/wxParse/wxParse.wxml @@ -17,7 +17,7 @@ {{index + 1}}. - + @@ -31,7 +31,7 @@ - + diff --git a/components/wxParse/wxParse.wxss b/components/wxParse/wxParse.wxss index 5e3fdfb..d8638d3 100644 --- a/components/wxParse/wxParse.wxss +++ b/components/wxParse/wxParse.wxss @@ -231,10 +231,6 @@ view{ flex: 1; } -.overflow-hide { - overflow: hidden; -} - @keyframes loading { 0% { transform: rotate(0deg); diff --git a/pages/index/index.js b/pages/index/index.js index 9555c82..9726477 100644 --- a/pages/index/index.js +++ b/pages/index/index.js @@ -1 +1,3 @@ -Page({}) \ No newline at end of file +"use strict"; +Page({}); +//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJpbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiO0FBQUEsSUFBSSxDQUFDLEVBQUUsQ0FBQyxDQUFBIiwic291cmNlc0NvbnRlbnQiOlsiUGFnZSh7fSkiXX0= \ No newline at end of file diff --git a/pages/index/index.ts b/pages/index/index.ts new file mode 100644 index 0000000..9555c82 --- /dev/null +++ b/pages/index/index.ts @@ -0,0 +1 @@ +Page({}) \ No newline at end of file diff --git a/pages/richTextPage/richTextPage.js b/pages/richTextPage/richTextPage.js index 87136bf..06a71c2 100644 --- a/pages/richTextPage/richTextPage.js +++ b/pages/richTextPage/richTextPage.js @@ -1,68 +1,6 @@ -const md = '# 我是一级标题\n' + - '## 我是二级标题\n' + - '### 我是三级标题\n' + - '[**Showdown**](http://www.showdownjs.com) is *great*\n' + - '\nbecause:\n' + - ' - it\'s easy to use\n' + - ' - it\'s extensible\n' + - ' - works in the server and in the browser'; - -const html = ` -
- 字体标签 -
-

h1

-

h2

-

h3

-

h4

- -
- audio标签 -
- - -
- 一. 转义字符 -
<div style="color:red">我是转义字符</div> - -
- 二. a标签跳转 -
- a标签跳转  - -
- 三. 内联标签 -
- 我是内联标签 -
-

-

- -
- 四. ul无序列表 -
-
    -
  • 1
  • -
- -
- 五. ol有序列表 -
-
    -
  1. test
  2. -
  3. test2
  4. -
- -
- 六. 图片渲染 -
-

-` - Page({ data: { - htmlText: html, - mdText: md, + htmlText: '

audio标签