From 24b3aebff0589b39d43d1908bdb0d2ee95e36fc5 Mon Sep 17 00:00:00 2001 From: daengdaenglee Date: Tue, 22 Sep 2020 14:21:27 +0900 Subject: [PATCH 1/6] =?UTF-8?q?FxDOM.contains=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/fxsvg.js | 2 ++ src/index.js | 1 + 2 files changed, 3 insertions(+) diff --git a/src/fxsvg.js b/src/fxsvg.js index 79336bc..a2475bb 100644 --- a/src/fxsvg.js +++ b/src/fxsvg.js @@ -2,6 +2,7 @@ import { $append, $appendTo, $closest, + $contains, $hide, $off, $on, @@ -83,6 +84,7 @@ export const FxSVG = { append: $append, appendTo: $appendTo, closest: $closest, + contains: $contains, hide: $hide, off: $off, on: $on, diff --git a/src/index.js b/src/index.js index 5bcac6e..f994bc4 100644 --- a/src/index.js +++ b/src/index.js @@ -2,6 +2,7 @@ export { $append as $$append, $appendTo as $$appendTo, $closest as $$closest, + $contains as $$contains, $hide as $$hide, $off as $$off, $on as $$on, From a080732c096403fb388bc3c91a724330f13e246a Mon Sep 17 00:00:00 2001 From: daengdaenglee Date: Wed, 23 Sep 2020 18:06:32 +0900 Subject: [PATCH 2/6] =?UTF-8?q?$$joinPathData=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/joinPathData/joinPathData.index.js | 251 ++++++++++++++++++++----- src/joinPathData/joinPathData.spec.js | 210 ++++++++++----------- 2 files changed, 297 insertions(+), 164 deletions(-) diff --git a/src/joinPathData/joinPathData.index.js b/src/joinPathData/joinPathData.index.js index c44f7fe..8dd29db 100644 --- a/src/joinPathData/joinPathData.index.js +++ b/src/joinPathData/joinPathData.index.js @@ -1,32 +1,17 @@ -import { deepFlatL, join, mapL, isArray } from "fxjs2"; -import { InvalidArgumentsError } from "../Errors/InvalidArgumentsError.js"; import { - REGEXP_STR_DRAW_TO_COMMAND, - REGEXP_STR_SVG_PATH, -} from "../parsePathData/const.js"; - -const VALID_COMMAND_SET = new Set([ - "M", - "m", - "L", - "l", - "H", - "h", - "V", - "v", - "C", - "c", - "S", - "s", - "Q", - "q", - "T", - "t", - "A", - "a", - "Z", - "z", -]); + join, + mapL, + isArray, + equals2, + go, + eachL, + not, + tap, + every, + toIter, + flatL, +} from "fxjs2"; +import { InvalidArgumentsError } from "../Errors/InvalidArgumentsError.js"; /** * @typedef {string} Command @@ -43,29 +28,181 @@ const FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS = `$$toStringPathCommandParamete * * @param {Object} path_command_parameters * @param {Command} path_command_parameters.command - * @param {Parameter|Array} path_command_parameters.parameters + * @param {Parameter} path_command_parameters.parameters * @returns {string} SVG path + parameters path data string * @throws {InvalidArgumentsError} */ export const $$toStringPathCommandParameters = ({ command, parameters }) => { - if (!VALID_COMMAND_SET.has(command)) { - throw new InvalidArgumentsError( - FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, - "command" + if ( + equals2(command.toLowerCase(), "m") || + equals2(command.toLowerCase(), "l") || + equals2(command.toLowerCase(), "t") + ) { + return go( + parameters, + tap((l) => { + if (!isArray(l) || not(equals2(l.length, 2))) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + eachL((a) => { + if (!Number.isFinite(a)) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + join(" "), + (s) => `${command} ${s}` + ); + } + + if ( + equals2(command.toLowerCase(), "h") || + equals2(command.toLowerCase(), "v") + ) { + if (!Number.isFinite(parameters)) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + return `${command} ${parameters}`; + } + + if (equals2(command.toLowerCase(), "c")) { + return go( + parameters, + tap((l) => { + if (!isArray(l) || not(equals2(l.length, 3))) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + eachL((pair) => { + if (!isArray(pair) || not(equals2(pair.length, 2))) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + flatL, + eachL((a) => { + if (!Number.isFinite(a)) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + join(" "), + (s) => `${command} ${s}` ); } - const str = `${command} ${ - isArray(parameters) ? join(" ", deepFlatL(parameters)) || "" : parameters - }`.trim(); - if (!new RegExp(`^${REGEXP_STR_DRAW_TO_COMMAND}$`).test(str)) { - throw new InvalidArgumentsError( - FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, - `"parameters"` + if ( + equals2(command.toLowerCase(), "s") || + equals2(command.toLowerCase(), "q") + ) { + return go( + parameters, + tap((l) => { + if (!isArray(l) || not(equals2(l.length, 2))) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + eachL((pair) => { + if (!isArray(pair) || not(equals2(pair.length, 2))) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + flatL, + eachL((a) => { + if (!Number.isFinite(a)) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + join(" "), + (s) => `${command} ${s}` ); } - return str; + if (equals2(command.toLowerCase(), "a")) { + if (!isArray(parameters) || not(equals2(parameters.length, 7))) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + const [ + rx, + ry, + x_axis_rotation, + large_arc_flag, + sweep_flag, + x, + y, + ] = parameters; + if ( + not(every(Number.isFinite, [rx, ry, x_axis_rotation, x, y])) || + not( + every((flag) => equals2(0, flag) || equals2(1, flag), [ + large_arc_flag, + sweep_flag, + ]) + ) + ) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + return `${command} ${rx} ${ry} ${x_axis_rotation} ${large_arc_flag} ${sweep_flag} ${x} ${y}`; + } + + if (equals2(command.toLowerCase(), "z")) { + if (!isArray(parameters) || parameters.length > 0) { + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + return command; + } + + throw new InvalidArgumentsError( + FN_PATH_TO_STRING_PATH_COMMAND_PARAMETERS, + `"command"`, + JSON.stringify({ command, parameters }) + ); }; const FN_PATH_JOIN_PATH_DATA = `$$joinPathData`; @@ -73,15 +210,31 @@ const FN_PATH_JOIN_PATH_DATA = `$$joinPathData`; * Make SVG path data string of the input list of command + parameters object. * The return string is for "d" attributes of a SVG element. * - * @param {Array<{command: Command, parameters: Parameter|Array}>} path_data + * @param {Array<{command: Command, parameters: Parameter}>} path_data * @returns {string} * @throws {InvalidArgumentsError} */ -export const $$joinPathData = (path_data = []) => { - const str = join(" ", mapL($$toStringPathCommandParameters, path_data)); - if (!new RegExp(REGEXP_STR_SVG_PATH).test(str)) { - throw new InvalidArgumentsError(FN_PATH_JOIN_PATH_DATA, `"path_data"`); - } +export const $$joinPathData = (path_data = []) => + go( + path_data, + toIter, + function* (iter) { + const { value, done } = iter.next(); + if (done) { + return; + } - return str; -}; + if (not(equals2(value.command.toLowerCase(), "m"))) { + throw new InvalidArgumentsError( + FN_PATH_JOIN_PATH_DATA, + `"path_data"`, + `The first command is not one of "M" and "m".` + ); + } + + yield value; + yield* iter; + }, + mapL($$toStringPathCommandParameters), + join(" ") + ); diff --git a/src/joinPathData/joinPathData.spec.js b/src/joinPathData/joinPathData.spec.js index e24cfb2..8d456bb 100644 --- a/src/joinPathData/joinPathData.spec.js +++ b/src/joinPathData/joinPathData.spec.js @@ -34,29 +34,6 @@ export default ({ describe, it }) => [ expect(str).equal(expect_str); } }); - - it(`When parameters is "Array<[number, number]>".`, function () { - for (const command of command_list) { - // given - /** @type {Array>} */ - const parameters = go( - rangeL(Infinity), - mapL(() => makeRandomNumber(-100, 100)), - chunkL(2), - take(makeRandomInt(1, 10)) - ); - const expect_str = `${command} ${join(" ", deepFlatL(parameters))}`; - - // when - const str = $$toStringPathCommandParameters({ - command, - parameters, - }); - - // then - expect(str).equal(expect_str); - } - }); }); describe(`Throw InvalidArgumentsError.`, function () { @@ -74,14 +51,10 @@ export default ({ describe, it }) => [ } }); - it(`When parameters is "Array{length: odd_natural_number}".`, function () { + it(`When parameters is "Array{length: 1}".`, function () { for (const command of command_list) { // given - /** @type {Array} */ - const parameters = map( - () => makeRandomNumber(-100, 100), - rangeL(3) - ); + const parameters = [makeRandomNumber(-100, 100)]; // when const f = () => @@ -92,15 +65,13 @@ export default ({ describe, it }) => [ } }); - it(`When parameters is "Array{length: odd_natural_number}>{length: odd_natural_number}".`, function () { + it(`When parameters is "Array{length: >=3}".`, function () { for (const command of command_list) { // given - /** @type {Array>} */ - const parameters = go( - rangeL(Infinity), - mapL(() => makeRandomNumber(-100, 100)), - chunkL(3), - take(7) + /** @type {Array} */ + const parameters = map( + () => makeRandomNumber(-100, 100), + rangeL(makeRandomInt(3, 10)) ); // when @@ -137,7 +108,7 @@ export default ({ describe, it }) => [ }); describe(`Throw InvalidArgumentsError.`, function () { - it(`When parameters is "Array".`, function () { + it(`When parameters is "Array{length: >=1}".`, function () { for (const command of command_list) { // given /** @type {Array} */ @@ -156,7 +127,7 @@ export default ({ describe, it }) => [ } }); - it(`When parameters is "Array>".`, function () { + it(`When parameters is "Array>{length: >=1}".`, function () { for (const command of command_list) { // given /** @type {Array} */ @@ -198,34 +169,31 @@ export default ({ describe, it }) => [ expect(receive_str).equal(expect_str); } }); + }); - it(`When parameters is "Array".`, function () { + describe(`Throw InvalidArgumentsError.`, function () { + it(`When parameters is an empty list.`, function () { for (const command of command_list) { // given - /** @type {Array} */ - const parameters = map( - () => makeRandomNumber(-100, 100), - rangeL(makeRandomInt(1, 10)) - ); - const expect_str = `${command} ${join(" ", deepFlatL(parameters))}`; + const parameters = []; // when - const receive_str = $$toStringPathCommandParameters({ - command, - parameters, - }); + const f = () => + $$toStringPathCommandParameters({ command, parameters }); // then - expect(receive_str).equal(expect_str); + expect(f).throw(InvalidArgumentsError); } }); - }); - describe(`Throw InvalidArgumentsError.`, function () { - it(`When parameters is an empty list.`, function () { + it(`When parameters is "Array".`, function () { for (const command of command_list) { // given - const parameters = []; + /** @type {Array} */ + const parameters = map( + () => makeRandomNumber(-100, 100), + rangeL(makeRandomInt(1, 10)) + ); // when const f = () => @@ -264,37 +232,33 @@ export default ({ describe, it }) => [ expect(receive_str).equal(expect_str); } }); + }); - it(`When parameters is "Array{length: 3}>".`, function () { + describe(`Throw InvalidArgumentsError.`, function () { + it(`When parameters is an empty list.`, function () { for (const command of command_list) { // given - /** @type {Array>} */ - const parameters = go( - rangeL(Infinity), - mapL(() => makeRandomNumber(-100, 100)), - chunkL(2), - chunkL(3), - take(makeRandomInt(1, 10)) - ); - const expect_str = `${command} ${join(" ", deepFlatL(parameters))}`; + const parameters = []; // when - const receive_str = $$toStringPathCommandParameters({ - command, - parameters, - }); + const f = () => + $$toStringPathCommandParameters({ command, parameters }); // then - expect(receive_str).equal(expect_str); + expect(f).throw(InvalidArgumentsError); } }); - }); - describe(`Throw InvalidArgumentsError.`, function () { - it(`When parameters is an empty list.`, function () { + it(`When parameters is "Array<[number, number]>{length: 1}".`, function () { for (const command of command_list) { // given - const parameters = []; + /** @type {Array>} */ + const parameters = go( + rangeL(Infinity), + mapL(() => makeRandomNumber(-100, 100)), + chunkL(2), + take(1) + ); // when const f = () => @@ -305,7 +269,7 @@ export default ({ describe, it }) => [ } }); - it(`When parameters is "Array<[number, number]>{length: 1}".`, function () { + it(`When parameters is "Array<[number, number]>{length: 2}".`, function () { for (const command of command_list) { // given /** @type {Array>} */ @@ -313,7 +277,7 @@ export default ({ describe, it }) => [ rangeL(Infinity), mapL(() => makeRandomNumber(-100, 100)), chunkL(2), - take(1) + take(2) ); // when @@ -325,7 +289,7 @@ export default ({ describe, it }) => [ } }); - it(`When parameters is "Array<[number, number]>{length: 2}".`, function () { + it(`When parameters is "Array{length: 3}>".`, function () { for (const command of command_list) { // given /** @type {Array>} */ @@ -333,7 +297,8 @@ export default ({ describe, it }) => [ rangeL(Infinity), mapL(() => makeRandomNumber(-100, 100)), chunkL(2), - take(2) + chunkL(3), + take(makeRandomInt(1, 10)) ); // when @@ -373,8 +338,24 @@ export default ({ describe, it }) => [ expect(receive_str).equal(expect_str); } }); + }); - it(`When parameters is "Array{length: 2}>".`, function () { + describe(`Throw InvalidArgumentsError.`, function () { + it(`When parameters is an empty list.`, function () { + for (const command of command_list) { + // given + const parameters = []; + + // when + const f = () => + $$toStringPathCommandParameters({ command, parameters }); + + // then + expect(f).throw(InvalidArgumentsError); + } + }); + + it(`When parameters is "[[number, number]]".`, function () { for (const command of command_list) { // given /** @type {Array>} */ @@ -382,28 +363,28 @@ export default ({ describe, it }) => [ rangeL(Infinity), mapL(() => makeRandomNumber(-100, 100)), chunkL(2), - chunkL(2), - take(makeRandomInt(1, 10)) + take(1) ); - const expect_str = `${command} ${join(" ", deepFlatL(parameters))}`; // when - const receive_str = $$toStringPathCommandParameters({ - command, - parameters, - }); + const f = () => + $$toStringPathCommandParameters({ command, parameters }); // then - expect(receive_str).equal(expect_str); + expect(f).throw(InvalidArgumentsError); } }); - }); - describe(`Throw InvalidArgumentsError.`, function () { - it(`When parameters is an empty list.`, function () { + it(`When parameters is "Array<[number, number]>{length: >=3}".`, function () { for (const command of command_list) { // given - const parameters = []; + /** @type {Array>} */ + const parameters = go( + rangeL(Infinity), + mapL(() => makeRandomNumber(-100, 100)), + chunkL(2), + take(makeRandomInt(3, 10)) + ); // when const f = () => @@ -414,7 +395,7 @@ export default ({ describe, it }) => [ } }); - it(`When parameters is "[[number, number]]".`, function () { + it(`When parameters is "Array{length: 2}>".`, function () { for (const command of command_list) { // given /** @type {Array>} */ @@ -422,7 +403,8 @@ export default ({ describe, it }) => [ rangeL(Infinity), mapL(() => makeRandomNumber(-100, 100)), chunkL(2), - take(1) + chunkL(2), + take(makeRandomInt(1, 10)) ); // when @@ -472,10 +454,27 @@ export default ({ describe, it }) => [ expect(receive_str).equal(expect_str); } }); + }); + + describe(`Throw InvalidArgumentsError.`, function () { + it(`When parameters is an empty list.`, function () { + for (const command of command_list) { + // given + const parameters = []; + + // when + const f = () => + $$toStringPathCommandParameters({ command, parameters }); + + // then + expect(f).throw(InvalidArgumentsError); + } + }); it(`When parameters is "Array<[number, number, number, 0|1, 0|1, number, number]>"`, function () { for (const command of command_list) { // given + /** @type {Array>} */ const parameters = map(() => { const [rx, ry, x_axis_rotation, x, y] = mapL( () => makeRandomNumber(-100, 100), @@ -495,25 +494,6 @@ export default ({ describe, it }) => [ y, ]; }, rangeL(makeRandomInt(1, 10))); - const expect_str = `${command} ${join(" ", deepFlatL(parameters))}`; - - // when - const receive_str = $$toStringPathCommandParameters({ - command, - parameters, - }); - - // then - expect(receive_str).equal(expect_str); - } - }); - }); - - describe(`Throw InvalidArgumentsError.`, function () { - it(`When parameters is an empty list.`, function () { - for (const command of command_list) { - // given - const parameters = []; // when const f = () => @@ -534,8 +514,8 @@ export default ({ describe, it }) => [ const path_data_list = [ { command: "M", parameters: [1, 2] }, { command: "L", parameters: [3, 4] }, - { command: "H", parameters: [5] }, - { command: "V", parameters: [6] }, + { command: "H", parameters: 5 }, + { command: "V", parameters: 6 }, { command: "C", parameters: [ @@ -563,8 +543,8 @@ export default ({ describe, it }) => [ { command: "Z", parameters: [] }, { command: "m", parameters: [28, 29] }, { command: "l", parameters: [30, 31] }, - { command: "h", parameters: [32] }, - { command: "v", parameters: [33] }, + { command: "h", parameters: 32 }, + { command: "v", parameters: 33 }, { command: "c", parameters: [ From 7185c60d72c9c3296dc7789e8cae328d01f38555 Mon Sep 17 00:00:00 2001 From: daengdaenglee Date: Wed, 23 Sep 2020 20:29:49 +0900 Subject: [PATCH 3/6] =?UTF-8?q?$$parsePathData=20=ED=95=A8=EC=88=98=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- doc/API.md | 5 - doc/API_KR.md | 8 - src/fxsvg.js | 2 - src/index.js | 1 - src/parsePathData/README.md | 15 +- src/parsePathData/const.js | 77 +--- src/parsePathData/parsePathData.index.js | 473 ++++++++++++++++------- src/parsePathData/parsePathData.spec.js | 68 ---- 8 files changed, 345 insertions(+), 304 deletions(-) diff --git a/doc/API.md b/doc/API.md index 5890ae5..119c8ab 100644 --- a/doc/API.md +++ b/doc/API.md @@ -239,11 +239,6 @@ - [source](../src/mergeTranslateTransform/mergeTranslateTransform.index.js) - [detail](../src/mergeTranslateTransform/README.md) -## \$\$isValidPathData - -- [source](../src/parsePathData/parsePathData.index.js) -- [detail](../src/parsePathData/parsePathData.spec.js) - ## \$\$splitPathDataByCommandL - [source](../src/parsePathData/parsePathData.index.js) diff --git a/doc/API_KR.md b/doc/API_KR.md index 16aca5c..bea1e60 100644 --- a/doc/API_KR.md +++ b/doc/API_KR.md @@ -357,14 +357,6 @@ svg 엘리먼트에 가장 마지막으로 적용된 `SVGTransform` 이 `SVGTran 해당 `SVGTransform` 을 svg 엘리먼트의 `x`, `y` 속성 (혹은 그에 준하는 속성) 에 반영합니다. svg 엘리먼트에 다른 `SVGTransform` 이 있는 경우 각 `SVGTransform` 을 업데이트합니다. -## \$\$isValidPathData - -- [source](../src/parsePathData/parsePathData.index.js) -- [detail](../src/parsePathData/parsePathData.spec.js) - -유효한 path data 문자열인지 여부를 판단합니다. -SVG 스펙과는 다르게 FxSVG 에서는 좌표 사이에 "COMMA_OR_WHITESPACE" 구분자가 필수입니다. - ## \$\$splitPathDataByCommandL - [source](../src/parsePathData/parsePathData.index.js) diff --git a/src/fxsvg.js b/src/fxsvg.js index a2475bb..5ac0d8a 100644 --- a/src/fxsvg.js +++ b/src/fxsvg.js @@ -65,7 +65,6 @@ import { $$mergeScaleTransform } from "./mergeScaleTransform/mergeScaleTransform import { $$mergeScaleTransform2 } from "./mergeScaleTransform2/mergeScaleTransform2.index.js"; import { $$mergeTranslateTransform } from "./mergeTranslateTransform/mergeTranslateTransform.index.js"; import { - $$isValidPathData, $$splitPathDataByCommandL, $$parsePathCommandParameters, $$convertPathCommandParametersRelativeToAbsoluteL, @@ -144,7 +143,6 @@ export const FxSVG = { mergeScaleTransform: $$mergeScaleTransform, mergeScaleTransform2: $$mergeScaleTransform2, mergeTranslateTransform: $$mergeTranslateTransform, - isValidPathData: $$isValidPathData, splitPathDataByCommandL: $$splitPathDataByCommandL, parsePathCommandParameters: $$parsePathCommandParameters, convertPathCommandParametersRelativeToAbsoluteL: $$convertPathCommandParametersRelativeToAbsoluteL, diff --git a/src/index.js b/src/index.js index f994bc4..aedef08 100644 --- a/src/index.js +++ b/src/index.js @@ -65,7 +65,6 @@ export { $$mergeScaleTransform } from "./mergeScaleTransform/mergeScaleTransform export { $$mergeScaleTransform2 } from "./mergeScaleTransform2/mergeScaleTransform2.index.js"; export { $$mergeTranslateTransform } from "./mergeTranslateTransform/mergeTranslateTransform.index.js"; export { - $$isValidPathData, $$splitPathDataByCommandL, $$parsePathCommandParameters, $$convertPathCommandParametersRelativeToAbsoluteL, diff --git a/src/parsePathData/README.md b/src/parsePathData/README.md index 0ea8425..ea46cce 100644 --- a/src/parsePathData/README.md +++ b/src/parsePathData/README.md @@ -1,11 +1,3 @@ -# \$\$isValidPathData - -- [source](./parsePathData.index.js) -- [test](./parsePathData.spec.js) - -유효한 path data 문자열인지 여부를 판단합니다. -SVG 스펙과는 다르게 FxSVG 에서는 좌표 사이에 "COMMA_OR_WHITESPACE" 구분자가 필수입니다. - # \$\$splitPathDataByCommandL - [source](./parsePathData.index.js) @@ -15,8 +7,7 @@ path data 문자열을 command 단위로 잘라냅니다. 제너레이터는 command 와 해당 command 의 parameters 를 yield 합니다. command 와 parameters 는 문자열입니다. -이 함수는 path data 의 유효성을 검사하지 않습니다! -먼저 `$$isValidPathData` 함수로 검사해주세요! +이 함수는 입력 데이터의 유효성을 검사하지 않습니다! # \$\$parsePathCommandParameters @@ -26,8 +17,6 @@ command 와 parameters 는 문자열입니다. parameters 문자열을 숫자 배열로 변환합니다. 제너레이터는 command 와 해당 command 의 변환된 parameters 를 yield 합니다. -이 함수는 입력 데이터의 유효성을 검사하지 않습니다! - # \$\$convertPathCommandParametersRelativeToAbsoluteL - [source](./parsePathData.index.js) @@ -66,4 +55,4 @@ path 의 command-parameter 를 각 parameter 별로 펼칩니다. - [test](./parsePathData.spec.js) `` 엘리먼트의 `d` 속성의 값을 파싱하여 `{command, parameters}` 객체의 이터레이터를 반환합니다. -위의 converting, compressing, flattening 작업을 모두 진행합니다. +위의 splitting, parsing, converting, compressing, flattening 작업을 모두 진행합니다. diff --git a/src/parsePathData/const.js b/src/parsePathData/const.js index 3bbbb0a..fef2d66 100644 --- a/src/parsePathData/const.js +++ b/src/parsePathData/const.js @@ -1,6 +1,8 @@ -import { constant, go, join, mapL, rangeL } from "fxjs2"; +import { go, join, mapL } from "fxjs2"; export const FN_PATH = `$$parsePathData`; +export const FN_PATH_PARSE_COORDINATE_SEQ = `$$parseCoordinateSeqL`; +export const FN_PATH_PARSE_PATH_COMMAND_PARAMETERS = `$$parsePathCommandParameters`; export const REGEXP_STR_COMMAND = `[MmZzLlHhVvCcSsQqTtAa]`; export const REGEXP_STR_WSP = `\\s`; @@ -40,81 +42,10 @@ export const REGEXP_STR_FLOATING_POINT_CONST = go( join("|"), (s) => `(?:${s})` ); -export const REGEXP_STR_INTEGER_CONST = REGEXP_STR_DIGIT_SEQ; /** @type {string} */ export const REGEXP_STR_NON_NEGATIVE_NUMBER = go( - [REGEXP_STR_FLOATING_POINT_CONST, REGEXP_STR_INTEGER_CONST], + [REGEXP_STR_FLOATING_POINT_CONST, REGEXP_STR_DIGIT_SEQ], join("|"), (s) => `(?:${s})` ); export const REGEXP_STR_NUMBER = `(?:${REGEXP_STR_SIGN}?${REGEXP_STR_NON_NEGATIVE_NUMBER})`; -export const REGEXP_STR_COORDINATE = REGEXP_STR_NUMBER; -export const REGEXP_STR_COORDINATE_SEQ = `(?:${REGEXP_STR_COORDINATE}(?:${REGEXP_STR_COMMA_WSP}${REGEXP_STR_COORDINATE})*)`; -/** @type {string} */ -export const REGEXP_STR_COORDINATE_PAIR = go( - rangeL(2), - mapL(constant(REGEXP_STR_COORDINATE)), - join(`${REGEXP_STR_COMMA_WSP}`), - (s) => `(?:${s})` -); -export const REGEXP_STR_COORDINATE_PAIR_SEQ = `(?:${REGEXP_STR_COORDINATE_PAIR}(?:${REGEXP_STR_COMMA_WSP}${REGEXP_STR_COORDINATE_PAIR})*)`; -/** @type {string} */ -export const REGEXP_STR_COORDINATE_PAIR_DOUBLE = go( - rangeL(2), - mapL(constant(REGEXP_STR_COORDINATE_PAIR)), - join(`${REGEXP_STR_COMMA_WSP}`), - (s) => `(?:${s})` -); -export const REGEXP_STR_COORDINATE_PAIR_DOUBLE_SEQ = `(?:${REGEXP_STR_COORDINATE_PAIR_DOUBLE}(?:${REGEXP_STR_COMMA_WSP}${REGEXP_STR_COORDINATE_PAIR_DOUBLE})*)`; -/** @type {string} */ -export const REGEXP_STR_COORDINATE_PAIR_TRIPLET = go( - rangeL(3), - mapL(constant(REGEXP_STR_COORDINATE_PAIR)), - join(`${REGEXP_STR_COMMA_WSP}`), - (s) => `(?:${s})` -); -export const REGEXP_STR_COORDINATE_PAIR_TRIPLET_SEQ = `(?:${REGEXP_STR_COORDINATE_PAIR_TRIPLET}(?:${REGEXP_STR_COMMA_WSP}${REGEXP_STR_COORDINATE_PAIR_TRIPLET})*)`; -/** @type {string} */ -export const REGEXP_STR_ELLIPTICAL_ARC_ARG = go( - rangeL(3), - mapL(constant(REGEXP_STR_NUMBER)), - join(`${REGEXP_STR_COMMA_WSP}`), - (s) => - go( - rangeL(2), - mapL(constant(REGEXP_STR_FLAG)), - join(`${REGEXP_STR_COMMA_WSP}`), - (s2) => `${s}${REGEXP_STR_COMMA_WSP}${s2}` - ), - (s) => `${s}${REGEXP_STR_COMMA_WSP}${REGEXP_STR_COORDINATE_PAIR}`, - (s) => `(?:${s})` -); -export const REGEXP_STR_ELLIPTICAL_ARC_ARG_SEQ = `(?:${REGEXP_STR_ELLIPTICAL_ARC_ARG}(?:${REGEXP_STR_COMMA_WSP}${REGEXP_STR_ELLIPTICAL_ARC_ARG})*)`; -export const REGEXP_STR_MOVE_TO_COMMAND = `(?:(?:M|m)${REGEXP_STR_WSP}*${REGEXP_STR_COORDINATE_PAIR_SEQ})`; -export const REGEXP_STR_CLOSE_PATH_COMMAND = `(?:Z|z)`; -export const REGEXP_STR_LINE_TO_COMMAND = `(?:(?:L|l)${REGEXP_STR_WSP}*${REGEXP_STR_COORDINATE_PAIR_SEQ})`; -export const REGEXP_STR_H_LINE_TO_COMMAND = `(?:(?:H|h)${REGEXP_STR_WSP}*${REGEXP_STR_COORDINATE_SEQ})`; -export const REGEXP_STR_V_LINE_TO_COMMAND = `(?:(?:V|v)${REGEXP_STR_WSP}*${REGEXP_STR_COORDINATE_SEQ})`; -export const REGEXP_STR_CURVE_TO_COMMAND = `(?:(?:C|c)${REGEXP_STR_WSP}*${REGEXP_STR_COORDINATE_PAIR_TRIPLET_SEQ})`; -export const REGEXP_STR_SMOOTH_CURVE_TO_COMMAND = `(?:(?:S|s)${REGEXP_STR_WSP}*${REGEXP_STR_COORDINATE_PAIR_DOUBLE_SEQ})`; -export const REGEXP_STR_QUADRATIC_CURVE_TO_COMMAND = `(?:(?:Q|q)${REGEXP_STR_WSP}*${REGEXP_STR_COORDINATE_PAIR_DOUBLE_SEQ})`; -export const REGEXP_STR_SMOOTH_QUADRATIC_CURVE_TO_COMMAND = `(?:(?:T|t)${REGEXP_STR_WSP}*${REGEXP_STR_COORDINATE_PAIR_SEQ})`; -export const REGEXP_STR_ELLIPTICAL_ARC_COMMAND = `(?:(?:A|a)${REGEXP_STR_WSP}*${REGEXP_STR_ELLIPTICAL_ARC_ARG_SEQ})`; -/** @type {string} */ -export const REGEXP_STR_DRAW_TO_COMMAND = go( - [ - REGEXP_STR_MOVE_TO_COMMAND, - REGEXP_STR_CLOSE_PATH_COMMAND, - REGEXP_STR_LINE_TO_COMMAND, - REGEXP_STR_H_LINE_TO_COMMAND, - REGEXP_STR_V_LINE_TO_COMMAND, - REGEXP_STR_CURVE_TO_COMMAND, - REGEXP_STR_SMOOTH_CURVE_TO_COMMAND, - REGEXP_STR_QUADRATIC_CURVE_TO_COMMAND, - REGEXP_STR_SMOOTH_QUADRATIC_CURVE_TO_COMMAND, - REGEXP_STR_ELLIPTICAL_ARC_COMMAND, - ], - join("|"), - (s) => `(?:${s})` -); -export const REGEXP_STR_SVG_PATH = `^${REGEXP_STR_WSP}*(?:${REGEXP_STR_MOVE_TO_COMMAND}(?:${REGEXP_STR_WSP}+${REGEXP_STR_DRAW_TO_COMMAND})*)*${REGEXP_STR_WSP}*$`; diff --git a/src/parsePathData/parsePathData.index.js b/src/parsePathData/parsePathData.index.js index 6bf26b4..c84760a 100644 --- a/src/parsePathData/parsePathData.index.js +++ b/src/parsePathData/parsePathData.index.js @@ -1,87 +1,72 @@ import { + chunkL, dropL, + each, + eachL, equals2, flatMapL, go, head, isNil, - isString, last, map, mapL, not, reduce, - take, + takeAll, + tap, toIter, } from "fxjs2"; import { InvalidArgumentsError } from "../Errors/InvalidArgumentsError.js"; import { FN_PATH, + FN_PATH_PARSE_COORDINATE_SEQ, + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + REGEXP_STR_COMMA_WSP, REGEXP_STR_COMMAND, - REGEXP_STR_COORDINATE, - REGEXP_STR_COORDINATE_PAIR, - REGEXP_STR_COORDINATE_PAIR_DOUBLE, - REGEXP_STR_COORDINATE_PAIR_TRIPLET, - REGEXP_STR_ELLIPTICAL_ARC_ARG, REGEXP_STR_FLAG, REGEXP_STR_NUMBER, - REGEXP_STR_SVG_PATH, } from "./const.js"; /** - * Check the input path data string is valid or not. - * Unlike SVG spec, it requires "COMMA_OR_WHITESPACE" between coordinates. - * It is not optional within FxSVG. - * - * @param {*} path_data - string value of "d" attribute. - * @returns {boolean} + * @typedef {string} Command + * @description one of "M", "m", "L", "l", "H", "h", "V", "v", "C", "c", "S", "s", "Q", "q", "T", "t", "A", "a", "Z", "z". */ -export const $$isValidPathData = (path_data) => - isString(path_data) && new RegExp(REGEXP_STR_SVG_PATH).test(path_data); /** * Split path data string by each command. * Generator yields command and parameters of the command. * Both command and parameters are string. * - * This function will not validate path data! - * Please check path data using "$$isValidPathData" first! + * This function will not validate input data! * * @param {string} path_data - * @returns {Generator<{command: string, parameters: string}, undefined, *>} + * @returns {Generator<{command: Command, parameters: string}, undefined, *>} */ export function* $$splitPathDataByCommandL(path_data) { - const command_index_iter = (function* (d_str) { - const regexp_command = new RegExp(REGEXP_STR_COMMAND, "g"); - let result; - while (not(isNil((result = regexp_command.exec(d_str))))) { - yield result.index; - } - })(path_data); - - let index1; - let done1; - let { value: index2, done: done2 } = command_index_iter.next(); - while (true) { - index1 = index2; - done1 = done2; - ({ value: index2, done: done2 } = command_index_iter.next()); + const regexp = new RegExp(REGEXP_STR_COMMAND, "g"); - if (done1) { - return undefined; - } + let prev_result; + let result; - if (done2) { - const command = path_data[index1]; - const parameters = path_data.slice(index1 + 1).trim(); - yield { command, parameters }; - return undefined; - } + prev_result = regexp.exec(path_data); + if (!prev_result) { + return undefined; + } - const command = path_data[index1]; - const parameters = path_data.slice(index1 + 1, index2).trim(); + while (not(isNil((result = regexp.exec(path_data))))) { + const command = path_data[prev_result.index]; + const parameters = path_data + .slice(prev_result.index + 1, result.index) + .trim(); yield { command, parameters }; + + prev_result = result; } + + const command = path_data[prev_result.index]; + const parameters = path_data.slice(prev_result.index + 1).trim(); + yield { command, parameters }; } /** @@ -110,30 +95,80 @@ export function* $$splitPathDataByCommandL(path_data) { */ /** - * This function will not validate input data! - * Please check yourself first! - * - * @param {string} coordinate_pair - * @returns {CoordinatePair} + * @param {string} str + * @returns {Iterator} + * @throws {InvalidArgumentsError} */ -const parseCoordinatePair = (coordinate_pair) => +const $$parseCoordinateSeqL = (str) => go( - coordinate_pair.matchAll(new RegExp(REGEXP_STR_COORDINATE, "g")), - mapL(head), - mapL(Number), - take(2) + str.trim(), + function* (str) { + if (!str) { + return; + } + + const regexp_number = new RegExp(REGEXP_STR_NUMBER, "g"); + const regexp_comma_wsp = new RegExp(REGEXP_STR_COMMA_WSP, "g"); + + let index = 0; + + let result; + + result = regexp_number.exec(str); + if (!result || not(equals2(index, result.index))) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_COORDINATE_SEQ, + `"str"`, + `input str : "${str}"` + ); + } + yield result[0]; + index = result.index + result[0].length; + + while (true) { + regexp_comma_wsp.lastIndex = index; + const comma_wsp_result = regexp_comma_wsp.exec(str); + if (comma_wsp_result) { + index = comma_wsp_result.index + comma_wsp_result[0].length; + } + + regexp_number.lastIndex = index; + const current_number_result = regexp_number.exec(str); + if (!current_number_result) { + break; + } + result = current_number_result; + if (not(equals2(index, result.index))) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_COORDINATE_SEQ, + `"str"`, + `input str : "${str}"` + ); + } + yield result[0]; + index = result.index + result[0].length; + } + + if (not(equals2(result.index + result[0].length, str.length))) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_COORDINATE_SEQ, + `"str"`, + `input str : "${str}"` + ); + } + }, + mapL(Number) ); + /** * Parse parameters string to array of numbers. * Generator yields command and parsed parameters of the command. * - * This function will not validate input data! - * Please check yourself first! - * * @param {Object} path_command_parameters - * @param {string} path_command_parameters.command + * @param {Command} path_command_parameters.command * @param {string} path_command_parameters.parameters - * @returns {{command: string, parameters: Array}} + * @returns {{command: Command, parameters: Array}} + * @throws {InvalidArgumentsError} */ export const $$parsePathCommandParameters = ({ command, parameters }) => { if ( @@ -143,9 +178,27 @@ export const $$parsePathCommandParameters = ({ command, parameters }) => { ) { /** @type {Array} */ const parsed_parameters = go( - parameters.matchAll(new RegExp(REGEXP_STR_COORDINATE_PAIR, "g")), - mapL(head), - map(parseCoordinatePair) + parameters, + $$parseCoordinateSeqL, + chunkL(2), + each((pair) => { + if (pair.length < 2) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + tap((l) => { + if (!l.length) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }) ); return { command, parameters: parsed_parameters }; } @@ -156,9 +209,18 @@ export const $$parsePathCommandParameters = ({ command, parameters }) => { ) { /** @type {Array} */ const parsed_parameters = go( - parameters.matchAll(new RegExp(REGEXP_STR_COORDINATE, "g")), - mapL(head), - map(Number) + parameters, + $$parseCoordinateSeqL, + takeAll, + tap((l) => { + if (!l.length) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }) ); return { command, parameters: parsed_parameters }; } @@ -166,16 +228,37 @@ export const $$parsePathCommandParameters = ({ command, parameters }) => { if (equals2(command.toLowerCase(), "c")) { /** @type {Array} */ const parsed_parameters = go( - parameters.matchAll(new RegExp(REGEXP_STR_COORDINATE_PAIR_TRIPLET, "g")), - mapL(head), - map((triplet) => - go( - triplet.matchAll(new RegExp(REGEXP_STR_COORDINATE_PAIR, "g")), - mapL(head), - mapL(parseCoordinatePair), - take(3) - ) - ) + parameters, + $$parseCoordinateSeqL, + chunkL(2), + eachL((pair) => { + if (pair.length < 2) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + chunkL(3), + each((triplet) => { + if (triplet.length < 3) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + tap((l) => { + if (!l.length) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }) ); return { command, parameters: parsed_parameters }; } @@ -186,16 +269,37 @@ export const $$parsePathCommandParameters = ({ command, parameters }) => { ) { /** @type {Array} */ const parsed_parameters = go( - parameters.matchAll(new RegExp(REGEXP_STR_COORDINATE_PAIR_DOUBLE, "g")), - mapL(head), - map((double) => - go( - double.matchAll(new RegExp(REGEXP_STR_COORDINATE_PAIR, "g")), - mapL(head), - mapL(parseCoordinatePair), - take(2) - ) - ) + parameters, + $$parseCoordinateSeqL, + chunkL(2), + eachL((pair) => { + if (pair.length < 2) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + chunkL(2), + each((pair) => { + if (pair.length < 2) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + tap((l) => { + if (!l.length) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }) ); return { command, parameters: parsed_parameters }; } @@ -203,57 +307,143 @@ export const $$parsePathCommandParameters = ({ command, parameters }) => { if (equals2(command.toLowerCase(), "a")) { /** @type {Array} */ const parsed_parameters = go( - parameters.matchAll(new RegExp(REGEXP_STR_ELLIPTICAL_ARC_ARG, "g")), - mapL(head), - map((arc_arg) => { - const [ - [rx_str], - [ry_str], - { 0: x_axis_rotation_str, index: _index1 }, - ] = arc_arg.matchAll(new RegExp(REGEXP_STR_NUMBER, "g")); - const index1 = _index1 + x_axis_rotation_str.length; - const [rx, ry, x_axis_rotation] = mapL(Number, [ - rx_str, - ry_str, - x_axis_rotation_str, - ]); - - const [ - [large_arc_flag_str], - { 0: sweep_flag_str, index: _index2 }, - ] = arc_arg.slice(index1).matchAll(new RegExp(REGEXP_STR_FLAG, "g")); - const index2 = index1 + _index2 + sweep_flag_str.length; - const [large_arc_flag, sweep_flag] = mapL(Number, [ - large_arc_flag_str, - sweep_flag_str, - ]); - - const [x, y] = parseCoordinatePair(arc_arg.slice(index2)); - - return [rx, ry, x_axis_rotation, large_arc_flag, sweep_flag, x, y]; + parameters.trim(), + function* (str) { + const regexp_number = new RegExp(REGEXP_STR_NUMBER, "g"); + const regexp_flag = new RegExp(REGEXP_STR_FLAG, "g"); + const regexp_comma_wsp = new RegExp(REGEXP_STR_COMMA_WSP, "g"); + + if (!str) { + return; + } + + let result; + let index = 0; + + const processOptional = (regexp) => { + regexp.lastIndex = index; + const result = regexp.exec(str); + if (!result) { + return result; + } + + if (not(equals2(result.index, index))) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + + index = result.index + result[0].length; + return result; + }; + const processRequired = (regexp) => { + regexp.lastIndex = index; + const result = regexp.exec(str); + if (!result || not(equals2(result.index, index))) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + index = result.index + result[0].length; + return result; + }; + + yield processRequired(regexp_number)[0]; + processOptional(regexp_comma_wsp); + yield processRequired(regexp_number)[0]; + processOptional(regexp_comma_wsp); + yield processRequired(regexp_number)[0]; + processRequired(regexp_comma_wsp); + yield processRequired(regexp_flag)[0]; + processOptional(regexp_comma_wsp); + yield processRequired(regexp_flag)[0]; + processOptional(regexp_comma_wsp); + yield processRequired(regexp_number)[0]; + processOptional(regexp_comma_wsp); + result = processRequired(regexp_number); + yield result[0]; + + while (true) { + processOptional(regexp_comma_wsp); + + const temp_result = processOptional(regexp_number); + if (!temp_result) { + break; + } + yield temp_result[0]; + processOptional(regexp_comma_wsp); + yield processRequired(regexp_number)[0]; + processOptional(regexp_comma_wsp); + yield processRequired(regexp_number)[0]; + processRequired(regexp_comma_wsp); + yield processRequired(regexp_flag)[0]; + processOptional(regexp_comma_wsp); + yield processRequired(regexp_flag)[0]; + processOptional(regexp_comma_wsp); + yield processRequired(regexp_number)[0]; + processOptional(regexp_comma_wsp); + result = processRequired(regexp_number); + yield result[0]; + } + + if (not(equals2(result.index + result[0].length, str.length))) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }, + mapL(Number), + chunkL(7), + each((l) => { + if (l.length < 7) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } + }), + tap((l) => { + if (!l.length) { + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"parameters"`, + JSON.stringify({ command, parameters }) + ); + } }) ); return { command, parameters: parsed_parameters }; } - // command === "z" or "Z" - return { command, parameters: [] }; + if (equals2(command.toLowerCase(), "z")) { + return { command, parameters: [] }; + } + + throw new InvalidArgumentsError( + FN_PATH_PARSE_PATH_COMMAND_PARAMETERS, + `"command"`, + JSON.stringify({ command, parameters }) + ); }; /** * Convert path command-parameters iterable from relative one to absolute one. * * This function will not validate input data! - * Please check yourself first! * - * @param {Iterator<{command: string, parameters: Array}, undefined, *>} path_command_parameters_iter - * @returns {Generator<{command: string, parameters: Array}, undefined, *>} + * @param {Iterator<{command: Command, parameters: Array}, undefined, *>} path_command_parameters_iter + * @returns {Generator<{command: Command, parameters: Array}, undefined, *>} */ export function* $$convertPathCommandParametersRelativeToAbsoluteL( path_command_parameters_iter ) { - path_command_parameters_iter = toIter(path_command_parameters_iter); - const { value: first_path_command_parameters, done, @@ -273,6 +463,7 @@ export function* $$convertPathCommandParametersRelativeToAbsoluteL( [cpx, cpy] = last(first_path_command_parameters.parameters); yield first_path_command_parameters; } else { + // first_path_command_parameters.command === "m" const parameters_iter = toIter(first_path_command_parameters.parameters); [ipx, ipy] = parameters_iter.next().value; const { parameters, cpx: updated_cpx, cpy: updated_cpy } = reduce( @@ -503,14 +694,11 @@ export function* $$convertPathCommandParametersRelativeToAbsoluteL( * "T" -> "Q" * * This function will not validate input data! - * Please check yourself first! * - * @param {Iterator<{command: string, parameters: Array}, undefined, *>} path_command_parameters_iter - * @returns {Generator<{command: string, parameters: Array}, undefined, *>} + * @param {Iterator<{command: Command, parameters: Array}, undefined, *>} path_command_parameters_iter + * @returns {Generator<{command: Command, parameters: Array}, undefined, *>} */ export function* $$compressPathCommandL(path_command_parameters_iter) { - path_command_parameters_iter = toIter(path_command_parameters_iter); - let { value: path_command_parameters1, done, @@ -739,12 +927,11 @@ export function* $$compressPathCommandL(path_command_parameters_iter) { * Flatten "command and parameter sequence" to "sequence of command and parameter". * * This function will not validate input data! - * Please check yourself first! * * @param {Object} path_command_parameters - * @param {string} path_command_parameters.command + * @param {Command} path_command_parameters.command * @param {Array} path_command_parameters.parameters - * @returns {Generator<{command: string, parameters: Parameter}, undefined, *>} + * @returns {Generator<{command: Command, parameters: Parameter}, undefined, *>} */ export function* $$flatPathCommandParametersL({ command, parameters }) { if (equals2(command.toUpperCase(), "M")) { @@ -783,15 +970,33 @@ export function* $$flatPathCommandParametersL({ command, parameters }) { */ export const $$parsePathDateL = (d_str) => { if (isNil(d_str)) { - return /** @type {Iterator<{command: string, parameters: Parameter}, undefined, *>} */ []; - } - - if (!$$isValidPathData(d_str)) { - throw new InvalidArgumentsError(FN_PATH, 1); + return /** @type {Iterator<{command: string, parameters: Parameter}, undefined, *>} */ toIter( + [] + ); } return go( $$splitPathDataByCommandL(d_str), + function* (iter) { + const { value, done } = iter.next(); + if (done) { + return; + } + + if ( + not(equals2(value.command, "M")) && + not(equals2(value.command, "m")) + ) { + throw new InvalidArgumentsError( + FN_PATH, + `"d_str"`, + `The first command is not one of "M" and "m".` + ); + } + + yield value; + yield* iter; + }, mapL($$parsePathCommandParameters), $$convertPathCommandParametersRelativeToAbsoluteL, $$compressPathCommandL, diff --git a/src/parsePathData/parsePathData.spec.js b/src/parsePathData/parsePathData.spec.js index d5f88db..cdad0b1 100644 --- a/src/parsePathData/parsePathData.spec.js +++ b/src/parsePathData/parsePathData.spec.js @@ -15,7 +15,6 @@ import { import { makeRandomBool } from "../../test/utils/makeRandomBool.js"; import { makeRandomInt } from "../../test/utils/makeRandomInt.js"; import { - $$isValidPathData, $$splitPathDataByCommandL, $$parsePathCommandParameters, $$convertPathCommandParametersRelativeToAbsoluteL, @@ -25,73 +24,6 @@ import { } from "./parsePathData.index.js"; export default ({ describe, it }) => [ - describe(`$$isValidPathData`, function () { - it(`The function returns true if the input value is a valid path data string.`, function () { - // given - const d_str = ` - M 1 2, 3 4 - L 1 2, 3 4, 5 6 - H 1, 2, 3 - V 4, 5, 6 - C 1 2 3 4 5 6, 7 8 9 1 2 3 - S 1 2 3 4, 5 6 7 8 - Q 1 2 3 4, 5 6 7 8 - T 1 2, 3 4 - A 1 2 3 0 1 4 5, 6 7 8 1 0 9 8 - Z - m 1 2, 3 4 - l 1 2, 3 4, 5 6 - h 1, 2, 3 - v 4, 5, 6 - c 1 2 3 4 5 6, 7 8 9 1 2 3 - s 1 2 3 4, 5 6 7 8 - q 1 2 3 4, 5 6 7 8 - t 1 2, 3 4 - a 1 2 3 0 1 4 5, 6 7 8 1 0 9 8 - z - `; - - // when - const is_valid = $$isValidPathData(d_str); - - // then - expect(is_valid).true; - }); - - it(`The function returns false if the input value has invalid command.`, function () { - // given - const d_str = ` - M 1 2, 3 4 - L 1 2, 3 4, 5 6 - H 1, 2, 3 - V 4, 5, 6 - C 1 2 3 4 5 6, 7 8 9 1 2 3 - S 1 2 3 4, 5 6 7 8 - Q 1 2 3 4, 5 6 7 8 - T 1 2, 3 4 - A 1 2 3 0 1 4 5, 6 7 8 1 0 9 8 - Z - m 1 2, 3 4 - l 1 2, 3 4, 5 6 - h 1, 2, 3 - v 4, 5, 6 - c 1 2 3 4 5 6, 7 8 9 1 2 3 - s 1 2 3 4, 5 6 7 8 - q 1 2 3 4, 5 6 7 8 - t 1 2, 3 4 - a 1 2 3 0 1 4 5, 6 7 8 1 0 9 8 - z - K 1 2 - `; - // last "K" is invalid command. - - // when - const is_valid = $$isValidPathData(d_str); - - // then - expect(is_valid).false; - }); - }), describe(`$$splitPathDataByCommandL`, function () { it(`The function yield each command and parameters of the command`, function () { // given From 9425ea3e55bf63b42054b4b437cf75b99a5ef5b8 Mon Sep 17 00:00:00 2001 From: daengdaenglee Date: Wed, 23 Sep 2020 20:48:30 +0900 Subject: [PATCH 4/6] FxDOM.before --- src/fxsvg.js | 2 ++ src/index.js | 1 + 2 files changed, 3 insertions(+) diff --git a/src/fxsvg.js b/src/fxsvg.js index 5ac0d8a..7e94743 100644 --- a/src/fxsvg.js +++ b/src/fxsvg.js @@ -1,6 +1,7 @@ import { $append, $appendTo, + $before, $closest, $contains, $hide, @@ -82,6 +83,7 @@ import { $$updateTranslateTransform } from "./updateTranslateTransform/updateTra export const FxSVG = { append: $append, appendTo: $appendTo, + before: $before, closest: $closest, contains: $contains, hide: $hide, diff --git a/src/index.js b/src/index.js index aedef08..e567791 100644 --- a/src/index.js +++ b/src/index.js @@ -1,6 +1,7 @@ export { $append as $$append, $appendTo as $$appendTo, + $before as $$before, $closest as $$closest, $contains as $$contains, $hide as $$hide, From c8ca39fef50c19f8a3a5cf1583ca50581fa1795e Mon Sep 17 00:00:00 2001 From: daengdaenglee Date: Wed, 23 Sep 2020 20:49:41 +0900 Subject: [PATCH 5/6] FxDOM.insertBefore --- src/fxsvg.js | 2 ++ src/index.js | 1 + 2 files changed, 3 insertions(+) diff --git a/src/fxsvg.js b/src/fxsvg.js index 7e94743..d754233 100644 --- a/src/fxsvg.js +++ b/src/fxsvg.js @@ -5,6 +5,7 @@ import { $closest, $contains, $hide, + $insertBefore, $off, $on, $qs, @@ -87,6 +88,7 @@ export const FxSVG = { closest: $closest, contains: $contains, hide: $hide, + insertBefore: $insertBefore, off: $off, on: $on, qs: $qs, diff --git a/src/index.js b/src/index.js index e567791..e4cc831 100644 --- a/src/index.js +++ b/src/index.js @@ -5,6 +5,7 @@ export { $closest as $$closest, $contains as $$contains, $hide as $$hide, + $insertBefore as $$insertBefore, $off as $$off, $on as $$on, $qs as $$qs, From 4906cce21bd46f1c9eb225cf4e139d28e71f996b Mon Sep 17 00:00:00 2001 From: daengdaenglee Date: Wed, 23 Sep 2020 20:52:25 +0900 Subject: [PATCH 6/6] Bump Version 0.4.12 -> 0.4.13 --- package-lock.json | 2 +- package.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 8cc3747..98388d9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "fxsvg", - "version": "0.4.12", + "version": "0.4.13", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index c866d37..8343d5b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fxsvg", - "version": "0.4.12", + "version": "0.4.13", "description": "Functional SVG Handling Library", "type": "module", "main": "./src/index.js",