Skip to content

Commit fb2ae44

Browse files
authored
Merge branch 'main' into dependabot/github_actions/actions/upload-artifact-6
2 parents d4d540d + 05a9e8e commit fb2ae44

19 files changed

Lines changed: 1559 additions & 349 deletions

.github/workflows/automerge.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
repository-projects: write
1414
if: github.actor == 'dependabot[bot]'
1515
steps:
16-
- uses: actions/checkout@v4
16+
- uses: actions/checkout@v6
1717
- run: gh pr merge ${{ github.event.pull_request.html_url }} --auto --squash
1818
- run: gh pr review ${{ github.event.pull_request.html_url }} --approve
1919
env:

.github/workflows/ci.yml

Lines changed: 37 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,65 +1,67 @@
11
name: CI
22

3-
on: [push]
3+
on:
4+
push:
5+
branches:
6+
- main
7+
pull_request:
48

59
jobs:
610
test:
711
runs-on: ubuntu-latest
812
strategy:
913
fail-fast: false
1014
matrix:
11-
node-version: [14.x, 16.x, 18.x, 20.x, 22.x]
15+
node-version: [14.x, 16.x, 18.x, 20.x, 22.x, 24.x]
1216
steps:
1317
- name: Use Node.js ${{ matrix.node-version }}
1418
uses: actions/setup-node@v4
1519
with:
1620
node-version: ${{ matrix.node-version }}
17-
- uses: actions/checkout@v4
21+
- uses: actions/checkout@v6
1822
- run: npm ci --ignore-scripts
1923
- run: npm run build
2024
- run: npm run test
21-
25+
2226
lint:
2327
runs-on: ubuntu-latest
2428
strategy:
2529
fail-fast: false
2630
matrix:
27-
node-version: [22.x]
31+
node-version: [24.x]
2832
steps:
2933
- name: Use Node.js ${{ matrix.node-version }}
3034
uses: actions/setup-node@v4
3135
with:
3236
node-version: ${{ matrix.node-version }}
33-
- uses: actions/checkout@v4
37+
- uses: actions/checkout@v6
3438
- run: npm ci --ignore-scripts
3539
- run: npm run build
3640
- run: npm run lint
37-
41+
3842
spec:
3943
runs-on: ubuntu-latest
4044
strategy:
4145
fail-fast: false
4246
matrix:
43-
node-version: [12.x, 14.x, 16.x, 18.x, 20.x, 22.x]
47+
node-version: [12.x, 14.x, 16.x, 18.x, 20.x, 22.x, 24.x]
48+
spec: [1-1-turtle, 1-1-ntriples, 1-1-nquads, 1-1-trig, 1-2-turtle, 1-2-ntriples, 1-2-nquads, 1-2-trig]
4449
steps:
4550
- name: Use Node.js ${{ matrix.node-version }}
4651
uses: actions/setup-node@v4
4752
with:
4853
node-version: ${{ matrix.node-version }}
49-
- uses: actions/checkout@v4
54+
- uses: actions/checkout@v6
5055
- run: npm ci
51-
- run: npm run spec-turtle
52-
- run: npm run spec-ntriples
53-
- run: npm run spec-nquads
54-
- run: npm run spec-trig
56+
- run: npm run spec-${{ matrix.spec }}
5557

5658
docs:
5759
runs-on: ubuntu-latest
5860
steps:
59-
- uses: actions/checkout@v4
61+
- uses: actions/checkout@v6
6062
- uses: actions/setup-node@v4
6163
with:
62-
node-version: 14.x
64+
node-version: 24.x
6365
- run: npm ci --ignore-scripts
6466
- run: npm run docs
6567
- uses: actions/upload-artifact@v6
@@ -70,6 +72,22 @@ jobs:
7072
docs
7173
retention-days: 1
7274

75+
summary:
76+
# Summary job that can be used as a single required check in branch protection
77+
if: always()
78+
needs:
79+
- test
80+
- lint
81+
- spec
82+
- docs
83+
runs-on: ubuntu-latest
84+
permissions: {}
85+
steps:
86+
- name: Decide whether all required jobs succeeded or failed
87+
uses: re-actors/alls-green@release/v1
88+
with:
89+
jobs: ${{ toJSON(needs) }}
90+
7391
gh-pages:
7492
needs:
7593
- test
@@ -96,15 +114,15 @@ jobs:
96114
packages: write
97115
issues: write
98116
pull-requests: write
117+
id-token: write
99118
steps:
100-
- uses: actions/checkout@v4
119+
- uses: actions/checkout@v6
101120
- uses: actions/setup-node@v4
102121
with:
103-
node-version: 22.x
122+
node-version: 24.x
104123
- run: npm ci
105124
- run: npm run build
106125
- name: Release
107126
env:
108-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
109127
GITHUB_TOKEN: ${{ github.token }}
110-
run: npx semantic-release@22
128+
run: npx semantic-release@25

README.md

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,22 +4,20 @@
44
[![npm version](https://badge.fury.io/js/n3.svg)](https://www.npmjs.com/package/n3)
55
[![DOI](https://zenodo.org/badge/3058202.svg)](https://zenodo.org/badge/latestdoi/3058202)
66

7-
The N3.js library is an implementation of the [RDF.js low-level specification](http://rdf.js.org/) that lets you handle [RDF](https://www.w3.org/TR/rdf-primer/) in JavaScript easily.
7+
The N3.js library is an implementation of the [RDF.js low-level specification](http://rdf.js.org/) that lets you handle [RDF 1.2](https://www.w3.org/TR/rdf-primer/) in JavaScript easily.
88
It offers:
99

1010
- [**Parsing**](#parsing) triples/quads from
1111
[Turtle](https://www.w3.org/TR/turtle/),
1212
[TriG](https://www.w3.org/TR/trig/),
1313
[N-Triples](https://www.w3.org/TR/n-triples/),
1414
[N-Quads](https://www.w3.org/TR/n-quads/),
15-
[RDF-star](https://www.w3.org/2021/12/rdf-star.html)
1615
and [Notation3 (N3)](https://www.w3.org/TeamSubmission/n3/)
1716
- [**Writing**](#writing) triples/quads to
1817
[Turtle](https://www.w3.org/TR/turtle/),
1918
[TriG](https://www.w3.org/TR/trig/),
2019
[N-Triples](https://www.w3.org/TR/n-triples/),
21-
[N-Quads](https://www.w3.org/TR/n-quads/)
22-
and [RDF-star](https://www.w3.org/2021/12/rdf-star.html)
20+
and [N-Quads](https://www.w3.org/TR/n-quads/)
2321
- [**Storage**](#storing) of triples/quads in memory
2422

2523
Parsing and writing is:
@@ -418,19 +416,17 @@ The N3.js parser and writer is fully compatible with the following W3C specifica
418416
[EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-ntriples.ttl)
419417
- [RDF 1.1 N-Quads](https://www.w3.org/TR/n-quads/)
420418
[EARL report](https://raw.githubusercontent.com/rdfjs/N3.js/earl/n3js-earl-report-nquads.ttl)
419+
- [RDF 1.2 Turtle](https://www.w3.org/TR/rdf12-turtle/)
420+
- [RDF 1.2 TriG](https://www.w3.org/TR/rdf12-trig/)
421+
- [RDF 1.2 N-Triples](https://www.w3.org/TR/rdf12-n-triples/)
422+
- [RDF 1.2 N-Quads](https://www.w3.org/TR/rdf12-n-quads/)
421423

422424
In addition, the N3.js parser also supports [Notation3 (N3)](https://www.w3.org/TeamSubmission/n3/) (no official specification yet).
423425

424-
The N3.js parser and writer are also fully compatible with the RDF-star variants
425-
of the W3C specifications.
426-
427426
The default mode is permissive
428-
and allows a mixture of different syntaxes, including RDF-star.
427+
and allows a mixture of different syntaxes.
429428
Pass a `format` option to the constructor with the name or MIME type of a format
430429
for strict, fault-intolerant behavior.
431-
If a format string contains `star` or `*`
432-
(e.g., `turtlestar` or `TriG*`),
433-
RDF-star support for that format will be enabled.
434430

435431
### Interface specifications
436432
The N3.js submodules are compatible with the following [RDF.js](http://rdf.js.org) interfaces:

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 22 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "n3",
3-
"version": "1.17.3",
3+
"version": "2.0.1",
44
"description": "Lightning fast, asynchronous, streaming Turtle / N3 / RDF library.",
55
"author": "Ruben Verborgh <ruben.verborgh@gmail.com>",
66
"keywords": [
@@ -54,16 +54,27 @@
5454
"test": "jest",
5555
"lint": "eslint src perf test spec",
5656
"prepare": "npm run build",
57-
"spec": "npm run spec-turtle && npm run spec-ntriples && npm run spec-nquads && npm run spec-trig",
58-
"spec-earl": "npm run spec-earl-turtle && npm run spec-earl-ntriples && npm run spec-earl-nquads && npm run spec-earl-trig",
59-
"spec-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-triples/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/",
60-
"spec-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-quads/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/",
61-
"spec-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-turtle/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/",
62-
"spec-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-trig/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/",
63-
"spec-earl-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-triples/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-ntriples.ttl",
64-
"spec-earl-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-quads/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-nquads.ttl",
65-
"spec-earl-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-turtle/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-turtle.ttl",
66-
"spec-earl-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-trig/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-trig.ttl",
57+
"spec": "npm run spec-1-1 && npm run spec-1-2",
58+
"spec-1-1": "npm run spec-1-1-turtle && npm run spec-1-1-ntriples && npm run spec-1-1-nquads && npm run spec-1-1-trig",
59+
"spec-1-1-earl": "npm run spec-1-1-earl-turtle && npm run spec-1-1-earl-ntriples && npm run spec-1-1-earl-nquads && npm run spec-1-1-earl-trig",
60+
"spec-1-1-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-triples/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/",
61+
"spec-1-1-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-quads/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/",
62+
"spec-1-1-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-turtle/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ --skip \"(bad-numeric-escape|bareword_decimal)\"",
63+
"spec-1-1-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-trig/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ --skip \"(bad-numeric-escape|IRI-resolution-01)\"",
64+
"spec-1-1-earl-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-triples/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-ntriples.ttl",
65+
"spec-1-1-earl-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-n-quads/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-nquads.ttl",
66+
"spec-1-1-earl-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-turtle/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-turtle.ttl",
67+
"spec-1-1-earl-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf11/rdf-trig/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-trig.ttl",
68+
"spec-1-2": "npm run spec-1-2-turtle && npm run spec-1-2-ntriples && npm run spec-1-2-nquads && npm run spec-1-2-trig",
69+
"spec-1-2-earl": "npm run spec-1-2-earl-turtle && npm run spec-1-2-earl-ntriples && npm run spec-1-2-earl-nquads && npm run spec-1-2-earl-trig",
70+
"spec-1-2-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-triples/syntax/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/",
71+
"spec-1-2-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-quads/syntax/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/",
72+
"spec-1-2-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-turtle/syntax/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/",
73+
"spec-1-2-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-trig/syntax/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/",
74+
"spec-1-2-earl-ntriples": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-triples/syntax/manifest.ttl -i '{ \"format\": \"n-triples\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-ntriples.ttl",
75+
"spec-1-2-earl-nquads": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-n-quads/syntax/manifest.ttl -i '{ \"format\": \"n-quads\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-nquads.ttl",
76+
"spec-1-2-earl-turtle": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-turtle/syntax/manifest.ttl -i '{ \"format\": \"turtle\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-turtle.ttl",
77+
"spec-1-2-earl-trig": "rdf-test-suite spec/parser.js https://w3c.github.io/rdf-tests/rdf/rdf12/rdf-trig/syntax/manifest.ttl -i '{ \"format\": \"trig\" }' -c .rdf-test-suite-cache/ -o earl -p spec/earl-meta.json > spec/earl-trig.ttl",
6778
"spec-clean": "rm -r .rdf-test-suite-cache/",
6879
"docs": "cd src && docco *.js -o ../docs && cd .."
6980
},

perf/N3Parser-perf.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ const TEST = `- Parsing file ${filename}`;
1616
console.time(TEST);
1717

1818
let count = 0;
19-
new N3.Parser({ baseIRI: base }).parse(fs.createReadStream(filename), (error, quad) => {
19+
new N3.Parser({ baseIRI: base, format: 'text/turtle' }).parse(fs.createReadStream(filename), (error, quad) => {
2020
assert(!error, error);
2121
if (quad)
2222
count++;

spec/parser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ const { StreamParser } = require('..');
55
module.exports = {
66
parse: function (data, baseIRI, options) {
77
return require('arrayify-stream').arrayifyStream(require('streamify-string')(data).pipe(
8-
new StreamParser(Object.assign({ baseIRI: baseIRI }, options))));
8+
new StreamParser(Object.assign({ baseIRI: baseIRI, parseUnsupportedVersions: true }, options))));
99
},
1010
};

src/IRIs.js

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,13 @@ export default {
1111
string: `${XSD}string`,
1212
},
1313
rdf: {
14-
type: `${RDF}type`,
15-
nil: `${RDF}nil`,
16-
first: `${RDF}first`,
17-
rest: `${RDF}rest`,
18-
langString: `${RDF}langString`,
14+
type: `${RDF}type`,
15+
nil: `${RDF}nil`,
16+
first: `${RDF}first`,
17+
rest: `${RDF}rest`,
18+
langString: `${RDF}langString`,
19+
dirLangString: `${RDF}dirLangString`,
20+
reifies: `${RDF}reifies`,
1921
},
2022
owl: {
2123
sameAs: 'http://www.w3.org/2002/07/owl#sameAs',

src/N3DataFactory.js

Lines changed: 39 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -88,8 +88,18 @@ export class Literal extends Term {
8888
// Find the last quotation mark (e.g., '"abc"@en-us')
8989
const id = this.id;
9090
let atPos = id.lastIndexOf('"') + 1;
91+
const dirPos = id.lastIndexOf('--');
9192
// If "@" it follows, return the remaining substring; empty otherwise
92-
return atPos < id.length && id[atPos++] === '@' ? id.substr(atPos).toLowerCase() : '';
93+
return atPos < id.length && id[atPos++] === '@' ? (dirPos > atPos ? id.substr(0, dirPos) : id).substr(atPos).toLowerCase() : '';
94+
}
95+
96+
// ### The direction of this literal
97+
get direction() {
98+
// Find the last double dash after the closing quote (e.g., '"abc"@en-us--ltr')
99+
const id = this.id;
100+
const endPos = id.lastIndexOf('"');
101+
const dirPos = id.lastIndexOf('--');
102+
return dirPos > endPos && dirPos + 2 < id.length ? id.substr(dirPos + 2).toLowerCase() : '';
93103
}
94104

95105
// ### The datatype IRI of this literal
@@ -104,8 +114,8 @@ export class Literal extends Term {
104114
const char = dtPos < id.length ? id[dtPos] : '';
105115
// If "^" it follows, return the remaining substring
106116
return char === '^' ? id.substr(dtPos + 2) :
107-
// If "@" follows, return rdf:langString; xsd:string otherwise
108-
(char !== '@' ? xsd.string : rdf.langString);
117+
// If "@" follows, return rdf:langString or rdf:dirLangString; xsd:string otherwise
118+
(char !== '@' ? xsd.string : (id.indexOf('--', dtPos) > 0 ? rdf.dirLangString : rdf.langString));
109119
}
110120

111121
// ### Returns whether this object represents the same term as the other
@@ -119,14 +129,16 @@ export class Literal extends Term {
119129
this.termType === other.termType &&
120130
this.value === other.value &&
121131
this.language === other.language &&
132+
((this.direction === other.direction) || (this.direction === '' && !other.direction)) &&
122133
this.datatype.value === other.datatype.value;
123134
}
124135

125136
toJSON() {
126137
return {
127-
termType: this.termType,
128-
value: this.value,
129-
language: this.language,
138+
termType: this.termType,
139+
value: this.value,
140+
language: this.language,
141+
direction: this.direction,
130142
datatype: { termType: 'NamedNode', value: this.datatypeString },
131143
};
132144
}
@@ -216,9 +228,22 @@ export function termFromId(id, factory, nested) {
216228
return factory.literal(id.substr(1, id.length - 2));
217229
// Literal with datatype or language
218230
const endPos = id.lastIndexOf('"', id.length - 1);
231+
let languageOrDatatype;
232+
if (id[endPos + 1] === '@') {
233+
languageOrDatatype = id.substr(endPos + 2);
234+
const dashDashIndex = languageOrDatatype.lastIndexOf('--');
235+
if (dashDashIndex > 0 && dashDashIndex < languageOrDatatype.length) {
236+
languageOrDatatype = {
237+
language: languageOrDatatype.substr(0, dashDashIndex),
238+
direction: languageOrDatatype.substr(dashDashIndex + 2),
239+
};
240+
}
241+
}
242+
else {
243+
languageOrDatatype = factory.namedNode(id.substr(endPos + 3));
244+
}
219245
return factory.literal(id.substr(1, endPos - 1),
220-
id[endPos + 1] === '@' ? id.substr(endPos + 2)
221-
: factory.namedNode(id.substr(endPos + 3)));
246+
languageOrDatatype);
222247
case '[':
223248
id = JSON.parse(id);
224249
break;
@@ -255,7 +280,7 @@ export function termToId(term, nested) {
255280
case 'Variable': return `?${term.value}`;
256281
case 'DefaultGraph': return '';
257282
case 'Literal': return `"${term.value}"${
258-
term.language ? `@${term.language}` :
283+
term.language ? `@${term.language}${term.direction ? `--${term.direction}` : ''}` :
259284
(term.datatype && term.datatype.value !== xsd.string ? `^^${term.datatype.value}` : '')}`;
260285
case 'Quad':
261286
const res = [
@@ -350,6 +375,11 @@ function literal(value, languageOrDataType) {
350375
if (typeof languageOrDataType === 'string')
351376
return new Literal(`"${value}"@${languageOrDataType.toLowerCase()}`);
352377

378+
// Create a language-tagged string with base direction
379+
if (languageOrDataType !== undefined && !('termType' in languageOrDataType)) {
380+
return new Literal(`"${value}"@${languageOrDataType.language.toLowerCase()}${languageOrDataType.direction ? `--${languageOrDataType.direction.toLowerCase()}` : ''}`);
381+
}
382+
353383
// Automatically determine datatype for booleans and numbers
354384
let datatype = languageOrDataType ? languageOrDataType.value : '';
355385
if (datatype === '') {

0 commit comments

Comments
 (0)