diff --git a/composer.json b/composer.json index c49c3fd..50841d0 100644 --- a/composer.json +++ b/composer.json @@ -5,7 +5,6 @@ "ext-json": "*", "twig/twig": "*", "phpmailer/phpmailer": "*", - "erusev/parsedown": "*", "paypal/rest-api-sdk-php": "*", "jbbcode/jbbcode": "^1.3" } diff --git a/libraries/News.php b/libraries/News.php index 3d01229..c33c687 100644 --- a/libraries/News.php +++ b/libraries/News.php @@ -29,9 +29,6 @@ class News $this->posters[$post['user_id']] = User::construct($post['user_id']); } - // Parse the news post - $post['news_content_parsed'] = Utils::mdParse($post['news_content']); - // Attach the poster $post['news_poster'] = $this->posters[$post['user_id']]; diff --git a/libraries/User.php b/libraries/User.php index 3c65517..4356d9d 100644 --- a/libraries/User.php +++ b/libraries/User.php @@ -692,13 +692,13 @@ class User // Get a user's userpage public function userPage() { - return Utils::mdParse($this->page, true); + return BBcode::toHTML(htmlentities($this->page)); } // Get a user's signature public function signature() { - return BBcode::toHTML($this->signature); + return BBcode::toHTML(htmlentities($this->signature)); } // Get username change history diff --git a/libraries/Utils.php b/libraries/Utils.php index f6de3a0..74b3ff7 100644 --- a/libraries/Utils.php +++ b/libraries/Utils.php @@ -5,7 +5,6 @@ namespace Sakura; -use Parsedown; use PHPMailer; /** @@ -14,16 +13,6 @@ use PHPMailer; */ class Utils { - // Parse markdown - public static function mdParse($text, $escape = false) - { - $pd = new Parsedown(); - - return $escape ? - $pd->setMarkupEscaped(true)->text($text) : - $pd->text($text); - } - // Get emoticons public static function getEmotes() { @@ -270,22 +259,8 @@ class Utils // Subject line $mail->Subject = $subject; - // Set the mail type to HTML - $mail->isHTML(true); - - // Set email contents - $htmlMail = file_get_contents(ROOT . 'templates/htmlEmail.html'); - - // Replace template tags - $htmlMail = str_replace('{{ sitename }}', Config::get('sitename'), $htmlMail); - $htmlMail = str_replace('{{ siteurl }}', '//' . Config::get('url_main'), $htmlMail); - $htmlMail = str_replace('{{ contents }}', self::mdParse($body), $htmlMail); - - // Set HTML body - $mail->Body = $htmlMail; - - // Set fallback body - $mail->AltBody = $body; + // Set body + $mail->Body = $body; // Send the message $send = $mail->send(); diff --git a/public/content/data/misaki/css/misaki.css b/public/content/data/misaki/css/misaki.css index 88731f4..c5ab121 100644 --- a/public/content/data/misaki/css/misaki.css +++ b/public/content/data/misaki/css/misaki.css @@ -16,11 +16,6 @@ @import url('/content/fonts/exo2-0-light/font.css'); /* Exo 2.0 Light */ @import url('/content/fonts/exo2-0-lightitalic/font.css'); /* Exo 2.0 Light Italic */ -/* - * Import markdown stylesheet - */ -@import url('markdown.css'); - /* * Keyframe definitions */ diff --git a/public/content/data/yuuno/css/bbcode.css b/public/content/data/yuuno/css/bbcode.css index 6555c86..c060d14 100644 --- a/public/content/data/yuuno/css/bbcode.css +++ b/public/content/data/yuuno/css/bbcode.css @@ -8,7 +8,9 @@ word-wrap: break-word; } -.bbcode h1 { +.bbcode h1, +.bbcode h2, +.bbcode h3 { text-shadow: 0 0 5px #8364A1; color: #614390; } @@ -30,7 +32,7 @@ border: 1px solid #9475b2; border-bottom: 0; border-right: 0; - background: linear-gradient(90deg, #B697d4, transparent) transparent; + background: #D8B9F6; margin: .5em; } @@ -44,7 +46,7 @@ .bbcode blockquote > .quote { margin-left: .5em; - padding-bottom: .5em; + padding-bottom: .2em; } .bbcode a { diff --git a/public/content/data/yuuno/css/markdown.css b/public/content/data/yuuno/css/markdown.css deleted file mode 100644 index 63bef1a..0000000 --- a/public/content/data/yuuno/css/markdown.css +++ /dev/null @@ -1,121 +0,0 @@ -/* - * Markdown Styling - */ -@charset "utf-8"; - -.markdown { - line-height: 1.4; - word-wrap: break-word; -} - -.markdown a { - color: #00F; - text-decoration: none; -} - -.markdown a:hover { - color: #00F; - text-decoration: underline; -} - -.markdown a:active { - color: #F00; - text-decoration: underline; -} - -.markdown h1, -.markdown h2, -.markdown h3, -.markdown h4, -.markdown h5, -.markdown h6 { - border-bottom: 1px solid; - padding-bottom: 5px; - font-weight: 700; -} - -.markdown p, -.markdown blockquote, -.markdown ul, -.markdown ol, -.markdown dl, -.markdown table, -.markdown pre { - margin-top: 0; - margin-bottom: 16px; -} - -.markdown hr { - border: 0; - height: 1px; - color: #000; - background: #000; - margin: 0; -} - -.markdown table { - display: block; - width: 100%; - overflow: auto; - word-break: keep-all; - border-collapse: collapse; - border-spacing: 0; -} - -.markdown table tr { - border-spacing: 0; - border: 1px solid #9475B2; -} - -.markdown table th, -.markdown table td { - padding: 6px 13px; - border: 1px solid #9475B2; -} - -.markdown table tr:nth-child(even) { - background: #B19DDD; -} - -.markdown hr { - background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAECAYAAACtBE5DAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyJpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBNYWNpbnRvc2giIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6OENDRjNBN0E2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6OENDRjNBN0I2NTZBMTFFMEI3QjRBODM4NzJDMjlGNDgiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo4Q0NGM0E3ODY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo4Q0NGM0E3OTY1NkExMUUwQjdCNEE4Mzg3MkMyOUY0OCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/PqqezsUAAAAfSURBVHjaYmRABcYwBiM2QSA4y4hNEKYDQxAEAAIMAHNGAzhkPOlYAAAAAElFTkSuQmCC') repeat-x scroll 0 0 transparent; - border: 0 none; - color: #CCC; - height: 4px; - padding: 0; - margin: 15px 0; -} - -.markdown blockquote { - border-left: 4px solid #9475B2; - padding: 0 15px; - color: #555; -} - -.markdown blockquote > blockquote { - margin-left: 0; -} - -.markdown blockquote > :last-child { - margin-bottom: 0; -} - -.markdown pre { - word-wrap: normal; -} - -.markdown .highlight pre, -.markdown pre { - background: #B19DDD; - border: 1px solid #9475B2; - font-size: 13px; - line-height: 19px; - overflow: auto; - padding: 6px 10px; - border-radius: 3px; -} - -.markdown ul, -.markdown ol { - margin-left: 40px; -} diff --git a/public/content/data/yuuno/css/yuuno.css b/public/content/data/yuuno/css/yuuno.css index 100f0f9..522c110 100644 --- a/public/content/data/yuuno/css/yuuno.css +++ b/public/content/data/yuuno/css/yuuno.css @@ -13,9 +13,8 @@ @import url('/content/fonts/segoeui/font.css'); @import url('/content/fonts/segoeui-light/font.css'); -/* Import bbcode and markdown specific style */ +/* Import bbcode specific style */ @import url('bbcode.css'); -@import url('markdown.css'); /* * Animation Keyframes @@ -733,6 +732,12 @@ a.default:active { font-size: .8em; } +.ad-container img { + box-sizing: border-box; + width: 100%; + height: 100%; +} + .ad-container a { color: #22E; text-decoration: none; @@ -752,7 +757,12 @@ a.default:active { .ad-container.ad-sidebar > .ad-box { text-align: center; - padding: 2px; + padding: 6px 2px; +} + +.ad-container.ad-sidebar img { + max-height: 250px; + max-width: 250px; } .ad-container.ad-footer { @@ -761,6 +771,11 @@ a.default:active { text-align: center; } +.ad-container.ad-footer img { + max-height: 90px; + max-width: 728px; +} + .ad-container.ad-footer > .ad-box { display: inline-block; padding: 2px; @@ -1198,10 +1213,15 @@ a.default:active { } .news-body { - font-size: 10pt; + font-size: .9em; padding: 2px 0 0 3px; } +.news-body p { + margin: 0; + margin-bottom: 1em; +} + .news-post-time { font-size: 8pt; padding: 6px 15px; diff --git a/public/content/data/yuuno/js/yuuno.js b/public/content/data/yuuno/js/yuuno.js index d9b7ec0..4ea7868 100644 --- a/public/content/data/yuuno/js/yuuno.js +++ b/public/content/data/yuuno/js/yuuno.js @@ -378,56 +378,6 @@ function registerVarCheck(id, mode, option) { input.className = input.className.replace(check ? 'red' : 'green', ''); } } -// Initialising the element parallax functionality -function initialiseParallax(id) { - // Assign the element to a variable - var parallax = document.getElementById(id); - // Set proper position values - parallax.style.top = '-2.5px'; - parallax.style.bottom = '-2.5px'; - parallax.style.left = '-2.5px'; - parallax.style.right = '-2.5px'; - // Add the event listener to the body element - document.addEventListener("mousemove", function (e) { - // Alter the position of the parallaxed element - parallax.style.top = convertParallaxPositionValue(e.clientY, true, false) + 'px'; - parallax.style.bottom = convertParallaxPositionValue(e.clientY, true, true) + 'px'; - parallax.style.left = convertParallaxPositionValue(e.clientX, false, false) + 'px'; - parallax.style.right = convertParallaxPositionValue(e.clientX, false, true) + 'px'; - }); -} -// Converting the position value of the mouseover to a pixel value -function convertParallaxPositionValue(pos, dir, neg) { - // Get the body element - var body = document.getElementsByTagName('body')[0]; - // Get percentage of current position - var position = (pos / (dir ? body.clientHeight : body.clientWidth)) * 100; - // If someone decided to fuck with the inputs reset it to 0% - if (position < 0 || position > 100) { - position = 0; - } - // Do the first maths - position = (position / (dir ? 25 : 20)) - 2.5; - // If the negative flag is set inverse the number - if (neg) { - position = -position; - } - // Subtract another 2.5 to make the element not go all over the place - position = position - 2.5; - // Return the proper position value - return position; -} -// """"""""Smooth"""""""" scrolling -function scrollToTop() { - // Get the current position - var windowY = window.pageYOffset - 100; - // Move up - window.scrollTo(0, windowY); - // Keep executing this function till we're at the top - if (windowY + 1 > 0) { - setTimeout(function () { scrollToTop(); }, 10); - } -} // Replace some special tags function replaceTag(tag) { return { '&': '&', '<': '<', '>': '>' }[tag] || tag; diff --git a/public/content/data/yuuno/js/yuuno.ts b/public/content/data/yuuno/js/yuuno.ts index 9ae9051..3644065 100644 --- a/public/content/data/yuuno/js/yuuno.ts +++ b/public/content/data/yuuno/js/yuuno.ts @@ -466,69 +466,6 @@ function registerVarCheck(id: string, mode: string, option: any = null): void { } } -// Initialising the element parallax functionality -function initialiseParallax(id: string) { - // Assign the element to a variable - var parallax: HTMLElement = document.getElementById(id); - - // Set proper position values - parallax.style.top = '-2.5px'; - parallax.style.bottom = '-2.5px'; - parallax.style.left = '-2.5px'; - parallax.style.right = '-2.5px'; - - // Add the event listener to the body element - document.addEventListener("mousemove", (e) => { - // Alter the position of the parallaxed element - parallax.style.top = convertParallaxPositionValue(e.clientY, true, false) + 'px'; - parallax.style.bottom = convertParallaxPositionValue(e.clientY, true, true) + 'px'; - parallax.style.left = convertParallaxPositionValue(e.clientX, false, false) + 'px'; - parallax.style.right = convertParallaxPositionValue(e.clientX, false, true) + 'px'; - }); -} - -// Converting the position value of the mouseover to a pixel value -function convertParallaxPositionValue(pos: number, dir: boolean, neg: boolean): number { - // Get the body element - var body: HTMLElement = document.getElementsByTagName('body')[0]; - - // Get percentage of current position - var position: number = (pos / (dir ? body.clientHeight : body.clientWidth)) * 100; - - // If someone decided to fuck with the inputs reset it to 0% - if (position < 0 || position > 100) { - position = 0; - } - - // Do the first maths - position = (position / (dir ? 25 : 20)) - 2.5; - - // If the negative flag is set inverse the number - if (neg) { - position = -position; - } - - // Subtract another 2.5 to make the element not go all over the place - position = position - 2.5; - - // Return the proper position value - return position; -} - -// """"""""Smooth"""""""" scrolling -function scrollToTop(): void { - // Get the current position - var windowY: number = window.pageYOffset - 100; - - // Move up - window.scrollTo(0, windowY); - - // Keep executing this function till we're at the top - if (windowY + 1 > 0) { - setTimeout(() => { scrollToTop(); }, 10); - } -} - // Replace some special tags function replaceTag(tag: string): string { return { '&': '&', '<': '<', '>': '>' }[tag] || tag; diff --git a/public/content/libraries/showdown.js b/public/content/libraries/showdown.js deleted file mode 100644 index 0433f6b..0000000 --- a/public/content/libraries/showdown.js +++ /dev/null @@ -1,2295 +0,0 @@ -;/*! showdown 27-08-2015 */ -(function(){ -/** - * Created by Tivie on 13-07-2015. - */ - -function getDefaultOpts(simple) { - 'use strict'; - - var defaultOptions = { - omitExtraWLInCodeBlocks: { - default: false, - describe: 'Omit the default extra whiteline added to code blocks', - type: 'boolean' - }, - noHeaderId: { - default: false, - describe: 'Turn on/off generated header id', - type: 'boolean' - }, - prefixHeaderId: { - default: false, - describe: 'Specify a prefix to generated header ids', - type: 'string' - }, - headerLevelStart: { - default: false, - describe: 'The header blocks level start', - type: 'integer' - }, - parseImgDimensions: { - default: false, - describe: 'Turn on/off image dimension parsing', - type: 'boolean' - }, - simplifiedAutoLink: { - default: false, - describe: 'Turn on/off GFM autolink style', - type: 'boolean' - }, - literalMidWordUnderscores: { - default: false, - describe: 'Parse midword underscores as literal underscores', - type: 'boolean' - }, - strikethrough: { - default: false, - describe: 'Turn on/off strikethrough support', - type: 'boolean' - }, - tables: { - default: false, - describe: 'Turn on/off tables support', - type: 'boolean' - }, - tablesHeaderId: { - default: false, - describe: 'Add an id to table headers', - type: 'boolean' - }, - ghCodeBlocks: { - default: true, - describe: 'Turn on/off GFM fenced code blocks support', - type: 'boolean' - }, - tasklists: { - default: false, - describe: 'Turn on/off GFM tasklist support', - type: 'boolean' - }, - smoothLivePreview: { - default: false, - describe: 'Prevents weird effects in live previews due to incomplete input', - 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].default; - } - } - 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') { - ret.valid = false; - ret.error = baseMsg + 'type ' + type + ' is not recognized. Valid values: "lang" or "output"'; - 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; - } - - } else { - ret.valid = false; - ret.error = baseMsg + 'extensions must define either a "regex" property or a "filter" method'; - return ret; - } - - if (showdown.helper.isUndefined(ext.filter) && showdown.helper.isUndefined(ext.regex)) { - ret.valid = false; - ret.error = baseMsg + 'output extensions must define a filter property'; - 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); -}; - -/** - * 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; -}; - -/** - * POLYFILLS - */ -if (showdown.helper.isUndefined(console)) { - console = { - warn: function (msg) { - 'use strict'; - alert(msg); - }, - log: function (msg) { - 'use strict'; - alert(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 = [], - - /** - * The parser Order - * @private - * @type {string[]} - */ - parserOrder = [ - 'githubCodeBlocks', - 'hashHTMLBlocks', - 'stripLinkDefinitions', - 'blockGamut', - 'unescapeSpecialChars' - ]; - - _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; - - default: - // should never reach here - throw Error('Extension loader error: Type unrecognized!!!'); - } - } - } - - /** - * 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!!!'); - } - } - } - - /** - * 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: [], - gUrls: {}, - gTitles: {}, - gDimensions: {}, - gListLevel: 0, - hashLinkCounts: {}, - langExtensions: langExtensions, - outputModifiers: outputModifiers, - converter: this - }; - - // 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 - - // Make sure text begins and ends with a couple of newlines: - text = '\n\n' + text + '\n\n'; - - // 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 all registered parsers - for (var i = 0; i < parserOrder.length; ++i) { - var name = parserOrder[i]; - text = parsers[name](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, config, globals) { - 'use strict'; - - 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); - - return text; - -}); - -showdown.subParser('autoLinks', function (text, options) { - 'use strict'; - - //simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi, - - var simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)(?=\s|$)(?!["<>])/gi, - delimUrlRegex = /<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)>/gi, - simpleMailRegex = /\b(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)\b/gi, - delimMailRegex = /<(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi; - - text = text.replace(delimUrlRegex, '$1'); - text = text.replace(delimMailRegex, replaceMail); - //simpleURLRegex = /\b(((https?|ftp|dict):\/\/|www\.)[-.+~:?#@!$&'()*,;=[\]\w]+)\b/gi, - // Email addresses: - - if (options.simplifiedAutoLink) { - text = text.replace(simpleURLRegex, '$1'); - text = text.replace(simpleMailRegex, replaceMail); - } - - function replaceMail(wholeMatch, m1) { - var unescapedStr = showdown.subParser('unescapeSpecialChars')(m1); - return showdown.subParser('encodeEmailAddress')(unescapedStr); - } - - 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'; - - // 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); - - return text; - -}); - -showdown.subParser('blockQuotes', function (text, options, globals) { - 'use strict'; - - /* - 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); - }); - return text; -}); - -/** - * Process Markdown `
` blocks.
- */
-showdown.subParser('codeBlocks', function (text, options, globals) {
-  'use strict';
-
-  /*
-   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/, ''); - - 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) { - 'use strict'; - - //special case -> literal html code tag - text = text.replace(/(<]*?>)([^]*?)<\/code>/g, function (wholeMatch, tag, c) { - c = c.replace(/^([ \t]*)/g, ''); // leading whitespace - c = c.replace(/[ \t]*$/g, ''); // trailing whitespace - c = showdown.subParser('encodeCode')(c); - return tag + c + '
'; - }); - - /* - 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(){...}); - */ - 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 + ''; - } - ); - - 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 += '~0'; - - text = text.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g, function (wholeMatch, language, codeblock) { - var end = (options.omitExtraWLInCodeBlocks) ? '' : '\n'; - - 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 + '
'; - - return showdown.subParser('hashBlock')(codeblock, options, globals); - }); - - // attacklab: strip sentinel - text = text.replace(/~0/, ''); - - return text; - -}); - -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'; - - // attacklab: Double up blank lines to reduce lookaround - text = text.replace(/\n/g, '\n\n'); - - // Hashify HTML blocks: - // We only want to do this for block-level HTML tags, such as headers, - // lists, and tables. That's because we still want to wrap

s around - // "paragraphs" that are wrapped in non-block-level tags, such as anchors, - // phrase emphasis, and spans. The list of tags we're looking for is - // hard-coded: - //var block_tags_a = - // 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del|style|section|header|footer|nav|article|aside'; - // var block_tags_b = - // 'p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside'; - - // First, look for nested blocks, e.g.: - //

- //
- // tags for inner block must be indented. - //
- //
- // - // The outermost tags must start at the left margin for this to match, and - // the inner nested divs must be indented. - // We need to do this before the next, more liberal match, because the next - // match will start at the first `
` and stop at the first `
`. - - // attacklab: This regex can be expensive when it fails. - /* - var text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_a) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*?\n // any number of lines, minimally matching - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm, - showdown.subParser('hashElement')(text, options, globals)); - - // - // Now match more liberally, simply from `\n` to `\n` - // - - /* - var text = text.replace(/ - ( // save in $1 - ^ // start of line (with /m) - <($block_tags_b) // start tag = $2 - \b // word break - // attacklab: hack around khtml/pcre bug... - [^\r]*? // any number of lines, minimally matching - // the matching end tag - [ \t]* // trailing spaces/tabs - (?=\n+) // followed by a newline - ) // attacklab: there are sentinel newlines at end of document - /gm,function(){...}}; - */ - text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|style|section|header|footer|nav|article|aside|address|audio|canvas|figure|hgroup|output|video)\b[^\r]*?<\/\2>[ \t]*(?=\n+)\n)/gm, - showdown.subParser('hashElement')(text, options, globals)); - - // Special case just for
. It was easier to make a special case than - // to make the other regex more complicated. - - /* - text = text.replace(/ - ( // save in $1 - \n\n // Starting after a blank line - [ ]{0,3} - (<(hr) // start tag = $2 - \b // word break - ([^<>])*? // - \/?>) // the matching end tag - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,showdown.subParser('hashElement')(text, options, globals)); - */ - 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(/ - ( // save in $1 - \n\n // Starting after a blank line - [ ]{0,3} // attacklab: g_tab_width - 1 - - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,showdown.subParser('hashElement')(text, options, globals)); - */ - text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g, - showdown.subParser('hashElement')(text, options, globals)); - - // PHP and ASP-style processor instructions ( and <%...%>) - - /* - text = text.replace(/ - (?: - \n\n // Starting after a blank line - ) - ( // save in $1 - [ ]{0,3} // attacklab: g_tab_width - 1 - (?: - <([?%]) // $2 - [^\r]*? - \2> - ) - [ \t]* - (?=\n{2,}) // followed by a blank line - ) - /g,showdown.subParser('hashElement')(text, options, globals)); - */ - text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, - showdown.subParser('hashElement')(text, options, globals)); - - // attacklab: Undo double lines (see comment at top of this function) - text = text.replace(/\n\n/g, '\n'); - return text; - -}); - -showdown.subParser('headers', function (text, options, globals) { - 'use strict'; - - 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; - } - - return text; -}); - -/** - * Turn Markdown image shortcuts into tags. - */ -showdown.subParser('images', function (text, options, globals) { - 'use strict'; - - 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); - - return text; -}); - -showdown.subParser('italicsAndBold', function (text, options) { - 'use strict'; - - if (options.literalMidWordUnderscores) { - //underscores - // Since we are consuming a \s character, we need to add it - text = text.replace(/(^|\s|>|\b)__(?=\S)([^]+?)__(?=\b|<|\s|$)/gm, '$1$2'); - text = text.replace(/(^|\s|>|\b)_(?=\S)([^]+?)_(?=\b|<|\s|$)/gm, '$1$2'); - //asterisks - text = text.replace(/\*\*(?=\S)([^]+?)\*\*/g, '$1'); - text = text.replace(/\*(?=\S)([^]+?)\*/g, '$1'); - - } else { - // must go first: - text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, '$2'); - text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, '$2'); - } - return text; -}); - -/** - * Form HTML ordered (numbered) and unordered (bulleted) lists. - */ -showdown.subParser('lists', function (text, options, globals) { - 'use strict'; - - /** - * 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| )?])?[ \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| )?]/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/, ''); - - 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'; - - // 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(\d+)K/g) >= 0) { - grafsOut.push(str); - } else if (str.search(/\S/) >= 0) { - 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++) { - // if this is a marker for an html block... - while (grafsOut[i].search(/~K(\d+)K/) >= 0) { - var blockText = globals.gHtmlBlocks[RegExp.$1]; - blockText = blockText.replace(/\$/g, '$$$$'); // Escape any dollar signs - grafsOut[i] = grafsOut[i].replace(/~K\d+K/, blockText); - } - } - - return grafsOut.join('\n\n'); -}); - -/** - * 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 = 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'); - - return text; - -}); - -showdown.subParser('strikethrough', function (text, options) { - 'use strict'; - - if (options.strikethrough) { - text = text.replace(/(?:~T){2}([^~]+)(?:~T){2}/g, '$1'); - } - - 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'; - - var table = function () { - - var tables = {}, - filter; - - tables.th = function (header, style) { - var id = ''; - header = header.trim(); - if (header === '') { - return ''; - } - if (options.tableHeaderId) { - id = ' id="' + header.replace(/ /g, '_').toLowerCase() + '"'; - } - header = showdown.subParser('spanGamut')(header, options, globals); - if (!style || style.trim() === '') { - style = ''; - } else { - style = ' style="' + style + '"'; - } - return '' + header + ''; - }; - - tables.td = function (cell, style) { - var subText = showdown.subParser('spanGamut')(cell.trim(), options, globals); - if (!style || style.trim() === '') { - style = ''; - } else { - style = ' style="' + style + '"'; - } - return '' + subText + ''; - }; - - tables.ths = function () { - var out = '', - i = 0, - hs = [].slice.apply(arguments[0]), - style = [].slice.apply(arguments[1]); - - for (i; i < hs.length; i += 1) { - out += tables.th(hs[i], style[i]) + '\n'; - } - - return out; - }; - - tables.tds = function () { - var out = '', - i = 0, - ds = [].slice.apply(arguments[0]), - style = [].slice.apply(arguments[1]); - - for (i; i < ds.length; i += 1) { - out += tables.td(ds[i], style[i]) + '\n'; - } - return out; - }; - - tables.thead = function () { - var out, - hs = [].slice.apply(arguments[0]), - style = [].slice.apply(arguments[1]); - - out = '\n'; - out += '\n'; - out += tables.ths.apply(this, [hs, style]); - out += '\n'; - out += '\n'; - return out; - }; - - tables.tr = function () { - var out, - cs = [].slice.apply(arguments[0]), - style = [].slice.apply(arguments[1]); - - out = '\n'; - out += tables.tds.apply(this, [cs, style]); - out += '\n'; - return out; - }; - - filter = function (text) { - var i = 0, - lines = text.split('\n'), - line, - hs, - out = []; - - for (i; i < lines.length; i += 1) { - line = lines[i]; - // looks like a table heading - if (line.trim().match(/^[|].*[|]$/)) { - line = line.trim(); - - var tbl = [], - align = lines[i + 1].trim(), - styles = [], - j = 0; - - if (align.match(/^[|][-=|: ]+[|]$/)) { - styles = align.substring(1, align.length - 1).split('|'); - for (j = 0; j < styles.length; ++j) { - styles[j] = styles[j].trim(); - if (styles[j].match(/^[:][-=| ]+[:]$/)) { - styles[j] = 'text-align:center;'; - - } else if (styles[j].match(/^[-=| ]+[:]$/)) { - styles[j] = 'text-align:right;'; - - } else if (styles[j].match(/^[:][-=| ]+$/)) { - styles[j] = 'text-align:left;'; - } else { - styles[j] = ''; - } - } - } - tbl.push(''); - hs = line.substring(1, line.length - 1).split('|'); - - if (styles.length === 0) { - for (j = 0; j < hs.length; ++j) { - styles.push('text-align:left'); - } - } - tbl.push(tables.thead.apply(this, [hs, styles])); - line = lines[++i]; - if (!line.trim().match(/^[|][-=|: ]+[|]$/)) { - // not a table rolling back - line = lines[--i]; - } else { - line = lines[++i]; - tbl.push(''); - while (line.trim().match(/^[|].*[|]$/)) { - line = line.trim(); - tbl.push(tables.tr.apply(this, [line.substring(1, line.length - 1).split('|'), styles])); - line = lines[++i]; - } - tbl.push(''); - tbl.push('
'); - // we are done with this table and we move along - out.push(tbl.join('\n')); - continue; - } - } - out.push(line); - } - return out.join('\n'); - }; - return {parse: filter}; - }; - - if (options.tables) { - var tableParser = table(); - return tableParser.parse(text); - } else { - 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; -}); - -var root = this; - -// CommonJS/nodeJS Loader -if (typeof module !== 'undefined' && module.exports) { - module.exports = showdown; - -// AMD Loader -} else if (typeof define === 'function' && define.amd) { - define('showdown', function () { - 'use strict'; - return showdown; - }); - -// Regular Browser loader -} else { - root.showdown = showdown; -} -}).call(this); -//# sourceMappingURL=showdown.js.map \ No newline at end of file diff --git a/public/index.php b/public/index.php index f819f62..8f69048 100644 --- a/public/index.php +++ b/public/index.php @@ -19,7 +19,7 @@ $template->setTemplate($templateName); if (isset($_GET['p'])) { // Set default variables $renderData['page'] = [ - 'content' => Utils::mdParse("# Unable to load the requested info page.\r\n\r\nCheck the URL and try again."), + 'content' => '

Unable to load the requested info page.

Check the URL and try again.

', ]; // Set page id @@ -31,7 +31,7 @@ if (isset($_GET['p'])) { $renderData['page'] = [ 'id' => $pageId, 'title' => $ipData['page_title'], - 'content' => Utils::mdParse($ipData['page_content']), + 'content' => $ipData['page_content'], ]; } @@ -52,6 +52,8 @@ $renderData['newsCount'] = Config::get('front_page_news_posts'); $renderData['forum'] = ($forumMode ? (new Forum\Forum()) : null); +$renderData['latestPosts'] = Database::fetch('posts', true, null, ['post_id', true], [3]); + $renderData['stats'] = [ 'userCount' => Database::count('users', ['password_algo' => ['nologin', '!='], 'rank_main' => ['1', '!=']])[0], 'newestUser' => ($_INDEX_NEWEST_USER = User::construct(Users::getNewestUserId())), diff --git a/public/members.php b/public/members.php index b138dac..fc03e5f 100644 --- a/public/members.php +++ b/public/members.php @@ -18,29 +18,17 @@ $template->setTemplate($templateName); // CHeck if the user is logged in if (Users::checkLogin()) { // Add page specific things - $renderData['page'] = [ - + $renderData['memberlist'] = [ 'ranks' => ($_MEMBERLIST_RANKS = Users::getAllRanks()), 'active' => ($_MEMBERLIST_ACTIVE = ( isset($_GET['rank']) && $_GET['rank'] && array_key_exists($_GET['rank'], $_MEMBERLIST_RANKS) ? $_GET['rank'] : 0 )), - 'notfound' => ($_MEMBERLIST_NFOUND = ( - isset($_GET['rank']) - && !array_key_exists($_GET['rank'], $_MEMBERLIST_RANKS) && $_GET['rank'] != 0 - )), - 'sorts' => ($_MEMBERLIST_SORTS = ['boxes', 'rectangles', 'list']), - 'sort' => isset($_GET['sort']) && $_GET['sort'] && in_array($_GET['sort'], $_MEMBERLIST_SORTS) ? - $_GET['sort'] : - $_MEMBERLIST_SORTS[0], - + 'users' => ($_MEMBERLIST_ACTIVE && !$_MEMBERLIST_NFOUND ? Users::getUsersInRank($_MEMBERLIST_ACTIVE) : Users::getAllUsers()), + 'membersPerPage' => Config::get('members_per_page'), ]; - $renderData['users'] = ($_MEMBERLIST_ACTIVE && !$_MEMBERLIST_NFOUND ? Users::getUsersInRank($_MEMBERLIST_ACTIVE) : Users::getAllUsers()); - - $renderData['membersPerPage'] = Config::get('members_per_page'); - // Set parse variables $template->setVariables($renderData); diff --git a/public/profile.php b/public/profile.php index 1c607f6..ba5c7cf 100644 --- a/public/profile.php +++ b/public/profile.php @@ -59,9 +59,10 @@ if (isset($_GET['restrict']) && $_GET['restrict'] == session_id() && $currentUse if ($restricted) { $profile->removeRanks([Config::get('restricted_rank_id')]); + $profile->addRanks([2]); } else { $profile->addRanks([Config::get('restricted_rank_id')]); - $profile->removeRanks($profile->ranks()); + $profile->removeRanks(array_keys($profile->ranks)); } $renderData['page'] = [ diff --git a/sakura.php b/sakura.php index 3aec30a..23e268b 100644 --- a/sakura.php +++ b/sakura.php @@ -8,7 +8,7 @@ namespace Sakura; // Define Sakura version -define('SAKURA_VERSION', '20160118'); +define('SAKURA_VERSION', '20160120'); define('SAKURA_VLABEL', 'Amethyst'); define('SAKURA_COLOUR', '#9966CC'); diff --git a/templates/misaki/profile/index.twig b/templates/misaki/profile/index.twig index 17b3290..2387986 100644 --- a/templates/misaki/profile/index.twig +++ b/templates/misaki/profile/index.twig @@ -1,3 +1,3 @@ -
+
{{ profile.userPage|raw }}
diff --git a/templates/yuuno/elements/indexPanel.twig b/templates/yuuno/elements/indexPanel.twig index 20d11b7..e073efa 100644 --- a/templates/yuuno/elements/indexPanel.twig +++ b/templates/yuuno/elements/indexPanel.twig @@ -38,11 +38,13 @@ {% else %} There were no online users in the past {{ sakura.onlineTimeout / 60 }} minute{% if sakura.onlineTimeout != 60 %}s{% endif %}. {% endif %} - {#
-
Advertisment
+ {% if not user.permission(constant('Sakura\\Perms\\Site::CHANGE_BACKGROUND')) or user.mainRankId == 4 %} + #} +
+ {% endif %}
diff --git a/templates/yuuno/elements/newsPost.twig b/templates/yuuno/elements/newsPost.twig index f2490ae..f9ea61a 100644 --- a/templates/yuuno/elements/newsPost.twig +++ b/templates/yuuno/elements/newsPost.twig @@ -6,8 +6,8 @@

{{ post.news_poster.username }}

-
- {{ post.news_content_parsed|raw }} +
+ {{ post.news_content|raw }}
diff --git a/templates/yuuno/global/master.twig b/templates/yuuno/global/master.twig index 48c5379..cbc6213 100644 --- a/templates/yuuno/global/master.twig +++ b/templates/yuuno/global/master.twig @@ -132,10 +132,6 @@ } {% endif %} - - {% if php.self == '/profile.php' ? (profile.background and not profile.optionFields.disableProfileParallax) : (user.permission(constant('Sakura\\Perms\\Site::CHANGE_BACKGROUND')) and user.optionFields.profileBackgroundSiteWide and user.background and not user.optionFields.disableProfileParallax) %} - initialiseParallax('userBackground'); - {% endif %} }); // Error reporter @@ -240,11 +236,15 @@

{{ php.self }} is now printing!

{% endblock %} - {# + {% endif %}