From 270c69710f839e56caa1b919b12aed5148ed8090 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Mon, 20 Jan 2020 18:54:00 +0300 Subject: [PATCH 01/14] add validate interface for scriptlets --- README.md | 10 ++++++++++ dist/scriptlets.js | 7 +++++-- src/scriptlets/index.js | 7 ++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 87f5f341e..499cd71bb 100644 --- a/README.md +++ b/README.md @@ -132,6 +132,16 @@ Creates a global variable `scriptlets`. scriptlets.invoke(source) ``` +```javascript +/** +* Returns if the scriptlet is valid +* +* @param {Source} source +* @returns {boolean} +*/ +scriptlets.validate(source) +``` + #### Corelibs library `dist/scriptlets.corelibs.json` diff --git a/dist/scriptlets.js b/dist/scriptlets.js index 18fd7180c..5650743fa 100644 --- a/dist/scriptlets.js +++ b/dist/scriptlets.js @@ -2472,14 +2472,17 @@ * * @returns {Object} object with method `invoke` * `invoke` method receives one argument with `Source` type + * `validate` method receives one argument with `Source` type */ + // eslint-disable-next-line no-undef scriptlets = function () { return { - invoke: getScriptletCode + invoke: getScriptletCode, + validate: isValidScriptletSource }; - }(); // eslint-disable-line no-undef + }(); }()); diff --git a/src/scriptlets/index.js b/src/scriptlets/index.js index 3ba80739f..73bfb1fc7 100644 --- a/src/scriptlets/index.js +++ b/src/scriptlets/index.js @@ -67,5 +67,10 @@ function getScriptletCode(source) { * * @returns {Object} object with method `invoke` * `invoke` method receives one argument with `Source` type + * `validate` method receives one argument with `Source` type */ -scriptlets = (() => ({ invoke: getScriptletCode }))(); // eslint-disable-line no-undef +// eslint-disable-next-line no-undef +scriptlets = (() => ({ + invoke: getScriptletCode, + validate: isValidScriptletSource, +}))(); From 873c0a419c31fd2b300a791aca8f515649d7a933 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Mon, 20 Jan 2020 19:24:15 +0300 Subject: [PATCH 02/14] fix scriptlet validation method in order to get 'name' as a string --- README.md | 6 +++--- dist/scriptlets.js | 8 ++++---- src/scriptlets/index.js | 14 +++++++------- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index 499cd71bb..fb4d12271 100644 --- a/README.md +++ b/README.md @@ -134,12 +134,12 @@ scriptlets.invoke(source) ```javascript /** -* Returns if the scriptlet is valid +* Returns if the scriptlet name is valid * -* @param {Source} source +* @param {String} name - scriptlet name * @returns {boolean} */ -scriptlets.validate(source) +scriptlets.validate(name) ``` #### Corelibs library diff --git a/dist/scriptlets.js b/dist/scriptlets.js index 5650743fa..65bbc4543 100644 --- a/dist/scriptlets.js +++ b/dist/scriptlets.js @@ -2437,12 +2437,12 @@ */ - function isValidScriptletSource(source) { - if (!source.name) { + function isValidScriptletSource(name) { + if (!name) { return false; } - var scriptlet = getScriptletByName(source.name); + var scriptlet = getScriptletByName(name); if (!scriptlet) { return false; @@ -2457,7 +2457,7 @@ function getScriptletCode(source) { - if (!isValidScriptletSource(source)) { + if (!isValidScriptletSource(source.name)) { return null; } diff --git a/src/scriptlets/index.js b/src/scriptlets/index.js index 73bfb1fc7..c8dff631c 100644 --- a/src/scriptlets/index.js +++ b/src/scriptlets/index.js @@ -30,14 +30,14 @@ function getScriptletByName(name) { } /** - * Check is scriptlet params valid - * @param {Object} source + * Returns if the scriptlet name is valid + * @param {String} name - Scriptlet name */ -function isValidScriptletSource(source) { - if (!source.name) { +function isValidScriptletSource(name) { + if (!name) { return false; } - const scriptlet = getScriptletByName(source.name); + const scriptlet = getScriptletByName(name); if (!scriptlet) { return false; } @@ -49,7 +49,7 @@ function isValidScriptletSource(source) { * @param {Source} source */ function getScriptletCode(source) { - if (!isValidScriptletSource(source)) { + if (!isValidScriptletSource(source.name)) { return null; } @@ -67,7 +67,7 @@ function getScriptletCode(source) { * * @returns {Object} object with method `invoke` * `invoke` method receives one argument with `Source` type - * `validate` method receives one argument with `Source` type + * `validate` method receives one argument with `String` type */ // eslint-disable-next-line no-undef scriptlets = (() => ({ From 9241903cdc1f5b77264bc6152e815a7bc57b5b79 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Mon, 20 Jan 2020 20:54:52 +0300 Subject: [PATCH 03/14] edit scriptlets validation method description --- README.md | 2 +- dist/scriptlets.js | 6 +++--- src/scriptlets/index.js | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index fb4d12271..7b4898f58 100644 --- a/README.md +++ b/README.md @@ -134,7 +134,7 @@ scriptlets.invoke(source) ```javascript /** -* Returns if the scriptlet name is valid +* Checks if the scriptlet name is valid * * @param {String} name - scriptlet name * @returns {boolean} diff --git a/dist/scriptlets.js b/dist/scriptlets.js index 65bbc4543..ba988bd3a 100644 --- a/dist/scriptlets.js +++ b/dist/scriptlets.js @@ -2432,8 +2432,8 @@ }); } /** - * Check is scriptlet params valid - * @param {Object} source + * Checks if the scriptlet name is valid + * @param {String} name - Scriptlet name */ @@ -2472,7 +2472,7 @@ * * @returns {Object} object with method `invoke` * `invoke` method receives one argument with `Source` type - * `validate` method receives one argument with `Source` type + * `validate` method receives one argument with `String` type */ // eslint-disable-next-line no-undef diff --git a/src/scriptlets/index.js b/src/scriptlets/index.js index c8dff631c..e71d2a1e3 100644 --- a/src/scriptlets/index.js +++ b/src/scriptlets/index.js @@ -30,7 +30,7 @@ function getScriptletByName(name) { } /** - * Returns if the scriptlet name is valid + * Checks if the scriptlet name is valid * @param {String} name - Scriptlet name */ function isValidScriptletSource(name) { From 254b2859e7773cf1372eb1f7d1b827e2a630b8b2 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Fri, 24 Jan 2020 13:59:50 +0300 Subject: [PATCH 04/14] add convertion (to adg), parsing and rule validation methods --- README.md | 49 +++- dist/scriptlets.js | 434 +++++++++++++++++++++++++++++++++- package.json | 7 +- rollup.config.js | 99 +++++++- src/helpers/converter.js | 164 +++++++++++++ src/helpers/parse-rule.js | 148 ++++++++++++ src/helpers/string-utils.js | 53 +++++ src/scriptlets/index.js | 42 +++- tests/lib-tests/index.test.js | 61 +++++ tests/lib-tests/tests.html | 19 ++ yarn.lock | 81 ++++++- 11 files changed, 1127 insertions(+), 30 deletions(-) create mode 100644 src/helpers/converter.js create mode 100644 src/helpers/parse-rule.js create mode 100644 tests/lib-tests/index.test.js create mode 100644 tests/lib-tests/tests.html diff --git a/README.md b/README.md index 7b4898f58..fc0e2658a 100644 --- a/README.md +++ b/README.md @@ -125,21 +125,58 @@ Creates a global variable `scriptlets`. ```javascript /** * Returns scriptlet code -* * @param {Source} source -* @returns {string} +* @returns {String} */ -scriptlets.invoke(source) +scriptlets.invoke(source); ``` ```javascript /** * Checks if the scriptlet name is valid -* * @param {String} name - scriptlet name -* @returns {boolean} +* @returns {Boolean} */ -scriptlets.validate(name) +scriptlets.validateName(name); +``` + +```javascript +/** +* Validates any scriptlet rule +* @param {String} input - can be Adguard or Ubo or Abp scriptlet rule +* @returns {Boolean} +*/ +scriptlets.validateRule(input); +``` + +```javascript +/** +* Checks is AdGuard / Ubo / Abp scriptlet rule +* @param {String} rule - rule text +* @returns {Boolean} +*/ +scriptlets.isAdgScriptletRule(rule); +scriptlets.isAdgScriptletRule(rule); +scriptlets.isAdgScriptletRule(rule); +``` + +```javascript +/** +* Converts Ubo / Abp scriptlet rule to AdGuard +* @param {String} rule - rule text +* @returns {String} - AdGuard scriptlet rule +*/ +scriptlets.convertUboToAdg(rule); +scriptlets.convertAbpToAdg(rule); +``` + +```javascript +/** +* Checks is any scriptlet rule and converts to AdGuard +* @param {String} rule - rule text +* @returns {String} - AdGuard scriptlet rule +*/ +scriptlets.convertScriptletToAdg(rule); ``` #### Corelibs library diff --git a/dist/scriptlets.js b/dist/scriptlets.js index ba988bd3a..526b7b8cb 100644 --- a/dist/scriptlets.js +++ b/dist/scriptlets.js @@ -98,6 +98,60 @@ var escaped = str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); return new RegExp(escaped); }; + /** + * Get string before regexp first match + * @param {string} str + * @param {RegExp} rx + */ + + var getBeforeRegExp = function getBeforeRegExp(str, rx) { + var index = str.search(rx); + return str.substring(0, index); + }; + var substringAfter = function substringAfter(str, separator) { + if (!str) { + return str; + } + + var index = str.indexOf(separator); + return index < 0 ? '' : str.substring(index + separator.length); + }; + var substringBefore = function substringBefore(str, separator) { + if (!str || !separator) { + return str; + } + + var index = str.indexOf(separator); + return index < 0 ? str : str.substring(0, index); + }; + /** + * Wrap str in double qoutes and replaces single quotes if need + * @param {string} str + */ + + var wrapInDoubleQuotes = function wrapInDoubleQuotes(str) { + if (str[0] === '\'' && str[str.length - 1] === '\'') { + str = str.substring(1, str.length - 1); // eslint-disable-next-line no-useless-escape + + str = str.replace(/\"/g, '\\"'); + } else if (str[0] === '"' && str[str.length - 1] === '"') { + str = str.substring(1, str.length - 1); // eslint-disable-next-line no-useless-escape + + str = str.replace(/\'/g, '\\\''); + } + + return "\"".concat(str, "\""); + }; + /** + * Returns substring enclosed in the widest braces + * @param {string} str + */ + + var getStringInBraces = function getStringInBraces(str) { + var firstIndex = str.indexOf('('); + var lastIndex = str.lastIndexOf(')'); + return str.substring(firstIndex + 1, lastIndex); + }; /** * Generates function which silents global errors on page generated by scriptlet @@ -208,6 +262,11 @@ getPropertyInChain: getPropertyInChain, escapeRegExp: escapeRegExp, toRegExp: toRegExp, + getBeforeRegExp: getBeforeRegExp, + substringAfter: substringAfter, + substringBefore: substringBefore, + wrapInDoubleQuotes: wrapInDoubleQuotes, + getStringInBraces: getStringInBraces, createOnErrorHandler: createOnErrorHandler, noop: noop, noopNull: noopNull, @@ -280,6 +339,353 @@ return "function(source, args){\n".concat(code, "\n}"); } + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + var defineProperty = _defineProperty; + + /** + * Iterate over iterable argument and evaluate current state with transitions + * @param {string} init first transition name + * @param {Array|Collection|string} iterable + * @param {Object} transitions transtion functions + * @param {any} args arguments which should be passed to transition functions + */ + function iterateWithTransitions(iterable, transitions, init, args) { + var state = init || Object.keys(transitions)[0]; + + for (var i = 0; i < iterable.length; i += 1) { + state = transitions[state](iterable, i, args); + } + + return state; + } + /** + * AdGuard scriptlet rule mask + */ + + + var ADG_SCRIPTLET_MASK = '#//scriptlet'; + /** + * Helper to accumulate an array of strings char by char + */ + + var wordSaver = function wordSaver() { + var str = ''; + var strs = []; + + var saveSymb = function saveSymb(s) { + str += s; + return str; + }; + + var saveStr = function saveStr() { + strs.push(str); + str = ''; + }; + + var getAll = function getAll() { + return [].concat(strs); + }; + + return { + saveSymb: saveSymb, + saveStr: saveStr, + getAll: getAll + }; + }; + + var substringAfter$1 = function substringAfter(str, separator) { + if (!str) { + return str; + } + + var index = str.indexOf(separator); + return index < 0 ? '' : str.substring(index + separator.length); + }; + /** + * Parse and validate scriptlet rule + * @param {*} ruleText + * @returns {{name: string, args: Array}} + */ + + + var parseRule = function parseRule(ruleText) { + var _transitions; + + ruleText = substringAfter$1(ruleText, ADG_SCRIPTLET_MASK); + /** + * Transition names + */ + + var TRANSITION = { + OPENED: 'opened', + PARAM: 'param', + CLOSED: 'closed' + }; + /** + * Transition function: the current index position in start, end or between params + * @param {string} rule + * @param {number} index + * @param {Object} Object + * @property {Object} Object.sep contains prop symb with current separator char + */ + + var opened = function opened(rule, index, _ref) { + var sep = _ref.sep; + var _char = rule[index]; + var transition; + + switch (_char) { + case ' ': + case '(': + case ',': + { + transition = TRANSITION.OPENED; + break; + } + + case '\'': + case '"': + { + sep.symb = _char; + transition = TRANSITION.PARAM; + break; + } + + case ')': + { + transition = index === rule.length - 1 ? TRANSITION.CLOSED : TRANSITION.OPENED; + break; + } + + default: + { + throw new Error('The rule is not a scriptlet'); + } + } + + return transition; + }; + /** + * Transition function: the current index position inside param + * @param {string} rule + * @param {number} index + * @param {Object} Object + * @property {Object} Object.sep contains prop `symb` with current separator char + * @property {Object} Object.saver helper which allow to save strings by car by char + */ + + + var param = function param(rule, index, _ref2) { + var saver = _ref2.saver, + sep = _ref2.sep; + var _char2 = rule[index]; + + switch (_char2) { + case '\'': + case '"': + { + var preIndex = index - 1; + var before = rule[preIndex]; + + if (_char2 === sep.symb && before !== '\\') { + sep.symb = null; + saver.saveStr(); + return TRANSITION.OPENED; + } + } + // eslint-disable-next-line no-fallthrough + + default: + { + saver.saveSymb(_char2); + return TRANSITION.PARAM; + } + } + }; + + var transitions = (_transitions = {}, defineProperty(_transitions, TRANSITION.OPENED, opened), defineProperty(_transitions, TRANSITION.PARAM, param), defineProperty(_transitions, TRANSITION.CLOSED, function () {}), _transitions); + var sep = { + symb: null + }; + var saver = wordSaver(); + var state = iterateWithTransitions(ruleText, transitions, TRANSITION.OPENED, { + sep: sep, + saver: saver + }); + + if (state !== 'closed') { + throw new Error("Invalid scriptlet rule ".concat(ruleText)); + } + + var args = saver.getAll(); + return { + name: args[0], + args: args.slice(1) + }; + }; + + /** + * AdGuard scriptlet rule + */ + // eslint-disable-next-line no-template-curly-in-string + + var ADGUARD_SCRIPTLET_TEMPLATE = '${domains}#%#//scriptlet(${args})'; // eslint-disable-next-line no-template-curly-in-string + + var ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE = '${domains}#@%#//scriptlet(${args})'; + /** + * uBlock scriptlet rule mask + */ + + var UBO_SCRIPTLET_MASK_REG = /#@?#script:inject|#@?#\s*\+js/; + var UBO_SCRIPTLET_MASK_1 = '##+js'; + var UBO_SCRIPTLET_MASK_2 = '##script:inject'; + var UBO_SCRIPTLET_EXCEPTION_MASK_1 = '#@#+js'; + var UBO_SCRIPTLET_EXCEPTION_MASK_2 = '#@#script:inject'; // const UBO_SCRIPT_TAG_MASK = '##^script'; + + /** + * AdBlock Plus snippet rule mask + */ + + var ABP_SCRIPTLET_MASK = '#$#'; + var ABP_SCRIPTLET_EXCEPTION_MASK = '#@$#'; + /** + * AdGuard CSS rule mask + */ + + var ADG_CSS_MASK_REG = /#@?\$#.+?\s*\{.*\}\s*$/g; + /** + * Return array of strings separated by space which not in quotes + * @param {string} str + */ + + var getSentences = function getSentences(str) { + var reg = /'.*?'|".*?"|\S+/g; + return str.match(reg); + }; + /** + * Replace string with data by placeholders + * @param {string} str + * @param {Object} data where keys is placeholdes names + */ + + + var replacePlaceholders = function replacePlaceholders(str, data) { + return Object.keys(data).reduce(function (acc, key) { + var reg = new RegExp("\\$\\{".concat(key, "\\}"), 'g'); + acc = acc.replace(reg, data[key]); + return acc; + }, str); + }; + /** + * Check is AdGuard scriptlet rule + * @param {string} rule rule text + */ + + + var isAdgScriptletRule = function isAdgScriptletRule(rule) { + return rule.indexOf(ADG_SCRIPTLET_MASK) > -1; + }; + /** + * Check is uBO scriptlet rule + * @param {string} rule rule text + */ + + var isUboScriptletRule = function isUboScriptletRule(rule) { + return (rule.indexOf(UBO_SCRIPTLET_MASK_1) > -1 || rule.indexOf(UBO_SCRIPTLET_MASK_2) > -1 || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_1) > -1 || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_2) > -1) && UBO_SCRIPTLET_MASK_REG.test(rule); + }; + /** + * Check is AdBlock Plus snippet + * @param {string} rule rule text + */ + + var isAbpSnippetRule = function isAbpSnippetRule(rule) { + return (rule.indexOf(ABP_SCRIPTLET_MASK) > -1 || rule.indexOf(ABP_SCRIPTLET_EXCEPTION_MASK) > -1) && rule.search(ADG_CSS_MASK_REG) === -1; + }; + /** + * Convert string of UBO scriptlet rule to AdGuard scritlet rule + * @param {string} rule UBO scriptlet rule + */ + + var convertUboToAdg = function convertUboToAdg(rule) { + var domains = getBeforeRegExp(rule, UBO_SCRIPTLET_MASK_REG); + var mask = rule.match(UBO_SCRIPTLET_MASK_REG)[0]; + var template; + + if (mask.indexOf('@') > -1) { + template = ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; + } else { + template = ADGUARD_SCRIPTLET_TEMPLATE; + } + + var args = getStringInBraces(rule).split(/, /g).map(function (arg, index) { + return index === 0 ? "ubo-".concat(arg) : arg; + }).map(function (arg) { + return wrapInDoubleQuotes(arg); + }).join(', '); + return replacePlaceholders(template, { + domains: domains, + args: args + }); + }; + /** + * Convert string of ABP scriptlet rule to AdGuard scritlet rule + * @param {string} rule ABP scriptlet rule + */ + + var convertAbpToAdg = function convertAbpToAdg(rule) { + var SEMICOLON_DIVIDER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/g; + var mask = rule.indexOf(ABP_SCRIPTLET_MASK) > -1 ? ABP_SCRIPTLET_MASK : ABP_SCRIPTLET_EXCEPTION_MASK; + var template = mask === ABP_SCRIPTLET_MASK ? ADGUARD_SCRIPTLET_TEMPLATE : ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; + var domains = substringBefore(rule, mask); + var args = substringAfter(rule, mask); + return args.split(SEMICOLON_DIVIDER).map(function (args) { + return getSentences(args).filter(function (arg) { + return arg; + }).map(function (arg, index) { + return index === 0 ? "abp-".concat(arg) : arg; + }).map(function (arg) { + return wrapInDoubleQuotes(arg); + }).join(', '); + }).map(function (args) { + return replacePlaceholders(template, { + domains: domains, + args: args + }); + }).toString(''); + }; + /** + * Converts scriptlet rule to AdGuard one + * @param {*} rule + */ + + var convertScriptletToAdg = function convertScriptletToAdg(rule) { + var result; + + if (isUboScriptletRule(rule)) { + result = convertUboToAdg(rule); + } else if (isAbpSnippetRule(rule)) { + result = convertAbpToAdg(rule); + } else if (isAdgScriptletRule(rule)) { + result = rule; + } + + return result; + }; + /* eslint-disable max-len */ /** @@ -2437,7 +2843,7 @@ */ - function isValidScriptletSource(name) { + function isValidScriptletName(name) { if (!name) { return false; } @@ -2457,7 +2863,7 @@ function getScriptletCode(source) { - if (!isValidScriptletSource(source.name)) { + if (!isValidScriptletName(source.name)) { return null; } @@ -2467,6 +2873,21 @@ result = source.engine === 'corelibs' ? wrapInNonameFunc(result) : passSourceAndProps(source, result); return result; } + /** + * Validates any scriptlet rule + * @param {String} input - can be Adguard or Ubo or Abp scriptlet rule + */ + + + function isValidScriptletRule(input) { + if (!input) { + return false; + } + + var rule = convertScriptletToAdg(input); + var parsedRule = parseRule(rule); + return isValidScriptletName(parsedRule.name); + } /** * Global scriptlet variable * @@ -2480,7 +2901,14 @@ scriptlets = function () { return { invoke: getScriptletCode, - validate: isValidScriptletSource + validateName: isValidScriptletName, + validateRule: isValidScriptletRule, + isAdgScriptletRule: isAdgScriptletRule, + isUboScriptletRule: isUboScriptletRule, + isAbpSnippetRule: isAbpSnippetRule, + convertUboToAdg: convertUboToAdg, + convertAbpToAdg: convertAbpToAdg, + convertScriptletToAdg: convertScriptletToAdg }; }(); diff --git a/package.json b/package.json index dc87d65e2..ce84c6693 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,9 @@ "corelibs": "rollup -c --environment CLEAN && babel-node corelibs.build.js && REDIRECTS=CORELIBS babel-node redirects.build.js", "build-test": "rollup -c --environment UI_TEST", "test": "yarn build-test && node tests/index.js", - "test-watch": "rollup -c -w --environment UI_TEST", + "test-watch": "rollup -c --watch --environment UI_TEST", + "test-lib-watch": "rollup -c --watch --environment UI_LIB_TEST", + "debug-lib-watch": "rollup -c --watch --environment DEBUG_LIB", "browserstack": "yarn build-test && node browserstack.js", "gui-test": "yarn build-test && open http://localhost:8585 && node ./tests/server.js", "lint": "eslint .", @@ -29,6 +31,7 @@ "@babel/plugin-transform-regenerator": "^7.7.0", "@babel/plugin-transform-runtime": "^7.6.2", "@babel/preset-env": "^7.7.1", + "chokidar": "^3.3.1", "dox": "^0.9.0", "eslint": "^6.6.0", "eslint-config-airbnb-base": "^14.0.0", @@ -37,7 +40,7 @@ "husky": "^3.1.0", "node-qunit-puppeteer": "^1.0.16", "qunit": "^2.9.3", - "rollup": "^1.27.4", + "rollup": "^1.29.1", "rollup-plugin-babel": "^4.3.3", "rollup-plugin-cleanup": "^3.1.1", "rollup-plugin-clear": "^2.0.7", diff --git a/rollup.config.js b/rollup.config.js index 31e15ebc1..c5bd7d12c 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -26,6 +26,7 @@ const footer = ` `; const TESTS_DIST = 'tests/dist'; +const LIB_TESTS_DIST = 'tests/dist/lib-tests'; const TMP_DIR = 'tmp'; const bundleBuild = { @@ -91,6 +92,83 @@ const testBuild = { ], }; +const debugLib = { + input: 'src/scriptlets/index.js', + output: { + dir: 'dist', + entryFileNames: '[name].js', + format: 'iife', + strict: false, + sourcemap: true, + }, + watch: { + include: ['*/**'], + chokidar: false, + }, + plugins: [ + clear({ + targets: [LIB_TESTS_DIST], + }), + resolve(), + commonjs({ + include: 'node_modules/**', + }), + babel({ + exclude: 'node_modules/**', + runtimeHelpers: true, + }), + copy({ + targets: [{ + src: [ + 'tests/lib-tests/tests.html', + 'tests/styles.css', + 'node_modules/qunit/qunit/qunit.js', + 'node_modules/sinon/pkg/sinon.js', + 'dist/scriptlets.js', + ], + dest: LIB_TESTS_DIST, + }], + }), + ], +}; + +const testLibTests = { + input: 'tests/lib-tests/index.test.js', + output: { + dir: LIB_TESTS_DIST, + entryFileNames: '[name].js', + format: 'iife', + strict: false, + sourcemap: true, + }, + watch: { + include: ['tests/lib-tests/**'], + chokidar: false, + }, + plugins: [ + clear({ + targets: [LIB_TESTS_DIST], + }), + resolve(), + commonjs({ + include: 'node_modules/**', + }), + babel({ + exclude: 'node_modules/**', + runtimeHelpers: true, + }), + copy({ + targets: [{ + src: [ + 'tests/lib-tests/index.test.js', + 'dist/scriptlets.js', + ], + dest: LIB_TESTS_DIST, + }], + }), + ], +}; + const tmpRedirectsBuild = { input: { tmpRedirects: 'src/redirects/index.js', @@ -122,8 +200,23 @@ if (isCleanBuild) { } const isTest = process.env.UI_TEST === 'true'; -const resultBuilds = isTest - ? [bundleBuild, testBuild] - : [bundleBuild, tmpRedirectsBuild]; +const isLibTest = process.env.UI_LIB_TEST === 'true'; +const isDebugLib = process.env.DEBUG_LIB === 'true'; + +let resultBuilds = []; + +if (isDebugLib) { + resultBuilds = [bundleBuild, debugLib]; +} else if (isLibTest) { + resultBuilds = [debugLib, testLibTests]; +} else if (isTest) { + resultBuilds = [bundleBuild, testBuild]; +} else { + resultBuilds = [bundleBuild, tmpRedirectsBuild]; +} + +// const resultBuilds = isTest +// ? [bundleBuild, testBuild] +// : [bundleBuild, tmpRedirectsBuild]; module.exports = resultBuilds; diff --git a/src/helpers/converter.js b/src/helpers/converter.js new file mode 100644 index 000000000..66809bed5 --- /dev/null +++ b/src/helpers/converter.js @@ -0,0 +1,164 @@ +import { + getBeforeRegExp, + substringAfter, + substringBefore, + wrapInDoubleQuotes, + getStringInBraces, +} from './string-utils'; + +import { ADG_SCRIPTLET_MASK } from './parse-rule'; + +/** + * AdGuard scriptlet rule + */ +// eslint-disable-next-line no-template-curly-in-string +const ADGUARD_SCRIPTLET_TEMPLATE = '${domains}#%#//scriptlet(${args})'; +// eslint-disable-next-line no-template-curly-in-string +const ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE = '${domains}#@%#//scriptlet(${args})'; + + +/** + * uBlock scriptlet rule mask + */ +const UBO_SCRIPTLET_MASK_REG = /#@?#script:inject|#@?#\s*\+js/; +const UBO_SCRIPTLET_MASK_1 = '##+js'; +const UBO_SCRIPTLET_MASK_2 = '##script:inject'; +const UBO_SCRIPTLET_EXCEPTION_MASK_1 = '#@#+js'; +const UBO_SCRIPTLET_EXCEPTION_MASK_2 = '#@#script:inject'; +// const UBO_SCRIPT_TAG_MASK = '##^script'; + +/** + * AdBlock Plus snippet rule mask + */ +const ABP_SCRIPTLET_MASK = '#$#'; +const ABP_SCRIPTLET_EXCEPTION_MASK = '#@$#'; + +/** + * AdGuard CSS rule mask + */ +const ADG_CSS_MASK_REG = /#@?\$#.+?\s*\{.*\}\s*$/g; + +/** + * Return array of strings separated by space which not in quotes + * @param {string} str + */ +const getSentences = (str) => { + const reg = /'.*?'|".*?"|\S+/g; + return str.match(reg); +}; + +/** + * Replace string with data by placeholders + * @param {string} str + * @param {Object} data where keys is placeholdes names + */ +const replacePlaceholders = (str, data) => { + return Object.keys(data).reduce((acc, key) => { + const reg = new RegExp(`\\$\\{${key}\\}`, 'g'); + acc = acc.replace(reg, data[key]); + return acc; + }, str); +}; + + +/** + * Check is AdGuard scriptlet rule + * @param {string} rule rule text + */ +export const isAdgScriptletRule = (rule) => { + return rule.indexOf(ADG_SCRIPTLET_MASK) > -1; +}; + +/** + * Check is uBO scriptlet rule + * @param {string} rule rule text + */ +export const isUboScriptletRule = (rule) => { + return ( + rule.indexOf(UBO_SCRIPTLET_MASK_1) > -1 + || rule.indexOf(UBO_SCRIPTLET_MASK_2) > -1 + || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_1) > -1 + || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_2) > -1 + ) + && UBO_SCRIPTLET_MASK_REG.test(rule); +}; + +/** + * Check is AdBlock Plus snippet + * @param {string} rule rule text + */ +export const isAbpSnippetRule = (rule) => { + return ( + rule.indexOf(ABP_SCRIPTLET_MASK) > -1 + || rule.indexOf(ABP_SCRIPTLET_EXCEPTION_MASK) > -1 + ) + && rule.search(ADG_CSS_MASK_REG) === -1; +}; + +/** + * Convert string of UBO scriptlet rule to AdGuard scritlet rule + * @param {string} rule UBO scriptlet rule + */ +export const convertUboToAdg = (rule) => { + const domains = getBeforeRegExp(rule, UBO_SCRIPTLET_MASK_REG); + const mask = rule.match(UBO_SCRIPTLET_MASK_REG)[0]; + let template; + if (mask.indexOf('@') > -1) { + template = ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; + } else { + template = ADGUARD_SCRIPTLET_TEMPLATE; + } + const args = getStringInBraces(rule) + .split(/, /g) + .map((arg, index) => (index === 0 ? `ubo-${arg}` : arg)) + .map((arg) => (wrapInDoubleQuotes(arg))) + .join(', '); + + return replacePlaceholders( + template, + { domains, args }, + ); +}; + +/** + * Convert string of ABP scriptlet rule to AdGuard scritlet rule + * @param {string} rule ABP scriptlet rule + */ +export const convertAbpToAdg = (rule) => { + const SEMICOLON_DIVIDER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/g; + const mask = rule.indexOf(ABP_SCRIPTLET_MASK) > -1 + ? ABP_SCRIPTLET_MASK + : ABP_SCRIPTLET_EXCEPTION_MASK; + const template = mask === ABP_SCRIPTLET_MASK + ? ADGUARD_SCRIPTLET_TEMPLATE + : ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; + const domains = substringBefore(rule, mask); + const args = substringAfter(rule, mask); + + return args.split(SEMICOLON_DIVIDER) + .map((args) => getSentences(args) + .filter((arg) => arg) + .map((arg, index) => (index === 0 ? `abp-${arg}` : arg)) + .map((arg) => wrapInDoubleQuotes(arg)) + .join(', ')) + .map((args) => replacePlaceholders(template, { domains, args })) + .toString(''); +}; + +/** + * Converts scriptlet rule to AdGuard one + * @param {*} rule + */ + +export const convertScriptletToAdg = (rule) => { + let result; + if (isUboScriptletRule(rule)) { + result = convertUboToAdg(rule); + } else if (isAbpSnippetRule(rule)) { + result = convertAbpToAdg(rule); + } else if (isAdgScriptletRule(rule)) { + result = rule; + } + + return result; +}; diff --git a/src/helpers/parse-rule.js b/src/helpers/parse-rule.js new file mode 100644 index 000000000..09ca4a14e --- /dev/null +++ b/src/helpers/parse-rule.js @@ -0,0 +1,148 @@ +/** + * Iterate over iterable argument and evaluate current state with transitions + * @param {string} init first transition name + * @param {Array|Collection|string} iterable + * @param {Object} transitions transtion functions + * @param {any} args arguments which should be passed to transition functions + */ +function iterateWithTransitions(iterable, transitions, init, args) { + let state = init || Object.keys(transitions)[0]; + for (let i = 0; i < iterable.length; i += 1) { + state = transitions[state](iterable, i, args); + } + return state; +} + +/** + * AdGuard scriptlet rule mask + */ +export const ADG_SCRIPTLET_MASK = '#//scriptlet'; + +/** + * Helper to accumulate an array of strings char by char + */ +const wordSaver = () => { + let str = ''; + const strs = []; + const saveSymb = (s) => { + str += s; + return str; + }; + const saveStr = () => { + strs.push(str); + str = ''; + }; + const getAll = () => [...strs]; + + return { saveSymb, saveStr, getAll }; +}; + +const substringAfter = (str, separator) => { + if (!str) { + return str; + } + const index = str.indexOf(separator); + return index < 0 ? '' : str.substring(index + separator.length); +}; + +/** + * Parse and validate scriptlet rule + * @param {*} ruleText + * @returns {{name: string, args: Array}} + */ +const parseRule = (ruleText) => { + ruleText = substringAfter(ruleText, ADG_SCRIPTLET_MASK); + /** + * Transition names + */ + const TRANSITION = { + OPENED: 'opened', + PARAM: 'param', + CLOSED: 'closed', + }; + + /** + * Transition function: the current index position in start, end or between params + * @param {string} rule + * @param {number} index + * @param {Object} Object + * @property {Object} Object.sep contains prop symb with current separator char + */ + const opened = (rule, index, { sep }) => { + const char = rule[index]; + let transition; + switch (char) { + case ' ': + case '(': + case ',': { + transition = TRANSITION.OPENED; + break; + } + case '\'': + case '"': { + sep.symb = char; + transition = TRANSITION.PARAM; + break; + } + case ')': { + transition = index === rule.length - 1 + ? TRANSITION.CLOSED + : TRANSITION.OPENED; + break; + } + default: { + throw new Error('The rule is not a scriptlet'); + } + } + + return transition; + }; + /** + * Transition function: the current index position inside param + * @param {string} rule + * @param {number} index + * @param {Object} Object + * @property {Object} Object.sep contains prop `symb` with current separator char + * @property {Object} Object.saver helper which allow to save strings by car by char + */ + const param = (rule, index, { saver, sep }) => { + const char = rule[index]; + switch (char) { + case '\'': + case '"': { + const preIndex = index - 1; + const before = rule[preIndex]; + if (char === sep.symb && before !== '\\') { + sep.symb = null; + saver.saveStr(); + return TRANSITION.OPENED; + } + } + // eslint-disable-next-line no-fallthrough + default: { + saver.saveSymb(char); + return TRANSITION.PARAM; + } + } + }; + const transitions = { + [TRANSITION.OPENED]: opened, + [TRANSITION.PARAM]: param, + [TRANSITION.CLOSED]: () => { }, + }; + const sep = { symb: null }; + const saver = wordSaver(); + const state = iterateWithTransitions(ruleText, transitions, TRANSITION.OPENED, { sep, saver }); + + if (state !== 'closed') { + throw new Error(`Invalid scriptlet rule ${ruleText}`); + } + + const args = saver.getAll(); + return { + name: args[0], + args: args.slice(1), + }; +}; + +export default parseRule; diff --git a/src/helpers/string-utils.js b/src/helpers/string-utils.js index 23530f4a7..dc3849bbf 100644 --- a/src/helpers/string-utils.js +++ b/src/helpers/string-utils.js @@ -18,3 +18,56 @@ export const toRegExp = (str) => { const escaped = str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); return new RegExp(escaped); }; + +/** + * Get string before regexp first match + * @param {string} str + * @param {RegExp} rx + */ +export const getBeforeRegExp = (str, rx) => { + const index = str.search(rx); + return str.substring(0, index); +}; + +export const substringAfter = (str, separator) => { + if (!str) { + return str; + } + const index = str.indexOf(separator); + return index < 0 ? '' : str.substring(index + separator.length); +}; + +export const substringBefore = (str, separator) => { + if (!str || !separator) { + return str; + } + const index = str.indexOf(separator); + return index < 0 ? str : str.substring(0, index); +}; + +/** + * Wrap str in double qoutes and replaces single quotes if need + * @param {string} str + */ +export const wrapInDoubleQuotes = (str) => { + if (str[0] === '\'' && str[str.length - 1] === '\'') { + str = str.substring(1, str.length - 1); + // eslint-disable-next-line no-useless-escape + str = str.replace(/\"/g, '\\"'); + } else if (str[0] === '"' && str[str.length - 1] === '"') { + str = str.substring(1, str.length - 1); + // eslint-disable-next-line no-useless-escape + str = str.replace(/\'/g, '\\\''); + } + return `"${str}"`; +}; + +/** + * Returns substring enclosed in the widest braces + * @param {string} str + */ +export const getStringInBraces = (str) => { + const firstIndex = str.indexOf('('); + const lastIndex = str.lastIndexOf(')'); + return str.substring(firstIndex + 1, lastIndex); +}; diff --git a/src/scriptlets/index.js b/src/scriptlets/index.js index e71d2a1e3..da05e2701 100644 --- a/src/scriptlets/index.js +++ b/src/scriptlets/index.js @@ -5,8 +5,18 @@ import { wrapInNonameFunc, } from '../helpers/injector'; -import * as scriptletsList from './scriptletsList'; +import { + isAdgScriptletRule, + isUboScriptletRule, + isAbpSnippetRule, + convertUboToAdg, + convertAbpToAdg, + convertScriptletToAdg, +} from '../helpers/converter'; +import parseRule from '../helpers/parse-rule'; + +import * as scriptletsList from './scriptletsList'; /** * @typedef {Object} Source - scriptlet properties @@ -33,7 +43,7 @@ function getScriptletByName(name) { * Checks if the scriptlet name is valid * @param {String} name - Scriptlet name */ -function isValidScriptletSource(name) { +function isValidScriptletName(name) { if (!name) { return false; } @@ -49,7 +59,7 @@ function isValidScriptletSource(name) { * @param {Source} source */ function getScriptletCode(source) { - if (!isValidScriptletSource(source.name)) { + if (!isValidScriptletName(source.name)) { return null; } @@ -62,6 +72,23 @@ function getScriptletCode(source) { return result; } +/** + * Validates any scriptlet rule + * @param {String} input - can be Adguard or Ubo or Abp scriptlet rule + */ +function isValidScriptletRule(input) { + if (!input) { + return false; + } + + const rule = convertScriptletToAdg(input); + + const parsedRule = parseRule(rule); + + return isValidScriptletName(parsedRule.name); +} + + /** * Global scriptlet variable * @@ -72,5 +99,12 @@ function getScriptletCode(source) { // eslint-disable-next-line no-undef scriptlets = (() => ({ invoke: getScriptletCode, - validate: isValidScriptletSource, + validateName: isValidScriptletName, + validateRule: isValidScriptletRule, + isAdgScriptletRule, + isUboScriptletRule, + isAbpSnippetRule, + convertUboToAdg, + convertAbpToAdg, + convertScriptletToAdg, }))(); diff --git a/tests/lib-tests/index.test.js b/tests/lib-tests/index.test.js new file mode 100644 index 000000000..bde518534 --- /dev/null +++ b/tests/lib-tests/index.test.js @@ -0,0 +1,61 @@ +/* global QUnit */ + +import { convertScriptletToAdg } from '../../src/helpers/converter'; + +const { test, module } = QUnit; +const name = 'debug-current-inline-script'; + +module(name); + + +// console.log('XXXXXXXXXXXXXXXXX'); + +/* eslint-disable max-len */ +// test('Test scriptlet adguard rule', (assert) => { +// const rule = "example.org#%#//scriptlet('abort-on-property-read', 'I10C')"; +// const exp = "example.org#%#//scriptlet('abort-on-property-read', 'I10C')"; +// const res = convertScriptletToAdg(rule); +// assert.equal(res, exp); +// }); + +test('Test scriptlet adguard rule exception', (assert) => { + const rule = "example.org#@%#//scriptlet('abort-on-property-read', 'I10C')"; + const exp = "example.org#@%#//scriptlet('abort-on-property-read', 'I10C')"; + const res = convertScriptletToAdg(rule); + assert.equal(res, exp); +}); + +test('Test converter scriptlet ubo rule', (assert) => { + // blocking rule + const rule = 'example.org##+js(setTimeout-defuser.js, [native code], 8000)'; + const exp = 'example.org#%#//scriptlet("ubo-setTimeout-defuser.js", "[native code]", "8000")'; + const res = convertScriptletToAdg(rule); + assert.equal(res, exp); + // whitelist rule + let whitelistRule = 'example.org#@#+js(setTimeout-defuser.js, [native code], 8000)'; + let expectedResult = 'example.org#@%#//scriptlet("ubo-setTimeout-defuser.js", "[native code]", "8000")'; + assert.equal(convertScriptletToAdg(whitelistRule), expectedResult); + + whitelistRule = 'example.org#@#script:inject(abort-on-property-read.js, some.prop)'; + expectedResult = 'example.org#@%#//scriptlet("ubo-abort-on-property-read.js", "some.prop")'; + assert.equal(convertScriptletToAdg(whitelistRule), expectedResult); +}); + +test('Test converter scriptlet abp rule', (assert) => { + const rule = "example.org#$#hide-if-contains li.serp-item 'li.serp-item div.label'"; + const exp = 'example.org#%#//scriptlet("abp-hide-if-contains", "li.serp-item", "li.serp-item div.label")'; + const res = convertScriptletToAdg(rule); + assert.equal(res, exp); +}); + +// test('Test converter scriptlet multiple abp rule', (assert) => { +// const rule = 'example.org#$#hide-if-has-and-matches-style \'d[id^="_"]\' \'div > s\' \'display: none\'; hide-if-contains /.*/ .p \'a[href^="/ad__c?"]\''; +// const exp1 = 'example.org#%#//scriptlet("abp-hide-if-has-and-matches-style", "d[id^=\\"_\\"]", "div > s", "display: none")'; +// const exp2 = 'example.org#%#//scriptlet("abp-hide-if-contains", "/.*/", ".p", "a[href^=\\"/ad__c?\\"]")'; +// const res = convertScriptletToAdg(rule); + +// assert.equal(res.length, 2); +// assert.equal(res[0], exp1); +// assert.equal(res[1], exp2); +// }); +/* eslint-enable max-len */ diff --git a/tests/lib-tests/tests.html b/tests/lib-tests/tests.html new file mode 100644 index 000000000..33b2c830a --- /dev/null +++ b/tests/lib-tests/tests.html @@ -0,0 +1,19 @@ + + + + + + Scriptlet tests + + + + +
+
+ + + + + + + diff --git a/yarn.lock b/yarn.lock index 7ff56f56c..5d0dc1f68 100644 --- a/yarn.lock +++ b/yarn.lock @@ -828,7 +828,12 @@ resolved "https://registry.yarnpkg.com/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz#8da5c6530915653f3a1f38fd5f101d8c3f8079c5" integrity sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ== -"@types/estree@*", "@types/estree@0.0.39": +"@types/estree@*": + version "0.0.42" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.42.tgz#8d0c1f480339efedb3e46070e22dd63e0430dd11" + integrity sha512-K1DPVvnBCPxzD+G51/cxVIoc2X8uUVl1zpJeE6iKcgHMj4+tbat5Xu4TjV7v2QSDbIeAfLi2hIk+u2+s0MlpUQ== + +"@types/estree@0.0.39": version "0.0.39" resolved "https://registry.yarnpkg.com/@types/estree/-/estree-0.0.39.tgz#e177e699ee1b8c22d23174caaa7422644389509f" integrity sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw== @@ -860,9 +865,9 @@ integrity sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA== "@types/node@*": - version "12.12.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.12.11.tgz#bec2961975888d964196bf0016a2f984d793d3ce" - integrity sha512-O+x6uIpa6oMNTkPuHDa9MhMMehlxLAd5QcOvKRjAFsBVpeFWTOPnXbDvILvFgFFZfQ1xh1EZi1FbXxUix+zpsQ== + version "13.1.8" + resolved "https://registry.yarnpkg.com/@types/node/-/node-13.1.8.tgz#1d590429fe8187a02707720ecf38a6fe46ce294b" + integrity sha512-6XzyyNM9EKQW4HKuzbo/CkOIjn/evtCmsU+MUM1xDfJ+3/rNjBttM1NgN7AOQvN6tP1Sl1D1PIKMreTArnxM9A== "@types/normalize-package-data@^2.4.0": version "2.4.0" @@ -955,6 +960,14 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" +anymatch@~3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.1.tgz#c55ecf02185e2469259399310c173ce31233b142" + integrity sha512-mM8522psRCqzV+6LhomX5wgp25YVibjh8Wj23I5RPkPppSVSjyKD2A2mBJmWGa+KN7f2D6LNh9jkBCeyLktzjg== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + aproba@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" @@ -1081,6 +1094,11 @@ binary-extensions@^1.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65" integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw== +binary-extensions@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.0.0.tgz#23c0df14f6a88077f5f986c0d167ec03c3d5537c" + integrity sha512-Phlt0plgpIIBOGTT/ehfFnbNlfsDEiqmzE2KRXoX1bLIlir4X/MR+zSyBEkL05ffWgnRSf/DXv+WrUAVr93/ow== + brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" @@ -1105,7 +1123,7 @@ braces@^2.3.1, braces@^2.3.2: split-string "^3.0.2" to-regex "^3.0.1" -braces@^3.0.1: +braces@^3.0.1, braces@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== @@ -1247,6 +1265,21 @@ chokidar@^2.1.8: optionalDependencies: fsevents "^1.2.7" +chokidar@^3.3.1: + version "3.3.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.3.1.tgz#c84e5b3d18d9a4d77558fef466b1bf16bbeb3450" + integrity sha512-4QYCEWOcK3OJrxwvyyAOxFuhpvOVCYkr33LPfFNBjAD/w3sEzWsp2BUOkI4l9bHvWioAd0rc6NlHUOEaWkTeqg== + dependencies: + anymatch "~3.1.1" + braces "~3.0.2" + glob-parent "~5.1.0" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.3.0" + optionalDependencies: + fsevents "~2.1.2" + chownr@^1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.1.3.tgz#42d837d5239688d55f303003a508230fa6727142" @@ -2108,6 +2141,11 @@ fsevents@^1.2.7: nan "^2.12.1" node-pre-gyp "^0.12.0" +fsevents@~2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.1.2.tgz#4c0a1fb34bc68e543b4b82a9ec392bfbda840805" + integrity sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA== + function-bind@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" @@ -2167,7 +2205,7 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0, glob-parent@^5.1.0: +glob-parent@^5.0.0, glob-parent@^5.1.0, glob-parent@~5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.0.tgz#5f4c1d1e748d30cd73ad2944b3577a81b081e8c2" integrity sha512-qjtRgnIVmOfnKUE3NJAQEdk+lKrxfw8t5ke7SXtfMTHcjsBfOfWXCQfdb30zfDoZQ2IRSIiidmjtbHZPZ++Ihw== @@ -2445,6 +2483,13 @@ is-binary-path@^1.0.0: dependencies: binary-extensions "^1.0.0" +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + is-buffer@^1.1.5: version "1.1.6" resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be" @@ -2543,7 +2588,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -3158,7 +3203,7 @@ normalize-path@^2.1.1: dependencies: remove-trailing-separator "^1.0.1" -normalize-path@^3.0.0: +normalize-path@^3.0.0, normalize-path@~3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== @@ -3490,6 +3535,11 @@ perf-regexes@^1.0.1: resolved "https://registry.yarnpkg.com/perf-regexes/-/perf-regexes-1.0.1.tgz#6da1d62f5a94bf9353a0451bccacf69068b75d0b" integrity sha512-L7MXxUDtqr4PUaLFCDCXBfGV/6KLIuSEccizDI7JxT+c9x1G1v04BQ4+4oag84SHaCdrBgQAIs/Cqn+flwFPng== +picomatch@^2.0.4, picomatch@^2.0.7: + version "2.2.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.1.tgz#21bac888b6ed8601f831ce7816e335bc779f0a4a" + integrity sha512-ISBaA8xQNmwELC7eOjqFKMESB2VIqt4PPDD0nsS95b/9dZXvVKOlz9keMSnoGGKcOHXfTvDD6WMaRoSc9UuhRA== + picomatch@^2.0.5: version "2.1.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.1.1.tgz#ecdfbea7704adb5fe6fb47f9866c4c0e15e905c5" @@ -3677,6 +3727,13 @@ readdirp@^2.2.1: micromatch "^3.1.10" readable-stream "^2.0.2" +readdirp@~3.3.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.3.0.tgz#984458d13a1e42e2e9f5841b129e162f369aff17" + integrity sha512-zz0pAkSPOXXm1viEwygWIPSPkcBYjW1xU5j/JBh5t9bGCJwa6f9+BJa6VaB2g+b55yVrmXzqkyLf4xaWYM0IkQ== + dependencies: + picomatch "^2.0.7" + regenerate-unicode-properties@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/regenerate-unicode-properties/-/regenerate-unicode-properties-8.1.0.tgz#ef51e0f0ea4ad424b77bf7cb41f3e015c70a3f0e" @@ -3892,10 +3949,10 @@ rollup-pluginutils@^2.3.3, rollup-pluginutils@^2.8.1: dependencies: estree-walker "^0.6.1" -rollup@^1.27.4: - version "1.27.4" - resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.27.4.tgz#5bca607a10c99c034288de9f9c756f9fe1a08431" - integrity sha512-UaGNOIax/Ixfd92CAAanUilx2RSkkwEfC1lCTw1eL5Re6NURWgX66ARZt5+3px4kYnpSwzyOot4r18c2b+QgJQ== +rollup@^1.29.1: + version "1.29.1" + resolved "https://registry.yarnpkg.com/rollup/-/rollup-1.29.1.tgz#8715d0a4ca439be3079f8095989ec8aa60f637bc" + integrity sha512-dGQ+b9d1FOX/gluiggTAVnTvzQZUEkCi/TwZcax7ujugVRHs0nkYJlV9U4hsifGEMojnO+jvEML2CJQ6qXgbHA== dependencies: "@types/estree" "*" "@types/node" "*" From 6091532a25a12735314e54c4a64ddca3ca24b558 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Tue, 28 Jan 2020 14:51:10 +0300 Subject: [PATCH 05/14] fix convertAbpToAdg, delete comments, up version --- README.md | 12 ++++++++++-- package.json | 3 +-- rollup.config.js | 4 ---- src/helpers/converter.js | 6 ++---- tests/lib-tests/index.test.js | 32 +++++++++++++++----------------- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/README.md b/README.md index fc0e2658a..b36eec96d 100644 --- a/README.md +++ b/README.md @@ -162,11 +162,19 @@ scriptlets.isAdgScriptletRule(rule); ```javascript /** -* Converts Ubo / Abp scriptlet rule to AdGuard +* Converts Ubo scriptlet rule to AdGuard * @param {String} rule - rule text * @returns {String} - AdGuard scriptlet rule */ scriptlets.convertUboToAdg(rule); +``` + +```javascript +/** +* Converts Abp snippet rule to AdGuard +* @param {String} rule - rule text +* @returns {Array} - AdGuard scriptlet rule or rules if Abp-rule has few snippet in one line +*/ scriptlets.convertAbpToAdg(rule); ``` @@ -174,7 +182,7 @@ scriptlets.convertAbpToAdg(rule); /** * Checks is any scriptlet rule and converts to AdGuard * @param {String} rule - rule text -* @returns {String} - AdGuard scriptlet rule +* @returns {String|Array} - AdGuard scriptlet rule (for Adg and Ubo) or array of rules (for Abp) */ scriptlets.convertScriptletToAdg(rule); ``` diff --git a/package.json b/package.json index ce84c6693..26b20ce8b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "scriptlets", - "version": "1.1.0", + "version": "1.1.1", "scripts": { "watch": "rollup -c -w", "build": "rollup -c && babel-node redirects.build.js", @@ -31,7 +31,6 @@ "@babel/plugin-transform-regenerator": "^7.7.0", "@babel/plugin-transform-runtime": "^7.6.2", "@babel/preset-env": "^7.7.1", - "chokidar": "^3.3.1", "dox": "^0.9.0", "eslint": "^6.6.0", "eslint-config-airbnb-base": "^14.0.0", diff --git a/rollup.config.js b/rollup.config.js index c5bd7d12c..257cc02b8 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -215,8 +215,4 @@ if (isDebugLib) { resultBuilds = [bundleBuild, tmpRedirectsBuild]; } -// const resultBuilds = isTest -// ? [bundleBuild, testBuild] -// : [bundleBuild, tmpRedirectsBuild]; - module.exports = resultBuilds; diff --git a/src/helpers/converter.js b/src/helpers/converter.js index 66809bed5..3ded8227f 100644 --- a/src/helpers/converter.js +++ b/src/helpers/converter.js @@ -25,7 +25,6 @@ const UBO_SCRIPTLET_MASK_1 = '##+js'; const UBO_SCRIPTLET_MASK_2 = '##script:inject'; const UBO_SCRIPTLET_EXCEPTION_MASK_1 = '#@#+js'; const UBO_SCRIPTLET_EXCEPTION_MASK_2 = '#@#script:inject'; -// const UBO_SCRIPT_TAG_MASK = '##^script'; /** * AdBlock Plus snippet rule mask @@ -50,7 +49,7 @@ const getSentences = (str) => { /** * Replace string with data by placeholders * @param {string} str - * @param {Object} data where keys is placeholdes names + * @param {Object} data - where keys are placeholders names */ const replacePlaceholders = (str, data) => { return Object.keys(data).reduce((acc, key) => { @@ -141,8 +140,7 @@ export const convertAbpToAdg = (rule) => { .map((arg, index) => (index === 0 ? `abp-${arg}` : arg)) .map((arg) => wrapInDoubleQuotes(arg)) .join(', ')) - .map((args) => replacePlaceholders(template, { domains, args })) - .toString(''); + .map((args) => replacePlaceholders(template, { domains, args })); }; /** diff --git a/tests/lib-tests/index.test.js b/tests/lib-tests/index.test.js index bde518534..2c1d477b6 100644 --- a/tests/lib-tests/index.test.js +++ b/tests/lib-tests/index.test.js @@ -8,15 +8,13 @@ const name = 'debug-current-inline-script'; module(name); -// console.log('XXXXXXXXXXXXXXXXX'); - /* eslint-disable max-len */ -// test('Test scriptlet adguard rule', (assert) => { -// const rule = "example.org#%#//scriptlet('abort-on-property-read', 'I10C')"; -// const exp = "example.org#%#//scriptlet('abort-on-property-read', 'I10C')"; -// const res = convertScriptletToAdg(rule); -// assert.equal(res, exp); -// }); +test('Test scriptlet adguard rule', (assert) => { + const rule = "example.org#%#//scriptlet('abort-on-property-read', 'I10C')"; + const exp = "example.org#%#//scriptlet('abort-on-property-read', 'I10C')"; + const res = convertScriptletToAdg(rule); + assert.equal(res, exp); +}); test('Test scriptlet adguard rule exception', (assert) => { const rule = "example.org#@%#//scriptlet('abort-on-property-read', 'I10C')"; @@ -48,14 +46,14 @@ test('Test converter scriptlet abp rule', (assert) => { assert.equal(res, exp); }); -// test('Test converter scriptlet multiple abp rule', (assert) => { -// const rule = 'example.org#$#hide-if-has-and-matches-style \'d[id^="_"]\' \'div > s\' \'display: none\'; hide-if-contains /.*/ .p \'a[href^="/ad__c?"]\''; -// const exp1 = 'example.org#%#//scriptlet("abp-hide-if-has-and-matches-style", "d[id^=\\"_\\"]", "div > s", "display: none")'; -// const exp2 = 'example.org#%#//scriptlet("abp-hide-if-contains", "/.*/", ".p", "a[href^=\\"/ad__c?\\"]")'; -// const res = convertScriptletToAdg(rule); +test('Test converter scriptlet multiple abp rule', (assert) => { + const rule = 'example.org#$#hide-if-has-and-matches-style \'d[id^="_"]\' \'div > s\' \'display: none\'; hide-if-contains /.*/ .p \'a[href^="/ad__c?"]\''; + const exp1 = 'example.org#%#//scriptlet("abp-hide-if-has-and-matches-style", "d[id^=\\"_\\"]", "div > s", "display: none")'; + const exp2 = 'example.org#%#//scriptlet("abp-hide-if-contains", "/.*/", ".p", "a[href^=\\"/ad__c?\\"]")'; + const res = convertScriptletToAdg(rule); -// assert.equal(res.length, 2); -// assert.equal(res[0], exp1); -// assert.equal(res[1], exp2); -// }); + assert.equal(res.length, 2); + assert.equal(res[0], exp1); + assert.equal(res[1], exp2); +}); /* eslint-enable max-len */ From bc32f5939d8be2e0b0c2a9059c89ad5cdee5c11b Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Wed, 29 Jan 2020 12:01:02 +0300 Subject: [PATCH 06/14] make convert methods return array --- README.md | 6 +++--- src/helpers/converter.js | 4 +--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b36eec96d..6232816d9 100644 --- a/README.md +++ b/README.md @@ -164,7 +164,7 @@ scriptlets.isAdgScriptletRule(rule); /** * Converts Ubo scriptlet rule to AdGuard * @param {String} rule - rule text -* @returns {String} - AdGuard scriptlet rule +* @returns {Array} - array with one item - AdGuard scriptlet rule */ scriptlets.convertUboToAdg(rule); ``` @@ -173,7 +173,7 @@ scriptlets.convertUboToAdg(rule); /** * Converts Abp snippet rule to AdGuard * @param {String} rule - rule text -* @returns {Array} - AdGuard scriptlet rule or rules if Abp-rule has few snippet in one line +* @returns {Array} - array with AdGuard scriptlet rule or rules if Abp-rule has few snippets in one line */ scriptlets.convertAbpToAdg(rule); ``` @@ -182,7 +182,7 @@ scriptlets.convertAbpToAdg(rule); /** * Checks is any scriptlet rule and converts to AdGuard * @param {String} rule - rule text -* @returns {String|Array} - AdGuard scriptlet rule (for Adg and Ubo) or array of rules (for Abp) +* @returns {Array} - array of AdGuard scriptlet rule - one item for Adg and Ubo or few items for Abp */ scriptlets.convertScriptletToAdg(rule); ``` diff --git a/src/helpers/converter.js b/src/helpers/converter.js index 3ded8227f..9b0908167 100644 --- a/src/helpers/converter.js +++ b/src/helpers/converter.js @@ -16,7 +16,6 @@ const ADGUARD_SCRIPTLET_TEMPLATE = '${domains}#%#//scriptlet(${args})'; // eslint-disable-next-line no-template-curly-in-string const ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE = '${domains}#@%#//scriptlet(${args})'; - /** * uBlock scriptlet rule mask */ @@ -116,7 +115,7 @@ export const convertUboToAdg = (rule) => { return replacePlaceholders( template, { domains, args }, - ); + ).split(); }; /** @@ -147,7 +146,6 @@ export const convertAbpToAdg = (rule) => { * Converts scriptlet rule to AdGuard one * @param {*} rule */ - export const convertScriptletToAdg = (rule) => { let result; if (isUboScriptletRule(rule)) { From 1fe2712056c75e295d100a5e26006bfaccc34bcb Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Wed, 29 Jan 2020 21:25:57 +0300 Subject: [PATCH 07/14] add convertAdgToUbo method and few tests for it --- README.md | 9 + dist/redirects.js | 3927 --------------------------------- dist/redirects.yml | 2 +- dist/scriptlets.corelibs.json | 2 +- dist/scriptlets.js | 406 ++-- src/helpers/converter.js | 87 +- src/helpers/parse-rule.js | 4 +- src/scriptlets/index.js | 4 +- tests/lib-tests/index.test.js | 14 +- 9 files changed, 352 insertions(+), 4103 deletions(-) delete mode 100644 dist/redirects.js diff --git a/README.md b/README.md index 6232816d9..5967dd3f2 100644 --- a/README.md +++ b/README.md @@ -187,6 +187,15 @@ scriptlets.convertAbpToAdg(rule); scriptlets.convertScriptletToAdg(rule); ``` +```javascript +/** + * Converts UBO scriptlet rule to AdGuard one + * @param {String} rule - AdGuard scriptlet rule + * @returns {String} - UBO scriptlet rule + */ +scriptlets.convertAdgToUbo(rule); +``` + #### Corelibs library `dist/scriptlets.corelibs.json` diff --git a/dist/redirects.js b/dist/redirects.js deleted file mode 100644 index 967f87593..000000000 --- a/dist/redirects.js +++ /dev/null @@ -1,3927 +0,0 @@ -var Redirects = (function () { - function _defineProperty(obj, key, value) { - if (key in obj) { - Object.defineProperty(obj, key, { - value: value, - enumerable: true, - configurable: true, - writable: true - }); - } else { - obj[key] = value; - } - - return obj; - } - - var defineProperty = _defineProperty; - - function _classCallCheck(instance, Constructor) { - if (!(instance instanceof Constructor)) { - throw new TypeError("Cannot call a class as a function"); - } - } - - var classCallCheck = _classCallCheck; - - function _defineProperties(target, props) { - for (var i = 0; i < props.length; i++) { - var descriptor = props[i]; - descriptor.enumerable = descriptor.enumerable || false; - descriptor.configurable = true; - if ("value" in descriptor) descriptor.writable = true; - Object.defineProperty(target, descriptor.key, descriptor); - } - } - - function _createClass(Constructor, protoProps, staticProps) { - if (protoProps) _defineProperties(Constructor.prototype, protoProps); - if (staticProps) _defineProperties(Constructor, staticProps); - return Constructor; - } - - var createClass = _createClass; - - function isNothing(subject) { - return (typeof subject === 'undefined') || (subject === null); - } - - - function isObject(subject) { - return (typeof subject === 'object') && (subject !== null); - } - - - function toArray(sequence) { - if (Array.isArray(sequence)) return sequence; - else if (isNothing(sequence)) return []; - - return [ sequence ]; - } - - - function extend(target, source) { - var index, length, key, sourceKeys; - - if (source) { - sourceKeys = Object.keys(source); - - for (index = 0, length = sourceKeys.length; index < length; index += 1) { - key = sourceKeys[index]; - target[key] = source[key]; - } - } - - return target; - } - - - function repeat(string, count) { - var result = '', cycle; - - for (cycle = 0; cycle < count; cycle += 1) { - result += string; - } - - return result; - } - - - function isNegativeZero(number) { - return (number === 0) && (Number.NEGATIVE_INFINITY === 1 / number); - } - - - var isNothing_1 = isNothing; - var isObject_1 = isObject; - var toArray_1 = toArray; - var repeat_1 = repeat; - var isNegativeZero_1 = isNegativeZero; - var extend_1 = extend; - - var common = { - isNothing: isNothing_1, - isObject: isObject_1, - toArray: toArray_1, - repeat: repeat_1, - isNegativeZero: isNegativeZero_1, - extend: extend_1 - }; - - // YAML error class. http://stackoverflow.com/questions/8458984 - - function YAMLException(reason, mark) { - // Super constructor - Error.call(this); - - this.name = 'YAMLException'; - this.reason = reason; - this.mark = mark; - this.message = (this.reason || '(unknown reason)') + (this.mark ? ' ' + this.mark.toString() : ''); - - // Include stack trace in error object - if (Error.captureStackTrace) { - // Chrome and NodeJS - Error.captureStackTrace(this, this.constructor); - } else { - // FF, IE 10+ and Safari 6+. Fallback for others - this.stack = (new Error()).stack || ''; - } - } - - - // Inherit from Error - YAMLException.prototype = Object.create(Error.prototype); - YAMLException.prototype.constructor = YAMLException; - - - YAMLException.prototype.toString = function toString(compact) { - var result = this.name + ': '; - - result += this.reason || '(unknown reason)'; - - if (!compact && this.mark) { - result += ' ' + this.mark.toString(); - } - - return result; - }; - - - var exception = YAMLException; - - function Mark(name, buffer, position, line, column) { - this.name = name; - this.buffer = buffer; - this.position = position; - this.line = line; - this.column = column; - } - - - Mark.prototype.getSnippet = function getSnippet(indent, maxLength) { - var head, start, tail, end, snippet; - - if (!this.buffer) return null; - - indent = indent || 4; - maxLength = maxLength || 75; - - head = ''; - start = this.position; - - while (start > 0 && '\x00\r\n\x85\u2028\u2029'.indexOf(this.buffer.charAt(start - 1)) === -1) { - start -= 1; - if (this.position - start > (maxLength / 2 - 1)) { - head = ' ... '; - start += 5; - break; - } - } - - tail = ''; - end = this.position; - - while (end < this.buffer.length && '\x00\r\n\x85\u2028\u2029'.indexOf(this.buffer.charAt(end)) === -1) { - end += 1; - if (end - this.position > (maxLength / 2 - 1)) { - tail = ' ... '; - end -= 5; - break; - } - } - - snippet = this.buffer.slice(start, end); - - return common.repeat(' ', indent) + head + snippet + tail + '\n' + - common.repeat(' ', indent + this.position - start + head.length) + '^'; - }; - - - Mark.prototype.toString = function toString(compact) { - var snippet, where = ''; - - if (this.name) { - where += 'in "' + this.name + '" '; - } - - where += 'at line ' + (this.line + 1) + ', column ' + (this.column + 1); - - if (!compact) { - snippet = this.getSnippet(); - - if (snippet) { - where += ':\n' + snippet; - } - } - - return where; - }; - - - var mark = Mark; - - var TYPE_CONSTRUCTOR_OPTIONS = [ - 'kind', - 'resolve', - 'construct', - 'instanceOf', - 'predicate', - 'represent', - 'defaultStyle', - 'styleAliases' - ]; - - var YAML_NODE_KINDS = [ - 'scalar', - 'sequence', - 'mapping' - ]; - - function compileStyleAliases(map) { - var result = {}; - - if (map !== null) { - Object.keys(map).forEach(function (style) { - map[style].forEach(function (alias) { - result[String(alias)] = style; - }); - }); - } - - return result; - } - - function Type(tag, options) { - options = options || {}; - - Object.keys(options).forEach(function (name) { - if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) { - throw new exception('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.'); - } - }); - - // TODO: Add tag format check. - this.tag = tag; - this.kind = options['kind'] || null; - this.resolve = options['resolve'] || function () { return true; }; - this.construct = options['construct'] || function (data) { return data; }; - this.instanceOf = options['instanceOf'] || null; - this.predicate = options['predicate'] || null; - this.represent = options['represent'] || null; - this.defaultStyle = options['defaultStyle'] || null; - this.styleAliases = compileStyleAliases(options['styleAliases'] || null); - - if (YAML_NODE_KINDS.indexOf(this.kind) === -1) { - throw new exception('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.'); - } - } - - var type = Type; - - /*eslint-disable max-len*/ - - - - - - - function compileList(schema, name, result) { - var exclude = []; - - schema.include.forEach(function (includedSchema) { - result = compileList(includedSchema, name, result); - }); - - schema[name].forEach(function (currentType) { - result.forEach(function (previousType, previousIndex) { - if (previousType.tag === currentType.tag && previousType.kind === currentType.kind) { - exclude.push(previousIndex); - } - }); - - result.push(currentType); - }); - - return result.filter(function (type, index) { - return exclude.indexOf(index) === -1; - }); - } - - - function compileMap(/* lists... */) { - var result = { - scalar: {}, - sequence: {}, - mapping: {}, - fallback: {} - }, index, length; - - function collectType(type) { - result[type.kind][type.tag] = result['fallback'][type.tag] = type; - } - - for (index = 0, length = arguments.length; index < length; index += 1) { - arguments[index].forEach(collectType); - } - return result; - } - - - function Schema(definition) { - this.include = definition.include || []; - this.implicit = definition.implicit || []; - this.explicit = definition.explicit || []; - - this.implicit.forEach(function (type) { - if (type.loadKind && type.loadKind !== 'scalar') { - throw new exception('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.'); - } - }); - - this.compiledImplicit = compileList(this, 'implicit', []); - this.compiledExplicit = compileList(this, 'explicit', []); - this.compiledTypeMap = compileMap(this.compiledImplicit, this.compiledExplicit); - } - - - Schema.DEFAULT = null; - - - Schema.create = function createSchema() { - var schemas, types; - - switch (arguments.length) { - case 1: - schemas = Schema.DEFAULT; - types = arguments[0]; - break; - - case 2: - schemas = arguments[0]; - types = arguments[1]; - break; - - default: - throw new exception('Wrong number of arguments for Schema.create function'); - } - - schemas = common.toArray(schemas); - types = common.toArray(types); - - if (!schemas.every(function (schema) { return schema instanceof Schema; })) { - throw new exception('Specified list of super schemas (or a single Schema object) contains a non-Schema object.'); - } - - if (!types.every(function (type$1) { return type$1 instanceof type; })) { - throw new exception('Specified list of YAML types (or a single Type object) contains a non-Type object.'); - } - - return new Schema({ - include: schemas, - explicit: types - }); - }; - - - var schema = Schema; - - var str = new type('tag:yaml.org,2002:str', { - kind: 'scalar', - construct: function (data) { return data !== null ? data : ''; } - }); - - var seq = new type('tag:yaml.org,2002:seq', { - kind: 'sequence', - construct: function (data) { return data !== null ? data : []; } - }); - - var map = new type('tag:yaml.org,2002:map', { - kind: 'mapping', - construct: function (data) { return data !== null ? data : {}; } - }); - - var failsafe = new schema({ - explicit: [ - str, - seq, - map - ] - }); - - function resolveYamlNull(data) { - if (data === null) return true; - - var max = data.length; - - return (max === 1 && data === '~') || - (max === 4 && (data === 'null' || data === 'Null' || data === 'NULL')); - } - - function constructYamlNull() { - return null; - } - - function isNull(object) { - return object === null; - } - - var _null = new type('tag:yaml.org,2002:null', { - kind: 'scalar', - resolve: resolveYamlNull, - construct: constructYamlNull, - predicate: isNull, - represent: { - canonical: function () { return '~'; }, - lowercase: function () { return 'null'; }, - uppercase: function () { return 'NULL'; }, - camelcase: function () { return 'Null'; } - }, - defaultStyle: 'lowercase' - }); - - function resolveYamlBoolean(data) { - if (data === null) return false; - - var max = data.length; - - return (max === 4 && (data === 'true' || data === 'True' || data === 'TRUE')) || - (max === 5 && (data === 'false' || data === 'False' || data === 'FALSE')); - } - - function constructYamlBoolean(data) { - return data === 'true' || - data === 'True' || - data === 'TRUE'; - } - - function isBoolean(object) { - return Object.prototype.toString.call(object) === '[object Boolean]'; - } - - var bool = new type('tag:yaml.org,2002:bool', { - kind: 'scalar', - resolve: resolveYamlBoolean, - construct: constructYamlBoolean, - predicate: isBoolean, - represent: { - lowercase: function (object) { return object ? 'true' : 'false'; }, - uppercase: function (object) { return object ? 'TRUE' : 'FALSE'; }, - camelcase: function (object) { return object ? 'True' : 'False'; } - }, - defaultStyle: 'lowercase' - }); - - function isHexCode(c) { - return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) || - ((0x41/* A */ <= c) && (c <= 0x46/* F */)) || - ((0x61/* a */ <= c) && (c <= 0x66/* f */)); - } - - function isOctCode(c) { - return ((0x30/* 0 */ <= c) && (c <= 0x37/* 7 */)); - } - - function isDecCode(c) { - return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)); - } - - function resolveYamlInteger(data) { - if (data === null) return false; - - var max = data.length, - index = 0, - hasDigits = false, - ch; - - if (!max) return false; - - ch = data[index]; - - // sign - if (ch === '-' || ch === '+') { - ch = data[++index]; - } - - if (ch === '0') { - // 0 - if (index + 1 === max) return true; - ch = data[++index]; - - // base 2, base 8, base 16 - - if (ch === 'b') { - // base 2 - index++; - - for (; index < max; index++) { - ch = data[index]; - if (ch === '_') continue; - if (ch !== '0' && ch !== '1') return false; - hasDigits = true; - } - return hasDigits && ch !== '_'; - } - - - if (ch === 'x') { - // base 16 - index++; - - for (; index < max; index++) { - ch = data[index]; - if (ch === '_') continue; - if (!isHexCode(data.charCodeAt(index))) return false; - hasDigits = true; - } - return hasDigits && ch !== '_'; - } - - // base 8 - for (; index < max; index++) { - ch = data[index]; - if (ch === '_') continue; - if (!isOctCode(data.charCodeAt(index))) return false; - hasDigits = true; - } - return hasDigits && ch !== '_'; - } - - // base 10 (except 0) or base 60 - - // value should not start with `_`; - if (ch === '_') return false; - - for (; index < max; index++) { - ch = data[index]; - if (ch === '_') continue; - if (ch === ':') break; - if (!isDecCode(data.charCodeAt(index))) { - return false; - } - hasDigits = true; - } - - // Should have digits and should not end with `_` - if (!hasDigits || ch === '_') return false; - - // if !base60 - done; - if (ch !== ':') return true; - - // base60 almost not used, no needs to optimize - return /^(:[0-5]?[0-9])+$/.test(data.slice(index)); - } - - function constructYamlInteger(data) { - var value = data, sign = 1, ch, base, digits = []; - - if (value.indexOf('_') !== -1) { - value = value.replace(/_/g, ''); - } - - ch = value[0]; - - if (ch === '-' || ch === '+') { - if (ch === '-') sign = -1; - value = value.slice(1); - ch = value[0]; - } - - if (value === '0') return 0; - - if (ch === '0') { - if (value[1] === 'b') return sign * parseInt(value.slice(2), 2); - if (value[1] === 'x') return sign * parseInt(value, 16); - return sign * parseInt(value, 8); - } - - if (value.indexOf(':') !== -1) { - value.split(':').forEach(function (v) { - digits.unshift(parseInt(v, 10)); - }); - - value = 0; - base = 1; - - digits.forEach(function (d) { - value += (d * base); - base *= 60; - }); - - return sign * value; - - } - - return sign * parseInt(value, 10); - } - - function isInteger(object) { - return (Object.prototype.toString.call(object)) === '[object Number]' && - (object % 1 === 0 && !common.isNegativeZero(object)); - } - - var int_1 = new type('tag:yaml.org,2002:int', { - kind: 'scalar', - resolve: resolveYamlInteger, - construct: constructYamlInteger, - predicate: isInteger, - represent: { - binary: function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1); }, - octal: function (obj) { return obj >= 0 ? '0' + obj.toString(8) : '-0' + obj.toString(8).slice(1); }, - decimal: function (obj) { return obj.toString(10); }, - /* eslint-disable max-len */ - hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() : '-0x' + obj.toString(16).toUpperCase().slice(1); } - }, - defaultStyle: 'decimal', - styleAliases: { - binary: [ 2, 'bin' ], - octal: [ 8, 'oct' ], - decimal: [ 10, 'dec' ], - hexadecimal: [ 16, 'hex' ] - } - }); - - var YAML_FLOAT_PATTERN = new RegExp( - // 2.5e4, 2.5 and integers - '^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?' + - // .2e4, .2 - // special case, seems not from spec - '|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?' + - // 20:59 - '|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*' + - // .inf - '|[-+]?\\.(?:inf|Inf|INF)' + - // .nan - '|\\.(?:nan|NaN|NAN))$'); - - function resolveYamlFloat(data) { - if (data === null) return false; - - if (!YAML_FLOAT_PATTERN.test(data) || - // Quick hack to not allow integers end with `_` - // Probably should update regexp & check speed - data[data.length - 1] === '_') { - return false; - } - - return true; - } - - function constructYamlFloat(data) { - var value, sign, base, digits; - - value = data.replace(/_/g, '').toLowerCase(); - sign = value[0] === '-' ? -1 : 1; - digits = []; - - if ('+-'.indexOf(value[0]) >= 0) { - value = value.slice(1); - } - - if (value === '.inf') { - return (sign === 1) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; - - } else if (value === '.nan') { - return NaN; - - } else if (value.indexOf(':') >= 0) { - value.split(':').forEach(function (v) { - digits.unshift(parseFloat(v, 10)); - }); - - value = 0.0; - base = 1; - - digits.forEach(function (d) { - value += d * base; - base *= 60; - }); - - return sign * value; - - } - return sign * parseFloat(value, 10); - } - - - var SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/; - - function representYamlFloat(object, style) { - var res; - - if (isNaN(object)) { - switch (style) { - case 'lowercase': return '.nan'; - case 'uppercase': return '.NAN'; - case 'camelcase': return '.NaN'; - } - } else if (Number.POSITIVE_INFINITY === object) { - switch (style) { - case 'lowercase': return '.inf'; - case 'uppercase': return '.INF'; - case 'camelcase': return '.Inf'; - } - } else if (Number.NEGATIVE_INFINITY === object) { - switch (style) { - case 'lowercase': return '-.inf'; - case 'uppercase': return '-.INF'; - case 'camelcase': return '-.Inf'; - } - } else if (common.isNegativeZero(object)) { - return '-0.0'; - } - - res = object.toString(10); - - // JS stringifier can build scientific format without dots: 5e-100, - // while YAML requres dot: 5.e-100. Fix it with simple hack - - return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res; - } - - function isFloat(object) { - return (Object.prototype.toString.call(object) === '[object Number]') && - (object % 1 !== 0 || common.isNegativeZero(object)); - } - - var float_1 = new type('tag:yaml.org,2002:float', { - kind: 'scalar', - resolve: resolveYamlFloat, - construct: constructYamlFloat, - predicate: isFloat, - represent: representYamlFloat, - defaultStyle: 'lowercase' - }); - - var json = new schema({ - include: [ - failsafe - ], - implicit: [ - _null, - bool, - int_1, - float_1 - ] - }); - - var core = new schema({ - include: [ - json - ] - }); - - var YAML_DATE_REGEXP = new RegExp( - '^([0-9][0-9][0-9][0-9])' + // [1] year - '-([0-9][0-9])' + // [2] month - '-([0-9][0-9])$'); // [3] day - - var YAML_TIMESTAMP_REGEXP = new RegExp( - '^([0-9][0-9][0-9][0-9])' + // [1] year - '-([0-9][0-9]?)' + // [2] month - '-([0-9][0-9]?)' + // [3] day - '(?:[Tt]|[ \\t]+)' + // ... - '([0-9][0-9]?)' + // [4] hour - ':([0-9][0-9])' + // [5] minute - ':([0-9][0-9])' + // [6] second - '(?:\\.([0-9]*))?' + // [7] fraction - '(?:[ \\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tz_hour - '(?::([0-9][0-9]))?))?$'); // [11] tz_minute - - function resolveYamlTimestamp(data) { - if (data === null) return false; - if (YAML_DATE_REGEXP.exec(data) !== null) return true; - if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true; - return false; - } - - function constructYamlTimestamp(data) { - var match, year, month, day, hour, minute, second, fraction = 0, - delta = null, tz_hour, tz_minute, date; - - match = YAML_DATE_REGEXP.exec(data); - if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data); - - if (match === null) throw new Error('Date resolve error'); - - // match: [1] year [2] month [3] day - - year = +(match[1]); - month = +(match[2]) - 1; // JS month starts with 0 - day = +(match[3]); - - if (!match[4]) { // no hour - return new Date(Date.UTC(year, month, day)); - } - - // match: [4] hour [5] minute [6] second [7] fraction - - hour = +(match[4]); - minute = +(match[5]); - second = +(match[6]); - - if (match[7]) { - fraction = match[7].slice(0, 3); - while (fraction.length < 3) { // milli-seconds - fraction += '0'; - } - fraction = +fraction; - } - - // match: [8] tz [9] tz_sign [10] tz_hour [11] tz_minute - - if (match[9]) { - tz_hour = +(match[10]); - tz_minute = +(match[11] || 0); - delta = (tz_hour * 60 + tz_minute) * 60000; // delta in mili-seconds - if (match[9] === '-') delta = -delta; - } - - date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction)); - - if (delta) date.setTime(date.getTime() - delta); - - return date; - } - - function representYamlTimestamp(object /*, style*/) { - return object.toISOString(); - } - - var timestamp = new type('tag:yaml.org,2002:timestamp', { - kind: 'scalar', - resolve: resolveYamlTimestamp, - construct: constructYamlTimestamp, - instanceOf: Date, - represent: representYamlTimestamp - }); - - function resolveYamlMerge(data) { - return data === '<<' || data === null; - } - - var merge = new type('tag:yaml.org,2002:merge', { - kind: 'scalar', - resolve: resolveYamlMerge - }); - - function commonjsRequire () { - throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs'); - } - - /*eslint-disable no-bitwise*/ - - var NodeBuffer; - - try { - // A trick for browserified version, to not include `Buffer` shim - var _require = commonjsRequire; - NodeBuffer = _require('buffer').Buffer; - } catch (__) {} - - - - - // [ 64, 65, 66 ] -> [ padding, CR, LF ] - var BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r'; - - - function resolveYamlBinary(data) { - if (data === null) return false; - - var code, idx, bitlen = 0, max = data.length, map = BASE64_MAP; - - // Convert one by one. - for (idx = 0; idx < max; idx++) { - code = map.indexOf(data.charAt(idx)); - - // Skip CR/LF - if (code > 64) continue; - - // Fail on illegal characters - if (code < 0) return false; - - bitlen += 6; - } - - // If there are any bits left, source was corrupted - return (bitlen % 8) === 0; - } - - function constructYamlBinary(data) { - var idx, tailbits, - input = data.replace(/[\r\n=]/g, ''), // remove CR/LF & padding to simplify scan - max = input.length, - map = BASE64_MAP, - bits = 0, - result = []; - - // Collect by 6*4 bits (3 bytes) - - for (idx = 0; idx < max; idx++) { - if ((idx % 4 === 0) && idx) { - result.push((bits >> 16) & 0xFF); - result.push((bits >> 8) & 0xFF); - result.push(bits & 0xFF); - } - - bits = (bits << 6) | map.indexOf(input.charAt(idx)); - } - - // Dump tail - - tailbits = (max % 4) * 6; - - if (tailbits === 0) { - result.push((bits >> 16) & 0xFF); - result.push((bits >> 8) & 0xFF); - result.push(bits & 0xFF); - } else if (tailbits === 18) { - result.push((bits >> 10) & 0xFF); - result.push((bits >> 2) & 0xFF); - } else if (tailbits === 12) { - result.push((bits >> 4) & 0xFF); - } - - // Wrap into Buffer for NodeJS and leave Array for browser - if (NodeBuffer) { - // Support node 6.+ Buffer API when available - return NodeBuffer.from ? NodeBuffer.from(result) : new NodeBuffer(result); - } - - return result; - } - - function representYamlBinary(object /*, style*/) { - var result = '', bits = 0, idx, tail, - max = object.length, - map = BASE64_MAP; - - // Convert every three bytes to 4 ASCII characters. - - for (idx = 0; idx < max; idx++) { - if ((idx % 3 === 0) && idx) { - result += map[(bits >> 18) & 0x3F]; - result += map[(bits >> 12) & 0x3F]; - result += map[(bits >> 6) & 0x3F]; - result += map[bits & 0x3F]; - } - - bits = (bits << 8) + object[idx]; - } - - // Dump tail - - tail = max % 3; - - if (tail === 0) { - result += map[(bits >> 18) & 0x3F]; - result += map[(bits >> 12) & 0x3F]; - result += map[(bits >> 6) & 0x3F]; - result += map[bits & 0x3F]; - } else if (tail === 2) { - result += map[(bits >> 10) & 0x3F]; - result += map[(bits >> 4) & 0x3F]; - result += map[(bits << 2) & 0x3F]; - result += map[64]; - } else if (tail === 1) { - result += map[(bits >> 2) & 0x3F]; - result += map[(bits << 4) & 0x3F]; - result += map[64]; - result += map[64]; - } - - return result; - } - - function isBinary(object) { - return NodeBuffer && NodeBuffer.isBuffer(object); - } - - var binary = new type('tag:yaml.org,2002:binary', { - kind: 'scalar', - resolve: resolveYamlBinary, - construct: constructYamlBinary, - predicate: isBinary, - represent: representYamlBinary - }); - - var _hasOwnProperty = Object.prototype.hasOwnProperty; - var _toString = Object.prototype.toString; - - function resolveYamlOmap(data) { - if (data === null) return true; - - var objectKeys = [], index, length, pair, pairKey, pairHasKey, - object = data; - - for (index = 0, length = object.length; index < length; index += 1) { - pair = object[index]; - pairHasKey = false; - - if (_toString.call(pair) !== '[object Object]') return false; - - for (pairKey in pair) { - if (_hasOwnProperty.call(pair, pairKey)) { - if (!pairHasKey) pairHasKey = true; - else return false; - } - } - - if (!pairHasKey) return false; - - if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey); - else return false; - } - - return true; - } - - function constructYamlOmap(data) { - return data !== null ? data : []; - } - - var omap = new type('tag:yaml.org,2002:omap', { - kind: 'sequence', - resolve: resolveYamlOmap, - construct: constructYamlOmap - }); - - var _toString$1 = Object.prototype.toString; - - function resolveYamlPairs(data) { - if (data === null) return true; - - var index, length, pair, keys, result, - object = data; - - result = new Array(object.length); - - for (index = 0, length = object.length; index < length; index += 1) { - pair = object[index]; - - if (_toString$1.call(pair) !== '[object Object]') return false; - - keys = Object.keys(pair); - - if (keys.length !== 1) return false; - - result[index] = [ keys[0], pair[keys[0]] ]; - } - - return true; - } - - function constructYamlPairs(data) { - if (data === null) return []; - - var index, length, pair, keys, result, - object = data; - - result = new Array(object.length); - - for (index = 0, length = object.length; index < length; index += 1) { - pair = object[index]; - - keys = Object.keys(pair); - - result[index] = [ keys[0], pair[keys[0]] ]; - } - - return result; - } - - var pairs = new type('tag:yaml.org,2002:pairs', { - kind: 'sequence', - resolve: resolveYamlPairs, - construct: constructYamlPairs - }); - - var _hasOwnProperty$1 = Object.prototype.hasOwnProperty; - - function resolveYamlSet(data) { - if (data === null) return true; - - var key, object = data; - - for (key in object) { - if (_hasOwnProperty$1.call(object, key)) { - if (object[key] !== null) return false; - } - } - - return true; - } - - function constructYamlSet(data) { - return data !== null ? data : {}; - } - - var set = new type('tag:yaml.org,2002:set', { - kind: 'mapping', - resolve: resolveYamlSet, - construct: constructYamlSet - }); - - var default_safe = new schema({ - include: [ - core - ], - implicit: [ - timestamp, - merge - ], - explicit: [ - binary, - omap, - pairs, - set - ] - }); - - function resolveJavascriptUndefined() { - return true; - } - - function constructJavascriptUndefined() { - /*eslint-disable no-undefined*/ - return undefined; - } - - function representJavascriptUndefined() { - return ''; - } - - function isUndefined(object) { - return typeof object === 'undefined'; - } - - var _undefined = new type('tag:yaml.org,2002:js/undefined', { - kind: 'scalar', - resolve: resolveJavascriptUndefined, - construct: constructJavascriptUndefined, - predicate: isUndefined, - represent: representJavascriptUndefined - }); - - function resolveJavascriptRegExp(data) { - if (data === null) return false; - if (data.length === 0) return false; - - var regexp = data, - tail = /\/([gim]*)$/.exec(data), - modifiers = ''; - - // if regexp starts with '/' it can have modifiers and must be properly closed - // `/foo/gim` - modifiers tail can be maximum 3 chars - if (regexp[0] === '/') { - if (tail) modifiers = tail[1]; - - if (modifiers.length > 3) return false; - // if expression starts with /, is should be properly terminated - if (regexp[regexp.length - modifiers.length - 1] !== '/') return false; - } - - return true; - } - - function constructJavascriptRegExp(data) { - var regexp = data, - tail = /\/([gim]*)$/.exec(data), - modifiers = ''; - - // `/foo/gim` - tail can be maximum 4 chars - if (regexp[0] === '/') { - if (tail) modifiers = tail[1]; - regexp = regexp.slice(1, regexp.length - modifiers.length - 1); - } - - return new RegExp(regexp, modifiers); - } - - function representJavascriptRegExp(object /*, style*/) { - var result = '/' + object.source + '/'; - - if (object.global) result += 'g'; - if (object.multiline) result += 'm'; - if (object.ignoreCase) result += 'i'; - - return result; - } - - function isRegExp(object) { - return Object.prototype.toString.call(object) === '[object RegExp]'; - } - - var regexp = new type('tag:yaml.org,2002:js/regexp', { - kind: 'scalar', - resolve: resolveJavascriptRegExp, - construct: constructJavascriptRegExp, - predicate: isRegExp, - represent: representJavascriptRegExp - }); - - var esprima; - - // Browserified version does not have esprima - // - // 1. For node.js just require module as deps - // 2. For browser try to require mudule via external AMD system. - // If not found - try to fallback to window.esprima. If not - // found too - then fail to parse. - // - try { - // workaround to exclude package from browserify list. - var _require$1 = commonjsRequire; - esprima = _require$1('esprima'); - } catch (_) { - /*global window */ - if (typeof window !== 'undefined') esprima = window.esprima; - } - - - - function resolveJavascriptFunction(data) { - if (data === null) return false; - - try { - var source = '(' + data + ')', - ast = esprima.parse(source, { range: true }); - - if (ast.type !== 'Program' || - ast.body.length !== 1 || - ast.body[0].type !== 'ExpressionStatement' || - (ast.body[0].expression.type !== 'ArrowFunctionExpression' && - ast.body[0].expression.type !== 'FunctionExpression')) { - return false; - } - - return true; - } catch (err) { - return false; - } - } - - function constructJavascriptFunction(data) { - /*jslint evil:true*/ - - var source = '(' + data + ')', - ast = esprima.parse(source, { range: true }), - params = [], - body; - - if (ast.type !== 'Program' || - ast.body.length !== 1 || - ast.body[0].type !== 'ExpressionStatement' || - (ast.body[0].expression.type !== 'ArrowFunctionExpression' && - ast.body[0].expression.type !== 'FunctionExpression')) { - throw new Error('Failed to resolve function'); - } - - ast.body[0].expression.params.forEach(function (param) { - params.push(param.name); - }); - - body = ast.body[0].expression.body.range; - - // Esprima's ranges include the first '{' and the last '}' characters on - // function expressions. So cut them out. - if (ast.body[0].expression.body.type === 'BlockStatement') { - /*eslint-disable no-new-func*/ - return new Function(params, source.slice(body[0] + 1, body[1] - 1)); - } - // ES6 arrow functions can omit the BlockStatement. In that case, just return - // the body. - /*eslint-disable no-new-func*/ - return new Function(params, 'return ' + source.slice(body[0], body[1])); - } - - function representJavascriptFunction(object /*, style*/) { - return object.toString(); - } - - function isFunction(object) { - return Object.prototype.toString.call(object) === '[object Function]'; - } - - var _function = new type('tag:yaml.org,2002:js/function', { - kind: 'scalar', - resolve: resolveJavascriptFunction, - construct: constructJavascriptFunction, - predicate: isFunction, - represent: representJavascriptFunction - }); - - var default_full = schema.DEFAULT = new schema({ - include: [ - default_safe - ], - explicit: [ - _undefined, - regexp, - _function - ] - }); - - /*eslint-disable max-len,no-use-before-define*/ - - - - - - - - - var _hasOwnProperty$2 = Object.prototype.hasOwnProperty; - - - var CONTEXT_FLOW_IN = 1; - var CONTEXT_FLOW_OUT = 2; - var CONTEXT_BLOCK_IN = 3; - var CONTEXT_BLOCK_OUT = 4; - - - var CHOMPING_CLIP = 1; - var CHOMPING_STRIP = 2; - var CHOMPING_KEEP = 3; - - - var PATTERN_NON_PRINTABLE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; - var PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/; - var PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/; - var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; - var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; - - - function _class(obj) { return Object.prototype.toString.call(obj); } - - function is_EOL(c) { - return (c === 0x0A/* LF */) || (c === 0x0D/* CR */); - } - - function is_WHITE_SPACE(c) { - return (c === 0x09/* Tab */) || (c === 0x20/* Space */); - } - - function is_WS_OR_EOL(c) { - return (c === 0x09/* Tab */) || - (c === 0x20/* Space */) || - (c === 0x0A/* LF */) || - (c === 0x0D/* CR */); - } - - function is_FLOW_INDICATOR(c) { - return c === 0x2C/* , */ || - c === 0x5B/* [ */ || - c === 0x5D/* ] */ || - c === 0x7B/* { */ || - c === 0x7D/* } */; - } - - function fromHexCode(c) { - var lc; - - if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) { - return c - 0x30; - } - - /*eslint-disable no-bitwise*/ - lc = c | 0x20; - - if ((0x61/* a */ <= lc) && (lc <= 0x66/* f */)) { - return lc - 0x61 + 10; - } - - return -1; - } - - function escapedHexLen(c) { - if (c === 0x78/* x */) { return 2; } - if (c === 0x75/* u */) { return 4; } - if (c === 0x55/* U */) { return 8; } - return 0; - } - - function fromDecimalCode(c) { - if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) { - return c - 0x30; - } - - return -1; - } - - function simpleEscapeSequence(c) { - /* eslint-disable indent */ - return (c === 0x30/* 0 */) ? '\x00' : - (c === 0x61/* a */) ? '\x07' : - (c === 0x62/* b */) ? '\x08' : - (c === 0x74/* t */) ? '\x09' : - (c === 0x09/* Tab */) ? '\x09' : - (c === 0x6E/* n */) ? '\x0A' : - (c === 0x76/* v */) ? '\x0B' : - (c === 0x66/* f */) ? '\x0C' : - (c === 0x72/* r */) ? '\x0D' : - (c === 0x65/* e */) ? '\x1B' : - (c === 0x20/* Space */) ? ' ' : - (c === 0x22/* " */) ? '\x22' : - (c === 0x2F/* / */) ? '/' : - (c === 0x5C/* \ */) ? '\x5C' : - (c === 0x4E/* N */) ? '\x85' : - (c === 0x5F/* _ */) ? '\xA0' : - (c === 0x4C/* L */) ? '\u2028' : - (c === 0x50/* P */) ? '\u2029' : ''; - } - - function charFromCodepoint(c) { - if (c <= 0xFFFF) { - return String.fromCharCode(c); - } - // Encode UTF-16 surrogate pair - // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF - return String.fromCharCode( - ((c - 0x010000) >> 10) + 0xD800, - ((c - 0x010000) & 0x03FF) + 0xDC00 - ); - } - - var simpleEscapeCheck = new Array(256); // integer, for fast access - var simpleEscapeMap = new Array(256); - for (var i = 0; i < 256; i++) { - simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0; - simpleEscapeMap[i] = simpleEscapeSequence(i); - } - - - function State(input, options) { - this.input = input; - - this.filename = options['filename'] || null; - this.schema = options['schema'] || default_full; - this.onWarning = options['onWarning'] || null; - this.legacy = options['legacy'] || false; - this.json = options['json'] || false; - this.listener = options['listener'] || null; - - this.implicitTypes = this.schema.compiledImplicit; - this.typeMap = this.schema.compiledTypeMap; - - this.length = input.length; - this.position = 0; - this.line = 0; - this.lineStart = 0; - this.lineIndent = 0; - - this.documents = []; - - /* - this.version; - this.checkLineBreaks; - this.tagMap; - this.anchorMap; - this.tag; - this.anchor; - this.kind; - this.result;*/ - - } - - - function generateError(state, message) { - return new exception( - message, - new mark(state.filename, state.input, state.position, state.line, (state.position - state.lineStart))); - } - - function throwError(state, message) { - throw generateError(state, message); - } - - function throwWarning(state, message) { - if (state.onWarning) { - state.onWarning.call(null, generateError(state, message)); - } - } - - - var directiveHandlers = { - - YAML: function handleYamlDirective(state, name, args) { - - var match, major, minor; - - if (state.version !== null) { - throwError(state, 'duplication of %YAML directive'); - } - - if (args.length !== 1) { - throwError(state, 'YAML directive accepts exactly one argument'); - } - - match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]); - - if (match === null) { - throwError(state, 'ill-formed argument of the YAML directive'); - } - - major = parseInt(match[1], 10); - minor = parseInt(match[2], 10); - - if (major !== 1) { - throwError(state, 'unacceptable YAML version of the document'); - } - - state.version = args[0]; - state.checkLineBreaks = (minor < 2); - - if (minor !== 1 && minor !== 2) { - throwWarning(state, 'unsupported YAML version of the document'); - } - }, - - TAG: function handleTagDirective(state, name, args) { - - var handle, prefix; - - if (args.length !== 2) { - throwError(state, 'TAG directive accepts exactly two arguments'); - } - - handle = args[0]; - prefix = args[1]; - - if (!PATTERN_TAG_HANDLE.test(handle)) { - throwError(state, 'ill-formed tag handle (first argument) of the TAG directive'); - } - - if (_hasOwnProperty$2.call(state.tagMap, handle)) { - throwError(state, 'there is a previously declared suffix for "' + handle + '" tag handle'); - } - - if (!PATTERN_TAG_URI.test(prefix)) { - throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive'); - } - - state.tagMap[handle] = prefix; - } - }; - - - function captureSegment(state, start, end, checkJson) { - var _position, _length, _character, _result; - - if (start < end) { - _result = state.input.slice(start, end); - - if (checkJson) { - for (_position = 0, _length = _result.length; _position < _length; _position += 1) { - _character = _result.charCodeAt(_position); - if (!(_character === 0x09 || - (0x20 <= _character && _character <= 0x10FFFF))) { - throwError(state, 'expected valid JSON character'); - } - } - } else if (PATTERN_NON_PRINTABLE.test(_result)) { - throwError(state, 'the stream contains non-printable characters'); - } - - state.result += _result; - } - } - - function mergeMappings(state, destination, source, overridableKeys) { - var sourceKeys, key, index, quantity; - - if (!common.isObject(source)) { - throwError(state, 'cannot merge mappings; the provided source object is unacceptable'); - } - - sourceKeys = Object.keys(source); - - for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) { - key = sourceKeys[index]; - - if (!_hasOwnProperty$2.call(destination, key)) { - destination[key] = source[key]; - overridableKeys[key] = true; - } - } - } - - function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, startLine, startPos) { - var index, quantity; - - // The output is a plain object here, so keys can only be strings. - // We need to convert keyNode to a string, but doing so can hang the process - // (deeply nested arrays that explode exponentially using aliases). - if (Array.isArray(keyNode)) { - keyNode = Array.prototype.slice.call(keyNode); - - for (index = 0, quantity = keyNode.length; index < quantity; index += 1) { - if (Array.isArray(keyNode[index])) { - throwError(state, 'nested arrays are not supported inside keys'); - } - - if (typeof keyNode === 'object' && _class(keyNode[index]) === '[object Object]') { - keyNode[index] = '[object Object]'; - } - } - } - - // Avoid code execution in load() via toString property - // (still use its own toString for arrays, timestamps, - // and whatever user schema extensions happen to have @@toStringTag) - if (typeof keyNode === 'object' && _class(keyNode) === '[object Object]') { - keyNode = '[object Object]'; - } - - - keyNode = String(keyNode); - - if (_result === null) { - _result = {}; - } - - if (keyTag === 'tag:yaml.org,2002:merge') { - if (Array.isArray(valueNode)) { - for (index = 0, quantity = valueNode.length; index < quantity; index += 1) { - mergeMappings(state, _result, valueNode[index], overridableKeys); - } - } else { - mergeMappings(state, _result, valueNode, overridableKeys); - } - } else { - if (!state.json && - !_hasOwnProperty$2.call(overridableKeys, keyNode) && - _hasOwnProperty$2.call(_result, keyNode)) { - state.line = startLine || state.line; - state.position = startPos || state.position; - throwError(state, 'duplicated mapping key'); - } - _result[keyNode] = valueNode; - delete overridableKeys[keyNode]; - } - - return _result; - } - - function readLineBreak(state) { - var ch; - - ch = state.input.charCodeAt(state.position); - - if (ch === 0x0A/* LF */) { - state.position++; - } else if (ch === 0x0D/* CR */) { - state.position++; - if (state.input.charCodeAt(state.position) === 0x0A/* LF */) { - state.position++; - } - } else { - throwError(state, 'a line break is expected'); - } - - state.line += 1; - state.lineStart = state.position; - } - - function skipSeparationSpace(state, allowComments, checkIndent) { - var lineBreaks = 0, - ch = state.input.charCodeAt(state.position); - - while (ch !== 0) { - while (is_WHITE_SPACE(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (allowComments && ch === 0x23/* # */) { - do { - ch = state.input.charCodeAt(++state.position); - } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && ch !== 0); - } - - if (is_EOL(ch)) { - readLineBreak(state); - - ch = state.input.charCodeAt(state.position); - lineBreaks++; - state.lineIndent = 0; - - while (ch === 0x20/* Space */) { - state.lineIndent++; - ch = state.input.charCodeAt(++state.position); - } - } else { - break; - } - } - - if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) { - throwWarning(state, 'deficient indentation'); - } - - return lineBreaks; - } - - function testDocumentSeparator(state) { - var _position = state.position, - ch; - - ch = state.input.charCodeAt(_position); - - // Condition state.position === state.lineStart is tested - // in parent on each call, for efficiency. No needs to test here again. - if ((ch === 0x2D/* - */ || ch === 0x2E/* . */) && - ch === state.input.charCodeAt(_position + 1) && - ch === state.input.charCodeAt(_position + 2)) { - - _position += 3; - - ch = state.input.charCodeAt(_position); - - if (ch === 0 || is_WS_OR_EOL(ch)) { - return true; - } - } - - return false; - } - - function writeFoldedLines(state, count) { - if (count === 1) { - state.result += ' '; - } else if (count > 1) { - state.result += common.repeat('\n', count - 1); - } - } - - - function readPlainScalar(state, nodeIndent, withinFlowCollection) { - var preceding, - following, - captureStart, - captureEnd, - hasPendingContent, - _line, - _lineStart, - _lineIndent, - _kind = state.kind, - _result = state.result, - ch; - - ch = state.input.charCodeAt(state.position); - - if (is_WS_OR_EOL(ch) || - is_FLOW_INDICATOR(ch) || - ch === 0x23/* # */ || - ch === 0x26/* & */ || - ch === 0x2A/* * */ || - ch === 0x21/* ! */ || - ch === 0x7C/* | */ || - ch === 0x3E/* > */ || - ch === 0x27/* ' */ || - ch === 0x22/* " */ || - ch === 0x25/* % */ || - ch === 0x40/* @ */ || - ch === 0x60/* ` */) { - return false; - } - - if (ch === 0x3F/* ? */ || ch === 0x2D/* - */) { - following = state.input.charCodeAt(state.position + 1); - - if (is_WS_OR_EOL(following) || - withinFlowCollection && is_FLOW_INDICATOR(following)) { - return false; - } - } - - state.kind = 'scalar'; - state.result = ''; - captureStart = captureEnd = state.position; - hasPendingContent = false; - - while (ch !== 0) { - if (ch === 0x3A/* : */) { - following = state.input.charCodeAt(state.position + 1); - - if (is_WS_OR_EOL(following) || - withinFlowCollection && is_FLOW_INDICATOR(following)) { - break; - } - - } else if (ch === 0x23/* # */) { - preceding = state.input.charCodeAt(state.position - 1); - - if (is_WS_OR_EOL(preceding)) { - break; - } - - } else if ((state.position === state.lineStart && testDocumentSeparator(state)) || - withinFlowCollection && is_FLOW_INDICATOR(ch)) { - break; - - } else if (is_EOL(ch)) { - _line = state.line; - _lineStart = state.lineStart; - _lineIndent = state.lineIndent; - skipSeparationSpace(state, false, -1); - - if (state.lineIndent >= nodeIndent) { - hasPendingContent = true; - ch = state.input.charCodeAt(state.position); - continue; - } else { - state.position = captureEnd; - state.line = _line; - state.lineStart = _lineStart; - state.lineIndent = _lineIndent; - break; - } - } - - if (hasPendingContent) { - captureSegment(state, captureStart, captureEnd, false); - writeFoldedLines(state, state.line - _line); - captureStart = captureEnd = state.position; - hasPendingContent = false; - } - - if (!is_WHITE_SPACE(ch)) { - captureEnd = state.position + 1; - } - - ch = state.input.charCodeAt(++state.position); - } - - captureSegment(state, captureStart, captureEnd, false); - - if (state.result) { - return true; - } - - state.kind = _kind; - state.result = _result; - return false; - } - - function readSingleQuotedScalar(state, nodeIndent) { - var ch, - captureStart, captureEnd; - - ch = state.input.charCodeAt(state.position); - - if (ch !== 0x27/* ' */) { - return false; - } - - state.kind = 'scalar'; - state.result = ''; - state.position++; - captureStart = captureEnd = state.position; - - while ((ch = state.input.charCodeAt(state.position)) !== 0) { - if (ch === 0x27/* ' */) { - captureSegment(state, captureStart, state.position, true); - ch = state.input.charCodeAt(++state.position); - - if (ch === 0x27/* ' */) { - captureStart = state.position; - state.position++; - captureEnd = state.position; - } else { - return true; - } - - } else if (is_EOL(ch)) { - captureSegment(state, captureStart, captureEnd, true); - writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); - captureStart = captureEnd = state.position; - - } else if (state.position === state.lineStart && testDocumentSeparator(state)) { - throwError(state, 'unexpected end of the document within a single quoted scalar'); - - } else { - state.position++; - captureEnd = state.position; - } - } - - throwError(state, 'unexpected end of the stream within a single quoted scalar'); - } - - function readDoubleQuotedScalar(state, nodeIndent) { - var captureStart, - captureEnd, - hexLength, - hexResult, - tmp, - ch; - - ch = state.input.charCodeAt(state.position); - - if (ch !== 0x22/* " */) { - return false; - } - - state.kind = 'scalar'; - state.result = ''; - state.position++; - captureStart = captureEnd = state.position; - - while ((ch = state.input.charCodeAt(state.position)) !== 0) { - if (ch === 0x22/* " */) { - captureSegment(state, captureStart, state.position, true); - state.position++; - return true; - - } else if (ch === 0x5C/* \ */) { - captureSegment(state, captureStart, state.position, true); - ch = state.input.charCodeAt(++state.position); - - if (is_EOL(ch)) { - skipSeparationSpace(state, false, nodeIndent); - - // TODO: rework to inline fn with no type cast? - } else if (ch < 256 && simpleEscapeCheck[ch]) { - state.result += simpleEscapeMap[ch]; - state.position++; - - } else if ((tmp = escapedHexLen(ch)) > 0) { - hexLength = tmp; - hexResult = 0; - - for (; hexLength > 0; hexLength--) { - ch = state.input.charCodeAt(++state.position); - - if ((tmp = fromHexCode(ch)) >= 0) { - hexResult = (hexResult << 4) + tmp; - - } else { - throwError(state, 'expected hexadecimal character'); - } - } - - state.result += charFromCodepoint(hexResult); - - state.position++; - - } else { - throwError(state, 'unknown escape sequence'); - } - - captureStart = captureEnd = state.position; - - } else if (is_EOL(ch)) { - captureSegment(state, captureStart, captureEnd, true); - writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); - captureStart = captureEnd = state.position; - - } else if (state.position === state.lineStart && testDocumentSeparator(state)) { - throwError(state, 'unexpected end of the document within a double quoted scalar'); - - } else { - state.position++; - captureEnd = state.position; - } - } - - throwError(state, 'unexpected end of the stream within a double quoted scalar'); - } - - function readFlowCollection(state, nodeIndent) { - var readNext = true, - _line, - _tag = state.tag, - _result, - _anchor = state.anchor, - following, - terminator, - isPair, - isExplicitPair, - isMapping, - overridableKeys = {}, - keyNode, - keyTag, - valueNode, - ch; - - ch = state.input.charCodeAt(state.position); - - if (ch === 0x5B/* [ */) { - terminator = 0x5D;/* ] */ - isMapping = false; - _result = []; - } else if (ch === 0x7B/* { */) { - terminator = 0x7D;/* } */ - isMapping = true; - _result = {}; - } else { - return false; - } - - if (state.anchor !== null) { - state.anchorMap[state.anchor] = _result; - } - - ch = state.input.charCodeAt(++state.position); - - while (ch !== 0) { - skipSeparationSpace(state, true, nodeIndent); - - ch = state.input.charCodeAt(state.position); - - if (ch === terminator) { - state.position++; - state.tag = _tag; - state.anchor = _anchor; - state.kind = isMapping ? 'mapping' : 'sequence'; - state.result = _result; - return true; - } else if (!readNext) { - throwError(state, 'missed comma between flow collection entries'); - } - - keyTag = keyNode = valueNode = null; - isPair = isExplicitPair = false; - - if (ch === 0x3F/* ? */) { - following = state.input.charCodeAt(state.position + 1); - - if (is_WS_OR_EOL(following)) { - isPair = isExplicitPair = true; - state.position++; - skipSeparationSpace(state, true, nodeIndent); - } - } - - _line = state.line; - composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); - keyTag = state.tag; - keyNode = state.result; - skipSeparationSpace(state, true, nodeIndent); - - ch = state.input.charCodeAt(state.position); - - if ((isExplicitPair || state.line === _line) && ch === 0x3A/* : */) { - isPair = true; - ch = state.input.charCodeAt(++state.position); - skipSeparationSpace(state, true, nodeIndent); - composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); - valueNode = state.result; - } - - if (isMapping) { - storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode); - } else if (isPair) { - _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode)); - } else { - _result.push(keyNode); - } - - skipSeparationSpace(state, true, nodeIndent); - - ch = state.input.charCodeAt(state.position); - - if (ch === 0x2C/* , */) { - readNext = true; - ch = state.input.charCodeAt(++state.position); - } else { - readNext = false; - } - } - - throwError(state, 'unexpected end of the stream within a flow collection'); - } - - function readBlockScalar(state, nodeIndent) { - var captureStart, - folding, - chomping = CHOMPING_CLIP, - didReadContent = false, - detectedIndent = false, - textIndent = nodeIndent, - emptyLines = 0, - atMoreIndented = false, - tmp, - ch; - - ch = state.input.charCodeAt(state.position); - - if (ch === 0x7C/* | */) { - folding = false; - } else if (ch === 0x3E/* > */) { - folding = true; - } else { - return false; - } - - state.kind = 'scalar'; - state.result = ''; - - while (ch !== 0) { - ch = state.input.charCodeAt(++state.position); - - if (ch === 0x2B/* + */ || ch === 0x2D/* - */) { - if (CHOMPING_CLIP === chomping) { - chomping = (ch === 0x2B/* + */) ? CHOMPING_KEEP : CHOMPING_STRIP; - } else { - throwError(state, 'repeat of a chomping mode identifier'); - } - - } else if ((tmp = fromDecimalCode(ch)) >= 0) { - if (tmp === 0) { - throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one'); - } else if (!detectedIndent) { - textIndent = nodeIndent + tmp - 1; - detectedIndent = true; - } else { - throwError(state, 'repeat of an indentation width identifier'); - } - - } else { - break; - } - } - - if (is_WHITE_SPACE(ch)) { - do { ch = state.input.charCodeAt(++state.position); } - while (is_WHITE_SPACE(ch)); - - if (ch === 0x23/* # */) { - do { ch = state.input.charCodeAt(++state.position); } - while (!is_EOL(ch) && (ch !== 0)); - } - } - - while (ch !== 0) { - readLineBreak(state); - state.lineIndent = 0; - - ch = state.input.charCodeAt(state.position); - - while ((!detectedIndent || state.lineIndent < textIndent) && - (ch === 0x20/* Space */)) { - state.lineIndent++; - ch = state.input.charCodeAt(++state.position); - } - - if (!detectedIndent && state.lineIndent > textIndent) { - textIndent = state.lineIndent; - } - - if (is_EOL(ch)) { - emptyLines++; - continue; - } - - // End of the scalar. - if (state.lineIndent < textIndent) { - - // Perform the chomping. - if (chomping === CHOMPING_KEEP) { - state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines); - } else if (chomping === CHOMPING_CLIP) { - if (didReadContent) { // i.e. only if the scalar is not empty. - state.result += '\n'; - } - } - - // Break this `while` cycle and go to the funciton's epilogue. - break; - } - - // Folded style: use fancy rules to handle line breaks. - if (folding) { - - // Lines starting with white space characters (more-indented lines) are not folded. - if (is_WHITE_SPACE(ch)) { - atMoreIndented = true; - // except for the first content line (cf. Example 8.1) - state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines); - - // End of more-indented block. - } else if (atMoreIndented) { - atMoreIndented = false; - state.result += common.repeat('\n', emptyLines + 1); - - // Just one line break - perceive as the same line. - } else if (emptyLines === 0) { - if (didReadContent) { // i.e. only if we have already read some scalar content. - state.result += ' '; - } - - // Several line breaks - perceive as different lines. - } else { - state.result += common.repeat('\n', emptyLines); - } - - // Literal style: just add exact number of line breaks between content lines. - } else { - // Keep all line breaks except the header line break. - state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines); - } - - didReadContent = true; - detectedIndent = true; - emptyLines = 0; - captureStart = state.position; - - while (!is_EOL(ch) && (ch !== 0)) { - ch = state.input.charCodeAt(++state.position); - } - - captureSegment(state, captureStart, state.position, false); - } - - return true; - } - - function readBlockSequence(state, nodeIndent) { - var _line, - _tag = state.tag, - _anchor = state.anchor, - _result = [], - following, - detected = false, - ch; - - if (state.anchor !== null) { - state.anchorMap[state.anchor] = _result; - } - - ch = state.input.charCodeAt(state.position); - - while (ch !== 0) { - - if (ch !== 0x2D/* - */) { - break; - } - - following = state.input.charCodeAt(state.position + 1); - - if (!is_WS_OR_EOL(following)) { - break; - } - - detected = true; - state.position++; - - if (skipSeparationSpace(state, true, -1)) { - if (state.lineIndent <= nodeIndent) { - _result.push(null); - ch = state.input.charCodeAt(state.position); - continue; - } - } - - _line = state.line; - composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true); - _result.push(state.result); - skipSeparationSpace(state, true, -1); - - ch = state.input.charCodeAt(state.position); - - if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) { - throwError(state, 'bad indentation of a sequence entry'); - } else if (state.lineIndent < nodeIndent) { - break; - } - } - - if (detected) { - state.tag = _tag; - state.anchor = _anchor; - state.kind = 'sequence'; - state.result = _result; - return true; - } - return false; - } - - function readBlockMapping(state, nodeIndent, flowIndent) { - var following, - allowCompact, - _line, - _pos, - _tag = state.tag, - _anchor = state.anchor, - _result = {}, - overridableKeys = {}, - keyTag = null, - keyNode = null, - valueNode = null, - atExplicitKey = false, - detected = false, - ch; - - if (state.anchor !== null) { - state.anchorMap[state.anchor] = _result; - } - - ch = state.input.charCodeAt(state.position); - - while (ch !== 0) { - following = state.input.charCodeAt(state.position + 1); - _line = state.line; // Save the current line. - _pos = state.position; - - // - // Explicit notation case. There are two separate blocks: - // first for the key (denoted by "?") and second for the value (denoted by ":") - // - if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && is_WS_OR_EOL(following)) { - - if (ch === 0x3F/* ? */) { - if (atExplicitKey) { - storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null); - keyTag = keyNode = valueNode = null; - } - - detected = true; - atExplicitKey = true; - allowCompact = true; - - } else if (atExplicitKey) { - // i.e. 0x3A/* : */ === character after the explicit key. - atExplicitKey = false; - allowCompact = true; - - } else { - throwError(state, 'incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line'); - } - - state.position += 1; - ch = following; - - // - // Implicit notation case. Flow-style node as the key first, then ":", and the value. - // - } else if (composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) { - - if (state.line === _line) { - ch = state.input.charCodeAt(state.position); - - while (is_WHITE_SPACE(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (ch === 0x3A/* : */) { - ch = state.input.charCodeAt(++state.position); - - if (!is_WS_OR_EOL(ch)) { - throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping'); - } - - if (atExplicitKey) { - storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null); - keyTag = keyNode = valueNode = null; - } - - detected = true; - atExplicitKey = false; - allowCompact = false; - keyTag = state.tag; - keyNode = state.result; - - } else if (detected) { - throwError(state, 'can not read an implicit mapping pair; a colon is missed'); - - } else { - state.tag = _tag; - state.anchor = _anchor; - return true; // Keep the result of `composeNode`. - } - - } else if (detected) { - throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key'); - - } else { - state.tag = _tag; - state.anchor = _anchor; - return true; // Keep the result of `composeNode`. - } - - } else { - break; // Reading is done. Go to the epilogue. - } - - // - // Common reading code for both explicit and implicit notations. - // - if (state.line === _line || state.lineIndent > nodeIndent) { - if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) { - if (atExplicitKey) { - keyNode = state.result; - } else { - valueNode = state.result; - } - } - - if (!atExplicitKey) { - storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _pos); - keyTag = keyNode = valueNode = null; - } - - skipSeparationSpace(state, true, -1); - ch = state.input.charCodeAt(state.position); - } - - if (state.lineIndent > nodeIndent && (ch !== 0)) { - throwError(state, 'bad indentation of a mapping entry'); - } else if (state.lineIndent < nodeIndent) { - break; - } - } - - // - // Epilogue. - // - - // Special case: last mapping's node contains only the key in explicit notation. - if (atExplicitKey) { - storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null); - } - - // Expose the resulting mapping. - if (detected) { - state.tag = _tag; - state.anchor = _anchor; - state.kind = 'mapping'; - state.result = _result; - } - - return detected; - } - - function readTagProperty(state) { - var _position, - isVerbatim = false, - isNamed = false, - tagHandle, - tagName, - ch; - - ch = state.input.charCodeAt(state.position); - - if (ch !== 0x21/* ! */) return false; - - if (state.tag !== null) { - throwError(state, 'duplication of a tag property'); - } - - ch = state.input.charCodeAt(++state.position); - - if (ch === 0x3C/* < */) { - isVerbatim = true; - ch = state.input.charCodeAt(++state.position); - - } else if (ch === 0x21/* ! */) { - isNamed = true; - tagHandle = '!!'; - ch = state.input.charCodeAt(++state.position); - - } else { - tagHandle = '!'; - } - - _position = state.position; - - if (isVerbatim) { - do { ch = state.input.charCodeAt(++state.position); } - while (ch !== 0 && ch !== 0x3E/* > */); - - if (state.position < state.length) { - tagName = state.input.slice(_position, state.position); - ch = state.input.charCodeAt(++state.position); - } else { - throwError(state, 'unexpected end of the stream within a verbatim tag'); - } - } else { - while (ch !== 0 && !is_WS_OR_EOL(ch)) { - - if (ch === 0x21/* ! */) { - if (!isNamed) { - tagHandle = state.input.slice(_position - 1, state.position + 1); - - if (!PATTERN_TAG_HANDLE.test(tagHandle)) { - throwError(state, 'named tag handle cannot contain such characters'); - } - - isNamed = true; - _position = state.position + 1; - } else { - throwError(state, 'tag suffix cannot contain exclamation marks'); - } - } - - ch = state.input.charCodeAt(++state.position); - } - - tagName = state.input.slice(_position, state.position); - - if (PATTERN_FLOW_INDICATORS.test(tagName)) { - throwError(state, 'tag suffix cannot contain flow indicator characters'); - } - } - - if (tagName && !PATTERN_TAG_URI.test(tagName)) { - throwError(state, 'tag name cannot contain such characters: ' + tagName); - } - - if (isVerbatim) { - state.tag = tagName; - - } else if (_hasOwnProperty$2.call(state.tagMap, tagHandle)) { - state.tag = state.tagMap[tagHandle] + tagName; - - } else if (tagHandle === '!') { - state.tag = '!' + tagName; - - } else if (tagHandle === '!!') { - state.tag = 'tag:yaml.org,2002:' + tagName; - - } else { - throwError(state, 'undeclared tag handle "' + tagHandle + '"'); - } - - return true; - } - - function readAnchorProperty(state) { - var _position, - ch; - - ch = state.input.charCodeAt(state.position); - - if (ch !== 0x26/* & */) return false; - - if (state.anchor !== null) { - throwError(state, 'duplication of an anchor property'); - } - - ch = state.input.charCodeAt(++state.position); - _position = state.position; - - while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (state.position === _position) { - throwError(state, 'name of an anchor node must contain at least one character'); - } - - state.anchor = state.input.slice(_position, state.position); - return true; - } - - function readAlias(state) { - var _position, alias, - ch; - - ch = state.input.charCodeAt(state.position); - - if (ch !== 0x2A/* * */) return false; - - ch = state.input.charCodeAt(++state.position); - _position = state.position; - - while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (state.position === _position) { - throwError(state, 'name of an alias node must contain at least one character'); - } - - alias = state.input.slice(_position, state.position); - - if (!state.anchorMap.hasOwnProperty(alias)) { - throwError(state, 'unidentified alias "' + alias + '"'); - } - - state.result = state.anchorMap[alias]; - skipSeparationSpace(state, true, -1); - return true; - } - - function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) { - var allowBlockStyles, - allowBlockScalars, - allowBlockCollections, - indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this parentIndent) { - indentStatus = 1; - } else if (state.lineIndent === parentIndent) { - indentStatus = 0; - } else if (state.lineIndent < parentIndent) { - indentStatus = -1; - } - } - } - - if (indentStatus === 1) { - while (readTagProperty(state) || readAnchorProperty(state)) { - if (skipSeparationSpace(state, true, -1)) { - atNewLine = true; - allowBlockCollections = allowBlockStyles; - - if (state.lineIndent > parentIndent) { - indentStatus = 1; - } else if (state.lineIndent === parentIndent) { - indentStatus = 0; - } else if (state.lineIndent < parentIndent) { - indentStatus = -1; - } - } else { - allowBlockCollections = false; - } - } - } - - if (allowBlockCollections) { - allowBlockCollections = atNewLine || allowCompact; - } - - if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) { - if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) { - flowIndent = parentIndent; - } else { - flowIndent = parentIndent + 1; - } - - blockIndent = state.position - state.lineStart; - - if (indentStatus === 1) { - if (allowBlockCollections && - (readBlockSequence(state, blockIndent) || - readBlockMapping(state, blockIndent, flowIndent)) || - readFlowCollection(state, flowIndent)) { - hasContent = true; - } else { - if ((allowBlockScalars && readBlockScalar(state, flowIndent)) || - readSingleQuotedScalar(state, flowIndent) || - readDoubleQuotedScalar(state, flowIndent)) { - hasContent = true; - - } else if (readAlias(state)) { - hasContent = true; - - if (state.tag !== null || state.anchor !== null) { - throwError(state, 'alias node should not have any properties'); - } - - } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) { - hasContent = true; - - if (state.tag === null) { - state.tag = '?'; - } - } - - if (state.anchor !== null) { - state.anchorMap[state.anchor] = state.result; - } - } - } else if (indentStatus === 0) { - // Special case: block sequences are allowed to have same indentation level as the parent. - // http://www.yaml.org/spec/1.2/spec.html#id2799784 - hasContent = allowBlockCollections && readBlockSequence(state, blockIndent); - } - } - - if (state.tag !== null && state.tag !== '!') { - if (state.tag === '?') { - for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) { - type = state.implicitTypes[typeIndex]; - - // Implicit resolving is not allowed for non-scalar types, and '?' - // non-specific tag is only assigned to plain scalars. So, it isn't - // needed to check for 'kind' conformity. - - if (type.resolve(state.result)) { // `state.result` updated in resolver if matched - state.result = type.construct(state.result); - state.tag = type.tag; - if (state.anchor !== null) { - state.anchorMap[state.anchor] = state.result; - } - break; - } - } - } else if (_hasOwnProperty$2.call(state.typeMap[state.kind || 'fallback'], state.tag)) { - type = state.typeMap[state.kind || 'fallback'][state.tag]; - - if (state.result !== null && type.kind !== state.kind) { - throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"'); - } - - if (!type.resolve(state.result)) { // `state.result` updated in resolver if matched - throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag'); - } else { - state.result = type.construct(state.result); - if (state.anchor !== null) { - state.anchorMap[state.anchor] = state.result; - } - } - } else { - throwError(state, 'unknown tag !<' + state.tag + '>'); - } - } - - if (state.listener !== null) { - state.listener('close', state); - } - return state.tag !== null || state.anchor !== null || hasContent; - } - - function readDocument(state) { - var documentStart = state.position, - _position, - directiveName, - directiveArgs, - hasDirectives = false, - ch; - - state.version = null; - state.checkLineBreaks = state.legacy; - state.tagMap = {}; - state.anchorMap = {}; - - while ((ch = state.input.charCodeAt(state.position)) !== 0) { - skipSeparationSpace(state, true, -1); - - ch = state.input.charCodeAt(state.position); - - if (state.lineIndent > 0 || ch !== 0x25/* % */) { - break; - } - - hasDirectives = true; - ch = state.input.charCodeAt(++state.position); - _position = state.position; - - while (ch !== 0 && !is_WS_OR_EOL(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - directiveName = state.input.slice(_position, state.position); - directiveArgs = []; - - if (directiveName.length < 1) { - throwError(state, 'directive name must not be less than one character in length'); - } - - while (ch !== 0) { - while (is_WHITE_SPACE(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - if (ch === 0x23/* # */) { - do { ch = state.input.charCodeAt(++state.position); } - while (ch !== 0 && !is_EOL(ch)); - break; - } - - if (is_EOL(ch)) break; - - _position = state.position; - - while (ch !== 0 && !is_WS_OR_EOL(ch)) { - ch = state.input.charCodeAt(++state.position); - } - - directiveArgs.push(state.input.slice(_position, state.position)); - } - - if (ch !== 0) readLineBreak(state); - - if (_hasOwnProperty$2.call(directiveHandlers, directiveName)) { - directiveHandlers[directiveName](state, directiveName, directiveArgs); - } else { - throwWarning(state, 'unknown document directive "' + directiveName + '"'); - } - } - - skipSeparationSpace(state, true, -1); - - if (state.lineIndent === 0 && - state.input.charCodeAt(state.position) === 0x2D/* - */ && - state.input.charCodeAt(state.position + 1) === 0x2D/* - */ && - state.input.charCodeAt(state.position + 2) === 0x2D/* - */) { - state.position += 3; - skipSeparationSpace(state, true, -1); - - } else if (hasDirectives) { - throwError(state, 'directives end mark is expected'); - } - - composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true); - skipSeparationSpace(state, true, -1); - - if (state.checkLineBreaks && - PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) { - throwWarning(state, 'non-ASCII line breaks are interpreted as content'); - } - - state.documents.push(state.result); - - if (state.position === state.lineStart && testDocumentSeparator(state)) { - - if (state.input.charCodeAt(state.position) === 0x2E/* . */) { - state.position += 3; - skipSeparationSpace(state, true, -1); - } - return; - } - - if (state.position < (state.length - 1)) { - throwError(state, 'end of the stream or a document separator is expected'); - } else { - return; - } - } - - - function loadDocuments(input, options) { - input = String(input); - options = options || {}; - - if (input.length !== 0) { - - // Add tailing `\n` if not exists - if (input.charCodeAt(input.length - 1) !== 0x0A/* LF */ && - input.charCodeAt(input.length - 1) !== 0x0D/* CR */) { - input += '\n'; - } - - // Strip BOM - if (input.charCodeAt(0) === 0xFEFF) { - input = input.slice(1); - } - } - - var state = new State(input, options); - - // Use 0 as string terminator. That significantly simplifies bounds check. - state.input += '\0'; - - while (state.input.charCodeAt(state.position) === 0x20/* Space */) { - state.lineIndent += 1; - state.position += 1; - } - - while (state.position < (state.length - 1)) { - readDocument(state); - } - - return state.documents; - } - - - function loadAll(input, iterator, options) { - var documents = loadDocuments(input, options), index, length; - - if (typeof iterator !== 'function') { - return documents; - } - - for (index = 0, length = documents.length; index < length; index += 1) { - iterator(documents[index]); - } - } - - - function load(input, options) { - var documents = loadDocuments(input, options); - - if (documents.length === 0) { - /*eslint-disable no-undefined*/ - return undefined; - } else if (documents.length === 1) { - return documents[0]; - } - throw new exception('expected a single document in the stream, but found more'); - } - - - function safeLoadAll(input, output, options) { - if (typeof output === 'function') { - loadAll(input, output, common.extend({ schema: default_safe }, options)); - } else { - return loadAll(input, common.extend({ schema: default_safe }, options)); - } - } - - - function safeLoad(input, options) { - return load(input, common.extend({ schema: default_safe }, options)); - } - - - var loadAll_1 = loadAll; - var load_1 = load; - var safeLoadAll_1 = safeLoadAll; - var safeLoad_1 = safeLoad; - - var loader = { - loadAll: loadAll_1, - load: load_1, - safeLoadAll: safeLoadAll_1, - safeLoad: safeLoad_1 - }; - - /*eslint-disable no-use-before-define*/ - - - - - - - var _toString$2 = Object.prototype.toString; - var _hasOwnProperty$3 = Object.prototype.hasOwnProperty; - - var CHAR_TAB = 0x09; /* Tab */ - var CHAR_LINE_FEED = 0x0A; /* LF */ - var CHAR_SPACE = 0x20; /* Space */ - var CHAR_EXCLAMATION = 0x21; /* ! */ - var CHAR_DOUBLE_QUOTE = 0x22; /* " */ - var CHAR_SHARP = 0x23; /* # */ - var CHAR_PERCENT = 0x25; /* % */ - var CHAR_AMPERSAND = 0x26; /* & */ - var CHAR_SINGLE_QUOTE = 0x27; /* ' */ - var CHAR_ASTERISK = 0x2A; /* * */ - var CHAR_COMMA = 0x2C; /* , */ - var CHAR_MINUS = 0x2D; /* - */ - var CHAR_COLON = 0x3A; /* : */ - var CHAR_GREATER_THAN = 0x3E; /* > */ - var CHAR_QUESTION = 0x3F; /* ? */ - var CHAR_COMMERCIAL_AT = 0x40; /* @ */ - var CHAR_LEFT_SQUARE_BRACKET = 0x5B; /* [ */ - var CHAR_RIGHT_SQUARE_BRACKET = 0x5D; /* ] */ - var CHAR_GRAVE_ACCENT = 0x60; /* ` */ - var CHAR_LEFT_CURLY_BRACKET = 0x7B; /* { */ - var CHAR_VERTICAL_LINE = 0x7C; /* | */ - var CHAR_RIGHT_CURLY_BRACKET = 0x7D; /* } */ - - var ESCAPE_SEQUENCES = {}; - - ESCAPE_SEQUENCES[0x00] = '\\0'; - ESCAPE_SEQUENCES[0x07] = '\\a'; - ESCAPE_SEQUENCES[0x08] = '\\b'; - ESCAPE_SEQUENCES[0x09] = '\\t'; - ESCAPE_SEQUENCES[0x0A] = '\\n'; - ESCAPE_SEQUENCES[0x0B] = '\\v'; - ESCAPE_SEQUENCES[0x0C] = '\\f'; - ESCAPE_SEQUENCES[0x0D] = '\\r'; - ESCAPE_SEQUENCES[0x1B] = '\\e'; - ESCAPE_SEQUENCES[0x22] = '\\"'; - ESCAPE_SEQUENCES[0x5C] = '\\\\'; - ESCAPE_SEQUENCES[0x85] = '\\N'; - ESCAPE_SEQUENCES[0xA0] = '\\_'; - ESCAPE_SEQUENCES[0x2028] = '\\L'; - ESCAPE_SEQUENCES[0x2029] = '\\P'; - - var DEPRECATED_BOOLEANS_SYNTAX = [ - 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON', - 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF' - ]; - - function compileStyleMap(schema, map) { - var result, keys, index, length, tag, style, type; - - if (map === null) return {}; - - result = {}; - keys = Object.keys(map); - - for (index = 0, length = keys.length; index < length; index += 1) { - tag = keys[index]; - style = String(map[tag]); - - if (tag.slice(0, 2) === '!!') { - tag = 'tag:yaml.org,2002:' + tag.slice(2); - } - type = schema.compiledTypeMap['fallback'][tag]; - - if (type && _hasOwnProperty$3.call(type.styleAliases, style)) { - style = type.styleAliases[style]; - } - - result[tag] = style; - } - - return result; - } - - function encodeHex(character) { - var string, handle, length; - - string = character.toString(16).toUpperCase(); - - if (character <= 0xFF) { - handle = 'x'; - length = 2; - } else if (character <= 0xFFFF) { - handle = 'u'; - length = 4; - } else if (character <= 0xFFFFFFFF) { - handle = 'U'; - length = 8; - } else { - throw new exception('code point within a string may not be greater than 0xFFFFFFFF'); - } - - return '\\' + handle + common.repeat('0', length - string.length) + string; - } - - function State$1(options) { - this.schema = options['schema'] || default_full; - this.indent = Math.max(1, (options['indent'] || 2)); - this.noArrayIndent = options['noArrayIndent'] || false; - this.skipInvalid = options['skipInvalid'] || false; - this.flowLevel = (common.isNothing(options['flowLevel']) ? -1 : options['flowLevel']); - this.styleMap = compileStyleMap(this.schema, options['styles'] || null); - this.sortKeys = options['sortKeys'] || false; - this.lineWidth = options['lineWidth'] || 80; - this.noRefs = options['noRefs'] || false; - this.noCompatMode = options['noCompatMode'] || false; - this.condenseFlow = options['condenseFlow'] || false; - - this.implicitTypes = this.schema.compiledImplicit; - this.explicitTypes = this.schema.compiledExplicit; - - this.tag = null; - this.result = ''; - - this.duplicates = []; - this.usedDuplicates = null; - } - - // Indents every line in a string. Empty lines (\n only) are not indented. - function indentString(string, spaces) { - var ind = common.repeat(' ', spaces), - position = 0, - next = -1, - result = '', - line, - length = string.length; - - while (position < length) { - next = string.indexOf('\n', position); - if (next === -1) { - line = string.slice(position); - position = length; - } else { - line = string.slice(position, next + 1); - position = next + 1; - } - - if (line.length && line !== '\n') result += ind; - - result += line; - } - - return result; - } - - function generateNextLine(state, level) { - return '\n' + common.repeat(' ', state.indent * level); - } - - function testImplicitResolving(state, str) { - var index, length, type; - - for (index = 0, length = state.implicitTypes.length; index < length; index += 1) { - type = state.implicitTypes[index]; - - if (type.resolve(str)) { - return true; - } - } - - return false; - } - - // [33] s-white ::= s-space | s-tab - function isWhitespace(c) { - return c === CHAR_SPACE || c === CHAR_TAB; - } - - // Returns true if the character can be printed without escaping. - // From YAML 1.2: "any allowed characters known to be non-printable - // should also be escaped. [However,] This isn’t mandatory" - // Derived from nb-char - \t - #x85 - #xA0 - #x2028 - #x2029. - function isPrintable(c) { - return (0x00020 <= c && c <= 0x00007E) - || ((0x000A1 <= c && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029) - || ((0x0E000 <= c && c <= 0x00FFFD) && c !== 0xFEFF /* BOM */) - || (0x10000 <= c && c <= 0x10FFFF); - } - - // Simplified test for values allowed after the first character in plain style. - function isPlainSafe(c) { - // Uses a subset of nb-char - c-flow-indicator - ":" - "#" - // where nb-char ::= c-printable - b-char - c-byte-order-mark. - return isPrintable(c) && c !== 0xFEFF - // - c-flow-indicator - && c !== CHAR_COMMA - && c !== CHAR_LEFT_SQUARE_BRACKET - && c !== CHAR_RIGHT_SQUARE_BRACKET - && c !== CHAR_LEFT_CURLY_BRACKET - && c !== CHAR_RIGHT_CURLY_BRACKET - // - ":" - "#" - && c !== CHAR_COLON - && c !== CHAR_SHARP; - } - - // Simplified test for values allowed as the first character in plain style. - function isPlainSafeFirst(c) { - // Uses a subset of ns-char - c-indicator - // where ns-char = nb-char - s-white. - return isPrintable(c) && c !== 0xFEFF - && !isWhitespace(c) // - s-white - // - (c-indicator ::= - // “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}” - && c !== CHAR_MINUS - && c !== CHAR_QUESTION - && c !== CHAR_COLON - && c !== CHAR_COMMA - && c !== CHAR_LEFT_SQUARE_BRACKET - && c !== CHAR_RIGHT_SQUARE_BRACKET - && c !== CHAR_LEFT_CURLY_BRACKET - && c !== CHAR_RIGHT_CURLY_BRACKET - // | “#” | “&” | “*” | “!” | “|” | “>” | “'” | “"” - && c !== CHAR_SHARP - && c !== CHAR_AMPERSAND - && c !== CHAR_ASTERISK - && c !== CHAR_EXCLAMATION - && c !== CHAR_VERTICAL_LINE - && c !== CHAR_GREATER_THAN - && c !== CHAR_SINGLE_QUOTE - && c !== CHAR_DOUBLE_QUOTE - // | “%” | “@” | “`”) - && c !== CHAR_PERCENT - && c !== CHAR_COMMERCIAL_AT - && c !== CHAR_GRAVE_ACCENT; - } - - // Determines whether block indentation indicator is required. - function needIndentIndicator(string) { - var leadingSpaceRe = /^\n* /; - return leadingSpaceRe.test(string); - } - - var STYLE_PLAIN = 1, - STYLE_SINGLE = 2, - STYLE_LITERAL = 3, - STYLE_FOLDED = 4, - STYLE_DOUBLE = 5; - - // Determines which scalar styles are possible and returns the preferred style. - // lineWidth = -1 => no limit. - // Pre-conditions: str.length > 0. - // Post-conditions: - // STYLE_PLAIN or STYLE_SINGLE => no \n are in the string. - // STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1). - // STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1). - function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, testAmbiguousType) { - var i; - var char; - var hasLineBreak = false; - var hasFoldableLine = false; // only checked if shouldTrackWidth - var shouldTrackWidth = lineWidth !== -1; - var previousLineBreak = -1; // count the first line correctly - var plain = isPlainSafeFirst(string.charCodeAt(0)) - && !isWhitespace(string.charCodeAt(string.length - 1)); - - if (singleLineOnly) { - // Case: no block styles. - // Check for disallowed characters to rule out plain and single. - for (i = 0; i < string.length; i++) { - char = string.charCodeAt(i); - if (!isPrintable(char)) { - return STYLE_DOUBLE; - } - plain = plain && isPlainSafe(char); - } - } else { - // Case: block styles permitted. - for (i = 0; i < string.length; i++) { - char = string.charCodeAt(i); - if (char === CHAR_LINE_FEED) { - hasLineBreak = true; - // Check if any line can be folded. - if (shouldTrackWidth) { - hasFoldableLine = hasFoldableLine || - // Foldable line = too long, and not more-indented. - (i - previousLineBreak - 1 > lineWidth && - string[previousLineBreak + 1] !== ' '); - previousLineBreak = i; - } - } else if (!isPrintable(char)) { - return STYLE_DOUBLE; - } - plain = plain && isPlainSafe(char); - } - // in case the end is missing a \n - hasFoldableLine = hasFoldableLine || (shouldTrackWidth && - (i - previousLineBreak - 1 > lineWidth && - string[previousLineBreak + 1] !== ' ')); - } - // Although every style can represent \n without escaping, prefer block styles - // for multiline, since they're more readable and they don't add empty lines. - // Also prefer folding a super-long line. - if (!hasLineBreak && !hasFoldableLine) { - // Strings interpretable as another type have to be quoted; - // e.g. the string 'true' vs. the boolean true. - return plain && !testAmbiguousType(string) - ? STYLE_PLAIN : STYLE_SINGLE; - } - // Edge case: block indentation indicator can only have one digit. - if (indentPerLevel > 9 && needIndentIndicator(string)) { - return STYLE_DOUBLE; - } - // At this point we know block styles are valid. - // Prefer literal style unless we want to fold. - return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL; - } - - // Note: line breaking/folding is implemented for only the folded style. - // NB. We drop the last trailing newline (if any) of a returned block scalar - // since the dumper adds its own newline. This always works: - // • No ending newline => unaffected; already using strip "-" chomping. - // • Ending newline => removed then restored. - // Importantly, this keeps the "+" chomp indicator from gaining an extra line. - function writeScalar(state, string, level, iskey) { - state.dump = (function () { - if (string.length === 0) { - return "''"; - } - if (!state.noCompatMode && - DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1) { - return "'" + string + "'"; - } - - var indent = state.indent * Math.max(1, level); // no 0-indent scalars - // As indentation gets deeper, let the width decrease monotonically - // to the lower bound min(state.lineWidth, 40). - // Note that this implies - // state.lineWidth ≤ 40 + state.indent: width is fixed at the lower bound. - // state.lineWidth > 40 + state.indent: width decreases until the lower bound. - // This behaves better than a constant minimum width which disallows narrower options, - // or an indent threshold which causes the width to suddenly increase. - var lineWidth = state.lineWidth === -1 - ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent); - - // Without knowing if keys are implicit/explicit, assume implicit for safety. - var singleLineOnly = iskey - // No block styles in flow mode. - || (state.flowLevel > -1 && level >= state.flowLevel); - function testAmbiguity(string) { - return testImplicitResolving(state, string); - } - - switch (chooseScalarStyle(string, singleLineOnly, state.indent, lineWidth, testAmbiguity)) { - case STYLE_PLAIN: - return string; - case STYLE_SINGLE: - return "'" + string.replace(/'/g, "''") + "'"; - case STYLE_LITERAL: - return '|' + blockHeader(string, state.indent) - + dropEndingNewline(indentString(string, indent)); - case STYLE_FOLDED: - return '>' + blockHeader(string, state.indent) - + dropEndingNewline(indentString(foldString(string, lineWidth), indent)); - case STYLE_DOUBLE: - return '"' + escapeString(string) + '"'; - default: - throw new exception('impossible error: invalid scalar style'); - } - }()); - } - - // Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9. - function blockHeader(string, indentPerLevel) { - var indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : ''; - - // note the special case: the string '\n' counts as a "trailing" empty line. - var clip = string[string.length - 1] === '\n'; - var keep = clip && (string[string.length - 2] === '\n' || string === '\n'); - var chomp = keep ? '+' : (clip ? '' : '-'); - - return indentIndicator + chomp + '\n'; - } - - // (See the note for writeScalar.) - function dropEndingNewline(string) { - return string[string.length - 1] === '\n' ? string.slice(0, -1) : string; - } - - // Note: a long line without a suitable break point will exceed the width limit. - // Pre-conditions: every char in str isPrintable, str.length > 0, width > 0. - function foldString(string, width) { - // In folded style, $k$ consecutive newlines output as $k+1$ newlines— - // unless they're before or after a more-indented line, or at the very - // beginning or end, in which case $k$ maps to $k$. - // Therefore, parse each chunk as newline(s) followed by a content line. - var lineRe = /(\n+)([^\n]*)/g; - - // first line (possibly an empty line) - var result = (function () { - var nextLF = string.indexOf('\n'); - nextLF = nextLF !== -1 ? nextLF : string.length; - lineRe.lastIndex = nextLF; - return foldLine(string.slice(0, nextLF), width); - }()); - // If we haven't reached the first content line yet, don't add an extra \n. - var prevMoreIndented = string[0] === '\n' || string[0] === ' '; - var moreIndented; - - // rest of the lines - var match; - while ((match = lineRe.exec(string))) { - var prefix = match[1], line = match[2]; - moreIndented = (line[0] === ' '); - result += prefix - + (!prevMoreIndented && !moreIndented && line !== '' - ? '\n' : '') - + foldLine(line, width); - prevMoreIndented = moreIndented; - } - - return result; - } - - // Greedy line breaking. - // Picks the longest line under the limit each time, - // otherwise settles for the shortest line over the limit. - // NB. More-indented lines *cannot* be folded, as that would add an extra \n. - function foldLine(line, width) { - if (line === '' || line[0] === ' ') return line; - - // Since a more-indented line adds a \n, breaks can't be followed by a space. - var breakRe = / [^ ]/g; // note: the match index will always be <= length-2. - var match; - // start is an inclusive index. end, curr, and next are exclusive. - var start = 0, end, curr = 0, next = 0; - var result = ''; - - // Invariants: 0 <= start <= length-1. - // 0 <= curr <= next <= max(0, length-2). curr - start <= width. - // Inside the loop: - // A match implies length >= 2, so curr and next are <= length-2. - while ((match = breakRe.exec(line))) { - next = match.index; - // maintain invariant: curr - start <= width - if (next - start > width) { - end = (curr > start) ? curr : next; // derive end <= length-2 - result += '\n' + line.slice(start, end); - // skip the space that was output as \n - start = end + 1; // derive start <= length-1 - } - curr = next; - } - - // By the invariants, start <= length-1, so there is something left over. - // It is either the whole string or a part starting from non-whitespace. - result += '\n'; - // Insert a break if the remainder is too long and there is a break available. - if (line.length - start > width && curr > start) { - result += line.slice(start, curr) + '\n' + line.slice(curr + 1); - } else { - result += line.slice(start); - } - - return result.slice(1); // drop extra \n joiner - } - - // Escapes a double-quoted string. - function escapeString(string) { - var result = ''; - var char, nextChar; - var escapeSeq; - - for (var i = 0; i < string.length; i++) { - char = string.charCodeAt(i); - // Check for surrogate pairs (reference Unicode 3.0 section "3.7 Surrogates"). - if (char >= 0xD800 && char <= 0xDBFF/* high surrogate */) { - nextChar = string.charCodeAt(i + 1); - if (nextChar >= 0xDC00 && nextChar <= 0xDFFF/* low surrogate */) { - // Combine the surrogate pair and store it escaped. - result += encodeHex((char - 0xD800) * 0x400 + nextChar - 0xDC00 + 0x10000); - // Advance index one extra since we already used that char here. - i++; continue; - } - } - escapeSeq = ESCAPE_SEQUENCES[char]; - result += !escapeSeq && isPrintable(char) - ? string[i] - : escapeSeq || encodeHex(char); - } - - return result; - } - - function writeFlowSequence(state, level, object) { - var _result = '', - _tag = state.tag, - index, - length; - - for (index = 0, length = object.length; index < length; index += 1) { - // Write only valid elements. - if (writeNode(state, level, object[index], false, false)) { - if (index !== 0) _result += ',' + (!state.condenseFlow ? ' ' : ''); - _result += state.dump; - } - } - - state.tag = _tag; - state.dump = '[' + _result + ']'; - } - - function writeBlockSequence(state, level, object, compact) { - var _result = '', - _tag = state.tag, - index, - length; - - for (index = 0, length = object.length; index < length; index += 1) { - // Write only valid elements. - if (writeNode(state, level + 1, object[index], true, true)) { - if (!compact || index !== 0) { - _result += generateNextLine(state, level); - } - - if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { - _result += '-'; - } else { - _result += '- '; - } - - _result += state.dump; - } - } - - state.tag = _tag; - state.dump = _result || '[]'; // Empty sequence if no valid values. - } - - function writeFlowMapping(state, level, object) { - var _result = '', - _tag = state.tag, - objectKeyList = Object.keys(object), - index, - length, - objectKey, - objectValue, - pairBuffer; - - for (index = 0, length = objectKeyList.length; index < length; index += 1) { - pairBuffer = state.condenseFlow ? '"' : ''; - - if (index !== 0) pairBuffer += ', '; - - objectKey = objectKeyList[index]; - objectValue = object[objectKey]; - - if (!writeNode(state, level, objectKey, false, false)) { - continue; // Skip this pair because of invalid key; - } - - if (state.dump.length > 1024) pairBuffer += '? '; - - pairBuffer += state.dump + (state.condenseFlow ? '"' : '') + ':' + (state.condenseFlow ? '' : ' '); - - if (!writeNode(state, level, objectValue, false, false)) { - continue; // Skip this pair because of invalid value. - } - - pairBuffer += state.dump; - - // Both key and value are valid. - _result += pairBuffer; - } - - state.tag = _tag; - state.dump = '{' + _result + '}'; - } - - function writeBlockMapping(state, level, object, compact) { - var _result = '', - _tag = state.tag, - objectKeyList = Object.keys(object), - index, - length, - objectKey, - objectValue, - explicitPair, - pairBuffer; - - // Allow sorting keys so that the output file is deterministic - if (state.sortKeys === true) { - // Default sorting - objectKeyList.sort(); - } else if (typeof state.sortKeys === 'function') { - // Custom sort function - objectKeyList.sort(state.sortKeys); - } else if (state.sortKeys) { - // Something is wrong - throw new exception('sortKeys must be a boolean or a function'); - } - - for (index = 0, length = objectKeyList.length; index < length; index += 1) { - pairBuffer = ''; - - if (!compact || index !== 0) { - pairBuffer += generateNextLine(state, level); - } - - objectKey = objectKeyList[index]; - objectValue = object[objectKey]; - - if (!writeNode(state, level + 1, objectKey, true, true, true)) { - continue; // Skip this pair because of invalid key. - } - - explicitPair = (state.tag !== null && state.tag !== '?') || - (state.dump && state.dump.length > 1024); - - if (explicitPair) { - if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { - pairBuffer += '?'; - } else { - pairBuffer += '? '; - } - } - - pairBuffer += state.dump; - - if (explicitPair) { - pairBuffer += generateNextLine(state, level); - } - - if (!writeNode(state, level + 1, objectValue, true, explicitPair)) { - continue; // Skip this pair because of invalid value. - } - - if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { - pairBuffer += ':'; - } else { - pairBuffer += ': '; - } - - pairBuffer += state.dump; - - // Both key and value are valid. - _result += pairBuffer; - } - - state.tag = _tag; - state.dump = _result || '{}'; // Empty mapping if no valid pairs. - } - - function detectType(state, object, explicit) { - var _result, typeList, index, length, type, style; - - typeList = explicit ? state.explicitTypes : state.implicitTypes; - - for (index = 0, length = typeList.length; index < length; index += 1) { - type = typeList[index]; - - if ((type.instanceOf || type.predicate) && - (!type.instanceOf || ((typeof object === 'object') && (object instanceof type.instanceOf))) && - (!type.predicate || type.predicate(object))) { - - state.tag = explicit ? type.tag : '?'; - - if (type.represent) { - style = state.styleMap[type.tag] || type.defaultStyle; - - if (_toString$2.call(type.represent) === '[object Function]') { - _result = type.represent(object, style); - } else if (_hasOwnProperty$3.call(type.represent, style)) { - _result = type.represent[style](object, style); - } else { - throw new exception('!<' + type.tag + '> tag resolver accepts not "' + style + '" style'); - } - - state.dump = _result; - } - - return true; - } - } - - return false; - } - - // Serializes `object` and writes it to global `result`. - // Returns true on success, or false on invalid object. - // - function writeNode(state, level, object, block, compact, iskey) { - state.tag = null; - state.dump = object; - - if (!detectType(state, object, false)) { - detectType(state, object, true); - } - - var type = _toString$2.call(state.dump); - - if (block) { - block = (state.flowLevel < 0 || state.flowLevel > level); - } - - var objectOrArray = type === '[object Object]' || type === '[object Array]', - duplicateIndex, - duplicate; - - if (objectOrArray) { - duplicateIndex = state.duplicates.indexOf(object); - duplicate = duplicateIndex !== -1; - } - - if ((state.tag !== null && state.tag !== '?') || duplicate || (state.indent !== 2 && level > 0)) { - compact = false; - } - - if (duplicate && state.usedDuplicates[duplicateIndex]) { - state.dump = '*ref_' + duplicateIndex; - } else { - if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) { - state.usedDuplicates[duplicateIndex] = true; - } - if (type === '[object Object]') { - if (block && (Object.keys(state.dump).length !== 0)) { - writeBlockMapping(state, level, state.dump, compact); - if (duplicate) { - state.dump = '&ref_' + duplicateIndex + state.dump; - } - } else { - writeFlowMapping(state, level, state.dump); - if (duplicate) { - state.dump = '&ref_' + duplicateIndex + ' ' + state.dump; - } - } - } else if (type === '[object Array]') { - var arrayLevel = (state.noArrayIndent && (level > 0)) ? level - 1 : level; - if (block && (state.dump.length !== 0)) { - writeBlockSequence(state, arrayLevel, state.dump, compact); - if (duplicate) { - state.dump = '&ref_' + duplicateIndex + state.dump; - } - } else { - writeFlowSequence(state, arrayLevel, state.dump); - if (duplicate) { - state.dump = '&ref_' + duplicateIndex + ' ' + state.dump; - } - } - } else if (type === '[object String]') { - if (state.tag !== '?') { - writeScalar(state, state.dump, level, iskey); - } - } else { - if (state.skipInvalid) return false; - throw new exception('unacceptable kind of an object to dump ' + type); - } - - if (state.tag !== null && state.tag !== '?') { - state.dump = '!<' + state.tag + '> ' + state.dump; - } - } - - return true; - } - - function getDuplicateReferences(object, state) { - var objects = [], - duplicatesIndexes = [], - index, - length; - - inspectNode(object, objects, duplicatesIndexes); - - for (index = 0, length = duplicatesIndexes.length; index < length; index += 1) { - state.duplicates.push(objects[duplicatesIndexes[index]]); - } - state.usedDuplicates = new Array(length); - } - - function inspectNode(object, objects, duplicatesIndexes) { - var objectKeyList, - index, - length; - - if (object !== null && typeof object === 'object') { - index = objects.indexOf(object); - if (index !== -1) { - if (duplicatesIndexes.indexOf(index) === -1) { - duplicatesIndexes.push(index); - } - } else { - objects.push(object); - - if (Array.isArray(object)) { - for (index = 0, length = object.length; index < length; index += 1) { - inspectNode(object[index], objects, duplicatesIndexes); - } - } else { - objectKeyList = Object.keys(object); - - for (index = 0, length = objectKeyList.length; index < length; index += 1) { - inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes); - } - } - } - } - } - - function dump(input, options) { - options = options || {}; - - var state = new State$1(options); - - if (!state.noRefs) getDuplicateReferences(input, state); - - if (writeNode(state, 0, input, true, true)) return state.dump + '\n'; - - return ''; - } - - function safeDump(input, options) { - return dump(input, common.extend({ schema: default_safe }, options)); - } - - var dump_1 = dump; - var safeDump_1 = safeDump; - - var dumper = { - dump: dump_1, - safeDump: safeDump_1 - }; - - function deprecated(name) { - return function () { - throw new Error('Function ' + name + ' is deprecated and cannot be used.'); - }; - } - - - var Type$1 = type; - var Schema$1 = schema; - var FAILSAFE_SCHEMA = failsafe; - var JSON_SCHEMA = json; - var CORE_SCHEMA = core; - var DEFAULT_SAFE_SCHEMA = default_safe; - var DEFAULT_FULL_SCHEMA = default_full; - var load$1 = loader.load; - var loadAll$1 = loader.loadAll; - var safeLoad$1 = loader.safeLoad; - var safeLoadAll$1 = loader.safeLoadAll; - var dump$1 = dumper.dump; - var safeDump$1 = dumper.safeDump; - var YAMLException$1 = exception; - - // Deprecated schema names from JS-YAML 2.0.x - var MINIMAL_SCHEMA = failsafe; - var SAFE_SCHEMA = default_safe; - var DEFAULT_SCHEMA = default_full; - - // Deprecated functions from JS-YAML 1.x.x - var scan = deprecated('scan'); - var parse = deprecated('parse'); - var compose = deprecated('compose'); - var addConstructor = deprecated('addConstructor'); - - var jsYaml = { - Type: Type$1, - Schema: Schema$1, - FAILSAFE_SCHEMA: FAILSAFE_SCHEMA, - JSON_SCHEMA: JSON_SCHEMA, - CORE_SCHEMA: CORE_SCHEMA, - DEFAULT_SAFE_SCHEMA: DEFAULT_SAFE_SCHEMA, - DEFAULT_FULL_SCHEMA: DEFAULT_FULL_SCHEMA, - load: load$1, - loadAll: loadAll$1, - safeLoad: safeLoad$1, - safeLoadAll: safeLoadAll$1, - dump: dump$1, - safeDump: safeDump$1, - YAMLException: YAMLException$1, - MINIMAL_SCHEMA: MINIMAL_SCHEMA, - SAFE_SCHEMA: SAFE_SCHEMA, - DEFAULT_SCHEMA: DEFAULT_SCHEMA, - scan: scan, - parse: parse, - compose: compose, - addConstructor: addConstructor - }; - - var jsYaml$1 = jsYaml; - - function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } - - function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } - /** - * Redirect - object used to redirect some requests - * e.g. - * { - * title: 1x1-transparent.gif - * comment: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever - * contentType: image/gif;base64 - * content: R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== - * } - * @typedef {Object} Redirect - * @property {string} title - * @property {string} comment - * @property {string} content - * @property {string} contentType - */ - - var Redirects = - /*#__PURE__*/ - function () { - /** - * Converts rawYaml into JS object with sources titles used as keys - * @param rawYaml - * @returns {Object} - return object with titles in the keys and RedirectSources - * in the values - */ - function Redirects(rawYaml) { - classCallCheck(this, Redirects); - - try { - var arrOfRedirects = jsYaml$1.safeLoad(rawYaml); - this.redirects = arrOfRedirects.reduce(function (acc, redirect) { - return _objectSpread({}, acc, defineProperty({}, redirect.title, redirect)); - }, {}); - } catch (e) { - // eslint-disable-next-line no-console - console.log("Was unable to load YAML into JS due to: ".concat(e.message)); - throw e; - } - } - /** - * Returns redirect source object - * @param {string} title - * @return {Redirect} - */ - - - createClass(Redirects, [{ - key: "getRedirect", - value: function getRedirect(title) { - var _this = this; - - if (Object.prototype.hasOwnProperty.call(this.redirects, title)) { - return this.redirects[title]; - } // look title among aliases - - - var values = Object.keys(this.redirects).map(function (key) { - return _this.redirects[key]; - }); - return values.find(function (redirect) { - var aliases = redirect.aliases; - - if (!aliases) { - return false; - } - - return aliases.indexOf(title) > -1; - }); - } - }]); - - return Redirects; - }(); - - return Redirects; - -}()); -//# sourceMappingURL=redirects.js.map diff --git a/dist/redirects.yml b/dist/redirects.yml index fc3e21369..cf26d39d1 100644 --- a/dist/redirects.yml +++ b/dist/redirects.yml @@ -1,6 +1,6 @@ # # AdGuard Scriptlets (Redirects Source) -# Version 1.1.0 +# Version 1.1.1 # - title: 1x1-transparent.gif description: |- diff --git a/dist/scriptlets.corelibs.json b/dist/scriptlets.corelibs.json index 4efe7b0a1..fb769cf03 100644 --- a/dist/scriptlets.corelibs.json +++ b/dist/scriptlets.corelibs.json @@ -1,5 +1,5 @@ { - "version": "1.1.0", + "version": "1.1.1", "scriptlets": [ { "names": [ diff --git a/dist/scriptlets.js b/dist/scriptlets.js index 526b7b8cb..f2f7d20e3 100644 --- a/dist/scriptlets.js +++ b/dist/scriptlets.js @@ -1,7 +1,7 @@ /** * AdGuard Scriptlets - * Version 1.1.0 + * Version 1.1.1 */ (function () { @@ -339,6 +339,30 @@ return "function(source, args){\n".concat(code, "\n}"); } + function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; + } + + var arrayWithHoles = _arrayWithHoles; + + function _iterableToArray(iter) { + if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); + } + + var iterableToArray = _iterableToArray; + + function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance"); + } + + var nonIterableRest = _nonIterableRest; + + function _toArray(arr) { + return arrayWithHoles(arr) || iterableToArray(arr) || nonIterableRest(); + } + + var toArray = _toArray; + function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { @@ -445,10 +469,10 @@ var opened = function opened(rule, index, _ref) { var sep = _ref.sep; - var _char = rule[index]; + var char = rule[index]; var transition; - switch (_char) { + switch (char) { case ' ': case '(': case ',': @@ -460,7 +484,7 @@ case '\'': case '"': { - sep.symb = _char; + sep.symb = char; transition = TRANSITION.PARAM; break; } @@ -492,16 +516,16 @@ var param = function param(rule, index, _ref2) { var saver = _ref2.saver, sep = _ref2.sep; - var _char2 = rule[index]; + var char = rule[index]; - switch (_char2) { + switch (char) { case '\'': case '"': { var preIndex = index - 1; var before = rule[preIndex]; - if (_char2 === sep.symb && before !== '\\') { + if (char === sep.symb && before !== '\\') { sep.symb = null; saver.saveStr(); return TRANSITION.OPENED; @@ -511,7 +535,7 @@ default: { - saver.saveSymb(_char2); + saver.saveSymb(char); return TRANSITION.PARAM; } } @@ -538,154 +562,6 @@ }; }; - /** - * AdGuard scriptlet rule - */ - // eslint-disable-next-line no-template-curly-in-string - - var ADGUARD_SCRIPTLET_TEMPLATE = '${domains}#%#//scriptlet(${args})'; // eslint-disable-next-line no-template-curly-in-string - - var ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE = '${domains}#@%#//scriptlet(${args})'; - /** - * uBlock scriptlet rule mask - */ - - var UBO_SCRIPTLET_MASK_REG = /#@?#script:inject|#@?#\s*\+js/; - var UBO_SCRIPTLET_MASK_1 = '##+js'; - var UBO_SCRIPTLET_MASK_2 = '##script:inject'; - var UBO_SCRIPTLET_EXCEPTION_MASK_1 = '#@#+js'; - var UBO_SCRIPTLET_EXCEPTION_MASK_2 = '#@#script:inject'; // const UBO_SCRIPT_TAG_MASK = '##^script'; - - /** - * AdBlock Plus snippet rule mask - */ - - var ABP_SCRIPTLET_MASK = '#$#'; - var ABP_SCRIPTLET_EXCEPTION_MASK = '#@$#'; - /** - * AdGuard CSS rule mask - */ - - var ADG_CSS_MASK_REG = /#@?\$#.+?\s*\{.*\}\s*$/g; - /** - * Return array of strings separated by space which not in quotes - * @param {string} str - */ - - var getSentences = function getSentences(str) { - var reg = /'.*?'|".*?"|\S+/g; - return str.match(reg); - }; - /** - * Replace string with data by placeholders - * @param {string} str - * @param {Object} data where keys is placeholdes names - */ - - - var replacePlaceholders = function replacePlaceholders(str, data) { - return Object.keys(data).reduce(function (acc, key) { - var reg = new RegExp("\\$\\{".concat(key, "\\}"), 'g'); - acc = acc.replace(reg, data[key]); - return acc; - }, str); - }; - /** - * Check is AdGuard scriptlet rule - * @param {string} rule rule text - */ - - - var isAdgScriptletRule = function isAdgScriptletRule(rule) { - return rule.indexOf(ADG_SCRIPTLET_MASK) > -1; - }; - /** - * Check is uBO scriptlet rule - * @param {string} rule rule text - */ - - var isUboScriptletRule = function isUboScriptletRule(rule) { - return (rule.indexOf(UBO_SCRIPTLET_MASK_1) > -1 || rule.indexOf(UBO_SCRIPTLET_MASK_2) > -1 || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_1) > -1 || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_2) > -1) && UBO_SCRIPTLET_MASK_REG.test(rule); - }; - /** - * Check is AdBlock Plus snippet - * @param {string} rule rule text - */ - - var isAbpSnippetRule = function isAbpSnippetRule(rule) { - return (rule.indexOf(ABP_SCRIPTLET_MASK) > -1 || rule.indexOf(ABP_SCRIPTLET_EXCEPTION_MASK) > -1) && rule.search(ADG_CSS_MASK_REG) === -1; - }; - /** - * Convert string of UBO scriptlet rule to AdGuard scritlet rule - * @param {string} rule UBO scriptlet rule - */ - - var convertUboToAdg = function convertUboToAdg(rule) { - var domains = getBeforeRegExp(rule, UBO_SCRIPTLET_MASK_REG); - var mask = rule.match(UBO_SCRIPTLET_MASK_REG)[0]; - var template; - - if (mask.indexOf('@') > -1) { - template = ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; - } else { - template = ADGUARD_SCRIPTLET_TEMPLATE; - } - - var args = getStringInBraces(rule).split(/, /g).map(function (arg, index) { - return index === 0 ? "ubo-".concat(arg) : arg; - }).map(function (arg) { - return wrapInDoubleQuotes(arg); - }).join(', '); - return replacePlaceholders(template, { - domains: domains, - args: args - }); - }; - /** - * Convert string of ABP scriptlet rule to AdGuard scritlet rule - * @param {string} rule ABP scriptlet rule - */ - - var convertAbpToAdg = function convertAbpToAdg(rule) { - var SEMICOLON_DIVIDER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/g; - var mask = rule.indexOf(ABP_SCRIPTLET_MASK) > -1 ? ABP_SCRIPTLET_MASK : ABP_SCRIPTLET_EXCEPTION_MASK; - var template = mask === ABP_SCRIPTLET_MASK ? ADGUARD_SCRIPTLET_TEMPLATE : ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; - var domains = substringBefore(rule, mask); - var args = substringAfter(rule, mask); - return args.split(SEMICOLON_DIVIDER).map(function (args) { - return getSentences(args).filter(function (arg) { - return arg; - }).map(function (arg, index) { - return index === 0 ? "abp-".concat(arg) : arg; - }).map(function (arg) { - return wrapInDoubleQuotes(arg); - }).join(', '); - }).map(function (args) { - return replacePlaceholders(template, { - domains: domains, - args: args - }); - }).toString(''); - }; - /** - * Converts scriptlet rule to AdGuard one - * @param {*} rule - */ - - var convertScriptletToAdg = function convertScriptletToAdg(rule) { - var result; - - if (isUboScriptletRule(rule)) { - result = convertUboToAdg(rule); - } else if (isAbpSnippetRule(rule)) { - result = convertAbpToAdg(rule); - } else if (isAdgScriptletRule(rule)) { - result = rule; - } - - return result; - }; - /* eslint-disable max-len */ /** @@ -2814,6 +2690,221 @@ jsonPrune: jsonPrune }); + /** + * AdGuard scriptlet rule + */ + + var ADGUARD_SCRIPTLET_MASK_REG = /#@?%#\/\/scriptlet\(.+\)/; // eslint-disable-next-line no-template-curly-in-string + + var ADGUARD_SCRIPTLET_TEMPLATE = '${domains}#%#//scriptlet(${args})'; // eslint-disable-next-line no-template-curly-in-string + + var ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE = '${domains}#@%#//scriptlet(${args})'; + /** + * uBlock scriptlet rule mask + */ + + var UBO_SCRIPTLET_MASK_REG = /#@?#script:inject|#@?#\s*\+js/; + var UBO_SCRIPTLET_MASK_1 = '##+js'; + var UBO_SCRIPTLET_MASK_2 = '##script:inject'; + var UBO_SCRIPTLET_EXCEPTION_MASK_1 = '#@#+js'; + var UBO_SCRIPTLET_EXCEPTION_MASK_2 = '#@#script:inject'; // eslint-disable-next-line no-template-curly-in-string + + var UBO_SCRIPTLET_TEMPLATE = '${domains}##+js(${args})'; // eslint-disable-next-line no-template-curly-in-string + + var UBO_SCRIPTLET_EXCEPTION_TEMPLATE = '${domains}#@#+js(${args})'; + var UBO_ALIAS_NAME_MARKER = 'ubo-'; + /** + * AdBlock Plus snippet rule mask + */ + + var ABP_SCRIPTLET_MASK = '#$#'; + var ABP_SCRIPTLET_EXCEPTION_MASK = '#@$#'; + /** + * AdGuard CSS rule mask + */ + + var ADG_CSS_MASK_REG = /#@?\$#.+?\s*\{.*\}\s*$/g; + /** + * Returns array of strings separated by space which not in quotes + * @param {string} str + */ + + var getSentences = function getSentences(str) { + var reg = /'.*?'|".*?"|\S+/g; + return str.match(reg); + }; + /** + * Replaces string with data by placeholders + * @param {string} str + * @param {Object} data - where keys are placeholders names + */ + + + var replacePlaceholders = function replacePlaceholders(str, data) { + return Object.keys(data).reduce(function (acc, key) { + var reg = new RegExp("\\$\\{".concat(key, "\\}"), 'g'); + acc = acc.replace(reg, data[key]); + return acc; + }, str); + }; + /** + * Checks is AdGuard scriptlet rule + * @param {string} rule rule text + */ + + + var isAdgScriptletRule = function isAdgScriptletRule(rule) { + return rule.indexOf(ADG_SCRIPTLET_MASK) > -1; + }; + /** + * Checks is uBO scriptlet rule + * @param {string} rule rule text + */ + + var isUboScriptletRule = function isUboScriptletRule(rule) { + return (rule.indexOf(UBO_SCRIPTLET_MASK_1) > -1 || rule.indexOf(UBO_SCRIPTLET_MASK_2) > -1 || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_1) > -1 || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_2) > -1) && UBO_SCRIPTLET_MASK_REG.test(rule); + }; + /** + * Checks is AdBlock Plus snippet + * @param {string} rule rule text + */ + + var isAbpSnippetRule = function isAbpSnippetRule(rule) { + return (rule.indexOf(ABP_SCRIPTLET_MASK) > -1 || rule.indexOf(ABP_SCRIPTLET_EXCEPTION_MASK) > -1) && rule.search(ADG_CSS_MASK_REG) === -1; + }; + /** + * Converts string of UBO scriptlet rule to AdGuard scritlet rule + * @param {String} rule - UBO scriptlet rule + */ + + var convertUboToAdg = function convertUboToAdg(rule) { + var domains = getBeforeRegExp(rule, UBO_SCRIPTLET_MASK_REG); + var mask = rule.match(UBO_SCRIPTLET_MASK_REG)[0]; + var template; + + if (mask.indexOf('@') > -1) { + template = ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; + } else { + template = ADGUARD_SCRIPTLET_TEMPLATE; + } + + var args = getStringInBraces(rule).split(/, /g).map(function (arg, index) { + return index === 0 ? "ubo-".concat(arg) : arg; + }).map(function (arg) { + return wrapInDoubleQuotes(arg); + }).join(', '); + var adgRule = replacePlaceholders(template, { + domains: domains, + args: args + }); + return [adgRule]; + }; + /** + * Convert string of ABP scriptlet rule to AdGuard scritlet rule + * @param {String} rule - ABP scriptlet rule + */ + + var convertAbpToAdg = function convertAbpToAdg(rule) { + var SEMICOLON_DIVIDER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/g; + var mask = rule.indexOf(ABP_SCRIPTLET_MASK) > -1 ? ABP_SCRIPTLET_MASK : ABP_SCRIPTLET_EXCEPTION_MASK; + var template = mask === ABP_SCRIPTLET_MASK ? ADGUARD_SCRIPTLET_TEMPLATE : ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; + var domains = substringBefore(rule, mask); + var args = substringAfter(rule, mask); + return args.split(SEMICOLON_DIVIDER).map(function (args) { + return getSentences(args).filter(function (arg) { + return arg; + }).map(function (arg, index) { + return index === 0 ? "abp-".concat(arg) : arg; + }).map(function (arg) { + return wrapInDoubleQuotes(arg); + }).join(', '); + }).map(function (args) { + return replacePlaceholders(template, { + domains: domains, + args: args + }); + }); + }; + /** + * Converts scriptlet rule to AdGuard one + * @param {*} rule + */ + + var convertScriptletToAdg = function convertScriptletToAdg(rule) { + var result; + + if (isUboScriptletRule(rule)) { + result = convertUboToAdg(rule); + } else if (isAbpSnippetRule(rule)) { + result = convertAbpToAdg(rule); + } else if (isAdgScriptletRule(rule)) { + result = rule; + } + + return result; + }; + /** + * Converts UBO scriptlet rule to AdGuard one + * @param {String} rule - AdGuard scriptlet rule + * @returns {String} - UBO scriptlet rule + */ + + var convertAdgToUbo = function convertAdgToUbo(rule) { + var res; + + if (isAdgScriptletRule(rule)) { + var _parseRule = parseRule(rule), + parsedName = _parseRule.name, + parsedParams = _parseRule.args; // object of name and aliases for the Adg-scriptlet + + + var adgScriptletObject = Object.keys(scriptletsList).map(function (el) { + return scriptletsList[el]; + }).map(function (s) { + var _s$names = toArray(s.names), + name = _s$names[0], + aliases = _s$names.slice(1); + + return { + name: name, + aliases: aliases + }; + }).find(function (el) { + return el.name === parsedName; + }); + var aliases = adgScriptletObject.aliases; + + if (aliases.length > 0) { + var uboAlias = adgScriptletObject.aliases // eslint-disable-next-line no-restricted-properties + .find(function (alias) { + return alias.includes(UBO_ALIAS_NAME_MARKER); + }); + + if (uboAlias) { + var mask = rule.match(ADGUARD_SCRIPTLET_MASK_REG)[0]; + var template; + + if (mask.indexOf('@') > -1) { + template = UBO_SCRIPTLET_EXCEPTION_TEMPLATE; + } else { + template = UBO_SCRIPTLET_TEMPLATE; + } + + var domains = getBeforeRegExp(rule, ADGUARD_SCRIPTLET_MASK_REG); + var uboName = uboAlias.replace(UBO_ALIAS_NAME_MARKER, ''); + var args = "".concat(uboName, ", ").concat(parsedParams.join(', ')); + var uboRule = replacePlaceholders(template, { + domains: domains, + args: args + }); + res = uboRule; + } + } + } + + return res; + }; + /** * @typedef {Object} Source - scriptlet properties * @property {string} name Scriptlet name @@ -2908,7 +2999,8 @@ isAbpSnippetRule: isAbpSnippetRule, convertUboToAdg: convertUboToAdg, convertAbpToAdg: convertAbpToAdg, - convertScriptletToAdg: convertScriptletToAdg + convertScriptletToAdg: convertScriptletToAdg, + convertAdgToUbo: convertAdgToUbo }; }(); diff --git a/src/helpers/converter.js b/src/helpers/converter.js index 9b0908167..1da081859 100644 --- a/src/helpers/converter.js +++ b/src/helpers/converter.js @@ -6,11 +6,14 @@ import { getStringInBraces, } from './string-utils'; -import { ADG_SCRIPTLET_MASK } from './parse-rule'; +import { parseRule, ADG_SCRIPTLET_MASK } from './parse-rule'; + +import * as scriptletList from '../scriptlets/scriptletsList'; /** * AdGuard scriptlet rule */ +const ADGUARD_SCRIPTLET_MASK_REG = /#@?%#\/\/scriptlet\(.+\)/; // eslint-disable-next-line no-template-curly-in-string const ADGUARD_SCRIPTLET_TEMPLATE = '${domains}#%#//scriptlet(${args})'; // eslint-disable-next-line no-template-curly-in-string @@ -24,6 +27,12 @@ const UBO_SCRIPTLET_MASK_1 = '##+js'; const UBO_SCRIPTLET_MASK_2 = '##script:inject'; const UBO_SCRIPTLET_EXCEPTION_MASK_1 = '#@#+js'; const UBO_SCRIPTLET_EXCEPTION_MASK_2 = '#@#script:inject'; +// eslint-disable-next-line no-template-curly-in-string +const UBO_SCRIPTLET_TEMPLATE = '${domains}##+js(${args})'; +// eslint-disable-next-line no-template-curly-in-string +const UBO_SCRIPTLET_EXCEPTION_TEMPLATE = '${domains}#@#+js(${args})'; + +const UBO_ALIAS_NAME_MARKER = 'ubo-'; /** * AdBlock Plus snippet rule mask @@ -37,7 +46,7 @@ const ABP_SCRIPTLET_EXCEPTION_MASK = '#@$#'; const ADG_CSS_MASK_REG = /#@?\$#.+?\s*\{.*\}\s*$/g; /** - * Return array of strings separated by space which not in quotes + * Returns array of strings separated by space which not in quotes * @param {string} str */ const getSentences = (str) => { @@ -46,7 +55,7 @@ const getSentences = (str) => { }; /** - * Replace string with data by placeholders + * Replaces string with data by placeholders * @param {string} str * @param {Object} data - where keys are placeholders names */ @@ -60,7 +69,7 @@ const replacePlaceholders = (str, data) => { /** - * Check is AdGuard scriptlet rule + * Checks is AdGuard scriptlet rule * @param {string} rule rule text */ export const isAdgScriptletRule = (rule) => { @@ -68,7 +77,7 @@ export const isAdgScriptletRule = (rule) => { }; /** - * Check is uBO scriptlet rule + * Checks is uBO scriptlet rule * @param {string} rule rule text */ export const isUboScriptletRule = (rule) => { @@ -82,7 +91,7 @@ export const isUboScriptletRule = (rule) => { }; /** - * Check is AdBlock Plus snippet + * Checks is AdBlock Plus snippet * @param {string} rule rule text */ export const isAbpSnippetRule = (rule) => { @@ -94,8 +103,8 @@ export const isAbpSnippetRule = (rule) => { }; /** - * Convert string of UBO scriptlet rule to AdGuard scritlet rule - * @param {string} rule UBO scriptlet rule + * Converts string of UBO scriptlet rule to AdGuard scritlet rule + * @param {String} rule - UBO scriptlet rule */ export const convertUboToAdg = (rule) => { const domains = getBeforeRegExp(rule, UBO_SCRIPTLET_MASK_REG); @@ -111,16 +120,16 @@ export const convertUboToAdg = (rule) => { .map((arg, index) => (index === 0 ? `ubo-${arg}` : arg)) .map((arg) => (wrapInDoubleQuotes(arg))) .join(', '); - - return replacePlaceholders( + const adgRule = replacePlaceholders( template, { domains, args }, - ).split(); + ); + return [adgRule]; }; /** * Convert string of ABP scriptlet rule to AdGuard scritlet rule - * @param {string} rule ABP scriptlet rule + * @param {String} rule - ABP scriptlet rule */ export const convertAbpToAdg = (rule) => { const SEMICOLON_DIVIDER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/g; @@ -158,3 +167,57 @@ export const convertScriptletToAdg = (rule) => { return result; }; + +/** + * Converts UBO scriptlet rule to AdGuard one + * @param {String} rule - AdGuard scriptlet rule + * @returns {String} - UBO scriptlet rule + */ +export const convertAdgToUbo = (rule) => { + let res; + + if (isAdgScriptletRule(rule)) { + const { name: parsedName, args: parsedParams } = parseRule(rule); + + // object of name and aliases for the Adg-scriptlet + const adgScriptletObject = Object + .keys(scriptletList) + .map((el) => scriptletList[el]) + .map((s) => { + const [name, ...aliases] = s.names; + return { name, aliases }; + }) + .find((el) => (el.name === parsedName)); + + const { aliases } = adgScriptletObject; + + if (aliases.length > 0) { + const uboAlias = adgScriptletObject.aliases + // eslint-disable-next-line no-restricted-properties + .find((alias) => (alias.includes(UBO_ALIAS_NAME_MARKER))); + + if (uboAlias) { + const mask = rule.match(ADGUARD_SCRIPTLET_MASK_REG)[0]; + let template; + if (mask.indexOf('@') > -1) { + template = UBO_SCRIPTLET_EXCEPTION_TEMPLATE; + } else { + template = UBO_SCRIPTLET_TEMPLATE; + } + const domains = getBeforeRegExp(rule, ADGUARD_SCRIPTLET_MASK_REG); + const uboName = uboAlias.replace(UBO_ALIAS_NAME_MARKER, ''); + + const args = `${uboName}, ${parsedParams.join(', ')}`; + + const uboRule = replacePlaceholders( + template, + { domains, args }, + ); + + res = uboRule; + } + } + } + + return res; +}; diff --git a/src/helpers/parse-rule.js b/src/helpers/parse-rule.js index 09ca4a14e..35284923b 100644 --- a/src/helpers/parse-rule.js +++ b/src/helpers/parse-rule.js @@ -50,7 +50,7 @@ const substringAfter = (str, separator) => { * @param {*} ruleText * @returns {{name: string, args: Array}} */ -const parseRule = (ruleText) => { +export const parseRule = (ruleText) => { ruleText = substringAfter(ruleText, ADG_SCRIPTLET_MASK); /** * Transition names @@ -144,5 +144,3 @@ const parseRule = (ruleText) => { args: args.slice(1), }; }; - -export default parseRule; diff --git a/src/scriptlets/index.js b/src/scriptlets/index.js index da05e2701..9016667e8 100644 --- a/src/scriptlets/index.js +++ b/src/scriptlets/index.js @@ -12,9 +12,10 @@ import { convertUboToAdg, convertAbpToAdg, convertScriptletToAdg, + convertAdgToUbo, } from '../helpers/converter'; -import parseRule from '../helpers/parse-rule'; +import { parseRule } from '../helpers/parse-rule'; import * as scriptletsList from './scriptletsList'; @@ -107,4 +108,5 @@ scriptlets = (() => ({ convertUboToAdg, convertAbpToAdg, convertScriptletToAdg, + convertAdgToUbo, }))(); diff --git a/tests/lib-tests/index.test.js b/tests/lib-tests/index.test.js index 2c1d477b6..b8db129a3 100644 --- a/tests/lib-tests/index.test.js +++ b/tests/lib-tests/index.test.js @@ -1,6 +1,6 @@ /* global QUnit */ -import { convertScriptletToAdg } from '../../src/helpers/converter'; +import { convertScriptletToAdg, convertAdgToUbo } from '../../src/helpers/converter'; const { test, module } = QUnit; const name = 'debug-current-inline-script'; @@ -56,4 +56,16 @@ test('Test converter scriptlet multiple abp rule', (assert) => { assert.equal(res[0], exp1); assert.equal(res[1], exp2); }); + +test('Test converter UBO scriptlet rule to AdGuard one', (assert) => { + // blocking rule + const rule = 'example.org#%#//scriptlet("prevent-setTimeout", "[native code]", "8000")'; + const exp = 'example.org##+js(setTimeout-defuser.js, [native code], 8000)'; + const res = convertAdgToUbo(rule); + assert.equal(res, exp); + // whitelist rule + const whitelistRule = 'example.org#@%#//scriptlet("prevent-setTimeout", "[native code]", "8000")'; + const expectedResult = 'example.org#@#+js(setTimeout-defuser.js, [native code], 8000)'; + assert.equal(convertAdgToUbo(whitelistRule), expectedResult); +}); /* eslint-enable max-len */ From 14863579bf7557fe1871679c46ad3c8da178d04c Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Thu, 30 Jan 2020 12:14:03 +0300 Subject: [PATCH 08/14] get redirects.js back --- .gitignore | 5 +- dist/redirects.js | 3927 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 3931 insertions(+), 1 deletion(-) create mode 100644 dist/redirects.js diff --git a/.gitignore b/.gitignore index 07a430ef9..1560a6831 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,10 @@ .idea/ .vscode/ *.iml -dist/ +# dist/ +dist/index.js +dist/index.js.map +dist/scriptlets.js.map yarn-error.log node_modules private diff --git a/dist/redirects.js b/dist/redirects.js new file mode 100644 index 000000000..75493be5d --- /dev/null +++ b/dist/redirects.js @@ -0,0 +1,3927 @@ +var Redirects = (function () { + function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; + } + + var defineProperty = _defineProperty; + + function _classCallCheck(instance, Constructor) { + if (!(instance instanceof Constructor)) { + throw new TypeError("Cannot call a class as a function"); + } + } + + var classCallCheck = _classCallCheck; + + function _defineProperties(target, props) { + for (var i = 0; i < props.length; i++) { + var descriptor = props[i]; + descriptor.enumerable = descriptor.enumerable || false; + descriptor.configurable = true; + if ("value" in descriptor) descriptor.writable = true; + Object.defineProperty(target, descriptor.key, descriptor); + } + } + + function _createClass(Constructor, protoProps, staticProps) { + if (protoProps) _defineProperties(Constructor.prototype, protoProps); + if (staticProps) _defineProperties(Constructor, staticProps); + return Constructor; + } + + var createClass = _createClass; + + function isNothing(subject) { + return (typeof subject === 'undefined') || (subject === null); + } + + + function isObject(subject) { + return (typeof subject === 'object') && (subject !== null); + } + + + function toArray(sequence) { + if (Array.isArray(sequence)) return sequence; + else if (isNothing(sequence)) return []; + + return [ sequence ]; + } + + + function extend(target, source) { + var index, length, key, sourceKeys; + + if (source) { + sourceKeys = Object.keys(source); + + for (index = 0, length = sourceKeys.length; index < length; index += 1) { + key = sourceKeys[index]; + target[key] = source[key]; + } + } + + return target; + } + + + function repeat(string, count) { + var result = '', cycle; + + for (cycle = 0; cycle < count; cycle += 1) { + result += string; + } + + return result; + } + + + function isNegativeZero(number) { + return (number === 0) && (Number.NEGATIVE_INFINITY === 1 / number); + } + + + var isNothing_1 = isNothing; + var isObject_1 = isObject; + var toArray_1 = toArray; + var repeat_1 = repeat; + var isNegativeZero_1 = isNegativeZero; + var extend_1 = extend; + + var common = { + isNothing: isNothing_1, + isObject: isObject_1, + toArray: toArray_1, + repeat: repeat_1, + isNegativeZero: isNegativeZero_1, + extend: extend_1 + }; + + // YAML error class. http://stackoverflow.com/questions/8458984 + + function YAMLException(reason, mark) { + // Super constructor + Error.call(this); + + this.name = 'YAMLException'; + this.reason = reason; + this.mark = mark; + this.message = (this.reason || '(unknown reason)') + (this.mark ? ' ' + this.mark.toString() : ''); + + // Include stack trace in error object + if (Error.captureStackTrace) { + // Chrome and NodeJS + Error.captureStackTrace(this, this.constructor); + } else { + // FF, IE 10+ and Safari 6+. Fallback for others + this.stack = (new Error()).stack || ''; + } + } + + + // Inherit from Error + YAMLException.prototype = Object.create(Error.prototype); + YAMLException.prototype.constructor = YAMLException; + + + YAMLException.prototype.toString = function toString(compact) { + var result = this.name + ': '; + + result += this.reason || '(unknown reason)'; + + if (!compact && this.mark) { + result += ' ' + this.mark.toString(); + } + + return result; + }; + + + var exception = YAMLException; + + function Mark(name, buffer, position, line, column) { + this.name = name; + this.buffer = buffer; + this.position = position; + this.line = line; + this.column = column; + } + + + Mark.prototype.getSnippet = function getSnippet(indent, maxLength) { + var head, start, tail, end, snippet; + + if (!this.buffer) return null; + + indent = indent || 4; + maxLength = maxLength || 75; + + head = ''; + start = this.position; + + while (start > 0 && '\x00\r\n\x85\u2028\u2029'.indexOf(this.buffer.charAt(start - 1)) === -1) { + start -= 1; + if (this.position - start > (maxLength / 2 - 1)) { + head = ' ... '; + start += 5; + break; + } + } + + tail = ''; + end = this.position; + + while (end < this.buffer.length && '\x00\r\n\x85\u2028\u2029'.indexOf(this.buffer.charAt(end)) === -1) { + end += 1; + if (end - this.position > (maxLength / 2 - 1)) { + tail = ' ... '; + end -= 5; + break; + } + } + + snippet = this.buffer.slice(start, end); + + return common.repeat(' ', indent) + head + snippet + tail + '\n' + + common.repeat(' ', indent + this.position - start + head.length) + '^'; + }; + + + Mark.prototype.toString = function toString(compact) { + var snippet, where = ''; + + if (this.name) { + where += 'in "' + this.name + '" '; + } + + where += 'at line ' + (this.line + 1) + ', column ' + (this.column + 1); + + if (!compact) { + snippet = this.getSnippet(); + + if (snippet) { + where += ':\n' + snippet; + } + } + + return where; + }; + + + var mark = Mark; + + var TYPE_CONSTRUCTOR_OPTIONS = [ + 'kind', + 'resolve', + 'construct', + 'instanceOf', + 'predicate', + 'represent', + 'defaultStyle', + 'styleAliases' + ]; + + var YAML_NODE_KINDS = [ + 'scalar', + 'sequence', + 'mapping' + ]; + + function compileStyleAliases(map) { + var result = {}; + + if (map !== null) { + Object.keys(map).forEach(function (style) { + map[style].forEach(function (alias) { + result[String(alias)] = style; + }); + }); + } + + return result; + } + + function Type(tag, options) { + options = options || {}; + + Object.keys(options).forEach(function (name) { + if (TYPE_CONSTRUCTOR_OPTIONS.indexOf(name) === -1) { + throw new exception('Unknown option "' + name + '" is met in definition of "' + tag + '" YAML type.'); + } + }); + + // TODO: Add tag format check. + this.tag = tag; + this.kind = options['kind'] || null; + this.resolve = options['resolve'] || function () { return true; }; + this.construct = options['construct'] || function (data) { return data; }; + this.instanceOf = options['instanceOf'] || null; + this.predicate = options['predicate'] || null; + this.represent = options['represent'] || null; + this.defaultStyle = options['defaultStyle'] || null; + this.styleAliases = compileStyleAliases(options['styleAliases'] || null); + + if (YAML_NODE_KINDS.indexOf(this.kind) === -1) { + throw new exception('Unknown kind "' + this.kind + '" is specified for "' + tag + '" YAML type.'); + } + } + + var type = Type; + + /*eslint-disable max-len*/ + + + + + + + function compileList(schema, name, result) { + var exclude = []; + + schema.include.forEach(function (includedSchema) { + result = compileList(includedSchema, name, result); + }); + + schema[name].forEach(function (currentType) { + result.forEach(function (previousType, previousIndex) { + if (previousType.tag === currentType.tag && previousType.kind === currentType.kind) { + exclude.push(previousIndex); + } + }); + + result.push(currentType); + }); + + return result.filter(function (type, index) { + return exclude.indexOf(index) === -1; + }); + } + + + function compileMap(/* lists... */) { + var result = { + scalar: {}, + sequence: {}, + mapping: {}, + fallback: {} + }, index, length; + + function collectType(type) { + result[type.kind][type.tag] = result['fallback'][type.tag] = type; + } + + for (index = 0, length = arguments.length; index < length; index += 1) { + arguments[index].forEach(collectType); + } + return result; + } + + + function Schema(definition) { + this.include = definition.include || []; + this.implicit = definition.implicit || []; + this.explicit = definition.explicit || []; + + this.implicit.forEach(function (type) { + if (type.loadKind && type.loadKind !== 'scalar') { + throw new exception('There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.'); + } + }); + + this.compiledImplicit = compileList(this, 'implicit', []); + this.compiledExplicit = compileList(this, 'explicit', []); + this.compiledTypeMap = compileMap(this.compiledImplicit, this.compiledExplicit); + } + + + Schema.DEFAULT = null; + + + Schema.create = function createSchema() { + var schemas, types; + + switch (arguments.length) { + case 1: + schemas = Schema.DEFAULT; + types = arguments[0]; + break; + + case 2: + schemas = arguments[0]; + types = arguments[1]; + break; + + default: + throw new exception('Wrong number of arguments for Schema.create function'); + } + + schemas = common.toArray(schemas); + types = common.toArray(types); + + if (!schemas.every(function (schema) { return schema instanceof Schema; })) { + throw new exception('Specified list of super schemas (or a single Schema object) contains a non-Schema object.'); + } + + if (!types.every(function (type$1) { return type$1 instanceof type; })) { + throw new exception('Specified list of YAML types (or a single Type object) contains a non-Type object.'); + } + + return new Schema({ + include: schemas, + explicit: types + }); + }; + + + var schema = Schema; + + var str = new type('tag:yaml.org,2002:str', { + kind: 'scalar', + construct: function (data) { return data !== null ? data : ''; } + }); + + var seq = new type('tag:yaml.org,2002:seq', { + kind: 'sequence', + construct: function (data) { return data !== null ? data : []; } + }); + + var map = new type('tag:yaml.org,2002:map', { + kind: 'mapping', + construct: function (data) { return data !== null ? data : {}; } + }); + + var failsafe = new schema({ + explicit: [ + str, + seq, + map + ] + }); + + function resolveYamlNull(data) { + if (data === null) return true; + + var max = data.length; + + return (max === 1 && data === '~') || + (max === 4 && (data === 'null' || data === 'Null' || data === 'NULL')); + } + + function constructYamlNull() { + return null; + } + + function isNull(object) { + return object === null; + } + + var _null = new type('tag:yaml.org,2002:null', { + kind: 'scalar', + resolve: resolveYamlNull, + construct: constructYamlNull, + predicate: isNull, + represent: { + canonical: function () { return '~'; }, + lowercase: function () { return 'null'; }, + uppercase: function () { return 'NULL'; }, + camelcase: function () { return 'Null'; } + }, + defaultStyle: 'lowercase' + }); + + function resolveYamlBoolean(data) { + if (data === null) return false; + + var max = data.length; + + return (max === 4 && (data === 'true' || data === 'True' || data === 'TRUE')) || + (max === 5 && (data === 'false' || data === 'False' || data === 'FALSE')); + } + + function constructYamlBoolean(data) { + return data === 'true' || + data === 'True' || + data === 'TRUE'; + } + + function isBoolean(object) { + return Object.prototype.toString.call(object) === '[object Boolean]'; + } + + var bool = new type('tag:yaml.org,2002:bool', { + kind: 'scalar', + resolve: resolveYamlBoolean, + construct: constructYamlBoolean, + predicate: isBoolean, + represent: { + lowercase: function (object) { return object ? 'true' : 'false'; }, + uppercase: function (object) { return object ? 'TRUE' : 'FALSE'; }, + camelcase: function (object) { return object ? 'True' : 'False'; } + }, + defaultStyle: 'lowercase' + }); + + function isHexCode(c) { + return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) || + ((0x41/* A */ <= c) && (c <= 0x46/* F */)) || + ((0x61/* a */ <= c) && (c <= 0x66/* f */)); + } + + function isOctCode(c) { + return ((0x30/* 0 */ <= c) && (c <= 0x37/* 7 */)); + } + + function isDecCode(c) { + return ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)); + } + + function resolveYamlInteger(data) { + if (data === null) return false; + + var max = data.length, + index = 0, + hasDigits = false, + ch; + + if (!max) return false; + + ch = data[index]; + + // sign + if (ch === '-' || ch === '+') { + ch = data[++index]; + } + + if (ch === '0') { + // 0 + if (index + 1 === max) return true; + ch = data[++index]; + + // base 2, base 8, base 16 + + if (ch === 'b') { + // base 2 + index++; + + for (; index < max; index++) { + ch = data[index]; + if (ch === '_') continue; + if (ch !== '0' && ch !== '1') return false; + hasDigits = true; + } + return hasDigits && ch !== '_'; + } + + + if (ch === 'x') { + // base 16 + index++; + + for (; index < max; index++) { + ch = data[index]; + if (ch === '_') continue; + if (!isHexCode(data.charCodeAt(index))) return false; + hasDigits = true; + } + return hasDigits && ch !== '_'; + } + + // base 8 + for (; index < max; index++) { + ch = data[index]; + if (ch === '_') continue; + if (!isOctCode(data.charCodeAt(index))) return false; + hasDigits = true; + } + return hasDigits && ch !== '_'; + } + + // base 10 (except 0) or base 60 + + // value should not start with `_`; + if (ch === '_') return false; + + for (; index < max; index++) { + ch = data[index]; + if (ch === '_') continue; + if (ch === ':') break; + if (!isDecCode(data.charCodeAt(index))) { + return false; + } + hasDigits = true; + } + + // Should have digits and should not end with `_` + if (!hasDigits || ch === '_') return false; + + // if !base60 - done; + if (ch !== ':') return true; + + // base60 almost not used, no needs to optimize + return /^(:[0-5]?[0-9])+$/.test(data.slice(index)); + } + + function constructYamlInteger(data) { + var value = data, sign = 1, ch, base, digits = []; + + if (value.indexOf('_') !== -1) { + value = value.replace(/_/g, ''); + } + + ch = value[0]; + + if (ch === '-' || ch === '+') { + if (ch === '-') sign = -1; + value = value.slice(1); + ch = value[0]; + } + + if (value === '0') return 0; + + if (ch === '0') { + if (value[1] === 'b') return sign * parseInt(value.slice(2), 2); + if (value[1] === 'x') return sign * parseInt(value, 16); + return sign * parseInt(value, 8); + } + + if (value.indexOf(':') !== -1) { + value.split(':').forEach(function (v) { + digits.unshift(parseInt(v, 10)); + }); + + value = 0; + base = 1; + + digits.forEach(function (d) { + value += (d * base); + base *= 60; + }); + + return sign * value; + + } + + return sign * parseInt(value, 10); + } + + function isInteger(object) { + return (Object.prototype.toString.call(object)) === '[object Number]' && + (object % 1 === 0 && !common.isNegativeZero(object)); + } + + var int_1 = new type('tag:yaml.org,2002:int', { + kind: 'scalar', + resolve: resolveYamlInteger, + construct: constructYamlInteger, + predicate: isInteger, + represent: { + binary: function (obj) { return obj >= 0 ? '0b' + obj.toString(2) : '-0b' + obj.toString(2).slice(1); }, + octal: function (obj) { return obj >= 0 ? '0' + obj.toString(8) : '-0' + obj.toString(8).slice(1); }, + decimal: function (obj) { return obj.toString(10); }, + /* eslint-disable max-len */ + hexadecimal: function (obj) { return obj >= 0 ? '0x' + obj.toString(16).toUpperCase() : '-0x' + obj.toString(16).toUpperCase().slice(1); } + }, + defaultStyle: 'decimal', + styleAliases: { + binary: [ 2, 'bin' ], + octal: [ 8, 'oct' ], + decimal: [ 10, 'dec' ], + hexadecimal: [ 16, 'hex' ] + } + }); + + var YAML_FLOAT_PATTERN = new RegExp( + // 2.5e4, 2.5 and integers + '^(?:[-+]?(?:0|[1-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?' + + // .2e4, .2 + // special case, seems not from spec + '|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?' + + // 20:59 + '|[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]*' + + // .inf + '|[-+]?\\.(?:inf|Inf|INF)' + + // .nan + '|\\.(?:nan|NaN|NAN))$'); + + function resolveYamlFloat(data) { + if (data === null) return false; + + if (!YAML_FLOAT_PATTERN.test(data) || + // Quick hack to not allow integers end with `_` + // Probably should update regexp & check speed + data[data.length - 1] === '_') { + return false; + } + + return true; + } + + function constructYamlFloat(data) { + var value, sign, base, digits; + + value = data.replace(/_/g, '').toLowerCase(); + sign = value[0] === '-' ? -1 : 1; + digits = []; + + if ('+-'.indexOf(value[0]) >= 0) { + value = value.slice(1); + } + + if (value === '.inf') { + return (sign === 1) ? Number.POSITIVE_INFINITY : Number.NEGATIVE_INFINITY; + + } else if (value === '.nan') { + return NaN; + + } else if (value.indexOf(':') >= 0) { + value.split(':').forEach(function (v) { + digits.unshift(parseFloat(v, 10)); + }); + + value = 0.0; + base = 1; + + digits.forEach(function (d) { + value += d * base; + base *= 60; + }); + + return sign * value; + + } + return sign * parseFloat(value, 10); + } + + + var SCIENTIFIC_WITHOUT_DOT = /^[-+]?[0-9]+e/; + + function representYamlFloat(object, style) { + var res; + + if (isNaN(object)) { + switch (style) { + case 'lowercase': return '.nan'; + case 'uppercase': return '.NAN'; + case 'camelcase': return '.NaN'; + } + } else if (Number.POSITIVE_INFINITY === object) { + switch (style) { + case 'lowercase': return '.inf'; + case 'uppercase': return '.INF'; + case 'camelcase': return '.Inf'; + } + } else if (Number.NEGATIVE_INFINITY === object) { + switch (style) { + case 'lowercase': return '-.inf'; + case 'uppercase': return '-.INF'; + case 'camelcase': return '-.Inf'; + } + } else if (common.isNegativeZero(object)) { + return '-0.0'; + } + + res = object.toString(10); + + // JS stringifier can build scientific format without dots: 5e-100, + // while YAML requres dot: 5.e-100. Fix it with simple hack + + return SCIENTIFIC_WITHOUT_DOT.test(res) ? res.replace('e', '.e') : res; + } + + function isFloat(object) { + return (Object.prototype.toString.call(object) === '[object Number]') && + (object % 1 !== 0 || common.isNegativeZero(object)); + } + + var float_1 = new type('tag:yaml.org,2002:float', { + kind: 'scalar', + resolve: resolveYamlFloat, + construct: constructYamlFloat, + predicate: isFloat, + represent: representYamlFloat, + defaultStyle: 'lowercase' + }); + + var json = new schema({ + include: [ + failsafe + ], + implicit: [ + _null, + bool, + int_1, + float_1 + ] + }); + + var core = new schema({ + include: [ + json + ] + }); + + var YAML_DATE_REGEXP = new RegExp( + '^([0-9][0-9][0-9][0-9])' + // [1] year + '-([0-9][0-9])' + // [2] month + '-([0-9][0-9])$'); // [3] day + + var YAML_TIMESTAMP_REGEXP = new RegExp( + '^([0-9][0-9][0-9][0-9])' + // [1] year + '-([0-9][0-9]?)' + // [2] month + '-([0-9][0-9]?)' + // [3] day + '(?:[Tt]|[ \\t]+)' + // ... + '([0-9][0-9]?)' + // [4] hour + ':([0-9][0-9])' + // [5] minute + ':([0-9][0-9])' + // [6] second + '(?:\\.([0-9]*))?' + // [7] fraction + '(?:[ \\t]*(Z|([-+])([0-9][0-9]?)' + // [8] tz [9] tz_sign [10] tz_hour + '(?::([0-9][0-9]))?))?$'); // [11] tz_minute + + function resolveYamlTimestamp(data) { + if (data === null) return false; + if (YAML_DATE_REGEXP.exec(data) !== null) return true; + if (YAML_TIMESTAMP_REGEXP.exec(data) !== null) return true; + return false; + } + + function constructYamlTimestamp(data) { + var match, year, month, day, hour, minute, second, fraction = 0, + delta = null, tz_hour, tz_minute, date; + + match = YAML_DATE_REGEXP.exec(data); + if (match === null) match = YAML_TIMESTAMP_REGEXP.exec(data); + + if (match === null) throw new Error('Date resolve error'); + + // match: [1] year [2] month [3] day + + year = +(match[1]); + month = +(match[2]) - 1; // JS month starts with 0 + day = +(match[3]); + + if (!match[4]) { // no hour + return new Date(Date.UTC(year, month, day)); + } + + // match: [4] hour [5] minute [6] second [7] fraction + + hour = +(match[4]); + minute = +(match[5]); + second = +(match[6]); + + if (match[7]) { + fraction = match[7].slice(0, 3); + while (fraction.length < 3) { // milli-seconds + fraction += '0'; + } + fraction = +fraction; + } + + // match: [8] tz [9] tz_sign [10] tz_hour [11] tz_minute + + if (match[9]) { + tz_hour = +(match[10]); + tz_minute = +(match[11] || 0); + delta = (tz_hour * 60 + tz_minute) * 60000; // delta in mili-seconds + if (match[9] === '-') delta = -delta; + } + + date = new Date(Date.UTC(year, month, day, hour, minute, second, fraction)); + + if (delta) date.setTime(date.getTime() - delta); + + return date; + } + + function representYamlTimestamp(object /*, style*/) { + return object.toISOString(); + } + + var timestamp = new type('tag:yaml.org,2002:timestamp', { + kind: 'scalar', + resolve: resolveYamlTimestamp, + construct: constructYamlTimestamp, + instanceOf: Date, + represent: representYamlTimestamp + }); + + function resolveYamlMerge(data) { + return data === '<<' || data === null; + } + + var merge = new type('tag:yaml.org,2002:merge', { + kind: 'scalar', + resolve: resolveYamlMerge + }); + + function commonjsRequire () { + throw new Error('Dynamic requires are not currently supported by rollup-plugin-commonjs'); + } + + /*eslint-disable no-bitwise*/ + + var NodeBuffer; + + try { + // A trick for browserified version, to not include `Buffer` shim + var _require = commonjsRequire; + NodeBuffer = _require('buffer').Buffer; + } catch (__) {} + + + + + // [ 64, 65, 66 ] -> [ padding, CR, LF ] + var BASE64_MAP = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n\r'; + + + function resolveYamlBinary(data) { + if (data === null) return false; + + var code, idx, bitlen = 0, max = data.length, map = BASE64_MAP; + + // Convert one by one. + for (idx = 0; idx < max; idx++) { + code = map.indexOf(data.charAt(idx)); + + // Skip CR/LF + if (code > 64) continue; + + // Fail on illegal characters + if (code < 0) return false; + + bitlen += 6; + } + + // If there are any bits left, source was corrupted + return (bitlen % 8) === 0; + } + + function constructYamlBinary(data) { + var idx, tailbits, + input = data.replace(/[\r\n=]/g, ''), // remove CR/LF & padding to simplify scan + max = input.length, + map = BASE64_MAP, + bits = 0, + result = []; + + // Collect by 6*4 bits (3 bytes) + + for (idx = 0; idx < max; idx++) { + if ((idx % 4 === 0) && idx) { + result.push((bits >> 16) & 0xFF); + result.push((bits >> 8) & 0xFF); + result.push(bits & 0xFF); + } + + bits = (bits << 6) | map.indexOf(input.charAt(idx)); + } + + // Dump tail + + tailbits = (max % 4) * 6; + + if (tailbits === 0) { + result.push((bits >> 16) & 0xFF); + result.push((bits >> 8) & 0xFF); + result.push(bits & 0xFF); + } else if (tailbits === 18) { + result.push((bits >> 10) & 0xFF); + result.push((bits >> 2) & 0xFF); + } else if (tailbits === 12) { + result.push((bits >> 4) & 0xFF); + } + + // Wrap into Buffer for NodeJS and leave Array for browser + if (NodeBuffer) { + // Support node 6.+ Buffer API when available + return NodeBuffer.from ? NodeBuffer.from(result) : new NodeBuffer(result); + } + + return result; + } + + function representYamlBinary(object /*, style*/) { + var result = '', bits = 0, idx, tail, + max = object.length, + map = BASE64_MAP; + + // Convert every three bytes to 4 ASCII characters. + + for (idx = 0; idx < max; idx++) { + if ((idx % 3 === 0) && idx) { + result += map[(bits >> 18) & 0x3F]; + result += map[(bits >> 12) & 0x3F]; + result += map[(bits >> 6) & 0x3F]; + result += map[bits & 0x3F]; + } + + bits = (bits << 8) + object[idx]; + } + + // Dump tail + + tail = max % 3; + + if (tail === 0) { + result += map[(bits >> 18) & 0x3F]; + result += map[(bits >> 12) & 0x3F]; + result += map[(bits >> 6) & 0x3F]; + result += map[bits & 0x3F]; + } else if (tail === 2) { + result += map[(bits >> 10) & 0x3F]; + result += map[(bits >> 4) & 0x3F]; + result += map[(bits << 2) & 0x3F]; + result += map[64]; + } else if (tail === 1) { + result += map[(bits >> 2) & 0x3F]; + result += map[(bits << 4) & 0x3F]; + result += map[64]; + result += map[64]; + } + + return result; + } + + function isBinary(object) { + return NodeBuffer && NodeBuffer.isBuffer(object); + } + + var binary = new type('tag:yaml.org,2002:binary', { + kind: 'scalar', + resolve: resolveYamlBinary, + construct: constructYamlBinary, + predicate: isBinary, + represent: representYamlBinary + }); + + var _hasOwnProperty = Object.prototype.hasOwnProperty; + var _toString = Object.prototype.toString; + + function resolveYamlOmap(data) { + if (data === null) return true; + + var objectKeys = [], index, length, pair, pairKey, pairHasKey, + object = data; + + for (index = 0, length = object.length; index < length; index += 1) { + pair = object[index]; + pairHasKey = false; + + if (_toString.call(pair) !== '[object Object]') return false; + + for (pairKey in pair) { + if (_hasOwnProperty.call(pair, pairKey)) { + if (!pairHasKey) pairHasKey = true; + else return false; + } + } + + if (!pairHasKey) return false; + + if (objectKeys.indexOf(pairKey) === -1) objectKeys.push(pairKey); + else return false; + } + + return true; + } + + function constructYamlOmap(data) { + return data !== null ? data : []; + } + + var omap = new type('tag:yaml.org,2002:omap', { + kind: 'sequence', + resolve: resolveYamlOmap, + construct: constructYamlOmap + }); + + var _toString$1 = Object.prototype.toString; + + function resolveYamlPairs(data) { + if (data === null) return true; + + var index, length, pair, keys, result, + object = data; + + result = new Array(object.length); + + for (index = 0, length = object.length; index < length; index += 1) { + pair = object[index]; + + if (_toString$1.call(pair) !== '[object Object]') return false; + + keys = Object.keys(pair); + + if (keys.length !== 1) return false; + + result[index] = [ keys[0], pair[keys[0]] ]; + } + + return true; + } + + function constructYamlPairs(data) { + if (data === null) return []; + + var index, length, pair, keys, result, + object = data; + + result = new Array(object.length); + + for (index = 0, length = object.length; index < length; index += 1) { + pair = object[index]; + + keys = Object.keys(pair); + + result[index] = [ keys[0], pair[keys[0]] ]; + } + + return result; + } + + var pairs = new type('tag:yaml.org,2002:pairs', { + kind: 'sequence', + resolve: resolveYamlPairs, + construct: constructYamlPairs + }); + + var _hasOwnProperty$1 = Object.prototype.hasOwnProperty; + + function resolveYamlSet(data) { + if (data === null) return true; + + var key, object = data; + + for (key in object) { + if (_hasOwnProperty$1.call(object, key)) { + if (object[key] !== null) return false; + } + } + + return true; + } + + function constructYamlSet(data) { + return data !== null ? data : {}; + } + + var set = new type('tag:yaml.org,2002:set', { + kind: 'mapping', + resolve: resolveYamlSet, + construct: constructYamlSet + }); + + var default_safe = new schema({ + include: [ + core + ], + implicit: [ + timestamp, + merge + ], + explicit: [ + binary, + omap, + pairs, + set + ] + }); + + function resolveJavascriptUndefined() { + return true; + } + + function constructJavascriptUndefined() { + /*eslint-disable no-undefined*/ + return undefined; + } + + function representJavascriptUndefined() { + return ''; + } + + function isUndefined(object) { + return typeof object === 'undefined'; + } + + var _undefined = new type('tag:yaml.org,2002:js/undefined', { + kind: 'scalar', + resolve: resolveJavascriptUndefined, + construct: constructJavascriptUndefined, + predicate: isUndefined, + represent: representJavascriptUndefined + }); + + function resolveJavascriptRegExp(data) { + if (data === null) return false; + if (data.length === 0) return false; + + var regexp = data, + tail = /\/([gim]*)$/.exec(data), + modifiers = ''; + + // if regexp starts with '/' it can have modifiers and must be properly closed + // `/foo/gim` - modifiers tail can be maximum 3 chars + if (regexp[0] === '/') { + if (tail) modifiers = tail[1]; + + if (modifiers.length > 3) return false; + // if expression starts with /, is should be properly terminated + if (regexp[regexp.length - modifiers.length - 1] !== '/') return false; + } + + return true; + } + + function constructJavascriptRegExp(data) { + var regexp = data, + tail = /\/([gim]*)$/.exec(data), + modifiers = ''; + + // `/foo/gim` - tail can be maximum 4 chars + if (regexp[0] === '/') { + if (tail) modifiers = tail[1]; + regexp = regexp.slice(1, regexp.length - modifiers.length - 1); + } + + return new RegExp(regexp, modifiers); + } + + function representJavascriptRegExp(object /*, style*/) { + var result = '/' + object.source + '/'; + + if (object.global) result += 'g'; + if (object.multiline) result += 'm'; + if (object.ignoreCase) result += 'i'; + + return result; + } + + function isRegExp(object) { + return Object.prototype.toString.call(object) === '[object RegExp]'; + } + + var regexp = new type('tag:yaml.org,2002:js/regexp', { + kind: 'scalar', + resolve: resolveJavascriptRegExp, + construct: constructJavascriptRegExp, + predicate: isRegExp, + represent: representJavascriptRegExp + }); + + var esprima; + + // Browserified version does not have esprima + // + // 1. For node.js just require module as deps + // 2. For browser try to require mudule via external AMD system. + // If not found - try to fallback to window.esprima. If not + // found too - then fail to parse. + // + try { + // workaround to exclude package from browserify list. + var _require$1 = commonjsRequire; + esprima = _require$1('esprima'); + } catch (_) { + /*global window */ + if (typeof window !== 'undefined') esprima = window.esprima; + } + + + + function resolveJavascriptFunction(data) { + if (data === null) return false; + + try { + var source = '(' + data + ')', + ast = esprima.parse(source, { range: true }); + + if (ast.type !== 'Program' || + ast.body.length !== 1 || + ast.body[0].type !== 'ExpressionStatement' || + (ast.body[0].expression.type !== 'ArrowFunctionExpression' && + ast.body[0].expression.type !== 'FunctionExpression')) { + return false; + } + + return true; + } catch (err) { + return false; + } + } + + function constructJavascriptFunction(data) { + /*jslint evil:true*/ + + var source = '(' + data + ')', + ast = esprima.parse(source, { range: true }), + params = [], + body; + + if (ast.type !== 'Program' || + ast.body.length !== 1 || + ast.body[0].type !== 'ExpressionStatement' || + (ast.body[0].expression.type !== 'ArrowFunctionExpression' && + ast.body[0].expression.type !== 'FunctionExpression')) { + throw new Error('Failed to resolve function'); + } + + ast.body[0].expression.params.forEach(function (param) { + params.push(param.name); + }); + + body = ast.body[0].expression.body.range; + + // Esprima's ranges include the first '{' and the last '}' characters on + // function expressions. So cut them out. + if (ast.body[0].expression.body.type === 'BlockStatement') { + /*eslint-disable no-new-func*/ + return new Function(params, source.slice(body[0] + 1, body[1] - 1)); + } + // ES6 arrow functions can omit the BlockStatement. In that case, just return + // the body. + /*eslint-disable no-new-func*/ + return new Function(params, 'return ' + source.slice(body[0], body[1])); + } + + function representJavascriptFunction(object /*, style*/) { + return object.toString(); + } + + function isFunction(object) { + return Object.prototype.toString.call(object) === '[object Function]'; + } + + var _function = new type('tag:yaml.org,2002:js/function', { + kind: 'scalar', + resolve: resolveJavascriptFunction, + construct: constructJavascriptFunction, + predicate: isFunction, + represent: representJavascriptFunction + }); + + var default_full = schema.DEFAULT = new schema({ + include: [ + default_safe + ], + explicit: [ + _undefined, + regexp, + _function + ] + }); + + /*eslint-disable max-len,no-use-before-define*/ + + + + + + + + + var _hasOwnProperty$2 = Object.prototype.hasOwnProperty; + + + var CONTEXT_FLOW_IN = 1; + var CONTEXT_FLOW_OUT = 2; + var CONTEXT_BLOCK_IN = 3; + var CONTEXT_BLOCK_OUT = 4; + + + var CHOMPING_CLIP = 1; + var CHOMPING_STRIP = 2; + var CHOMPING_KEEP = 3; + + + var PATTERN_NON_PRINTABLE = /[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/; + var PATTERN_NON_ASCII_LINE_BREAKS = /[\x85\u2028\u2029]/; + var PATTERN_FLOW_INDICATORS = /[,\[\]\{\}]/; + var PATTERN_TAG_HANDLE = /^(?:!|!!|![a-z\-]+!)$/i; + var PATTERN_TAG_URI = /^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i; + + + function _class(obj) { return Object.prototype.toString.call(obj); } + + function is_EOL(c) { + return (c === 0x0A/* LF */) || (c === 0x0D/* CR */); + } + + function is_WHITE_SPACE(c) { + return (c === 0x09/* Tab */) || (c === 0x20/* Space */); + } + + function is_WS_OR_EOL(c) { + return (c === 0x09/* Tab */) || + (c === 0x20/* Space */) || + (c === 0x0A/* LF */) || + (c === 0x0D/* CR */); + } + + function is_FLOW_INDICATOR(c) { + return c === 0x2C/* , */ || + c === 0x5B/* [ */ || + c === 0x5D/* ] */ || + c === 0x7B/* { */ || + c === 0x7D/* } */; + } + + function fromHexCode(c) { + var lc; + + if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) { + return c - 0x30; + } + + /*eslint-disable no-bitwise*/ + lc = c | 0x20; + + if ((0x61/* a */ <= lc) && (lc <= 0x66/* f */)) { + return lc - 0x61 + 10; + } + + return -1; + } + + function escapedHexLen(c) { + if (c === 0x78/* x */) { return 2; } + if (c === 0x75/* u */) { return 4; } + if (c === 0x55/* U */) { return 8; } + return 0; + } + + function fromDecimalCode(c) { + if ((0x30/* 0 */ <= c) && (c <= 0x39/* 9 */)) { + return c - 0x30; + } + + return -1; + } + + function simpleEscapeSequence(c) { + /* eslint-disable indent */ + return (c === 0x30/* 0 */) ? '\x00' : + (c === 0x61/* a */) ? '\x07' : + (c === 0x62/* b */) ? '\x08' : + (c === 0x74/* t */) ? '\x09' : + (c === 0x09/* Tab */) ? '\x09' : + (c === 0x6E/* n */) ? '\x0A' : + (c === 0x76/* v */) ? '\x0B' : + (c === 0x66/* f */) ? '\x0C' : + (c === 0x72/* r */) ? '\x0D' : + (c === 0x65/* e */) ? '\x1B' : + (c === 0x20/* Space */) ? ' ' : + (c === 0x22/* " */) ? '\x22' : + (c === 0x2F/* / */) ? '/' : + (c === 0x5C/* \ */) ? '\x5C' : + (c === 0x4E/* N */) ? '\x85' : + (c === 0x5F/* _ */) ? '\xA0' : + (c === 0x4C/* L */) ? '\u2028' : + (c === 0x50/* P */) ? '\u2029' : ''; + } + + function charFromCodepoint(c) { + if (c <= 0xFFFF) { + return String.fromCharCode(c); + } + // Encode UTF-16 surrogate pair + // https://en.wikipedia.org/wiki/UTF-16#Code_points_U.2B010000_to_U.2B10FFFF + return String.fromCharCode( + ((c - 0x010000) >> 10) + 0xD800, + ((c - 0x010000) & 0x03FF) + 0xDC00 + ); + } + + var simpleEscapeCheck = new Array(256); // integer, for fast access + var simpleEscapeMap = new Array(256); + for (var i = 0; i < 256; i++) { + simpleEscapeCheck[i] = simpleEscapeSequence(i) ? 1 : 0; + simpleEscapeMap[i] = simpleEscapeSequence(i); + } + + + function State(input, options) { + this.input = input; + + this.filename = options['filename'] || null; + this.schema = options['schema'] || default_full; + this.onWarning = options['onWarning'] || null; + this.legacy = options['legacy'] || false; + this.json = options['json'] || false; + this.listener = options['listener'] || null; + + this.implicitTypes = this.schema.compiledImplicit; + this.typeMap = this.schema.compiledTypeMap; + + this.length = input.length; + this.position = 0; + this.line = 0; + this.lineStart = 0; + this.lineIndent = 0; + + this.documents = []; + + /* + this.version; + this.checkLineBreaks; + this.tagMap; + this.anchorMap; + this.tag; + this.anchor; + this.kind; + this.result;*/ + + } + + + function generateError(state, message) { + return new exception( + message, + new mark(state.filename, state.input, state.position, state.line, (state.position - state.lineStart))); + } + + function throwError(state, message) { + throw generateError(state, message); + } + + function throwWarning(state, message) { + if (state.onWarning) { + state.onWarning.call(null, generateError(state, message)); + } + } + + + var directiveHandlers = { + + YAML: function handleYamlDirective(state, name, args) { + + var match, major, minor; + + if (state.version !== null) { + throwError(state, 'duplication of %YAML directive'); + } + + if (args.length !== 1) { + throwError(state, 'YAML directive accepts exactly one argument'); + } + + match = /^([0-9]+)\.([0-9]+)$/.exec(args[0]); + + if (match === null) { + throwError(state, 'ill-formed argument of the YAML directive'); + } + + major = parseInt(match[1], 10); + minor = parseInt(match[2], 10); + + if (major !== 1) { + throwError(state, 'unacceptable YAML version of the document'); + } + + state.version = args[0]; + state.checkLineBreaks = (minor < 2); + + if (minor !== 1 && minor !== 2) { + throwWarning(state, 'unsupported YAML version of the document'); + } + }, + + TAG: function handleTagDirective(state, name, args) { + + var handle, prefix; + + if (args.length !== 2) { + throwError(state, 'TAG directive accepts exactly two arguments'); + } + + handle = args[0]; + prefix = args[1]; + + if (!PATTERN_TAG_HANDLE.test(handle)) { + throwError(state, 'ill-formed tag handle (first argument) of the TAG directive'); + } + + if (_hasOwnProperty$2.call(state.tagMap, handle)) { + throwError(state, 'there is a previously declared suffix for "' + handle + '" tag handle'); + } + + if (!PATTERN_TAG_URI.test(prefix)) { + throwError(state, 'ill-formed tag prefix (second argument) of the TAG directive'); + } + + state.tagMap[handle] = prefix; + } + }; + + + function captureSegment(state, start, end, checkJson) { + var _position, _length, _character, _result; + + if (start < end) { + _result = state.input.slice(start, end); + + if (checkJson) { + for (_position = 0, _length = _result.length; _position < _length; _position += 1) { + _character = _result.charCodeAt(_position); + if (!(_character === 0x09 || + (0x20 <= _character && _character <= 0x10FFFF))) { + throwError(state, 'expected valid JSON character'); + } + } + } else if (PATTERN_NON_PRINTABLE.test(_result)) { + throwError(state, 'the stream contains non-printable characters'); + } + + state.result += _result; + } + } + + function mergeMappings(state, destination, source, overridableKeys) { + var sourceKeys, key, index, quantity; + + if (!common.isObject(source)) { + throwError(state, 'cannot merge mappings; the provided source object is unacceptable'); + } + + sourceKeys = Object.keys(source); + + for (index = 0, quantity = sourceKeys.length; index < quantity; index += 1) { + key = sourceKeys[index]; + + if (!_hasOwnProperty$2.call(destination, key)) { + destination[key] = source[key]; + overridableKeys[key] = true; + } + } + } + + function storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, startLine, startPos) { + var index, quantity; + + // The output is a plain object here, so keys can only be strings. + // We need to convert keyNode to a string, but doing so can hang the process + // (deeply nested arrays that explode exponentially using aliases). + if (Array.isArray(keyNode)) { + keyNode = Array.prototype.slice.call(keyNode); + + for (index = 0, quantity = keyNode.length; index < quantity; index += 1) { + if (Array.isArray(keyNode[index])) { + throwError(state, 'nested arrays are not supported inside keys'); + } + + if (typeof keyNode === 'object' && _class(keyNode[index]) === '[object Object]') { + keyNode[index] = '[object Object]'; + } + } + } + + // Avoid code execution in load() via toString property + // (still use its own toString for arrays, timestamps, + // and whatever user schema extensions happen to have @@toStringTag) + if (typeof keyNode === 'object' && _class(keyNode) === '[object Object]') { + keyNode = '[object Object]'; + } + + + keyNode = String(keyNode); + + if (_result === null) { + _result = {}; + } + + if (keyTag === 'tag:yaml.org,2002:merge') { + if (Array.isArray(valueNode)) { + for (index = 0, quantity = valueNode.length; index < quantity; index += 1) { + mergeMappings(state, _result, valueNode[index], overridableKeys); + } + } else { + mergeMappings(state, _result, valueNode, overridableKeys); + } + } else { + if (!state.json && + !_hasOwnProperty$2.call(overridableKeys, keyNode) && + _hasOwnProperty$2.call(_result, keyNode)) { + state.line = startLine || state.line; + state.position = startPos || state.position; + throwError(state, 'duplicated mapping key'); + } + _result[keyNode] = valueNode; + delete overridableKeys[keyNode]; + } + + return _result; + } + + function readLineBreak(state) { + var ch; + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x0A/* LF */) { + state.position++; + } else if (ch === 0x0D/* CR */) { + state.position++; + if (state.input.charCodeAt(state.position) === 0x0A/* LF */) { + state.position++; + } + } else { + throwError(state, 'a line break is expected'); + } + + state.line += 1; + state.lineStart = state.position; + } + + function skipSeparationSpace(state, allowComments, checkIndent) { + var lineBreaks = 0, + ch = state.input.charCodeAt(state.position); + + while (ch !== 0) { + while (is_WHITE_SPACE(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (allowComments && ch === 0x23/* # */) { + do { + ch = state.input.charCodeAt(++state.position); + } while (ch !== 0x0A/* LF */ && ch !== 0x0D/* CR */ && ch !== 0); + } + + if (is_EOL(ch)) { + readLineBreak(state); + + ch = state.input.charCodeAt(state.position); + lineBreaks++; + state.lineIndent = 0; + + while (ch === 0x20/* Space */) { + state.lineIndent++; + ch = state.input.charCodeAt(++state.position); + } + } else { + break; + } + } + + if (checkIndent !== -1 && lineBreaks !== 0 && state.lineIndent < checkIndent) { + throwWarning(state, 'deficient indentation'); + } + + return lineBreaks; + } + + function testDocumentSeparator(state) { + var _position = state.position, + ch; + + ch = state.input.charCodeAt(_position); + + // Condition state.position === state.lineStart is tested + // in parent on each call, for efficiency. No needs to test here again. + if ((ch === 0x2D/* - */ || ch === 0x2E/* . */) && + ch === state.input.charCodeAt(_position + 1) && + ch === state.input.charCodeAt(_position + 2)) { + + _position += 3; + + ch = state.input.charCodeAt(_position); + + if (ch === 0 || is_WS_OR_EOL(ch)) { + return true; + } + } + + return false; + } + + function writeFoldedLines(state, count) { + if (count === 1) { + state.result += ' '; + } else if (count > 1) { + state.result += common.repeat('\n', count - 1); + } + } + + + function readPlainScalar(state, nodeIndent, withinFlowCollection) { + var preceding, + following, + captureStart, + captureEnd, + hasPendingContent, + _line, + _lineStart, + _lineIndent, + _kind = state.kind, + _result = state.result, + ch; + + ch = state.input.charCodeAt(state.position); + + if (is_WS_OR_EOL(ch) || + is_FLOW_INDICATOR(ch) || + ch === 0x23/* # */ || + ch === 0x26/* & */ || + ch === 0x2A/* * */ || + ch === 0x21/* ! */ || + ch === 0x7C/* | */ || + ch === 0x3E/* > */ || + ch === 0x27/* ' */ || + ch === 0x22/* " */ || + ch === 0x25/* % */ || + ch === 0x40/* @ */ || + ch === 0x60/* ` */) { + return false; + } + + if (ch === 0x3F/* ? */ || ch === 0x2D/* - */) { + following = state.input.charCodeAt(state.position + 1); + + if (is_WS_OR_EOL(following) || + withinFlowCollection && is_FLOW_INDICATOR(following)) { + return false; + } + } + + state.kind = 'scalar'; + state.result = ''; + captureStart = captureEnd = state.position; + hasPendingContent = false; + + while (ch !== 0) { + if (ch === 0x3A/* : */) { + following = state.input.charCodeAt(state.position + 1); + + if (is_WS_OR_EOL(following) || + withinFlowCollection && is_FLOW_INDICATOR(following)) { + break; + } + + } else if (ch === 0x23/* # */) { + preceding = state.input.charCodeAt(state.position - 1); + + if (is_WS_OR_EOL(preceding)) { + break; + } + + } else if ((state.position === state.lineStart && testDocumentSeparator(state)) || + withinFlowCollection && is_FLOW_INDICATOR(ch)) { + break; + + } else if (is_EOL(ch)) { + _line = state.line; + _lineStart = state.lineStart; + _lineIndent = state.lineIndent; + skipSeparationSpace(state, false, -1); + + if (state.lineIndent >= nodeIndent) { + hasPendingContent = true; + ch = state.input.charCodeAt(state.position); + continue; + } else { + state.position = captureEnd; + state.line = _line; + state.lineStart = _lineStart; + state.lineIndent = _lineIndent; + break; + } + } + + if (hasPendingContent) { + captureSegment(state, captureStart, captureEnd, false); + writeFoldedLines(state, state.line - _line); + captureStart = captureEnd = state.position; + hasPendingContent = false; + } + + if (!is_WHITE_SPACE(ch)) { + captureEnd = state.position + 1; + } + + ch = state.input.charCodeAt(++state.position); + } + + captureSegment(state, captureStart, captureEnd, false); + + if (state.result) { + return true; + } + + state.kind = _kind; + state.result = _result; + return false; + } + + function readSingleQuotedScalar(state, nodeIndent) { + var ch, + captureStart, captureEnd; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x27/* ' */) { + return false; + } + + state.kind = 'scalar'; + state.result = ''; + state.position++; + captureStart = captureEnd = state.position; + + while ((ch = state.input.charCodeAt(state.position)) !== 0) { + if (ch === 0x27/* ' */) { + captureSegment(state, captureStart, state.position, true); + ch = state.input.charCodeAt(++state.position); + + if (ch === 0x27/* ' */) { + captureStart = state.position; + state.position++; + captureEnd = state.position; + } else { + return true; + } + + } else if (is_EOL(ch)) { + captureSegment(state, captureStart, captureEnd, true); + writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); + captureStart = captureEnd = state.position; + + } else if (state.position === state.lineStart && testDocumentSeparator(state)) { + throwError(state, 'unexpected end of the document within a single quoted scalar'); + + } else { + state.position++; + captureEnd = state.position; + } + } + + throwError(state, 'unexpected end of the stream within a single quoted scalar'); + } + + function readDoubleQuotedScalar(state, nodeIndent) { + var captureStart, + captureEnd, + hexLength, + hexResult, + tmp, + ch; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x22/* " */) { + return false; + } + + state.kind = 'scalar'; + state.result = ''; + state.position++; + captureStart = captureEnd = state.position; + + while ((ch = state.input.charCodeAt(state.position)) !== 0) { + if (ch === 0x22/* " */) { + captureSegment(state, captureStart, state.position, true); + state.position++; + return true; + + } else if (ch === 0x5C/* \ */) { + captureSegment(state, captureStart, state.position, true); + ch = state.input.charCodeAt(++state.position); + + if (is_EOL(ch)) { + skipSeparationSpace(state, false, nodeIndent); + + // TODO: rework to inline fn with no type cast? + } else if (ch < 256 && simpleEscapeCheck[ch]) { + state.result += simpleEscapeMap[ch]; + state.position++; + + } else if ((tmp = escapedHexLen(ch)) > 0) { + hexLength = tmp; + hexResult = 0; + + for (; hexLength > 0; hexLength--) { + ch = state.input.charCodeAt(++state.position); + + if ((tmp = fromHexCode(ch)) >= 0) { + hexResult = (hexResult << 4) + tmp; + + } else { + throwError(state, 'expected hexadecimal character'); + } + } + + state.result += charFromCodepoint(hexResult); + + state.position++; + + } else { + throwError(state, 'unknown escape sequence'); + } + + captureStart = captureEnd = state.position; + + } else if (is_EOL(ch)) { + captureSegment(state, captureStart, captureEnd, true); + writeFoldedLines(state, skipSeparationSpace(state, false, nodeIndent)); + captureStart = captureEnd = state.position; + + } else if (state.position === state.lineStart && testDocumentSeparator(state)) { + throwError(state, 'unexpected end of the document within a double quoted scalar'); + + } else { + state.position++; + captureEnd = state.position; + } + } + + throwError(state, 'unexpected end of the stream within a double quoted scalar'); + } + + function readFlowCollection(state, nodeIndent) { + var readNext = true, + _line, + _tag = state.tag, + _result, + _anchor = state.anchor, + following, + terminator, + isPair, + isExplicitPair, + isMapping, + overridableKeys = {}, + keyNode, + keyTag, + valueNode, + ch; + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x5B/* [ */) { + terminator = 0x5D;/* ] */ + isMapping = false; + _result = []; + } else if (ch === 0x7B/* { */) { + terminator = 0x7D;/* } */ + isMapping = true; + _result = {}; + } else { + return false; + } + + if (state.anchor !== null) { + state.anchorMap[state.anchor] = _result; + } + + ch = state.input.charCodeAt(++state.position); + + while (ch !== 0) { + skipSeparationSpace(state, true, nodeIndent); + + ch = state.input.charCodeAt(state.position); + + if (ch === terminator) { + state.position++; + state.tag = _tag; + state.anchor = _anchor; + state.kind = isMapping ? 'mapping' : 'sequence'; + state.result = _result; + return true; + } else if (!readNext) { + throwError(state, 'missed comma between flow collection entries'); + } + + keyTag = keyNode = valueNode = null; + isPair = isExplicitPair = false; + + if (ch === 0x3F/* ? */) { + following = state.input.charCodeAt(state.position + 1); + + if (is_WS_OR_EOL(following)) { + isPair = isExplicitPair = true; + state.position++; + skipSeparationSpace(state, true, nodeIndent); + } + } + + _line = state.line; + composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); + keyTag = state.tag; + keyNode = state.result; + skipSeparationSpace(state, true, nodeIndent); + + ch = state.input.charCodeAt(state.position); + + if ((isExplicitPair || state.line === _line) && ch === 0x3A/* : */) { + isPair = true; + ch = state.input.charCodeAt(++state.position); + skipSeparationSpace(state, true, nodeIndent); + composeNode(state, nodeIndent, CONTEXT_FLOW_IN, false, true); + valueNode = state.result; + } + + if (isMapping) { + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode); + } else if (isPair) { + _result.push(storeMappingPair(state, null, overridableKeys, keyTag, keyNode, valueNode)); + } else { + _result.push(keyNode); + } + + skipSeparationSpace(state, true, nodeIndent); + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x2C/* , */) { + readNext = true; + ch = state.input.charCodeAt(++state.position); + } else { + readNext = false; + } + } + + throwError(state, 'unexpected end of the stream within a flow collection'); + } + + function readBlockScalar(state, nodeIndent) { + var captureStart, + folding, + chomping = CHOMPING_CLIP, + didReadContent = false, + detectedIndent = false, + textIndent = nodeIndent, + emptyLines = 0, + atMoreIndented = false, + tmp, + ch; + + ch = state.input.charCodeAt(state.position); + + if (ch === 0x7C/* | */) { + folding = false; + } else if (ch === 0x3E/* > */) { + folding = true; + } else { + return false; + } + + state.kind = 'scalar'; + state.result = ''; + + while (ch !== 0) { + ch = state.input.charCodeAt(++state.position); + + if (ch === 0x2B/* + */ || ch === 0x2D/* - */) { + if (CHOMPING_CLIP === chomping) { + chomping = (ch === 0x2B/* + */) ? CHOMPING_KEEP : CHOMPING_STRIP; + } else { + throwError(state, 'repeat of a chomping mode identifier'); + } + + } else if ((tmp = fromDecimalCode(ch)) >= 0) { + if (tmp === 0) { + throwError(state, 'bad explicit indentation width of a block scalar; it cannot be less than one'); + } else if (!detectedIndent) { + textIndent = nodeIndent + tmp - 1; + detectedIndent = true; + } else { + throwError(state, 'repeat of an indentation width identifier'); + } + + } else { + break; + } + } + + if (is_WHITE_SPACE(ch)) { + do { ch = state.input.charCodeAt(++state.position); } + while (is_WHITE_SPACE(ch)); + + if (ch === 0x23/* # */) { + do { ch = state.input.charCodeAt(++state.position); } + while (!is_EOL(ch) && (ch !== 0)); + } + } + + while (ch !== 0) { + readLineBreak(state); + state.lineIndent = 0; + + ch = state.input.charCodeAt(state.position); + + while ((!detectedIndent || state.lineIndent < textIndent) && + (ch === 0x20/* Space */)) { + state.lineIndent++; + ch = state.input.charCodeAt(++state.position); + } + + if (!detectedIndent && state.lineIndent > textIndent) { + textIndent = state.lineIndent; + } + + if (is_EOL(ch)) { + emptyLines++; + continue; + } + + // End of the scalar. + if (state.lineIndent < textIndent) { + + // Perform the chomping. + if (chomping === CHOMPING_KEEP) { + state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines); + } else if (chomping === CHOMPING_CLIP) { + if (didReadContent) { // i.e. only if the scalar is not empty. + state.result += '\n'; + } + } + + // Break this `while` cycle and go to the funciton's epilogue. + break; + } + + // Folded style: use fancy rules to handle line breaks. + if (folding) { + + // Lines starting with white space characters (more-indented lines) are not folded. + if (is_WHITE_SPACE(ch)) { + atMoreIndented = true; + // except for the first content line (cf. Example 8.1) + state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines); + + // End of more-indented block. + } else if (atMoreIndented) { + atMoreIndented = false; + state.result += common.repeat('\n', emptyLines + 1); + + // Just one line break - perceive as the same line. + } else if (emptyLines === 0) { + if (didReadContent) { // i.e. only if we have already read some scalar content. + state.result += ' '; + } + + // Several line breaks - perceive as different lines. + } else { + state.result += common.repeat('\n', emptyLines); + } + + // Literal style: just add exact number of line breaks between content lines. + } else { + // Keep all line breaks except the header line break. + state.result += common.repeat('\n', didReadContent ? 1 + emptyLines : emptyLines); + } + + didReadContent = true; + detectedIndent = true; + emptyLines = 0; + captureStart = state.position; + + while (!is_EOL(ch) && (ch !== 0)) { + ch = state.input.charCodeAt(++state.position); + } + + captureSegment(state, captureStart, state.position, false); + } + + return true; + } + + function readBlockSequence(state, nodeIndent) { + var _line, + _tag = state.tag, + _anchor = state.anchor, + _result = [], + following, + detected = false, + ch; + + if (state.anchor !== null) { + state.anchorMap[state.anchor] = _result; + } + + ch = state.input.charCodeAt(state.position); + + while (ch !== 0) { + + if (ch !== 0x2D/* - */) { + break; + } + + following = state.input.charCodeAt(state.position + 1); + + if (!is_WS_OR_EOL(following)) { + break; + } + + detected = true; + state.position++; + + if (skipSeparationSpace(state, true, -1)) { + if (state.lineIndent <= nodeIndent) { + _result.push(null); + ch = state.input.charCodeAt(state.position); + continue; + } + } + + _line = state.line; + composeNode(state, nodeIndent, CONTEXT_BLOCK_IN, false, true); + _result.push(state.result); + skipSeparationSpace(state, true, -1); + + ch = state.input.charCodeAt(state.position); + + if ((state.line === _line || state.lineIndent > nodeIndent) && (ch !== 0)) { + throwError(state, 'bad indentation of a sequence entry'); + } else if (state.lineIndent < nodeIndent) { + break; + } + } + + if (detected) { + state.tag = _tag; + state.anchor = _anchor; + state.kind = 'sequence'; + state.result = _result; + return true; + } + return false; + } + + function readBlockMapping(state, nodeIndent, flowIndent) { + var following, + allowCompact, + _line, + _pos, + _tag = state.tag, + _anchor = state.anchor, + _result = {}, + overridableKeys = {}, + keyTag = null, + keyNode = null, + valueNode = null, + atExplicitKey = false, + detected = false, + ch; + + if (state.anchor !== null) { + state.anchorMap[state.anchor] = _result; + } + + ch = state.input.charCodeAt(state.position); + + while (ch !== 0) { + following = state.input.charCodeAt(state.position + 1); + _line = state.line; // Save the current line. + _pos = state.position; + + // + // Explicit notation case. There are two separate blocks: + // first for the key (denoted by "?") and second for the value (denoted by ":") + // + if ((ch === 0x3F/* ? */ || ch === 0x3A/* : */) && is_WS_OR_EOL(following)) { + + if (ch === 0x3F/* ? */) { + if (atExplicitKey) { + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null); + keyTag = keyNode = valueNode = null; + } + + detected = true; + atExplicitKey = true; + allowCompact = true; + + } else if (atExplicitKey) { + // i.e. 0x3A/* : */ === character after the explicit key. + atExplicitKey = false; + allowCompact = true; + + } else { + throwError(state, 'incomplete explicit mapping pair; a key node is missed; or followed by a non-tabulated empty line'); + } + + state.position += 1; + ch = following; + + // + // Implicit notation case. Flow-style node as the key first, then ":", and the value. + // + } else if (composeNode(state, flowIndent, CONTEXT_FLOW_OUT, false, true)) { + + if (state.line === _line) { + ch = state.input.charCodeAt(state.position); + + while (is_WHITE_SPACE(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (ch === 0x3A/* : */) { + ch = state.input.charCodeAt(++state.position); + + if (!is_WS_OR_EOL(ch)) { + throwError(state, 'a whitespace character is expected after the key-value separator within a block mapping'); + } + + if (atExplicitKey) { + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null); + keyTag = keyNode = valueNode = null; + } + + detected = true; + atExplicitKey = false; + allowCompact = false; + keyTag = state.tag; + keyNode = state.result; + + } else if (detected) { + throwError(state, 'can not read an implicit mapping pair; a colon is missed'); + + } else { + state.tag = _tag; + state.anchor = _anchor; + return true; // Keep the result of `composeNode`. + } + + } else if (detected) { + throwError(state, 'can not read a block mapping entry; a multiline key may not be an implicit key'); + + } else { + state.tag = _tag; + state.anchor = _anchor; + return true; // Keep the result of `composeNode`. + } + + } else { + break; // Reading is done. Go to the epilogue. + } + + // + // Common reading code for both explicit and implicit notations. + // + if (state.line === _line || state.lineIndent > nodeIndent) { + if (composeNode(state, nodeIndent, CONTEXT_BLOCK_OUT, true, allowCompact)) { + if (atExplicitKey) { + keyNode = state.result; + } else { + valueNode = state.result; + } + } + + if (!atExplicitKey) { + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, valueNode, _line, _pos); + keyTag = keyNode = valueNode = null; + } + + skipSeparationSpace(state, true, -1); + ch = state.input.charCodeAt(state.position); + } + + if (state.lineIndent > nodeIndent && (ch !== 0)) { + throwError(state, 'bad indentation of a mapping entry'); + } else if (state.lineIndent < nodeIndent) { + break; + } + } + + // + // Epilogue. + // + + // Special case: last mapping's node contains only the key in explicit notation. + if (atExplicitKey) { + storeMappingPair(state, _result, overridableKeys, keyTag, keyNode, null); + } + + // Expose the resulting mapping. + if (detected) { + state.tag = _tag; + state.anchor = _anchor; + state.kind = 'mapping'; + state.result = _result; + } + + return detected; + } + + function readTagProperty(state) { + var _position, + isVerbatim = false, + isNamed = false, + tagHandle, + tagName, + ch; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x21/* ! */) return false; + + if (state.tag !== null) { + throwError(state, 'duplication of a tag property'); + } + + ch = state.input.charCodeAt(++state.position); + + if (ch === 0x3C/* < */) { + isVerbatim = true; + ch = state.input.charCodeAt(++state.position); + + } else if (ch === 0x21/* ! */) { + isNamed = true; + tagHandle = '!!'; + ch = state.input.charCodeAt(++state.position); + + } else { + tagHandle = '!'; + } + + _position = state.position; + + if (isVerbatim) { + do { ch = state.input.charCodeAt(++state.position); } + while (ch !== 0 && ch !== 0x3E/* > */); + + if (state.position < state.length) { + tagName = state.input.slice(_position, state.position); + ch = state.input.charCodeAt(++state.position); + } else { + throwError(state, 'unexpected end of the stream within a verbatim tag'); + } + } else { + while (ch !== 0 && !is_WS_OR_EOL(ch)) { + + if (ch === 0x21/* ! */) { + if (!isNamed) { + tagHandle = state.input.slice(_position - 1, state.position + 1); + + if (!PATTERN_TAG_HANDLE.test(tagHandle)) { + throwError(state, 'named tag handle cannot contain such characters'); + } + + isNamed = true; + _position = state.position + 1; + } else { + throwError(state, 'tag suffix cannot contain exclamation marks'); + } + } + + ch = state.input.charCodeAt(++state.position); + } + + tagName = state.input.slice(_position, state.position); + + if (PATTERN_FLOW_INDICATORS.test(tagName)) { + throwError(state, 'tag suffix cannot contain flow indicator characters'); + } + } + + if (tagName && !PATTERN_TAG_URI.test(tagName)) { + throwError(state, 'tag name cannot contain such characters: ' + tagName); + } + + if (isVerbatim) { + state.tag = tagName; + + } else if (_hasOwnProperty$2.call(state.tagMap, tagHandle)) { + state.tag = state.tagMap[tagHandle] + tagName; + + } else if (tagHandle === '!') { + state.tag = '!' + tagName; + + } else if (tagHandle === '!!') { + state.tag = 'tag:yaml.org,2002:' + tagName; + + } else { + throwError(state, 'undeclared tag handle "' + tagHandle + '"'); + } + + return true; + } + + function readAnchorProperty(state) { + var _position, + ch; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x26/* & */) return false; + + if (state.anchor !== null) { + throwError(state, 'duplication of an anchor property'); + } + + ch = state.input.charCodeAt(++state.position); + _position = state.position; + + while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (state.position === _position) { + throwError(state, 'name of an anchor node must contain at least one character'); + } + + state.anchor = state.input.slice(_position, state.position); + return true; + } + + function readAlias(state) { + var _position, alias, + ch; + + ch = state.input.charCodeAt(state.position); + + if (ch !== 0x2A/* * */) return false; + + ch = state.input.charCodeAt(++state.position); + _position = state.position; + + while (ch !== 0 && !is_WS_OR_EOL(ch) && !is_FLOW_INDICATOR(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (state.position === _position) { + throwError(state, 'name of an alias node must contain at least one character'); + } + + alias = state.input.slice(_position, state.position); + + if (!state.anchorMap.hasOwnProperty(alias)) { + throwError(state, 'unidentified alias "' + alias + '"'); + } + + state.result = state.anchorMap[alias]; + skipSeparationSpace(state, true, -1); + return true; + } + + function composeNode(state, parentIndent, nodeContext, allowToSeek, allowCompact) { + var allowBlockStyles, + allowBlockScalars, + allowBlockCollections, + indentStatus = 1, // 1: this>parent, 0: this=parent, -1: this parentIndent) { + indentStatus = 1; + } else if (state.lineIndent === parentIndent) { + indentStatus = 0; + } else if (state.lineIndent < parentIndent) { + indentStatus = -1; + } + } + } + + if (indentStatus === 1) { + while (readTagProperty(state) || readAnchorProperty(state)) { + if (skipSeparationSpace(state, true, -1)) { + atNewLine = true; + allowBlockCollections = allowBlockStyles; + + if (state.lineIndent > parentIndent) { + indentStatus = 1; + } else if (state.lineIndent === parentIndent) { + indentStatus = 0; + } else if (state.lineIndent < parentIndent) { + indentStatus = -1; + } + } else { + allowBlockCollections = false; + } + } + } + + if (allowBlockCollections) { + allowBlockCollections = atNewLine || allowCompact; + } + + if (indentStatus === 1 || CONTEXT_BLOCK_OUT === nodeContext) { + if (CONTEXT_FLOW_IN === nodeContext || CONTEXT_FLOW_OUT === nodeContext) { + flowIndent = parentIndent; + } else { + flowIndent = parentIndent + 1; + } + + blockIndent = state.position - state.lineStart; + + if (indentStatus === 1) { + if (allowBlockCollections && + (readBlockSequence(state, blockIndent) || + readBlockMapping(state, blockIndent, flowIndent)) || + readFlowCollection(state, flowIndent)) { + hasContent = true; + } else { + if ((allowBlockScalars && readBlockScalar(state, flowIndent)) || + readSingleQuotedScalar(state, flowIndent) || + readDoubleQuotedScalar(state, flowIndent)) { + hasContent = true; + + } else if (readAlias(state)) { + hasContent = true; + + if (state.tag !== null || state.anchor !== null) { + throwError(state, 'alias node should not have any properties'); + } + + } else if (readPlainScalar(state, flowIndent, CONTEXT_FLOW_IN === nodeContext)) { + hasContent = true; + + if (state.tag === null) { + state.tag = '?'; + } + } + + if (state.anchor !== null) { + state.anchorMap[state.anchor] = state.result; + } + } + } else if (indentStatus === 0) { + // Special case: block sequences are allowed to have same indentation level as the parent. + // http://www.yaml.org/spec/1.2/spec.html#id2799784 + hasContent = allowBlockCollections && readBlockSequence(state, blockIndent); + } + } + + if (state.tag !== null && state.tag !== '!') { + if (state.tag === '?') { + for (typeIndex = 0, typeQuantity = state.implicitTypes.length; typeIndex < typeQuantity; typeIndex += 1) { + type = state.implicitTypes[typeIndex]; + + // Implicit resolving is not allowed for non-scalar types, and '?' + // non-specific tag is only assigned to plain scalars. So, it isn't + // needed to check for 'kind' conformity. + + if (type.resolve(state.result)) { // `state.result` updated in resolver if matched + state.result = type.construct(state.result); + state.tag = type.tag; + if (state.anchor !== null) { + state.anchorMap[state.anchor] = state.result; + } + break; + } + } + } else if (_hasOwnProperty$2.call(state.typeMap[state.kind || 'fallback'], state.tag)) { + type = state.typeMap[state.kind || 'fallback'][state.tag]; + + if (state.result !== null && type.kind !== state.kind) { + throwError(state, 'unacceptable node kind for !<' + state.tag + '> tag; it should be "' + type.kind + '", not "' + state.kind + '"'); + } + + if (!type.resolve(state.result)) { // `state.result` updated in resolver if matched + throwError(state, 'cannot resolve a node with !<' + state.tag + '> explicit tag'); + } else { + state.result = type.construct(state.result); + if (state.anchor !== null) { + state.anchorMap[state.anchor] = state.result; + } + } + } else { + throwError(state, 'unknown tag !<' + state.tag + '>'); + } + } + + if (state.listener !== null) { + state.listener('close', state); + } + return state.tag !== null || state.anchor !== null || hasContent; + } + + function readDocument(state) { + var documentStart = state.position, + _position, + directiveName, + directiveArgs, + hasDirectives = false, + ch; + + state.version = null; + state.checkLineBreaks = state.legacy; + state.tagMap = {}; + state.anchorMap = {}; + + while ((ch = state.input.charCodeAt(state.position)) !== 0) { + skipSeparationSpace(state, true, -1); + + ch = state.input.charCodeAt(state.position); + + if (state.lineIndent > 0 || ch !== 0x25/* % */) { + break; + } + + hasDirectives = true; + ch = state.input.charCodeAt(++state.position); + _position = state.position; + + while (ch !== 0 && !is_WS_OR_EOL(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + directiveName = state.input.slice(_position, state.position); + directiveArgs = []; + + if (directiveName.length < 1) { + throwError(state, 'directive name must not be less than one character in length'); + } + + while (ch !== 0) { + while (is_WHITE_SPACE(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + if (ch === 0x23/* # */) { + do { ch = state.input.charCodeAt(++state.position); } + while (ch !== 0 && !is_EOL(ch)); + break; + } + + if (is_EOL(ch)) break; + + _position = state.position; + + while (ch !== 0 && !is_WS_OR_EOL(ch)) { + ch = state.input.charCodeAt(++state.position); + } + + directiveArgs.push(state.input.slice(_position, state.position)); + } + + if (ch !== 0) readLineBreak(state); + + if (_hasOwnProperty$2.call(directiveHandlers, directiveName)) { + directiveHandlers[directiveName](state, directiveName, directiveArgs); + } else { + throwWarning(state, 'unknown document directive "' + directiveName + '"'); + } + } + + skipSeparationSpace(state, true, -1); + + if (state.lineIndent === 0 && + state.input.charCodeAt(state.position) === 0x2D/* - */ && + state.input.charCodeAt(state.position + 1) === 0x2D/* - */ && + state.input.charCodeAt(state.position + 2) === 0x2D/* - */) { + state.position += 3; + skipSeparationSpace(state, true, -1); + + } else if (hasDirectives) { + throwError(state, 'directives end mark is expected'); + } + + composeNode(state, state.lineIndent - 1, CONTEXT_BLOCK_OUT, false, true); + skipSeparationSpace(state, true, -1); + + if (state.checkLineBreaks && + PATTERN_NON_ASCII_LINE_BREAKS.test(state.input.slice(documentStart, state.position))) { + throwWarning(state, 'non-ASCII line breaks are interpreted as content'); + } + + state.documents.push(state.result); + + if (state.position === state.lineStart && testDocumentSeparator(state)) { + + if (state.input.charCodeAt(state.position) === 0x2E/* . */) { + state.position += 3; + skipSeparationSpace(state, true, -1); + } + return; + } + + if (state.position < (state.length - 1)) { + throwError(state, 'end of the stream or a document separator is expected'); + } else { + return; + } + } + + + function loadDocuments(input, options) { + input = String(input); + options = options || {}; + + if (input.length !== 0) { + + // Add tailing `\n` if not exists + if (input.charCodeAt(input.length - 1) !== 0x0A/* LF */ && + input.charCodeAt(input.length - 1) !== 0x0D/* CR */) { + input += '\n'; + } + + // Strip BOM + if (input.charCodeAt(0) === 0xFEFF) { + input = input.slice(1); + } + } + + var state = new State(input, options); + + // Use 0 as string terminator. That significantly simplifies bounds check. + state.input += '\0'; + + while (state.input.charCodeAt(state.position) === 0x20/* Space */) { + state.lineIndent += 1; + state.position += 1; + } + + while (state.position < (state.length - 1)) { + readDocument(state); + } + + return state.documents; + } + + + function loadAll(input, iterator, options) { + var documents = loadDocuments(input, options), index, length; + + if (typeof iterator !== 'function') { + return documents; + } + + for (index = 0, length = documents.length; index < length; index += 1) { + iterator(documents[index]); + } + } + + + function load(input, options) { + var documents = loadDocuments(input, options); + + if (documents.length === 0) { + /*eslint-disable no-undefined*/ + return undefined; + } else if (documents.length === 1) { + return documents[0]; + } + throw new exception('expected a single document in the stream, but found more'); + } + + + function safeLoadAll(input, output, options) { + if (typeof output === 'function') { + loadAll(input, output, common.extend({ schema: default_safe }, options)); + } else { + return loadAll(input, common.extend({ schema: default_safe }, options)); + } + } + + + function safeLoad(input, options) { + return load(input, common.extend({ schema: default_safe }, options)); + } + + + var loadAll_1 = loadAll; + var load_1 = load; + var safeLoadAll_1 = safeLoadAll; + var safeLoad_1 = safeLoad; + + var loader = { + loadAll: loadAll_1, + load: load_1, + safeLoadAll: safeLoadAll_1, + safeLoad: safeLoad_1 + }; + + /*eslint-disable no-use-before-define*/ + + + + + + + var _toString$2 = Object.prototype.toString; + var _hasOwnProperty$3 = Object.prototype.hasOwnProperty; + + var CHAR_TAB = 0x09; /* Tab */ + var CHAR_LINE_FEED = 0x0A; /* LF */ + var CHAR_SPACE = 0x20; /* Space */ + var CHAR_EXCLAMATION = 0x21; /* ! */ + var CHAR_DOUBLE_QUOTE = 0x22; /* " */ + var CHAR_SHARP = 0x23; /* # */ + var CHAR_PERCENT = 0x25; /* % */ + var CHAR_AMPERSAND = 0x26; /* & */ + var CHAR_SINGLE_QUOTE = 0x27; /* ' */ + var CHAR_ASTERISK = 0x2A; /* * */ + var CHAR_COMMA = 0x2C; /* , */ + var CHAR_MINUS = 0x2D; /* - */ + var CHAR_COLON = 0x3A; /* : */ + var CHAR_GREATER_THAN = 0x3E; /* > */ + var CHAR_QUESTION = 0x3F; /* ? */ + var CHAR_COMMERCIAL_AT = 0x40; /* @ */ + var CHAR_LEFT_SQUARE_BRACKET = 0x5B; /* [ */ + var CHAR_RIGHT_SQUARE_BRACKET = 0x5D; /* ] */ + var CHAR_GRAVE_ACCENT = 0x60; /* ` */ + var CHAR_LEFT_CURLY_BRACKET = 0x7B; /* { */ + var CHAR_VERTICAL_LINE = 0x7C; /* | */ + var CHAR_RIGHT_CURLY_BRACKET = 0x7D; /* } */ + + var ESCAPE_SEQUENCES = {}; + + ESCAPE_SEQUENCES[0x00] = '\\0'; + ESCAPE_SEQUENCES[0x07] = '\\a'; + ESCAPE_SEQUENCES[0x08] = '\\b'; + ESCAPE_SEQUENCES[0x09] = '\\t'; + ESCAPE_SEQUENCES[0x0A] = '\\n'; + ESCAPE_SEQUENCES[0x0B] = '\\v'; + ESCAPE_SEQUENCES[0x0C] = '\\f'; + ESCAPE_SEQUENCES[0x0D] = '\\r'; + ESCAPE_SEQUENCES[0x1B] = '\\e'; + ESCAPE_SEQUENCES[0x22] = '\\"'; + ESCAPE_SEQUENCES[0x5C] = '\\\\'; + ESCAPE_SEQUENCES[0x85] = '\\N'; + ESCAPE_SEQUENCES[0xA0] = '\\_'; + ESCAPE_SEQUENCES[0x2028] = '\\L'; + ESCAPE_SEQUENCES[0x2029] = '\\P'; + + var DEPRECATED_BOOLEANS_SYNTAX = [ + 'y', 'Y', 'yes', 'Yes', 'YES', 'on', 'On', 'ON', + 'n', 'N', 'no', 'No', 'NO', 'off', 'Off', 'OFF' + ]; + + function compileStyleMap(schema, map) { + var result, keys, index, length, tag, style, type; + + if (map === null) return {}; + + result = {}; + keys = Object.keys(map); + + for (index = 0, length = keys.length; index < length; index += 1) { + tag = keys[index]; + style = String(map[tag]); + + if (tag.slice(0, 2) === '!!') { + tag = 'tag:yaml.org,2002:' + tag.slice(2); + } + type = schema.compiledTypeMap['fallback'][tag]; + + if (type && _hasOwnProperty$3.call(type.styleAliases, style)) { + style = type.styleAliases[style]; + } + + result[tag] = style; + } + + return result; + } + + function encodeHex(character) { + var string, handle, length; + + string = character.toString(16).toUpperCase(); + + if (character <= 0xFF) { + handle = 'x'; + length = 2; + } else if (character <= 0xFFFF) { + handle = 'u'; + length = 4; + } else if (character <= 0xFFFFFFFF) { + handle = 'U'; + length = 8; + } else { + throw new exception('code point within a string may not be greater than 0xFFFFFFFF'); + } + + return '\\' + handle + common.repeat('0', length - string.length) + string; + } + + function State$1(options) { + this.schema = options['schema'] || default_full; + this.indent = Math.max(1, (options['indent'] || 2)); + this.noArrayIndent = options['noArrayIndent'] || false; + this.skipInvalid = options['skipInvalid'] || false; + this.flowLevel = (common.isNothing(options['flowLevel']) ? -1 : options['flowLevel']); + this.styleMap = compileStyleMap(this.schema, options['styles'] || null); + this.sortKeys = options['sortKeys'] || false; + this.lineWidth = options['lineWidth'] || 80; + this.noRefs = options['noRefs'] || false; + this.noCompatMode = options['noCompatMode'] || false; + this.condenseFlow = options['condenseFlow'] || false; + + this.implicitTypes = this.schema.compiledImplicit; + this.explicitTypes = this.schema.compiledExplicit; + + this.tag = null; + this.result = ''; + + this.duplicates = []; + this.usedDuplicates = null; + } + + // Indents every line in a string. Empty lines (\n only) are not indented. + function indentString(string, spaces) { + var ind = common.repeat(' ', spaces), + position = 0, + next = -1, + result = '', + line, + length = string.length; + + while (position < length) { + next = string.indexOf('\n', position); + if (next === -1) { + line = string.slice(position); + position = length; + } else { + line = string.slice(position, next + 1); + position = next + 1; + } + + if (line.length && line !== '\n') result += ind; + + result += line; + } + + return result; + } + + function generateNextLine(state, level) { + return '\n' + common.repeat(' ', state.indent * level); + } + + function testImplicitResolving(state, str) { + var index, length, type; + + for (index = 0, length = state.implicitTypes.length; index < length; index += 1) { + type = state.implicitTypes[index]; + + if (type.resolve(str)) { + return true; + } + } + + return false; + } + + // [33] s-white ::= s-space | s-tab + function isWhitespace(c) { + return c === CHAR_SPACE || c === CHAR_TAB; + } + + // Returns true if the character can be printed without escaping. + // From YAML 1.2: "any allowed characters known to be non-printable + // should also be escaped. [However,] This isn’t mandatory" + // Derived from nb-char - \t - #x85 - #xA0 - #x2028 - #x2029. + function isPrintable(c) { + return (0x00020 <= c && c <= 0x00007E) + || ((0x000A1 <= c && c <= 0x00D7FF) && c !== 0x2028 && c !== 0x2029) + || ((0x0E000 <= c && c <= 0x00FFFD) && c !== 0xFEFF /* BOM */) + || (0x10000 <= c && c <= 0x10FFFF); + } + + // Simplified test for values allowed after the first character in plain style. + function isPlainSafe(c) { + // Uses a subset of nb-char - c-flow-indicator - ":" - "#" + // where nb-char ::= c-printable - b-char - c-byte-order-mark. + return isPrintable(c) && c !== 0xFEFF + // - c-flow-indicator + && c !== CHAR_COMMA + && c !== CHAR_LEFT_SQUARE_BRACKET + && c !== CHAR_RIGHT_SQUARE_BRACKET + && c !== CHAR_LEFT_CURLY_BRACKET + && c !== CHAR_RIGHT_CURLY_BRACKET + // - ":" - "#" + && c !== CHAR_COLON + && c !== CHAR_SHARP; + } + + // Simplified test for values allowed as the first character in plain style. + function isPlainSafeFirst(c) { + // Uses a subset of ns-char - c-indicator + // where ns-char = nb-char - s-white. + return isPrintable(c) && c !== 0xFEFF + && !isWhitespace(c) // - s-white + // - (c-indicator ::= + // “-” | “?” | “:” | “,” | “[” | “]” | “{” | “}” + && c !== CHAR_MINUS + && c !== CHAR_QUESTION + && c !== CHAR_COLON + && c !== CHAR_COMMA + && c !== CHAR_LEFT_SQUARE_BRACKET + && c !== CHAR_RIGHT_SQUARE_BRACKET + && c !== CHAR_LEFT_CURLY_BRACKET + && c !== CHAR_RIGHT_CURLY_BRACKET + // | “#” | “&” | “*” | “!” | “|” | “>” | “'” | “"” + && c !== CHAR_SHARP + && c !== CHAR_AMPERSAND + && c !== CHAR_ASTERISK + && c !== CHAR_EXCLAMATION + && c !== CHAR_VERTICAL_LINE + && c !== CHAR_GREATER_THAN + && c !== CHAR_SINGLE_QUOTE + && c !== CHAR_DOUBLE_QUOTE + // | “%” | “@” | “`”) + && c !== CHAR_PERCENT + && c !== CHAR_COMMERCIAL_AT + && c !== CHAR_GRAVE_ACCENT; + } + + // Determines whether block indentation indicator is required. + function needIndentIndicator(string) { + var leadingSpaceRe = /^\n* /; + return leadingSpaceRe.test(string); + } + + var STYLE_PLAIN = 1, + STYLE_SINGLE = 2, + STYLE_LITERAL = 3, + STYLE_FOLDED = 4, + STYLE_DOUBLE = 5; + + // Determines which scalar styles are possible and returns the preferred style. + // lineWidth = -1 => no limit. + // Pre-conditions: str.length > 0. + // Post-conditions: + // STYLE_PLAIN or STYLE_SINGLE => no \n are in the string. + // STYLE_LITERAL => no lines are suitable for folding (or lineWidth is -1). + // STYLE_FOLDED => a line > lineWidth and can be folded (and lineWidth != -1). + function chooseScalarStyle(string, singleLineOnly, indentPerLevel, lineWidth, testAmbiguousType) { + var i; + var char; + var hasLineBreak = false; + var hasFoldableLine = false; // only checked if shouldTrackWidth + var shouldTrackWidth = lineWidth !== -1; + var previousLineBreak = -1; // count the first line correctly + var plain = isPlainSafeFirst(string.charCodeAt(0)) + && !isWhitespace(string.charCodeAt(string.length - 1)); + + if (singleLineOnly) { + // Case: no block styles. + // Check for disallowed characters to rule out plain and single. + for (i = 0; i < string.length; i++) { + char = string.charCodeAt(i); + if (!isPrintable(char)) { + return STYLE_DOUBLE; + } + plain = plain && isPlainSafe(char); + } + } else { + // Case: block styles permitted. + for (i = 0; i < string.length; i++) { + char = string.charCodeAt(i); + if (char === CHAR_LINE_FEED) { + hasLineBreak = true; + // Check if any line can be folded. + if (shouldTrackWidth) { + hasFoldableLine = hasFoldableLine || + // Foldable line = too long, and not more-indented. + (i - previousLineBreak - 1 > lineWidth && + string[previousLineBreak + 1] !== ' '); + previousLineBreak = i; + } + } else if (!isPrintable(char)) { + return STYLE_DOUBLE; + } + plain = plain && isPlainSafe(char); + } + // in case the end is missing a \n + hasFoldableLine = hasFoldableLine || (shouldTrackWidth && + (i - previousLineBreak - 1 > lineWidth && + string[previousLineBreak + 1] !== ' ')); + } + // Although every style can represent \n without escaping, prefer block styles + // for multiline, since they're more readable and they don't add empty lines. + // Also prefer folding a super-long line. + if (!hasLineBreak && !hasFoldableLine) { + // Strings interpretable as another type have to be quoted; + // e.g. the string 'true' vs. the boolean true. + return plain && !testAmbiguousType(string) + ? STYLE_PLAIN : STYLE_SINGLE; + } + // Edge case: block indentation indicator can only have one digit. + if (indentPerLevel > 9 && needIndentIndicator(string)) { + return STYLE_DOUBLE; + } + // At this point we know block styles are valid. + // Prefer literal style unless we want to fold. + return hasFoldableLine ? STYLE_FOLDED : STYLE_LITERAL; + } + + // Note: line breaking/folding is implemented for only the folded style. + // NB. We drop the last trailing newline (if any) of a returned block scalar + // since the dumper adds its own newline. This always works: + // • No ending newline => unaffected; already using strip "-" chomping. + // • Ending newline => removed then restored. + // Importantly, this keeps the "+" chomp indicator from gaining an extra line. + function writeScalar(state, string, level, iskey) { + state.dump = (function () { + if (string.length === 0) { + return "''"; + } + if (!state.noCompatMode && + DEPRECATED_BOOLEANS_SYNTAX.indexOf(string) !== -1) { + return "'" + string + "'"; + } + + var indent = state.indent * Math.max(1, level); // no 0-indent scalars + // As indentation gets deeper, let the width decrease monotonically + // to the lower bound min(state.lineWidth, 40). + // Note that this implies + // state.lineWidth ≤ 40 + state.indent: width is fixed at the lower bound. + // state.lineWidth > 40 + state.indent: width decreases until the lower bound. + // This behaves better than a constant minimum width which disallows narrower options, + // or an indent threshold which causes the width to suddenly increase. + var lineWidth = state.lineWidth === -1 + ? -1 : Math.max(Math.min(state.lineWidth, 40), state.lineWidth - indent); + + // Without knowing if keys are implicit/explicit, assume implicit for safety. + var singleLineOnly = iskey + // No block styles in flow mode. + || (state.flowLevel > -1 && level >= state.flowLevel); + function testAmbiguity(string) { + return testImplicitResolving(state, string); + } + + switch (chooseScalarStyle(string, singleLineOnly, state.indent, lineWidth, testAmbiguity)) { + case STYLE_PLAIN: + return string; + case STYLE_SINGLE: + return "'" + string.replace(/'/g, "''") + "'"; + case STYLE_LITERAL: + return '|' + blockHeader(string, state.indent) + + dropEndingNewline(indentString(string, indent)); + case STYLE_FOLDED: + return '>' + blockHeader(string, state.indent) + + dropEndingNewline(indentString(foldString(string, lineWidth), indent)); + case STYLE_DOUBLE: + return '"' + escapeString(string) + '"'; + default: + throw new exception('impossible error: invalid scalar style'); + } + }()); + } + + // Pre-conditions: string is valid for a block scalar, 1 <= indentPerLevel <= 9. + function blockHeader(string, indentPerLevel) { + var indentIndicator = needIndentIndicator(string) ? String(indentPerLevel) : ''; + + // note the special case: the string '\n' counts as a "trailing" empty line. + var clip = string[string.length - 1] === '\n'; + var keep = clip && (string[string.length - 2] === '\n' || string === '\n'); + var chomp = keep ? '+' : (clip ? '' : '-'); + + return indentIndicator + chomp + '\n'; + } + + // (See the note for writeScalar.) + function dropEndingNewline(string) { + return string[string.length - 1] === '\n' ? string.slice(0, -1) : string; + } + + // Note: a long line without a suitable break point will exceed the width limit. + // Pre-conditions: every char in str isPrintable, str.length > 0, width > 0. + function foldString(string, width) { + // In folded style, $k$ consecutive newlines output as $k+1$ newlines— + // unless they're before or after a more-indented line, or at the very + // beginning or end, in which case $k$ maps to $k$. + // Therefore, parse each chunk as newline(s) followed by a content line. + var lineRe = /(\n+)([^\n]*)/g; + + // first line (possibly an empty line) + var result = (function () { + var nextLF = string.indexOf('\n'); + nextLF = nextLF !== -1 ? nextLF : string.length; + lineRe.lastIndex = nextLF; + return foldLine(string.slice(0, nextLF), width); + }()); + // If we haven't reached the first content line yet, don't add an extra \n. + var prevMoreIndented = string[0] === '\n' || string[0] === ' '; + var moreIndented; + + // rest of the lines + var match; + while ((match = lineRe.exec(string))) { + var prefix = match[1], line = match[2]; + moreIndented = (line[0] === ' '); + result += prefix + + (!prevMoreIndented && !moreIndented && line !== '' + ? '\n' : '') + + foldLine(line, width); + prevMoreIndented = moreIndented; + } + + return result; + } + + // Greedy line breaking. + // Picks the longest line under the limit each time, + // otherwise settles for the shortest line over the limit. + // NB. More-indented lines *cannot* be folded, as that would add an extra \n. + function foldLine(line, width) { + if (line === '' || line[0] === ' ') return line; + + // Since a more-indented line adds a \n, breaks can't be followed by a space. + var breakRe = / [^ ]/g; // note: the match index will always be <= length-2. + var match; + // start is an inclusive index. end, curr, and next are exclusive. + var start = 0, end, curr = 0, next = 0; + var result = ''; + + // Invariants: 0 <= start <= length-1. + // 0 <= curr <= next <= max(0, length-2). curr - start <= width. + // Inside the loop: + // A match implies length >= 2, so curr and next are <= length-2. + while ((match = breakRe.exec(line))) { + next = match.index; + // maintain invariant: curr - start <= width + if (next - start > width) { + end = (curr > start) ? curr : next; // derive end <= length-2 + result += '\n' + line.slice(start, end); + // skip the space that was output as \n + start = end + 1; // derive start <= length-1 + } + curr = next; + } + + // By the invariants, start <= length-1, so there is something left over. + // It is either the whole string or a part starting from non-whitespace. + result += '\n'; + // Insert a break if the remainder is too long and there is a break available. + if (line.length - start > width && curr > start) { + result += line.slice(start, curr) + '\n' + line.slice(curr + 1); + } else { + result += line.slice(start); + } + + return result.slice(1); // drop extra \n joiner + } + + // Escapes a double-quoted string. + function escapeString(string) { + var result = ''; + var char, nextChar; + var escapeSeq; + + for (var i = 0; i < string.length; i++) { + char = string.charCodeAt(i); + // Check for surrogate pairs (reference Unicode 3.0 section "3.7 Surrogates"). + if (char >= 0xD800 && char <= 0xDBFF/* high surrogate */) { + nextChar = string.charCodeAt(i + 1); + if (nextChar >= 0xDC00 && nextChar <= 0xDFFF/* low surrogate */) { + // Combine the surrogate pair and store it escaped. + result += encodeHex((char - 0xD800) * 0x400 + nextChar - 0xDC00 + 0x10000); + // Advance index one extra since we already used that char here. + i++; continue; + } + } + escapeSeq = ESCAPE_SEQUENCES[char]; + result += !escapeSeq && isPrintable(char) + ? string[i] + : escapeSeq || encodeHex(char); + } + + return result; + } + + function writeFlowSequence(state, level, object) { + var _result = '', + _tag = state.tag, + index, + length; + + for (index = 0, length = object.length; index < length; index += 1) { + // Write only valid elements. + if (writeNode(state, level, object[index], false, false)) { + if (index !== 0) _result += ',' + (!state.condenseFlow ? ' ' : ''); + _result += state.dump; + } + } + + state.tag = _tag; + state.dump = '[' + _result + ']'; + } + + function writeBlockSequence(state, level, object, compact) { + var _result = '', + _tag = state.tag, + index, + length; + + for (index = 0, length = object.length; index < length; index += 1) { + // Write only valid elements. + if (writeNode(state, level + 1, object[index], true, true)) { + if (!compact || index !== 0) { + _result += generateNextLine(state, level); + } + + if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { + _result += '-'; + } else { + _result += '- '; + } + + _result += state.dump; + } + } + + state.tag = _tag; + state.dump = _result || '[]'; // Empty sequence if no valid values. + } + + function writeFlowMapping(state, level, object) { + var _result = '', + _tag = state.tag, + objectKeyList = Object.keys(object), + index, + length, + objectKey, + objectValue, + pairBuffer; + + for (index = 0, length = objectKeyList.length; index < length; index += 1) { + pairBuffer = state.condenseFlow ? '"' : ''; + + if (index !== 0) pairBuffer += ', '; + + objectKey = objectKeyList[index]; + objectValue = object[objectKey]; + + if (!writeNode(state, level, objectKey, false, false)) { + continue; // Skip this pair because of invalid key; + } + + if (state.dump.length > 1024) pairBuffer += '? '; + + pairBuffer += state.dump + (state.condenseFlow ? '"' : '') + ':' + (state.condenseFlow ? '' : ' '); + + if (!writeNode(state, level, objectValue, false, false)) { + continue; // Skip this pair because of invalid value. + } + + pairBuffer += state.dump; + + // Both key and value are valid. + _result += pairBuffer; + } + + state.tag = _tag; + state.dump = '{' + _result + '}'; + } + + function writeBlockMapping(state, level, object, compact) { + var _result = '', + _tag = state.tag, + objectKeyList = Object.keys(object), + index, + length, + objectKey, + objectValue, + explicitPair, + pairBuffer; + + // Allow sorting keys so that the output file is deterministic + if (state.sortKeys === true) { + // Default sorting + objectKeyList.sort(); + } else if (typeof state.sortKeys === 'function') { + // Custom sort function + objectKeyList.sort(state.sortKeys); + } else if (state.sortKeys) { + // Something is wrong + throw new exception('sortKeys must be a boolean or a function'); + } + + for (index = 0, length = objectKeyList.length; index < length; index += 1) { + pairBuffer = ''; + + if (!compact || index !== 0) { + pairBuffer += generateNextLine(state, level); + } + + objectKey = objectKeyList[index]; + objectValue = object[objectKey]; + + if (!writeNode(state, level + 1, objectKey, true, true, true)) { + continue; // Skip this pair because of invalid key. + } + + explicitPair = (state.tag !== null && state.tag !== '?') || + (state.dump && state.dump.length > 1024); + + if (explicitPair) { + if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { + pairBuffer += '?'; + } else { + pairBuffer += '? '; + } + } + + pairBuffer += state.dump; + + if (explicitPair) { + pairBuffer += generateNextLine(state, level); + } + + if (!writeNode(state, level + 1, objectValue, true, explicitPair)) { + continue; // Skip this pair because of invalid value. + } + + if (state.dump && CHAR_LINE_FEED === state.dump.charCodeAt(0)) { + pairBuffer += ':'; + } else { + pairBuffer += ': '; + } + + pairBuffer += state.dump; + + // Both key and value are valid. + _result += pairBuffer; + } + + state.tag = _tag; + state.dump = _result || '{}'; // Empty mapping if no valid pairs. + } + + function detectType(state, object, explicit) { + var _result, typeList, index, length, type, style; + + typeList = explicit ? state.explicitTypes : state.implicitTypes; + + for (index = 0, length = typeList.length; index < length; index += 1) { + type = typeList[index]; + + if ((type.instanceOf || type.predicate) && + (!type.instanceOf || ((typeof object === 'object') && (object instanceof type.instanceOf))) && + (!type.predicate || type.predicate(object))) { + + state.tag = explicit ? type.tag : '?'; + + if (type.represent) { + style = state.styleMap[type.tag] || type.defaultStyle; + + if (_toString$2.call(type.represent) === '[object Function]') { + _result = type.represent(object, style); + } else if (_hasOwnProperty$3.call(type.represent, style)) { + _result = type.represent[style](object, style); + } else { + throw new exception('!<' + type.tag + '> tag resolver accepts not "' + style + '" style'); + } + + state.dump = _result; + } + + return true; + } + } + + return false; + } + + // Serializes `object` and writes it to global `result`. + // Returns true on success, or false on invalid object. + // + function writeNode(state, level, object, block, compact, iskey) { + state.tag = null; + state.dump = object; + + if (!detectType(state, object, false)) { + detectType(state, object, true); + } + + var type = _toString$2.call(state.dump); + + if (block) { + block = (state.flowLevel < 0 || state.flowLevel > level); + } + + var objectOrArray = type === '[object Object]' || type === '[object Array]', + duplicateIndex, + duplicate; + + if (objectOrArray) { + duplicateIndex = state.duplicates.indexOf(object); + duplicate = duplicateIndex !== -1; + } + + if ((state.tag !== null && state.tag !== '?') || duplicate || (state.indent !== 2 && level > 0)) { + compact = false; + } + + if (duplicate && state.usedDuplicates[duplicateIndex]) { + state.dump = '*ref_' + duplicateIndex; + } else { + if (objectOrArray && duplicate && !state.usedDuplicates[duplicateIndex]) { + state.usedDuplicates[duplicateIndex] = true; + } + if (type === '[object Object]') { + if (block && (Object.keys(state.dump).length !== 0)) { + writeBlockMapping(state, level, state.dump, compact); + if (duplicate) { + state.dump = '&ref_' + duplicateIndex + state.dump; + } + } else { + writeFlowMapping(state, level, state.dump); + if (duplicate) { + state.dump = '&ref_' + duplicateIndex + ' ' + state.dump; + } + } + } else if (type === '[object Array]') { + var arrayLevel = (state.noArrayIndent && (level > 0)) ? level - 1 : level; + if (block && (state.dump.length !== 0)) { + writeBlockSequence(state, arrayLevel, state.dump, compact); + if (duplicate) { + state.dump = '&ref_' + duplicateIndex + state.dump; + } + } else { + writeFlowSequence(state, arrayLevel, state.dump); + if (duplicate) { + state.dump = '&ref_' + duplicateIndex + ' ' + state.dump; + } + } + } else if (type === '[object String]') { + if (state.tag !== '?') { + writeScalar(state, state.dump, level, iskey); + } + } else { + if (state.skipInvalid) return false; + throw new exception('unacceptable kind of an object to dump ' + type); + } + + if (state.tag !== null && state.tag !== '?') { + state.dump = '!<' + state.tag + '> ' + state.dump; + } + } + + return true; + } + + function getDuplicateReferences(object, state) { + var objects = [], + duplicatesIndexes = [], + index, + length; + + inspectNode(object, objects, duplicatesIndexes); + + for (index = 0, length = duplicatesIndexes.length; index < length; index += 1) { + state.duplicates.push(objects[duplicatesIndexes[index]]); + } + state.usedDuplicates = new Array(length); + } + + function inspectNode(object, objects, duplicatesIndexes) { + var objectKeyList, + index, + length; + + if (object !== null && typeof object === 'object') { + index = objects.indexOf(object); + if (index !== -1) { + if (duplicatesIndexes.indexOf(index) === -1) { + duplicatesIndexes.push(index); + } + } else { + objects.push(object); + + if (Array.isArray(object)) { + for (index = 0, length = object.length; index < length; index += 1) { + inspectNode(object[index], objects, duplicatesIndexes); + } + } else { + objectKeyList = Object.keys(object); + + for (index = 0, length = objectKeyList.length; index < length; index += 1) { + inspectNode(object[objectKeyList[index]], objects, duplicatesIndexes); + } + } + } + } + } + + function dump(input, options) { + options = options || {}; + + var state = new State$1(options); + + if (!state.noRefs) getDuplicateReferences(input, state); + + if (writeNode(state, 0, input, true, true)) return state.dump + '\n'; + + return ''; + } + + function safeDump(input, options) { + return dump(input, common.extend({ schema: default_safe }, options)); + } + + var dump_1 = dump; + var safeDump_1 = safeDump; + + var dumper = { + dump: dump_1, + safeDump: safeDump_1 + }; + + function deprecated(name) { + return function () { + throw new Error('Function ' + name + ' is deprecated and cannot be used.'); + }; + } + + + var Type$1 = type; + var Schema$1 = schema; + var FAILSAFE_SCHEMA = failsafe; + var JSON_SCHEMA = json; + var CORE_SCHEMA = core; + var DEFAULT_SAFE_SCHEMA = default_safe; + var DEFAULT_FULL_SCHEMA = default_full; + var load$1 = loader.load; + var loadAll$1 = loader.loadAll; + var safeLoad$1 = loader.safeLoad; + var safeLoadAll$1 = loader.safeLoadAll; + var dump$1 = dumper.dump; + var safeDump$1 = dumper.safeDump; + var YAMLException$1 = exception; + + // Deprecated schema names from JS-YAML 2.0.x + var MINIMAL_SCHEMA = failsafe; + var SAFE_SCHEMA = default_safe; + var DEFAULT_SCHEMA = default_full; + + // Deprecated functions from JS-YAML 1.x.x + var scan = deprecated('scan'); + var parse = deprecated('parse'); + var compose = deprecated('compose'); + var addConstructor = deprecated('addConstructor'); + + var jsYaml = { + Type: Type$1, + Schema: Schema$1, + FAILSAFE_SCHEMA: FAILSAFE_SCHEMA, + JSON_SCHEMA: JSON_SCHEMA, + CORE_SCHEMA: CORE_SCHEMA, + DEFAULT_SAFE_SCHEMA: DEFAULT_SAFE_SCHEMA, + DEFAULT_FULL_SCHEMA: DEFAULT_FULL_SCHEMA, + load: load$1, + loadAll: loadAll$1, + safeLoad: safeLoad$1, + safeLoadAll: safeLoadAll$1, + dump: dump$1, + safeDump: safeDump$1, + YAMLException: YAMLException$1, + MINIMAL_SCHEMA: MINIMAL_SCHEMA, + SAFE_SCHEMA: SAFE_SCHEMA, + DEFAULT_SCHEMA: DEFAULT_SCHEMA, + scan: scan, + parse: parse, + compose: compose, + addConstructor: addConstructor + }; + + var jsYaml$1 = jsYaml; + + function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (enumerableOnly) symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; }); keys.push.apply(keys, symbols); } return keys; } + + function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } + /** + * Redirect - object used to redirect some requests + * e.g. + * { + * title: 1x1-transparent.gif + * comment: http://probablyprogramming.com/2009/03/15/the-tiniest-gif-ever + * contentType: image/gif;base64 + * content: R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== + * } + * @typedef {Object} Redirect + * @property {string} title + * @property {string} comment + * @property {string} content + * @property {string} contentType + */ + + var Redirects = + /*#__PURE__*/ + function () { + /** + * Converts rawYaml into JS object with sources titles used as keys + * @param rawYaml + * @returns {Object} - return object with titles in the keys and RedirectSources + * in the values + */ + function Redirects(rawYaml) { + classCallCheck(this, Redirects); + + try { + var arrOfRedirects = jsYaml$1.safeLoad(rawYaml); + this.redirects = arrOfRedirects.reduce(function (acc, redirect) { + return _objectSpread({}, acc, defineProperty({}, redirect.title, redirect)); + }, {}); + } catch (e) { + // eslint-disable-next-line no-console + console.log("Was unable to load YAML into JS due to: ".concat(e.message)); + throw e; + } + } + /** + * Returns redirect source object + * @param {string} title + * @return {Redirect} + */ + + + createClass(Redirects, [{ + key: "getRedirect", + value: function getRedirect(title) { + var _this = this; + + if (Object.prototype.hasOwnProperty.call(this.redirects, title)) { + return this.redirects[title]; + } // look title among aliases + + + var values = Object.keys(this.redirects).map(function (key) { + return _this.redirects[key]; + }); + return values.find(function (redirect) { + var aliases = redirect.aliases; + + if (!aliases) { + return false; + } + + return aliases.indexOf(title) > -1; + }); + } + }]); + + return Redirects; + }(); + + return Redirects; + +}()); +//# sourceMappingURL=redirects.js.map \ No newline at end of file From 367c503f87b4f9e0b1cbeded156a0011a91fdc97 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Thu, 30 Jan 2020 12:14:42 +0300 Subject: [PATCH 09/14] refix gitignore --- .gitignore | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 1560a6831..07a430ef9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,10 +3,7 @@ .idea/ .vscode/ *.iml -# dist/ -dist/index.js -dist/index.js.map -dist/scriptlets.js.map +dist/ yarn-error.log node_modules private From a599f8c34aa64906ce10b4ae79f43edeee1f5506 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Thu, 30 Jan 2020 14:05:38 +0300 Subject: [PATCH 10/14] add scriptlets as a cjs module --- dist/cjs/scriptlets.js | 3137 ++++++++++++++++++++++++++++++++++++++++ package.json | 1 - rollup.config.js | 81 +- 3 files changed, 3163 insertions(+), 56 deletions(-) create mode 100644 dist/cjs/scriptlets.js diff --git a/dist/cjs/scriptlets.js b/dist/cjs/scriptlets.js new file mode 100644 index 000000000..81a54b184 --- /dev/null +++ b/dist/cjs/scriptlets.js @@ -0,0 +1,3137 @@ + +/** + * AdGuard Scriptlets + * Version 1.1.1 + */ + +/** + * Generate random six symbols id + */ +function randomId() { + return Math.random().toString(36).substr(2, 9); +} + +/** + * Set getter and setter to property if it's configurable + * @param {Object} object target object with property + * @param {string} property property name + * @param {Object} descriptor contains getter and setter functions + * @returns {boolean} is operation successful + */ +function setPropertyAccess(object, property, descriptor) { + var currentDescriptor = Object.getOwnPropertyDescriptor(object, property); + + if (currentDescriptor && !currentDescriptor.configurable) { + return false; + } + + Object.defineProperty(object, property, descriptor); + return true; +} + +/** + * @typedef Chain + * @property {Object} base + * @property {string} prop + * @property {string} [chain] + */ + +/** + * Check is property exist in base object recursively + * + * If property doesn't exist in base object + * defines this property and returns base, property name and remaining part of property chain + * + * @param {Object} base + * @param {string} chain + * @returns {Chain} + */ +function getPropertyInChain(base, chain) { + var pos = chain.indexOf('.'); + + if (pos === -1) { + return { + base: base, + prop: chain + }; + } + + var prop = chain.slice(0, pos); + var own = base[prop]; + chain = chain.slice(pos + 1); + + if (own !== undefined) { + return getPropertyInChain(own, chain); + } + + Object.defineProperty(base, prop, { + configurable: true + }); + return { + base: own, + prop: prop, + chain: chain + }; +} + +/** + * Escapes special chars in string + * @param {string} str + * @returns {string} + */ +var escapeRegExp = function escapeRegExp(str) { + return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +}; +/** + * Converts search string to the regexp + * TODO think about nested dependencies, but be careful with dependency loops + * @param {string} str search string + * @returns {RegExp} + */ + +var toRegExp = function toRegExp(str) { + if (str[0] === '/' && str[str.length - 1] === '/') { + return new RegExp(str.slice(1, -1)); + } + + var escaped = str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); + return new RegExp(escaped); +}; +/** + * Get string before regexp first match + * @param {string} str + * @param {RegExp} rx + */ + +var getBeforeRegExp = function getBeforeRegExp(str, rx) { + var index = str.search(rx); + return str.substring(0, index); +}; +var substringAfter = function substringAfter(str, separator) { + if (!str) { + return str; + } + + var index = str.indexOf(separator); + return index < 0 ? '' : str.substring(index + separator.length); +}; +var substringBefore = function substringBefore(str, separator) { + if (!str || !separator) { + return str; + } + + var index = str.indexOf(separator); + return index < 0 ? str : str.substring(0, index); +}; +/** + * Wrap str in double qoutes and replaces single quotes if need + * @param {string} str + */ + +var wrapInDoubleQuotes = function wrapInDoubleQuotes(str) { + if (str[0] === '\'' && str[str.length - 1] === '\'') { + str = str.substring(1, str.length - 1); // eslint-disable-next-line no-useless-escape + + str = str.replace(/\"/g, '\\"'); + } else if (str[0] === '"' && str[str.length - 1] === '"') { + str = str.substring(1, str.length - 1); // eslint-disable-next-line no-useless-escape + + str = str.replace(/\'/g, '\\\''); + } + + return "\"".concat(str, "\""); +}; +/** + * Returns substring enclosed in the widest braces + * @param {string} str + */ + +var getStringInBraces = function getStringInBraces(str) { + var firstIndex = str.indexOf('('); + var lastIndex = str.lastIndexOf(')'); + return str.substring(firstIndex + 1, lastIndex); +}; + +/** + * Generates function which silents global errors on page generated by scriptlet + * If error doesn't belong to our error we transfer it to the native onError handler + * @param {string} rid - unique identifier of scriptlet + * @return {onError} + */ +function createOnErrorHandler(rid) { + // eslint-disable-next-line consistent-return + var nativeOnError = window.onerror; + return function onError(error) { + if (typeof error === 'string' && error.indexOf(rid) !== -1) { + return true; + } + + if (nativeOnError instanceof Function) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + return nativeOnError.apply(this, [error].concat(args)); + } + + return false; + }; +} + +/** + * Noop function + */ +var noop = function noop() {}; +/** + * Function returns null + */ + +var noopNull = function noopNull() { + return null; +}; +/** + * Function returns this + */ + +function noopThis() { + return this; +} +/** + * Function returns empty array + */ + +var noopArray = function noopArray() { + return []; +}; +/** + * Function returns empty string + */ + +var noopStr = function noopStr() { + return ''; +}; + +/* eslint-disable no-console, no-underscore-dangle */ + +/** + * Hit used only for debug purposes now + * @param {Source} source + * @param {String} message optional message + */ +var hit = function hit(source, message) { + if (source.verbose !== true) { + return; + } + + try { + var log = console.log.bind(console); + var trace = console.trace.bind(console); + var prefix = source.ruleText || ''; + + if (message) { + log("".concat(prefix, " message:\n").concat(message)); + } + + log("".concat(prefix, " trace start")); + + if (trace) { + trace(); + } + + log("".concat(prefix, " trace end")); + } catch (e) {} // try catch for Edge 15 + // In according to this issue https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/14495220/ + // console.log throws an error + // This is necessary for unit-tests only! + + + if (typeof window.__debugScriptlets === 'function') { + window.__debugScriptlets(source); + } +}; + +/** + * This file must export all used dependencies + */ + +var dependencies = /*#__PURE__*/Object.freeze({ + __proto__: null, + randomId: randomId, + setPropertyAccess: setPropertyAccess, + getPropertyInChain: getPropertyInChain, + escapeRegExp: escapeRegExp, + toRegExp: toRegExp, + getBeforeRegExp: getBeforeRegExp, + substringAfter: substringAfter, + substringBefore: substringBefore, + wrapInDoubleQuotes: wrapInDoubleQuotes, + getStringInBraces: getStringInBraces, + createOnErrorHandler: createOnErrorHandler, + noop: noop, + noopNull: noopNull, + noopThis: noopThis, + noopArray: noopArray, + noopStr: noopStr, + hit: hit +}); + +/** + * Concat dependencies to scriptlet code + * @param {string} scriptlet string view of scriptlet + */ + +function attachDependencies(scriptlet) { + var _scriptlet$injections = scriptlet.injections, + injections = _scriptlet$injections === void 0 ? [] : _scriptlet$injections; + return injections.reduce(function (accum, dep) { + return "".concat(accum, "\n").concat(dependencies[dep.name]); + }, scriptlet.toString()); +} +/** + * Add scriptlet call to existing code + * @param {Function} scriptlet + * @param {string} code + */ + +function addCall(scriptlet, code) { + return "".concat(code, ";\n const updatedArgs = args ? [].concat(source).concat(args) : [source];\n ").concat(scriptlet.name, ".apply(this, updatedArgs);\n "); +} +/** + * Wrap function into IIFE (Immediately invoked function expression) + * + * @param {Source} source - object with scriptlet properties + * @param {string} code - scriptlet source code with dependencies + * + * @returns {string} full scriptlet code + * + * @example + * const source = { + * args: ["aaa", "bbb"], + * name: 'noeval', + * }; + * const code = "function noeval(source, args) { alert(source); } noeval.apply(this, args);" + * const result = wrapInIIFE(source, code); + * + * // result + * `(function(source, args) { + * function noeval(source) { alert(source); } + * noeval.apply(this, args); + * )({"args": ["aaa", "bbb"], "name":"noeval"}, ["aaa", "bbb"])` + */ + +function passSourceAndProps(source, code) { + if (source.hit) { + source.hit = source.hit.toString(); + } + + var sourceString = JSON.stringify(source); + var argsString = source.args ? "[".concat(source.args.map(JSON.stringify), "]") : undefined; + var params = argsString ? "".concat(sourceString, ", ").concat(argsString) : sourceString; + return "(function(source, args){\n".concat(code, "\n})(").concat(params, ");"); +} +/** + * Wrap code in no name function + * @param {string} code which must be wrapped + */ + +function wrapInNonameFunc(code) { + return "function(source, args){\n".concat(code, "\n}"); +} + +function _arrayWithHoles(arr) { + if (Array.isArray(arr)) return arr; +} + +var arrayWithHoles = _arrayWithHoles; + +function _iterableToArray(iter) { + if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); +} + +var iterableToArray = _iterableToArray; + +function _nonIterableRest() { + throw new TypeError("Invalid attempt to destructure non-iterable instance"); +} + +var nonIterableRest = _nonIterableRest; + +function _toArray(arr) { + return arrayWithHoles(arr) || iterableToArray(arr) || nonIterableRest(); +} + +var toArray = _toArray; + +function _defineProperty(obj, key, value) { + if (key in obj) { + Object.defineProperty(obj, key, { + value: value, + enumerable: true, + configurable: true, + writable: true + }); + } else { + obj[key] = value; + } + + return obj; +} + +var defineProperty = _defineProperty; + +/** + * Iterate over iterable argument and evaluate current state with transitions + * @param {string} init first transition name + * @param {Array|Collection|string} iterable + * @param {Object} transitions transtion functions + * @param {any} args arguments which should be passed to transition functions + */ +function iterateWithTransitions(iterable, transitions, init, args) { + var state = init || Object.keys(transitions)[0]; + + for (var i = 0; i < iterable.length; i += 1) { + state = transitions[state](iterable, i, args); + } + + return state; +} +/** + * AdGuard scriptlet rule mask + */ + + +var ADG_SCRIPTLET_MASK = '#//scriptlet'; +/** + * Helper to accumulate an array of strings char by char + */ + +var wordSaver = function wordSaver() { + var str = ''; + var strs = []; + + var saveSymb = function saveSymb(s) { + str += s; + return str; + }; + + var saveStr = function saveStr() { + strs.push(str); + str = ''; + }; + + var getAll = function getAll() { + return [].concat(strs); + }; + + return { + saveSymb: saveSymb, + saveStr: saveStr, + getAll: getAll + }; +}; + +var substringAfter$1 = function substringAfter(str, separator) { + if (!str) { + return str; + } + + var index = str.indexOf(separator); + return index < 0 ? '' : str.substring(index + separator.length); +}; +/** + * Parse and validate scriptlet rule + * @param {*} ruleText + * @returns {{name: string, args: Array}} + */ + + +var parseRule = function parseRule(ruleText) { + var _transitions; + + ruleText = substringAfter$1(ruleText, ADG_SCRIPTLET_MASK); + /** + * Transition names + */ + + var TRANSITION = { + OPENED: 'opened', + PARAM: 'param', + CLOSED: 'closed' + }; + /** + * Transition function: the current index position in start, end or between params + * @param {string} rule + * @param {number} index + * @param {Object} Object + * @property {Object} Object.sep contains prop symb with current separator char + */ + + var opened = function opened(rule, index, _ref) { + var sep = _ref.sep; + var char = rule[index]; + var transition; + + switch (char) { + case ' ': + case '(': + case ',': + { + transition = TRANSITION.OPENED; + break; + } + + case '\'': + case '"': + { + sep.symb = char; + transition = TRANSITION.PARAM; + break; + } + + case ')': + { + transition = index === rule.length - 1 ? TRANSITION.CLOSED : TRANSITION.OPENED; + break; + } + + default: + { + throw new Error('The rule is not a scriptlet'); + } + } + + return transition; + }; + /** + * Transition function: the current index position inside param + * @param {string} rule + * @param {number} index + * @param {Object} Object + * @property {Object} Object.sep contains prop `symb` with current separator char + * @property {Object} Object.saver helper which allow to save strings by car by char + */ + + + var param = function param(rule, index, _ref2) { + var saver = _ref2.saver, + sep = _ref2.sep; + var char = rule[index]; + + switch (char) { + case '\'': + case '"': + { + var preIndex = index - 1; + var before = rule[preIndex]; + + if (char === sep.symb && before !== '\\') { + sep.symb = null; + saver.saveStr(); + return TRANSITION.OPENED; + } + } + // eslint-disable-next-line no-fallthrough + + default: + { + saver.saveSymb(char); + return TRANSITION.PARAM; + } + } + }; + + var transitions = (_transitions = {}, defineProperty(_transitions, TRANSITION.OPENED, opened), defineProperty(_transitions, TRANSITION.PARAM, param), defineProperty(_transitions, TRANSITION.CLOSED, function () {}), _transitions); + var sep = { + symb: null + }; + var saver = wordSaver(); + var state = iterateWithTransitions(ruleText, transitions, TRANSITION.OPENED, { + sep: sep, + saver: saver + }); + + if (state !== 'closed') { + throw new Error("Invalid scriptlet rule ".concat(ruleText)); + } + + var args = saver.getAll(); + return { + name: args[0], + args: args.slice(1) + }; +}; + +/* eslint-disable max-len */ + +/** + * @scriptlet abort-on-property-read + * + * @description + * Aborts a script when it attempts to **read** the specified property. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#abort-on-property-readjs- + * + * Related ABP source: + * https://github.com/adblockplus/adblockpluscore/blob/6b2a309054cc23432102b85d13f12559639ef495/lib/content/snippets.js#L864 + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("abort-on-property-read", ) + * ``` + * + * **Parameters** + * - `property` (required) path to a property (joined with `.` if needed). The property must be attached to `window`. + * + * **Examples** + * ``` + * ! Aborts script when it tries to access `window.alert` + * example.org#%#//scriptlet("abort-on-property-read", "alert") + * + * ! Aborts script when it tries to access `navigator.language` + * example.org#%#//scriptlet("abort-on-property-read", "navigator.language") + * ``` + */ + +/* eslint-enable max-len */ + +function abortOnPropertyRead(source, property) { + if (!property) { + return; + } + + var rid = randomId(); + + var abort = function abort() { + hit(source); + throw new ReferenceError(rid); + }; + + var setChainPropAccess = function setChainPropAccess(owner, property) { + var chainInfo = getPropertyInChain(owner, property); + var base = chainInfo.base; + var prop = chainInfo.prop, + chain = chainInfo.chain; + + if (chain) { + var setter = function setter(a) { + base = a; + + if (a instanceof Object) { + setChainPropAccess(a, chain); + } + }; + + Object.defineProperty(owner, prop, { + get: function get() { + return base; + }, + set: setter + }); + return; + } + + setPropertyAccess(base, prop, { + get: abort, + set: function set() {} + }); + }; + + setChainPropAccess(window, property); + window.onerror = createOnErrorHandler(rid).bind(); +} +abortOnPropertyRead.names = ['abort-on-property-read', 'abort-on-property-read.js', 'ubo-abort-on-property-read.js', 'abp-abort-on-property-read']; +abortOnPropertyRead.injections = [randomId, setPropertyAccess, getPropertyInChain, createOnErrorHandler, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet abort-on-property-write + * + * @description + * Aborts a script when it attempts to **write** the specified property. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#abort-on-property-writejs- + * + * Related ABP source: + * https://github.com/adblockplus/adblockpluscore/blob/6b2a309054cc23432102b85d13f12559639ef495/lib/content/snippets.js#L896 + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("abort-on-property-write", ) + * ``` + * + * **Parameters** + * - `property` (required) path to a property (joined with `.` if needed). The property must be attached to `window`. + * + * **Examples** + * ``` + * ! Aborts all inline scripts trying to access `window.alert` + * utils.escape('') + * // => '<script></script>' + * ``` + */ + +/* eslint-enable max-len */ + +function abortOnPropertyWrite(source, property) { + if (!property) { + return; + } + + var rid = randomId(); + + var abort = function abort() { + hit(source); + throw new ReferenceError(rid); + }; + + var setChainPropAccess = function setChainPropAccess(owner, property) { + var chainInfo = getPropertyInChain(owner, property); + var base = chainInfo.base; + var prop = chainInfo.prop, + chain = chainInfo.chain; + + if (chain) { + var setter = function setter(a) { + base = a; + + if (a instanceof Object) { + setChainPropAccess(a, chain); + } + }; + + Object.defineProperty(owner, prop, { + get: function get() { + return base; + }, + set: setter + }); + return; + } + + setPropertyAccess(base, prop, { + set: abort + }); + }; + + setChainPropAccess(window, property); + window.onerror = createOnErrorHandler(rid).bind(); +} +abortOnPropertyWrite.names = ['abort-on-property-write', 'abort-on-property-write.js', 'ubo-abort-on-property-write.js', 'abp-abort-on-property-write']; +abortOnPropertyWrite.injections = [randomId, setPropertyAccess, getPropertyInChain, createOnErrorHandler, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet prevent-setTimeout + * + * @description + * Prevents a `setTimeout` call if the text of the callback is matching the specified search string/regexp and (optionally) have the specified delay. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#settimeout-defuserjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("prevent-setTimeout"[, [, ]]) + * ``` + * + * **Parameters** + * - `search` (optional) string or regular expression that must match the stringified callback . If not set, prevents all `setTimeout` calls. + * - `delay` (optional) must be an integer. If set, it matches the delay passed to the `setTimeout` call. + * + * **Examples** + * + * 1. Prevents `setTimeout` calls if the callback contains `value` and the delay is set to `300`. + * ``` + * example.org#%#//scriptlet("prevent-setTimeout", "value", "300") + * ``` + * + * For instance, the followiing call will be prevented: + * ```javascript + * setTimeout(function () { + * window.test = "value"; + * }, 300); + * ``` + * + * 2. Prevents `setTimeout` calls if the callback matches `/\.test/` regardless of the delay. + * ```bash + * example.org#%#//scriptlet("prevent-setTimeout", "/\.test/") + * ``` + * + * For instance, the followiing call will be prevented: + * ```javascript + * setTimeout(function () { + * window.test = "value"; + * }, 100); + * ``` + */ + +/* eslint-enable max-len */ + +function preventSetTimeout(source, match, delay) { + var nativeTimeout = window.setTimeout; + var nativeIsNaN = Number.isNaN || window.isNaN; // eslint-disable-line compat/compat + + delay = parseInt(delay, 10); + delay = nativeIsNaN(delay) ? null : delay; + match = match ? toRegExp(match) : toRegExp('/.?/'); + + var timeoutWrapper = function timeoutWrapper(cb, d) { + if ((!delay || d === delay) && match.test(cb.toString())) { + hit(source); + return nativeTimeout(function () {}, d); + } + + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + + return nativeTimeout.apply(window, [cb, d].concat(args)); + }; + + window.setTimeout = timeoutWrapper; +} +preventSetTimeout.names = ['prevent-setTimeout', 'setTimeout-defuser.js', 'ubo-setTimeout-defuser.js']; +preventSetTimeout.injections = [toRegExp, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet prevent-setInterval + * + * @description + * Prevents a `setInterval` call if the text of the callback is matching the specified search string/regexp and (optionally) have the specified interval. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#setinterval-defuserjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("prevent-setInterval"[, [, ]]) + * ``` + * + * **Parameters** + * - `search` (optional) string or regular expression that must match the stringified callback . If not set, prevents all `setInterval` calls. + * - `interval` (optional) must be an integer. If set, it matches the interval passed to the `setInterval` call. + * + * **Example** + * + * 1. Prevents `setInterval` calls if the callback contains `value` and the interval is set to `300`. + * ``` + * example.org#%#//scriptlet("prevent-setInterval", "value", "300") + * ``` + * + * For instance, the followiing call will be prevented: + * ```javascript + * setInterval(function () { + * window.test = "value"; + * }, 300); + * ``` + * + * 2. Prevents `setInterval` calls if the callback matches `/\.test/` regardless of the interval. + * ``` + * example.org#%#//scriptlet("prevent-setInterval", "/\.test/") + * ``` + * + * For instance, the followiing call will be prevented: + * ```javascript + * setInterval(function () { + * window.test = "value"; + * }, 100); + * ``` + */ + +/* eslint-enable max-len */ + +function preventSetInterval(source, match, interval) { + var nativeInterval = window.setInterval; + var nativeIsNaN = Number.isNaN || window.isNaN; // eslint-disable-line compat/compat + + interval = parseInt(interval, 10); + interval = nativeIsNaN(interval) ? null : interval; + match = match ? toRegExp(match) : toRegExp('/.?/'); + + var intervalWrapper = function intervalWrapper(cb, d) { + if ((!interval || d === interval) && match.test(cb.toString())) { + hit(source); + return nativeInterval(function () {}, d); + } + + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + + return nativeInterval.apply(window, [cb, d].concat(args)); + }; + + window.setInterval = intervalWrapper; +} +preventSetInterval.names = ['prevent-setInterval', 'setInterval-defuser.js', 'ubo-setInterval-defuser.js']; +preventSetInterval.injections = [toRegExp, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet prevent-window-open + * + * @description + * Prevents `window.open` calls when URL either matches or not matches the specified string/regexp. Using it without parameters prevents all `window.open` calls. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#windowopen-defuserjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("prevent-window-open"[, [, ]]) + * ``` + * + * **Parameters** + * - `match` (optional) defaults to "matching", any positive number for "matching", 0 or any string for "not matching", + * - `search` (optional) string or regexp for matching the URL passed to `window.open` call. + * + * **Example** + * + * 1. Prevent all `window.open` calls: + * ``` + * example.org#%#//scriptlet("prevent-window-open") + * ``` + * + * 2. Prevent `window.open` for all URLs containing `example`: + * ``` + * example.org#%#//scriptlet("prevent-window-open", "1", "example") + * ``` + * + * 3. Prevent `window.open` for all URLs matching RegExp `/example\./`: + * ``` + * example.org#%#//scriptlet("prevent-window-open", "1", "/example\./") + * ``` + * + * 4. Prevent `window.open` for all URLs **NOT** containing `example`: + * ``` + * example.org#%#//scriptlet("prevent-window-open", "0", "example") + * ``` + */ + +/* eslint-enable max-len */ + +function preventWindowOpen(source, inverse, match) { + var nativeOpen = window.open; + inverse = inverse ? !+inverse : !!inverse; + match = match ? toRegExp(match) : toRegExp('/.?/'); // eslint-disable-next-line consistent-return + + var openWrapper = function openWrapper(str) { + if (inverse === match.test(str)) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + return nativeOpen.apply(window, [str].concat(args)); + } + + hit(source); + }; + + window.open = openWrapper; +} +preventWindowOpen.names = ['prevent-window-open', 'window.open-defuser.js', 'ubo-window.open-defuser.js']; +preventWindowOpen.injections = [toRegExp, hit]; + +/* eslint-disable no-new-func */ +/* eslint-disable max-len */ + +/** + * @scriptlet abort-current-inline-script + * + * @description + * Aborts an inline script when it attempts to **read** the specified property + * AND when the contents of the ` + * ``` + * + * 3. Aborts inline scripts which are trying to access `window.alert` and match this regexp: `/Hello.+world/`. + * ``` + * example.org#%#//scriptlet("abort-current-inline-script", "alert", "/Hello.+world/") + * ``` + * + * For instance, the following scripts will be aborted: + * ```html + * + * ``` + * ```html + * + * ``` + * + * This script will not be aborted: + * ```html + * + * ``` + */ + +/* eslint-enable max-len */ + +function abortCurrentInlineScript(source, property) { + var search = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; + var regex = search ? toRegExp(search) : null; + var rid = randomId(); + + var getCurrentScript = function getCurrentScript() { + if (!document.currentScript) { + // eslint-disable-line compat/compat + var scripts = document.getElementsByTagName('script'); + return scripts[scripts.length - 1]; + } + + return document.currentScript; // eslint-disable-line compat/compat + }; + + var ourScript = getCurrentScript(); + + var abort = function abort() { + var scriptEl = getCurrentScript(); + var content = scriptEl.textContent; + + try { + var textContentGetter = Object.getOwnPropertyDescriptor(Node.prototype, 'textContent').get; + content = textContentGetter.call(scriptEl); // eslint-disable-next-line no-empty + } catch (e) {} + + if (scriptEl instanceof HTMLScriptElement && content.length > 0 && scriptEl !== ourScript && (!regex || regex.test(scriptEl.textContent))) { + hit(source); + throw new ReferenceError(rid); + } + }; + + var setChainPropAccess = function setChainPropAccess(owner, property) { + var chainInfo = getPropertyInChain(owner, property); + var base = chainInfo.base; + var prop = chainInfo.prop, + chain = chainInfo.chain; + + if (chain) { + var setter = function setter(a) { + base = a; + + if (a instanceof Object) { + setChainPropAccess(a, chain); + } + }; + + Object.defineProperty(owner, prop, { + get: function get() { + return base; + }, + set: setter + }); + return; + } + + var currentValue = base[prop]; + setPropertyAccess(base, prop, { + set: function set(value) { + abort(); + currentValue = value; + }, + get: function get() { + abort(); + return currentValue; + } + }); + }; + + setChainPropAccess(window, property); + window.onerror = createOnErrorHandler(rid).bind(); +} +abortCurrentInlineScript.names = ['abort-current-inline-script', 'abort-current-inline-script.js', 'ubo-abort-current-inline-script.js', 'abp-abort-current-inline-script']; +abortCurrentInlineScript.injections = [randomId, setPropertyAccess, getPropertyInChain, toRegExp, createOnErrorHandler, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet set-constant + * + * @description + * Creates a constant property and assigns it one of the values from the predefined list. + * + * > Actually, it's not a constant. Please note, that it can be rewritten with a value of a different type. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#set-constantjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("set-constant", , ) + * ``` + * + * **Parameters** + * - `property` (required) path to a property (joined with `.` if needed). The property must be attached to `window`. + * - `value` (required). Possible values: + * - positive decimal integer `<= 32767` + * - one of the predefined constants: + * - `undefined` + * - `false` + * - `true` + * - `null` + * - `noopFunc` - function with empty body + * - `trueFunc` - function returning true + * - `falseFunc` - function returning false + * - `''` - empty string + * + * **Examples** + * ``` + * ! window.firstConst === false // this comparision will return true + * example.org#%#//scriptlet("set-constant", "firstConst", "false") + * + * ! window.secondConst() === true // call to the secondConst will return true + * example.org#%#//scriptlet("set-constant", "secondConst", "trueFunc") + * ``` + */ + +/* eslint-enable max-len */ + +function setConstant(source, property, value) { + if (!property) { + return; + } + + var nativeIsNaN = Number.isNaN || window.isNaN; // eslint-disable-line compat/compat + + var constantValue; + + if (value === 'undefined') { + constantValue = undefined; + } else if (value === 'false') { + constantValue = false; + } else if (value === 'true') { + constantValue = true; + } else if (value === 'null') { + constantValue = null; + } else if (value === 'noopFunc') { + constantValue = function constantValue() {}; + } else if (value === 'trueFunc') { + constantValue = function constantValue() { + return true; + }; + } else if (value === 'falseFunc') { + constantValue = function constantValue() { + return false; + }; + } else if (/^\d+$/.test(value)) { + constantValue = parseFloat(value); + + if (nativeIsNaN(constantValue)) { + return; + } + + if (Math.abs(constantValue) > 0x7FFF) { + return; + } + } else if (value === '-1') { + constantValue = -1; + } else if (value === '') { + constantValue = ''; + } else { + return; + } + + var canceled = false; + + var mustCancel = function mustCancel(value) { + if (canceled) { + return canceled; + } + + canceled = value !== undefined && constantValue !== undefined && typeof value !== typeof constantValue; + return canceled; + }; + + var setChainPropAccess = function setChainPropAccess(owner, property) { + var chainInfo = getPropertyInChain(owner, property); + var base = chainInfo.base; + var prop = chainInfo.prop, + chain = chainInfo.chain; + + if (chain) { + var setter = function setter(a) { + base = a; + + if (a instanceof Object) { + setChainPropAccess(a, chain); + } + }; + + Object.defineProperty(owner, prop, { + get: function get() { + return base; + }, + set: setter + }); + return; + } + + if (mustCancel(base[prop])) { + return; + } + + hit(source); + setPropertyAccess(base, prop, { + get: function get() { + return constantValue; + }, + set: function set(a) { + if (mustCancel(a)) { + constantValue = a; + } + } + }); + }; + + setChainPropAccess(window, property); +} +setConstant.names = ['set-constant', 'set-constant.js', 'ubo-set-constant.js']; +setConstant.injections = [getPropertyInChain, setPropertyAccess, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet remove-cookie + * + * @description + * Removes current page cookies by passed string matching with name. For current domain and subdomains. Runs on load and before unload. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#cookie-removerjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("remove-cookie"[, match]) + * ``` + * + * **Parameters** + * - `match` (optional) String or regex matching the cookie name. If not specified all accessible cookies will be removed. + * + * **Examples** + * 1. Removes all cookies: + * ``` + * example.org#%#//scriptlet("remove-cookie") + * ``` + * + * 2. Removes cookies which name contains `example` string. + * ``` + * example.org#%#//scriptlet("remove-cookie", "example") + * ``` + * + * For instance this cookie will be removed + * ```javascript + * document.cookie = '__example=randomValue'; + * ``` + */ + +/* eslint-enable max-len */ + +function removeCookie(source, match) { + var regex = match ? toRegExp(match) : toRegExp('/.?/'); + + var removeCookieFromHost = function removeCookieFromHost(cookieName, hostName) { + var cookieSpec = "".concat(cookieName, "="); + var domain1 = "; domain=".concat(hostName); + var domain2 = "; domain=.".concat(hostName); + var path = '; path=/'; + var expiration = '; expires=Thu, 01 Jan 1970 00:00:00 GMT'; + document.cookie = cookieSpec + expiration; + document.cookie = cookieSpec + domain1 + expiration; + document.cookie = cookieSpec + domain2 + expiration; + document.cookie = cookieSpec + path + expiration; + document.cookie = cookieSpec + domain1 + path + expiration; + document.cookie = cookieSpec + domain2 + path + expiration; + hit(source); + }; + + var rmCookie = function rmCookie() { + document.cookie.split(';').forEach(function (cookieStr) { + var pos = cookieStr.indexOf('='); + + if (pos === -1) { + return; + } + + var cookieName = cookieStr.slice(0, pos).trim(); + + if (!regex.test(cookieName)) { + return; + } + + var hostParts = document.location.hostname.split('.'); + + for (var i = 0; i <= hostParts.length - 1; i += 1) { + var hostName = hostParts.slice(i).join('.'); + + if (hostName) { + removeCookieFromHost(cookieName, hostName); + } + } + }); + }; + + rmCookie(); + window.addEventListener('beforeunload', rmCookie); +} +removeCookie.names = ['remove-cookie', 'cookie-remover.js', 'ubo-cookie-remover.js']; +removeCookie.injections = [toRegExp, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet prevent-addEventListener + * + * @description + * Prevents adding event listeners for the specified events and callbacks. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#addeventlistener-defuserjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("prevent-addEventListener"[, eventSearch[, functionSearch]]) + * ``` + * + * **Parameters** + * - `eventSearch` (optional) String or regex matching the event name. If not specified, the scriptlets prevents all event listeners. + * - `functionSearch` (optional) String or regex matching the event listener function body. If not set, the scriptlet prevents all event listeners with event name matching `eventSearch`. + * + * **Examples** + * 1. Prevent all `click` listeners: + * ``` + * example.org#%#//scriptlet("prevent-addEventListener", "click") + * ``` + +2. Prevent 'click' listeners with the callback body containing `searchString`. + * ``` + * example.org#%#//scriptlet("prevent-addEventListener", "click", "searchString") + * ``` + * + * For instance, this listener will not be called: + * ```javascript + * el.addEventListener('click', () => { + * window.test = 'searchString'; + * }); + * ``` + */ + +/* eslint-enable max-len */ + +function preventAddEventListener(source, event, funcStr) { + event = event ? toRegExp(event) : toRegExp('/.?/'); + funcStr = funcStr ? toRegExp(funcStr) : toRegExp('/.?/'); + var nativeAddEventListener = window.EventTarget.prototype.addEventListener; + + function addEventListenerWrapper(eventName, callback) { + if (event.test(eventName.toString()) && funcStr.test(callback.toString())) { + hit(source); + return undefined; + } + + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + + return nativeAddEventListener.apply(this, [eventName, callback].concat(args)); + } + + window.EventTarget.prototype.addEventListener = addEventListenerWrapper; +} +preventAddEventListener.names = ['prevent-addEventListener', 'addEventListener-defuser.js', 'ubo-addEventListener-defuser.js']; +preventAddEventListener.injections = [toRegExp, hit]; + +/* eslint-disable consistent-return, no-eval */ +/** + * @scriptlet prevent-bab + * + * @description + * Prevents BlockAdblock script from detecting an ad blocker. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#bab-defuserjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("prevent-bab") + * ``` + */ + +function preventBab(source) { + var _this = this; + + var nativeSetTimeout = window.setTimeout; + var babRegex = /\.bab_elementid.$/; + + window.setTimeout = function (callback) { + if (typeof callback !== 'string' || !babRegex.test(callback)) { + for (var _len = arguments.length, args = new Array(_len > 1 ? _len - 1 : 0), _key = 1; _key < _len; _key++) { + args[_key - 1] = arguments[_key]; + } + + return nativeSetTimeout.call.apply(nativeSetTimeout, [_this, callback].concat(args)); + } + + hit(source); + }; + + var signatures = [['blockadblock'], ['babasbm'], [/getItem\('babn'\)/], ['getElementById', 'String.fromCharCode', 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789', 'charAt', 'DOMContentLoaded', 'AdBlock', 'addEventListener', 'doScroll', 'fromCharCode', '<<2|r>>4', 'sessionStorage', 'clientWidth', 'localStorage', 'Math', 'random']]; + + var check = function check(str) { + for (var i = 0; i < signatures.length; i += 1) { + var tokens = signatures[i]; + var match = 0; + + for (var j = 0; j < tokens.length; j += 1) { + var token = tokens[j]; + var found = token instanceof RegExp ? token.test(str) : str.indexOf(token) > -1; + + if (found) { + match += 1; + } + } + + if (match / tokens.length >= 0.8) { + return true; + } + } + + return false; + }; + + var nativeEval = window.eval; + + window.eval = function (str) { + if (!check(str)) { + return nativeEval(str); + } + + hit(source); + var bodyEl = document.body; + + if (bodyEl) { + bodyEl.style.removeProperty('visibility'); + } + + var el = document.getElementById('babasbmsgx'); + + if (el) { + el.parentNode.removeChild(el); + } + }; +} +preventBab.names = ['prevent-bab', 'bab-defuser.js', 'ubo-bab-defuser.js']; +preventBab.injections = [hit]; + +/* eslint-disable no-unused-vars, no-extra-bind, func-names */ +/* eslint-disable max-len */ + +/** + * @scriptlet nowebrtc + * + * @description + * Disables WebRTC by overriding `RTCPeerConnection`. The overriden function will log every attempt to create a new connection. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#nowebrtcjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("nowebrtc") + * ``` + */ + +/* eslint-enable max-len */ + +function nowebrtc(source) { + var propertyName = ''; + + if (window.RTCPeerConnection) { + propertyName = 'RTCPeerConnection'; + } else if (window.webkitRTCPeerConnection) { + propertyName = 'webkitRTCPeerConnection'; + } + + if (propertyName === '') { + return; + } + + var rtcReplacement = function rtcReplacement(config) { + hit(source, "Document tried to create an RTCPeerConnection: ".concat(config)); + }; + + var noop = function noop() {}; + + rtcReplacement.prototype = { + close: noop, + createDataChannel: noop, + createOffer: noop, + setRemoteDescription: noop + }; + var rtc = window[propertyName]; + window[propertyName] = rtcReplacement; + + if (rtc.prototype) { + rtc.prototype.createDataChannel = function (a, b) { + return { + close: noop, + send: noop + }; + }.bind(null); + } +} +nowebrtc.names = ['nowebrtc', 'nowebrtc.js', 'ubo-nowebrtc.js']; +nowebrtc.injections = [hit]; + +/* eslint-disable no-console */ +/** + * @scriptlet log-addEventListener + * + * @description + * Logs all addEventListener calls to the console. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#addeventlistener-loggerjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("log-addEventListener") + * ``` + */ + +function logAddEventListener(source) { + var log = console.log.bind(console); + var nativeAddEventListener = window.EventTarget.prototype.addEventListener; + + function addEventListenerWrapper(eventName, callback) { + hit(source); + log("addEventListener(\"".concat(eventName, "\", ").concat(callback.toString(), ")")); + + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + + return nativeAddEventListener.apply(this, [eventName, callback].concat(args)); + } + + window.EventTarget.prototype.addEventListener = addEventListenerWrapper; +} +logAddEventListener.names = ['log-addEventListener', 'addEventListener-logger.js', 'ubo-addEventListener-logger.js']; +logAddEventListener.injections = [hit]; + +/* eslint-disable no-console */ +/** + * @scriptlet log-setInterval + * + * @description + * Logs all setInterval calls to the console. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#setinterval-loggerjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("log-setInterval") + * ``` + */ + +function logSetInterval(source) { + var log = console.log.bind(console); + var nativeSetInterval = window.setInterval; + + function setIntervalWrapper(callback, timeout) { + hit(source); + log("setInterval(\"".concat(callback.toString(), "\", ").concat(timeout, ")")); + + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + + return nativeSetInterval.apply(window, [callback, timeout].concat(args)); + } + + window.setInterval = setIntervalWrapper; +} +logSetInterval.names = ['log-setInterval', 'setInterval-logger.js', 'ubo-setInterval-logger.js']; +logSetInterval.injections = [hit]; + +/* eslint-disable no-console */ +/** + * @scriptlet log-setTimeout + * + * @description + * Logs all setTimeout call to the console. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#settimeout-loggerjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("log-setTimeout") + * ``` + */ + +function logSetTimeout(source) { + var log = console.log.bind(console); + var nativeSetTimeout = window.setTimeout; + + function setTimeoutWrapper(callback, timeout) { + hit(source); + log("setTimeout(\"".concat(callback.toString(), "\", ").concat(timeout, ")")); + + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + + return nativeSetTimeout.apply(window, [callback, timeout].concat(args)); + } + + window.setTimeout = setTimeoutWrapper; +} +logSetTimeout.names = ['log-setTimeout', 'setTimeout-logger.js', 'ubo-setTimeout-logger.js']; +logSetTimeout.injections = [hit]; + +/* eslint-disable no-console, no-eval */ +/** + * @scriptlet log-eval + * + * @description + * Logs all `eval()` or `new Function()` calls to the console. + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("log-eval") + * ``` + */ + +function logEval(source) { + var log = console.log.bind(console); // wrap eval function + + var nativeEval = window.eval; + + function evalWrapper(str) { + hit(source); + log("eval(\"".concat(str, "\")")); + return nativeEval(str); + } + + window.eval = evalWrapper; // wrap new Function + + var nativeFunction = window.Function; + + function FunctionWrapper() { + hit(source); + + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + log("new Function(".concat(args.join(', '), ")")); + return nativeFunction.apply(this, [].concat(args)); + } + + FunctionWrapper.prototype = Object.create(nativeFunction.prototype); + FunctionWrapper.prototype.constructor = FunctionWrapper; + window.Function = FunctionWrapper; +} +logEval.names = ['log-eval']; +logEval.injections = [hit]; + +/** + * @scriptlet log + * + * @description + * A simple scriptlet which only purpose is to print arguments to console. + * This scriptlet can be helpful for debugging and troubleshooting other scriptlets. + * **Example** + * ``` + * example.org#%#//scriptlet("log", "arg1", "arg2") + * ``` + */ +function log() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + console.log(args); // eslint-disable-line no-console +} +log.names = ['log']; + +/* eslint-disable no-eval, no-extra-bind */ +/** + * @scriptlet noeval + * + * @description + * Prevents page to use eval. + * Notifies about attempts in the console + * + * It is mostly used for `$redirect` rules. + * See [redirect description](../wiki/about-redirects.md#noeval). + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("noeval") + * ``` + */ + +function noeval(source) { + window.eval = function evalWrapper(s) { + hit(source, "AdGuard has prevented eval:\n".concat(s)); + }.bind(); +} +noeval.names = ['noeval', 'noeval.js', 'silent-noeval.js', 'ubo-noeval.js', 'ubo-silent-noeval.js']; +noeval.injections = [hit]; + +/* eslint-disable no-eval, no-extra-bind, func-names */ +/** + * @scriptlet prevent-eval-if + * + * @description + * Prevents page to use eval matching payload. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#noeval-ifjs- + * + * **Parameters** + * - `search` string or regexp matching stringified eval payload + * + * **Examples** + * ``` + * ! + * ``` + * + * @param {string|RegExp} [search] string or regexp matching stringified eval payload + */ + +function preventEvalIf(source, search) { + search = search ? toRegExp(search) : toRegExp('/.?/'); + var nativeEval = window.eval; + + window.eval = function (payload) { + if (!search.test(payload.toString())) { + return nativeEval.call(window, payload); + } + + hit(source, payload); + return undefined; + }.bind(window); +} +preventEvalIf.names = ['prevent-eval-if', 'noeval-if.js', 'ubo-noeval-if.js']; +preventEvalIf.injections = [toRegExp, hit]; + +/* eslint-disable no-console, func-names, no-multi-assign */ +/** + * @scriptlet prevent-fab-3.2.0 + * + * @description + * Prevents execution of the FAB script v3.2.0. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#fuckadblockjs-320- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("prevent-fab-3.2.0") + * ``` + */ + +function preventFab(source) { + hit(source); + + var Fab = function Fab() {}; + + Fab.prototype.check = noop; + Fab.prototype.clearEvent = noop; + Fab.prototype.emitEvent = noop; + + Fab.prototype.on = function (a, b) { + if (!a) { + b(); + } + + return this; + }; + + Fab.prototype.onDetected = function () { + return this; + }; + + Fab.prototype.onNotDetected = function (a) { + a(); + return this; + }; + + Fab.prototype.setOption = noop; + window.FuckAdBlock = window.BlockAdBlock = Fab; // + + window.fuckAdBlock = window.blockAdBlock = new Fab(); +} +preventFab.names = ['prevent-fab-3.2.0', 'fuckadblock.js-3.2.0', 'ubo-fuckadblock.js-3.2.0']; +preventFab.injections = [noop, hit]; + +/* eslint-disable no-console, func-names, no-multi-assign */ +/** + * @scriptlet set-popads-dummy + * + * @description + * Sets static properties PopAds and popns. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#popads-dummyjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("set-popads-dummy") + * ``` + */ + +function setPopadsDummy(source) { + delete window.PopAds; + delete window.popns; + Object.defineProperties(window, { + PopAds: { + get: function get() { + hit(source); + return {}; + } + }, + popns: { + get: function get() { + hit(source); + return {}; + } + } + }); +} +setPopadsDummy.names = ['set-popads-dummy', 'popads-dummy.js', 'ubo-popads-dummy.js']; +setPopadsDummy.injections = [hit]; + +/** + * @scriptlet prevent-popads-net + * + * @description + * Aborts on property write (PopAds, popns), throws reference error with random id. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#popadsnetjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("prevent-popads-net") + * ``` + */ + +function preventPopadsNet(source) { + var rid = randomId(); + + var throwError = function throwError() { + throw new ReferenceError(rid); + }; + + delete window.PopAds; + delete window.popns; + Object.defineProperties(window, { + PopAds: { + set: throwError + }, + popns: { + set: throwError + } + }); + window.onerror = createOnErrorHandler(rid).bind(); + hit(source); +} +preventPopadsNet.names = ['prevent-popads-net', 'popads.net.js', 'ubo-popads.net.js']; +preventPopadsNet.injections = [createOnErrorHandler, randomId, hit]; + +/* eslint-disable func-names */ +/** + * @scriptlet prevent-adfly + * + * @description + * Prevents anti-adblock scripts on adfly short links. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#adfly-defuserjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("prevent-adfly") + * ``` + */ + +function preventAdfly(source) { + var isDigit = function isDigit(data) { + return /^\d$/.test(data); + }; + + var handler = function handler(encodedURL) { + var evenChars = ''; + var oddChars = ''; + + for (var i = 0; i < encodedURL.length; i += 1) { + if (i % 2 === 0) { + evenChars += encodedURL.charAt(i); + } else { + oddChars = encodedURL.charAt(i) + oddChars; + } + } + + var data = (evenChars + oddChars).split(''); + + for (var _i = 0; _i < data.length; _i += 1) { + if (isDigit(data[_i])) { + for (var ii = _i + 1; ii < data.length; ii += 1) { + if (isDigit(data[ii])) { + // eslint-disable-next-line no-bitwise + var temp = parseInt(data[_i], 10) ^ parseInt(data[ii], 10); + + if (temp < 10) { + data[_i] = temp.toString(); + } + + _i = ii; + break; + } + } + } + } + + data = data.join(''); + var decodedURL = window.atob(data).slice(16, -16); + /* eslint-disable compat/compat */ + + if (window.stop) { + window.stop(); + } + /* eslint-enable compat/compat */ + + + window.onbeforeunload = null; + window.location.href = decodedURL; + }; + + var val; // Do not apply handler more than one time + + var applyHandler = true; + var result = setPropertyAccess(window, 'ysmm', { + configurable: false, + set: function set(value) { + if (applyHandler) { + applyHandler = false; + + try { + if (typeof value === 'string') { + handler(value); + } + } catch (err) {} // eslint-disable-line no-empty + + } + + val = value; + }, + get: function get() { + return val; + } + }); + + if (result) { + hit(source); + } else { + window.console.error('Failed to set up prevent-adfly scriptlet'); + } +} +preventAdfly.names = ['prevent-adfly', 'adfly-defuser.js', 'ubo-adfly-defuser.js']; +preventAdfly.injections = [setPropertyAccess, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet debug-on-property-read + * + * @description + * This scriptlet is basically the same as [abort-on-property-read](#abort-on-property-read), but instead of aborting it starts the debugger. + * + * **It is not supposed to be used in production filter lists!** + * + * **Syntax** + * ``` + * ! Aborts script when it tries to access `window.alert` + * example.org#%#//scriptlet("debug-on-property-read", "alert") + * ``` + */ + +/* eslint-enable max-len */ + +function debugOnPropertyRead(source, property) { + if (!property) { + return; + } + + var rid = randomId(); + + var abort = function abort() { + hit(source); // eslint-disable-next-line no-debugger + + debugger; + }; + + var setChainPropAccess = function setChainPropAccess(owner, property) { + var chainInfo = getPropertyInChain(owner, property); + var base = chainInfo.base; + var prop = chainInfo.prop, + chain = chainInfo.chain; + + if (chain) { + var setter = function setter(a) { + base = a; + + if (a instanceof Object) { + setChainPropAccess(a, chain); + } + }; + + Object.defineProperty(owner, prop, { + get: function get() { + return base; + }, + set: setter + }); + return; + } + + setPropertyAccess(base, prop, { + get: abort, + set: function set() {} + }); + }; + + setChainPropAccess(window, property); + window.onerror = createOnErrorHandler(rid).bind(); +} +debugOnPropertyRead.names = ['debug-on-property-read']; +debugOnPropertyRead.injections = [randomId, setPropertyAccess, getPropertyInChain, createOnErrorHandler, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet debug-on-property-write + * + * @description + * This scriptlet is basically the same as [abort-on-property-write](#abort-on-property-write), but instead of aborting it starts the debugger. + * + * **It is not supposed to be used in production filter lists!** + * + * **Syntax** + * ``` + * ! Aborts script when it tries to write in property `window.test` + * example.org#%#//scriptlet("debug-on-property-write", "test") + * ``` + */ + +/* eslint-enable max-len */ + +function debugOnPropertyWrite(source, property) { + if (!property) { + return; + } + + var rid = randomId(); + + var abort = function abort() { + hit(source); // eslint-disable-next-line no-debugger + + debugger; + }; + + var setChainPropAccess = function setChainPropAccess(owner, property) { + var chainInfo = getPropertyInChain(owner, property); + var base = chainInfo.base; + var prop = chainInfo.prop, + chain = chainInfo.chain; + + if (chain) { + var setter = function setter(a) { + base = a; + + if (a instanceof Object) { + setChainPropAccess(a, chain); + } + }; + + Object.defineProperty(owner, prop, { + get: function get() { + return base; + }, + set: setter + }); + return; + } + + setPropertyAccess(base, prop, { + set: abort + }); + }; + + setChainPropAccess(window, property); + window.onerror = createOnErrorHandler(rid).bind(); +} +debugOnPropertyWrite.names = ['debug-on-property-write']; +debugOnPropertyWrite.injections = [randomId, setPropertyAccess, getPropertyInChain, createOnErrorHandler, hit]; + +/* eslint-disable no-new-func */ +/* eslint-disable max-len */ + +/** + * @scriptlet debug-current-inline-script + * + * @description + * This scriptlet is basically the same as [abort-current-inline-script](#abort-current-inline-script), but instead of aborting it starts the debugger. + * + * **It is not supposed to be used in production filter lists!** + * + * **Syntax** + *``` + * ! Aborts script when it tries to access `window.alert` + * example.org#%#//scriptlet("debug-current-inline-script", "alert") + * ``` + */ + +/* eslint-enable max-len */ + +function debugCurrentInlineScript(source, property) { + var search = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; + var regex = search ? toRegExp(search) : null; + var rid = randomId(); + + var getCurrentScript = function getCurrentScript() { + if (!document.currentScript) { + // eslint-disable-line compat/compat + var scripts = document.getElementsByTagName('script'); + return scripts[scripts.length - 1]; + } + + return document.currentScript; // eslint-disable-line compat/compat + }; + + var ourScript = getCurrentScript(); + + var abort = function abort() { + var scriptEl = getCurrentScript(); + + if (scriptEl instanceof HTMLScriptElement && scriptEl.textContent.length > 0 && scriptEl !== ourScript && (!regex || regex.test(scriptEl.textContent))) { + hit(source); // eslint-disable-next-line no-debugger + + debugger; + } + }; + + var setChainPropAccess = function setChainPropAccess(owner, property) { + var chainInfo = getPropertyInChain(owner, property); + var base = chainInfo.base; + var prop = chainInfo.prop, + chain = chainInfo.chain; + + if (chain) { + var setter = function setter(a) { + base = a; + + if (a instanceof Object) { + setChainPropAccess(a, chain); + } + }; + + Object.defineProperty(owner, prop, { + get: function get() { + return base; + }, + set: setter + }); + return; + } + + var currentValue = base[prop]; + setPropertyAccess(base, prop, { + set: function set(value) { + abort(); + currentValue = value; + }, + get: function get() { + abort(); + return currentValue; + } + }); + }; + + setChainPropAccess(window, property); + window.onerror = createOnErrorHandler(rid).bind(); +} +debugCurrentInlineScript.names = ['debug-current-inline-script']; +debugCurrentInlineScript.injections = [randomId, setPropertyAccess, getPropertyInChain, toRegExp, createOnErrorHandler, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet remove-attr + * + * @description + * Removes the specified attributes from DOM notes. This scriptlet runs only once after the page load (DOMContentLoaded). + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#remove-attrjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("remove-attr", attrs[, selector]) + * ``` + * + * - `attrs` — required, attribute or list of attributes joined by '|'; + * - `selector` — optional, CSS selector, specifies DOM nodes from which the attributes will be removed + * + * **Examples** + * 1. Removes by attribute + * ``` + * example.org#%#//scriptlet("remove-attr", "example|test") + * ``` + * + * ```html + * + *
Some text
+ * + * + *
Some text
+ * ``` + * + * 2. Removes with specified selector + * ``` + * example.org#%#//scriptlet("remove-attr", "example", ".inner") + * ``` + * + * ```html + * + *
+ *
Some text
+ *
+ * + * + *
+ *
Some text
+ *
+ * ``` + */ + +/* eslint-enable max-len */ + +function removeAttr(source, attrs, selector) { + if (!attrs) { + return; + } + + attrs = attrs.split(/\s*\|\s*/); + + if (!selector) { + selector = "[".concat(attrs.join('],['), "]"); + } + + var rmattr = function rmattr(ev) { + if (ev) { + window.removeEventListener(ev.type, rmattr, true); + } + + var nodes = [].slice.call(document.querySelectorAll(selector)); + var removed = false; + nodes.forEach(function (node) { + attrs.forEach(function (attr) { + node.removeAttribute(attr); + removed = true; + }); + }); + + if (removed) { + hit(source); + } + }; + + if (document.readyState === 'loading') { + window.addEventListener('DOMContentLoaded', rmattr, true); + } else { + rmattr(); + } +} +removeAttr.names = ['remove-attr', 'remove-attr.js', 'ubo-remove-attr.js']; +removeAttr.injections = [hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet remove-class + * + * @description + * Removes the specified classes from DOM notes. This scriptlet runs only once after the page load (DOMContentLoaded). + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("remove-class", classes[, selector]) + * ``` + * + * - `classes` — required, class or list of classes separated by '|'; + * - `selector` — optional, CSS selector, specifies DOM nodes from which the classes will be removed; + * if there is no selector, every class independently will be removed from all nodes which has one + * + * **Examples** + * 1. Removes by classes + * ``` + * example.org#%#//scriptlet("remove-class", "example|test") + * ``` + * + * ```html + * + *
Some text
+ *
Some text
+ *
Some text
+ * + * + *
Some text
+ *
Some text
+ *
Some text
+ * ``` + * + * 2. Removes with specified selector + * ``` + * example.org#%#//scriptlet("remove-class", "branding", ".inner") + * ``` + * + * ```html + * + *
+ *
Some text
+ *
+ * + * + *
+ *
Some text
+ *
+ * ``` + */ + +/* eslint-enable max-len */ + +function removeClass(source, classNames, selector) { + if (!classNames) { + return; + } + + classNames = classNames.split(/\s*\|\s*/); + var selectors = []; + + if (!selector) { + selectors = classNames.map(function (className) { + return ".".concat(className); + }); + } + + var removeClassHandler = function removeClassHandler(ev) { + if (ev) { + window.removeEventListener(ev.type, removeClassHandler, true); + } + + var nodes = new Set(); + + if (selector) { + var foundedNodes = [].slice.call(document.querySelectorAll(selector)); + foundedNodes.forEach(function (n) { + return nodes.add(n); + }); + } else if (selectors.length > 0) { + selectors.forEach(function (s) { + var elements = document.querySelectorAll(s); + + for (var i = 0; i < elements.length; i += 1) { + var element = elements[i]; + nodes.add(element); + } + }); + } + + var removed = false; + nodes.forEach(function (node) { + classNames.forEach(function (className) { + if (node.classList.contains(className)) { + node.classList.remove(className); + removed = true; + } + }); + }); + + if (removed) { + hit(source); + } + }; + + if (document.readyState === 'loading') { + window.addEventListener('DOMContentLoaded', removeClassHandler, true); + } else { + removeClassHandler(); + } +} +removeClass.names = ['remove-class']; +removeClass.injections = [hit]; + +/** + * @scriptlet disable-newtab-links + * + * @description + * Prevents opening new tabs and windows if there is `target` attribute in element. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#disable-newtab-linksjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("disable-newtab-links") + * ``` + */ + +function disableNewtabLinks(source) { + document.addEventListener('click', function (ev) { + var target = ev.target; + + while (target !== null) { + if (target.localName === 'a' && target.hasAttribute('target')) { + ev.stopPropagation(); + ev.preventDefault(); + hit(source); + break; + } + + target = target.parentNode; + } + }); +} +disableNewtabLinks.names = ['disable-newtab-links', 'disable-newtab-links.js', 'ubo-disable-newtab-links.js']; +disableNewtabLinks.injections = [hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet adjust-setInterval + * + * @description + * Adjusts interval for specified setInterval() callbacks. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#nano-setinterval-boosterjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("adjust-setInterval"[, match [, interval[, boost]]]) + * ``` + * + * - `match` - optional, string/regular expression, matching in stringified callback function + * - `interval` - optional, defaults to 1000, decimal integer, matching interval + * - `boost` - optional, default to 0.05, float, capped at 50 times for up and down, interval multiplier + * + * **Examples** + * 1. Adjust all setInterval() x20 times where interval equal 1000ms: + * ``` + * example.org#%#//scriptlet("adjust-setInterval") + * ``` + * + * 2. Adjust all setInterval() x20 times where callback mathed with `example` and interval equal 1000ms + * ``` + * example.org#%#//scriptlet("adjust-setInterval", "example") + * ``` + * + * 3. Adjust all setInterval() x20 times where callback mathed with `example` and interval equal 400ms + * ``` + * example.org#%#//scriptlet("adjust-setInterval", "example", "400") + * ``` + * + * 4. Slow down setInterval() x2 times where callback matched with `example` and interval equal 400ms + * ``` + * example.org#%#//scriptlet("adjust-setInterval", "example", "400", "2") + * ``` + */ + +/* eslint-enable max-len */ + +function adjustSetInterval(source, match, interval, boost) { + var nativeInterval = window.setInterval; + var nativeIsNaN = Number.isNaN || window.isNaN; // eslint-disable-line compat/compat + + var nativeIsFinite = Number.isFinite || window.isFinite; // eslint-disable-line compat/compat + + interval = parseInt(interval, 10); + interval = nativeIsNaN(interval) ? 1000 : interval; + boost = parseInt(boost, 10); + boost = nativeIsNaN(interval) || !nativeIsFinite(boost) ? 0.05 : boost; + match = match ? toRegExp(match) : toRegExp('/.?/'); + + if (boost < 0.02) { + boost = 0.02; + } + + if (boost > 50) { + boost = 50; + } + + var intervalWrapper = function intervalWrapper(cb, d) { + if (d === interval && match.test(cb.toString())) { + d *= boost; + hit(source); + } + + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + + return nativeInterval.apply(window, [cb, d].concat(args)); + }; + + window.setInterval = intervalWrapper; +} +adjustSetInterval.names = ['adjust-setInterval', 'nano-setInterval-booster.js', 'ubo-nano-setInterval-booster.js']; +adjustSetInterval.injections = [toRegExp, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet adjust-setTimeout + * + * @description + * Adjusts timeout for specified setTimout() callbacks. + * + * Related UBO scriptlet: + * https://github.com/gorhill/uBlock/wiki/Resources-Library#nano-settimeout-boosterjs- + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("adjust-setTimeout"[, match [, timeout[, boost]]]) + * ``` + * + * - `match` - optional, string/regular expression, matching in stringified callback function + * - `timeout` - optional, defaults to 1000, decimal integer, matching interval + * - `boost` - optional, default to 0.05, float, capped at 50 times for up and down, interval multiplier + * + * **Examples** + * 1. Adjust all setTimeout() x20 times where interval equal 1000ms: + * ``` + * example.org#%#//scriptlet("adjust-setTimeout") + * ``` + * + * 2. Adjust all setTimeout() x20 times where callback mathed with `example` and interval equal 1000ms + * ``` + * example.org#%#//scriptlet("adjust-setTimeout", "example") + * ``` + * + * 3. Adjust all setTimeout() x20 times where callback mathed with `example` and interval equal 400ms + * ``` + * example.org#%#//scriptlet("adjust-setTimeout", "example", "400") + * ``` + * + * 4. Slow down setTimeout() x2 times where callback matched with `example` and interval equal 400ms + * ``` + * example.org#%#//scriptlet("adjust-setTimeout", "example", "400", "2") + * ``` + */ + +/* eslint-enable max-len */ + +function adjustSetTimeout(source, match, timeout, boost) { + var nativeTimeout = window.setTimeout; + var nativeIsNaN = Number.isNaN || window.isNaN; // eslint-disable-line compat/compat + + var nativeIsFinite = Number.isFinite || window.isFinite; // eslint-disable-line compat/compat + + timeout = parseInt(timeout, 10); + timeout = nativeIsNaN(timeout) ? 1000 : timeout; + boost = parseInt(boost, 10); + boost = nativeIsNaN(timeout) || !nativeIsFinite(boost) ? 0.05 : boost; + match = match ? toRegExp(match) : toRegExp('/.?/'); + + if (boost < 0.02) { + boost = 0.02; + } + + if (boost > 50) { + boost = 50; + } + + var timeoutWrapper = function timeoutWrapper(cb, d) { + if (d === timeout && match.test(cb.toString())) { + d *= boost; + hit(source); + } + + for (var _len = arguments.length, args = new Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { + args[_key - 2] = arguments[_key]; + } + + return nativeTimeout.apply(window, [cb, d].concat(args)); + }; + + window.setTimeout = timeoutWrapper; +} +adjustSetTimeout.names = ['adjust-setTimeout', 'nano-setTimeout-booster.js', 'ubo-nano-setTimeout-booster.js']; +adjustSetTimeout.injections = [toRegExp, hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet dir-string + * + * @description + * Wraps the `console.dir` API to call the `toString` method of the argument. + * There are several adblock circumvention systems that detect browser devtools + * and hide themselves. Therefore, if we force them to think + * that devtools are open (using this scrciptlet), + * it will automatically disable the adblock circumvention script. + * + * Related ABP source: + * https://github.com/adblockplus/adblockpluscore/blob/6b2a309054cc23432102b85d13f12559639ef495/lib/content/snippets.js#L766 + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("dir-string"[, times]) + * ``` + * - `times` - optional, the number of times to call the `toString` method of the argument to `console.dir` + * + * **Example** + * ``` + * ! Run 2 times + * example.org#%#//scriptlet("dir-string", "2") + * ``` + */ + +/* eslint-enable max-len */ + +function dirString(source, times) { + var _console = console, + dir = _console.dir; + times = parseInt(times, 10); + + function dirWrapper(object) { + // eslint-disable-next-line no-unused-vars + var temp; + + for (var i = 0; i < times; i += 1) { + // eslint-disable-next-line no-unused-expressions + temp = "".concat(object); + } + + if (typeof dir === 'function') { + dir.call(this, object); + } + + hit(source, temp); + } // eslint-disable-next-line no-console + + + console.dir = dirWrapper; +} +dirString.names = ['dir-string', 'abp-dir-string']; +dirString.injections = [hit]; + +/* eslint-disable max-len */ + +/** + * @scriptlet json-prune + * + * @description + * Removes specified properties from the result of calling JSON.parse and returns the caller + * + * **Syntax** + * ``` + * example.org#%#//scriptlet("json-prune"[, propsToRemove [, obligatoryProps]]) + * ``` + * + * - `propsToRemove` - string of space-separated properties to remove + * - `obligatoryProps` - optional, string of space-separated properties which must be all present for the pruning to occur + * + * **Examples** + * 1. Removes property `example` from the results of JSON.parse call + * ``` + * example.org#%#//scriptlet("json-prune", "example") + * ``` + * + * For instance, the following call will return `{ one: 1}` + * + * ```html + * JSON.parse('{"one":1,"example":true}') + * ``` + * + * 2. If there are no specified properties in the result of JSON.parse call, pruning will NOT occur + * ``` + * example.org#%#//scriptlet("json-prune", "one", "obligatoryProp") + * ``` + * + * For instance, the following call will return `{ one: 1, two: 2}` + * + * ```html + * JSON.parse('{"one":1,"two":2}') + * ``` + * + * 3. A property in a list of properties can be a chain of properties + * + * ``` + * example.org#%#//scriptlet("json-prune", "a.b", "adpath.url.first") + * ``` + * + * 4. Call with no arguments will log the current hostname and json payload at the console + * ``` + * example.org#%#//scriptlet("json-prune") + * ``` + */ + +/* eslint-enable max-len */ + +function jsonPrune(source, propsToRemove, requiredInitialProps) { + // eslint-disable-next-line no-console + var log = console.log.bind(console); + var prunePaths = propsToRemove !== undefined && propsToRemove !== '' ? propsToRemove.split(/ +/) : []; + var needlePaths = requiredInitialProps !== undefined && requiredInitialProps !== '' ? requiredInitialProps.split(/ +/) : []; + + function isPruningNeeded(root) { + for (var i = 0; i < needlePaths.length; i += 1) { + var needlePath = needlePaths[i]; + var details = getPropertyInChain(root, needlePath); + var nestedPropName = needlePath.split('').pop(); + + if (details.base[nestedPropName] === undefined) { + return false; + } + } + + return true; + } + + var nativeParse = JSON.parse; + + var parseWrapper = function parseWrapper() { + for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) { + args[_key] = arguments[_key]; + } + + var r = nativeParse.apply(window, args); + + if (prunePaths.length === 0) { + log(window.location.hostname, r); + return r; + } + + if (isPruningNeeded(r) === false) { + return r; + } + + prunePaths.forEach(function (path) { + var ownerObj = getPropertyInChain(r, path); + + if (ownerObj.base) { + delete ownerObj.base[ownerObj.prop]; + } + }); + hit(source); + return r; + }; + + JSON.parse = parseWrapper; +} +jsonPrune.names = ['json-prune', 'json-prune.js', 'ubo-json-prune.js']; +jsonPrune.injections = [hit, getPropertyInChain]; + +/** + * This file must export all scriptlets which should be accessible + */ + +var scriptletsList = /*#__PURE__*/Object.freeze({ + __proto__: null, + abortOnPropertyRead: abortOnPropertyRead, + abortOnPropertyWrite: abortOnPropertyWrite, + preventSetTimeout: preventSetTimeout, + preventSetInterval: preventSetInterval, + preventWindowOpen: preventWindowOpen, + abortCurrentInlineScript: abortCurrentInlineScript, + setConstant: setConstant, + removeCookie: removeCookie, + preventAddEventListener: preventAddEventListener, + preventBab: preventBab, + nowebrtc: nowebrtc, + logAddEventListener: logAddEventListener, + logSetInterval: logSetInterval, + logSetTimeout: logSetTimeout, + logEval: logEval, + log: log, + noeval: noeval, + preventEvalIf: preventEvalIf, + preventFab: preventFab, + setPopadsDummy: setPopadsDummy, + preventPopadsNet: preventPopadsNet, + preventAdfly: preventAdfly, + debugOnPropertyRead: debugOnPropertyRead, + debugOnPropertyWrite: debugOnPropertyWrite, + debugCurrentInlineScript: debugCurrentInlineScript, + removeAttr: removeAttr, + removeClass: removeClass, + disableNewtabLinks: disableNewtabLinks, + adjustSetInterval: adjustSetInterval, + adjustSetTimeout: adjustSetTimeout, + dirString: dirString, + jsonPrune: jsonPrune +}); + +/** + * AdGuard scriptlet rule + */ + +var ADGUARD_SCRIPTLET_MASK_REG = /#@?%#\/\/scriptlet\(.+\)/; // eslint-disable-next-line no-template-curly-in-string + +var ADGUARD_SCRIPTLET_TEMPLATE = '${domains}#%#//scriptlet(${args})'; // eslint-disable-next-line no-template-curly-in-string + +var ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE = '${domains}#@%#//scriptlet(${args})'; +/** + * uBlock scriptlet rule mask + */ + +var UBO_SCRIPTLET_MASK_REG = /#@?#script:inject|#@?#\s*\+js/; +var UBO_SCRIPTLET_MASK_1 = '##+js'; +var UBO_SCRIPTLET_MASK_2 = '##script:inject'; +var UBO_SCRIPTLET_EXCEPTION_MASK_1 = '#@#+js'; +var UBO_SCRIPTLET_EXCEPTION_MASK_2 = '#@#script:inject'; // eslint-disable-next-line no-template-curly-in-string + +var UBO_SCRIPTLET_TEMPLATE = '${domains}##+js(${args})'; // eslint-disable-next-line no-template-curly-in-string + +var UBO_SCRIPTLET_EXCEPTION_TEMPLATE = '${domains}#@#+js(${args})'; +var UBO_ALIAS_NAME_MARKER = 'ubo-'; +/** + * AdBlock Plus snippet rule mask + */ + +var ABP_SCRIPTLET_MASK = '#$#'; +var ABP_SCRIPTLET_EXCEPTION_MASK = '#@$#'; +/** + * AdGuard CSS rule mask + */ + +var ADG_CSS_MASK_REG = /#@?\$#.+?\s*\{.*\}\s*$/g; +/** + * Returns array of strings separated by space which not in quotes + * @param {string} str + */ + +var getSentences = function getSentences(str) { + var reg = /'.*?'|".*?"|\S+/g; + return str.match(reg); +}; +/** + * Replaces string with data by placeholders + * @param {string} str + * @param {Object} data - where keys are placeholders names + */ + + +var replacePlaceholders = function replacePlaceholders(str, data) { + return Object.keys(data).reduce(function (acc, key) { + var reg = new RegExp("\\$\\{".concat(key, "\\}"), 'g'); + acc = acc.replace(reg, data[key]); + return acc; + }, str); +}; +/** + * Checks is AdGuard scriptlet rule + * @param {string} rule rule text + */ + + +var isAdgScriptletRule = function isAdgScriptletRule(rule) { + return rule.indexOf(ADG_SCRIPTLET_MASK) > -1; +}; +/** + * Checks is uBO scriptlet rule + * @param {string} rule rule text + */ + +var isUboScriptletRule = function isUboScriptletRule(rule) { + return (rule.indexOf(UBO_SCRIPTLET_MASK_1) > -1 || rule.indexOf(UBO_SCRIPTLET_MASK_2) > -1 || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_1) > -1 || rule.indexOf(UBO_SCRIPTLET_EXCEPTION_MASK_2) > -1) && UBO_SCRIPTLET_MASK_REG.test(rule); +}; +/** + * Checks is AdBlock Plus snippet + * @param {string} rule rule text + */ + +var isAbpSnippetRule = function isAbpSnippetRule(rule) { + return (rule.indexOf(ABP_SCRIPTLET_MASK) > -1 || rule.indexOf(ABP_SCRIPTLET_EXCEPTION_MASK) > -1) && rule.search(ADG_CSS_MASK_REG) === -1; +}; +/** + * Converts string of UBO scriptlet rule to AdGuard scritlet rule + * @param {String} rule - UBO scriptlet rule + */ + +var convertUboToAdg = function convertUboToAdg(rule) { + var domains = getBeforeRegExp(rule, UBO_SCRIPTLET_MASK_REG); + var mask = rule.match(UBO_SCRIPTLET_MASK_REG)[0]; + var template; + + if (mask.indexOf('@') > -1) { + template = ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; + } else { + template = ADGUARD_SCRIPTLET_TEMPLATE; + } + + var args = getStringInBraces(rule).split(/, /g).map(function (arg, index) { + return index === 0 ? "ubo-".concat(arg) : arg; + }).map(function (arg) { + return wrapInDoubleQuotes(arg); + }).join(', '); + var adgRule = replacePlaceholders(template, { + domains: domains, + args: args + }); + return [adgRule]; +}; +/** + * Convert string of ABP scriptlet rule to AdGuard scritlet rule + * @param {String} rule - ABP scriptlet rule + */ + +var convertAbpToAdg = function convertAbpToAdg(rule) { + var SEMICOLON_DIVIDER = /;(?=(?:(?:[^"]*"){2})*[^"]*$)/g; + var mask = rule.indexOf(ABP_SCRIPTLET_MASK) > -1 ? ABP_SCRIPTLET_MASK : ABP_SCRIPTLET_EXCEPTION_MASK; + var template = mask === ABP_SCRIPTLET_MASK ? ADGUARD_SCRIPTLET_TEMPLATE : ADGUARD_SCRIPTLET_EXCEPTION_TEMPLATE; + var domains = substringBefore(rule, mask); + var args = substringAfter(rule, mask); + return args.split(SEMICOLON_DIVIDER).map(function (args) { + return getSentences(args).filter(function (arg) { + return arg; + }).map(function (arg, index) { + return index === 0 ? "abp-".concat(arg) : arg; + }).map(function (arg) { + return wrapInDoubleQuotes(arg); + }).join(', '); + }).map(function (args) { + return replacePlaceholders(template, { + domains: domains, + args: args + }); + }); +}; +/** + * Converts scriptlet rule to AdGuard one + * @param {*} rule + */ + +var convertScriptletToAdg = function convertScriptletToAdg(rule) { + var result; + + if (isUboScriptletRule(rule)) { + result = convertUboToAdg(rule); + } else if (isAbpSnippetRule(rule)) { + result = convertAbpToAdg(rule); + } else if (isAdgScriptletRule(rule)) { + result = rule; + } + + return result; +}; +/** + * Converts UBO scriptlet rule to AdGuard one + * @param {String} rule - AdGuard scriptlet rule + * @returns {String} - UBO scriptlet rule + */ + +var convertAdgToUbo = function convertAdgToUbo(rule) { + var res; + + if (isAdgScriptletRule(rule)) { + var _parseRule = parseRule(rule), + parsedName = _parseRule.name, + parsedParams = _parseRule.args; // object of name and aliases for the Adg-scriptlet + + + var adgScriptletObject = Object.keys(scriptletsList).map(function (el) { + return scriptletsList[el]; + }).map(function (s) { + var _s$names = toArray(s.names), + name = _s$names[0], + aliases = _s$names.slice(1); + + return { + name: name, + aliases: aliases + }; + }).find(function (el) { + return el.name === parsedName; + }); + var aliases = adgScriptletObject.aliases; + + if (aliases.length > 0) { + var uboAlias = adgScriptletObject.aliases // eslint-disable-next-line no-restricted-properties + .find(function (alias) { + return alias.includes(UBO_ALIAS_NAME_MARKER); + }); + + if (uboAlias) { + var mask = rule.match(ADGUARD_SCRIPTLET_MASK_REG)[0]; + var template; + + if (mask.indexOf('@') > -1) { + template = UBO_SCRIPTLET_EXCEPTION_TEMPLATE; + } else { + template = UBO_SCRIPTLET_TEMPLATE; + } + + var domains = getBeforeRegExp(rule, ADGUARD_SCRIPTLET_MASK_REG); + var uboName = uboAlias.replace(UBO_ALIAS_NAME_MARKER, ''); + var args = "".concat(uboName, ", ").concat(parsedParams.join(', ')); + var uboRule = replacePlaceholders(template, { + domains: domains, + args: args + }); + res = uboRule; + } + } + } + + return res; +}; + +/** + * @typedef {Object} Source - scriptlet properties + * @property {string} name Scriptlet name + * @property {Array} args Arguments for scriptlet function + * @property {'extension'|'corelibs'} engine Defines the final form of scriptlet string presentation + * @property {string} [version] + * @property {boolean} [verbose] flag to enable printing to console debug information + * @property {string} [ruleText] Source rule text is used for debugging purposes + */ + +/** + * Find scriptlet by it's name + * @param {string} name + */ + +function getScriptletByName(name) { + var scriptlets = Object.keys(scriptletsList).map(function (key) { + return scriptletsList[key]; + }); + return scriptlets.find(function (s) { + return s.names && s.names.indexOf(name) > -1; + }); +} +/** + * Checks if the scriptlet name is valid + * @param {String} name - Scriptlet name + */ + + +function isValidScriptletName(name) { + if (!name) { + return false; + } + + var scriptlet = getScriptletByName(name); + + if (!scriptlet) { + return false; + } + + return true; +} +/** +* Returns scriptlet code by param +* @param {Source} source +*/ + + +function getScriptletCode(source) { + if (!isValidScriptletName(source.name)) { + return null; + } + + var scriptlet = getScriptletByName(source.name); + var result = attachDependencies(scriptlet); + result = addCall(scriptlet, result); + result = source.engine === 'corelibs' ? wrapInNonameFunc(result) : passSourceAndProps(source, result); + return result; +} +/** + * Validates any scriptlet rule + * @param {String} input - can be Adguard or Ubo or Abp scriptlet rule + */ + + +function isValidScriptletRule(input) { + if (!input) { + return false; + } + + var rule = convertScriptletToAdg(input); + var parsedRule = parseRule(rule); + return isValidScriptletName(parsedRule.name); +} +/** + * Global scriptlet variable + * + * @returns {Object} object with method `invoke` + * `invoke` method receives one argument with `Source` type + * `validate` method receives one argument with `String` type + */ +// eslint-disable-next-line no-undef + + +scriptlets = function () { + return { + invoke: getScriptletCode, + validateName: isValidScriptletName, + validateRule: isValidScriptletRule, + isAdgScriptletRule: isAdgScriptletRule, + isUboScriptletRule: isUboScriptletRule, + isAbpSnippetRule: isAbpSnippetRule, + convertUboToAdg: convertUboToAdg, + convertAbpToAdg: convertAbpToAdg, + convertScriptletToAdg: convertScriptletToAdg, + convertAdgToUbo: convertAdgToUbo + }; +}(); + +/** + * ------------------------------------------- + * | | + * | If you want to add your own scriptlet | + * | please put your code below | + * | | + * ------------------------------------------- + */ diff --git a/package.json b/package.json index 5f82fb57a..a872533d6 100644 --- a/package.json +++ b/package.json @@ -9,7 +9,6 @@ "test": "yarn build-test && node tests/index.js", "test-watch": "rollup -c --watch --environment UI_TEST", "test-lib-watch": "rollup -c --watch --environment UI_LIB_TEST", - "debug-lib-watch": "rollup -c --watch --environment DEBUG_LIB", "browserstack": "yarn build-test && node browserstack.js", "gui-test": "yarn build-test && open http://localhost:8585 && node ./tests/server.js", "lint": "eslint .", diff --git a/rollup.config.js b/rollup.config.js index 257cc02b8..df48d6133 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -33,15 +33,26 @@ const bundleBuild = { input: { scriptlets: 'src/scriptlets/index.js', }, - output: { - dir: 'dist', - entryFileNames: '[name].js', - format: 'iife', - strict: false, - sourcemap: true, - banner, - footer, - }, + output: [ + { + dir: 'dist', + entryFileNames: '[name].js', + format: 'iife', + strict: false, + sourcemap: true, + banner, + footer, + }, + { + dir: 'dist/cjs', + chunkFileNames: '[name].[format].js', + format: 'cjs', + strict: false, + sourcemap: false, + banner, + footer, + }, + ], plugins: [ resolve(), commonjs({ @@ -92,46 +103,6 @@ const testBuild = { ], }; -const debugLib = { - input: 'src/scriptlets/index.js', - output: { - dir: 'dist', - entryFileNames: '[name].js', - format: 'iife', - strict: false, - sourcemap: true, - }, - watch: { - include: ['*/**'], - chokidar: false, - }, - plugins: [ - clear({ - targets: [LIB_TESTS_DIST], - }), - resolve(), - commonjs({ - include: 'node_modules/**', - }), - babel({ - exclude: 'node_modules/**', - runtimeHelpers: true, - }), - copy({ - targets: [{ - src: [ - 'tests/lib-tests/tests.html', - 'tests/styles.css', - 'node_modules/qunit/qunit/qunit.js', - 'node_modules/sinon/pkg/sinon.js', - 'dist/scriptlets.js', - ], - dest: LIB_TESTS_DIST, - }], - }), - ], -}; - const testLibTests = { input: 'tests/lib-tests/index.test.js', output: { @@ -160,7 +131,10 @@ const testLibTests = { copy({ targets: [{ src: [ - 'tests/lib-tests/index.test.js', + 'tests/lib-tests/tests.html', + 'tests/styles.css', + 'node_modules/qunit/qunit/qunit.js', + 'node_modules/sinon/pkg/sinon.js', 'dist/scriptlets.js', ], dest: LIB_TESTS_DIST, @@ -201,14 +175,11 @@ if (isCleanBuild) { const isTest = process.env.UI_TEST === 'true'; const isLibTest = process.env.UI_LIB_TEST === 'true'; -const isDebugLib = process.env.DEBUG_LIB === 'true'; let resultBuilds = []; -if (isDebugLib) { - resultBuilds = [bundleBuild, debugLib]; -} else if (isLibTest) { - resultBuilds = [debugLib, testLibTests]; +if (isLibTest) { + resultBuilds = [bundleBuild, testLibTests]; } else if (isTest) { resultBuilds = [bundleBuild, testBuild]; } else { From 5f2c006b2e720448d7ea06eb163fc47131def68c Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Thu, 30 Jan 2020 14:07:37 +0300 Subject: [PATCH 11/14] slightly fix rollup --- dist/cjs/scriptlets.js | 1 + rollup.config.js | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dist/cjs/scriptlets.js b/dist/cjs/scriptlets.js index 81a54b184..2081284c2 100644 --- a/dist/cjs/scriptlets.js +++ b/dist/cjs/scriptlets.js @@ -3135,3 +3135,4 @@ scriptlets = function () { * | | * ------------------------------------------- */ +//# sourceMappingURL=scriptlets.js.map diff --git a/rollup.config.js b/rollup.config.js index df48d6133..55168fe2e 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -48,7 +48,7 @@ const bundleBuild = { chunkFileNames: '[name].[format].js', format: 'cjs', strict: false, - sourcemap: false, + sourcemap: true, banner, footer, }, From f11c01c7df11eb429cb607fd5410c5b94acdfdd3 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Thu, 30 Jan 2020 18:58:28 +0300 Subject: [PATCH 12/14] fix building of scriptlets as cjs module --- dist/cjs/scriptlets.js | 29 ++++----- dist/scriptlets.js | 32 +++++----- package.json | 1 + rollup.config.js | 74 ++++++++++++++--------- scripts/build-docs.js | 1 + src/scriptlets/index.js | 112 +--------------------------------- src/scriptlets/scriptlets.js | 114 +++++++++++++++++++++++++++++++++++ 7 files changed, 193 insertions(+), 170 deletions(-) create mode 100644 src/scriptlets/scriptlets.js diff --git a/dist/cjs/scriptlets.js b/dist/cjs/scriptlets.js index 2081284c2..114ff6422 100644 --- a/dist/cjs/scriptlets.js +++ b/dist/cjs/scriptlets.js @@ -3112,20 +3112,21 @@ function isValidScriptletRule(input) { // eslint-disable-next-line no-undef -scriptlets = function () { - return { - invoke: getScriptletCode, - validateName: isValidScriptletName, - validateRule: isValidScriptletRule, - isAdgScriptletRule: isAdgScriptletRule, - isUboScriptletRule: isUboScriptletRule, - isAbpSnippetRule: isAbpSnippetRule, - convertUboToAdg: convertUboToAdg, - convertAbpToAdg: convertAbpToAdg, - convertScriptletToAdg: convertScriptletToAdg, - convertAdgToUbo: convertAdgToUbo - }; -}(); +var scriptlets = { + invoke: getScriptletCode, + validateName: isValidScriptletName, + validateRule: isValidScriptletRule, + isAdgScriptletRule: isAdgScriptletRule, + isUboScriptletRule: isUboScriptletRule, + isAbpSnippetRule: isAbpSnippetRule, + convertUboToAdg: convertUboToAdg, + convertAbpToAdg: convertAbpToAdg, + convertScriptletToAdg: convertScriptletToAdg, + convertAdgToUbo: convertAdgToUbo +}; + // module.exports = scriptlets; + +module.exports = scriptlets; /** * ------------------------------------------- diff --git a/dist/scriptlets.js b/dist/scriptlets.js index b26a73862..692724bf8 100644 --- a/dist/scriptlets.js +++ b/dist/scriptlets.js @@ -3106,27 +3106,27 @@ /** * Global scriptlet variable * - * @returns {Object} object with method `invoke` + * @returns {Object} object with methods: * `invoke` method receives one argument with `Source` type * `validate` method receives one argument with `String` type */ - // eslint-disable-next-line no-undef - scriptlets = function () { - return { - invoke: getScriptletCode, - validateName: isValidScriptletName, - validateRule: isValidScriptletRule, - isAdgScriptletRule: isAdgScriptletRule, - isUboScriptletRule: isUboScriptletRule, - isAbpSnippetRule: isAbpSnippetRule, - convertUboToAdg: convertUboToAdg, - convertAbpToAdg: convertAbpToAdg, - convertScriptletToAdg: convertScriptletToAdg, - convertAdgToUbo: convertAdgToUbo - }; - }(); + var scriptlets$1 = { + invoke: getScriptletCode, + validateName: isValidScriptletName, + validateRule: isValidScriptletRule, + isAdgScriptletRule: isAdgScriptletRule, + isUboScriptletRule: isUboScriptletRule, + isAbpSnippetRule: isAbpSnippetRule, + convertUboToAdg: convertUboToAdg, + convertAbpToAdg: convertAbpToAdg, + convertScriptletToAdg: convertScriptletToAdg, + convertAdgToUbo: convertAdgToUbo + }; + // module.exports = scriptlets; + + scriptlets = scriptlets$1; }()); diff --git a/package.json b/package.json index a872533d6..92cbd1791 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ }, "author": "devteam@adguard.com", "license": "LGPL-3.0", + "main": "dist/cjs/scriptlets.js", "devDependencies": { "@babel/cli": "^7.7.0", "@babel/core": "^7.2.2", diff --git a/rollup.config.js b/rollup.config.js index 55168fe2e..f76cbdb2a 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -29,30 +29,44 @@ const TESTS_DIST = 'tests/dist'; const LIB_TESTS_DIST = 'tests/dist/lib-tests'; const TMP_DIR = 'tmp'; -const bundleBuild = { +const mainConfig = { input: { scriptlets: 'src/scriptlets/index.js', }, - output: [ - { - dir: 'dist', - entryFileNames: '[name].js', - format: 'iife', - strict: false, - sourcemap: true, - banner, - footer, - }, - { - dir: 'dist/cjs', - chunkFileNames: '[name].[format].js', - format: 'cjs', - strict: false, - sourcemap: true, - banner, - footer, - }, + output: { + dir: 'dist', + entryFileNames: '[name].js', + format: 'iife', + strict: false, + sourcemap: true, + banner, + footer, + }, + plugins: [ + resolve(), + commonjs({ + include: 'node_modules/**', + }), + babel({ + exclude: 'node_modules/**', + runtimeHelpers: true, + }), ], +}; + +const cjsConfig = { + input: { + scriptletsCjs: 'src/scriptlets/scriptlets.js', + }, + output: { + dir: 'dist/cjs', + chunkFileNames: '[name].js', + format: 'cjs', + strict: false, + sourcemap: true, + banner, + footer, + }, plugins: [ resolve(), commonjs({ @@ -65,7 +79,7 @@ const bundleBuild = { ], }; -const testBuild = { +const testConfig = { input: { tests: 'tests/index.test.js', }, @@ -103,7 +117,7 @@ const testBuild = { ], }; -const testLibTests = { +const testLibConfig = { input: 'tests/lib-tests/index.test.js', output: { dir: LIB_TESTS_DIST, @@ -143,7 +157,7 @@ const testLibTests = { ], }; -const tmpRedirectsBuild = { +const tmpRedirectsConfig = { input: { tmpRedirects: 'src/redirects/index.js', }, @@ -169,21 +183,21 @@ const tmpRedirectsBuild = { const isCleanBuild = process.env.CLEAN === 'true'; // strip comments if (isCleanBuild) { - bundleBuild.plugins.push(cleanup()); - tmpRedirectsBuild.plugins.push(cleanup()); + mainConfig.plugins.push(cleanup()); + tmpRedirectsConfig.plugins.push(cleanup()); } const isTest = process.env.UI_TEST === 'true'; const isLibTest = process.env.UI_LIB_TEST === 'true'; -let resultBuilds = []; +let resultConfig = []; if (isLibTest) { - resultBuilds = [bundleBuild, testLibTests]; + resultConfig = [mainConfig, testLibConfig]; } else if (isTest) { - resultBuilds = [bundleBuild, testBuild]; + resultConfig = [mainConfig, testConfig]; } else { - resultBuilds = [bundleBuild, tmpRedirectsBuild]; + resultConfig = [mainConfig, cjsConfig, tmpRedirectsConfig]; } -module.exports = resultBuilds; +module.exports = resultConfig; diff --git a/scripts/build-docs.js b/scripts/build-docs.js index 1b2234225..4007072ff 100644 --- a/scripts/build-docs.js +++ b/scripts/build-docs.js @@ -14,6 +14,7 @@ const ABOUT_REDIRECTS_PATH = path.resolve(__dirname, '../wiki/about-redirects.md // files which are not scriptlets or redirects in their directories const NON_SCRIPTLETS_FILES = [ 'index.js', + 'scriptlets.js', 'scriptletsList.js', ]; diff --git a/src/scriptlets/index.js b/src/scriptlets/index.js index 9016667e8..696c4b3c9 100644 --- a/src/scriptlets/index.js +++ b/src/scriptlets/index.js @@ -1,112 +1,4 @@ -import { - attachDependencies, - addCall, - passSourceAndProps, - wrapInNonameFunc, -} from '../helpers/injector'; +import scriptletsModule from './scriptlets'; -import { - isAdgScriptletRule, - isUboScriptletRule, - isAbpSnippetRule, - convertUboToAdg, - convertAbpToAdg, - convertScriptletToAdg, - convertAdgToUbo, -} from '../helpers/converter'; - -import { parseRule } from '../helpers/parse-rule'; - -import * as scriptletsList from './scriptletsList'; - -/** - * @typedef {Object} Source - scriptlet properties - * @property {string} name Scriptlet name - * @property {Array} args Arguments for scriptlet function - * @property {'extension'|'corelibs'} engine Defines the final form of scriptlet string presentation - * @property {string} [version] - * @property {boolean} [verbose] flag to enable printing to console debug information - * @property {string} [ruleText] Source rule text is used for debugging purposes - */ - - -/** - * Find scriptlet by it's name - * @param {string} name - */ -function getScriptletByName(name) { - const scriptlets = Object.keys(scriptletsList).map((key) => scriptletsList[key]); - return scriptlets - .find((s) => s.names && s.names.indexOf(name) > -1); -} - -/** - * Checks if the scriptlet name is valid - * @param {String} name - Scriptlet name - */ -function isValidScriptletName(name) { - if (!name) { - return false; - } - const scriptlet = getScriptletByName(name); - if (!scriptlet) { - return false; - } - return true; -} - -/** -* Returns scriptlet code by param -* @param {Source} source -*/ -function getScriptletCode(source) { - if (!isValidScriptletName(source.name)) { - return null; - } - - const scriptlet = getScriptletByName(source.name); - let result = attachDependencies(scriptlet); - result = addCall(scriptlet, result); - result = source.engine === 'corelibs' - ? wrapInNonameFunc(result) - : passSourceAndProps(source, result); - return result; -} - -/** - * Validates any scriptlet rule - * @param {String} input - can be Adguard or Ubo or Abp scriptlet rule - */ -function isValidScriptletRule(input) { - if (!input) { - return false; - } - - const rule = convertScriptletToAdg(input); - - const parsedRule = parseRule(rule); - - return isValidScriptletName(parsedRule.name); -} - - -/** - * Global scriptlet variable - * - * @returns {Object} object with method `invoke` - * `invoke` method receives one argument with `Source` type - * `validate` method receives one argument with `String` type - */ // eslint-disable-next-line no-undef -scriptlets = (() => ({ - invoke: getScriptletCode, - validateName: isValidScriptletName, - validateRule: isValidScriptletRule, - isAdgScriptletRule, - isUboScriptletRule, - isAbpSnippetRule, - convertUboToAdg, - convertAbpToAdg, - convertScriptletToAdg, - convertAdgToUbo, -}))(); +scriptlets = scriptletsModule; diff --git a/src/scriptlets/scriptlets.js b/src/scriptlets/scriptlets.js new file mode 100644 index 000000000..9deb0c433 --- /dev/null +++ b/src/scriptlets/scriptlets.js @@ -0,0 +1,114 @@ +import { + attachDependencies, + addCall, + passSourceAndProps, + wrapInNonameFunc, +} from '../helpers/injector'; + +import { + isAdgScriptletRule, + isUboScriptletRule, + isAbpSnippetRule, + convertUboToAdg, + convertAbpToAdg, + convertScriptletToAdg, + convertAdgToUbo, +} from '../helpers/converter'; + +import { parseRule } from '../helpers/parse-rule'; + +import * as scriptletsList from './scriptletsList'; + +/** + * @typedef {Object} Source - scriptlet properties + * @property {string} name Scriptlet name + * @property {Array} args Arguments for scriptlet function + * @property {'extension'|'corelibs'} engine Defines the final form of scriptlet string presentation + * @property {string} [version] + * @property {boolean} [verbose] flag to enable printing to console debug information + * @property {string} [ruleText] Source rule text is used for debugging purposes + */ + + +/** + * Find scriptlet by it's name + * @param {string} name + */ +function getScriptletByName(name) { + const scriptlets = Object.keys(scriptletsList).map((key) => scriptletsList[key]); + return scriptlets + .find((s) => s.names && s.names.indexOf(name) > -1); +} + +/** + * Checks if the scriptlet name is valid + * @param {String} name - Scriptlet name + */ +function isValidScriptletName(name) { + if (!name) { + return false; + } + const scriptlet = getScriptletByName(name); + if (!scriptlet) { + return false; + } + return true; +} + +/** +* Returns scriptlet code by param +* @param {Source} source +*/ +function getScriptletCode(source) { + if (!isValidScriptletName(source.name)) { + return null; + } + + const scriptlet = getScriptletByName(source.name); + let result = attachDependencies(scriptlet); + result = addCall(scriptlet, result); + result = source.engine === 'corelibs' + ? wrapInNonameFunc(result) + : passSourceAndProps(source, result); + return result; +} + +/** + * Validates any scriptlet rule + * @param {String} input - can be Adguard or Ubo or Abp scriptlet rule + */ +function isValidScriptletRule(input) { + if (!input) { + return false; + } + + const rule = convertScriptletToAdg(input); + + const parsedRule = parseRule(rule); + + return isValidScriptletName(parsedRule.name); +} + + +/** + * Global scriptlet variable + * + * @returns {Object} object with methods: + * `invoke` method receives one argument with `Source` type + * `validate` method receives one argument with `String` type + */ +const scriptlets = { + invoke: getScriptletCode, + validateName: isValidScriptletName, + validateRule: isValidScriptletRule, + isAdgScriptletRule, + isUboScriptletRule, + isAbpSnippetRule, + convertUboToAdg, + convertAbpToAdg, + convertScriptletToAdg, + convertAdgToUbo, +}; + +export default scriptlets; +// module.exports = scriptlets; From f8758cb8183fcbe2cb5afa7a4937776a39b8ccb6 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Thu, 30 Jan 2020 19:11:02 +0300 Subject: [PATCH 13/14] edit readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5967dd3f2..c8ca87c6b 100644 --- a/README.md +++ b/README.md @@ -118,9 +118,9 @@ yarn browserstack #### Scriptlets library -`dist/scriptlets.js` +Finally we have build Scriptlets as a CJS module which can be imported from `dist/cjs/scriptlets.js`. -Creates a global variable `scriptlets`. +And also there is a module at `dist/scriptlets.js` which has been exported to a global variable `scriptlets` with such methods: ```javascript /** From a4baba50327fad7fe03a174d345001bcbe9d0ad3 Mon Sep 17 00:00:00 2001 From: Slava Leleka Date: Thu, 30 Jan 2020 19:13:44 +0300 Subject: [PATCH 14/14] delete comments --- dist/scriptlets.js | 1 - src/scriptlets/scriptlets.js | 1 - 2 files changed, 2 deletions(-) diff --git a/dist/scriptlets.js b/dist/scriptlets.js index 692724bf8..1fbed6dc9 100644 --- a/dist/scriptlets.js +++ b/dist/scriptlets.js @@ -3124,7 +3124,6 @@ convertScriptletToAdg: convertScriptletToAdg, convertAdgToUbo: convertAdgToUbo }; - // module.exports = scriptlets; scriptlets = scriptlets$1; diff --git a/src/scriptlets/scriptlets.js b/src/scriptlets/scriptlets.js index 9deb0c433..a0d56d37c 100644 --- a/src/scriptlets/scriptlets.js +++ b/src/scriptlets/scriptlets.js @@ -111,4 +111,3 @@ const scriptlets = { }; export default scriptlets; -// module.exports = scriptlets;