From 4cfddb60647560d6e8846f7476fae53e802ca55e Mon Sep 17 00:00:00 2001 From: ue Date: Tue, 13 Nov 2018 20:54:32 +0100 Subject: [PATCH] created markdown2hmtl utilities --- package-lock.json | 266 +++++++++++++++----------- package.json | 1 + src/utils/markdown2Html.js | 355 +++++++++++++++++++++++++++++++++++ src/utils/postParser.js | 69 +++---- src/utils/proxifyImageSrc.js | 11 ++ 5 files changed, 555 insertions(+), 147 deletions(-) create mode 100644 src/utils/markdown2Html.js create mode 100644 src/utils/proxifyImageSrc.js diff --git a/package-lock.json b/package-lock.json index b80a43792..74a4377c6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -882,8 +882,7 @@ "abab": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", - "dev": true + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" }, "abbrev": { "version": "1.1.1", @@ -914,17 +913,15 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz", "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==", - "dev": true, "requires": { "acorn": "^6.0.1", "acorn-walk": "^6.0.1" }, "dependencies": { "acorn": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.2.tgz", - "integrity": "sha512-GXmKIvbrN3TV7aVqAzVFaMW8F8wzVX7voEBRO3bDA64+EX37YSayggRJP5Xig6HYHBkWKpFg9W5gg6orklubhg==", - "dev": true + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz", + "integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==" } } }, @@ -938,10 +935,9 @@ } }, "acorn-walk": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.0.tgz", - "integrity": "sha512-ugTb7Lq7u4GfWSqqpwE0bGyoBZNMTok/zDBXxfEG0QM50jNlGhIWjRC1pPN7bvV1anhF+bs+/gNcRw+o55Evbg==", - "dev": true + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" }, "ajv": { "version": "5.5.2", @@ -1371,9 +1367,8 @@ }, "array-equal": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", - "dev": true + "resolved": "http://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" }, "array-filter": { "version": "0.0.1", @@ -2296,8 +2291,7 @@ "browser-process-hrtime": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", - "dev": true + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==" }, "browser-resolve": { "version": "1.11.3", @@ -2892,14 +2886,12 @@ "cssom": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", - "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", - "dev": true + "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==" }, "cssstyle": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz", "integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==", - "dev": true, "requires": { "cssom": "0.3.x" } @@ -2924,27 +2916,13 @@ } }, "data-urls": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.0.1.tgz", - "integrity": "sha512-0HdcMZzK6ubMUnsMmQmG0AcLQPvbvb47R0+7CCZQCYgcd8OUWG91CG7sM6GoXgjz+WLl4ArFzHtBMy/QqSF4eg==", - "dev": true, + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", + "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", "requires": { "abab": "^2.0.0", - "whatwg-mimetype": "^2.1.0", + "whatwg-mimetype": "^2.2.0", "whatwg-url": "^7.0.0" - }, - "dependencies": { - "whatwg-url": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", - "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", - "dev": true, - "requires": { - "lodash.sortby": "^4.7.0", - "tr46": "^1.0.1", - "webidl-conversions": "^4.0.2" - } - } } }, "date-fns": { @@ -3072,8 +3050,7 @@ "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", - "dev": true + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" }, "deepmerge": { "version": "2.1.0", @@ -3265,7 +3242,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", - "dev": true, "requires": { "webidl-conversions": "^4.0.2" } @@ -3467,7 +3443,6 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", - "dev": true, "requires": { "esprima": "^3.1.3", "estraverse": "^4.2.0", @@ -3479,14 +3454,12 @@ "esprima": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", - "dev": true + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", - "dev": true, "optional": true } } @@ -3884,8 +3857,7 @@ "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", - "dev": true + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" }, "esutils": { "version": "2.0.2", @@ -4055,8 +4027,7 @@ "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" }, "fb-watchman": { "version": "2.0.0", @@ -5171,7 +5142,6 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", - "dev": true, "requires": { "whatwg-encoding": "^1.0.1" } @@ -6200,6 +6170,74 @@ "jest-mock": "^23.2.0", "jest-util": "^23.4.0", "jsdom": "^11.5.1" + }, + "dependencies": { + "jsdom": { + "version": "11.12.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", + "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^5.5.3", + "acorn-globals": "^4.1.0", + "array-equal": "^1.0.0", + "cssom": ">= 0.3.2 < 0.4.0", + "cssstyle": "^1.0.0", + "data-urls": "^1.0.0", + "domexception": "^1.0.1", + "escodegen": "^1.9.1", + "html-encoding-sniffer": "^1.0.2", + "left-pad": "^1.3.0", + "nwsapi": "^2.0.7", + "parse5": "4.0.0", + "pn": "^1.1.0", + "request": "^2.87.0", + "request-promise-native": "^1.0.5", + "sax": "^1.2.4", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.3.4", + "w3c-hr-time": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.3", + "whatwg-mimetype": "^2.1.0", + "whatwg-url": "^6.4.1", + "ws": "^5.2.0", + "xml-name-validator": "^3.0.0" + } + }, + "parse5": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true + }, + "sax": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "whatwg-url": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", + "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + } } }, "jest-environment-node": { @@ -6963,50 +7001,47 @@ "optional": true }, "jsdom": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", - "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", - "dev": true, + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.0.0.tgz", + "integrity": "sha512-Kmq4ASMNkgpY+YufE322EnIKoiz0UWY2DRkKlU7d5YrIW4xiVRhWFrZV1fr6w/ZNxQ50wGAH5gGRzydgnmkkvw==", "requires": { "abab": "^2.0.0", - "acorn": "^5.5.3", - "acorn-globals": "^4.1.0", + "acorn": "^6.0.2", + "acorn-globals": "^4.3.0", "array-equal": "^1.0.0", - "cssom": ">= 0.3.2 < 0.4.0", - "cssstyle": "^1.0.0", - "data-urls": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.0.1", "domexception": "^1.0.1", - "escodegen": "^1.9.1", + "escodegen": "^1.11.0", "html-encoding-sniffer": "^1.0.2", - "left-pad": "^1.3.0", - "nwsapi": "^2.0.7", - "parse5": "4.0.0", + "nwsapi": "^2.0.9", + "parse5": "5.1.0", "pn": "^1.1.0", - "request": "^2.87.0", + "request": "^2.88.0", "request-promise-native": "^1.0.5", - "sax": "^1.2.4", + "saxes": "^3.1.3", "symbol-tree": "^3.2.2", - "tough-cookie": "^2.3.4", + "tough-cookie": "^2.4.3", "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.0.0", "webidl-conversions": "^4.0.2", - "whatwg-encoding": "^1.0.3", - "whatwg-mimetype": "^2.1.0", - "whatwg-url": "^6.4.1", - "ws": "^5.2.0", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.2.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.0", "xml-name-validator": "^3.0.0" }, "dependencies": { - "sax": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", - "dev": true + "acorn": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.4.tgz", + "integrity": "sha512-VY4i5EKSKkofY2I+6QLTbTTN/UvEQPCo6eiwzzSaSWfpaDhOmStMCMod6wmuPciNq+XS0faCglFu2lHZpdHUtg==" }, "ws": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", - "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", - "dev": true, + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.1.0.tgz", + "integrity": "sha512-H3dGVdGvW2H8bnYpIDc3u3LH8Wue3Qh+Zto6aXXFzvESkTVT6rAfKR6tR/+coaUvxs8yHtmNV0uioBF62ZGSTg==", "requires": { "async-limiter": "~1.0.0" } @@ -7140,7 +7175,6 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", - "dev": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -7770,8 +7804,7 @@ "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", - "dev": true + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" }, "lodash.throttle": { "version": "4.1.1", @@ -8916,8 +8949,7 @@ "nwsapi": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.9.tgz", - "integrity": "sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ==", - "dev": true + "integrity": "sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ==" }, "oauth-sign": { "version": "0.9.0", @@ -9103,7 +9135,6 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", - "dev": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.4", @@ -9199,10 +9230,9 @@ } }, "parse5": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", - "dev": true + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==" }, "parseurl": { "version": "1.3.2", @@ -9352,8 +9382,7 @@ "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", - "dev": true + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" }, "posix-character-classes": { "version": "0.1.1", @@ -9363,8 +9392,7 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", - "dev": true + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" }, "preserve": { "version": "0.2.0", @@ -10552,7 +10580,6 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", - "dev": true, "requires": { "lodash": "^4.13.1" } @@ -10561,7 +10588,6 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", - "dev": true, "requires": { "request-promise-core": "1.1.1", "stealthy-require": "^1.1.0", @@ -11017,6 +11043,14 @@ "resolved": "http://registry.npmjs.org/sax/-/sax-1.1.6.tgz", "integrity": "sha1-XWFr6KXmB9VOEUr65Vt+ry/MMkA=" }, + "saxes": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.3.tgz", + "integrity": "sha512-Nc5DXc5A+m3rUDtkS+vHlBWKT7mCKjJPyia7f8YMW773hsXVv2wEHQZGE0zs4+5PLwz9U5Sbl/94Cnd9vHV7Bg==", + "requires": { + "xmlchars": "^1.3.1" + } + }, "scheduler": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.10.0.tgz", @@ -11527,8 +11561,7 @@ "stealthy-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", - "dev": true + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" }, "steemconnect": { "version": "2.0.1", @@ -11687,8 +11720,7 @@ "symbol-tree": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", - "dev": true + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=" }, "sync-request": { "version": "3.0.1", @@ -12088,7 +12120,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", - "dev": true, "requires": { "punycode": "^2.1.0" }, @@ -12096,8 +12127,7 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" } } }, @@ -12135,7 +12165,6 @@ "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", - "dev": true, "requires": { "prelude-ls": "~1.1.2" } @@ -12511,11 +12540,20 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", - "dev": true, "requires": { "browser-process-hrtime": "^0.1.2" } }, + "w3c-xmlserializer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.0.0.tgz", + "integrity": "sha512-0et1+9uXYiIRAecx1D5Z1nk60+vimniGdIKl4XjeqkWi6acoHNlXMv1VR5jV+jF4ooeO08oWbYxeAJOcon1oMA==", + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", @@ -12536,14 +12574,12 @@ "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", - "dev": true + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" }, "whatwg-encoding": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", - "dev": true, "requires": { "iconv-lite": "0.4.24" } @@ -12556,14 +12592,12 @@ "whatwg-mimetype": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.2.0.tgz", - "integrity": "sha512-5YSO1nMd5D1hY3WzAQV3PzZL83W3YeyR1yW9PcH26Weh1t+Vzh9B6XkDh7aXm83HBZ4nSMvkjvN2H2ySWIvBgw==", - "dev": true + "integrity": "sha512-5YSO1nMd5D1hY3WzAQV3PzZL83W3YeyR1yW9PcH26Weh1t+Vzh9B6XkDh7aXm83HBZ4nSMvkjvN2H2ySWIvBgw==" }, "whatwg-url": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", - "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", - "dev": true, + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", "requires": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", @@ -12663,14 +12697,18 @@ "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", - "dev": true + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" }, "xmlbuilder": { "version": "9.0.7", "resolved": "http://registry.npmjs.org/xmlbuilder/-/xmlbuilder-9.0.7.tgz", "integrity": "sha1-Ey7mPS7FVlxVfiD0wi35rKaGsQ0=" }, + "xmlchars": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-1.3.1.tgz", + "integrity": "sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==" + }, "xmldoc": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/xmldoc/-/xmldoc-0.4.0.tgz", diff --git a/package.json b/package.json index 8a30e2071..583faeab8 100644 --- a/package.json +++ b/package.json @@ -26,6 +26,7 @@ "axios": "^0.18.0", "crypto-js": "^3.1.9-1", "dsteem": "^0.10.1", + "jsdom": "^13.0.0", "lodash": "^4.17.11", "moment": "^2.22.2", "native-base": "^2.8.1", diff --git a/src/utils/markdown2Html.js b/src/utils/markdown2Html.js new file mode 100644 index 000000000..24aaf6a08 --- /dev/null +++ b/src/utils/markdown2Html.js @@ -0,0 +1,355 @@ +import { JSDOM } from 'jsdom'; +import Remarkable from 'remarkable'; +import proxifyImageSrc from './proxifyImageSrc'; + +const md = new Remarkable({ html: true, breaks: true, linkify: true }); + +export const sanitizeNode = (node) => { + const ALLOWED_TAGS = [ + 'A', + 'STRONG', + 'B', + 'I', + 'EM', + 'CODE', + 'PRE', + 'BLOCKQUOTE', + 'SUP', + 'SUB', + 'H2', + 'H1', + 'H3', + 'H4', + 'H5', + 'H6', + 'DIV', + 'P', + 'IFRAME', + 'CENTER', + 'UL', + 'OL', + 'LI', + 'TABLE', + 'THEAD', + 'TBODY', + 'TR', + 'TD', + 'TH', + 'HR', + 'BR', + 'IMG', + 'DEL', + 'INS', + ]; + + const ALLOWED_ATTRS = [ + 'data-permlink', + 'data-tag', + 'data-author', + 'data-href', + 'class', + 'src', + 'alt', + 'title', + 'width', + 'height', + 'border', + 'frameborder', + 'allowfullscreen', + 'mozallowfullscreen', + 'webkitallowfullscreen', + ]; + + const allElems = node.querySelectorAll('*'); + allElems.forEach((el) => { + if (ALLOWED_TAGS.indexOf(el.tagName) === -1) { + el.outerHTML = `${el.textContent.replace('>', '>').replace('<', '<')}`; + } + + [...el.attributes].forEach((attr) => { + if ( + ALLOWED_ATTRS.indexOf(attr.name) === -1 + || attr.value.toLowerCase().indexOf('javascript:') !== -1 + ) { + el.removeAttribute(attr.name); + } + }); + }); + + return node; +}; + +const traverse = (node, depth = 0) => { + if (!node || !node.childNodes) return; + + node.childNodes.forEach((child) => { + if (child.nodeName === '#text') linkifyNode(child); + if (child.nodeName === 'IMG') img(child); + + traverse(child, depth + 1); + }); +}; + +const img = (node) => { + node.removeAttribute('width'); + node.removeAttribute('height'); + + const src = node.getAttribute('src'); + + node.setAttribute('src', proxifyImageSrc(src)); +}; + +const linkifyNode = (node) => { + if (['A', 'CODE'].includes(node.parentNode.nodeName)) return; + + const linkified = linkify(node.nodeValue); + if (linkified !== node.nodeValue) { + const replaceNode = node.ownerDocument.createElement('span'); + replaceNode.setAttribute('class', 'will-replaced'); // it will be replaced with its own innerHTML + node.parentNode.insertBefore(replaceNode, node); + node.parentNode.removeChild(node); + replaceNode.innerHTML = linkified; + } +}; + +export const linkify = (content) => { + // Tags + content = content.replace(/(^|\s|>)(#[-a-z\d]+)/gi, (tag) => { + if (/#[\d]+$/.test(tag)) return tag; // do not allow only numbers (like #1) + const preceding = /^\s|>/.test(tag) ? tag[0] : ''; // space or closing tag (>) + tag = tag.replace('>', ''); // remove closing tag + const tag2 = tag.trim().substring(1); + const tagLower = tag2.toLowerCase(); + return `${preceding}${tag.trim()}`; + }); + + // User mentions + content = content.replace( + /(^|[^a-zA-Z0-9_!#$%&*@@/]|(^|[^a-zA-Z0-9_+~.-/]))[@@]([a-z][-.a-z\d]+[a-z\d])/gi, + (match, preceeding1, preceeding2, user) => { + const userLower = user.toLowerCase(); + const preceedings = (preceeding1 || '') + (preceeding2 || ''); + + return `${preceedings}@${user}`; + }, + ); + + return content; +}; + +export default (input) => { + if (!input) { + return ''; + } + + let output = md.render(input); + + const imgRegex = /(https?:\/\/.*\.(?:tiff?|jpe?g|gif|png|svg|ico))(.*)/gim; + const postRegex = /^https?:\/\/(.*)\/(.*)\/(@[\w.\d-]+)\/(.*)/i; + const copiedPostRegex = /\/(.*)\/(@[\w.\d-]+)\/(.*)/i; + const youTubeRegex = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([^& \n<]+)(?:[^ \n<]+)?/g; + const vimeoRegex = /(https?:\/\/)?(www\.)?(?:vimeo)\.com.*(?:videos|video|channels|)\/([\d]+)/i; + const dTubeRegex = /(https?:\/\/d.tube.#!\/v\/)(\w+)\/(\w+)/g; + + // Create temporary document to manipulate html + const dom = new JSDOM(output, { + ProcessExternalResources: false, + }); + + // Manipulate link (a) elements + const links = dom.window.document.body.querySelectorAll('a'); + links.forEach((el) => { + const href = el.getAttribute('href'); + + // Continue if href has no value + if (!href) { + return; + } + + // Don't touch user and hashtag links + if (['markdown-author-link', 'markdown-tag-link'].indexOf(el.className) !== -1) { + return; + } + + let f = false; + + // Do not allow js hrefs + if (href.startsWith('javascript')) { + el.removeAttribute('href'); + f = true; + } + + // if href is an image url and innerHTML same with href then mark it as image + // & => & can break equality + if ( + href.match(imgRegex) + && href.trim().replace(/&/g, '&') === el.innerHTML.trim().replace(/&/g, '&') + ) { + el.setAttribute('data-href', href); + el.removeAttribute('href'); + + el.className = 'markdown-img-link'; + el.innerHTML = ``; + f = true; + } + + // If a steem post + if (!f) { + const postMatch = href.match(postRegex); + if (postMatch) { + el.className = 'markdown-post-link'; + el.removeAttribute('href'); + + el.setAttribute('data-tag', postMatch[2]); + el.setAttribute('data-author', postMatch[3].replace('@', '')); + el.setAttribute('data-permlink', postMatch[4]); + + f = true; + } + } + + // If a copied post link + if (!f) { + const postMatch = href.match(copiedPostRegex); + if (postMatch) { + el.className = 'markdown-post-link'; + el.removeAttribute('href'); + + el.setAttribute('data-tag', postMatch[1]); + el.setAttribute('data-author', postMatch[2].replace('@', '')); + el.setAttribute('data-permlink', postMatch[3]); + + f = true; + } + } + + // If a youtube video + if (!f) { + const match = href.match(youTubeRegex); + if (match && el.textContent.trim() === href) { + const e = youTubeRegex.exec(href); + if (e[1]) { + el.className = 'markdown-video-link markdown-video-link-youtube'; + el.removeAttribute('href'); + + const vid = e[1]; + const embedSrc = `https://www.youtube.com/embed/${vid}`; + el.innerHTML = ``; + f = true; + } + } + } + + // If vimeo video + if (!f) { + const match = href.match(vimeoRegex); + if (match && href === el.textContent) { + const e = vimeoRegex.exec(href); + if (e[3]) { + el.className = 'markdown-video-link markdown-video-link-vimeo'; + el.removeAttribute('href'); + + const embedSrc = `https://player.vimeo.com/video/${e[3]}`; + el.innerHTML = ``; + f = true; + } + } + } + + // If a d.tube video + if (!f) { + const match = href.match(dTubeRegex); + if (match) { + // Only d.tube links contains an image + const imgEls = el.querySelectorAll('img'); + if (el.textContent === href || imgEls.length === 1) { + const e = dTubeRegex.exec(href); + // e[2] = username, e[3] object id + if (e[2] && e[3]) { + el.className = 'markdown-video-link markdown-video-link-dtube'; + el.removeAttribute('href'); + + const embedSrc = `https://emb.d.tube/#!/${e[2]}/${e[3]}`; + el.innerHTML = ``; + f = true; + } + } + } + } + + if (!f) { + if ( + href.indexOf('https://steemit.com/~witnesses') === 0 + || href.indexOf('https://steemconnect.com/sign/account-witness-vote?witness=') === 0 + ) { + el.className = 'markdown-witnesses-link'; + + el.setAttribute('data-href', href); + el.removeAttribute('href'); + + f = true; + } + } + + // Convert image links using steemitimages proxy + if (!f) { + if (href.trim().indexOf('https://steemitimages.com/0x0/') === 0) { + el.setAttribute('data-href', href); + el.removeAttribute('href'); + + el.className = 'markdown-img-link'; + el.innerHTML = ``; + f = true; + } + } + + // If nothing matched element as external link so it will be opened in external window + if (!f) { + el.className = 'markdown-external-link'; + + el.setAttribute('data-href', href); + el.removeAttribute('href'); + } + }); + + // Try to convert image links that are Remarkable could not converted. + // Find text nodes not wrapper with a and node value matchs with image regex + const pars = dom.window.document.body.querySelectorAll('*'); + pars.forEach((el) => { + el.childNodes.forEach((n) => { + if ( + n.nodeValue + && n.nodeValue.trim() + && ['P', 'DIV', 'CENTER', 'STRONG'].indexOf(n.parentNode.tagName) !== -1 + ) { + const href = n.nodeValue.trim(); + if (href.match(imgRegex)) { + const replace = dom.window.document.createElement('a'); + replace.setAttribute('data-href', href); + + replace.className = 'markdown-img-link'; + replace.innerHTML = ``; + + n.parentNode.insertBefore(replace, n); + n.parentNode.removeChild(n); + } + } + }); + }); + + // Sanitize + const tempEl = sanitizeNode(dom.window.document.body); + + // traverse over elements and manipulate + traverse(tempEl); + + // replace temporary elements with their innerHTML's + const replaceElems = tempEl.querySelectorAll('.will-replaced'); + replaceElems.forEach((child) => { + child.outerHTML = child.innerHTML; + }); + + output = tempEl.innerHTML; + + return output; +}; diff --git a/src/utils/postParser.js b/src/utils/postParser.js index 77ce77e69..68d84588c 100644 --- a/src/utils/postParser.js +++ b/src/utils/postParser.js @@ -1,47 +1,50 @@ -import Remarkable from 'remarkable'; +//import Remarkable from 'remarkable'; + +// Utils import { getPostSummary } from './formatter'; import { getReputation } from './reputation'; import { getTimeFromNow } from './time'; +import markdown2Html from './markdown2Html'; -const md = new Remarkable({ html: true, breaks: true, linkify: true }); +//const md = new Remarkable({ html: true, breaks: true, linkify: true }); -export const replaceAuthorNames = input => input.replace( - /* eslint-disable-next-line */ - /(^|[^a-zA-Z0-9_!#$%&*@@\/]|(^|[^a-zA-Z0-9_+~.-\/]))[@@]([a-z][-\.a-z\d]+[a-z\d])/gi, - (match, preceeding1, preceeding2, user) => { - const userLower = user.toLowerCase(); - const preceedings = (preceeding1 || '') + (preceeding2 || ''); +// export const replaceAuthorNames = input => input.replace( +// /* eslint-disable-next-line */ +// /(^|[^a-zA-Z0-9_!#$%&*@@\/]|(^|[^a-zA-Z0-9_+~.-\/]))[@@]([a-z][-\.a-z\d]+[a-z\d])/gi, +// (match, preceeding1, preceeding2, user) => { +// const userLower = user.toLowerCase(); +// const preceedings = (preceeding1 || '') + (preceeding2 || ''); - return `${preceedings}@${user}`; - }, -); +// return `${preceedings}@${user}`; +// }, +// ); -export const replaceTags = input => input.replace(/(^|\s|>)(#[-a-z\d]+)/gi, (tag) => { - if (/#[\d]+$/.test(tag)) return tag; // do not allow only numbers (like #1) - const preceding = /^\s|>/.test(tag) ? tag[0] : ''; // space or closing tag (>) - tag = tag.replace('>', ''); // remove closing tag - const tag2 = tag.trim().substring(1); - const tagLower = tag2.toLowerCase(); - return `${preceding}${tag.trim()}`; -}); +// export const replaceTags = input => input.replace(/(^|\s|>)(#[-a-z\d]+)/gi, (tag) => { +// if (/#[\d]+$/.test(tag)) return tag; // do not allow only numbers (like #1) +// const preceding = /^\s|>/.test(tag) ? tag[0] : ''; // space or closing tag (>) +// tag = tag.replace('>', ''); // remove closing tag +// const tag2 = tag.trim().substring(1); +// const tagLower = tag2.toLowerCase(); +// return `${preceding}${tag.trim()}`; +// }); -export const markDown2Html = (input) => { - if (!input) { - return ''; - } +// export const markDown2Html = (input) => { +// if (!input) { +// return ''; +// } - // Start replacing user names - let output = replaceAuthorNames(input); +// // Start replacing user names +// let output = replaceAuthorNames(input); - // Replace tags - output = replaceTags(output); +// // Replace tags +// output = replaceTags(output); - output = md.render(output); +// output = md.render(output); - // TODO: Implement Regex --> Look at utls/formatter.js +// // TODO: Implement Regex --> Look at utls/formatter.js - return output; -}; +// return output; +// }; export const parsePosts = (posts, user) => { posts.map((post) => { @@ -52,7 +55,7 @@ export const parsePosts = (posts, user) => { post.vote_count = post.active_votes.length; post.author_reputation = getReputation(post.author_reputation); post.avatar = `https://steemitimages.com/u/${post.author}/avatar/small`; - post.body = markDown2Html(post.body); + post.body = markdown2Html(post.body); post.summary = getPostSummary(post.body, 100); post.raw_body = post.body; post.active_votes.sort((a, b) => b.rshares - a.rshares); @@ -99,7 +102,7 @@ export const parsePost = (post) => { post.vote_count = post.active_votes.length; post.author_reputation = getReputation(post.author_reputation); post.avatar = `https://steemitimages.com/u/${post.author}/avatar/small`; - post.body = markDown2Html(post.body); + post.body = markdown2Html(post.body); post.summary = getPostSummary(post.body, 100); post.raw_body = post.body; post.active_votes.sort((a, b) => b.rshares - a.rshares); diff --git a/src/utils/proxifyImageSrc.js b/src/utils/proxifyImageSrc.js new file mode 100644 index 000000000..2bab7c5ea --- /dev/null +++ b/src/utils/proxifyImageSrc.js @@ -0,0 +1,11 @@ +export default (url, width = 0, height = 0) => { + if (!url) { + return ''; + } + + const prefix = `https://steemitimages.com/${width}x${height}/`; + + if (url.startsWith(prefix)) return url; + + return `${prefix}${url}`; +};