├── index.js
├── .gitignore
├── gulpfile.js
├── .github
├── workflows
│ ├── node.js.yml
│ └── npm-publish.yml
└── ISSUE_TEMPLATE
│ ├── feature_request.yml
│ └── bug_report.yml
├── nodes
└── TextManipulation
│ ├── TextManipulation.svg
│ └── TextManipulation.node.ts
├── tsconfig.json
├── .prettierrc.js
├── package.json
├── eslint.config.mjs
└── README.md
/index.js:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | .DS_Store
3 | .tmp
4 | tmp
5 | dist
6 | npm-debug.log*
7 | yarn.lock
8 | .vscode/launch.json
--------------------------------------------------------------------------------
/gulpfile.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const { task, src, dest } = require('gulp');
3 |
4 | task('build:icons', copyIcons);
5 |
6 | function copyIcons() {
7 | const nodeSource = path.resolve('nodes', '**', '*.{png,svg}');
8 | const nodeDestination = path.resolve('dist', 'nodes');
9 |
10 | return src(nodeSource).pipe(dest(nodeDestination));
11 | }
12 |
--------------------------------------------------------------------------------
/.github/workflows/node.js.yml:
--------------------------------------------------------------------------------
1 | name: Node.js CI
2 |
3 | on:
4 | push:
5 | branches: [ main ]
6 | pull_request:
7 | branches: [ main ]
8 | workflow_dispatch:
9 |
10 | jobs:
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | strategy:
16 | matrix:
17 | node-version: [18.x]
18 |
19 | steps:
20 | - uses: actions/checkout@v2
21 | - name: Use Node.js ${{ matrix.node-version }}
22 | uses: actions/setup-node@v2
23 | with:
24 | node-version: ${{ matrix.node-version }}
25 | - name: Install dependencies
26 | run: npm ci
27 | - name: Lint
28 | run: npm run lint
29 | - name: Build
30 | run: npm run build --if-present
31 |
--------------------------------------------------------------------------------
/nodes/TextManipulation/TextManipulation.svg:
--------------------------------------------------------------------------------
1 |
2 |
10 |
12 |
15 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "strict": true,
4 | "module": "commonjs",
5 | "moduleResolution": "node",
6 | "target": "es2019",
7 | "lib": [
8 | "es2019",
9 | "es2020",
10 | "es2022.error"
11 | ],
12 | "removeComments": true,
13 | "useUnknownInCatchVariables": false,
14 | "forceConsistentCasingInFileNames": true,
15 | "noImplicitAny": true,
16 | "noImplicitReturns": true,
17 | "noUnusedLocals": true,
18 | "strictNullChecks": true,
19 | "preserveConstEnums": true,
20 | "esModuleInterop": true,
21 | "resolveJsonModule": true,
22 | "incremental": true,
23 | "declaration": true,
24 | "sourceMap": true,
25 | "skipLibCheck": true,
26 | "outDir": "./dist/",
27 | },
28 | "include": [
29 | "credentials/**/*",
30 | "nodes/**/*",
31 | "nodes/**/*.json",
32 | "package.json",
33 | ],
34 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.yml:
--------------------------------------------------------------------------------
1 | name: Feature Request
2 | description: Suggest an idea for this project
3 | labels: [enhancement]
4 | assignees:
5 | - lublak
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | Thanks for taking the time to fill out this feature request!
11 | - type: textarea
12 | id: feature-description
13 | attributes:
14 | label: Describe the function you would like to have
15 | description: A clear and concise description of what you want to happen.
16 | placeholder: The description of the function.
17 | validations:
18 | required: true
19 | - type: textarea
20 | id: alternative-solution
21 | attributes:
22 | label: Describe your current alternative solution.
23 | description: Your current solution that you use, if there is one.
24 | placeholder: Your alternative solution.
25 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish.yml:
--------------------------------------------------------------------------------
1 | name: Node.js Package
2 |
3 | on:
4 | release:
5 | types: [created]
6 | workflow_dispatch:
7 |
8 | jobs:
9 | build:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: actions/checkout@v2
13 | - uses: actions/setup-node@v2
14 | with:
15 | node-version: 18
16 | - run: npm ci
17 |
18 | publish-npm:
19 | needs: build
20 | runs-on: ubuntu-latest
21 | steps:
22 | - uses: actions/checkout@v2
23 | - uses: actions/setup-node@v2
24 | with:
25 | node-version: 18
26 | registry-url: https://registry.npmjs.org/
27 | - run: npm ci
28 | - run: npm publish --access public
29 | env:
30 | NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
31 | if: ${{ !github.event.release.prerelease }}
32 | - run: npm publish --access public --tag beta
33 | env:
34 | NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
35 | if: ${{ github.event.release.prerelease }}
36 |
--------------------------------------------------------------------------------
/.prettierrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "plugins": ["prettier-plugin-jsdoc"],
3 |
4 | jsdocDescriptionWithDot: true,
5 | jsdocVerticalAlignment: true,
6 | jsdocPreferCodeFences: true,
7 |
8 | /**
9 | * https://prettier.io/docs/en/options.html#semicolons
10 | */
11 | semi: true,
12 |
13 | /**
14 | * https://prettier.io/docs/en/options.html#trailing-commas
15 | */
16 | trailingComma: 'all',
17 |
18 | /**
19 | * https://prettier.io/docs/en/options.html#bracket-spacing
20 | */
21 | bracketSpacing: true,
22 |
23 | /**
24 | * https://prettier.io/docs/en/options.html#tabs
25 | */
26 | useTabs: true,
27 |
28 | /**
29 | * https://prettier.io/docs/en/options.html#tab-width
30 | */
31 | tabWidth: 2,
32 |
33 | /**
34 | * https://prettier.io/docs/en/options.html#arrow-function-parentheses
35 | */
36 | arrowParens: 'always',
37 |
38 | /**
39 | * https://prettier.io/docs/en/options.html#quotes
40 | */
41 | singleQuote: true,
42 |
43 | /**
44 | * https://prettier.io/docs/en/options.html#quote-props
45 | */
46 | quoteProps: 'as-needed',
47 |
48 | /**
49 | * https://prettier.io/docs/en/options.html#end-of-line
50 | */
51 | endOfLine: 'lf',
52 |
53 | /**
54 | * https://prettier.io/docs/en/options.html#print-width
55 | */
56 | printWidth: 100,
57 | };
58 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "n8n-nodes-text-manipulation",
3 | "version": "1.4.1",
4 | "description": "This node allows to modify texts.",
5 | "keywords": [
6 | "n8n-community-node-package"
7 | ],
8 | "main": "index.js",
9 | "scripts": {
10 | "lint": "eslint nodes package.json",
11 | "lint:fix": "eslint nodes package.json --fix",
12 | "build": "tsc && gulp build:icons",
13 | "prepare": "npm run build",
14 | "dev": "tsc --watch",
15 | "format": "prettier nodes --write",
16 | "tsc": "tsc"
17 | },
18 | "files": [
19 | "dist"
20 | ],
21 | "n8n": {
22 | "n8nNodesApiVersion": 1,
23 | "credentials": [],
24 | "nodes": [
25 | "dist/nodes/TextManipulation/TextManipulation.node.js"
26 | ]
27 | },
28 | "repository": {
29 | "type": "git",
30 | "url": "git+https://github.com/lublak/n8n-nodes-text-manipulation.git"
31 | },
32 | "author": {
33 | "name": "lublak"
34 | },
35 | "license": "MIT",
36 | "bugs": {
37 | "url": "https://github.com/lublak/n8n-nodes-text-manipulation/issues"
38 | },
39 | "homepage": "https://github.com/lublak/n8n-nodes-text-manipulation#readme",
40 | "devDependencies": {
41 | "@types/lodash": "^4.17.16",
42 | "@types/node": "^22.14.0",
43 | "@typescript-eslint/parser": "^8.29.1",
44 | "eslint": "^9.24.0",
45 | "eslint-plugin-n8n-nodes-base": "^1.16.3",
46 | "gulp": "^5.0.0",
47 | "prettier": "^3.5.3",
48 | "prettier-plugin-jsdoc": "^1.3.2",
49 | "typescript": "^5.8.3"
50 | },
51 | "dependencies": {
52 | "entities": "^6.0.0",
53 | "iconv-lite": "^0.6.3",
54 | "lodash": "^4.17.21",
55 | "string-strip-html": "^13.4.12"
56 | },
57 | "peerDependencies": {
58 | "n8n-workflow": "*"
59 | }
60 | }
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.yml:
--------------------------------------------------------------------------------
1 | name: Bug Report
2 | description: Create a report to help us improve
3 | labels: [bug]
4 | assignees:
5 | - lublak
6 | body:
7 | - type: markdown
8 | attributes:
9 | value: |
10 | Thanks for taking the time to fill out this bug report!
11 | Please always be sure to use the latest compatible version.
12 | - type: textarea
13 | id: bug-description
14 | attributes:
15 | label: Describe the bug
16 | description: A clear and concise description of what the bug is.
17 | placeholder: The description of the bug.
18 | validations:
19 | required: true
20 | - type: textarea
21 | id: expected-behavior
22 | attributes:
23 | label: Describe the expected behavior
24 | description: A clear and concise description of what you expected to happen.
25 | placeholder: The expected behavior.
26 | validations:
27 | required: true
28 | - type: input
29 | attributes:
30 | label: What is your Node.js version?
31 | placeholder: 14.X.X
32 | validations:
33 | required: true
34 | - type: input
35 | attributes:
36 | label: What is your n8n version?
37 | placeholder: 0.189.0
38 | validations:
39 | required: true
40 | - type: input
41 | attributes:
42 | label: What is your n8n-nodes-text-manipulation version?
43 | placeholder: 0.189.0
44 | validations:
45 | required: true
46 | - type: dropdown
47 | id: os
48 | attributes:
49 | label: What operating system are you seeing the problem on?
50 | multiple: true
51 | options:
52 | - Linux
53 | - Windows
54 | - MacOS
55 | - Other (enter below with the version)
56 | - type: input
57 | attributes:
58 | label: Operating system version (or if other, then please fill in complete name and version)
59 | validations:
60 | required: true
61 | - type: textarea
62 | id: logs
63 | attributes:
64 | label: Relevant log output
65 | description: Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
66 | render: shell
67 |
--------------------------------------------------------------------------------
/eslint.config.mjs:
--------------------------------------------------------------------------------
1 | import { defineConfig, globalIgnores } from "eslint/config";
2 | import globals from "globals";
3 | import tsParser from "@typescript-eslint/parser";
4 | import n8nNodesBase from "eslint-plugin-n8n-nodes-base";
5 | import path from "node:path";
6 | import { fileURLToPath } from "node:url";
7 | import js from "@eslint/js";
8 | import { FlatCompat } from "@eslint/eslintrc";
9 |
10 | const __filename = fileURLToPath(import.meta.url);
11 | const __dirname = path.dirname(__filename);
12 | const compat = new FlatCompat({
13 | baseDirectory: __dirname,
14 | recommendedConfig: js.configs.recommended,
15 | allConfig: js.configs.all
16 | });
17 |
18 | export default defineConfig([
19 | globalIgnores(["**/.eslintrc.js", "** /*.js", "**/node_modules /**/*", "**/dist /**/*"]),
20 | {
21 | languageOptions: {
22 | globals: {
23 | ...globals.browser,
24 | ...globals.node,
25 | },
26 |
27 | parser: tsParser,
28 | ecmaVersion: 5,
29 | sourceType: "module",
30 |
31 | parserOptions: {
32 | project: ["./tsconfig.json"],
33 | extraFileExtensions: [".json"],
34 | },
35 | },
36 | },
37 | {
38 | files: ["**/package.json"],
39 | extends: compat.extends("plugin:n8n-nodes-base/community"),
40 |
41 | plugins: {
42 | "n8n-nodes-base": n8nNodesBase,
43 | },
44 |
45 | rules: {
46 | "n8n-nodes-base/community-package-json-name-still-default": "off",
47 | },
48 | },
49 | {
50 | files: ["./nodes/**/*.ts"],
51 | extends: compat.extends("plugin:n8n-nodes-base/nodes"),
52 |
53 | plugins: {
54 | "n8n-nodes-base": n8nNodesBase,
55 | },
56 |
57 | rules: {
58 | "n8n-nodes-base/node-execute-block-missing-continue-on-fail": "off",
59 | "n8n-nodes-base/node-resource-description-filename-against-convention": "off",
60 | "n8n-nodes-base/node-param-fixed-collection-type-unsorted-items": "off",
61 | "n8n-nodes-base/node-class-description-inputs-wrong-regular-node": "off",
62 | "n8n-nodes-base/node-class-description-outputs-wrong": "off",
63 | },
64 | },
65 | ]);
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | # n8n-nodes-text-manipulation
6 |
7 | [](https://www.npmjs.org/package/n8n-nodes-text-manipulation)
8 | [](https://www.npmjs.org/package/n8n-nodes-text-manipulation)
9 | [](https://github.com/lublak/n8n-nodes-text-manipulation/actions/workflows/node.js.yml)
10 |
11 | Text manipulation allows various manipulations of strings.
12 | Features:
13 |
14 | - From
15 | - Text
16 | - File (Binary)
17 | - With decode options see icon-v (utf8, base64, utf16, etc...)
18 | - JSON
19 | - To
20 | - Text
21 | - File (Binary)
22 | - With encode options see icon-v (utf8, base64, utf16, etc...)
23 | - JSON
24 | - Get Manipulated Data (use previously manipulated)
25 | - Skip Non-String
26 | - Concat
27 | - Before
28 | - After
29 | - Decode/Encode
30 | - see icon-v (utf8, base64, utf16, etc...)
31 | - with strip/add BOM
32 | - Decode Entities
33 | - Url
34 | - Url Component
35 | - Xml
36 | - Legacy
37 | - Strict
38 | - Html
39 | - Legacy
40 | - Strict
41 | - Encode Entities
42 | - Url
43 | - Url Component
44 | - Xml
45 | - Extensive
46 | - UTF8
47 | - NonAscii
48 | - Html
49 | - Extensive
50 | - UTF8
51 | - NonAscii
52 | - Letter Case
53 | - Upper Case
54 | - Lower Case
55 | - Locale Upper Case
56 | - Locale Lower Case
57 | - Capitalize
58 | - Titlecase
59 | - Camel Case
60 | - Kebab Case
61 | - Snake Case
62 | - Start Case
63 | - Replace
64 | - Substring
65 | - All
66 | - Extended
67 | - Extended Substring
68 | - All
69 | - Extended
70 | - Regex
71 | - Extended
72 | - Predefined Rule
73 | - Tags
74 | - Only Recognised HTML
75 | - Character Groups
76 | - Newline
77 | - Newline Min
78 | - Newline Max
79 | - Number
80 | - Number Min
81 | - Number Max
82 | - Alpha
83 | - Alpha Min
84 | - Alpha Max
85 | - Whitespace
86 | - Whitespace Min
87 | - Whitespace Mac
88 | - Trim
89 | - Left
90 | - as an unit
91 | - Right
92 | - as an unit
93 | - Both
94 | - as an unit
95 | - Pad
96 | - Start
97 | - End
98 | - Substring
99 | - StartPosition
100 | - With
101 | - Complete
102 | - Position
103 | - Length
104 | - Repeat
105 | - Times
106 | - Normalize
107 |
108 | ## Install
109 |
110 | 1. Go to Settings (Cogwheel)
111 | 2. Click on "Community Nodes"
112 | 3. Enter "n8n-nodes-text-manipulation" into the text box
113 | 4. Click on "I understand the risk ..."
114 | 5. Click on "Install"
--------------------------------------------------------------------------------
/nodes/TextManipulation/TextManipulation.node.ts:
--------------------------------------------------------------------------------
1 | import * as entities from 'entities';
2 | import * as iconv from 'iconv-lite';
3 | import {
4 | camelCase,
5 | capitalize,
6 | escapeRegExp,
7 | get,
8 | kebabCase,
9 | set,
10 | snakeCase,
11 | startCase,
12 | trim,
13 | trimEnd,
14 | trimStart,
15 | } from 'lodash';
16 | import { BINARY_ENCODING, deepCopy, IExecuteFunctions, NodeConnectionType } from 'n8n-workflow';
17 | import {
18 | IBinaryData,
19 | IBinaryKeyData,
20 | IDataObject,
21 | INodeExecutionData,
22 | INodeParameters,
23 | INodePropertyOptions,
24 | INodeType,
25 | INodeTypeDescription,
26 | NodeOperationError,
27 | } from 'n8n-workflow';
28 | import stringStripHtml from 'string-strip-html';
29 |
30 | iconv.encodingExists('utf8');
31 |
32 | // Create options for bomAware and encoding
33 | const bomAware: string[] = [];
34 | const encodeDecodeOptions: INodePropertyOptions[] = [];
35 | const encodings = (
36 | iconv as unknown as {
37 | encodings: Record<
38 | string,
39 | | string
40 | | {
41 | bomAware: boolean;
42 | }
43 | >;
44 | }
45 | ).encodings;
46 | Object.keys(encodings).forEach((encoding) => {
47 | if (!(encoding.startsWith('_') || typeof encodings[encoding] === 'string')) {
48 | // only encodings without direct alias or internals
49 | if (
50 | (
51 | encodings[encoding] as {
52 | bomAware: boolean;
53 | }
54 | ).bomAware
55 | ) {
56 | bomAware.push(encoding);
57 | }
58 | encodeDecodeOptions.push({ name: encoding, value: encoding });
59 | }
60 | });
61 |
62 | /**
63 | * Allows to replace substrings in a string.
64 | *
65 | * @param {string} str - A string in which a part of the string is to be replaced.
66 | * @param {string} substr - A string that should be replaced.
67 | * @param {string} newSubstr - The new string which replaces the old string.
68 | * @returns {string} - String with replaced substrings.
69 | */
70 | function replaceAll(str: string, substr: string, newSubstr: string) {
71 | return str.replace(new RegExp(escapeRegExp(substr), 'g'), newSubstr);
72 | }
73 |
74 | /**
75 | * Removes leading characters as an unit from string.
76 | *
77 | * @param {string} str - The string to trim.
78 | * @param {string} chars - The characters to trim as a unit.
79 | * @returns {string} - Returns the trimmed string.
80 | */
81 | function charsTrimStart(str: string, chars: string) {
82 | if (chars === ' ') return str.trimStart();
83 | chars = escapeRegExp(chars);
84 | return str.replace(new RegExp('^(' + chars + ')+', 'g'), '');
85 | }
86 |
87 | /**
88 | * Removes trailing characters as an unit from string.
89 | *
90 | * @param {string} str - The string to trim.
91 | * @param {string} chars - The characters to trim as a unit.
92 | * @returns {string} - Returns the trimmed string.
93 | */
94 | function charsTrimEnd(str: string, chars: string) {
95 | if (chars === ' ') return str.trimEnd();
96 | chars = escapeRegExp(chars);
97 | return str.replace(new RegExp('(' + chars + ')+$', 'g'), '');
98 | }
99 |
100 | /**
101 | * Removes leading and trailing characters as an unit from string.
102 | *
103 | * @param {string} str - The string to trim.
104 | * @param {string} chars - The characters to trim as a unit.
105 | * @returns {string} - Returns the trimmed string.
106 | */
107 | function charsTrim(str: string, chars: string) {
108 | if (chars === ' ') return str.trim();
109 | chars = escapeRegExp(chars);
110 | return str.replace(new RegExp('^(' + chars + ')+|(' + chars + ')+$', 'g'), '');
111 | }
112 |
113 | /**
114 | * Escaped characters are unescaped.
115 | *
116 | * @param {string} str - The string for which the escaped characters should be unescaped.
117 | * @returns {string} - Returns string with unescaped escaped characters.
118 | */
119 | function unescapeEscapedCharacters(str: string) {
120 |
121 | const escapeCharacters: Record = {
122 | '\\0': '\0',
123 | "\\'": "'",
124 | '\\"': '"',
125 | '\\\\': '\\',
126 | '\\n': '\n',
127 | '\\r': '\r',
128 | '\\v': '\v',
129 | '\\t': '\t',
130 | '\\b': '\b',
131 | '\\f': '\f',
132 | };
133 |
134 |
135 | return str.replace(
136 | /(\\0|\\'|\\"|\\n|\\r|\\v|\\t|\\b|\\f)|\\u([\da-fA-F]{4})|\\x([\da-fA-F]{2})|\\u{(0*(?:10|[\da-fA-F])?[\da-fA-F]{1,4})}|\\(.)/g,
137 | (
138 | _,
139 | escapeCharacter,
140 | unicodeCharacter,
141 | unicodeShortCharacter,
142 | unicodeBracesCharacter,
143 | anyCharacter,
144 | ) => {
145 | if (escapeCharacter) return escapeCharacters[escapeCharacter as string];
146 | if (anyCharacter) return anyCharacter as string;
147 | return String.fromCharCode(
148 | parseInt(
149 | (unicodeCharacter ?? unicodeShortCharacter ?? unicodeBracesCharacter) as string,
150 | 16,
151 | ),
152 | );
153 | },
154 | );
155 | }
156 |
157 | /**
158 | * Builds a regex string from a regex string with min and max count.
159 | *
160 | * @param {string} base - The regex string.
161 | * @param {number} [min=0] - The minimum number of regex strings. Default is `0`
162 | * @param {number} [max=0] - The maximum number of regex strings. Default is `0`
163 | * @returns {string} - The new regex string with min and max.
164 | */
165 | function buildRegexGroup(base: string, min = 0, max = 0): string {
166 | if (min) {
167 | if (max) {
168 | return `${base}{${min},${max}}`;
169 | } else {
170 | return `${base}{${min},}`;
171 | }
172 | } else if (max) {
173 | return `${base}{${max}}`;
174 | } else {
175 | return `${base}*`;
176 | }
177 | }
178 |
179 | /** A node which allows you to manipulate string values. */
180 | export class TextManipulation implements INodeType {
181 | description: INodeTypeDescription = {
182 | displayName: 'TextManipulation',
183 | name: 'textManipulation',
184 | icon: 'file:TextManipulation.svg',
185 | group: ['transform'],
186 | version: 1,
187 | description: 'Allows you to manipulate string values.',
188 | defaults: {
189 | name: 'TextManipulation',
190 | },
191 | inputs: [NodeConnectionType.Main],
192 | outputs: [NodeConnectionType.Main],
193 | properties: [
194 | {
195 | displayName: 'Keep Only Set',
196 | name: 'keepOnlySet',
197 | type: 'boolean',
198 | default: false,
199 | description:
200 | 'Whether only the values set on this node should be kept and all others removed',
201 | },
202 | {
203 | displayName: 'Texts with Manipulations',
204 | name: 'textsWithManipulations',
205 | placeholder: 'Add Texts Manipulations',
206 | type: 'fixedCollection',
207 | typeOptions: {
208 | multipleValues: true,
209 | sortable: true,
210 | },
211 | description: 'The texts to manipulate',
212 | default: {},
213 | options: [
214 | {
215 | name: 'textsWithManipulationsValues',
216 | displayName: 'Texts with Manipulations',
217 | values: [
218 | {
219 | displayName: 'Data Sources',
220 | name: 'dataSources',
221 | placeholder: 'Add Data Source',
222 | type: 'fixedCollection',
223 | typeOptions: {
224 | multipleValues: true,
225 | sortable: true,
226 | },
227 | description: 'The data sources for the manipulations',
228 | default: {},
229 | options: [
230 | {
231 | displayName: 'Data Source',
232 | name: 'dataSource',
233 | values: [
234 | {
235 | displayName: 'Read Operation',
236 | name: 'readOperation',
237 | type: 'options',
238 | options: [
239 | {
240 | name: 'Text',
241 | value: 'fromText',
242 | description: 'Declare text directly',
243 | },
244 | {
245 | name: 'Read From File',
246 | value: 'fromFile',
247 | description: 'Read text from file',
248 | },
249 | {
250 | name: 'Read From JSON',
251 | value: 'fromJSON',
252 | description: 'Read text from JSON',
253 | },
254 | ],
255 | default: 'fromText',
256 | },
257 | {
258 | displayName: 'Binary Property',
259 | name: 'binaryPropertyName',
260 | required: true,
261 | displayOptions: {
262 | show: {
263 | readOperation: ['fromFile'],
264 | },
265 | },
266 | type: 'string',
267 | default: 'data',
268 | description:
269 | 'Name of the binary property from which the binary data is to be read',
270 | },
271 | {
272 | displayName: 'Decode With',
273 | name: 'fileDecodeWith',
274 | displayOptions: {
275 | show: {
276 | readOperation: ['fromFile'],
277 | },
278 | },
279 | type: 'options',
280 | options: encodeDecodeOptions,
281 | default: 'utf8',
282 | },
283 | {
284 | displayName: 'Strip BOM',
285 | name: 'fileStripBOM',
286 | displayOptions: {
287 | show: {
288 | readOperation: ['fromFile'],
289 | fileDecodeWith: bomAware,
290 | },
291 | },
292 | type: 'boolean',
293 | default: true,
294 | },
295 | {
296 | displayName: 'Get Manipulated Data',
297 | name: 'getManipulatedData',
298 | required: true,
299 | displayOptions: {
300 | show: {
301 | readOperation: ['fromFile', 'fromJSON'],
302 | },
303 | },
304 | type: 'boolean',
305 | default: false,
306 | description:
307 | 'Whether to use the newly manipulated data instead of the raw data. If none are available, raw data is used.',
308 | },
309 | {
310 | displayName: 'Source Key',
311 | name: 'sourceKey',
312 | required: true,
313 | displayOptions: {
314 | show: {
315 | readOperation: ['fromJSON'],
316 | },
317 | },
318 | type: 'string',
319 | default: 'data',
320 | description:
321 | 'The name of the JSON key to get data from.It is also possible to define deep keys by using dot-notation like for example:"level1.level2.currentKey"',
322 | },
323 | {
324 | displayName: 'Skip Non-String',
325 | name: 'skipNonString',
326 | required: true,
327 | displayOptions: {
328 | show: {
329 | readOperation: ['fromJSON'],
330 | },
331 | },
332 | type: 'boolean',
333 | default: true,
334 | description:
335 | 'Whether to skip non-string data. If they are not skipped, they are automatically converted to a string.',
336 | },
337 | {
338 | displayName: 'Text',
339 | name: 'text',
340 | required: true,
341 | displayOptions: {
342 | show: {
343 | readOperation: ['fromText'],
344 | },
345 | },
346 | type: 'string',
347 | default: '',
348 | description: 'Plain text',
349 | },
350 | {
351 | displayName: 'Write Operation',
352 | name: 'writeOperation',
353 | type: 'options',
354 | options: [
355 | {
356 | name: 'Write to File',
357 | value: 'toFile',
358 | description: 'Write the manipulated text to a file',
359 | },
360 | {
361 | name: 'Write to JSON',
362 | value: 'toJSON',
363 | description: 'Write the manipulated text to a JSON key',
364 | },
365 | ],
366 | default: 'toJSON',
367 | },
368 | {
369 | displayName: 'Destination Binary Property',
370 | name: 'destinationBinaryPropertyName',
371 | required: true,
372 | displayOptions: {
373 | show: {
374 | writeOperation: ['toFile'],
375 | },
376 | },
377 | type: 'string',
378 | default: 'data',
379 | description:
380 | 'Name of the binary property where the binary data should be written',
381 | },
382 | {
383 | displayName: 'Encode With',
384 | name: 'fileEncodeWith',
385 | displayOptions: {
386 | show: {
387 | writeOperation: ['toFile'],
388 | },
389 | },
390 | type: 'options',
391 | options: encodeDecodeOptions,
392 | default: 'utf8',
393 | },
394 | {
395 | displayName: 'Add BOM',
396 | name: 'fileAddBOM',
397 | displayOptions: {
398 | show: {
399 | writeOperation: ['toFile'],
400 | fileEncodeWith: bomAware,
401 | },
402 | },
403 | type: 'boolean',
404 | default: false,
405 | },
406 | {
407 | displayName: 'File Name',
408 | name: 'fileName',
409 | type: 'string',
410 | displayOptions: {
411 | show: {
412 | writeOperation: ['toFile'],
413 | },
414 | },
415 | default: '',
416 | placeholder: 'example.txt',
417 | description: 'The file name to set',
418 | },
419 | {
420 | displayName: 'Mime Type',
421 | name: 'mimeType',
422 | type: 'string',
423 | displayOptions: {
424 | show: {
425 | writeOperation: ['toFile'],
426 | },
427 | },
428 | default: 'text/plain',
429 | placeholder: 'text/plain',
430 | description:
431 | 'The mime-type to set. By default will the mime-type for plan text be set.',
432 | },
433 | {
434 | displayName: 'Destination Key',
435 | name: 'destinationKey',
436 | displayOptions: {
437 | show: {
438 | writeOperation: ['toJSON'],
439 | },
440 | },
441 | type: 'string',
442 | default: 'data',
443 | required: true,
444 | placeholder: 'data',
445 | description:
446 | 'The name the JSON key to copy data to. It is also possibleto define deep keys by using dot-notation like for example:"level1.level2.newKey".',
447 | },
448 | ],
449 | },
450 | ],
451 | },
452 | {
453 | displayName: 'Manipulations',
454 | name: 'manipulations',
455 | placeholder: 'Add Manipulation',
456 | type: 'fixedCollection',
457 | typeOptions: {
458 | multipleValues: true,
459 | sortable: true,
460 | },
461 | description: 'The manipulations for the data sources',
462 | default: {},
463 | options: [
464 | {
465 | name: 'manipulation',
466 | displayName: 'Manipulation',
467 | values: [
468 | {
469 | displayName: 'Action',
470 | name: 'action',
471 | type: 'options',
472 | options: [
473 | {
474 | name: 'Concat',
475 | value: 'concat',
476 | description: 'Add string to the beginning or/and end',
477 | action: 'Add string to the beginning or and end',
478 | },
479 | {
480 | name: 'Decode/Encode',
481 | value: 'decodeEncode',
482 | description: 'Decode and Encode string',
483 | action: 'Decode and encode string',
484 | },
485 | {
486 | name: 'Decode/Encode Entities',
487 | value: 'decodeEncodeEntities',
488 | description: 'Decode and Encode HTML & XML entities',
489 | action: 'Decode and encode html xml entities',
490 | },
491 | {
492 | name: 'Letter Case',
493 | value: 'letterCase',
494 | description: 'Upper and lowercase letters in a string',
495 | action: 'Upper and lowercase letters in a string',
496 | },
497 | {
498 | name: 'Normalize',
499 | value: 'normalize',
500 | description: 'Normalize a string',
501 | action: 'Normalize a string',
502 | },
503 | {
504 | name: 'Pad',
505 | value: 'pad',
506 | description: 'Pad the string at the beginning or end',
507 | action: 'Pad the string at the beginning or end',
508 | },
509 | {
510 | name: 'Repeat',
511 | value: 'repeat',
512 | description: 'Repeat the string',
513 | action: 'Repeat the string',
514 | },
515 | {
516 | name: 'Replace',
517 | value: 'replace',
518 | description: 'Replace a substring or regex',
519 | action: 'Replace a substring or regex',
520 | },
521 | {
522 | name: 'Substring',
523 | value: 'substring',
524 | description: 'Get a substring',
525 | action: 'Get a substring',
526 | },
527 | {
528 | name: 'Trim',
529 | value: 'trim',
530 | description: 'Removes characters from the beginning or/and end',
531 | action: 'Removes characters from the beginning or and end',
532 | },
533 | ],
534 | default: 'letterCase',
535 | },
536 | {
537 | displayName: 'Normalize Form',
538 | name: 'normalizeForm',
539 | displayOptions: {
540 | show: {
541 | action: ['normalize'],
542 | },
543 | },
544 | type: 'options',
545 | options: [
546 | {
547 | name: 'NFC',
548 | value: 'nfc',
549 | description:
550 | 'Canonical Decomposition, followed by Canonical Composition',
551 | },
552 | {
553 | name: 'NFD',
554 | value: 'nfd',
555 | description: 'Canonical Decomposition',
556 | },
557 | {
558 | name: 'NFKC',
559 | value: 'nfkc',
560 | description:
561 | 'Compatibility Decomposition, followed by Canonical Composition',
562 | },
563 | {
564 | name: 'NFKD',
565 | value: 'nfkd',
566 | description: 'Compatibility Decomposition',
567 | },
568 | ],
569 | default: 'nfc',
570 | },
571 | {
572 | displayName: 'Case Type',
573 | name: 'caseType',
574 | displayOptions: {
575 | show: {
576 | action: ['letterCase'],
577 | },
578 | },
579 | type: 'options',
580 | options: [
581 | {
582 | name: 'Camel Case',
583 | value: 'camelCase',
584 | description: 'Converts string to camel case',
585 | },
586 | {
587 | name: 'Capitalize',
588 | value: 'capitalize',
589 | description: 'Capitalize text',
590 | },
591 | {
592 | name: 'Kebab Case',
593 | value: 'kebabCase',
594 | description: 'Converts string to kebab case',
595 | },
596 | {
597 | name: 'Locale Lower Case',
598 | value: 'localeLowerCase',
599 | description: 'Locale lower case all characters',
600 | },
601 | {
602 | name: 'Locale Upper Case',
603 | value: 'localeUpperCase',
604 | description: 'Locale upper case all characters',
605 | },
606 | {
607 | name: 'Lower Case',
608 | value: 'lowerCase',
609 | description: 'Lower case all characters',
610 | },
611 | {
612 | name: 'Snake Case',
613 | value: 'snakeCase',
614 | description: 'Converts string to snake case',
615 | },
616 | {
617 | name: 'Start Case',
618 | value: 'startCase',
619 | description: 'Converts string to start case',
620 | },
621 | {
622 | name: 'Titlecase',
623 | value: 'titlecase',
624 | description: 'Titlecase text',
625 | },
626 | {
627 | name: 'Upper Case',
628 | value: 'upperCase',
629 | description: 'Upper case all characters',
630 | },
631 | ],
632 | default: 'lowerCase',
633 | },
634 | {
635 | displayName: 'Language',
636 | name: 'language',
637 | displayOptions: {
638 | show: {
639 | action: ['letterCase'],
640 | caseType: ['localeLowerCase', 'localeUpperCase'],
641 | },
642 | },
643 | type: 'string',
644 | default: 'en',
645 | required: true,
646 | description: 'Change the language of the localbase method',
647 | },
648 | {
649 | displayName: 'Before',
650 | name: 'before',
651 | displayOptions: {
652 | show: {
653 | action: ['concat'],
654 | },
655 | },
656 | type: 'string',
657 | default: '',
658 | description: 'String to be added at the beginning',
659 | },
660 | {
661 | displayName: 'After',
662 | name: 'after',
663 | displayOptions: {
664 | show: {
665 | action: ['concat'],
666 | },
667 | },
668 | type: 'string',
669 | default: '',
670 | description: 'String to be added at the end',
671 | },
672 | {
673 | displayName: 'Decode With',
674 | name: 'decodeWith',
675 | displayOptions: {
676 | show: {
677 | action: ['decodeEncode'],
678 | },
679 | },
680 | type: 'options',
681 | options: encodeDecodeOptions,
682 | default: 'utf8',
683 | },
684 | {
685 | displayName: 'Decode With',
686 | name: 'decodeWithEntities',
687 | displayOptions: {
688 | show: {
689 | action: ['decodeEncodeEntities'],
690 | },
691 | },
692 | type: 'options',
693 | options: [
694 | {
695 | name: 'Html',
696 | value: 'html',
697 | },
698 | {
699 | name: 'Nothing',
700 | value: 'nothing',
701 | },
702 | {
703 | name: 'Url',
704 | value: 'url',
705 | },
706 | {
707 | name: 'Url Component',
708 | value: 'urlComponent',
709 | },
710 | {
711 | name: 'Xml',
712 | value: 'xml',
713 | },
714 | ],
715 | default: 'nothing',
716 | },
717 | {
718 | displayName: 'Decode Mode',
719 | name: 'entitiesDecodeMode',
720 | displayOptions: {
721 | show: {
722 | action: ['decodeEncodeEntities'],
723 | decodeWithEntities: ['xml', 'html'],
724 | },
725 | },
726 | type: 'options',
727 | options: [
728 | {
729 | name: 'Legacy',
730 | value: 'legacy',
731 | },
732 | {
733 | name: 'Strict',
734 | value: 'strict',
735 | },
736 | ],
737 | default: 'legacy',
738 | },
739 | {
740 | displayName: 'Strip BOM',
741 | name: 'stripBOM',
742 | displayOptions: {
743 | show: {
744 | action: ['decodeEncode'],
745 | decodeWith: bomAware,
746 | },
747 | },
748 | type: 'boolean',
749 | default: true,
750 | },
751 | {
752 | displayName: 'Encode With',
753 | name: 'encodeWith',
754 | displayOptions: {
755 | show: {
756 | action: ['decodeEncode'],
757 | },
758 | },
759 | type: 'options',
760 | options: encodeDecodeOptions,
761 | default: 'utf8',
762 | },
763 | {
764 | displayName: 'Encode With',
765 | name: 'encodeWithEntities',
766 | displayOptions: {
767 | show: {
768 | action: ['decodeEncodeEntities'],
769 | },
770 | },
771 | type: 'options',
772 | options: [
773 | {
774 | name: 'Html',
775 | value: 'html',
776 | },
777 | {
778 | name: 'Nothing',
779 | value: 'nothing',
780 | },
781 | {
782 | name: 'Url',
783 | value: 'url',
784 | },
785 | {
786 | name: 'Url Component',
787 | value: 'urlComponent',
788 | },
789 | {
790 | name: 'Xml',
791 | value: 'xml',
792 | },
793 | ],
794 | default: 'nothing',
795 | },
796 | {
797 | displayName: 'Encode Mode',
798 | name: 'entitiesEncodeMode',
799 | displayOptions: {
800 | show: {
801 | action: ['decodeEncodeEntities'],
802 | encodeWithEntities: ['xml', 'html'],
803 | },
804 | },
805 | type: 'options',
806 | options: [
807 | {
808 | name: 'Extensive',
809 | value: 'extensive',
810 | },
811 | {
812 | name: 'UTF8',
813 | value: 'utf8',
814 | },
815 | {
816 | name: 'NonAscii',
817 | value: 'nonAscii',
818 | },
819 | ],
820 | default: 'extensive',
821 | },
822 | {
823 | displayName: 'Add BOM',
824 | name: 'addBOM',
825 | displayOptions: {
826 | show: {
827 | action: ['decodeEncode'],
828 | encodeWith: bomAware,
829 | },
830 | },
831 | type: 'boolean',
832 | default: false,
833 | },
834 | {
835 | displayName: 'Replace Mode',
836 | name: 'replaceMode',
837 | displayOptions: {
838 | show: {
839 | action: ['replace'],
840 | },
841 | },
842 | type: 'options',
843 | options: [
844 | {
845 | name: 'Substring',
846 | value: 'substring',
847 | description: 'Replace a substring with a value',
848 | },
849 | {
850 | name: 'Extended Substring',
851 | value: 'extendedSubstring',
852 | description:
853 | 'Replace a substring including escape characters with a value',
854 | },
855 | {
856 | name: 'Regex',
857 | value: 'regex',
858 | description: 'Replace regex with a pattern',
859 | },
860 | {
861 | name: 'Predefined Rule',
862 | value: 'predefinedRule',
863 | description: 'Use a predefined rule to replace',
864 | },
865 | ],
866 | default: 'substring',
867 | },
868 | {
869 | displayName: 'Regex',
870 | name: 'regex',
871 | displayOptions: {
872 | show: {
873 | action: ['replace'],
874 | replaceMode: ['regex'],
875 | },
876 | },
877 | type: 'string',
878 | default: '',
879 | required: true,
880 | placeholder: '.*',
881 | description: 'Regular expression',
882 | },
883 | {
884 | displayName: 'Pattern',
885 | name: 'pattern',
886 | displayOptions: {
887 | show: {
888 | action: ['replace'],
889 | replaceMode: ['regex'],
890 | },
891 | },
892 | type: 'string',
893 | default: '',
894 | placeholder: '$&',
895 | // eslint-disable-next-line n8n-nodes-base/node-param-description-unencoded-angle-brackets
896 | description:
897 | 'Pattern Inserts $$ Inserts a "$". $& Inserts the matched substring. $` Inserts the portion of the string that precedes the matched substring. $\' Inserts the portion of the string that follows the matched substring. $n Where n is a positive integer less than 100, inserts the nth parenthesized submatch string, provided the first argument was a RegExp object. Note that this is 1-indexed. If a group n is not present (e.g., if group is 3), it will be replaced as a literal (e.g., $3). $ Where Name is a capturing group name. If the group is not in the match, or not in the regular expression, or if a string was passed as the first argument to replace instead of a regular expression, this resolves to a literal (e.g., $).
',
898 | },
899 | {
900 | displayName: 'Predefined Rule',
901 | name: 'predefinedRule',
902 | displayOptions: {
903 | show: {
904 | action: ['replace'],
905 | replaceMode: ['predefinedRule'],
906 | },
907 | },
908 | type: 'options',
909 | options: [
910 | {
911 | name: 'Tags',
912 | value: 'tags',
913 | description: 'Replace all tags',
914 | },
915 | {
916 | name: 'Character Groups',
917 | value: 'characterGroups',
918 | description: 'Replace all defined character groups',
919 | },
920 | ],
921 | default: 'tags',
922 | },
923 | {
924 | displayName: 'Only Recognised HTML',
925 | name: 'onlyRecognisedHTML',
926 | displayOptions: {
927 | show: {
928 | action: ['replace'],
929 | replaceMode: ['predefinedRule'],
930 | predefinedRule: ['tags'],
931 | },
932 | },
933 | type: 'boolean',
934 | default: false,
935 | },
936 | {
937 | displayName: 'Newline',
938 | name: 'newline',
939 | displayOptions: {
940 | show: {
941 | action: ['replace'],
942 | replaceMode: ['predefinedRule'],
943 | predefinedRule: ['characterGroups'],
944 | },
945 | },
946 | type: 'boolean',
947 | default: false,
948 | },
949 | {
950 | displayName: 'Newline Min',
951 | name: 'newlineMin',
952 | displayOptions: {
953 | show: {
954 | action: ['replace'],
955 | replaceMode: ['predefinedRule'],
956 | predefinedRule: ['characterGroups'],
957 | newline: [true],
958 | },
959 | },
960 | type: 'number',
961 | default: 1,
962 | },
963 | {
964 | displayName: 'Newline Max',
965 | name: 'newlineMax',
966 | displayOptions: {
967 | show: {
968 | action: ['replace'],
969 | replaceMode: ['predefinedRule'],
970 | predefinedRule: ['characterGroups'],
971 | newline: [true],
972 | },
973 | },
974 | type: 'number',
975 | default: 1,
976 | },
977 | {
978 | displayName: 'Number',
979 | name: 'number',
980 | displayOptions: {
981 | show: {
982 | action: ['replace'],
983 | replaceMode: ['predefinedRule'],
984 | predefinedRule: ['characterGroups'],
985 | },
986 | },
987 | type: 'boolean',
988 | default: false,
989 | },
990 | {
991 | displayName: 'Number Min',
992 | name: 'numberMin',
993 | displayOptions: {
994 | show: {
995 | action: ['replace'],
996 | replaceMode: ['predefinedRule'],
997 | predefinedRule: ['characterGroups'],
998 | number: [true],
999 | },
1000 | },
1001 | type: 'number',
1002 | default: 1,
1003 | },
1004 | {
1005 | displayName: 'Number Max',
1006 | name: 'numberMax',
1007 | displayOptions: {
1008 | show: {
1009 | action: ['replace'],
1010 | replaceMode: ['predefinedRule'],
1011 | predefinedRule: ['characterGroups'],
1012 | number: [true],
1013 | },
1014 | },
1015 | type: 'number',
1016 | default: 1,
1017 | },
1018 | {
1019 | displayName: 'Alpha',
1020 | name: 'alpha',
1021 | displayOptions: {
1022 | show: {
1023 | action: ['replace'],
1024 | replaceMode: ['predefinedRule'],
1025 | predefinedRule: ['characterGroups'],
1026 | },
1027 | },
1028 | type: 'boolean',
1029 | default: false,
1030 | },
1031 | {
1032 | displayName: 'Alpha Min',
1033 | name: 'alphaMin',
1034 | displayOptions: {
1035 | show: {
1036 | action: ['replace'],
1037 | replaceMode: ['predefinedRule'],
1038 | predefinedRule: ['characterGroups'],
1039 | alpha: [true],
1040 | },
1041 | },
1042 | type: 'number',
1043 | default: 1,
1044 | },
1045 | {
1046 | displayName: 'Alpha Max',
1047 | name: 'alphaMax',
1048 | displayOptions: {
1049 | show: {
1050 | action: ['replace'],
1051 | replaceMode: ['predefinedRule'],
1052 | predefinedRule: ['characterGroups'],
1053 | alpha: [true],
1054 | },
1055 | },
1056 | type: 'number',
1057 | default: 1,
1058 | },
1059 | {
1060 | displayName: 'Whitespace',
1061 | name: 'whitespace',
1062 | displayOptions: {
1063 | show: {
1064 | action: ['replace'],
1065 | replaceMode: ['predefinedRule'],
1066 | predefinedRule: ['characterGroups'],
1067 | },
1068 | },
1069 | type: 'boolean',
1070 | default: false,
1071 | },
1072 | {
1073 | displayName: 'Whitespace Min',
1074 | name: 'whitespaceMin',
1075 | displayOptions: {
1076 | show: {
1077 | action: ['replace'],
1078 | replaceMode: ['predefinedRule'],
1079 | predefinedRule: ['characterGroups'],
1080 | whitespace: [true],
1081 | },
1082 | },
1083 | type: 'number',
1084 | default: 1,
1085 | },
1086 | {
1087 | displayName: 'Whitespace Max',
1088 | name: 'whitespaceMax',
1089 | displayOptions: {
1090 | show: {
1091 | action: ['replace'],
1092 | replaceMode: ['predefinedRule'],
1093 | predefinedRule: ['characterGroups'],
1094 | whitespace: [true],
1095 | },
1096 | },
1097 | type: 'number',
1098 | default: 1,
1099 | },
1100 | {
1101 | displayName: 'Substring',
1102 | name: 'substring',
1103 | displayOptions: {
1104 | show: {
1105 | action: ['replace'],
1106 | replaceMode: ['substring', 'extendedSubstring'],
1107 | },
1108 | },
1109 | type: 'string',
1110 | default: '',
1111 | required: true,
1112 | placeholder: 'sub',
1113 | description: 'The substring to be replaced',
1114 | },
1115 | {
1116 | displayName: 'Value',
1117 | name: 'value',
1118 | displayOptions: {
1119 | show: {
1120 | action: ['replace'],
1121 | replaceMode: ['substring', 'extendedSubstring', 'predefinedRule'],
1122 | },
1123 | },
1124 | type: 'string',
1125 | default: '',
1126 | placeholder: '',
1127 | description: 'The value that should replace the substring',
1128 | },
1129 | {
1130 | displayName: 'Replace All',
1131 | name: 'replaceAll',
1132 | displayOptions: {
1133 | show: {
1134 | action: ['replace'],
1135 | replaceMode: ['substring', 'extendedSubstring'],
1136 | },
1137 | },
1138 | type: 'boolean',
1139 | default: true,
1140 | placeholder: '',
1141 | description:
1142 | 'Whether all substrings should be replaced (not only the first)',
1143 | },
1144 | {
1145 | displayName: 'Extended',
1146 | name: 'extended',
1147 | displayOptions: {
1148 | show: {
1149 | action: ['replace'],
1150 | },
1151 | },
1152 | type: 'boolean',
1153 | default: false,
1154 | placeholder: '',
1155 | description:
1156 | 'Whether all escape characters should be used for replacement (\\n, \\r, \\t, ...)',
1157 | },
1158 | {
1159 | displayName: 'Trim',
1160 | name: 'trim',
1161 | displayOptions: {
1162 | show: {
1163 | action: ['trim'],
1164 | },
1165 | },
1166 | type: 'options',
1167 | options: [
1168 | {
1169 | name: 'Trim Both',
1170 | value: 'trimBoth',
1171 | description: 'Removes characters from the beginning and end',
1172 | },
1173 | {
1174 | name: 'Trim Start',
1175 | value: 'trimStart',
1176 | description: 'Removes characters from the beginning',
1177 | },
1178 | {
1179 | name: 'Trim End',
1180 | value: 'trimEnd',
1181 | description: 'Removes characters from the end',
1182 | },
1183 | ],
1184 | default: 'trimBoth',
1185 | },
1186 | {
1187 | displayName: 'Trim String',
1188 | name: 'trimString',
1189 | displayOptions: {
1190 | show: {
1191 | action: ['trim'],
1192 | },
1193 | },
1194 | type: 'string',
1195 | default: ' ',
1196 | required: true,
1197 | description: 'The string to trim',
1198 | },
1199 | {
1200 | displayName: 'Trim String as an Unit',
1201 | name: 'trimStringUnit',
1202 | displayOptions: {
1203 | show: {
1204 | action: ['trim'],
1205 | },
1206 | },
1207 | type: 'boolean',
1208 | default: true,
1209 | required: true,
1210 | description:
1211 | 'Whether to use the trim chain as a whole unit and not each individual character in that chain',
1212 | },
1213 | {
1214 | displayName: 'Pad',
1215 | name: 'pad',
1216 | displayOptions: {
1217 | show: {
1218 | action: ['pad'],
1219 | },
1220 | },
1221 | type: 'options',
1222 | options: [
1223 | {
1224 | name: 'Pad Start',
1225 | value: 'padStart',
1226 | description: 'Pad the string at the beginning',
1227 | },
1228 | {
1229 | name: 'Pad End',
1230 | value: 'padEnd',
1231 | description: 'Pad the string at the end',
1232 | },
1233 | ],
1234 | default: 'padStart',
1235 | },
1236 | {
1237 | displayName: 'Target Length',
1238 | name: 'targetLength',
1239 | displayOptions: {
1240 | show: {
1241 | action: ['pad'],
1242 | },
1243 | },
1244 | type: 'number',
1245 | typeOptions: {
1246 | minValue: 0,
1247 | },
1248 | default: 1,
1249 | required: true,
1250 | placeholder: '1',
1251 | description: 'The length to which the string should be padded',
1252 | },
1253 | {
1254 | displayName: 'Pad String',
1255 | name: 'padString',
1256 | displayOptions: {
1257 | show: {
1258 | action: ['pad'],
1259 | },
1260 | },
1261 | type: 'string',
1262 | default: ' ',
1263 | required: true,
1264 | description: 'The filling string',
1265 | },
1266 | {
1267 | displayName: 'Start Position',
1268 | name: 'startPosition',
1269 | displayOptions: {
1270 | show: {
1271 | action: ['substring'],
1272 | },
1273 | },
1274 | type: 'number',
1275 | default: 0,
1276 | placeholder: '0',
1277 | description:
1278 | 'The start position (string begins with 0). Can also be negativ.',
1279 | },
1280 | {
1281 | displayName: 'End',
1282 | name: 'end',
1283 | displayOptions: {
1284 | show: {
1285 | action: ['substring'],
1286 | },
1287 | },
1288 | type: 'options',
1289 | options: [
1290 | {
1291 | name: 'Complete',
1292 | value: 'complete',
1293 | description: 'Selects everything to the end',
1294 | },
1295 | {
1296 | name: 'Position',
1297 | value: 'position',
1298 | description:
1299 | 'Selects everything up to the position (exclusive position). Can also be negative.',
1300 | },
1301 | {
1302 | name: 'Length',
1303 | value: 'length',
1304 | description: 'The length of the selected rows',
1305 | },
1306 | ],
1307 | default: 'complete',
1308 | description: 'The end of the substring',
1309 | },
1310 | {
1311 | displayName: 'Position',
1312 | name: 'endPosition',
1313 | displayOptions: {
1314 | show: {
1315 | action: ['substring'],
1316 | end: ['position'],
1317 | },
1318 | },
1319 | type: 'number',
1320 | default: 1,
1321 | placeholder: '1',
1322 | description: 'The end position of the substring. Can also be negative.',
1323 | },
1324 | {
1325 | displayName: 'Length',
1326 | name: 'endLength',
1327 | displayOptions: {
1328 | show: {
1329 | action: ['substring'],
1330 | end: ['length'],
1331 | },
1332 | },
1333 | typeOptions: {
1334 | minValue: 0,
1335 | },
1336 | type: 'number',
1337 | default: 1,
1338 | placeholder: '1',
1339 | description: 'The length of the substring',
1340 | },
1341 | {
1342 | displayName: 'Times',
1343 | name: 'times',
1344 | displayOptions: {
1345 | show: {
1346 | action: ['repeat'],
1347 | },
1348 | },
1349 | type: 'number',
1350 | typeOptions: {
1351 | minValue: 0,
1352 | },
1353 | default: 1,
1354 | required: true,
1355 | placeholder: '1',
1356 | description: 'The number of times the string should be repeated',
1357 | },
1358 | ],
1359 | },
1360 | ],
1361 | },
1362 | ],
1363 | },
1364 | ],
1365 | },
1366 | ],
1367 | };
1368 |
1369 | async execute(this: IExecuteFunctions): Promise {
1370 | const items = this.getInputData();
1371 | const returnData: INodeExecutionData[] = [];
1372 |
1373 | let text: string;
1374 |
1375 | for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
1376 | const keepOnlySet = this.getNodeParameter('keepOnlySet', itemIndex, false) as boolean;
1377 |
1378 | const item = items[itemIndex];
1379 | let newItemJson: IDataObject = {};
1380 | const newItemBinary: IBinaryKeyData = {};
1381 |
1382 | if (!keepOnlySet) {
1383 | if (item.binary !== undefined) {
1384 | Object.assign(newItemBinary, item.binary);
1385 | }
1386 |
1387 | newItemJson = deepCopy(item.json);
1388 | }
1389 |
1390 | for (const textsWithManipulationsValues of (this.getNodeParameter(
1391 | 'textsWithManipulations.textsWithManipulationsValues',
1392 | itemIndex,
1393 | [],
1394 | ) as INodeParameters[] | null) ?? []) {
1395 | for (const dataSource of ((textsWithManipulationsValues.dataSources as INodeParameters)
1396 | .dataSource as INodeParameters[] | null) ?? []) {
1397 | switch (dataSource.readOperation) {
1398 | case 'fromFile':
1399 | if (dataSource.getManipulatedData) {
1400 | if (
1401 | (newItemBinary[dataSource.binaryPropertyName as string] as
1402 | | IBinaryData
1403 | | undefined) === undefined
1404 | ) {
1405 | if (
1406 | item.binary === undefined ||
1407 | (item.binary[dataSource.binaryPropertyName as string] as
1408 | | IBinaryData
1409 | | undefined) === undefined
1410 | ) {
1411 | continue;
1412 | }
1413 | text = iconv.decode(
1414 | Buffer.from(
1415 | item.binary[dataSource.binaryPropertyName as string].data,
1416 | BINARY_ENCODING,
1417 | ),
1418 | dataSource.fileDecodeWith as string,
1419 | { stripBOM: dataSource.fileStripBOM as boolean },
1420 | );
1421 | } else {
1422 | text = iconv.decode(
1423 | Buffer.from(
1424 | newItemBinary[dataSource.binaryPropertyName as string].data,
1425 | BINARY_ENCODING,
1426 | ),
1427 | dataSource.fileDecodeWith as string,
1428 | { stripBOM: dataSource.fileStripBOM as boolean },
1429 | );
1430 | }
1431 | } else if (
1432 | item.binary === undefined ||
1433 | (item.binary[dataSource.binaryPropertyName as string] as
1434 | | IBinaryData
1435 | | undefined) === undefined
1436 | ) {
1437 | continue;
1438 | } else {
1439 | text = iconv.decode(
1440 | Buffer.from(
1441 | item.binary[dataSource.binaryPropertyName as string].data,
1442 | BINARY_ENCODING,
1443 | ),
1444 | dataSource.fileDecodeWith as string,
1445 | { stripBOM: dataSource.fileStripBOM as boolean },
1446 | );
1447 | }
1448 | break;
1449 | case 'fromJSON': {
1450 | const value =
1451 | (dataSource.getManipulatedData &&
1452 | get(newItemJson, dataSource.sourceKey as string)) ||
1453 | get(item.json, dataSource.sourceKey as string);
1454 | if (typeof value === 'string') {
1455 | text = value;
1456 | } else if (dataSource.skipNonString) {
1457 | continue;
1458 | } else {
1459 | text = ((value as string | null) ?? '').toString();
1460 | }
1461 | break;
1462 | }
1463 | case 'fromText':
1464 | text = dataSource.text as string;
1465 | break;
1466 | default:
1467 | throw new NodeOperationError(
1468 | this.getNode(),
1469 | 'fromFile, fromJSON or fromText are valid options',
1470 | { itemIndex },
1471 | );
1472 | }
1473 |
1474 | for (const manipulation of ((
1475 | textsWithManipulationsValues.manipulations as INodeParameters
1476 | ).manipulation as INodeParameters[] | null) ?? []) {
1477 | switch (manipulation.action) {
1478 | case 'concat':
1479 | text =
1480 | ((manipulation.before as string | null) ?? '') +
1481 | text +
1482 | ((manipulation.after as string | null) ?? '');
1483 | break;
1484 | case 'decodeEncode':
1485 | if (manipulation.encodeWith !== manipulation.decodeWith) {
1486 | text = iconv
1487 | .encode(
1488 | iconv.decode(Buffer.from(text), manipulation.decodeWith as string, {
1489 | addBOM: manipulation.addBOM as boolean,
1490 | }),
1491 | manipulation.encodeWith as string,
1492 | { stripBOM: manipulation.stripBOM as boolean },
1493 | )
1494 | .toString();
1495 | }
1496 | break;
1497 | case 'decodeEncodeEntities':
1498 | if (manipulation.encodeWithEntities !== manipulation.decodeWithEntities) {
1499 | switch (manipulation.decodeWithEntities) {
1500 | case 'url':
1501 | text = decodeURI(text);
1502 | break;
1503 | case 'urlComponent':
1504 | text = decodeURIComponent(text);
1505 | break;
1506 | case 'xml':
1507 | switch (manipulation.entitiesDecodeMode) {
1508 | case 'legacy':
1509 | text = entities.decodeXML(text);
1510 | break;
1511 | case 'strict':
1512 | text = entities.decodeXMLStrict(text);
1513 | break;
1514 | default:
1515 | throw new NodeOperationError(
1516 | this.getNode(),
1517 | 'legacy or strict are valid options',
1518 | { itemIndex },
1519 | );
1520 | }
1521 | break;
1522 | case 'html':
1523 | switch (manipulation.entitiesDecodeMode) {
1524 | case 'legacy':
1525 | text = entities.decodeHTML(text);
1526 | break;
1527 | case 'strict':
1528 | text = entities.decodeHTMLStrict(text);
1529 | break;
1530 | default:
1531 | throw new NodeOperationError(
1532 | this.getNode(),
1533 | 'legacy or strict are valid options',
1534 | { itemIndex },
1535 | );
1536 | }
1537 | break;
1538 | case 'nothing':
1539 | break;
1540 | default:
1541 | throw new NodeOperationError(
1542 | this.getNode(),
1543 | 'url, xml, html or nothing are valid options',
1544 | { itemIndex },
1545 | );
1546 | }
1547 |
1548 | switch (manipulation.encodeWithEntities) {
1549 | case 'url':
1550 | text = encodeURI(text);
1551 | break;
1552 | case 'urlComponent':
1553 | text = encodeURIComponent(text);
1554 | break;
1555 | case 'xml':
1556 | switch (manipulation.entitiesEncodeMode) {
1557 | case 'extensive':
1558 | text = entities.encodeXML(text);
1559 | break;
1560 | case 'utf8':
1561 | text = entities.escapeUTF8(text);
1562 | break;
1563 | case 'nonAscii':
1564 | text = entities.encodeXML(text);
1565 | break;
1566 | default:
1567 | throw new NodeOperationError(
1568 | this.getNode(),
1569 | 'extensive, utf8 or nonAscii are valid options',
1570 | { itemIndex },
1571 | );
1572 | }
1573 | break;
1574 | case 'html':
1575 | switch (manipulation.entitiesEncodeMode) {
1576 | case 'extensive':
1577 | text = entities.encodeHTML(text);
1578 | break;
1579 | case 'utf8':
1580 | text = entities.escapeUTF8(text);
1581 | break;
1582 | case 'nonAscii':
1583 | text = entities.encodeNonAsciiHTML(text);
1584 | break;
1585 | default:
1586 | throw new NodeOperationError(
1587 | this.getNode(),
1588 | 'extensive, utf8 or nonAscii are valid options',
1589 | { itemIndex },
1590 | );
1591 | }
1592 | break;
1593 | case 'nothing':
1594 | break;
1595 | default:
1596 | throw new NodeOperationError(
1597 | this.getNode(),
1598 | 'url, xml, html or nothing are valid options',
1599 | { itemIndex },
1600 | );
1601 | }
1602 | }
1603 | break;
1604 | case 'letterCase':
1605 | switch (manipulation.caseType) {
1606 | case 'camelCase':
1607 | text = camelCase(text);
1608 | break;
1609 | case 'capitalize':
1610 | text = capitalize(text);
1611 | break;
1612 | case 'titlecase':
1613 | text = text.split(' ').map(capitalize).join(' ');
1614 | break;
1615 | case 'kebabCase':
1616 | text = kebabCase(text);
1617 | break;
1618 | case 'snakeCase':
1619 | text = snakeCase(text);
1620 | break;
1621 | case 'startCase':
1622 | text = startCase(text);
1623 | break;
1624 | case 'upperCase':
1625 | text = text.toUpperCase();
1626 | break;
1627 | case 'lowerCase':
1628 | text = text.toLowerCase();
1629 | break;
1630 | case 'localeUpperCase':
1631 | text = text.toLocaleUpperCase(manipulation.language as string);
1632 | break;
1633 | case 'localeLowerCase':
1634 | text = text.toLocaleLowerCase(manipulation.language as string);
1635 | break;
1636 | default:
1637 | throw new NodeOperationError(
1638 | this.getNode(),
1639 | 'upperCase, lowerCase, capitalize, camelCase, kebabCase or snakeCase are valid options',
1640 | { itemIndex },
1641 | );
1642 | }
1643 | break;
1644 | case 'normalize':
1645 | switch (manipulation.normalizeForm) {
1646 | case 'nfc':
1647 | text = text.normalize('NFC');
1648 | break;
1649 | case 'nfd':
1650 | text = text.normalize('NFD');
1651 | break;
1652 | case 'nfkc':
1653 | text = text.normalize('NFKC');
1654 | break;
1655 | case 'nfkd':
1656 | text = text.normalize('NFKD');
1657 | break;
1658 | }
1659 | break;
1660 | case 'replace':
1661 | switch (manipulation.replaceMode) {
1662 | case 'substring':
1663 | if (manipulation.replaceAll) {
1664 | text = replaceAll(
1665 | text,
1666 | manipulation.substring as string,
1667 | manipulation.extended
1668 | ? unescapeEscapedCharacters(manipulation.value as string)
1669 | : (manipulation.value as string),
1670 | );
1671 | } else {
1672 | text = text.replace(
1673 | manipulation.substring as string,
1674 | manipulation.extended
1675 | ? unescapeEscapedCharacters(manipulation.value as string)
1676 | : (manipulation.value as string),
1677 | );
1678 | }
1679 | break;
1680 | case 'extendedSubstring':
1681 | if (manipulation.replaceAll) {
1682 | text = replaceAll(
1683 | text,
1684 | unescapeEscapedCharacters(manipulation.substring as string),
1685 | manipulation.extended
1686 | ? unescapeEscapedCharacters(manipulation.value as string)
1687 | : (manipulation.value as string),
1688 | );
1689 | } else {
1690 | text = text.replace(
1691 | unescapeEscapedCharacters(manipulation.substring as string),
1692 | manipulation.extended
1693 | ? unescapeEscapedCharacters(manipulation.value as string)
1694 | : (manipulation.value as string),
1695 | );
1696 | }
1697 | break;
1698 | case 'regex': {
1699 | const regexMatch = (manipulation.regex as string).match(
1700 | new RegExp('^/(.*?)/([gimusy]*)$'),
1701 | );
1702 |
1703 | if (!regexMatch) {
1704 | text = text.replace(
1705 | new RegExp(manipulation.regex as string),
1706 | manipulation.extended
1707 | ? unescapeEscapedCharacters(manipulation.pattern as string)
1708 | : (manipulation.pattern as string),
1709 | );
1710 | } else if (regexMatch.length === 1) {
1711 | text = text.replace(
1712 | new RegExp(regexMatch[1]),
1713 | manipulation.extended
1714 | ? unescapeEscapedCharacters(manipulation.pattern as string)
1715 | : (manipulation.pattern as string),
1716 | );
1717 | } else {
1718 | text = text.replace(
1719 | new RegExp(regexMatch[1], regexMatch[2]),
1720 | manipulation.extended
1721 | ? unescapeEscapedCharacters(manipulation.pattern as string)
1722 | : (manipulation.pattern as string),
1723 | );
1724 | }
1725 | break;
1726 | }
1727 | case 'predefinedRule':
1728 | switch (manipulation.predefinedRule) {
1729 | case 'tags': {
1730 | const value = manipulation.extended
1731 | ? unescapeEscapedCharacters(manipulation.value as string)
1732 | : (manipulation.value as string);
1733 | text = stringStripHtml.stripHtml(text, {
1734 | stripRecognisedHTMLOnly: manipulation.onlyRecognisedHTML as boolean,
1735 | skipHtmlDecoding: true,
1736 | cb: (obj) => {
1737 | if (obj.deleteFrom && obj.deleteTo) {
1738 | if (obj.tag.slashPresent)
1739 | obj.rangesArr.push(
1740 | obj.deleteFrom,
1741 | obj.deleteTo,
1742 | `${value}${obj.insert ?? ''}`,
1743 | );
1744 | else
1745 | obj.rangesArr.push(
1746 | obj.deleteFrom,
1747 | obj.deleteTo,
1748 | `${value}${obj.insert ?? ''}`,
1749 | );
1750 | } else obj.rangesArr.push(obj.proposedReturn);
1751 | },
1752 | }).result;
1753 | break;
1754 | }
1755 | case 'characterGroups': {
1756 | const groups = [];
1757 | if (manipulation.newline)
1758 | groups.push(
1759 | buildRegexGroup(
1760 | '(\\r\\n|\\r|\\n)',
1761 | manipulation.newlineMin as number,
1762 | manipulation.newlineMax as number,
1763 | ),
1764 | );
1765 | if (manipulation.number)
1766 | groups.push(
1767 | buildRegexGroup(
1768 | '\\d',
1769 | manipulation.numberMin as number,
1770 | manipulation.numberMax as number,
1771 | ),
1772 | );
1773 | if (manipulation.alpha)
1774 | groups.push(
1775 | buildRegexGroup(
1776 | '[a-zA-Z]',
1777 | manipulation.alphaMin as number,
1778 | manipulation.alphaMax as number,
1779 | ),
1780 | );
1781 | if (manipulation.whitespace)
1782 | groups.push(
1783 | buildRegexGroup(
1784 | '\\s',
1785 | manipulation.whitespaceMin as number,
1786 | manipulation.whitespaceMax as number,
1787 | ),
1788 | );
1789 | text = text.replace(
1790 | new RegExp(groups.join('|'), 'g'),
1791 | manipulation.extended
1792 | ? unescapeEscapedCharacters(manipulation.value as string)
1793 | : (manipulation.value as string),
1794 | );
1795 | break;
1796 | }
1797 | default:
1798 | throw new NodeOperationError(
1799 | this.getNode(),
1800 | 'tags or characterGroups are valid options',
1801 | { itemIndex },
1802 | );
1803 | }
1804 | break;
1805 | default:
1806 | throw new NodeOperationError(
1807 | this.getNode(),
1808 | 'substring, extendedSubstring, regex or predefinedRule are valid options',
1809 | { itemIndex },
1810 | );
1811 | }
1812 | break;
1813 | case 'trim':
1814 | switch (manipulation.trim) {
1815 | case 'trimBoth':
1816 | text = manipulation.trimStringUnit
1817 | ? charsTrim(text, manipulation.trimString as string)
1818 | : trim(text, manipulation.trimString as string);
1819 | break;
1820 | case 'trimStart':
1821 | text = manipulation.trimStringUnit
1822 | ? charsTrimStart(text, manipulation.trimString as string)
1823 | : trimStart(text, manipulation.trimString as string);
1824 | break;
1825 | case 'trimEnd':
1826 | text = manipulation.trimStringUnit
1827 | ? charsTrimEnd(text, manipulation.trimString as string)
1828 | : trimEnd(text, manipulation.trimString as string);
1829 | break;
1830 | default:
1831 | throw new NodeOperationError(
1832 | this.getNode(),
1833 | 'trimBoth, trimStart or trimEnd are valid options',
1834 | { itemIndex },
1835 | );
1836 | }
1837 | break;
1838 | case 'pad':
1839 | if (manipulation.targetLength == null || (manipulation.targetLength as number) < 0)
1840 | throw new NodeOperationError(
1841 | this.getNode(),
1842 | 'The Target Length has to be set to at least 0 or higher!',
1843 | { itemIndex },
1844 | );
1845 | switch (manipulation.pad) {
1846 | case 'padStart':
1847 | text = text.padStart(
1848 | manipulation.targetLength as number,
1849 | manipulation.padString as string,
1850 | );
1851 | break;
1852 | case 'padEnd':
1853 | text = text.padEnd(
1854 | manipulation.targetLength as number,
1855 | manipulation.padString as string,
1856 | );
1857 | break;
1858 | default:
1859 | throw new NodeOperationError(
1860 | this.getNode(),
1861 | 'padStart or padEnd are valid options',
1862 | { itemIndex },
1863 | );
1864 | }
1865 | break;
1866 | case 'substring':
1867 | switch (manipulation.end) {
1868 | case 'complete':
1869 | text = text.substring(manipulation.startPosition as number);
1870 | break;
1871 | case 'position':
1872 | text = text.substring(
1873 | manipulation.startPosition as number,
1874 | manipulation.endPosition as number,
1875 | );
1876 | break;
1877 | case 'length':
1878 | if (manipulation.endLength == null || (manipulation.endLength as number) < 0) {
1879 | throw new NodeOperationError(
1880 | this.getNode(),
1881 | 'The Length has to be set to at least 0 or higher!',
1882 | { itemIndex },
1883 | );
1884 | }
1885 | if (((manipulation.startPosition as number | null) || 0) < 0)
1886 | text = text.substring(
1887 | manipulation.startPosition as number,
1888 | text.length +
1889 | (manipulation.startPosition as number) +
1890 | (manipulation.endLength as number),
1891 | );
1892 | else
1893 | text = text.substring(
1894 | manipulation.startPosition as number,
1895 | (manipulation.startPosition as number) + (manipulation.endLength as number),
1896 | );
1897 | break;
1898 | default:
1899 | throw new NodeOperationError(
1900 | this.getNode(),
1901 | 'complete, position or length are valid options',
1902 | { itemIndex },
1903 | );
1904 | }
1905 | break;
1906 | case 'repeat':
1907 | if (manipulation.times == null || (manipulation.times as number) < 0)
1908 | throw new NodeOperationError(
1909 | this.getNode(),
1910 | 'The Times has to be set to at least 0 or higher!',
1911 | { itemIndex },
1912 | );
1913 | text = text.repeat(manipulation.times as number);
1914 | break;
1915 | default:
1916 | throw new NodeOperationError(
1917 | this.getNode(),
1918 | 'decodeEncode, replace, trim, pad, substring or repeat are valid options',
1919 | { itemIndex },
1920 | );
1921 | }
1922 | }
1923 | switch (dataSource.writeOperation) {
1924 | case 'toFile':
1925 | newItemBinary[dataSource.destinationBinaryPropertyName as string] =
1926 | await this.helpers.prepareBinaryData(
1927 | iconv.encode(text, dataSource.fileEncodeWith as string, {
1928 | addBOM: dataSource.fileAddBOM as boolean,
1929 | }),
1930 | dataSource.fileName as string,
1931 | dataSource.mimeType as string,
1932 | );
1933 | break;
1934 | case 'toJSON':
1935 | set(newItemJson, dataSource.destinationKey as string, text);
1936 | break;
1937 | default:
1938 | throw new NodeOperationError(this.getNode(), 'toFile or toJSON are valid options', {
1939 | itemIndex,
1940 | });
1941 | }
1942 | }
1943 | }
1944 | returnData.push({
1945 | json: newItemJson,
1946 | binary: Object.keys(newItemBinary).length === 0 ? undefined : newItemBinary,
1947 | pairedItem: {
1948 | item: itemIndex,
1949 | }
1950 | });
1951 | }
1952 |
1953 | return this.prepareOutputData(returnData);
1954 | }
1955 | }
1956 |
--------------------------------------------------------------------------------