diff options
-rw-r--r-- | vnext/package.json | 1 | ||||
-rw-r--r-- | vnext/src/components/Message.js | 5 | ||||
-rw-r--r-- | vnext/src/utils/embed.js | 336 | ||||
-rw-r--r-- | vnext/yarn.lock | 183 |
4 files changed, 342 insertions, 183 deletions
diff --git a/vnext/package.json b/vnext/package.json index da21b308b..3eaec73f5 100644 --- a/vnext/package.json +++ b/vnext/package.json @@ -39,7 +39,6 @@ "query-string": "^6.1.0", "react": "^16.4.1", "react-dom": "^16.4.1", - "react-markdown": "^3.3.3", "react-router-dom": "^4.3.1", "whatwg-fetch": "^2.0.4" } diff --git a/vnext/src/components/Message.js b/vnext/src/components/Message.js index d1a9c51f0..53c2a033a 100644 --- a/vnext/src/components/Message.js +++ b/vnext/src/components/Message.js @@ -1,12 +1,13 @@ import React from 'react'; import PropTypes from 'prop-types'; -import ReactMarkdown from 'react-markdown'; import moment from 'moment'; import { UserType } from './Types'; import Icon from './Icon'; import Avatar from './Avatar'; +import { format } from '../utils/embed'; + export default function Message(props) { const msg = props.data; const visitor = props.visitor; @@ -29,7 +30,7 @@ export default function Message(props) { <Tags data={msg.tags || []} /> </div> </header> - <ReactMarkdown itemProp="description" source={msg.body} /> + <p itemProp="description" dangerouslySetInnerHTML={{ __html: format(msg.body, msg.mid, (msg.tags || []).indexOf('code') >= 0) }}></p> {msg.photo && <p className="ir"><a href={`//i.juick.com/p/${msg.mid}.${msg.attach}`} data-fname={`${msg.mid}.${msg.attach}`}> <img itemProp="image" src={`//i.juick.com/p/${msg.mid}.${msg.attach}`} alt="" /></a> diff --git a/vnext/src/utils/embed.js b/vnext/src/utils/embed.js new file mode 100644 index 000000000..25c371422 --- /dev/null +++ b/vnext/src/utils/embed.js @@ -0,0 +1,336 @@ + +function insertAfter(newNode, referenceNode) { + referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); +} + +function setContent(containerNode, ...newNodes) { + removeAllFrom(containerNode); + newNodes.forEach(n => containerNode.appendChild(n)); + return containerNode; +} + +function removeAllFrom(fromNode) { + for (let c; c = fromNode.lastChild; ) { fromNode.removeChild(c); } +} + +function htmlEscape(html) { + let textarea = document.createElement('textarea'); + textarea.textContent = html; + return textarea.innerHTML; +} + +// rules :: [{pr: number, re: RegExp, with: string}] +// rules :: [{pr: number, re: RegExp, with: Function}] +// rules :: [{pr: number, re: RegExp, brackets: true, with: [string, string]}] +// rules :: [{pr: number, re: RegExp, brackets: true, with: [string, string, Function]}] +function formatText(txt, rules) { + let idCounter = 0; + function nextId() { return idCounter++; } + function ft(txt, rules) { + let matches = rules.map(r => { r.re.lastIndex = 0; return [r, r.re.exec(txt)]; }) + .filter(([,m]) => m !== null) + .sort(([r1,m1],[r2,m2]) => (r1.pr - r2.pr) || (m1.index - m2.index)); + if (matches && matches.length > 0) { + let [rule, match] = matches[0]; + let subsequentRules = rules.filter(r => r.pr >= rule.pr); + let idStr = `<>(${nextId()})<>`; + let outerStr = txt.substring(0, match.index) + idStr + txt.substring(rule.re.lastIndex); + let innerStr = (rule.brackets) + ? (() => { let [l ,r ,f] = rule.with; return l + ft((f ? f(match[1]) : match[1]), subsequentRules) + r; })() + : match[0].replace(rule.re, rule.with); + return ft(outerStr, subsequentRules).replace(idStr, innerStr); + } + return txt; + } + return ft(htmlEscape(txt), rules); // idStr above relies on the fact the text is escaped +} + +function fixWwwLink(url) { + return url.replace(/^(?!([a-z]+:)?\/\/)/i, '//'); +} + +function makeNewNode(embedType, aNode, reResult) { + const withClasses = el => { + if (embedType.className) { + el.classList.add(...embedType.className.split(' ')); + } + return el; + }; + return embedType.makeNode(aNode, reResult, withClasses(document.createElement('div'))); +} + +function makeIframe(src, w, h, scrolling='no') { + let iframe = document.createElement('iframe'); + iframe.style.width = w; + iframe.style.height = h; + iframe.frameBorder = 0; + iframe.scrolling = scrolling; + iframe.setAttribute('allowFullScreen', ''); + iframe.src = src; + iframe.innerHTML = 'Cannot show iframes.'; + return iframe; +} + +function makeResizableToRatio(element, ratio) { + element.dataset['ratio'] = ratio; + makeResizable(element, w => w * element.dataset['ratio']); +} + +// calcHeight :: Number -> Number -- calculate element height for a given width +function makeResizable(element, calcHeight) { + const setHeight = el => { + if (document.body.contains(el) && (el.offsetWidth > 0)) { + el.style.height = (calcHeight(el.offsetWidth)).toFixed(2) + 'px'; + } + }; + window.addEventListener('resize', () => setHeight(element)); + setHeight(element); +} + +function extractDomain(url) { + const domainRe = /^(?:https?:\/\/)?(?:[^@\/\n]+@)?(?:www\.)?([^:\/\n]+)/i; + return domainRe.exec(url)[1]; +} + +function urlReplace(match, p1, p2, p3) { + let isBrackets = (p1 !== undefined); + return (isBrackets) + ? `<a href="${fixWwwLink(p2 || p3)}">${p1}</a>` + : `<a href="${fixWwwLink(match)}">${extractDomain(match)}</a>`; +} + +function urlReplaceInCode(match, p1, p2, p3) { + let isBrackets = (p1 !== undefined); + return (isBrackets) + ? `<a href="${fixWwwLink(p2 || p3)}">${match}</a>` + : `<a href="${fixWwwLink(match)}">${match}</a>`; +} + +function messageReplyReplace(messageId) { + return function(match, mid, rid) { + let replyPart = (rid && rid != '0') ? '#' + rid : ''; + return `<a href="/${mid || messageId}${replyPart}">${match}</a>`; + }; +} + +/** + * Given "txt" message in unescaped plaintext with Juick markup, this function + * returns escaped formatted HTML string. + * + * @param {string} txt + * @param {string} messageId - current message id + * @param {boolean} isCode + * @returns {string} + */ +function juickFormat(txt, messageId, isCode) { + const urlRe = /(?:\[([^\]\[]+)\](?:\[([^\]]+)\]|\(((?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[-\w+*&@#/%=~|$?!:;,.])*(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[\w+*&@#/%=~|$]))\))|\b(?:[a-z]+:\/\/|www\.|ftp\.)(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[-\w+*&@#/%=~|$?!:;,.])*(?:\([-\w+*&@#/%=~|$?!:;,.]*\)|[\w+*&@#/%=~|$]))/gi; + const bqReplace = m => m.replace(/^(?:>|>)\s?/gmi, ''); + return (isCode) + ? formatText(txt, [ + { pr: 1, re: urlRe, with: urlReplaceInCode }, + { pr: 1, re: /\B(?:#(\d+))?(?:\/(\d+))?\b/g, with: messageReplyReplace(messageId) }, + { pr: 1, re: /\B@([\w-]+)\b/gi, with: '<a href="/$1">@$1</a>' }, + ]) + : formatText(txt, [ + { pr: 0, re: /((?:^(?:>|>)\s?[\s\S]+?$\n?)+)/gmi, brackets: true, with: ['<q>', '</q>', bqReplace] }, + { pr: 1, re: urlRe, with: urlReplace }, + { pr: 1, re: /\B(?:#(\d+))?(?:\/(\d+))?\b/g, with: messageReplyReplace(messageId) }, + { pr: 1, re: /\B@([\w-]+)\b/gi, with: '<a href="/$1">@$1</a>' }, + { pr: 2, re: /\B\*([^\n]+?)\*((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<b>', '</b>'] }, + { pr: 2, re: /\B\/([^\n]+?)\/((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<i>', '</i>'] }, + { pr: 2, re: /\b\_([^\n]+?)\_((?=\s)|(?=$)|(?=[!\"#$%&'*+,\-./:;<=>?@[\]^_`{|}~()]+))/g, brackets: true, with: ['<span class="u">', '</span>'] }, + { pr: 3, re: /\n/g, with: '<br/>' }, + ]); +} + +function getEmbeddableLinkTypes() { + return [ + { + name: 'Jpeg and png images', + id: 'embed_jpeg_and_png_images', + className: 'picture compact', + ctsDefault: false, + re: /\.(jpe?g|png|svg)(:[a-zA-Z]+)?(?:\?[\w&;\?=]*)?$/i, + makeNode: function(aNode, reResult, div) { + div.innerHTML = `<a href="${aNode.href}"><img src="${aNode.href}"></a>`; + return div; + } + }, + { + name: 'Gif images', + id: 'embed_gif_images', + className: 'picture compact', + ctsDefault: true, + re: /\.gif(:[a-zA-Z]+)?(?:\?[\w&;\?=]*)?$/i, + makeNode: function(aNode, reResult, div) { + div.innerHTML = `<a href="${aNode.href}"><img src="${aNode.href}"></a>`; + return div; + } + }, + { + name: 'Video (webm, mp4, ogv)', + id: 'embed_webm_and_mp4_videos', + className: 'video compact', + ctsDefault: false, + re: /\.(webm|mp4|m4v|ogv)(?:\?[\w&;\?=]*)?$/i, + makeNode: function(aNode, reResult, div) { + div.innerHTML = `<video src="${aNode.href}" title="${aNode.href}" controls></video>`; + return div; + } + }, + { + name: 'Audio (mp3, ogg, weba, opus, m4a, oga, wav)', + id: 'embed_sound_files', + className: 'audio singleColumn', + ctsDefault: false, + re: /\.(mp3|ogg|weba|opus|m4a|oga|wav)(?:\?[\w&;\?=]*)?$/i, + makeNode: function(aNode, reResult, div) { + div.innerHTML = `<audio src="${aNode.href}" title="${aNode.href}" controls></audio>`; + return div; + } + }, + { + name: 'YouTube videos (and playlists)', + id: 'embed_youtube_videos', + className: 'youtube resizableV singleColumn', + ctsDefault: false, + re: /^(?:https?:)?\/\/(?:www\.|m\.|gaming\.)?(?:youtu(?:(?:\.be\/|be\.com\/(?:v|embed)\/)([-\w]+)|be\.com\/watch)((?:(?:\?|&(?:amp;)?)(?:\w+=[-\.\w]*[-\w]))*)|youtube\.com\/playlist\?list=([-\w]*)(&(amp;)?[-\w\?=]*)?)/i, + makeNode: function(aNode, reResult, div) { + let [url, v, args, plist] = reResult; + let iframeUrl; + if (plist) { + iframeUrl = '//www.youtube-nocookie.com/embed/videoseries?list=' + plist; + } else { + let pp = {}; args.replace(/^\?/, '') + .split('&') + .map(s => s.split('=')) + .forEach(z => pp[z[0]] = z[1]); + let embedArgs = { rel: '0' }; + if (pp.t) { + const tre = /^(?:(\d+)|(?:(\d+)h)?(?:(\d+)m)?(\d+)s|(?:(\d+)h)?(\d+)m|(\d+)h)$/i; + let [, t, h, m, s, h1, m1, h2] = tre.exec(pp.t); + embedArgs['start'] = (+t) || ((+(h || h1 || h2 || 0))*60*60 + (+(m || m1 || 0))*60 + (+(s || 0))); + } + if (pp.list) { + embedArgs['list'] = pp.list; + } + v = v || pp.v; + let argsStr = Object.keys(embedArgs) + .map(k => `${k}=${embedArgs[k]}`) + .join('&'); + iframeUrl = `//www.youtube-nocookie.com/embed/${v}?${argsStr}`; + } + let iframe = makeIframe(iframeUrl, '100%', '360px'); + iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0); + return setContent(div, iframe); + } + }, + { + name: 'Vimeo videos', + id: 'embed_vimeo_videos', + className: 'vimeo resizableV', + ctsDefault: false, + re: /^(?:https?:)?\/\/(?:www\.)?(?:player\.)?vimeo\.com\/(?:video\/|album\/[\d]+\/video\/)?([\d]+)/i, + makeNode: function(aNode, reResult, div) { + let iframe = makeIframe('//player.vimeo.com/video/' + reResult[1], '100%', '360px'); + iframe.onload = () => makeResizableToRatio(iframe, 9.0 / 16.0); + return setContent(div, iframe); + } + } + ]; +} + +function embedLink(aNode, linkTypes, container, afterNode) { + let anyEmbed = false; + let linkId = (aNode.href.replace(/^https?:/i, '').replace(/\'/gi,'')); + let sameEmbed = container.querySelector(`*[data-linkid='${linkId}']`); // do not embed the same thing twice + if (sameEmbed === null) { + anyEmbed = [].some.call(linkTypes, function(linkType) { + let reResult = linkType.re.exec(aNode.href); + if (reResult) { + if (linkType.match && (linkType.match(aNode, reResult) === false)) { return false; } + let newNode = makeNewNode(linkType, aNode, reResult); + if (!newNode) { return false; } + newNode.setAttribute('data-linkid', linkId); + if (afterNode) { + insertAfter(newNode, afterNode); + } else { + container.appendChild(newNode); + } + aNode.classList.add('embedLink'); + return true; + } + }); + } + return anyEmbed; +} + +function embedLinks(aNodes, container) { + let anyEmbed = false; + let embeddableLinkTypes = getEmbeddableLinkTypes(); + Array.from(aNodes).forEach(aNode => { + let isEmbedded = embedLink(aNode, embeddableLinkTypes, container); + anyEmbed = anyEmbed || isEmbedded; + }); + return anyEmbed; +} + +/** + * Embed all the links inside element "x" that match to "allLinksSelector". + * All the embedded media is placed inside "div.embedContainer". + * "div.embedContainer" is inserted before an element matched by "beforeNodeSelector" + * if not present. Existing container is used otherwise. + * + * @param {Element} x + * @param {string} beforeNodeSelector + * @param {string} allLinksSelector + */ +function embedLinksToX(x, beforeNodeSelector, allLinksSelector) { + let isCtsPost = false; + let allLinks = x.querySelectorAll(allLinksSelector); + + let existingContainer = x.querySelector('div.embedContainer'); + if (existingContainer) { + embedLinks(allLinks, existingContainer); + } else { + let embedContainer = document.createElement('div'); + embedContainer.className = 'embedContainer'; + + let anyEmbed = embedLinks(allLinks, embedContainer); + if (anyEmbed) { + let beforeNode = x.querySelector(beforeNodeSelector); + x.insertBefore(embedContainer, beforeNode); + } + } +} + +function embedLinksToArticles() { + let beforeNodeSelector = 'nav.l'; + let allLinksSelector = 'p:not(.ir) a, pre a'; + Array.from(document.querySelectorAll('#content article')).forEach(article => { + embedLinksToX(article, beforeNodeSelector, allLinksSelector); + }); +} + +function embedLinksToPost() { + let beforeNodeSelector = '.msg-txt + *'; + let allLinksSelector = '.msg-txt a'; + Array.from(document.querySelectorAll('#content .msg-cont')).forEach(msg => { + embedLinksToX(msg, beforeNodeSelector, allLinksSelector); + }); +} + +/** + * Embed all the links in all messages/replies on the page. + */ +function embedAll() { + if (document.querySelector('#content article[data-mid]')) { + embedLinksToArticles(); + } else { + embedLinksToPost(); + } +} + +exports.embedAll = embedAll; +exports.embedLinksToX = embedLinksToX; +exports.format = juickFormat; diff --git a/vnext/yarn.lock b/vnext/yarn.lock index 6081581d5..5e77fbe77 100644 --- a/vnext/yarn.lock +++ b/vnext/yarn.lock @@ -883,10 +883,6 @@ babylon@^6.18.0: version "6.18.0" resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" -bail@^1.0.0: - version "1.0.3" - resolved "https://registry.yarnpkg.com/bail/-/bail-1.0.3.tgz#63cfb9ddbac829b02a3128cd53224be78e6c21a3" - balanced-match@^0.4.2: version "0.4.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" @@ -1142,18 +1138,6 @@ chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.0, chalk@^2.3.1, chalk@^2.3.2, chalk@^2.4 escape-string-regexp "^1.0.5" supports-color "^5.3.0" -character-entities-legacy@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/character-entities-legacy/-/character-entities-legacy-1.1.2.tgz#7c6defb81648498222c9855309953d05f4d63a9c" - -character-entities@^1.0.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/character-entities/-/character-entities-1.2.2.tgz#58c8f371c0774ef0ba9b2aca5f00d8f100e6e363" - -character-reference-invalid@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/character-reference-invalid/-/character-reference-invalid-1.1.2.tgz#21e421ad3d84055952dab4a43a04e73cd425d3ed" - chokidar@^2.0.2: version "2.0.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.0.3.tgz#dcbd4f6cbb2a55b4799ba8a840ac527e5f4b1176" @@ -1247,10 +1231,6 @@ code-point-at@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" -collapse-white-space@^1.0.2: - version "1.0.4" - resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.4.tgz#ce05cf49e54c3277ae573036a26851ba430a0091" - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -1939,10 +1919,6 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" - extglob@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/extglob/-/extglob-2.0.4.tgz#ad00fe4dc612a9232e8718711dc5cb5ab0285543" @@ -2392,17 +2368,6 @@ is-accessor-descriptor@^1.0.0: dependencies: kind-of "^6.0.0" -is-alphabetical@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-alphabetical/-/is-alphabetical-1.0.2.tgz#1fa6e49213cb7885b75d15862fb3f3d96c884f41" - -is-alphanumerical@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-alphanumerical/-/is-alphanumerical-1.0.2.tgz#1138e9ae5040158dc6ff76b820acd6b7a181fd40" - dependencies: - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -2413,7 +2378,7 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" -is-buffer@^1.1.4, is-buffer@^1.1.5: +is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -2449,10 +2414,6 @@ is-date-object@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.1.tgz#9aa20eb6aeebbff77fbd33e74ca01b33581d3a16" -is-decimal@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-decimal/-/is-decimal-1.0.2.tgz#894662d6a8709d307f3a276ca4339c8fa5dff0ff" - is-descriptor@^0.1.0: version "0.1.6" resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca" @@ -2519,10 +2480,6 @@ is-glob@^4.0.0: dependencies: is-extglob "^2.1.1" -is-hexadecimal@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-hexadecimal/-/is-hexadecimal-1.0.2.tgz#b6e710d7d07bb66b98cb8cece5c9b4921deeb835" - is-installed-globally@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/is-installed-globally/-/is-installed-globally-0.1.0.tgz#0dfd98f5a9111716dd535dda6492f67bf3d25a80" @@ -2598,18 +2555,10 @@ is-symbol@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.1.tgz#3cc59f00025194b6ab2e38dbae6689256b660572" -is-whitespace-character@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-whitespace-character/-/is-whitespace-character-1.0.2.tgz#ede53b4c6f6fb3874533751ec9280d01928d03ed" - is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" -is-word-character@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/is-word-character/-/is-word-character-1.0.2.tgz#46a5dac3f2a1840898b91e576cd40d493f3ae553" - is-wsl@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-wsl/-/is-wsl-1.1.0.tgz#1f16e4aa22b04d1336b66188a66af3c600c3a66d" @@ -2911,10 +2860,6 @@ map-visit@^1.0.0: dependencies: object-visit "^1.0.0" -markdown-escapes@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/markdown-escapes/-/markdown-escapes-1.0.2.tgz#e639cbde7b99c841c0bacc8a07982873b46d2122" - math-expression-evaluator@^1.2.14: version "1.2.17" resolved "https://registry.yarnpkg.com/math-expression-evaluator/-/math-expression-evaluator-1.2.17.tgz#de819fdbcd84dccd8fae59c6aeb79615b9d266ac" @@ -3451,17 +3396,6 @@ parse-asn1@^5.0.0: evp_bytestokey "^1.0.0" pbkdf2 "^3.0.3" -parse-entities@^1.1.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/parse-entities/-/parse-entities-1.1.2.tgz#9eaf719b29dc3bd62246b4332009072e01527777" - dependencies: - character-entities "^1.0.0" - character-entities-legacy "^1.0.0" - character-reference-invalid "^1.0.0" - is-alphanumerical "^1.0.0" - is-decimal "^1.0.0" - is-hexadecimal "^1.0.0" - parse-json@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/parse-json/-/parse-json-2.2.0.tgz#f480f40434ef80741f8469099f8dea18f55a4dc9" @@ -3978,16 +3912,6 @@ react-dom@^16.4.1: object-assign "^4.1.1" prop-types "^15.6.0" -react-markdown@^3.3.3: - version "3.3.3" - resolved "https://registry.yarnpkg.com/react-markdown/-/react-markdown-3.3.3.tgz#1e57e63e9f4e87d2919a51d77815b803f4b7b5d6" - dependencies: - prop-types "^15.6.1" - remark-parse "^5.0.0" - unified "^6.1.5" - unist-util-visit "^1.3.0" - xtend "^4.0.1" - react-router-dom@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/react-router-dom/-/react-router-dom-4.3.1.tgz#4c2619fc24c4fa87c9fd18f4fb4a43fe63fbd5c6" @@ -4139,26 +4063,6 @@ regjsparser@^0.1.4: dependencies: jsesc "~0.5.0" -remark-parse@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/remark-parse/-/remark-parse-5.0.0.tgz#4c077f9e499044d1d5c13f80d7a98cf7b9285d95" - dependencies: - collapse-white-space "^1.0.2" - is-alphabetical "^1.0.0" - is-decimal "^1.0.0" - is-whitespace-character "^1.0.0" - is-word-character "^1.0.0" - markdown-escapes "^1.0.0" - parse-entities "^1.1.0" - repeat-string "^1.5.4" - state-toggle "^1.0.0" - trim "0.0.1" - trim-trailing-lines "^1.0.0" - unherit "^1.0.4" - unist-util-remove-position "^1.0.0" - vfile-location "^2.0.0" - xtend "^4.0.1" - remove-array-items@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/remove-array-items/-/remove-array-items-1.0.0.tgz#07bf42cb332f4cf6e85ead83b5e4e896d2326b21" @@ -4171,7 +4075,7 @@ repeat-element@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" -repeat-string@^1.5.4, repeat-string@^1.6.1: +repeat-string@^1.6.1: version "1.6.1" resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" @@ -4181,10 +4085,6 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -replace-ext@1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.0.tgz#de63128373fcbf7c3ccfa4de5a480c45a67958eb" - require-from-string@^1.1.0: version "1.2.1" resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-1.2.1.tgz#529c9ccef27380adfec9a2f965b649bbee636418" @@ -4451,10 +4351,6 @@ ssri@^5.2.4: dependencies: safe-buffer "^5.1.1" -state-toggle@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/state-toggle/-/state-toggle-1.0.1.tgz#c3cb0974f40a6a0f8e905b96789eb41afa1cde3a" - static-extend@^0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/static-extend/-/static-extend-0.1.2.tgz#60809c39cbff55337226fd5e0b520f341f1fb5c6" @@ -4687,18 +4583,6 @@ trim-right@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/trim-right/-/trim-right-1.0.1.tgz#cb2e1203067e0c8de1f614094b9fe45704ea6003" -trim-trailing-lines@^1.0.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/trim-trailing-lines/-/trim-trailing-lines-1.1.1.tgz#e0ec0810fd3c3f1730516b45f49083caaf2774d9" - -trim@0.0.1: - version "0.0.1" - resolved "https://registry.yarnpkg.com/trim/-/trim-0.0.1.tgz#5858547f6b290757ee95cccc666fb50084c460dd" - -trough@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.2.tgz#7f1663ec55c480139e2de5e486c6aef6cc24a535" - tslib@^1.9.0: version "1.9.2" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.9.2.tgz#8be0cc9a1f6dc7727c38deb16c2ebd1a2892988e" @@ -4742,24 +4626,6 @@ uglifyjs-webpack-plugin@^1.2.4: webpack-sources "^1.1.0" worker-farm "^1.5.2" -unherit@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/unherit/-/unherit-1.1.1.tgz#132748da3e88eab767e08fabfbb89c5e9d28628c" - dependencies: - inherits "^2.0.1" - xtend "^4.0.1" - -unified@^6.1.5: - version "6.2.0" - resolved "https://registry.yarnpkg.com/unified/-/unified-6.2.0.tgz#7fbd630f719126d67d40c644b7e3f617035f6dba" - dependencies: - bail "^1.0.0" - extend "^3.0.0" - is-plain-obj "^1.1.0" - trough "^1.0.0" - vfile "^2.0.0" - x-is-string "^0.1.0" - union-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.0.tgz#5c71c34cb5bad5dcebe3ea0cd08207ba5aa1aea4" @@ -4795,26 +4661,6 @@ unique-string@^1.0.0: dependencies: crypto-random-string "^1.0.0" -unist-util-is@^2.1.1: - version "2.1.2" - resolved "https://registry.yarnpkg.com/unist-util-is/-/unist-util-is-2.1.2.tgz#1193fa8f2bfbbb82150633f3a8d2eb9a1c1d55db" - -unist-util-remove-position@^1.0.0: - version "1.1.2" - resolved "https://registry.yarnpkg.com/unist-util-remove-position/-/unist-util-remove-position-1.1.2.tgz#86b5dad104d0bbfbeb1db5f5c92f3570575c12cb" - dependencies: - unist-util-visit "^1.1.0" - -unist-util-stringify-position@^1.0.0, unist-util-stringify-position@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/unist-util-stringify-position/-/unist-util-stringify-position-1.1.2.tgz#3f37fcf351279dcbca7480ab5889bb8a832ee1c6" - -unist-util-visit@^1.1.0, unist-util-visit@^1.3.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/unist-util-visit/-/unist-util-visit-1.3.1.tgz#c019ac9337a62486be58531bc27e7499ae7d55c7" - dependencies: - unist-util-is "^2.1.1" - unset-value@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/unset-value/-/unset-value-1.0.0.tgz#8376873f7d2335179ffb1e6fc3a8ed0dfc8ab559" @@ -4919,25 +4765,6 @@ vendors@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/vendors/-/vendors-1.0.2.tgz#7fcb5eef9f5623b156bcea89ec37d63676f21801" -vfile-location@^2.0.0: - version "2.0.3" - resolved "https://registry.yarnpkg.com/vfile-location/-/vfile-location-2.0.3.tgz#083ba80e50968e8d420be49dd1ea9a992131df77" - -vfile-message@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/vfile-message/-/vfile-message-1.0.1.tgz#51a2ccd8a6b97a7980bb34efb9ebde9632e93677" - dependencies: - unist-util-stringify-position "^1.1.1" - -vfile@^2.0.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/vfile/-/vfile-2.3.0.tgz#e62d8e72b20e83c324bc6c67278ee272488bf84a" - dependencies: - is-buffer "^1.1.4" - replace-ext "1.0.0" - unist-util-stringify-position "^1.0.0" - vfile-message "^1.0.0" - vm-browserify@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-0.0.4.tgz#5d7ea45bbef9e4a6ff65f95438e0a87c357d5a73" @@ -5155,10 +4982,6 @@ ws@^4.0.0: async-limiter "~1.0.0" safe-buffer "~5.1.0" -x-is-string@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/x-is-string/-/x-is-string-0.1.0.tgz#474b50865af3a49a9c4657f05acd145458f77d82" - xdg-basedir@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4" @@ -5167,7 +4990,7 @@ xregexp@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/xregexp/-/xregexp-4.0.0.tgz#e698189de49dd2a18cc5687b05e17c8e43943020" -xtend@^4.0.0, xtend@^4.0.1, xtend@~4.0.1: +xtend@^4.0.0, xtend@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" |