From 198ae073cbb7787fcde1f4ca5306718b72f24297 Mon Sep 17 00:00:00 2001 From: Frazer Smith Date: Fri, 26 Jan 2024 13:26:47 +0000 Subject: [PATCH 01/34] chore(misc): update `Match` type with `freeze` property --- types/misc.d.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/types/misc.d.ts b/types/misc.d.ts index 91fb6cd17..687b70006 100644 --- a/types/misc.d.ts +++ b/types/misc.d.ts @@ -83,6 +83,7 @@ export interface Match { unTag?: string | string[], group?: string | number, reason?: string, + freeze?: boolean } export interface Net { @@ -91,4 +92,4 @@ export interface Net { isNet: boolean } -export type ParsedMatch = object[] \ No newline at end of file +export type ParsedMatch = object[] From 60fc6e4d6db5b7f116c4c76eb8e3c35939ec8802 Mon Sep 17 00:00:00 2001 From: Frazer Smith Date: Fri, 26 Jan 2024 14:23:25 +0000 Subject: [PATCH 02/34] chore(misc): update `Plugin` type with `frozen` property --- types/misc.d.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/types/misc.d.ts b/types/misc.d.ts index 687b70006..df20d929b 100644 --- a/types/misc.d.ts +++ b/types/misc.d.ts @@ -67,6 +67,7 @@ export interface Plugin { hooks?: string[], tags?: object, words?: object, + frozen?: {[key: string]: string}, lib?: () => object, api?: (fn: (view: any) => {}) => void, //should be View mutate?: (fn: (world: object) => {}) => void, From 20710abe9570191d8441249978e72345c3a725a9 Mon Sep 17 00:00:00 2001 From: Frazer Smith Date: Fri, 26 Jan 2024 14:28:39 +0000 Subject: [PATCH 03/34] ci: bump github actions --- .github/workflows/build-and-test.yml | 15 ++++----------- .github/workflows/coverage.yml | 13 ++++--------- .github/workflows/release.yml | 15 ++++----------- 3 files changed, 12 insertions(+), 31 deletions(-) diff --git a/.github/workflows/build-and-test.yml b/.github/workflows/build-and-test.yml index c54066345..0a350bc10 100644 --- a/.github/workflows/build-and-test.yml +++ b/.github/workflows/build-and-test.yml @@ -12,21 +12,14 @@ jobs: os: [macos-latest, ubuntu-latest, windows-latest] steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: use node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 + uses: actions/setup-node@v4 with: node-version: ${{ matrix.node-version }} - - - name: cache dependencies - uses: actions/cache@v3 - with: - path: ~/.npm - key: ${{ runner.os }}-npm-${{ matrix.node-version }}-${{ hashFiles('package-lock.json') }} - restore-keys: | - ${{ runner.os }}-npm-${{ matrix.node-version }}- - ${{ runner.os }}-npm- + cache: npm + cache-dependency-path: package-lock.json - name: install run: | diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index e74442409..ff15cc052 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -11,17 +11,12 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 with: node-version: '16' - - - uses: actions/cache@v3 - with: - path: ~/.npm - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- + cache: npm + cache-dependency-path: package-lock.json - run: npm ci - run: npm i -g c8 codecov diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6c250dcf1..874126a20 100755 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,20 +14,13 @@ jobs: # delegate to the build- and-test workflow, but I haven't found a way to do # that yet. steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - - uses: actions/setup-node@v3 + - uses: actions/setup-node@v4 with: node-version: 16.x - - - name: cache dependencies - uses: actions/cache@v3 - with: - path: ~/.npm - key: ${{ runner.os }}-npm-10.x-${{ hashFiles('package-lock.json') }} - restore-keys: | - ${{ runner.os }}-npm-10.x- - ${{ runner.os }}-npm- + cache: npm + cache-dependency-path: package-lock.json - name: install run: | From 4e4b42d3bcf54bcfd4a575babf51ec711531e4d9 Mon Sep 17 00:00:00 2001 From: Frazer Smith Date: Tue, 6 Feb 2024 13:31:01 +0000 Subject: [PATCH 04/34] ci(release): update workflow --- .github/workflows/release.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 874126a20..dd53c7eb6 100755 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -7,6 +7,9 @@ on: jobs: release: runs-on: ubuntu-latest + permissions: + contents: read + id-token: write # Note that these steps are *identical* to build-and-test (with the caveat # that build-and-test uses several versions of Node, and Release only uses @@ -15,12 +18,13 @@ jobs: # that yet. steps: - uses: actions/checkout@v4 + with: + persist-credentials: false - uses: actions/setup-node@v4 with: node-version: 16.x cache: npm - cache-dependency-path: package-lock.json - name: install run: | @@ -51,6 +55,6 @@ jobs: - name: publish run: | echo '//registry.npmjs.org/:_authToken=${NPM_TOKEN}' > .npmrc - npm publish + npm publish --access public --provenance env: NPM_TOKEN: ${{ secrets.NPM_TOKEN }} From 9e3320eb323b547b63402321edff2158e21c83cf Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 7 Feb 2024 13:16:36 -0500 Subject: [PATCH 05/34] payload proof of concept --- plugins/_experiments/payload/README.md | 45 +++++++++++++++++++ plugins/_experiments/payload/package.json | 45 +++++++++++++++++++ plugins/_experiments/payload/rollup.config.js | 23 ++++++++++ plugins/_experiments/payload/scratch.js | 11 +++++ plugins/_experiments/payload/src/plugin.js | 35 +++++++++++++++ scratch.js | 18 ++++++++ tests/three/sweep-tag.test.js | 30 ++++++++++--- 7 files changed, 200 insertions(+), 7 deletions(-) create mode 100644 plugins/_experiments/payload/README.md create mode 100644 plugins/_experiments/payload/package.json create mode 100644 plugins/_experiments/payload/rollup.config.js create mode 100644 plugins/_experiments/payload/scratch.js create mode 100644 plugins/_experiments/payload/src/plugin.js diff --git a/plugins/_experiments/payload/README.md b/plugins/_experiments/payload/README.md new file mode 100644 index 000000000..b4c69805f --- /dev/null +++ b/plugins/_experiments/payload/README.md @@ -0,0 +1,45 @@ +
+ + +
An ad-hoc datastore for compromise
+ + + + + + + + + + + +
+ npm install compromise-payload +
+
+ + + + +This plugin provides a facilty for storing a retreiving more complex data than tags, for compromise documents and matches. + +### payload + +```js +import plg from 'compromise-payload' +nlp.extend(plg) + +let doc = nlp('i saw John Lennon play the guitar') +doc.people().forEach(m => { + if (m.has('lennon')) { + m.addPayload({ height: `5'11` }) + } + if (m.has('ringo')) { + m.addPayload({ height: `5'8` }) + } +}) +doc.getPayloads() +// [...] +``` + +MIT diff --git a/plugins/_experiments/payload/package.json b/plugins/_experiments/payload/package.json new file mode 100644 index 000000000..6872c1fa7 --- /dev/null +++ b/plugins/_experiments/payload/package.json @@ -0,0 +1,45 @@ +{ + "name": "compromise-payload", + "description": "plugin for nlp-compromise", + "version": "0.1.0", + "author": "Spencer Kelly (http://spencermounta.in)", + "main": "./src/plugin.js", + "unpkg": "./builds/compromise-payload.min.js", + "type": "module", + "sideEffects": false, + "types": "./index.d.ts", + "exports": { + ".": { + "import": "./src/plugin.js", + "require": "./builds/compromise-payload.cjs", + "types": "./index.d.ts" + } + }, + "repository": { + "type": "git", + "url": "git://github.com/spencermountain/compromise.git" + }, + "homepage": "https://github.com/spencermountain/compromise/tree/master/plugins/payload", + "scripts": { + "test": "tape \"./tests/**/*.test.js\" | tap-dancer --color always", + "testb": "TESTENV=prod tape \"./tests/**/*.test.js\" | tap-dancer --color always", + "watch": "amble ./scratch.js", + "generate": "node ./scripts/generate/index.js", + "stat": "node ./scripts/stat.js", + "perf": "node ./scripts/perf.js", + "build": "rollup -c --silent" + }, + "peerDependencies": { + "compromise": ">=14.0.0" + }, + "dependencies": { + "efrt": "^2.5.0" + }, + "files": [ + "builds/", + "src/", + "index.d.ts" + ], + "devDependencies": {}, + "license": "MIT" +} diff --git a/plugins/_experiments/payload/rollup.config.js b/plugins/_experiments/payload/rollup.config.js new file mode 100644 index 000000000..b79861347 --- /dev/null +++ b/plugins/_experiments/payload/rollup.config.js @@ -0,0 +1,23 @@ +import terser from '@rollup/plugin-terser' +import sizeCheck from 'rollup-plugin-filesize-check' +import { nodeResolve } from '@rollup/plugin-node-resolve' + +const opts = { keep_classnames: true, module: true } + +export default [ + { + input: 'src/plugin.js', + output: [{ file: 'builds/compromise-payload.cjs', format: 'umd', name: 'compromisePayload' }], + plugins: [nodeResolve(), sizeCheck({ expect: 492, warn: 15 })], + }, + { + input: 'src/plugin.js', + output: [{ file: 'builds/compromise-payload.min.js', format: 'umd', name: 'compromisePayload' }], + plugins: [nodeResolve(), terser(opts), sizeCheck({ expect: 387, warn: 15 })], + }, + { + input: 'src/plugin.js', + output: [{ file: 'builds/compromise-payload.mjs', format: 'esm' }], + plugins: [nodeResolve(), terser(opts), sizeCheck({ expect: 387, warn: 15 })], + }, +] diff --git a/plugins/_experiments/payload/scratch.js b/plugins/_experiments/payload/scratch.js new file mode 100644 index 000000000..0dcc9f03e --- /dev/null +++ b/plugins/_experiments/payload/scratch.js @@ -0,0 +1,11 @@ +import nlp from '../../../src/three.js' +import plugin from './src/plugin.js' +nlp.extend(plugin) + +let doc = nlp('i saw John Lennon play the guitar') +doc.people().forEach(m => { + if (m.has('lennon')) { + m.addPayload({ height: `5'11`, instrument: 'guitar' }) + } +}) +console.log(doc.getPayloads()) diff --git a/plugins/_experiments/payload/src/plugin.js b/plugins/_experiments/payload/src/plugin.js new file mode 100644 index 000000000..8def06b8f --- /dev/null +++ b/plugins/_experiments/payload/src/plugin.js @@ -0,0 +1,35 @@ +export default { + //establish payload db + mutate: function (world) { + world.model.one.db = {} + }, + + api: function (View) { + /** add data about our current matches */ + View.prototype.addPayload = function (data) { + let db = this.world.model.one.db || {} + this.fullPointer.forEach(ptr => { + let n = ptr[0] + db[n] = db[n] || [] + db[n].push(ptr, data) + }) + return this + } + /** return any data on our given matches */ + View.prototype.getPayloads = function () { + let res = [] + let db = this.world.model.one.db || {} + this.fullPointer.forEach(ptr => { + let n = ptr[0] + if (db.hasOwnProperty(n)) { + let match = this.update(db[n][0]) + res = res.concat({ + match, + payload: db[n][1], + }) + } + }) + return res + } + }, +} diff --git a/scratch.js b/scratch.js index 62254c242..d000cd9d3 100644 --- a/scratch.js +++ b/scratch.js @@ -7,6 +7,24 @@ import nlp from './src/three.js' // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() +nlp('this is (kinda) messy').not('this').parentheses().debug() + +// nlp('15-ounce (15-ounce)').debug() + +let doc = nlp('2 to 3 people') +doc.match('#Value [to] #Value', 0).replaceWith('-') +console.log(doc.text()) + +// let doc = nlp('one foo two foo') +// let m = doc.terms() +// m = m.join().debug() + +// let doc = nlp("one two John Carreyrou three four Roger Moore") +// let m = doc.terms() +// let people = doc.people() +// m = m.joinIf(people) +// return m.out('array') + let arr = [ // 'I left the window open for fresh air.', // 'Iran also stands accused of aiding terrorism ', diff --git a/tests/three/sweep-tag.test.js b/tests/three/sweep-tag.test.js index 66980d4fe..6fd56c7ec 100644 --- a/tests/three/sweep-tag.test.js +++ b/tests/three/sweep-tag.test.js @@ -3,9 +3,7 @@ import nlp from './_lib.js' const here = '[three/sweep-tag] ' test('cache-one:', function (t) { - let matches = [ - { match: '(he|she|they|#Noun) (has|have) (a|an)' } - ] + let matches = [{ match: '(he|she|they|#Noun) (has|have) (a|an)' }] let net = nlp.buildNet(matches) let doc = nlp(`you have a appointment`) t.equal(doc.has(net), true, here + 'Noun cache') @@ -13,12 +11,30 @@ test('cache-one:', function (t) { }) test('sweep-root:', function (t) { - let matches = [ - { match: '{appointment}' } - ] + let matches = [{ match: '{appointment}' }] let net = nlp.buildNet(matches) let doc = nlp(`you have some appointments`) doc.compute('root') t.equal(doc.has(net), true, here + 'root cache') t.end() -}) \ No newline at end of file +}) + +test('sweep-freeze:', function (t) { + let matches = [ + { match: 'juicy fruit', tag: 'Singular', freeze: true }, + { match: 'front steps', tag: 'Plural', freeze: true }, + { match: 'juicy', tag: 'Adjective' }, + { match: 'front', tag: 'Adjective' }, + ] + let doc = nlp(`i ate juicy fruit on the front steps`) + let net = nlp.buildNet(matches) + doc.sweep(net) + let m = doc.match('juicy fruit') + t.equal(m.has('#Singular #Singular'), true, here + 'juicy fruit') + t.equal(m.has('#Adjective'), false, here + 'juicy fruit frozen') + + m = doc.match('front steps') + t.equal(m.has('#Plural #Plural'), true, here + 'front steps') + t.equal(m.has('#Adjective'), false, here + 'front steps frozen') + t.end() +}) From d0f88d7f35da2a15d26a0662c703d3812a915f27 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 7 Feb 2024 13:27:00 -0500 Subject: [PATCH 06/34] cleanup api --- plugins/_experiments/payload/scratch.js | 10 +++++--- plugins/_experiments/payload/src/plugin.js | 30 ++++++++++++---------- 2 files changed, 23 insertions(+), 17 deletions(-) diff --git a/plugins/_experiments/payload/scratch.js b/plugins/_experiments/payload/scratch.js index 0dcc9f03e..30cb458b6 100644 --- a/plugins/_experiments/payload/scratch.js +++ b/plugins/_experiments/payload/scratch.js @@ -2,10 +2,14 @@ import nlp from '../../../src/three.js' import plugin from './src/plugin.js' nlp.extend(plugin) -let doc = nlp('i saw John Lennon play the guitar') +let doc = nlp('i saw John Lennon, and tom cruise.') + doc.people().forEach(m => { - if (m.has('lennon')) { - m.addPayload({ height: `5'11`, instrument: 'guitar' }) + if (m.has('john lennon')) { + m.addPayload({ height: `5'11` }) + } + if (m.has('tom cruise')) { + m.addPayload({ height: `5'8` }) } }) console.log(doc.getPayloads()) diff --git a/plugins/_experiments/payload/src/plugin.js b/plugins/_experiments/payload/src/plugin.js index 8def06b8f..78c3a4ce1 100644 --- a/plugins/_experiments/payload/src/plugin.js +++ b/plugins/_experiments/payload/src/plugin.js @@ -5,16 +5,6 @@ export default { }, api: function (View) { - /** add data about our current matches */ - View.prototype.addPayload = function (data) { - let db = this.world.model.one.db || {} - this.fullPointer.forEach(ptr => { - let n = ptr[0] - db[n] = db[n] || [] - db[n].push(ptr, data) - }) - return this - } /** return any data on our given matches */ View.prototype.getPayloads = function () { let res = [] @@ -22,14 +12,26 @@ export default { this.fullPointer.forEach(ptr => { let n = ptr[0] if (db.hasOwnProperty(n)) { - let match = this.update(db[n][0]) - res = res.concat({ - match, - payload: db[n][1], + db[n].forEach(obj => { + res = res.concat({ + match: this.update(obj.ptr), + val: obj.val, + }) }) } }) return res } + + /** add data about our current matches */ + View.prototype.addPayload = function (val) { + let db = this.world.model.one.db || {} + this.fullPointer.forEach(ptr => { + let n = ptr[0] + db[n] = db[n] || [] + db[n].push({ ptr, val }) + }) + return this + } }, } From 3dc8c6f0b13069fa706af3ca6c3783360fa11c92 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 7 Feb 2024 15:28:07 -0500 Subject: [PATCH 07/34] has match issue --- package-lock.json | 41 ++++++++++++---------- package.json | 2 +- plugins/_experiments/payload/scratch.js | 3 +- plugins/_experiments/payload/src/plugin.js | 12 ++++--- scratch.js | 14 +++++--- tests/one/match/doc-match.test.js | 14 +++++++- 6 files changed, 56 insertions(+), 30 deletions(-) diff --git a/package-lock.json b/package-lock.json index 907a5a672..033f3cb84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -25,7 +25,7 @@ "rollup-plugin-filesize-check": "^0.0.2", "shelljs": "0.8.5", "tap-dancer": "0.3.4", - "tape": "5.7.3" + "tape": "5.7.4" }, "engines": { "node": ">=12.0.0" @@ -1458,13 +1458,16 @@ } }, "node_modules/has-dynamic-import": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz", - "integrity": "sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.1.0.tgz", + "integrity": "sha512-su0anMkNEnJKZ/rB99jn3y6lV/J8Ro96hBJ28YAeVzj5rWxH+YL/AdCyiYYA1HDLV9YhmvqpWSJJj2KLo1MX6g==", "dev": true, "dependencies": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -2802,9 +2805,9 @@ } }, "node_modules/tape": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/tape/-/tape-5.7.3.tgz", - "integrity": "sha512-un2/TkloCBIxbrLac88Z9WElQ85WLE4t+jjfgrmxrlInWGYQf9r9Wbycc/nNP3zap6GBb7qln7h85QoNBNZdHg==", + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.7.4.tgz", + "integrity": "sha512-uaigP+5H9+E8aaMLKMbGkDd33G5TKu4UFpapqT7um+8xSHQQUS2lJNd+hTj9fFVQLg8bmcIofwc8b9f6+ISSfQ==", "dev": true, "dependencies": { "@ljharb/resumer": "^0.0.1", @@ -2817,7 +2820,7 @@ "for-each": "^0.3.3", "get-package-type": "^0.1.0", "glob": "^7.2.3", - "has-dynamic-import": "^2.0.1", + "has-dynamic-import": "^2.1.0", "hasown": "^2.0.0", "inherits": "^2.0.4", "is-regex": "^1.1.4", @@ -4167,13 +4170,13 @@ "dev": true }, "has-dynamic-import": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.0.1.tgz", - "integrity": "sha512-X3fbtsZmwb6W7fJGR9o7x65fZoodygCrZ3TVycvghP62yYQfS0t4RS0Qcz+j5tQYUKeSWS09tHkWW6WhFV3XhQ==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/has-dynamic-import/-/has-dynamic-import-2.1.0.tgz", + "integrity": "sha512-su0anMkNEnJKZ/rB99jn3y6lV/J8Ro96hBJ28YAeVzj5rWxH+YL/AdCyiYYA1HDLV9YhmvqpWSJJj2KLo1MX6g==", "dev": true, "requires": { - "call-bind": "^1.0.2", - "get-intrinsic": "^1.1.1" + "call-bind": "^1.0.5", + "get-intrinsic": "^1.2.2" } }, "has-property-descriptors": { @@ -5128,9 +5131,9 @@ } }, "tape": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/tape/-/tape-5.7.3.tgz", - "integrity": "sha512-un2/TkloCBIxbrLac88Z9WElQ85WLE4t+jjfgrmxrlInWGYQf9r9Wbycc/nNP3zap6GBb7qln7h85QoNBNZdHg==", + "version": "5.7.4", + "resolved": "https://registry.npmjs.org/tape/-/tape-5.7.4.tgz", + "integrity": "sha512-uaigP+5H9+E8aaMLKMbGkDd33G5TKu4UFpapqT7um+8xSHQQUS2lJNd+hTj9fFVQLg8bmcIofwc8b9f6+ISSfQ==", "dev": true, "requires": { "@ljharb/resumer": "^0.0.1", @@ -5143,7 +5146,7 @@ "for-each": "^0.3.3", "get-package-type": "^0.1.0", "glob": "^7.2.3", - "has-dynamic-import": "^2.0.1", + "has-dynamic-import": "^2.1.0", "hasown": "^2.0.0", "inherits": "^2.0.4", "is-regex": "^1.1.4", diff --git a/package.json b/package.json index f3826ffd4..671152423 100644 --- a/package.json +++ b/package.json @@ -113,7 +113,7 @@ "rollup-plugin-filesize-check": "^0.0.2", "shelljs": "0.8.5", "tap-dancer": "0.3.4", - "tape": "5.7.3" + "tape": "5.7.4" }, "eslintIgnore": [ "builds/*.js", diff --git a/plugins/_experiments/payload/scratch.js b/plugins/_experiments/payload/scratch.js index 30cb458b6..318a4adf1 100644 --- a/plugins/_experiments/payload/scratch.js +++ b/plugins/_experiments/payload/scratch.js @@ -12,4 +12,5 @@ doc.people().forEach(m => { m.addPayload({ height: `5'8` }) } }) -console.log(doc.getPayloads()) +// console.log(doc.getPayloads()) +console.log(doc.match('and tom .').debug().getPayloads()) diff --git a/plugins/_experiments/payload/src/plugin.js b/plugins/_experiments/payload/src/plugin.js index 78c3a4ce1..8c9c63434 100644 --- a/plugins/_experiments/payload/src/plugin.js +++ b/plugins/_experiments/payload/src/plugin.js @@ -12,11 +12,15 @@ export default { this.fullPointer.forEach(ptr => { let n = ptr[0] if (db.hasOwnProperty(n)) { + // look at all vals for this sentence db[n].forEach(obj => { - res = res.concat({ - match: this.update(obj.ptr), - val: obj.val, - }) + let m = this.update([obj.ptr]) + if (this.match(m).found) { + res = res.concat({ + match: m, + val: obj.val, + }) + } }) } }) diff --git a/scratch.js b/scratch.js index d000cd9d3..800cb64d5 100644 --- a/scratch.js +++ b/scratch.js @@ -4,16 +4,22 @@ import nlp from './src/three.js' // nlp.plugin(plg) // nlp.verbose('tagger') +let doc = nlp('i saw John Lennon, and tom cruise.') +let m = doc.match('john lennon') +let end = doc.match('and tom cruise') +console.log(end.has(m)) +console.log(end.match(m).found) + // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() -nlp('this is (kinda) messy').not('this').parentheses().debug() +// nlp('this is (kinda) messy').not('this').parentheses().debug() // nlp('15-ounce (15-ounce)').debug() -let doc = nlp('2 to 3 people') -doc.match('#Value [to] #Value', 0).replaceWith('-') -console.log(doc.text()) +// let doc = nlp('2 to 3 people') +// doc.match('#Value [to] #Value', 0).replaceWith('-') +// console.log(doc.text()) // let doc = nlp('one foo two foo') // let m = doc.terms() diff --git a/tests/one/match/doc-match.test.js b/tests/one/match/doc-match.test.js index e479eeb1f..4abb4e355 100644 --- a/tests/one/match/doc-match.test.js +++ b/tests/one/match/doc-match.test.js @@ -42,10 +42,22 @@ test('split-doc-input', function (t) { t.end() }) - test('two-or-matches overlap', function (t) { let doc = nlp('one two three four five') let m = doc.match('(two|four) (three|five)') t.deepEqual(m.out('array'), ['two three', 'four five'], here + 'or-overlap') t.end() }) + +test('has==match', function (t) { + let doc = nlp('i saw John Lennon, and tom cruise.') + let m = doc.match('john lennon') + let start = doc.match('i saw john .') + let end = doc.match('and tom cruise') + + t.equal(start.has(m), true, here + 'has-start') + t.equal(start.match(m).found, true, here + 'match-start') + t.equal(end.has(m), false, here + 'not-end') + t.equal(end.match(m).found, false, here + 'match:not-end') + t.end() +}) From c7c6e09676ad0e90baefdfe18042a412bc2d62a1 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Wed, 7 Feb 2024 15:34:59 -0500 Subject: [PATCH 08/34] first payload test --- plugins/_experiments/payload/src/plugin.js | 2 +- plugins/_experiments/payload/tests/_lib.js | 15 +++++++++++ .../payload/tests/payload.test.js | 25 +++++++++++++++++++ src/1-one/match/api/match.js | 2 +- 4 files changed, 42 insertions(+), 2 deletions(-) create mode 100644 plugins/_experiments/payload/tests/_lib.js create mode 100644 plugins/_experiments/payload/tests/payload.test.js diff --git a/plugins/_experiments/payload/src/plugin.js b/plugins/_experiments/payload/src/plugin.js index 8c9c63434..34e6a5cd2 100644 --- a/plugins/_experiments/payload/src/plugin.js +++ b/plugins/_experiments/payload/src/plugin.js @@ -15,7 +15,7 @@ export default { // look at all vals for this sentence db[n].forEach(obj => { let m = this.update([obj.ptr]) - if (this.match(m).found) { + if (this.has(m)) { res = res.concat({ match: m, val: obj.val, diff --git a/plugins/_experiments/payload/tests/_lib.js b/plugins/_experiments/payload/tests/_lib.js new file mode 100644 index 000000000..12198ecf9 --- /dev/null +++ b/plugins/_experiments/payload/tests/_lib.js @@ -0,0 +1,15 @@ +import build from '../../../../builds/one/compromise-one.mjs' +import src from '../../../../src/one.js' +// import plgBuild from '../builds/compromise-speech.mjs' +import plg from '../src/plugin.js' +let nlp + +if (process.env.TESTENV === 'prod') { + console.warn('== production build test 🚀 ==') // eslint-disable-line + nlp = build + // nlp.plugin(plgBuild) +} else { + nlp = src + nlp.plugin(plg) +} +export default nlp diff --git a/plugins/_experiments/payload/tests/payload.test.js b/plugins/_experiments/payload/tests/payload.test.js new file mode 100644 index 000000000..777e0e3ea --- /dev/null +++ b/plugins/_experiments/payload/tests/payload.test.js @@ -0,0 +1,25 @@ +import test from 'tape' +import nlp from './_lib.js' + +test('payload-misc', function (t) { + let doc = nlp('i saw John Lennon, and tom cruise.') + + doc.match('(john lennon|tom cruise|johnny carson)').forEach(m => { + if (m.has('john lennon')) { + m.addPayload({ height: `5'11` }) + } + if (m.has('tom cruise')) { + m.addPayload({ height: `5'8` }) + } + }) + + t.equal(doc.getPayloads().length, 2, 'full-doc-2') + + let end = doc.match('and tom .') + t.equal(end.getPayloads().length, 1, 'end-1') + + let tom = doc.match('tom') + t.equal(tom.getPayloads().length, 1, 'tom-0') + + t.end() +}) diff --git a/src/1-one/match/api/match.js b/src/1-one/match/api/match.js index ec886cfa4..f9722f29a 100644 --- a/src/1-one/match/api/match.js +++ b/src/1-one/match/api/match.js @@ -42,7 +42,7 @@ const has = function (regs, group, opts) { const one = this.methods.one // support view as input if (isView(regs)) { - let ptrs = regs.fullPointer // support a view object as input + let ptrs = this.intersection(regs).fullPointer return ptrs.length > 0 } // support a compiled set of matches From 91409434e84f17cf8f0957bbbb1e6477276dd61a Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Mon, 12 Feb 2024 11:48:07 -0500 Subject: [PATCH 09/34] add isUnit method to value #1089 --- README.md | 1 + scratch.js | 11 +++++----- scripts/typescript/three.ts | 1 + src/3-three/numbers/numbers/api.js | 25 +++++++++++++-------- src/3-three/numbers/numbers/isUnit.js | 31 +++++++++++++++++++++++++++ src/3-three/numbers/plugin.js | 10 +++++++++ tests/three/api.test.js | 1 + types/view/three.d.ts | 2 ++ 8 files changed, 68 insertions(+), 14 deletions(-) create mode 100644 src/3-three/numbers/numbers/isUnit.js diff --git a/README.md b/README.md index 57fe98e04..a186a06d2 100644 --- a/README.md +++ b/README.md @@ -636,6 +636,7 @@ _(these methods are on the main `nlp` object)_ - **[.numbers().greaterThan(min)](https://observablehq.com/@spencermountain/compromise-values)** - return numbers bigger than n - **[.numbers().lessThan(max)](https://observablehq.com/@spencermountain/compromise-values)** - return numbers smaller than n - **[.numbers().between(min, max)](https://observablehq.com/@spencermountain/compromise-values)** - return numbers between min and max + - **[.numbers().isUnit(unit)](https://observablehq.com/@spencermountain/compromise-values)** - return only numbers in the given unit, like 'km' - **[.numbers().set(n)](https://observablehq.com/@spencermountain/compromise-values)** - set number to n - **[.numbers().add(n)](https://observablehq.com/@spencermountain/compromise-values)** - increase number by n - **[.numbers().subtract(n)](https://observablehq.com/@spencermountain/compromise-values)** - decrease number by n diff --git a/scratch.js b/scratch.js index 800cb64d5..1626d7ee7 100644 --- a/scratch.js +++ b/scratch.js @@ -4,11 +4,12 @@ import nlp from './src/three.js' // nlp.plugin(plg) // nlp.verbose('tagger') -let doc = nlp('i saw John Lennon, and tom cruise.') -let m = doc.match('john lennon') -let end = doc.match('and tom cruise') -console.log(end.has(m)) -console.log(end.match(m).found) +nlp('fifty five mg but not 23km').values().isUnit(['mg', 'dt']).debug() +// let doc = nlp('i saw John Lennon, and tom cruise.') +// let m = doc.match('john lennon') +// let end = doc.match('and tom cruise') +// console.log(end.has(m)) +// console.log(end.match(m).found) // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() diff --git a/scripts/typescript/three.ts b/scripts/typescript/three.ts index 37b724982..0eb2b0095 100644 --- a/scripts/typescript/three.ts +++ b/scripts/typescript/three.ts @@ -58,6 +58,7 @@ doc.numbers().get() doc.numbers().json() doc.numbers().isOrdinal() doc.numbers().isCardinal() +doc.numbers().isUnit('km') doc.numbers().toNumber() doc.numbers().toLocaleString() doc.numbers().toText() diff --git a/src/3-three/numbers/numbers/api.js b/src/3-three/numbers/numbers/api.js index 80b7df8e9..4801cf300 100644 --- a/src/3-three/numbers/numbers/api.js +++ b/src/3-three/numbers/numbers/api.js @@ -1,6 +1,7 @@ import find from './find.js' import parse from './parse/index.js' import format from './format/index.js' +import isUnit from './isUnit.js' const addMethod = function (View) { /** */ @@ -13,7 +14,9 @@ const addMethod = function (View) { return this.getNth(n).map(parse) } get(n) { - return this.getNth(n).map(parse).map(o => o.num) + return this.getNth(n) + .map(parse) + .map(o => o.num) } json(n) { let opts = typeof n === 'object' ? n : {} @@ -25,7 +28,7 @@ const addMethod = function (View) { num: parsed.num, suffix: parsed.suffix, hasComma: parsed.hasComma, - unit: parsed.unit + unit: parsed.unit, } return json }, []) @@ -34,6 +37,10 @@ const addMethod = function (View) { units() { return this.growRight('#Unit').match('#Unit$') } + /** return values that match a given unit */ + isUnit(allowed) { + return isUnit(this, allowed) + } /** return only ordinal numbers */ isOrdinal() { return this.if('#Ordinal') @@ -61,7 +68,7 @@ const addMethod = function (View) { /** add commas, or nicer formatting for numbers */ toLocaleString() { let m = this - m.forEach((val) => { + m.forEach(val => { let obj = parse(val) if (obj.num === null) { return @@ -139,28 +146,28 @@ const addMethod = function (View) { /** return only numbers that are == n */ isEqual(n) { - return this.filter((val) => { + return this.filter(val => { let num = parse(val).num return num === n }) } /** return only numbers that are > n*/ greaterThan(n) { - return this.filter((val) => { + return this.filter(val => { let num = parse(val).num return num > n }) } /** return only numbers that are < n*/ lessThan(n) { - return this.filter((val) => { + return this.filter(val => { let num = parse(val).num return num < n }) } /** return only numbers > min and < max */ between(min, max) { - return this.filter((val) => { + return this.filter(val => { let num = parse(val).num return num > min && num < max }) @@ -174,7 +181,7 @@ const addMethod = function (View) { n = parse(n).num } let m = this - let res = m.map((val) => { + let res = m.map(val => { let obj = parse(val) obj.num = n if (obj.num === null) { @@ -205,7 +212,7 @@ const addMethod = function (View) { n = parse(n).num } let m = this - let res = m.map((val) => { + let res = m.map(val => { let obj = parse(val) if (obj.num === null) { return val diff --git a/src/3-three/numbers/numbers/isUnit.js b/src/3-three/numbers/numbers/isUnit.js new file mode 100644 index 000000000..21e23fe42 --- /dev/null +++ b/src/3-three/numbers/numbers/isUnit.js @@ -0,0 +1,31 @@ +import parse from './parse/index.js' + +const isArray = function (arr) { + return Object.prototype.toString.call(arr) === '[object Array]' +} + +const toObject = function (input) { + if (typeof input === 'string') { + let tmp = {} + tmp[input] = true + input = tmp + } else if (isArray(input)) { + input = input.reduce((h, s) => { + h[s] = true + return h + }, {}) + } + return input +} + +const isUnit = function (doc, input = {}) { + input = toObject(input) + return doc.filter(p => { + let { unit } = parse(p) + if (unit && input[unit] === true) { + return true + } + return false + }) +} +export default isUnit diff --git a/src/3-three/numbers/plugin.js b/src/3-three/numbers/plugin.js index 81b543b8c..bca0c0e9d 100644 --- a/src/3-three/numbers/plugin.js +++ b/src/3-three/numbers/plugin.js @@ -8,4 +8,14 @@ const api = function (View) { export default { api, + + // add @greaterThan, @lessThan + mutate: world => { + let termMethods = world.methods.one.termMethods + + termMethods.lessThan = function (term) { + console.log(term) + // return /[aeiou]/.test(term.text) + } + }, } diff --git a/tests/three/api.test.js b/tests/three/api.test.js index dd2ea3d0c..ff18078bc 100644 --- a/tests/three/api.test.js +++ b/tests/three/api.test.js @@ -161,6 +161,7 @@ test('api:', function (t) { doc.numbers().json() doc.numbers().isOrdinal() doc.numbers().isCardinal() + doc.numbers().isUnit() doc.numbers().toNumber() doc.numbers().toLocaleString() doc.numbers().toText() diff --git a/types/view/three.d.ts b/types/view/three.d.ts index 53314129e..afd0eeef3 100644 --- a/types/view/three.d.ts +++ b/types/view/three.d.ts @@ -107,6 +107,8 @@ export interface Numbers extends View { isOrdinal: () => View /** return only cardinal numbers */ isCardinal: () => View + /** return only numbers with the given unit(s), like 'km' */ + isUnit: (units: string | string[] | object) => View /** convert number to `5` or `5th` */ toNumber: () => View /** add commas, or nicer formatting for numbers */ From 5f701e151b7ef9069feff7e0a1f7eb4d995b95b2 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Mon, 12 Feb 2024 11:54:14 -0500 Subject: [PATCH 10/34] tests for isUnit --- scratch.js | 2 +- tests/three/numbers/units.test.js | 21 +++++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/scratch.js b/scratch.js index 1626d7ee7..b88591681 100644 --- a/scratch.js +++ b/scratch.js @@ -4,7 +4,7 @@ import nlp from './src/three.js' // nlp.plugin(plg) // nlp.verbose('tagger') -nlp('fifty five mg but not 23km').values().isUnit(['mg', 'dt']).debug() +nlp('i stored 300gb on a 600 GB harddrive').values().isUnit(['gb', 'dt']).debug() // let doc = nlp('i saw John Lennon, and tom cruise.') // let m = doc.match('john lennon') // let end = doc.match('and tom cruise') diff --git a/tests/three/numbers/units.test.js b/tests/three/numbers/units.test.js index 2090f0c6d..608d72409 100644 --- a/tests/three/numbers/units.test.js +++ b/tests/three/numbers/units.test.js @@ -44,7 +44,6 @@ test('units-parse:', function (t) { t.end() }) - test('units-convert:', function (t) { let doc = nlp('we covered 3 km² in total') doc.numbers().toText() @@ -54,7 +53,6 @@ test('units-convert:', function (t) { doc.numbers().toText() t.equal(doc.text(), 'we covered three km² in total', here + 'no space unit') - t.equal(nlp('44,000 ft').has(44000), true, here + '44,000 ft') t.equal(nlp('44,000ft').has('44,000'), true, here + '44,000') // t.equal(nlp('44,000ft').has(44000), true, here + '44000') @@ -64,6 +62,25 @@ test('units-convert:', function (t) { t.end() }) +test('isUnits:', function (t) { + let doc = nlp('i stored 300gb of text in 8mins') + let m = doc.numbers().isUnit('mins') + t.equal(m.text(), '8mins', 'str-min') + + m = doc.numbers().isUnit(['mins', 'db', 'gb']) + t.deepEqual(m.out('array'), ['300gb', '8mins'], 'arr-input') + + m = doc.numbers().isUnit({ gb: true, gbs: true }) + t.deepEqual(m.out('array'), ['300gb'], 'obj-input') + + m = doc.numbers().isUnit() + t.deepEqual(m.out('array'), [], 'empty-input') + + doc = nlp('i stored 300gb on a 600 GB harddrive') + m = doc.numbers().isUnit('gb') + t.deepEqual(m.out('array'), ['300gb', '600'], 'unit-normalized') + t.end() +}) // test('implicit units', function (t) { // let arr = [ // // ['99%', '99%'], From fb0249adabfcf8807f60d4c12bc33b1549f710bc Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Mon, 12 Feb 2024 13:56:21 -0500 Subject: [PATCH 11/34] support custom debug methods --- changelog.md | 15 +++- scratch.js | 15 ++-- src/1-one/output/api/{lib => }/_text.js | 0 src/1-one/output/api/debug.js | 22 ++++++ src/1-one/output/api/debug/index.js | 38 ---------- src/1-one/output/api/json.js | 4 +- src/1-one/output/api/lib/hash.js | 73 ------------------- src/1-one/output/api/out.js | 4 +- src/1-one/output/api/text.js | 2 +- .../output/{api => methods}/debug/_color.js | 0 .../output/{api => methods}/debug/chunks.js | 1 + .../{api => methods}/debug/client-side.js | 2 +- .../{api => methods}/debug/highlight.js | 4 +- src/1-one/output/methods/debug/index.js | 12 +++ .../output/{api => methods}/debug/tags.js | 1 + src/1-one/output/methods/hash.js | 62 ++++++++++++++++ src/1-one/output/methods/index.js | 4 + src/1-one/output/plugin.js | 9 ++- src/3-three/numbers/numbers/isUnit.js | 21 +++--- 19 files changed, 147 insertions(+), 142 deletions(-) rename src/1-one/output/api/{lib => }/_text.js (100%) create mode 100644 src/1-one/output/api/debug.js delete mode 100644 src/1-one/output/api/debug/index.js delete mode 100644 src/1-one/output/api/lib/hash.js rename src/1-one/output/{api => methods}/debug/_color.js (100%) rename src/1-one/output/{api => methods}/debug/chunks.js (97%) rename src/1-one/output/{api => methods}/debug/client-side.js (95%) rename src/1-one/output/{api => methods}/debug/highlight.js (93%) create mode 100644 src/1-one/output/methods/debug/index.js rename src/1-one/output/{api => methods}/debug/tags.js (98%) create mode 100644 src/1-one/output/methods/hash.js create mode 100644 src/1-one/output/methods/index.js diff --git a/changelog.md b/changelog.md index 6951e4d2f..823802215 100644 --- a/changelog.md +++ b/changelog.md @@ -17,9 +17,22 @@ While all _Major_ releases should be reviewed, our only _large_ releases are **v +#### 14.12.0 [Feb 2024] + +- **[new]** - .payload() plugin +- **[new]** - `.numbers().isUnit()` method #1089 +- **[change]** - update github workflow (thanks FDawgs!) +- **[fix]** - README issues (thanks track0x1!) +- **[fix]** - .has() inconsistency +- **[new]** - support adding debug methods via plugins +- **[change]** - remove deprecated .debug(object) support + +- **[new]** - `dates().isBefore()`, `dates().isBefore()` +- **[fix]** - join() issue +- **[fix]** - .before() .after() + #### 14.11.2 [Jan 2024] - **[new]** - support for frozen lex in plugin object #1080 diff --git a/scratch.js b/scratch.js index b88591681..e3f8de5ee 100644 --- a/scratch.js +++ b/scratch.js @@ -4,20 +4,17 @@ import nlp from './src/three.js' // nlp.plugin(plg) // nlp.verbose('tagger') -nlp('i stored 300gb on a 600 GB harddrive').values().isUnit(['gb', 'dt']).debug() -// let doc = nlp('i saw John Lennon, and tom cruise.') -// let m = doc.match('john lennon') -// let end = doc.match('and tom cruise') -// console.log(end.has(m)) -// console.log(end.match(m).found) +// nlp('i went on June 5th 1999 and then on April 7 2008').dates().isBefore('2000-02-01').debug() // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() - +// doc.debug() // nlp('this is (kinda) messy').not('this').parentheses().debug() -// nlp('15-ounce (15-ounce)').debug() - +let doc = nlp('i saw the cat run up the stairs') +doc.debug('tags') +doc.debug('chunks') +doc.match('highlight').debug('highlight') // let doc = nlp('2 to 3 people') // doc.match('#Value [to] #Value', 0).replaceWith('-') // console.log(doc.text()) diff --git a/src/1-one/output/api/lib/_text.js b/src/1-one/output/api/_text.js similarity index 100% rename from src/1-one/output/api/lib/_text.js rename to src/1-one/output/api/_text.js diff --git a/src/1-one/output/api/debug.js b/src/1-one/output/api/debug.js new file mode 100644 index 000000000..128fc1d5d --- /dev/null +++ b/src/1-one/output/api/debug.js @@ -0,0 +1,22 @@ +/* eslint-disable no-console */ +const isClientSide = () => typeof window !== 'undefined' && window.document + +//output some helpful stuff to the console +const debug = function (fmt) { + let view = this + let debugMethods = this.methods.one.debug || {} + // see if method name exists + if (fmt && debugMethods.hasOwnProperty(fmt)) { + debugMethods[fmt](view) + return view + } + // log default client-side view + if (isClientSide()) { + debugMethods.clientSide(view) + return view + } + // else, show regular server-side tags view + debugMethods.tags(view) + return view +} +export default debug diff --git a/src/1-one/output/api/debug/index.js b/src/1-one/output/api/debug/index.js deleted file mode 100644 index d1ff95e79..000000000 --- a/src/1-one/output/api/debug/index.js +++ /dev/null @@ -1,38 +0,0 @@ -/* eslint-disable no-console */ -import logClientSide from './client-side.js' -import showTags from './tags.js' -import showChunks from './chunks.js' -import showHighlight from './highlight.js' - -function isClientSide() { - return typeof window !== 'undefined' && window.document -} -//output some helpful stuff to the console -const debug = function (opts = {}) { - let view = this - if (typeof opts === 'string') { - let tmp = {} - tmp[opts] = true //allow string input - opts = tmp - } - if (isClientSide()) { - logClientSide(view) - return view - } - if (opts.tags !== false) { - showTags(view) - console.log('\n') - } - // output chunk-view, too - if (opts.chunks === true) { - showChunks(view) - console.log('\n') - } - // highlight match in sentence - if (opts.highlight === true) { - showHighlight(view) - console.log('\n') - } - return view -} -export default debug diff --git a/src/1-one/output/api/json.js b/src/1-one/output/api/json.js index 93d477fc0..cc68f7c7c 100644 --- a/src/1-one/output/api/json.js +++ b/src/1-one/output/api/json.js @@ -1,6 +1,6 @@ -import { textFromTerms } from './lib/_text.js' +import { textFromTerms } from './_text.js' import fmts from './_fmts.js' -import hash from './lib/hash.js' +import hash from '../methods/hash.js' const defaults = { text: true, diff --git a/src/1-one/output/api/lib/hash.js b/src/1-one/output/api/lib/hash.js deleted file mode 100644 index 129221af8..000000000 --- a/src/1-one/output/api/lib/hash.js +++ /dev/null @@ -1,73 +0,0 @@ -/* eslint-disable no-bitwise */ -/* eslint-disable no-mixed-operators */ -/* eslint-disable no-multi-assign */ - -// https://github.com/jbt/tiny-hashes/ -let k = [], i = 0; -for (; i < 64;) { - k[i] = 0 | Math.sin(++i % Math.PI) * 4294967296; -} - -export default function md5(s) { - let b, c, d, - h = [b = 0x67452301, c = 0xEFCDAB89, ~b, ~c], - words = [], - j = decodeURI(encodeURI(s)) + '\x80', - a = j.length; - - s = (--a / 4 + 2) | 15; - - words[--s] = a * 8; - - for (; ~a;) { - words[a >> 2] |= j.charCodeAt(a) << 8 * a--; - } - - for (i = j = 0; i < s; i += 16) { - a = h; - - for (; j < 64; - a = [ - d = a[3], - ( - b + - ((d = - a[0] + - [ - b & c | ~b & d, - d & b | ~d & c, - b ^ c ^ d, - c ^ (b | ~d) - ][a = j >> 4] + - k[j] + - ~~words[i | [ - j, - 5 * j + 1, - 3 * j + 5, - 7 * j - ][a] & 15] - ) << (a = [ - 7, 12, 17, 22, - 5, 9, 14, 20, - 4, 11, 16, 23, - 6, 10, 15, 21 - ][4 * a + j++ % 4]) | d >>> -a) - ), - b, - c - ] - ) { - b = a[1] | 0; - c = a[2]; - } - for (j = 4; j;) h[--j] += a[j]; - } - - for (s = ''; j < 32;) { - s += ((h[j >> 3] >> ((1 ^ j++) * 4)) & 15).toString(16); - } - - return s; -} - -// console.log(md5('food-safety')) \ No newline at end of file diff --git a/src/1-one/output/api/out.js b/src/1-one/output/api/out.js index e16e31e75..5b6c7720f 100644 --- a/src/1-one/output/api/out.js +++ b/src/1-one/output/api/out.js @@ -1,6 +1,6 @@ -import debug from './debug/index.js' +import debug from './debug.js' import wrap from './wrap.js' -import hash from './lib/hash.js' +import hash from '../methods/hash.js' const isObject = val => { return Object.prototype.toString.call(val) === '[object Object]' diff --git a/src/1-one/output/api/text.js b/src/1-one/output/api/text.js index 0a79982e1..eba3af18e 100644 --- a/src/1-one/output/api/text.js +++ b/src/1-one/output/api/text.js @@ -1,4 +1,4 @@ -import { textFromDoc } from './lib/_text.js' +import { textFromDoc } from './_text.js' import fmts from './_fmts.js' const isObject = val => { diff --git a/src/1-one/output/api/debug/_color.js b/src/1-one/output/methods/debug/_color.js similarity index 100% rename from src/1-one/output/api/debug/_color.js rename to src/1-one/output/methods/debug/_color.js diff --git a/src/1-one/output/api/debug/chunks.js b/src/1-one/output/methods/debug/chunks.js similarity index 97% rename from src/1-one/output/api/debug/chunks.js rename to src/1-one/output/methods/debug/chunks.js index 50e6dc7a0..a195f07c5 100644 --- a/src/1-one/output/api/debug/chunks.js +++ b/src/1-one/output/methods/debug/chunks.js @@ -21,5 +21,6 @@ const showChunks = function (view) { }) console.log(out.join(' '), '\n') }) + console.log('\n') } export default showChunks diff --git a/src/1-one/output/api/debug/client-side.js b/src/1-one/output/methods/debug/client-side.js similarity index 95% rename from src/1-one/output/api/debug/client-side.js rename to src/1-one/output/methods/debug/client-side.js index af47aa069..b7daf09dd 100644 --- a/src/1-one/output/api/debug/client-side.js +++ b/src/1-one/output/methods/debug/client-side.js @@ -13,7 +13,7 @@ const logClientSide = function (view) { return { text, tags } }) console.table(out, ['text', 'tags']) - console.groupEnd(); + console.groupEnd() }) } export default logClientSide diff --git a/src/1-one/output/api/debug/highlight.js b/src/1-one/output/methods/debug/highlight.js similarity index 93% rename from src/1-one/output/api/debug/highlight.js rename to src/1-one/output/methods/debug/highlight.js index b1c77eb76..81a7b8576 100644 --- a/src/1-one/output/api/debug/highlight.js +++ b/src/1-one/output/methods/debug/highlight.js @@ -1,3 +1,4 @@ +/* eslint-disable no-console */ import cli from './_color.js' const split = (txt, offset, index) => { @@ -32,7 +33,8 @@ const showHighlight = function (doc) { json.forEach((obj, i) => { txt = spliceIn(txt, obj.offset, i) }) - console.log(txt) // eslint-disable-line + console.log(txt) }) + console.log('\n') } export default showHighlight diff --git a/src/1-one/output/methods/debug/index.js b/src/1-one/output/methods/debug/index.js new file mode 100644 index 000000000..9414d7291 --- /dev/null +++ b/src/1-one/output/methods/debug/index.js @@ -0,0 +1,12 @@ +import clientSide from './client-side.js' +import tags from './tags.js' +import chunks from './chunks.js' +import highlight from './highlight.js' + +const debug = { + tags, + clientSide, + chunks, + highlight, +} +export default debug diff --git a/src/1-one/output/api/debug/tags.js b/src/1-one/output/methods/debug/tags.js similarity index 98% rename from src/1-one/output/api/debug/tags.js rename to src/1-one/output/methods/debug/tags.js index 3b99cc194..854a1d8b1 100644 --- a/src/1-one/output/api/debug/tags.js +++ b/src/1-one/output/methods/debug/tags.js @@ -41,5 +41,6 @@ const showTags = function (view) { console.log(str) }) }) + console.log('\n') } export default showTags diff --git a/src/1-one/output/methods/hash.js b/src/1-one/output/methods/hash.js new file mode 100644 index 000000000..42c2673ae --- /dev/null +++ b/src/1-one/output/methods/hash.js @@ -0,0 +1,62 @@ +/* eslint-disable no-bitwise */ +/* eslint-disable no-mixed-operators */ +/* eslint-disable no-multi-assign */ + +// https://github.com/jbt/tiny-hashes/ +let k = [], + i = 0 +for (; i < 64; ) { + k[i] = 0 | (Math.sin(++i % Math.PI) * 4294967296) +} + +const md5 = function (s) { + let b, + c, + d, + h = [(b = 0x67452301), (c = 0xefcdab89), ~b, ~c], + words = [], + j = decodeURI(encodeURI(s)) + '\x80', + a = j.length + + s = (--a / 4 + 2) | 15 + + words[--s] = a * 8 + + for (; ~a; ) { + words[a >> 2] |= j.charCodeAt(a) << (8 * a--) + } + + for (i = j = 0; i < s; i += 16) { + a = h + + for ( + ; + j < 64; + a = [ + (d = a[3]), + b + + (((d = + a[0] + + [(b & c) | (~b & d), (d & b) | (~d & c), b ^ c ^ d, c ^ (b | ~d)][(a = j >> 4)] + + k[j] + + ~~words[i | ([j, 5 * j + 1, 3 * j + 5, 7 * j][a] & 15)]) << + (a = [7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21][4 * a + (j++ % 4)])) | + (d >>> -a)), + b, + c, + ] + ) { + b = a[1] | 0 + c = a[2] + } + for (j = 4; j; ) h[--j] += a[j] + } + + for (s = ''; j < 32; ) { + s += ((h[j >> 3] >> ((1 ^ j++) * 4)) & 15).toString(16) + } + + return s +} +export default md5 +// console.log(md5('food-safety')) diff --git a/src/1-one/output/methods/index.js b/src/1-one/output/methods/index.js new file mode 100644 index 000000000..ef2890e8f --- /dev/null +++ b/src/1-one/output/methods/index.js @@ -0,0 +1,4 @@ +import hash from './hash.js' +import debug from './debug/index.js' + +export { hash, debug } diff --git a/src/1-one/output/plugin.js b/src/1-one/output/plugin.js index 3d5412f2a..cbfe05903 100644 --- a/src/1-one/output/plugin.js +++ b/src/1-one/output/plugin.js @@ -1,11 +1,12 @@ import api from './api/index.js' -import hash from './api/lib/hash.js' +import { debug, hash } from './methods/index.js' export default { api, methods: { one: { - hash - } - } + hash, + debug, + }, + }, } diff --git a/src/3-three/numbers/numbers/isUnit.js b/src/3-three/numbers/numbers/isUnit.js index 21e23fe42..920cbe3bc 100644 --- a/src/3-three/numbers/numbers/isUnit.js +++ b/src/3-three/numbers/numbers/isUnit.js @@ -1,25 +1,26 @@ import parse from './parse/index.js' -const isArray = function (arr) { - return Object.prototype.toString.call(arr) === '[object Array]' -} +const isArray = arr => Object.prototype.toString.call(arr) === '[object Array]' -const toObject = function (input) { - if (typeof input === 'string') { +// turn anything into {foo:true} format +const coerceToObject = function (input) { + if (typeof input === 'string' || typeof input === 'number') { let tmp = {} tmp[input] = true - input = tmp - } else if (isArray(input)) { - input = input.reduce((h, s) => { + return tmp + } + if (isArray(input)) { + return input.reduce((h, s) => { h[s] = true return h }, {}) } - return input + return input || {} } +// only return values with the given unit const isUnit = function (doc, input = {}) { - input = toObject(input) + input = coerceToObject(input) return doc.filter(p => { let { unit } = parse(p) if (unit && input[unit] === true) { From 87c6f70345a256fee884e28ae335b308a87c2f58 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Mon, 12 Feb 2024 13:59:06 -0500 Subject: [PATCH 12/34] debug methods working --- scratch.js | 4 ++-- src/1-one/output/api/debug.js | 13 ++++++------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/scratch.js b/scratch.js index e3f8de5ee..8b18cb337 100644 --- a/scratch.js +++ b/scratch.js @@ -11,10 +11,10 @@ import nlp from './src/three.js' // doc.debug() // nlp('this is (kinda) messy').not('this').parentheses().debug() -let doc = nlp('i saw the cat run up the stairs') +let doc = nlp('i saw the blue cat run up the stairs') doc.debug('tags') doc.debug('chunks') -doc.match('highlight').debug('highlight') +doc.match('cat run').debug('highlight') // let doc = nlp('2 to 3 people') // doc.match('#Value [to] #Value', 0).replaceWith('-') // console.log(doc.text()) diff --git a/src/1-one/output/api/debug.js b/src/1-one/output/api/debug.js index 128fc1d5d..a2221bb8c 100644 --- a/src/1-one/output/api/debug.js +++ b/src/1-one/output/api/debug.js @@ -3,20 +3,19 @@ const isClientSide = () => typeof window !== 'undefined' && window.document //output some helpful stuff to the console const debug = function (fmt) { - let view = this let debugMethods = this.methods.one.debug || {} // see if method name exists if (fmt && debugMethods.hasOwnProperty(fmt)) { - debugMethods[fmt](view) - return view + debugMethods[fmt](this) + return this } // log default client-side view if (isClientSide()) { - debugMethods.clientSide(view) - return view + debugMethods.clientSide(this) + return this } // else, show regular server-side tags view - debugMethods.tags(view) - return view + debugMethods.tags(this) + return this } export default debug From 5d031d9c5310faf32072337ab818fbec76671862 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Mon, 12 Feb 2024 14:06:53 -0500 Subject: [PATCH 13/34] payload debug working --- plugins/_experiments/payload/scratch.js | 4 +++- plugins/_experiments/payload/src/debug.js | 13 +++++++++++++ plugins/_experiments/payload/src/plugin.js | 3 +++ 3 files changed, 19 insertions(+), 1 deletion(-) create mode 100644 plugins/_experiments/payload/src/debug.js diff --git a/plugins/_experiments/payload/scratch.js b/plugins/_experiments/payload/scratch.js index 318a4adf1..753a65777 100644 --- a/plugins/_experiments/payload/scratch.js +++ b/plugins/_experiments/payload/scratch.js @@ -13,4 +13,6 @@ doc.people().forEach(m => { } }) // console.log(doc.getPayloads()) -console.log(doc.match('and tom .').debug().getPayloads()) +doc.debug('payload') +// let m = doc.match('and tom .').debug('payload') +// console.log(m.getPayloads()) diff --git a/plugins/_experiments/payload/src/debug.js b/plugins/_experiments/payload/src/debug.js new file mode 100644 index 000000000..ae40d1b0a --- /dev/null +++ b/plugins/_experiments/payload/src/debug.js @@ -0,0 +1,13 @@ +/* eslint-disable no-console */ + +// pretty-print each match that has a payload +const debug = function (view) { + view.getPayloads().forEach(res => { + let { match, val } = res + console.log('\n────────') + match.debug('highlight') + console.log(' ', JSON.stringify(val)) + console.log('\n') + }) +} +export default debug diff --git a/plugins/_experiments/payload/src/plugin.js b/plugins/_experiments/payload/src/plugin.js index 34e6a5cd2..adf9e0fba 100644 --- a/plugins/_experiments/payload/src/plugin.js +++ b/plugins/_experiments/payload/src/plugin.js @@ -1,7 +1,10 @@ +import debug from './debug.js' + export default { //establish payload db mutate: function (world) { world.model.one.db = {} + world.methods.one.debug.payload = debug }, api: function (View) { From abd1902dc752c638d562cfe6bd79cb6fbe7a3c93 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Mon, 12 Feb 2024 17:02:01 -0500 Subject: [PATCH 14/34] fix for dirty parentheses match #1065 --- scratch.js | 8 +++---- src/3-three/misc/parentheses/fns.js | 4 +++- src/3-three/misc/parentheses/index.js | 1 - tests/three/parentheses.test.js | 33 +++++++++++++++++++++++++++ 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/scratch.js b/scratch.js index 8b18cb337..3f6f3c21c 100644 --- a/scratch.js +++ b/scratch.js @@ -9,12 +9,10 @@ import nlp from './src/three.js' // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() // doc.debug() -// nlp('this is (kinda) messy').not('this').parentheses().debug() +let doc = nlp('this is (kinda) messy') +let m = doc.not('this') +m.parentheses().debug() -let doc = nlp('i saw the blue cat run up the stairs') -doc.debug('tags') -doc.debug('chunks') -doc.match('cat run').debug('highlight') // let doc = nlp('2 to 3 people') // doc.match('#Value [to] #Value', 0).replaceWith('-') // console.log(doc.text()) diff --git a/src/3-three/misc/parentheses/fns.js b/src/3-three/misc/parentheses/fns.js index d244829ec..a8633ff54 100644 --- a/src/3-three/misc/parentheses/fns.js +++ b/src/3-three/misc/parentheses/fns.js @@ -4,7 +4,9 @@ const hasClosed = /\)/ const findEnd = function (terms, i) { for (; i < terms.length; i += 1) { if (terms[i].post && hasClosed.test(terms[i].post)) { - return i + let [, index] = terms[i].index + index = index || 0 + return index } } return null diff --git a/src/3-three/misc/parentheses/index.js b/src/3-three/misc/parentheses/index.js index fa27d63df..69afae163 100644 --- a/src/3-three/misc/parentheses/index.js +++ b/src/3-three/misc/parentheses/index.js @@ -1,7 +1,6 @@ import { find, strip } from './fns.js' const api = function (View) { - class Parentheses extends View { constructor(document, pointer, groups) { super(document, pointer, groups) diff --git a/tests/three/parentheses.test.js b/tests/three/parentheses.test.js index 5776c318e..26aaa40cc 100644 --- a/tests/three/parentheses.test.js +++ b/tests/three/parentheses.test.js @@ -17,3 +17,36 @@ test('parentheses test', function (t) { }) t.end() }) + +test('parentheses on mangled doc', function (t) { + let doc = nlp('this is (kinda) messy') + let m = doc.not('this') + let str = m.parentheses().text() + t.equal(str, 'kinda', here + '+not-before') + + m = doc.not('kinda') + str = m.parentheses().text() + t.equal(str, '', here + '+not-self') + + m = doc.not('messy') + str = m.parentheses().text() + t.equal(str, 'kinda', here + '+not-after') + t.end() +}) + +test('partial-parentheses', function (t) { + let doc = nlp('before (one two three) after') + let m = doc.not('two') + let str = m.parentheses().text() + t.equal(str, 'one three', here + '+not-inside') + + m = doc.not('one') + str = m.parentheses().text() + t.equal(str, '', here + '+not-start') + + m = doc.not('three') + str = m.parentheses().text() + t.equal(str, '', here + '+not-after') + + t.end() +}) From 1c622a4658c73040082cc69cf8d0842d2f6ad692 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Mon, 12 Feb 2024 17:20:14 -0500 Subject: [PATCH 15/34] tests for parentheses --- scratch.js | 3 --- tests/three/parentheses.test.js | 4 ++-- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/scratch.js b/scratch.js index 3f6f3c21c..c723fdba4 100644 --- a/scratch.js +++ b/scratch.js @@ -9,9 +9,6 @@ import nlp from './src/three.js' // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() // doc.debug() -let doc = nlp('this is (kinda) messy') -let m = doc.not('this') -m.parentheses().debug() // let doc = nlp('2 to 3 people') // doc.match('#Value [to] #Value', 0).replaceWith('-') diff --git a/tests/three/parentheses.test.js b/tests/three/parentheses.test.js index 26aaa40cc..838442e8b 100644 --- a/tests/three/parentheses.test.js +++ b/tests/three/parentheses.test.js @@ -18,7 +18,7 @@ test('parentheses test', function (t) { t.end() }) -test('parentheses on mangled doc', function (t) { +test('dirty-parentheses', function (t) { let doc = nlp('this is (kinda) messy') let m = doc.not('this') let str = m.parentheses().text() @@ -38,7 +38,7 @@ test('partial-parentheses', function (t) { let doc = nlp('before (one two three) after') let m = doc.not('two') let str = m.parentheses().text() - t.equal(str, 'one three', here + '+not-inside') + t.equal(str, '', here + '+not-inside') m = doc.not('one') str = m.parentheses().text() From 69ce2b29b142c57386065e0ad9897fc52be80ecf Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Tue, 13 Feb 2024 12:53:25 -0500 Subject: [PATCH 16/34] isbefore working --- plugins/dates/package-lock.json | 14 +++++++------- plugins/dates/package.json | 2 +- plugins/dates/src/api/dates.js | 18 ++++++++++++++++++ scratch.js | 6 +++--- 4 files changed, 29 insertions(+), 11 deletions(-) diff --git a/plugins/dates/package-lock.json b/plugins/dates/package-lock.json index 2ad49e7b0..9bfcc6ea3 100644 --- a/plugins/dates/package-lock.json +++ b/plugins/dates/package-lock.json @@ -9,7 +9,7 @@ "version": "3.4.2", "license": "MIT", "dependencies": { - "spacetime": "7.1.4", + "spacetime": "7.6.0", "spacetime-holiday": "0.3.0" }, "peerDependencies": { @@ -49,9 +49,9 @@ } }, "node_modules/spacetime": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/spacetime/-/spacetime-7.1.4.tgz", - "integrity": "sha512-ZzYuGjaMPE42p/fVU9Qcd+aMhgYzC83hgqMKyWlGILtponsLm5KJZgWzblLnOU8OblMQa3YXo/0IiZZ5JsTDfA==" + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/spacetime/-/spacetime-7.6.0.tgz", + "integrity": "sha512-pQ1K7XNmmcFGRtF2LzXkfgQ02+NbV6W9v+0UmxnPe0xVByqFDbKkKI1uangS+9ZG5Ey2HeFh74EL9rKE7V/k9A==" }, "node_modules/spacetime-holiday": { "version": "0.3.0", @@ -93,9 +93,9 @@ "peer": true }, "spacetime": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/spacetime/-/spacetime-7.1.4.tgz", - "integrity": "sha512-ZzYuGjaMPE42p/fVU9Qcd+aMhgYzC83hgqMKyWlGILtponsLm5KJZgWzblLnOU8OblMQa3YXo/0IiZZ5JsTDfA==" + "version": "7.6.0", + "resolved": "https://registry.npmjs.org/spacetime/-/spacetime-7.6.0.tgz", + "integrity": "sha512-pQ1K7XNmmcFGRtF2LzXkfgQ02+NbV6W9v+0UmxnPe0xVByqFDbKkKI1uangS+9ZG5Ey2HeFh74EL9rKE7V/k9A==" }, "spacetime-holiday": { "version": "0.3.0", diff --git a/plugins/dates/package.json b/plugins/dates/package.json index bbb396e7e..7d76d774f 100644 --- a/plugins/dates/package.json +++ b/plugins/dates/package.json @@ -41,7 +41,7 @@ "compromise": ">=14.2.0" }, "dependencies": { - "spacetime": "7.1.4", + "spacetime": "7.6.0", "spacetime-holiday": "0.3.0" }, "license": "MIT" diff --git a/plugins/dates/src/api/dates.js b/plugins/dates/src/api/dates.js index fee0c3c70..f6772ff58 100644 --- a/plugins/dates/src/api/dates.js +++ b/plugins/dates/src/api/dates.js @@ -34,6 +34,7 @@ const api = function (View) { }, []) } + /** replace date terms with a formatted date */ format(fmt) { let found = this let res = found.map(m => { @@ -53,6 +54,23 @@ const api = function (View) { }) return new Dates(this.document, res.pointer, null, this.opts) } + + /** return only dates occuring before a given date */ + isBefore(iso) { + let tmp = this.fromText(iso) + let found = parseDates(tmp, this.opts)[0] + if (!found || !found.start || !found.start.d) { + return this.none() //return nothing + } + let pivot = found.start.d // our given date + return this.filter(m => { + let obj = parseDates(m, this.opts)[0] || {} + if (!obj.start || !obj.start.d) { + return false + } + return obj.start.d.isBefore(pivot) + }) + } } View.prototype.dates = function (opts) { diff --git a/scratch.js b/scratch.js index c723fdba4..c34f92f0a 100644 --- a/scratch.js +++ b/scratch.js @@ -1,10 +1,10 @@ /* eslint-disable no-console, no-unused-vars */ import nlp from './src/three.js' -// import plg from './plugins/dates/src/plugin.js' -// nlp.plugin(plg) +import plg from './plugins/dates/src/plugin.js' +nlp.plugin(plg) // nlp.verbose('tagger') -// nlp('i went on June 5th 1999 and then on April 7 2008').dates().isBefore('2000-02-01').debug() +nlp('i went on June 5th 1999 and then on April 7 2008').dates().isBefore('2000-02-01').debug() // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() From 9cc069a64a6ebfb1b8bfafab08fbd728cee8a28f Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Tue, 13 Feb 2024 13:38:41 -0500 Subject: [PATCH 17/34] dates debug method --- README.md | 8 ++++---- plugins/dates/src/debug.js | 21 +++++++++++++++++++++ plugins/dates/src/plugin.js | 14 +++++++------- plugins/dates/tests/before-after.test.js | 15 +++++++++++++++ scratch.js | 9 ++++++++- src/1-one/pointers/api/index.js | 3 +-- 6 files changed, 56 insertions(+), 14 deletions(-) create mode 100644 plugins/dates/src/debug.js create mode 100644 plugins/dates/tests/before-after.test.js diff --git a/README.md b/README.md index a186a06d2..ef2554a81 100644 --- a/README.md +++ b/README.md @@ -483,10 +483,10 @@ _(match methods use the [match-syntax](https://docs.compromise.cool/compromise-m - **[.has('')](https://observablehq.com/@spencermountain/compromise-match)** - Return a boolean if this match exists - **[.before('')](https://observablehq.com/@spencermountain/compromise-match)** - return all terms before a match, in each phrase - **[.after('')](https://observablehq.com/@spencermountain/compromise-match)** - return all terms after a match, in each phrase -- **.union()** - return combined matches without duplicates -- **.intersection()** - return only duplicate matches -- **.complement()** - get everything not in another match -- **.settle()** - remove overlaps from matches +- **[.union()](https://observablehq.com/@spencermountain/compromise-set)** - return combined matches without duplicates +- **[.intersection()](https://observablehq.com/@spencermountain/compromise-set)** - return only duplicate matches +- **[.complement()](https://observablehq.com/@spencermountain/compromise-set)** - get everything not in another match +- **[.settle()](https://observablehq.com/@spencermountain/compromise-set)** - remove overlaps from matches - **[.growRight('')](https://observablehq.com/@spencermountain/compromise-match)** - add any matching terms immediately after each match - **[.growLeft('')](https://observablehq.com/@spencermountain/compromise-match)** - add any matching terms immediately before each match - **[.grow('')](https://observablehq.com/@spencermountain/compromise-match)** - add any matching terms before or after each match diff --git a/plugins/dates/src/debug.js b/plugins/dates/src/debug.js new file mode 100644 index 000000000..8954533c8 --- /dev/null +++ b/plugins/dates/src/debug.js @@ -0,0 +1,21 @@ +/* eslint-disable no-console */ +import spacetime from 'spacetime' + +const fmt = iso => (iso ? spacetime(iso).format('{day-short} {nice} {year}') : '-') + +const debug = function (view) { + console.log('=-=-=-= here -=-=-=-') + view.dates().forEach(m => { + // console.log(m.dates().get()) + // console.log(found[0].dates) + // found.forEach((o) => { + // console.log('start: ', fmt(o.dates.start)) + // console.log(' end: ', fmt(o.dates.end)) + // }) + + console.log('\n────────') + m.debug('highlight') + console.log('\n') + }) +} +export default debug diff --git a/plugins/dates/src/plugin.js b/plugins/dates/src/plugin.js index 6ef856715..b8c237c9e 100644 --- a/plugins/dates/src/plugin.js +++ b/plugins/dates/src/plugin.js @@ -4,20 +4,20 @@ import tags from './model/tags.js' import words from './model/words/index.js' import regex from './model/regex.js' import version from './_version.js' - -// import matches from './compute/matches.js' +import debug from './debug.js' export default { tags, words, compute, api, - mutate: (world) => { + mutate: world => { + // add our regexes world.model.two.regexText = world.model.two.regexText || [] world.model.two.regexText = world.model.two.regexText.concat(regex) - // net = net || methods.one.buildNet(matches, world) - // world.model.two.matches = world.model.two.matches.concat(matches) + // add our debug('dates') method + world.methods.one.debug.dates = debug }, hooks: ['dates'], - version -} \ No newline at end of file + version, +} diff --git a/plugins/dates/tests/before-after.test.js b/plugins/dates/tests/before-after.test.js new file mode 100644 index 000000000..a24b82574 --- /dev/null +++ b/plugins/dates/tests/before-after.test.js @@ -0,0 +1,15 @@ +import test from 'tape' +import nlp from './_lib.js' +const here = ' [dates/before-after] ' + +test('isBefore misc', function (t) { + let doc = nlp('i went on June 5th 1999 and then on April 7 2008') + let m = doc.dates().isBefore('2000-02-01') + t.deepEqual(m.out('array'), ['June 5th 1999'], here + 'days') + + doc = nlp('and between Sept and Oct 2008, but then June 2010') + m = doc.dates().isBefore('2009-01') + t.deepEqual(m.out('array'), ['between Sept and Oct 2008'], here + 'months') + + t.end() +}) diff --git a/scratch.js b/scratch.js index c34f92f0a..835628d34 100644 --- a/scratch.js +++ b/scratch.js @@ -4,7 +4,14 @@ import plg from './plugins/dates/src/plugin.js' nlp.plugin(plg) // nlp.verbose('tagger') -nlp('i went on June 5th 1999 and then on April 7 2008').dates().isBefore('2000-02-01').debug() +// let doc = nlp('one two three four') +// let a = doc.match('one .') +// let b = doc.match('. three') +// a.difference(b).debug() + +// let doc = nlp('and between Sept and Oct 2008, but then June 2010') +// doc.dates().debug('dates') +// let m = doc.dates().isBefore('2009-01').debug() // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() diff --git a/src/1-one/pointers/api/index.js b/src/1-one/pointers/api/index.js index 737cd6a17..d0a6ee77d 100644 --- a/src/1-one/pointers/api/index.js +++ b/src/1-one/pointers/api/index.js @@ -56,7 +56,7 @@ methods.not = function (m) { } methods.difference = methods.not -// get opposite of a +// get opposite of a match methods.complement = function () { let doc = this.all() let ptrs = getDifference(doc.fullPointer, this.fullPointer) @@ -74,7 +74,6 @@ methods.settle = function () { return this.update(ptrs) } - const addAPI = function (View) { // add set/intersection/union Object.assign(View.prototype, methods) From d5f96b61358ec8b35d0fcc715f14ac2bf9f444d8 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Tue, 13 Feb 2024 13:51:10 -0500 Subject: [PATCH 18/34] date debug okay --- changelog.md | 4 ++++ plugins/dates/scratch.js | 7 +++---- plugins/dates/src/debug.js | 32 +++++++++++++++++++++++--------- scratch.js | 4 ++-- 4 files changed, 32 insertions(+), 15 deletions(-) diff --git a/changelog.md b/changelog.md index 823802215..593c89a26 100644 --- a/changelog.md +++ b/changelog.md @@ -28,8 +28,12 @@ While all _Major_ releases should be reviewed, our only _large_ releases are **v - **[fix]** - .has() inconsistency - **[new]** - support adding debug methods via plugins - **[change]** - remove deprecated .debug(object) support +- **[fix]** - parentheses() match issue - **[new]** - `dates().isBefore()`, `dates().isBefore()` +- **[new]** - `debug('dates')` + +- **[fix]** - .not() overlap detection - **[fix]** - join() issue - **[fix]** - .before() .after() diff --git a/plugins/dates/scratch.js b/plugins/dates/scratch.js index a9cbc5fb9..9e040eb2e 100644 --- a/plugins/dates/scratch.js +++ b/plugins/dates/scratch.js @@ -8,12 +8,10 @@ nlp.plugin(datePlugin) // nlp.verbose('tagger') // nlp.verbose('date') -const fmt = (iso) => (iso ? spacetime(iso).format('{day-short} {nice} {year}') : '-') +const fmt = iso => (iso ? spacetime(iso).format('{day-short} {nice} {year}') : '-') // process.env.DEBUG_DATE = true - - // date issues: // 'the month before christmas' vs 'a month before christmas' // middle september @@ -37,6 +35,7 @@ const context = { let txt = ` We will see him in mid-September` txt = `5th day of q1 2002` +txt = 'between Sept and Oct 2008' // txt = `tomorrow at 5:45pm` // txt = 'aug. 3' // txt = 'lets meet 1 weeks from now ' @@ -62,4 +61,4 @@ let doc = nlp(txt).debug() // let doc = nlp(txt).debug() // let m = doc.dates(context) -// console.log(m.get()) \ No newline at end of file +// console.log(m.get()) diff --git a/plugins/dates/src/debug.js b/plugins/dates/src/debug.js index 8954533c8..28f29fa06 100644 --- a/plugins/dates/src/debug.js +++ b/plugins/dates/src/debug.js @@ -1,21 +1,35 @@ /* eslint-disable no-console */ import spacetime from 'spacetime' -const fmt = iso => (iso ? spacetime(iso).format('{day-short} {nice} {year}') : '-') +const fmt = iso => (iso ? spacetime(iso).format('{nice-day} {year}') : '-') + +// https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color +const reset = '\x1b[0m' + +//cheaper than requiring chalk +const cli = { + magenta: str => '\x1b[35m' + str + reset, + cyan: str => '\x1b[36m' + str + reset, + yellow: str => '\x1b[33m' + str + reset, + dim: str => '\x1b[2m' + str + reset, + i: str => '\x1b[3m' + str + reset, +} const debug = function (view) { - console.log('=-=-=-= here -=-=-=-') view.dates().forEach(m => { - // console.log(m.dates().get()) - // console.log(found[0].dates) - // found.forEach((o) => { - // console.log('start: ', fmt(o.dates.start)) - // console.log(' end: ', fmt(o.dates.end)) - // }) + let res = m.dates().get()[0] console.log('\n────────') m.debug('highlight') - console.log('\n') + + let msg = '' + if (res && res.start) { + msg = ' ' + cli.magenta(fmt(res.start)) + } + if (res && res.end) { + msg += cli.dim(' → ') + cli.cyan(fmt(res.end)) + } + console.log(msg + '\n') }) } export default debug diff --git a/scratch.js b/scratch.js index 835628d34..b1ad686cb 100644 --- a/scratch.js +++ b/scratch.js @@ -9,8 +9,8 @@ nlp.plugin(plg) // let b = doc.match('. three') // a.difference(b).debug() -// let doc = nlp('and between Sept and Oct 2008, but then June 2010') -// doc.dates().debug('dates') +let doc = nlp('and between Sept and Oct 2008, but then June 2010') +doc.dates().debug('dates') // let m = doc.dates().isBefore('2009-01').debug() // let doc = nlp('one two three four five. one three four') From 386c1d64572dbd9fc80b900df5db0a1a87e68ec8 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Tue, 13 Feb 2024 15:18:35 -0500 Subject: [PATCH 19/34] isSame method --- changelog.md | 4 +- plugins/dates/README.md | 408 +++++++++++------------ plugins/dates/src/api/dates.js | 37 +- plugins/dates/tests/before-after.test.js | 36 +- scratch.js | 5 +- 5 files changed, 264 insertions(+), 226 deletions(-) diff --git a/changelog.md b/changelog.md index 593c89a26..ea29598d2 100644 --- a/changelog.md +++ b/changelog.md @@ -30,8 +30,8 @@ While all _Major_ releases should be reviewed, our only _large_ releases are **v - **[change]** - remove deprecated .debug(object) support - **[fix]** - parentheses() match issue -- **[new]** - `dates().isBefore()`, `dates().isBefore()` -- **[new]** - `debug('dates')` +- **[new]** - `dates().isBefore()`, `dates().isBefore()` methods +- **[new]** - `.debug('dates')` method - **[fix]** - .not() overlap detection - **[fix]** - join() issue diff --git a/plugins/dates/README.md b/plugins/dates/README.md index bcaebd344..96976f271 100644 --- a/plugins/dates/README.md +++ b/plugins/dates/README.md @@ -1,7 +1,7 @@
-
date-parsing plugin for compromise
+
date-parsing plugin for compromise
@@ -22,6 +22,7 @@ This library is an earnest attempt to get date information out of text, in a clear way - +
- including all informal text formats, and folksy shorthands.
@@ -29,7 +30,6 @@ This library is an earnest attempt to get date information out of text, in a cle - ```js import nlp from 'compromise' import datePlugin from 'compromise-dates' @@ -46,7 +46,6 @@ doc.dates().get()[0] -
@@ -70,155 +69,152 @@ doc.dates().get()[0] -
-### *Things it does well:* - -| `explicit-dates` | *description* | `Start` | `End` | -| ------------- |:-------------:| -----:| -----:| -| *march 2nd* | |March 2, 12:00am | March 2, 11:59pm -| *2 march* | | '' | '' -| *tues march 2*| | '' | '' -| *march the second* | *natural-language number* | '' | '' -| *on the 2nd* | *implicit months* | '' | '' -| *tuesday the 2nd* | *date-reckoning* | '' | '' -|
**`numeric-dates:`**| | -| *2020/03/02* |*iso formats* | '' | '' -| *2020-03-02* | | '' | '' -| *03-02-2020* |*british formats* | '' | '' -| *03/02* | | '' | '' -| *2020.08.13* | *alt-ISO*| '' | '' -|
**`named-dates:`**| | -| *today* | | - | - -| *tomorrow* | | '' | '' -| *christmas eve* |*calendar-holidays* | Dec 24, 12:00am | Dec 24, 11:59pm -| *easter* |*astronomical holidays* | -depends- | - -| *q1* | | Jan 1, 12:00am | Mar 31, 11:59pm -|
**`times:`**| | -| *2pm* | | '' | '' -| *2:12pm* | | '' | '' -| *2:12* | | '' | '' -| *02:12:00* | *weird iso-times*| '' | '' -| *two oclock* |*written formats* | '' | '' -| *before 1* | | '' | '' -| *noon* | | '' | '' -| *at night* | *informal daytimes* | '' | '' -| *in the morning* | | '' | '' -| *tomorrow evening* | | '' | '' -|
**`timezones:`**| | -| *eastern time* | *informal zone support*| '' | '' -| *est* |*TZ shorthands* | '' | '' -| *peru time* | | '' | '' -| *..in beirut* | *by location* | '' | '' -| *GMT+9* | *by UTC/GMT offset*| '' | '' -| *-4h* | '' | '' | '' -| *Canada/Eastern* | *IANA codes*| '' | '' -|
**`relative durations:`**| | -| *this march* | | '' | '' -| *this week* | | '' | '' -| *this sunday* | | '' | '' -| *next april* | | '' | '' -| *this past year* | | '' | '' -| *second week of march* | | '' | '' -| *last weekend of march* | | '' | '' -| *last spring* | | '' | '' -| *the saturday after next* | | '' | '' -|
**`punted dates:`**| | -| *in seven weeks* | *now+duration*| '' | '' -| *two days after june 6th* | *date+duration*| '' | '' -| *2 weeks from now* | | '' | '' -| *2 weeks after june* | | '' | '' -| *2 years, 4 months, and 5 days ago* | *complex durations*| '' | '' -| *a week and a half before* | *written-out numbers*| '' | '' -| *a week friday* | *idiom format*| '' | '' -|
**`start/end:`**| | -| *end of the week* | *up-against the ending* | '' | '' -| *start of next year* | *lean-toward starting*| '' | '' -| *middle of q2 last year* |*rough-center calculation* | '' | '' -|
**`date-ranges:`**| | -| *between june and july* |*explicit ranges* | '' | '' -| *from today to next haloween* | | '' | '' -| *aug 1 - aug 31* | *dash-ranges*| '' | '' -| *22-23 February* | | '' | '' -| *today to next friday* | | '' | '' -| *during june* | | '' | '' -| *aug to june 1999* | *shared range info*| '' | '' -| *before [2019]* |*up-to a date* | '' | '' -| *by march* | | '' | '' -| *after february* | *date-to-infinity*| '' | '' -|
**`repeating-intervals:`**| | -| *any wednesday* | *n-repeating dates* | | -| *any day in June* | *repeating-date in range* | June 1 ... | .. June 30 -| *any wednesday this week* | | '' | '' -| *weekends in July* | *more-complex interval*| '' | '' -| *every weekday until February* | *interval until date*| '' | '' +### _Things it does well:_ + +| `explicit-dates` | _description_ | `Start` | `End` | +| ----------------------------------- | :-----------------------------------: | ---------------: | ---------------: | +| _march 2nd_ | | March 2, 12:00am | March 2, 11:59pm | +| _2 march_ | | '' | '' | +| _tues march 2_ | | '' | '' | +| _march the second_ | _natural-language number_ | '' | '' | +| _on the 2nd_ | _implicit months_ | '' | '' | +| _tuesday the 2nd_ | _date-reckoning_ | '' | '' | +|
**`numeric-dates:`** | | +| _2020/03/02_ | _iso formats_ | '' | '' | +| _2020-03-02_ | | '' | '' | +| _03-02-2020_ | _british formats_ | '' | '' | +| _03/02_ | | '' | '' | +| _2020.08.13_ | _alt-ISO_ | '' | '' | +|
**`named-dates:`** | | +| _today_ | | - | - | +| _tomorrow_ | | '' | '' | +| _christmas eve_ | _calendar-holidays_ | Dec 24, 12:00am | Dec 24, 11:59pm | +| _easter_ | _astronomical holidays_ | -depends- | - | +| _q1_ | | Jan 1, 12:00am | Mar 31, 11:59pm | +|
**`times:`** | | +| _2pm_ | | '' | '' | +| _2:12pm_ | | '' | '' | +| _2:12_ | | '' | '' | +| _02:12:00_ | _weird iso-times_ | '' | '' | +| _two oclock_ | _written formats_ | '' | '' | +| _before 1_ | | '' | '' | +| _noon_ | | '' | '' | +| _at night_ | _informal daytimes_ | '' | '' | +| _in the morning_ | | '' | '' | +| _tomorrow evening_ | | '' | '' | +|
**`timezones:`** | | +| _eastern time_ | _informal zone support_ | '' | '' | +| _est_ | _TZ shorthands_ | '' | '' | +| _peru time_ | | '' | '' | +| _..in beirut_ | _by location_ | '' | '' | +| _GMT+9_ | _by UTC/GMT offset_ | '' | '' | +| _-4h_ | '' | '' | '' | +| _Canada/Eastern_ | _IANA codes_ | '' | '' | +|
**`relative durations:`** | | +| _this march_ | | '' | '' | +| _this week_ | | '' | '' | +| _this sunday_ | | '' | '' | +| _next april_ | | '' | '' | +| _this past year_ | | '' | '' | +| _second week of march_ | | '' | '' | +| _last weekend of march_ | | '' | '' | +| _last spring_ | | '' | '' | +| _the saturday after next_ | | '' | '' | +|
**`punted dates:`** | | +| _in seven weeks_ | _now+duration_ | '' | '' | +| _two days after june 6th_ | _date+duration_ | '' | '' | +| _2 weeks from now_ | | '' | '' | +| _2 weeks after june_ | | '' | '' | +| _2 years, 4 months, and 5 days ago_ | _complex durations_ | '' | '' | +| _a week and a half before_ | _written-out numbers_ | '' | '' | +| _a week friday_ | _idiom format_ | '' | '' | +|
**`start/end:`** | | +| _end of the week_ | _up-against the ending_ | '' | '' | +| _start of next year_ | _lean-toward starting_ | '' | '' | +| _middle of q2 last year_ | _rough-center calculation_ | '' | '' | +|
**`date-ranges:`** | | +| _between june and july_ | _explicit ranges_ | '' | '' | +| _from today to next haloween_ | | '' | '' | +| _aug 1 - aug 31_ | _dash-ranges_ | '' | '' | +| _22-23 February_ | | '' | '' | +| _today to next friday_ | | '' | '' | +| _during june_ | | '' | '' | +| _aug to june 1999_ | _shared range info_ | '' | '' | +| _before [2019]_ | _up-to a date_ | '' | '' | +| _by march_ | | '' | '' | +| _after february_ | _date-to-infinity_ | '' | '' | +|
**`repeating-intervals:`** | | +| _any wednesday_ | _n-repeating dates_ | | +| _any day in June_ | _repeating-date in range_ | June 1 ... | .. June 30 | +| _any wednesday this week_ | | '' | '' | +| _weekends in July_ | _more-complex interval_ | '' | '' | +| _every weekday until February_ | _interval until date_ | '' | '' | -### *Things it does awkwardly:* -| *`hmmm,`* | *description* | `Start` | `End` | -| ------------- |:-------------:| :-------------:| :-------------:| -| *middle of 2019/June* | tries to find the sorta-center | June 15 | '' | -| *good friday 2025* | tries to reckon astronomically-set holidays| '' | '' | -| *Oct 22 1975 2am in PST* | historical DST changes (assumes current dates) | '' | '' | +### _Things it does awkwardly:_ + +| _`hmmm,`_ | _description_ | `Start` | `End` | +| ------------------------ | :--------------------------------------------: | :-----: | :---: | +| _middle of 2019/June_ | tries to find the sorta-center | June 15 | '' | +| _good friday 2025_ | tries to reckon astronomically-set holidays | '' | '' | +| _Oct 22 1975 2am in PST_ | historical DST changes (assumes current dates) | '' | '' | -### *Things it doesn't do:* -| *😓,* | *description* | `Start` | `End` | -| ------------- |:-------------:| :-------------:| :-------------:| -| *not this Saturday, but the Saturday after* | self-reference logic | '' | '' | -| *3 years ago tomorrow* | folksy short-hand | '' | '' | -| *2100* | military time formats | '' | '' | -| *may 97* | 'bare' 2-digit years | '' | '' | +### _Things it doesn't do:_ +| _😓,_ | _description_ | `Start` | `End` | +| ------------------------------------------- | :----------------------: | :-----: | :---: | +| _not this Saturday, but the Saturday after_ | self-reference logic | '' | '' | +| _3 years ago tomorrow_ | folksy short-hand | '' | '' | +| _2100_ | military time formats | '' | '' | +| _may 97_ | 'bare' 2-digit years | '' | '' | - - -
- + +
+
## API -- **.dates()** - find dates like `June 8th` or `03/03/18` - - **.dates().get()** - parsed dates as json - - **.dates().json()** - overloaded json output with date metadata - - **.dates().format('')** - convert the dates to specific formats - -- **.durations()** - find unspecified lengths of time like `two hours and 30mins` - - **.durations().get()** - parsed durations as json - - **.durations().json()** - overloaded json output with duration metadata - - **.durations().normalize()** - turn 3mins into 3 minutes - -- **.times()** - find day-times like `three oclock` - - **.times().get()** - parsed times as json - - **.times().json()** - overloaded json output with time metadata - - **.times().format(fmt)** - convert between time formats '24h' | '{hour-pad}:{minute-pad}' [etc](https://github.com/spencermountain/spacetime/wiki/Formatting) - - **.times().normalize()** - turn 3mins into 3 minutes - - +- **[.dates()](https://observablehq.com/@spencermountain/compromise-dates)** - find dates like `June 8th` or `03/03/18` + - **[.dates().get()](https://observablehq.com/@spencermountain/compromise-dates)** - simple start/end json result + - **[.dates().json()](https://observablehq.com/@spencermountain/compromise-dates)** - overloaded output with date metadata + - **[.dates().format('')](https://observablehq.com/@spencermountain/compromise-dates)** - convert the dates to specific formats + - **[.dates().isBefore(iso)](https://observablehq.com/@spencermountain/compromise-dates)** - return only dates occuring before given date + - **[.dates().isAfter(iso)](https://observablehq.com/@spencermountain/compromise-dates)** - return only dates occuring after given date + - **[.dates().isSame(unit, iso)](https://observablehq.com/@spencermountain/compromise-dates)** - return only dates within a given year, month, date +- **[.durations()](https://observablehq.com/@spencermountain/compromise-dates)** - `2 weeks` or `5mins` + - **[.durations().get()](https://observablehq.com/@spencermountain/compromise-dates)** - return simple json for duration + - **[.durations().json()](https://observablehq.com/@spencermountain/compromise-dates)** - overloaded output with duration metadata +- **[.times()](https://observablehq.com/@spencermountain/compromise-dates)** - `4:30pm` or `half past five` + - **[.durations().get()](https://observablehq.com/@spencermountain/compromise-dates)** - return simple json for times + - **[.times().json()](https://observablehq.com/@spencermountain/compromise-dates)** - overloaded output with time metadata - + ### Configuration: + `.dates()` accepts an optional object, that lets you set the context for the date parsing. + ```js const context = { timezone: 'Canada/Eastern', //the default timezone is 'ETC/UTC' today: '2020-02-20', //the implicit, or reference day/year punt: { weeks: 2 }, // the implied duration to use for 'after june 2nd' dayStart: '8:00am', - dayEnd: '5:30pm' + dayEnd: '5:30pm', } nlp('in two days').dates(context).get() @@ -227,117 +223,111 @@ nlp('in two days').dates(context).get() */ ``` - - - -
- -
- -## API -- **[.dates()](https://observablehq.com/@spencermountain/compromise-dates)** - find dates like `June 8th` or `03/03/18` - - **[.dates().get()](https://observablehq.com/@spencermountain/compromise-dates)** - simple start/end json result - - **[.dates().json()](https://observablehq.com/@spencermountain/compromise-dates)** - overloaded output with date metadata - - **[.dates().format('')](https://observablehq.com/@spencermountain/compromise-dates)** - convert the dates to specific formats - - **[.dates().toShortForm()](https://observablehq.com/@spencermountain/compromise-dates)** - convert 'Wednesday' to 'Wed', etc - - **[.dates().toLongForm()](https://observablehq.com/@spencermountain/compromise-dates)** - convert 'Feb' to 'February', etc -- **[.durations()](https://observablehq.com/@spencermountain/compromise-dates)** - `2 weeks` or `5mins` - - **[.durations().get()](https://observablehq.com/@spencermountain/compromise-dates)** - return simple json for duration - - **[.durations().json()](https://observablehq.com/@spencermountain/compromise-dates)** - overloaded output with duration metadata -- **[.times()](https://observablehq.com/@spencermountain/compromise-dates)** - `4:30pm` or `half past five` - - **[.durations().get()](https://observablehq.com/@spencermountain/compromise-dates)** - return simple json for times - - **[.times().json()](https://observablehq.com/@spencermountain/compromise-dates)** - overloaded output with time metadata - - - - -## *Opinions*: +## _Opinions_: -### *Start of week:* -By default, weeks start on a Monday, and *'next week'* will run from Monday morning to Sunday night. +### _Start of week:_ + +By default, weeks start on a Monday, and _'next week'_ will run from Monday morning to Sunday night. This can be configued in spacetime, but right now we are not passing-through this config. -### *Implied durations:* -*'after October'* returns a range starting **Nov 1st**, and ending **2-weeks** after, by default. +### _Implied durations:_ + +_'after October'_ returns a range starting **Nov 1st**, and ending **2-weeks** after, by default. This can be configured by setting `punt` param in the context object: + ```js -doc.dates({punt: { month: 1 }}) +doc.dates({ punt: { month: 1 } }) ``` -### *Future bias:* -*'May 7th'* will prefer a May 7th in the future. +### _Future bias:_ + +_'May 7th'_ will prefer a May 7th in the future. The parser will return a past-date though, in the current-month: + ```js // from march 2nd -nlp('feb 30th').dates({today: '2021-02-01'}).get() - +nlp('feb 30th').dates({ today: '2021-02-01' }).get() ``` -### *This/Next/Last:* -named-weeks or months eg *'this/next/last week'* are mostly straight-forward. +### _This/Next/Last:_ -#### *This monday* -A bare 'monday' will always refer to itself, or the upcoming monday. +named-weeks or months eg _'this/next/last week'_ are mostly straight-forward. -* Saying *'this monday'* on monday, is itself. -* Saying *'this monday'* on tuesday , is next week. +#### _This monday_ -Likewise, *'this june'* in June, is itself. *'this june'* in any other month, is the nearest June in the future. +A bare 'monday' will always refer to itself, or the upcoming monday. -Future versions of this library could look at sentence-tense to help disambiguate these dates - *'i paid on monday'* vs *'i will pay on monday'*. +- Saying _'this monday'_ on monday, is itself. +- Saying _'this monday'_ on tuesday , is next week. -#### *Last monday* -If it's Tuesday, *'last monday'* will not mean yesterday. +Likewise, _'this june'_ in June, is itself. _'this june'_ in any other month, is the nearest June in the future. -* Saying *'last monday'* on a tuesday will be -1 week. -* Saying *'a week ago monday'* will also work. -* Saying *'this past monday'* will return yesterday. +Future versions of this library could look at sentence-tense to help disambiguate these dates - _'i paid on monday'_ vs _'i will pay on monday'_. + +#### _Last monday_ + +If it's Tuesday, _'last monday'_ will not mean yesterday. + +- Saying _'last monday'_ on a tuesday will be -1 week. +- Saying _'a week ago monday'_ will also work. +- Saying _'this past monday'_ will return yesterday. For reference, **Wit.ai** & **chronic** libraries both return yesterday. **Natty** and **SugarJs** returns -1 week, like we do. -*'last X'* can be less than 7 days backward, if it crosses a week starting-point: -* Saying *'last friday'* on a monday will be only a few days back. +_'last X'_ can be less than 7 days backward, if it crosses a week starting-point: + +- Saying _'last friday'_ on a monday will be only a few days back. + +#### _Next Friday_ -#### *Next Friday* -If it's Tuesday, *'next wednesday'* will not be tomorrow. It will be a week after tomorrow. +If it's Tuesday, _'next wednesday'_ will not be tomorrow. It will be a week after tomorrow. -* Saying *'next wednesday'* on a tuesday, will be +1 week. -* Saying *'a week wednesday'* will also be +1 week. -* Saying *'this coming wednesday'* will be tomorrow. +- Saying _'next wednesday'_ on a tuesday, will be +1 week. +- Saying _'a week wednesday'_ will also be +1 week. +- Saying _'this coming wednesday'_ will be tomorrow. For reference, **Wit.ai**, **chronic**, and **Natty** libraries all return tomorrow. **SugarJs** returns +1 week, like we do. -### *Nth Week:* -The first week of a month, or a year is the first week *with a thursday in it*. This is a weird, but widely-held standard. I believe it's a military formalism. It cannot be (easily) configued. This means that the start-date for *first week of January* may be a Monday in December, etc. +### _Nth Week:_ -As expected, *first monday of January* will always be in January. +The first week of a month, or a year is the first week _with a thursday in it_. This is a weird, but widely-held standard. I believe it's a military formalism. It cannot be (easily) configued. This means that the start-date for _first week of January_ may be a Monday in December, etc. + +As expected, _first monday of January_ will always be in January. + +### _British/American ambiguity:_ -### *British/American ambiguity:* by default, we use the same interpretation of dates as javascript does - we assume `01/02/2020` is Jan 2nd, (US-version) but allow `13/01/2020` to be Jan 13th (UK-version). This should be possible to configure in the near future. -### *Seasons:* -By default, *'this summer'* will return **June 1 - Sept 1**, which is northern hemisphere ISO. +### _Seasons:_ + +By default, _'this summer'_ will return **June 1 - Sept 1**, which is northern hemisphere ISO. Configuring the default hemisphere should be possible in the future. -### *Day times:* -There are some hardcoded times for *'lunch time'* and others, but mainly, a day begins at `12:00am` and ends at `11:59pm` - the last millisecond of the day. +### _Day times:_ + +There are some hardcoded times for _'lunch time'_ and others, but mainly, a day begins at `12:00am` and ends at `11:59pm` - the last millisecond of the day. + +### _Invalid dates:_ -### *Invalid dates:* compromise will tag anything that looks like a date, but not validate the dates until they are parsed. -* *'january 34th 2020'* will return **Jan 31 2020**. -* *'tomorrow at 2:62pm'* will return just return 'tomorrow'. -* *'6th week of february* will return the 2nd week of march. -* Setting an hour that's skipped, or repeated by a DST change will return the closest valid time to the DST change. -### *Inclusive/exclusive ranges:* -*'between january and march'* will include all of march. This is usually pretty-ambiguous normally. +- _'january 34th 2020'_ will return **Jan 31 2020**. +- _'tomorrow at 2:62pm'_ will return just return 'tomorrow'. +- _'6th week of february_ will return the 2nd week of march. +- Setting an hour that's skipped, or repeated by a DST change will return the closest valid time to the DST change. + +### _Inclusive/exclusive ranges:_ + +_'between january and march'_ will include all of march. This is usually pretty-ambiguous normally. + +### _Date greediness:_ -### *Date greediness:* This library makes no assumptions about the input text, and is careful to avoid false-positive dates. If you know your text is a date, you can crank-up the date-tagger with a [compromise-plugin](https://observablehq.com/@spencermountain/compromise-plugins), like so: + ```js nlp.extend(function (Doc, world) { // ambiguous words @@ -347,7 +337,7 @@ nlp.extend(function (Doc, world) { sat: 'WeekDay', sun: 'WeekDay', }) - world.postProcess((doc) => { + world.postProcess(doc => { // tag '2nd quarter' as a date doc.match('#Ordinal quarter').tag('#Date') // tag '2/2' as a date (not a fraction) @@ -356,12 +346,12 @@ nlp.extend(function (Doc, world) { }) ``` +### _Misc:_ -### *Misc:* -* *'thursday the 16th'* - will set to the 16th, even if it's not thursday -* *'in a few hours/years'* - in 2 hours/years -* *'jan 5th 2008 to Jan 6th the following year'* - date-range explicit references -* assume *'half past 5'* is 5pm +- _'thursday the 16th'_ - will set to the 16th, even if it's not thursday +- _'in a few hours/years'_ - in 2 hours/years +- _'jan 5th 2008 to Jan 6th the following year'_ - date-range explicit references +- assume _'half past 5'_ is 5pm
@@ -372,12 +362,11 @@ nlp.extend(function (Doc, world) {
-### *About:* +### _About:_ -
1 - Regular-expressions are too-brittle to parse dates.
@@ -390,27 +379,26 @@ nlp.extend(function (Doc, world) { 3 - A corporation, or startup is the wrong place to build a universal date-parser.
- -Parsing *dates*, *times*, *durations*, and *intervals* from natural language can be a solved-problem. +Parsing _dates_, _times_, _durations_, and _intervals_ from natural language can be a solved-problem. -A rule-based, community open-source library - *one based on simple NLP* - is the best way to build a natural language date parser - commercial, or otherwise - for the frontend, or the backend. - -The *[match-syntax](https://observablehq.com/@spencermountain/compromise-match-syntax)* is effective and easy, *javascript* is prevailing, and the more people who contribute, the better. +A rule-based, community open-source library - _one based on simple NLP_ - is the best way to build a natural language date parser - commercial, or otherwise - for the frontend, or the backend. +The _[match-syntax](https://observablehq.com/@spencermountain/compromise-match-syntax)_ is effective and easy, _javascript_ is prevailing, and the more people who contribute, the better. ### See also -* [Duckling](https://duckling.wit.ai/) - by wit.ai (facebook) -* [Sugarjs/dates](https://sugarjs.com/dates/) - by Andrew Plummer (js) -* [Chronic](https://github.com/mojombo/chronic) - by Tom Preston-Werner (Ruby) -* [SUTime](https://nlp.stanford.edu/software/sutime.shtml) - by Angel Chang, Christopher Manning (Java) -* [Natty](http://natty.joestelmach.com/) - by Joe Stelmach (Java) -* [rrule](https://github.com/jakubroztocil/rrule) - repeating date-interval handler (js) -* [ParseDateTime](https://pypi.org/project/parsedatetime/) by Mike Taylor (Python) + +- [Duckling](https://duckling.wit.ai/) - by wit.ai (facebook) +- [Sugarjs/dates](https://sugarjs.com/dates/) - by Andrew Plummer (js) +- [Chronic](https://github.com/mojombo/chronic) - by Tom Preston-Werner (Ruby) +- [SUTime](https://nlp.stanford.edu/software/sutime.shtml) - by Angel Chang, Christopher Manning (Java) +- [Natty](http://natty.joestelmach.com/) - by Joe Stelmach (Java) +- [rrule](https://github.com/jakubroztocil/rrule) - repeating date-interval handler (js) +- [ParseDateTime](https://pypi.org/project/parsedatetime/) by Mike Taylor (Python)
diff --git a/plugins/dates/src/api/dates.js b/plugins/dates/src/api/dates.js index f6772ff58..54232e595 100644 --- a/plugins/dates/src/api/dates.js +++ b/plugins/dates/src/api/dates.js @@ -2,6 +2,15 @@ import find from './find/index.js' import parseDates from './parse/index.js' import toJSON from './toJSON.js' +const quickDate = function (view, str) { + let tmp = view.fromText(str) + let found = parseDates(tmp, view.opts)[0] + if (!found || !found.start || !found.start.d) { + return null + } + return found.start.d +} + const api = function (View) { class Dates extends View { constructor(document, pointer, groups, opts = {}) { @@ -57,18 +66,26 @@ const api = function (View) { /** return only dates occuring before a given date */ isBefore(iso) { - let tmp = this.fromText(iso) - let found = parseDates(tmp, this.opts)[0] - if (!found || !found.start || !found.start.d) { - return this.none() //return nothing - } - let pivot = found.start.d // our given date + let pivot = quickDate(this, iso) return this.filter(m => { let obj = parseDates(m, this.opts)[0] || {} - if (!obj.start || !obj.start.d) { - return false - } - return obj.start.d.isBefore(pivot) + return obj.start && obj.start.d && obj.start.d.isBefore(pivot) + }) + } + /** return only dates occuring after a given date */ + isAfter(iso) { + let pivot = quickDate(this, iso) + return this.filter(m => { + let obj = parseDates(m, this.opts)[0] || {} + return obj.start && obj.start.d && obj.start.d.isAfter(pivot) + }) + } + /** return only dates occuring after a given date */ + isSame(unit, iso) { + let pivot = quickDate(this, iso) + return this.filter(m => { + let obj = parseDates(m, this.opts)[0] || {} + return obj.start && obj.start.d && obj.start.d.isSame(pivot, unit) }) } } diff --git a/plugins/dates/tests/before-after.test.js b/plugins/dates/tests/before-after.test.js index a24b82574..07a32def0 100644 --- a/plugins/dates/tests/before-after.test.js +++ b/plugins/dates/tests/before-after.test.js @@ -7,9 +7,41 @@ test('isBefore misc', function (t) { let m = doc.dates().isBefore('2000-02-01') t.deepEqual(m.out('array'), ['June 5th 1999'], here + 'days') - doc = nlp('and between Sept and Oct 2008, but then June 2010') + doc = nlp('and between Sept 2008 and Oct 2008 but then June 2010') m = doc.dates().isBefore('2009-01') - t.deepEqual(m.out('array'), ['between Sept and Oct 2008'], here + 'months') + t.deepEqual(m.out('array'), ['between Sept 2008 and Oct 2008'], here + 'months') + + doc = nlp('only in 2018 and 2020') + m = doc.dates().isBefore('2019') + t.deepEqual(m.out('array'), ['2018'], here + 'years') + + doc = nlp('saw him 2024/02/05 and 2024/03/09') + m = doc.dates().isBefore('2025') + t.deepEqual(m.out('array'), ['2024/02/05', '2024/03/09'], here + 'isos') + + t.end() +}) + +test('isAfter misc', function (t) { + let doc = nlp('i went on June 5th 1999 and then on April 7 2008') + let m = doc.dates().isAfter('2000-02-01') + t.deepEqual(m.out('array'), ['April 7 2008'], here + 'after-days') + + doc = nlp('saw him 2024/02/05 and 2024/03/09') + m = doc.dates().isAfter('2020') + t.deepEqual(m.out('array'), ['2024/02/05', '2024/03/09'], here + 'after-isos') + + t.end() +}) + +test('isSame', function (t) { + let doc = nlp('i went on June 5th 1999 and then on April 7 2008') + let m = doc.dates().isSame('year', '1999') + t.deepEqual(m.out('array'), ['June 5th 1999'], here + 'same-year') + + doc = nlp('saw him 2024-02-05 and 2024-03-09') + m = doc.dates().isSame('month', '2024-03-17') + t.deepEqual(m.out('array'), ['2024-03-09'], here + 'same-month') t.end() }) diff --git a/scratch.js b/scratch.js index b1ad686cb..c1afa34c4 100644 --- a/scratch.js +++ b/scratch.js @@ -9,9 +9,10 @@ nlp.plugin(plg) // let b = doc.match('. three') // a.difference(b).debug() -let doc = nlp('and between Sept and Oct 2008, but then June 2010') +// let doc = nlp('and between Sept and Oct 2008') +let doc = nlp('only in 2018 and 2020') doc.dates().debug('dates') -// let m = doc.dates().isBefore('2009-01').debug() +// let m = doc.dates().isBefore('2009-01').debug('dates') // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() From 7b95c8b594cbd01faf620f7e24b1f93863daadea Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Tue, 13 Feb 2024 17:20:34 -0500 Subject: [PATCH 20/34] fix for #1085 --- scratch.js | 15 +++++++-------- .../tokenize/methods/02-terms/01-hyphens.js | 2 +- .../compute/tagger/1st-pass/02-hyphens.js | 2 +- .../{hyphens.ignore.js => hyphens.test.js} | 19 +++++++++++++------ 4 files changed, 22 insertions(+), 16 deletions(-) rename tests/one/tokenize/{hyphens.ignore.js => hyphens.test.js} (86%) diff --git a/scratch.js b/scratch.js index c1afa34c4..cb99e415f 100644 --- a/scratch.js +++ b/scratch.js @@ -1,7 +1,7 @@ /* eslint-disable no-console, no-unused-vars */ -import nlp from './src/three.js' -import plg from './plugins/dates/src/plugin.js' -nlp.plugin(plg) +import nlp from './src/one.js' +// import plg from './plugins/dates/src/plugin.js' +// nlp.plugin(plg) // nlp.verbose('tagger') // let doc = nlp('one two three four') @@ -10,17 +10,16 @@ nlp.plugin(plg) // a.difference(b).debug() // let doc = nlp('and between Sept and Oct 2008') -let doc = nlp('only in 2018 and 2020') -doc.dates().debug('dates') +// let doc = nlp('only in 2018 and 2020') +// doc.dates().debug('dates') // let m = doc.dates().isBefore('2009-01').debug('dates') // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() // doc.debug() -// let doc = nlp('2 to 3 people') -// doc.match('#Value [to] #Value', 0).replaceWith('-') -// console.log(doc.text()) +let doc = nlp(`The pro-choice movement`).debug() +// nlp('10-ounce (12-ounce)').debug() // let doc = nlp('one foo two foo') // let m = doc.terms() diff --git a/src/1-one/tokenize/methods/02-terms/01-hyphens.js b/src/1-one/tokenize/methods/02-terms/01-hyphens.js index e3b160fd5..80d93abfc 100644 --- a/src/1-one/tokenize/methods/02-terms/01-hyphens.js +++ b/src/1-one/tokenize/methods/02-terms/01-hyphens.js @@ -24,7 +24,7 @@ const hasHyphen = function (str, model) { return true } //number-letter '20-aug' - let reg2 = /^([0-9]{1,4})[-–—]([a-z\u00C0-\u00FF`"'/-]+$)/i + let reg2 = /^[('"]?([0-9]{1,4})[-–—]([a-z\u00C0-\u00FF`"'/-]+[)'"]?$)/i if (reg2.test(str) === true) { return true } diff --git a/src/2-two/preTagger/compute/tagger/1st-pass/02-hyphens.js b/src/2-two/preTagger/compute/tagger/1st-pass/02-hyphens.js index ed095906b..83a3f1cdc 100644 --- a/src/2-two/preTagger/compute/tagger/1st-pass/02-hyphens.js +++ b/src/2-two/preTagger/compute/tagger/1st-pass/02-hyphens.js @@ -14,4 +14,4 @@ const byHyphen = function (terms, i, model, world) { // } } } -export default byHyphen \ No newline at end of file +export default byHyphen diff --git a/tests/one/tokenize/hyphens.ignore.js b/tests/one/tokenize/hyphens.test.js similarity index 86% rename from tests/one/tokenize/hyphens.ignore.js rename to tests/one/tokenize/hyphens.test.js index 6015dfa03..a5088bacf 100644 --- a/tests/one/tokenize/hyphens.ignore.js +++ b/tests/one/tokenize/hyphens.test.js @@ -2,6 +2,15 @@ import test from 'tape' import nlp from '../_lib.js' const here = '[one/hyphens] ' +test('nums-punctuation', function (t) { + let doc = nlp('10-ounce (12-ounce)') + t.equal(doc.terms().length, 4, here + 'w/ brackets') + + // doc = nlp('2-for-1') + // t.equal(doc.terms().length, 3, here + 'w/ numbers') + + t.end() +}) test('independence', function (t) { let doc @@ -20,8 +29,8 @@ test('independence', function (t) { t.equal(doc.has('181181'), false, here + 'combined-number') doc = nlp(`The pro-choice movement`) - t.equal(doc.has('pro'), true, here + 'pro') - t.equal(doc.has('choice'), true, here + 'choice') + // t.equal(doc.has('pro'), true, here + 'pro') + // t.equal(doc.has('choice'), true, here + 'choice') t.equal(doc.has('pro-choice'), true, here + 'pro-choice') t.equal(doc.has('prochoice'), true, here + 'prochoice') t.equal(doc.has('the pro movement'), false, here + 'pro movement') @@ -43,13 +52,13 @@ test('independence', function (t) { doc = nlp('re-do and reuse') t.equal(doc.has('redo'), true, here + 're- one word') t.equal(doc.has('re-do'), true, here + 're- dashed word') - t.equal(doc.has('re do'), true, here + 'two words') + // t.equal(doc.has('re do'), true, here + 'two words') t.equal(doc.has('re and'), false, here + 're and') doc = nlp('inter-species communication') t.equal(doc.has('interspecies'), true, here + 'inter one word') t.equal(doc.has('inter-species'), true, here + 'inter dashed word') - t.equal(doc.has('inter species'), true, here + 'inter two words') + // t.equal(doc.has('inter species'), true, here + 'inter two words') t.equal(doc.has('interspecies communication'), true, here + 'interspecies communication') t.equal(doc.has('inter communication'), false, here + 'inter communication') @@ -70,5 +79,3 @@ test('independence', function (t) { t.end() }) - - From adf28d07b404459a38538dde4e3b9474a025a8f7 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Tue, 13 Feb 2024 17:27:22 -0500 Subject: [PATCH 21/34] fix penn mapping for #1090 --- scratch.js | 12 ++++-- src/2-two/preTagger/compute/penn.js | 64 ++++++++++++++--------------- 2 files changed, 41 insertions(+), 35 deletions(-) diff --git a/scratch.js b/scratch.js index cb99e415f..6dbf268d0 100644 --- a/scratch.js +++ b/scratch.js @@ -1,5 +1,5 @@ /* eslint-disable no-console, no-unused-vars */ -import nlp from './src/one.js' +import nlp from './src/three.js' // import plg from './plugins/dates/src/plugin.js' // nlp.plugin(plg) // nlp.verbose('tagger') @@ -18,8 +18,14 @@ import nlp from './src/one.js' // doc.before('three four').debug() // doc.debug() -let doc = nlp(`The pro-choice movement`).debug() -// nlp('10-ounce (12-ounce)').debug() +function CompromiseTagger(word) { + const doc = nlp(word) + doc.compute('penn') + const terms = doc.out('json')[0].terms[0] + return terms.penn +} +console.log(CompromiseTagger('bishop')) +console.log(CompromiseTagger('doctor')) // let doc = nlp('one foo two foo') // let m = doc.terms() diff --git a/src/2-two/preTagger/compute/penn.js b/src/2-two/preTagger/compute/penn.js index e96836c85..b8cfb731e 100644 --- a/src/2-two/preTagger/compute/penn.js +++ b/src/2-two/preTagger/compute/penn.js @@ -5,49 +5,49 @@ const mapping = { // adverbs // 'Comparative': 'RBR', // 'Superlative': 'RBS', - 'Adverb': 'RB', + Adverb: 'RB', // adjectives - 'Comparative': 'JJR', - 'Superlative': 'JJS', - 'Adjective': 'JJ', - 'TO': 'Conjunction', + Comparative: 'JJR', + Superlative: 'JJS', + Adjective: 'JJ', + TO: 'Conjunction', // verbs - 'Modal': 'MD', - 'Auxiliary': 'MD', - 'Gerund': 'VBG', //throwing - 'PastTense': 'VBD', //threw - 'Participle': 'VBN', //thrown - 'PresentTense': 'VBZ', //throws - 'Infinitive': 'VB', //throw - 'Particle': 'RP', //phrasal particle - 'Verb': 'VB', // throw + Modal: 'MD', + Auxiliary: 'MD', + Gerund: 'VBG', //throwing + PastTense: 'VBD', //threw + Participle: 'VBN', //thrown + PresentTense: 'VBZ', //throws + Infinitive: 'VB', //throw + Particle: 'RP', //phrasal particle + Verb: 'VB', // throw // pronouns - 'Pronoun': 'PRP', + Pronoun: 'PRP', // misc - 'Cardinal': 'CD', - 'Conjunction': 'CC', - 'Determiner': 'DT', - 'Preposition': 'IN', + Cardinal: 'CD', + Conjunction: 'CC', + Determiner: 'DT', + Preposition: 'IN', // 'Determiner': 'WDT', // 'Expression': 'FW', - 'QuestionWord': 'WP', - 'Expression': 'UH', + QuestionWord: 'WP', + Expression: 'UH', //nouns - 'Possessive': 'POS', - 'ProperNoun': 'NNP', - 'Person': 'NNP', - 'Place': 'NNP', - 'Organization': 'NNP', - 'Singular': 'NNP', - 'Plural': 'NNS', - 'Noun': 'NN', + Possessive: 'POS', + ProperNoun: 'NNP', + Person: 'NNP', + Place: 'NNP', + Organization: 'NNP', + Singular: 'NN', + Plural: 'NNS', + Noun: 'NN', - 'There': 'EX', //'there' + There: 'EX', //'there' // 'Adverb':'WRB', // 'Noun':'PDT', //predeterminer // 'Noun':'SYM', //symbol @@ -56,7 +56,7 @@ const mapping = { // WDT Wh-determiner // WP Wh-pronoun // WP$ Possessive wh-pronoun - // WRB Wh-adverb + // WRB Wh-adverb } const toPenn = function (term) { @@ -91,4 +91,4 @@ const pennTag = function (view) { }) }) } -export default pennTag \ No newline at end of file +export default pennTag From fc9b61b24b8da02831461dc7443c60ebce616d72 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Fri, 16 Feb 2024 10:22:58 -0500 Subject: [PATCH 22/34] move plugin --- changelog.md | 3 ++ plugins/_experiments/payload/scratch.js | 18 ------- .../payload/tests/payload.test.js | 25 --------- plugins/{_experiments => }/payload/README.md | 21 +++++++- .../{_experiments => }/payload/package.json | 2 +- .../payload/rollup.config.js | 0 plugins/payload/scratch.js | 16 ++++++ .../{_experiments => }/payload/src/debug.js | 0 .../{_experiments => }/payload/src/plugin.js | 36 ++++++++++++- .../{_experiments => }/payload/tests/_lib.js | 4 +- plugins/payload/tests/payload.test.js | 54 +++++++++++++++++++ scratch.js | 10 ---- 12 files changed, 131 insertions(+), 58 deletions(-) delete mode 100644 plugins/_experiments/payload/scratch.js delete mode 100644 plugins/_experiments/payload/tests/payload.test.js rename plugins/{_experiments => }/payload/README.md (76%) rename plugins/{_experiments => }/payload/package.json (98%) rename plugins/{_experiments => }/payload/rollup.config.js (100%) create mode 100644 plugins/payload/scratch.js rename plugins/{_experiments => }/payload/src/debug.js (100%) rename plugins/{_experiments => }/payload/src/plugin.js (50%) rename plugins/{_experiments => }/payload/tests/_lib.js (75%) create mode 100644 plugins/payload/tests/payload.test.js diff --git a/changelog.md b/changelog.md index ea29598d2..25d761160 100644 --- a/changelog.md +++ b/changelog.md @@ -29,6 +29,9 @@ While all _Major_ releases should be reviewed, our only _large_ releases are **v - **[new]** - support adding debug methods via plugins - **[change]** - remove deprecated .debug(object) support - **[fix]** - parentheses() match issue +- **[fix]** - tokenization issue #1085 + +date-plugin: - **[new]** - `dates().isBefore()`, `dates().isBefore()` methods - **[new]** - `.debug('dates')` method diff --git a/plugins/_experiments/payload/scratch.js b/plugins/_experiments/payload/scratch.js deleted file mode 100644 index 753a65777..000000000 --- a/plugins/_experiments/payload/scratch.js +++ /dev/null @@ -1,18 +0,0 @@ -import nlp from '../../../src/three.js' -import plugin from './src/plugin.js' -nlp.extend(plugin) - -let doc = nlp('i saw John Lennon, and tom cruise.') - -doc.people().forEach(m => { - if (m.has('john lennon')) { - m.addPayload({ height: `5'11` }) - } - if (m.has('tom cruise')) { - m.addPayload({ height: `5'8` }) - } -}) -// console.log(doc.getPayloads()) -doc.debug('payload') -// let m = doc.match('and tom .').debug('payload') -// console.log(m.getPayloads()) diff --git a/plugins/_experiments/payload/tests/payload.test.js b/plugins/_experiments/payload/tests/payload.test.js deleted file mode 100644 index 777e0e3ea..000000000 --- a/plugins/_experiments/payload/tests/payload.test.js +++ /dev/null @@ -1,25 +0,0 @@ -import test from 'tape' -import nlp from './_lib.js' - -test('payload-misc', function (t) { - let doc = nlp('i saw John Lennon, and tom cruise.') - - doc.match('(john lennon|tom cruise|johnny carson)').forEach(m => { - if (m.has('john lennon')) { - m.addPayload({ height: `5'11` }) - } - if (m.has('tom cruise')) { - m.addPayload({ height: `5'8` }) - } - }) - - t.equal(doc.getPayloads().length, 2, 'full-doc-2') - - let end = doc.match('and tom .') - t.equal(end.getPayloads().length, 1, 'end-1') - - let tom = doc.match('tom') - t.equal(tom.getPayloads().length, 1, 'tom-0') - - t.end() -}) diff --git a/plugins/_experiments/payload/README.md b/plugins/payload/README.md similarity index 76% rename from plugins/_experiments/payload/README.md rename to plugins/payload/README.md index b4c69805f..03a0b9b22 100644 --- a/plugins/_experiments/payload/README.md +++ b/plugins/payload/README.md @@ -23,7 +23,7 @@ This plugin provides a facilty for storing a retreiving more complex data than tags, for compromise documents and matches. -### payload +### Payload ```js import plg from 'compromise-payload' @@ -42,4 +42,23 @@ doc.getPayloads() // [...] ``` +You can also pass a callback into `.addPayload()`: + +```js +let doc = nlp('i saw John Lennon, and john smith and bob dylan') +doc.people().addPayload(m => { + return { lastName: m.terms().last().text() } +}) +``` + +You can remove all, or selected payloads with `.clearPayload()`: + +```js +doc.match('bob .').clearPayloads() +doc.getPayloads().length // now 2 + +doc.clearPayloads() +doc.getPayloads().length // now 0 +``` + MIT diff --git a/plugins/_experiments/payload/package.json b/plugins/payload/package.json similarity index 98% rename from plugins/_experiments/payload/package.json rename to plugins/payload/package.json index 6872c1fa7..5778fdb85 100644 --- a/plugins/_experiments/payload/package.json +++ b/plugins/payload/package.json @@ -1,7 +1,7 @@ { "name": "compromise-payload", "description": "plugin for nlp-compromise", - "version": "0.1.0", + "version": "0.0.1", "author": "Spencer Kelly (http://spencermounta.in)", "main": "./src/plugin.js", "unpkg": "./builds/compromise-payload.min.js", diff --git a/plugins/_experiments/payload/rollup.config.js b/plugins/payload/rollup.config.js similarity index 100% rename from plugins/_experiments/payload/rollup.config.js rename to plugins/payload/rollup.config.js diff --git a/plugins/payload/scratch.js b/plugins/payload/scratch.js new file mode 100644 index 000000000..ae3eb4be4 --- /dev/null +++ b/plugins/payload/scratch.js @@ -0,0 +1,16 @@ +import nlp from '../../src/three.js' +import plugin from './src/plugin.js' +nlp.extend(plugin) + +let doc = nlp('i saw John Lennon, and john smith and bob dylan') +doc.match('(john|bob|dave) .').addPayload(m => { + return m.text().match(/john/i) ? { isjohn: true } : null +}) + +// console.log(doc.getPayloads()) +// doc.match('john .').clearPayloads() +console.log(doc.match('john .').getPayloads()) + +// doc.match('(john|bob|dave) .').addPayload(m => { +// return { lastName: m.terms().last().text() } +// }) diff --git a/plugins/_experiments/payload/src/debug.js b/plugins/payload/src/debug.js similarity index 100% rename from plugins/_experiments/payload/src/debug.js rename to plugins/payload/src/debug.js diff --git a/plugins/_experiments/payload/src/plugin.js b/plugins/payload/src/plugin.js similarity index 50% rename from plugins/_experiments/payload/src/plugin.js rename to plugins/payload/src/plugin.js index adf9e0fba..5de44191b 100644 --- a/plugins/_experiments/payload/src/plugin.js +++ b/plugins/payload/src/plugin.js @@ -17,6 +17,7 @@ export default { if (db.hasOwnProperty(n)) { // look at all vals for this sentence db[n].forEach(obj => { + console.log(obj.ptr) let m = this.update([obj.ptr]) if (this.has(m)) { res = res.concat({ @@ -36,7 +37,40 @@ export default { this.fullPointer.forEach(ptr => { let n = ptr[0] db[n] = db[n] || [] - db[n].push({ ptr, val }) + if (typeof val === 'function') { + //push in whatever the callback wants + let m = this.update([ptr]) + let res = val(m) + if (res !== null && res !== undefined) { + db[n].push({ ptr, val: res }) + } + } else { + db[n].push({ ptr, val }) //push some static data + } + }) + return this + } + + /** remove all payloads in selection */ + View.prototype.clearPayloads = function () { + let db = this.world.model.one.db || {} + // get each payload + let res = this.getPayloads() + res.forEach(obj => { + let ptr = obj.match.fullPointer[0] || [] + let [n, start, end] = ptr + // db[n] = db[n] || [] + // remove it from our list of payloads + db[n] = db[n].filter(r => { + if (r.ptr[1] === start && r.ptr[2] === end) { + return false + } + return true + }) + // clean-up any empty arrays + if (db[n].length === 0) { + delete db[n] + } }) return this } diff --git a/plugins/_experiments/payload/tests/_lib.js b/plugins/payload/tests/_lib.js similarity index 75% rename from plugins/_experiments/payload/tests/_lib.js rename to plugins/payload/tests/_lib.js index 12198ecf9..ba292e331 100644 --- a/plugins/_experiments/payload/tests/_lib.js +++ b/plugins/payload/tests/_lib.js @@ -1,5 +1,5 @@ -import build from '../../../../builds/one/compromise-one.mjs' -import src from '../../../../src/one.js' +import build from '../../../builds/one/compromise-one.mjs' +import src from '../../../src/one.js' // import plgBuild from '../builds/compromise-speech.mjs' import plg from '../src/plugin.js' let nlp diff --git a/plugins/payload/tests/payload.test.js b/plugins/payload/tests/payload.test.js new file mode 100644 index 000000000..1f81a46be --- /dev/null +++ b/plugins/payload/tests/payload.test.js @@ -0,0 +1,54 @@ +import test from 'tape' +import nlp from './_lib.js' + +test('payload-misc', function (t) { + let doc = nlp('i saw John Lennon, and tom cruise.') + + doc.match('(john lennon|tom cruise|johnny carson)').forEach(m => { + if (m.has('john lennon')) { + m.addPayload({ height: `5'11` }) + } + if (m.has('tom cruise')) { + m.addPayload({ height: `5'8` }) + } + }) + + t.equal(doc.getPayloads().length, 2, 'full-doc-2') + + let end = doc.match('and tom .') + t.equal(end.getPayloads().length, 1, 'end-1') + + let tom = doc.match('tom') + t.equal(tom.getPayloads().length, 1, 'tom-0') + + tom.clearPayloads() + t.equal(doc.getPayloads().length, 1, 'now-1') + doc.clearPayloads() + t.equal(doc.getPayloads().length, 0, 'now-0') + + t.end() +}) + +test('payload-fn', function (t) { + let doc = nlp('i saw John Lennon, and john smith and bob dylan') + doc.match('(john|bob|dave) .').addPayload(m => { + return m.text().match(/john/i) ? { isjohn: true } : null + }) + t.equal(doc.getPayloads().length, 2, 'now-2') + t.equal(doc.match('john .').getPayloads().length, 2, 'double-match-still-2') + + doc.match('bob .').clearPayloads() + t.equal(doc.getPayloads().length, 2, 'still-2') + + doc.match('john .').eq(1).clearPayloads() + t.equal(doc.getPayloads().length, 1, 'now-1') + doc.match('john .').clearPayloads() + t.equal(doc.getPayloads().length, 0, 'now-0') + + // add two-payloads per match + doc.match('(john|bob|dave) .').addPayload(m => { + return { lastName: m.terms().last().text() } + }) + t.equal(doc.getPayloads().length, 2, '2-again') + t.end() +}) diff --git a/scratch.js b/scratch.js index 6dbf268d0..5fb2bddc2 100644 --- a/scratch.js +++ b/scratch.js @@ -17,16 +17,6 @@ import nlp from './src/three.js' // let doc = nlp('one two three four five. one three four') // doc.before('three four').debug() // doc.debug() - -function CompromiseTagger(word) { - const doc = nlp(word) - doc.compute('penn') - const terms = doc.out('json')[0].terms[0] - return terms.penn -} -console.log(CompromiseTagger('bishop')) -console.log(CompromiseTagger('doctor')) - // let doc = nlp('one foo two foo') // let m = doc.terms() // m = m.join().debug() From da06dbd3c52a8cdcebb7e156edae43f036632d2e Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Fri, 16 Feb 2024 11:35:25 -0500 Subject: [PATCH 23/34] fix payload bug --- plugins/payload/src/plugin.js | 6 +++--- scratch.js | 2 ++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/plugins/payload/src/plugin.js b/plugins/payload/src/plugin.js index 5de44191b..497f39f77 100644 --- a/plugins/payload/src/plugin.js +++ b/plugins/payload/src/plugin.js @@ -16,10 +16,10 @@ export default { let n = ptr[0] if (db.hasOwnProperty(n)) { // look at all vals for this sentence + let seeking = this.update([ptr]) db[n].forEach(obj => { - console.log(obj.ptr) let m = this.update([obj.ptr]) - if (this.has(m)) { + if (seeking.has(m)) { res = res.concat({ match: m, val: obj.val, @@ -59,7 +59,7 @@ export default { res.forEach(obj => { let ptr = obj.match.fullPointer[0] || [] let [n, start, end] = ptr - // db[n] = db[n] || [] + db[n] = db[n] || [] // remove it from our list of payloads db[n] = db[n].filter(r => { if (r.ptr[1] === start && r.ptr[2] === end) { diff --git a/scratch.js b/scratch.js index 5fb2bddc2..fec4e820a 100644 --- a/scratch.js +++ b/scratch.js @@ -4,6 +4,8 @@ import nlp from './src/three.js' // nlp.plugin(plg) // nlp.verbose('tagger') +let doc = nlp('i saw John Lennon, and john smith and bob dylan') + // let doc = nlp('one two three four') // let a = doc.match('one .') // let b = doc.match('. three') From f54dd5236856cd0d20067334721ec236e0430935 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Fri, 16 Feb 2024 11:55:32 -0500 Subject: [PATCH 24/34] build tests working --- plugins/payload/README.md | 17 ++- plugins/payload/builds/compromise-payload.cjs | 100 ++++++++++++++++++ .../payload/builds/compromise-payload.min.js | 1 + plugins/payload/builds/compromise-payload.mjs | 1 + plugins/payload/index.d.ts | 18 ++++ plugins/payload/package.json | 6 +- plugins/payload/scratch.js | 15 +-- plugins/payload/tests/_lib.js | 9 +- plugins/payload/tests/payload.test.js | 7 +- 9 files changed, 153 insertions(+), 21 deletions(-) create mode 100644 plugins/payload/builds/compromise-payload.cjs create mode 100644 plugins/payload/builds/compromise-payload.min.js create mode 100644 plugins/payload/builds/compromise-payload.mjs create mode 100644 plugins/payload/index.d.ts diff --git a/plugins/payload/README.md b/plugins/payload/README.md index 03a0b9b22..2cb1e3062 100644 --- a/plugins/payload/README.md +++ b/plugins/payload/README.md @@ -29,7 +29,12 @@ This plugin provides a facilty for storing a retreiving more complex data than t import plg from 'compromise-payload' nlp.extend(plg) -let doc = nlp('i saw John Lennon play the guitar') +let doc = nlp('i saw John Lennon in Manchester, and Bob Dylan in Southhampton') + +// markup metadata +doc.match('(john lennon|bob dylan)').addPayload({ instrument: 'guitar' }) + +// add more doc.people().forEach(m => { if (m.has('lennon')) { m.addPayload({ height: `5'11` }) @@ -38,8 +43,16 @@ doc.people().forEach(m => { m.addPayload({ height: `5'8` }) } }) + +// retrieve specific payloads +doc.match('john lennon').getPayloads() +doc.match('bob dylabn').getPayloads() + +// retrieve them all doc.getPayloads() -// [...] + +// inspect given payloads: +doc.debug('payload') ``` You can also pass a callback into `.addPayload()`: diff --git a/plugins/payload/builds/compromise-payload.cjs b/plugins/payload/builds/compromise-payload.cjs new file mode 100644 index 000000000..46056e8e3 --- /dev/null +++ b/plugins/payload/builds/compromise-payload.cjs @@ -0,0 +1,100 @@ +(function (global, factory) { + typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : + typeof define === 'function' && define.amd ? define(factory) : + (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.compromisePayload = factory()); +})(this, (function () { 'use strict'; + + /* eslint-disable no-console */ + + // pretty-print each match that has a payload + const debug = function (view) { + view.getPayloads().forEach(res => { + let { match, val } = res; + console.log('\n────────'); + match.debug('highlight'); + console.log(' ', JSON.stringify(val)); + console.log('\n'); + }); + }; + var debug$1 = debug; + + var plugin = { + //establish payload db + mutate: function (world) { + world.model.one.db = {}; + world.methods.one.debug.payload = debug$1; + }, + + api: function (View) { + /** return any data on our given matches */ + View.prototype.getPayloads = function () { + let res = []; + let db = this.world.model.one.db || {}; + this.fullPointer.forEach(ptr => { + let n = ptr[0]; + if (db.hasOwnProperty(n)) { + // look at all vals for this sentence + let seeking = this.update([ptr]); + db[n].forEach(obj => { + let m = this.update([obj.ptr]); + if (seeking.has(m)) { + res = res.concat({ + match: m, + val: obj.val, + }); + } + }); + } + }); + return res + }; + + /** add data about our current matches */ + View.prototype.addPayload = function (val) { + let db = this.world.model.one.db || {}; + this.fullPointer.forEach(ptr => { + let n = ptr[0]; + db[n] = db[n] || []; + if (typeof val === 'function') { + //push in whatever the callback wants + let m = this.update([ptr]); + let res = val(m); + if (res !== null && res !== undefined) { + db[n].push({ ptr, val: res }); + } + } else { + db[n].push({ ptr, val }); //push some static data + } + }); + return this + }; + + /** remove all payloads in selection */ + View.prototype.clearPayloads = function () { + let db = this.world.model.one.db || {}; + // get each payload + let res = this.getPayloads(); + res.forEach(obj => { + let ptr = obj.match.fullPointer[0] || []; + let [n, start, end] = ptr; + db[n] = db[n] || []; + // remove it from our list of payloads + db[n] = db[n].filter(r => { + if (r.ptr[1] === start && r.ptr[2] === end) { + return false + } + return true + }); + // clean-up any empty arrays + if (db[n].length === 0) { + delete db[n]; + } + }); + return this + }; + }, + }; + + return plugin; + +})); diff --git a/plugins/payload/builds/compromise-payload.min.js b/plugins/payload/builds/compromise-payload.min.js new file mode 100644 index 000000000..c0e45dee7 --- /dev/null +++ b/plugins/payload/builds/compromise-payload.min.js @@ -0,0 +1 @@ +var t,e;t=this,e=function(){var t=function(t){t.getPayloads().forEach((t=>{let{match:e,val:o}=t;console.log("\n────────"),e.debug("highlight"),console.log(" ",JSON.stringify(o)),console.log("\n")}))};return{mutate:function(e){e.model.one.db={},e.methods.one.debug.payload=t},api:function(t){t.prototype.getPayloads=function(){let t=[],e=this.world.model.one.db||{};return this.fullPointer.forEach((o=>{let l=o[0];if(e.hasOwnProperty(l)){let n=this.update([o]);e[l].forEach((e=>{let o=this.update([e.ptr]);n.has(o)&&(t=t.concat({match:o,val:e.val}))}))}})),t},t.prototype.addPayload=function(t){let e=this.world.model.one.db||{};return this.fullPointer.forEach((o=>{let l=o[0];if(e[l]=e[l]||[],"function"==typeof t){let n=this.update([o]),a=t(n);null!=a&&e[l].push({ptr:o,val:a})}else e[l].push({ptr:o,val:t})})),this},t.prototype.clearPayloads=function(){let t=this.world.model.one.db||{};return this.getPayloads().forEach((e=>{let o=e.match.fullPointer[0]||[],[l,n,a]=o;t[l]=t[l]||[],t[l]=t[l].filter((t=>t.ptr[1]!==n||t.ptr[2]!==a)),0===t[l].length&&delete t[l]})),this}}}},"object"==typeof exports&&"undefined"!=typeof module?module.exports=e():"function"==typeof define&&define.amd?define(e):(t="undefined"!=typeof globalThis?globalThis:t||self).compromisePayload=e(); diff --git a/plugins/payload/builds/compromise-payload.mjs b/plugins/payload/builds/compromise-payload.mjs new file mode 100644 index 000000000..f61ff1174 --- /dev/null +++ b/plugins/payload/builds/compromise-payload.mjs @@ -0,0 +1 @@ +var t=function(t){t.getPayloads().forEach((t=>{let{match:e,val:o}=t;console.log("\n────────"),e.debug("highlight"),console.log(" ",JSON.stringify(o)),console.log("\n")}))},e={mutate:function(e){e.model.one.db={},e.methods.one.debug.payload=t},api:function(t){t.prototype.getPayloads=function(){let t=[],e=this.world.model.one.db||{};return this.fullPointer.forEach((o=>{let l=o[0];if(e.hasOwnProperty(l)){let a=this.update([o]);e[l].forEach((e=>{let o=this.update([e.ptr]);a.has(o)&&(t=t.concat({match:o,val:e.val}))}))}})),t},t.prototype.addPayload=function(t){let e=this.world.model.one.db||{};return this.fullPointer.forEach((o=>{let l=o[0];if(e[l]=e[l]||[],"function"==typeof t){let a=this.update([o]),n=t(a);null!=n&&e[l].push({ptr:o,val:n})}else e[l].push({ptr:o,val:t})})),this},t.prototype.clearPayloads=function(){let t=this.world.model.one.db||{};return this.getPayloads().forEach((e=>{let o=e.match.fullPointer[0]||[],[l,a,n]=o;t[l]=t[l]||[],t[l]=t[l].filter((t=>t.ptr[1]!==a||t.ptr[2]!==n)),0===t[l].length&&delete t[l]})),this}}};export{e as default}; diff --git a/plugins/payload/index.d.ts b/plugins/payload/index.d.ts new file mode 100644 index 000000000..b950cdc5e --- /dev/null +++ b/plugins/payload/index.d.ts @@ -0,0 +1,18 @@ +import nlp from 'compromise' +type View = ReturnType + +type Payload = { match: View, val: any } + +export interface PayloadMethods { + /** return any data on our given matches */ + getPayloads(): Payload[] + /** add data about our current matches */ + addPayload(input:any): View + /** remove all payloads in match */ + clearPayloads(): View +} + +/** extended compromise lib **/ +declare const nlpPayload: nlp.TypedPlugin + +export default nlpPayload diff --git a/plugins/payload/package.json b/plugins/payload/package.json index 5778fdb85..76183ae2c 100644 --- a/plugins/payload/package.json +++ b/plugins/payload/package.json @@ -30,11 +30,9 @@ "build": "rollup -c --silent" }, "peerDependencies": { - "compromise": ">=14.0.0" - }, - "dependencies": { - "efrt": "^2.5.0" + "compromise": ">=14.12.0" }, + "dependencies": {}, "files": [ "builds/", "src/", diff --git a/plugins/payload/scratch.js b/plugins/payload/scratch.js index ae3eb4be4..aca3f9354 100644 --- a/plugins/payload/scratch.js +++ b/plugins/payload/scratch.js @@ -1,16 +1,11 @@ import nlp from '../../src/three.js' -import plugin from './src/plugin.js' +// import plugin from './src/plugin.js' +import plugin from './builds/compromise-payload.mjs' nlp.extend(plugin) let doc = nlp('i saw John Lennon, and john smith and bob dylan') + doc.match('(john|bob|dave) .').addPayload(m => { - return m.text().match(/john/i) ? { isjohn: true } : null + return { lastName: m.terms().last().text() } }) - -// console.log(doc.getPayloads()) -// doc.match('john .').clearPayloads() -console.log(doc.match('john .').getPayloads()) - -// doc.match('(john|bob|dave) .').addPayload(m => { -// return { lastName: m.terms().last().text() } -// }) +console.log(doc.getPayloads()) diff --git a/plugins/payload/tests/_lib.js b/plugins/payload/tests/_lib.js index ba292e331..0a4a16e8b 100644 --- a/plugins/payload/tests/_lib.js +++ b/plugins/payload/tests/_lib.js @@ -1,15 +1,16 @@ -import build from '../../../builds/one/compromise-one.mjs' import src from '../../../src/one.js' -// import plgBuild from '../builds/compromise-speech.mjs' +import build from '../../../builds/one/compromise-one.mjs' + import plg from '../src/plugin.js' +import plgBuild from '../builds/compromise-payload.mjs' let nlp - if (process.env.TESTENV === 'prod') { console.warn('== production build test 🚀 ==') // eslint-disable-line nlp = build - // nlp.plugin(plgBuild) + nlp.plugin(plgBuild) } else { nlp = src nlp.plugin(plg) } + export default nlp diff --git a/plugins/payload/tests/payload.test.js b/plugins/payload/tests/payload.test.js index 1f81a46be..698b38053 100644 --- a/plugins/payload/tests/payload.test.js +++ b/plugins/payload/tests/payload.test.js @@ -49,6 +49,11 @@ test('payload-fn', function (t) { doc.match('(john|bob|dave) .').addPayload(m => { return { lastName: m.terms().last().text() } }) - t.equal(doc.getPayloads().length, 2, '2-again') + t.equal(doc.getPayloads().length, 3, '3-now') + + doc.match('(john lennon|bob dylan)').addPayload(() => { + return { instrument: 'guitar' } + }) + t.equal(doc.getPayloads().length, 5, '5-now') t.end() }) From 17b8b915e65ffc01efa91e7ebbcdd0767aab7ea7 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Fri, 16 Feb 2024 11:57:32 -0500 Subject: [PATCH 25/34] rc 0.0.1 --- plugins/payload/package.json | 3 --- plugins/payload/scratch.js | 2 ++ 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/plugins/payload/package.json b/plugins/payload/package.json index 76183ae2c..cefbb7aff 100644 --- a/plugins/payload/package.json +++ b/plugins/payload/package.json @@ -24,9 +24,6 @@ "test": "tape \"./tests/**/*.test.js\" | tap-dancer --color always", "testb": "TESTENV=prod tape \"./tests/**/*.test.js\" | tap-dancer --color always", "watch": "amble ./scratch.js", - "generate": "node ./scripts/generate/index.js", - "stat": "node ./scripts/stat.js", - "perf": "node ./scripts/perf.js", "build": "rollup -c --silent" }, "peerDependencies": { diff --git a/plugins/payload/scratch.js b/plugins/payload/scratch.js index aca3f9354..87aa48b75 100644 --- a/plugins/payload/scratch.js +++ b/plugins/payload/scratch.js @@ -1,3 +1,5 @@ +/* eslint-disable no-console, no-unused-vars */ + import nlp from '../../src/three.js' // import plugin from './src/plugin.js' import plugin from './builds/compromise-payload.mjs' From d5ae5c171dd6a5fe95033fb902aaec1512dd2ad8 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Fri, 16 Feb 2024 16:54:35 -0500 Subject: [PATCH 26/34] date tests passing --- plugins/dates/scratch.js | 15 +++--- plugins/dates/src/api/find/index.js | 2 +- plugins/dates/src/api/find/split.js | 6 +++ plugins/dates/src/api/parse/one/index.js | 4 +- plugins/dates/src/compute/matches.js | 58 ++++++++++++++++++------ plugins/dates/tests/before-after.test.js | 2 +- plugins/dates/tests/day-start.test.js | 8 ++-- plugins/dates/tests/duration.test.js | 11 +++-- 8 files changed, 72 insertions(+), 34 deletions(-) diff --git a/plugins/dates/scratch.js b/plugins/dates/scratch.js index 9e040eb2e..0022dcfbe 100644 --- a/plugins/dates/scratch.js +++ b/plugins/dates/scratch.js @@ -2,10 +2,10 @@ import nlp from '../../src/three.js' import spacetime from 'spacetime' -// import datePlugin from './src/plugin.js' -import datePlugin from './builds/compromise-dates.mjs' +import datePlugin from './src/plugin.js' +// import datePlugin from './builds/compromise-dates.mjs' nlp.plugin(datePlugin) -// nlp.verbose('tagger') +nlp.verbose('tagger') // nlp.verbose('date') const fmt = iso => (iso ? spacetime(iso).format('{day-short} {nice} {year}') : '-') @@ -35,7 +35,6 @@ const context = { let txt = ` We will see him in mid-September` txt = `5th day of q1 2002` -txt = 'between Sept and Oct 2008' // txt = `tomorrow at 5:45pm` // txt = 'aug. 3' // txt = 'lets meet 1 weeks from now ' @@ -44,10 +43,14 @@ txt = 'between Sept and Oct 2008' // txt = 'on april 22nd' txt = 'in basically one week from now' txt = 'go shopping with april' +txt = 'between Sept and Oct 2008' +txt = 'only in 2018 and 2020' +txt = '2024/02/05 and 2024/03/09' -nlp.verbose('tagger') +// nlp.verbose('tagger') let doc = nlp(txt).debug() -// console.log(doc.times().get()) +// doc.debug('dates') +console.log(doc.dates().get()) // doc.times().format('24h') // doc.debug() diff --git a/plugins/dates/src/api/find/index.js b/plugins/dates/src/api/find/index.js index 0df00c8c0..b5ae5f1be 100644 --- a/plugins/dates/src/api/find/index.js +++ b/plugins/dates/src/api/find/index.js @@ -7,7 +7,7 @@ const findDate = function (doc) { // } let dates = doc.match('#Date+') // ignore only-durations like '20 minutes' - dates = dates.filter((m) => { + dates = dates.filter(m => { let isDuration = m.has('^#Duration+$') || m.has('^#Value #Duration+$') // allow 'q4', etc if (isDuration === true && m.has('(#FinancialQuarter|quarter)')) { diff --git a/plugins/dates/src/api/find/split.js b/plugins/dates/src/api/find/split.js index 311126ee3..6fdb4b507 100644 --- a/plugins/dates/src/api/find/split.js +++ b/plugins/dates/src/api/find/split.js @@ -67,8 +67,14 @@ const split = function (dates) { if (m.found) { dates = dates.splitAfter(m) } + //1998 and 1999 + m = dates.match('#Year [and] #Year', 0) + if (m.found) { + dates = dates.splitAfter(m) + } // cleanup any splits dates = dates.not('^and') + dates = dates.not('and$') return dates } export default split diff --git a/plugins/dates/src/api/parse/one/index.js b/plugins/dates/src/api/parse/one/index.js index 6d373312a..6b967e073 100644 --- a/plugins/dates/src/api/parse/one/index.js +++ b/plugins/dates/src/api/parse/one/index.js @@ -9,10 +9,10 @@ const log = parts => { console.log(`\n==== '${parts.doc.text()}' =====`) // eslint-disable-line Object.keys(parts).forEach(k => { if (k !== 'doc' && parts[k]) { - console.log(k, parts[k])// eslint-disable-line + console.log(k, parts[k]) // eslint-disable-line } }) - parts.doc.debug()// allow + parts.doc.debug() // allow } } diff --git a/plugins/dates/src/compute/matches.js b/plugins/dates/src/compute/matches.js index a3e7405d1..350bd685a 100644 --- a/plugins/dates/src/compute/matches.js +++ b/plugins/dates/src/compute/matches.js @@ -50,11 +50,11 @@ let matches = [ { match: 'in? #Value to #Value #Duration time?', tag: 'Date', reason: '6-to-8-years' }, //two years old { match: '#Value #Duration old', unTag: 'Date', reason: 'val-years-old' }, - // + // { match: `${preps}? ${thisNext} ${seasons}`, tag: 'Date', reason: 'thisNext-season' }, - // + // { match: `the? ${sections} of ${seasons}`, tag: 'Date', reason: 'section-season' }, - // + // { match: `${seasons} ${preps}? #Cardinal`, tag: 'Date', reason: 'season-year' }, //june the 5th { match: '#Date the? #Ordinal', tag: 'Date', reason: 'correction' }, @@ -89,7 +89,6 @@ let matches = [ // mid-august { match: `[${sections}] #Date`, group: 0, tag: 'Date', reason: 'mid-sept' }, - //june 5 to 7th { match: '#Month #Value to #Value of? #Year?', tag: 'Date', reason: 'june 5 to 7th' }, //5 to 7th june @@ -130,19 +129,34 @@ let matches = [ //between x and y { match: '(between|from) #Date', tag: 'Date', reason: 'between x and y' }, { match: '(to|until|upto) #Date', tag: 'Date', reason: 'between x and y2' }, - { match: '#Date and #Date', tag: 'Date', reason: 'between x and y3' }, + { match: 'between #Date+ and #Date+', tag: 'Date', reason: 'between x and y3' }, + { match: '#Month and #Month #Year', tag: 'Date', reason: 'x and y4' }, //day after next { match: 'the? #Date after next one?', tag: 'Date', reason: 'day after next' }, //approximately... { match: '(about|approx|approximately|around) #Date', tag: 'Date', reason: 'approximately june' }, // until june - { match: '(by|until|on|in|at|during|over|every|each|due) the? #Date', ifNo: '#PhrasalVerb', tag: 'Date', reason: 'until june' }, + { + match: '(by|until|on|in|at|during|over|every|each|due) the? #Date', + ifNo: '#PhrasalVerb', + tag: 'Date', + reason: 'until june', + }, // until last june - { match: '(by|until|after|before|during|on|in|following|since) (next|this|last)? #Date', ifNo: '#PhrasalVerb', tag: 'Date', reason: 'until last june' }, + { + match: '(by|until|after|before|during|on|in|following|since) (next|this|last)? #Date', + ifNo: '#PhrasalVerb', + tag: 'Date', + reason: 'until last june', + }, //next september - { match: 'this? (last|next|past|this|previous|current|upcoming|coming|the) #Date', tag: 'Date', reason: 'next september' }, + { + match: 'this? (last|next|past|this|previous|current|upcoming|coming|the) #Date', + tag: 'Date', + reason: 'next september', + }, //starting this june { match: '(starting|beginning|ending) #Date', tag: 'Date', reason: 'starting this june' }, //start of june @@ -161,9 +175,17 @@ let matches = [ // 3 in the morning { match: '#Value (in|at) the? (morning|evening|night|nighttime)', tag: 'Time', reason: '3 in the morning' }, // ten to seven - { match: '(5|10|15|20|five|ten|fifteen|quarter|twenty|half) (after|past) #Cardinal', tag: 'Time', reason: 'ten to seven' }, //add check for 1 to 1 etc. + { + match: '(5|10|15|20|five|ten|fifteen|quarter|twenty|half) (after|past) #Cardinal', + tag: 'Time', + reason: 'ten to seven', + }, //add check for 1 to 1 etc. // at 10 past - { match: '(at|by|before) (5|10|15|20|five|ten|fifteen|twenty|quarter|half) (after|past|to)', tag: 'Time', reason: 'at-20-past' }, + { + match: '(at|by|before) (5|10|15|20|five|ten|fifteen|twenty|quarter|half) (after|past|to)', + tag: 'Time', + reason: 'at-20-past', + }, // iso (2020-03-02T00:00:00.000Z) // { match: '/^[0-9]{4}[:-][0-9]{2}[:-][0-9]{2}T[0-9]/', tag: 'Time', reason: 'iso-time-tag' }, // tuesday at 4 @@ -178,7 +200,12 @@ let matches = [ { match: '#Time [(sharp|on the dot)]', group: 0, tag: 'Time', reason: '4pm sharp' }, // around four thirty - { match: '(at|around|near|#Date) [#Cardinal (thirty|fifteen) (am|pm)?]', group: 0, tag: 'Time', reason: 'around four thirty' }, + { + match: '(at|around|near|#Date) [#Cardinal (thirty|fifteen) (am|pm)?]', + group: 0, + tag: 'Time', + reason: 'around four thirty', + }, // four thirty am { match: '#Cardinal (thirty|fifteen) (am|pm)', tag: 'Time', reason: 'four thirty am' }, // four thirty tomorrow @@ -186,9 +213,12 @@ let matches = [ //anytime around 3 { match: '(anytime|sometime) (before|after|near) [#Cardinal]', group: 0, tag: 'Time', reason: 'antime-after-3' }, - //'two days before'/ 'nine weeks frow now' - { match: '(#Cardinal|a|an) #Duration (before|after|ago|from|hence|back)', tag: 'DateShift', reason: 'nine weeks frow now' }, + { + match: '(#Cardinal|a|an) #Duration (before|after|ago|from|hence|back)', + tag: 'DateShift', + reason: 'nine weeks frow now', + }, // in two weeks { match: 'in (around|about|maybe|perhaps)? #Cardinal #Duration', tag: 'DateShift', reason: 'in two weeks' }, { match: 'in (a|an) #Duration', tag: 'DateShift', reason: 'in a week' }, @@ -246,7 +276,5 @@ let matches = [ { match: '#Ordinal quarter of? #Year', unTag: 'Fraction' }, // a month from now { match: '(from|by|before) now', unTag: 'Time', tag: 'Date' }, - - ] export default matches diff --git a/plugins/dates/tests/before-after.test.js b/plugins/dates/tests/before-after.test.js index 07a32def0..bd5e6aeaa 100644 --- a/plugins/dates/tests/before-after.test.js +++ b/plugins/dates/tests/before-after.test.js @@ -13,7 +13,7 @@ test('isBefore misc', function (t) { doc = nlp('only in 2018 and 2020') m = doc.dates().isBefore('2019') - t.deepEqual(m.out('array'), ['2018'], here + 'years') + t.deepEqual(m.out('array'), ['in 2018'], here + 'years') doc = nlp('saw him 2024/02/05 and 2024/03/09') m = doc.dates().isBefore('2025') diff --git a/plugins/dates/tests/day-start.test.js b/plugins/dates/tests/day-start.test.js index 63c2ffa08..7a08d0f20 100644 --- a/plugins/dates/tests/day-start.test.js +++ b/plugins/dates/tests/day-start.test.js @@ -26,9 +26,9 @@ let arr = [ test('day start', function (t) { const startTime = '5:30am' - arr.forEach((str) => { + arr.forEach(str => { let doc = nlp(str) - let date = doc.dates({ dayStart: startTime }).get()[0] + let date = doc.dates({ dayStart: startTime }).get()[0] || {} let have = spacetime(date.start).time() t.equal(have, startTime, '[start] ' + str) }) @@ -37,9 +37,9 @@ test('day start', function (t) { test('day end', function (t) { const endTime = '8:30pm' - arr.forEach((str) => { + arr.forEach(str => { let doc = nlp(str) - let date = doc.dates({ dayEnd: endTime }).get()[0] + let date = doc.dates({ dayEnd: endTime }).get()[0] || {} let have = spacetime(date.end).time() t.equal(have, endTime, '[end] ' + str) }) diff --git a/plugins/dates/tests/duration.test.js b/plugins/dates/tests/duration.test.js index 39c3554ec..395911198 100644 --- a/plugins/dates/tests/duration.test.js +++ b/plugins/dates/tests/duration.test.js @@ -49,15 +49,16 @@ const tests = [ }, ] -test('day durations', (t) => { - tests.forEach((obj) => { +test('day durations', t => { + tests.forEach(obj => { const context = { today: obj.today, } let today = obj.today.join('-') - obj.tests.forEach((a) => { - let json = nlp(a[0]).dates(context).json()[0] || {} - t.equal(json.dates.duration.days, a[1], `[${today}] ${a[0]}`) + obj.tests.forEach(a => { + let json = nlp(a[0]).dates(context).json()[0] || { dates: {} } + let duration = json.dates.duration || {} + t.equal(duration.days, a[1], `[${today}] ${a[0]}`) }) }) t.end() From dd546c266644382418ceb95a12278b8ad0b98d26 Mon Sep 17 00:00:00 2001 From: spencer kelly Date: Fri, 16 Feb 2024 17:14:52 -0500 Subject: [PATCH 27/34] dates 3.5.0rc --- plugins/dates/builds/compromise-dates.cjs | 556 +++++++++++++------ plugins/dates/builds/compromise-dates.min.js | 2 +- plugins/dates/builds/compromise-dates.mjs | 2 +- plugins/dates/changelog.md | 7 + plugins/dates/package.json | 2 +- plugins/dates/src/_version.js | 2 +- 6 files changed, 391 insertions(+), 180 deletions(-) diff --git a/plugins/dates/builds/compromise-dates.cjs b/plugins/dates/builds/compromise-dates.cjs index 0fc830349..e08e46509 100644 --- a/plugins/dates/builds/compromise-dates.cjs +++ b/plugins/dates/builds/compromise-dates.cjs @@ -73,8 +73,14 @@ if (m.found) { dates = dates.splitAfter(m); } + //1998 and 1999 + m = dates.match('#Year [and] #Year', 0); + if (m.found) { + dates = dates.splitAfter(m); + } // cleanup any splits dates = dates.not('^and'); + dates = dates.not('and$'); return dates }; var split$1 = split; @@ -86,7 +92,7 @@ // } let dates = doc.match('#Date+'); // ignore only-durations like '20 minutes' - dates = dates.filter((m) => { + dates = dates.filter(m => { let isDuration = m.has('^#Duration+$') || m.has('^#Value #Duration+$'); // allow 'q4', etc if (isDuration === true && m.has('(#FinancialQuarter|quarter)')) { @@ -314,10 +320,10 @@ var data = { "9|s": "2/dili,2/jayapura", - "9|n": "2/chita,2/khandyga,2/pyongyang,2/seoul,2/tokyo,11/palau,japan,rok", - "9.5|s|04/03:03->10/02:02": "4/adelaide,4/broken_hill,4/south,4/yancowinna", + "9|n": "2/chita,2/khandyga,2/pyongyang,2/seoul,2/tokyo,2/yakutsk,11/palau,japan,rok", + "9.5|s|04/07:03->10/06:02": "4/adelaide,4/broken_hill,4/south,4/yancowinna", "9.5|s": "4/darwin,4/north", - "8|s|03/08:01->10/04:00": "12/casey", + "8|s|03/13:01->10/02:00": "12/casey", "8|s": "2/kuala_lumpur,2/makassar,2/singapore,4/perth,2/ujung_pandang,4/west,singapore", "8|n": "2/brunei,2/choibalsan,2/hong_kong,2/irkutsk,2/kuching,2/macau,2/manila,2/shanghai,2/taipei,2/ulaanbaatar,2/chongqing,2/chungking,2/harbin,2/macao,2/ulan_bator,hongkong,prc,roc", "8.75|s": "4/eucla", @@ -328,83 +334,75 @@ "6.5|n": "2/yangon,9/cocos,2/rangoon", "5|s": "12/mawson,9/kerguelen", "5|n": "2/aqtau,2/aqtobe,2/ashgabat,2/atyrau,2/dushanbe,2/karachi,2/oral,2/samarkand,2/tashkent,2/yekaterinburg,9/maldives,2/ashkhabad", - "5.75|n": "2/katmandu,2/kathmandu", + "5.75|n": "2/kathmandu,2/katmandu", "5.5|n": "2/kolkata,2/colombo,2/calcutta", "4|s": "9/reunion", - "4|n": "2/baku,2/dubai,2/muscat,2/tbilisi,2/yerevan,8/astrakhan,8/samara,8/saratov,8/ulyanovsk,8/volgograd,2/volgograd,9/mahe,9/mauritius", - "4.5|n|03/22:00->09/21:24": "2/tehran,iran", + "4|n": "2/baku,2/dubai,2/muscat,2/tbilisi,2/yerevan,8/astrakhan,8/samara,8/saratov,8/ulyanovsk,8/volgograd,9/mahe,9/mauritius,2/volgograd", "4.5|n": "2/kabul", "3|s": "12/syowa,9/antananarivo", - "3|n|03/27:03->10/30:04": "2/famagusta,2/nicosia,8/athens,8/bucharest,8/helsinki,8/kiev,8/mariehamn,8/riga,8/sofia,8/tallinn,8/uzhgorod,8/vilnius,8/zaporozhye,8/nicosia", - "3|n|03/27:02->10/30:03": "8/chisinau,8/tiraspol", - "3|n|03/27:00->10/29:24": "2/beirut", - "3|n|03/27:00->10/28:01": "2/gaza,2/hebron", - "3|n|03/25:02->10/30:02": "2/jerusalem,2/tel_aviv,israel", - "3|n|03/25:00->10/27:24": "2/damascus", - "3|n|02/25:00->10/28:01": "2/amman", - "3|n": "0/addis_ababa,0/asmara,0/asmera,0/dar_es_salaam,0/djibouti,0/juba,0/kampala,0/mogadishu,0/nairobi,2/aden,2/baghdad,2/bahrain,2/kuwait,2/qatar,2/riyadh,8/istanbul,8/kirov,8/minsk,8/moscow,8/simferopol,9/comoro,9/mayotte,2/istanbul,turkey,w-su", - "2|s|03/27:02->10/30:02": "12/troll", + "3|n|04/26:00->10/31:24": "0/cairo,egypt", + "3|n|04/20:02->10/26:02": "2/gaza,2/hebron", + "3|n|03/31:03->10/27:04": "2/famagusta,2/nicosia,8/athens,8/bucharest,8/helsinki,8/kyiv,8/mariehamn,8/riga,8/sofia,8/tallinn,8/uzhgorod,8/vilnius,8/zaporozhye,8/nicosia,8/kiev,eet", + "3|n|03/31:02->10/27:03": "8/chisinau,8/tiraspol", + "3|n|03/31:00->10/26:24": "2/beirut", + "3|n|03/29:02->10/27:02": "2/jerusalem,2/tel_aviv,israel", + "3|n": "0/addis_ababa,0/asmara,0/asmera,0/dar_es_salaam,0/djibouti,0/juba,0/kampala,0/mogadishu,0/nairobi,2/aden,2/amman,2/baghdad,2/bahrain,2/damascus,2/kuwait,2/qatar,2/riyadh,8/istanbul,8/kirov,8/minsk,8/moscow,8/simferopol,9/comoro,9/mayotte,2/istanbul,turkey,w-su", + "3.5|n": "2/tehran,iran", + "2|s|03/31:02->10/27:02": "12/troll", "2|s": "0/gaborone,0/harare,0/johannesburg,0/lubumbashi,0/lusaka,0/maputo,0/maseru,0/mbabane", - "2|n|03/27:02->10/30:03": "0/ceuta,arctic/longyearbyen,8/amsterdam,8/andorra,8/belgrade,8/berlin,8/bratislava,8/brussels,8/budapest,8/busingen,8/copenhagen,8/gibraltar,8/ljubljana,8/luxembourg,8/madrid,8/malta,8/monaco,8/oslo,8/paris,8/podgorica,8/prague,8/rome,8/san_marino,8/sarajevo,8/skopje,8/stockholm,8/tirane,8/vaduz,8/vatican,8/vienna,8/warsaw,8/zagreb,8/zurich,3/jan_mayen,poland", - "2|n": "0/blantyre,0/bujumbura,0/cairo,0/khartoum,0/kigali,0/tripoli,8/kaliningrad,egypt,libya", + "2|n|03/31:02->10/27:03": "0/ceuta,arctic/longyearbyen,8/amsterdam,8/andorra,8/belgrade,8/berlin,8/bratislava,8/brussels,8/budapest,8/busingen,8/copenhagen,8/gibraltar,8/ljubljana,8/luxembourg,8/madrid,8/malta,8/monaco,8/oslo,8/paris,8/podgorica,8/prague,8/rome,8/san_marino,8/sarajevo,8/skopje,8/stockholm,8/tirane,8/vaduz,8/vatican,8/vienna,8/warsaw,8/zagreb,8/zurich,3/jan_mayen,poland,cet,met", + "2|n": "0/blantyre,0/bujumbura,0/khartoum,0/kigali,0/tripoli,8/kaliningrad,libya", "1|s": "0/brazzaville,0/kinshasa,0/luanda,0/windhoek", - "1|n|03/27:03->05/08:02": "0/casablanca,0/el_aaiun", - "1|n|03/27:01->10/30:02": "3/canary,3/faroe,3/madeira,8/dublin,8/guernsey,8/isle_of_man,8/jersey,8/lisbon,8/london,3/faeroe,eire,8/belfast,gb-eire,gb,portugal", + "1|n|03/31:01->10/27:02": "3/canary,3/faroe,3/madeira,8/dublin,8/guernsey,8/isle_of_man,8/jersey,8/lisbon,8/london,3/faeroe,eire,8/belfast,gb-eire,gb,portugal,wet", "1|n": "0/algiers,0/bangui,0/douala,0/lagos,0/libreville,0/malabo,0/ndjamena,0/niamey,0/porto-novo,0/tunis", "14|n": "11/kiritimati", - "13|s|04/04:04->09/26:03": "11/apia", - "13|s|01/15:02->11/05:03": "11/tongatapu", - "13|n": "11/enderbury,11/fakaofo", - "12|s|04/03:03->09/25:02": "12/mcmurdo,11/auckland,12/south_pole,nz", - "12|s|01/17:03->11/14:02": "11/fiji", + "13|s": "11/apia,11/tongatapu", + "13|n": "11/enderbury,11/kanton,11/fakaofo", + "12|s|04/07:03->09/29:02": "12/mcmurdo,11/auckland,12/south_pole,nz", + "12|s": "11/fiji", "12|n": "2/anadyr,2/kamchatka,2/srednekolymsk,11/funafuti,11/kwajalein,11/majuro,11/nauru,11/tarawa,11/wake,11/wallis,kwajalein", - "12.75|s|04/03:03->04/03:02": "11/chatham,nz-chat", - "11|s|04/03:03->10/02:02": "12/macquarie", + "12.75|s|04/07:03->04/07:02": "11/chatham,nz-chat", + "11|s|04/07:03->10/06:02": "12/macquarie", "11|s": "11/bougainville", "11|n": "2/magadan,2/sakhalin,11/efate,11/guadalcanal,11/kosrae,11/noumea,11/pohnpei,11/ponape", - "11.5|n|04/03:03->10/02:02": "11/norfolk", - "10|s|04/03:03->10/02:02": "4/currie,4/hobart,4/melbourne,4/sydney,4/act,4/canberra,4/nsw,4/tasmania,4/victoria", + "11.5|n|04/07:03->10/06:02": "11/norfolk", + "10|s|04/07:03->10/06:02": "4/currie,4/hobart,4/melbourne,4/sydney,4/act,4/canberra,4/nsw,4/tasmania,4/victoria", "10|s": "12/dumontdurville,4/brisbane,4/lindeman,11/port_moresby,4/queensland", - "10|n": "2/ust-nera,2/vladivostok,2/yakutsk,11/guam,11/saipan,11/chuuk,11/truk,11/yap", - "10.5|s|04/03:01->10/02:02": "4/lord_howe,4/lhi", - "0|n|03/27:00->10/30:01": "1/scoresbysund,3/azores", - "0|n": "0/abidjan,0/accra,0/bamako,0/banjul,0/bissau,0/conakry,0/dakar,0/freetown,0/lome,0/monrovia,0/nouakchott,0/ouagadougou,0/sao_tome,1/danmarkshavn,3/reykjavik,3/st_helena,13/gmt,13/utc,0/timbuktu,13/greenwich,13/uct,13/universal,13/zulu,gmt-0,gmt+0,gmt0,greenwich,iceland,uct,universal,utc,zulu", - "-9|n|03/13:02->11/06:02": "1/adak,1/atka,us/aleutian", + "10|n": "2/ust-nera,2/vladivostok,11/guam,11/saipan,11/chuuk,11/truk,11/yap", + "10.5|s|04/07:01->10/06:02": "4/lord_howe,4/lhi", + "0|s|03/10:03->04/14:02": "0/casablanca,0/el_aaiun", + "0|n|03/31:00->10/27:01": "1/scoresbysund,3/azores", + "0|n": "0/abidjan,0/accra,0/bamako,0/banjul,0/bissau,0/conakry,0/dakar,0/freetown,0/lome,0/monrovia,0/nouakchott,0/ouagadougou,0/sao_tome,1/danmarkshavn,3/reykjavik,3/st_helena,13/gmt,13/utc,0/timbuktu,13/greenwich,13/uct,13/universal,13/zulu,gmt-0,gmt+0,gmt0,greenwich,iceland,uct,universal,utc,zulu,13/unknown,factory", + "-9|n|03/10:02->11/03:02": "1/adak,1/atka,us/aleutian", "-9|n": "11/gambier", "-9.5|n": "11/marquesas", - "-8|n|03/13:02->11/06:02": "1/anchorage,1/juneau,1/metlakatla,1/nome,1/sitka,1/yakutat,us/alaska", + "-8|n|03/10:02->11/03:02": "1/anchorage,1/juneau,1/metlakatla,1/nome,1/sitka,1/yakutat,us/alaska", "-8|n": "11/pitcairn", - "-7|n|03/13:02->11/06:02": "1/los_angeles,1/santa_isabel,1/tijuana,1/vancouver,1/ensenada,6/pacific,10/bajanorte,us/pacific-new,us/pacific", - "-7|n|03/08:02->11/01:01": "1/dawson,1/whitehorse,6/yukon", - "-7|n": "1/creston,1/dawson_creek,1/fort_nelson,1/hermosillo,1/phoenix,us/arizona", - "-6|s|04/02:22->09/03:22": "11/easter,7/easterisland", - "-6|n|04/03:02->10/30:02": "1/chihuahua,1/mazatlan,10/bajasur", - "-6|n|03/13:02->11/06:02": "1/boise,1/cambridge_bay,1/denver,1/edmonton,1/inuvik,1/ojinaga,1/yellowknife,1/shiprock,6/mountain,navajo,us/mountain", - "-6|n": "1/belize,1/costa_rica,1/el_salvador,1/guatemala,1/managua,1/regina,1/swift_current,1/tegucigalpa,11/galapagos,6/east-saskatchewan,6/saskatchewan", + "-7|n|03/10:02->11/03:02": "1/los_angeles,1/santa_isabel,1/tijuana,1/vancouver,1/ensenada,6/pacific,10/bajanorte,us/pacific-new,us/pacific", + "-7|n": "1/creston,1/dawson,1/dawson_creek,1/fort_nelson,1/hermosillo,1/mazatlan,1/phoenix,1/whitehorse,6/yukon,10/bajasur,us/arizona,mst", + "-6|s|04/06:22->09/07:22": "11/easter,7/easterisland", + "-6|n|04/07:02->10/27:02": "1/merida", + "-6|n|03/10:02->11/03:02": "1/boise,1/cambridge_bay,1/denver,1/edmonton,1/inuvik,1/north_dakota,1/ojinaga,1/ciudad_juarez,1/yellowknife,1/shiprock,6/mountain,navajo,us/mountain", + "-6|n": "1/bahia_banderas,1/belize,1/chihuahua,1/costa_rica,1/el_salvador,1/guatemala,1/managua,1/mexico_city,1/monterrey,1/regina,1/swift_current,1/tegucigalpa,11/galapagos,6/east-saskatchewan,6/saskatchewan,10/general", "-5|s": "1/lima,1/rio_branco,1/porto_acre,5/acre", - "-5|n|04/03:02->10/30:02": "1/bahia_banderas,1/merida,1/mexico_city,1/monterrey,10/general", - "-5|n|03/13:02->11/06:02": "1/chicago,1/matamoros,1/menominee,1/rainy_river,1/rankin_inlet,1/resolute,1/winnipeg,1/indiana/knox,1/indiana/tell_city,1/north_dakota/beulah,1/north_dakota/center,1/north_dakota/new_salem,1/knox_in,6/central,us/central,us/indiana-starke", - "-5|n|03/12:03->11/05:01": "1/north_dakota", - "-5|n": "1/bogota,1/cancun,1/cayman,1/coral_harbour,1/eirunepe,1/guayaquil,1/jamaica,1/panama,1/atikokan,jamaica", - "-4|s|05/13:23->08/13:01": "12/palmer", - "-4|s|04/02:24->09/04:00": "1/santiago,7/continental", - "-4|s|03/26:24->10/02:00": "1/asuncion", - "-4|s|02/16:24->11/03:00": "1/campo_grande,1/cuiaba", - "-4|s": "1/la_paz,1/manaus,5/west", - "-4|n|03/13:02->11/06:02": "1/detroit,1/grand_turk,1/indianapolis,1/iqaluit,1/louisville,1/montreal,1/nassau,1/new_york,1/nipigon,1/pangnirtung,1/port-au-prince,1/thunder_bay,1/toronto,1/indiana/marengo,1/indiana/petersburg,1/indiana/vevay,1/indiana/vincennes,1/indiana/winamac,1/kentucky/monticello,1/fort_wayne,1/indiana/indianapolis,1/kentucky/louisville,6/eastern,us/east-indiana,us/eastern,us/michigan", - "-4|n|03/13:00->11/06:01": "1/havana,cuba", - "-4|n|03/12:03->11/05:01": "1/indiana,1/kentucky", + "-5|n|03/10:02->11/03:02": "1/chicago,1/matamoros,1/menominee,1/rainy_river,1/rankin_inlet,1/resolute,1/winnipeg,1/indiana/knox,1/indiana/tell_city,1/north_dakota/beulah,1/north_dakota/center,1/north_dakota/new_salem,1/knox_in,6/central,us/central,us/indiana-starke", + "-5|n": "1/bogota,1/cancun,1/cayman,1/coral_harbour,1/eirunepe,1/guayaquil,1/jamaica,1/panama,1/atikokan,jamaica,est", + "-4|s|04/06:24->09/08:00": "1/santiago,7/continental", + "-4|s|03/23:24->10/06:00": "1/asuncion", + "-4|s": "1/campo_grande,1/cuiaba,1/la_paz,1/manaus,5/west", + "-4|n|03/10:02->11/03:02": "1/detroit,1/grand_turk,1/indiana,1/indianapolis,1/iqaluit,1/kentucky,1/louisville,1/montreal,1/nassau,1/new_york,1/nipigon,1/pangnirtung,1/port-au-prince,1/thunder_bay,1/toronto,1/indiana/marengo,1/indiana/petersburg,1/indiana/vevay,1/indiana/vincennes,1/indiana/winamac,1/kentucky/monticello,1/fort_wayne,1/indiana/indianapolis,1/kentucky/louisville,6/eastern,us/east-indiana,us/eastern,us/michigan", + "-4|n|03/10:00->11/03:01": "1/havana,cuba", "-4|n": "1/anguilla,1/antigua,1/aruba,1/barbados,1/blanc-sablon,1/boa_vista,1/caracas,1/curacao,1/dominica,1/grenada,1/guadeloupe,1/guyana,1/kralendijk,1/lower_princes,1/marigot,1/martinique,1/montserrat,1/port_of_spain,1/porto_velho,1/puerto_rico,1/santo_domingo,1/st_barthelemy,1/st_kitts,1/st_lucia,1/st_thomas,1/st_vincent,1/tortola,1/virgin", - "-3|s": "1/argentina,1/buenos_aires,1/catamarca,1/cordoba,1/fortaleza,1/jujuy,1/mendoza,1/montevideo,1/punta_arenas,1/sao_paulo,12/rothera,3/stanley,1/argentina/la_rioja,1/argentina/rio_gallegos,1/argentina/salta,1/argentina/san_juan,1/argentina/san_luis,1/argentina/tucuman,1/argentina/ushuaia,1/argentina/comodrivadavia,1/argentina/buenos_aires,1/argentina/catamarca,1/argentina/cordoba,1/argentina/jujuy,1/argentina/mendoza,1/argentina/rosario,1/rosario,5/east", - "-3|n|03/13:02->11/06:02": "1/glace_bay,1/goose_bay,1/halifax,1/moncton,1/thule,3/bermuda,6/atlantic", + "-3|s": "1/argentina,1/buenos_aires,1/catamarca,1/cordoba,1/fortaleza,1/jujuy,1/mendoza,1/montevideo,1/punta_arenas,1/sao_paulo,12/palmer,12/rothera,3/stanley,1/argentina/la_rioja,1/argentina/rio_gallegos,1/argentina/salta,1/argentina/san_juan,1/argentina/san_luis,1/argentina/tucuman,1/argentina/ushuaia,1/argentina/comodrivadavia,1/argentina/buenos_aires,1/argentina/catamarca,1/argentina/cordoba,1/argentina/jujuy,1/argentina/mendoza,1/argentina/rosario,1/rosario,5/east", + "-3|n|03/10:02->11/03:02": "1/glace_bay,1/goose_bay,1/halifax,1/moncton,1/thule,3/bermuda,6/atlantic", "-3|n": "1/araguaina,1/bahia,1/belem,1/cayenne,1/maceio,1/paramaribo,1/recife,1/santarem", - "-2|n|03/26:22->10/29:23": "1/nuuk,1/godthab", - "-2|n|03/13:02->11/06:02": "1/miquelon", + "-2|n|03/30:22->10/26:23": "1/nuuk,1/godthab", + "-2|n|03/10:02->11/03:02": "1/miquelon", "-2|n": "1/noronha,3/south_georgia,5/denoronha", - "-2.5|n|03/13:02->11/06:02": "1/st_johns,6/newfoundland", + "-2.5|n|03/10:02->11/03:02": "1/st_johns,6/newfoundland", "-1|n": "3/cape_verde", "-11|n": "11/midway,11/niue,11/pago_pago,11/samoa,us/samoa", - "-10|n": "11/honolulu,11/johnston,11/rarotonga,11/tahiti,us/hawaii" + "-10|n": "11/honolulu,11/johnston,11/rarotonga,11/tahiti,us/hawaii,hst" }; //prefixes for iana names.. @@ -542,7 +540,7 @@ }; var parseOffset$4 = parseOffset$3; - const local = guessTz$1(); + let local = guessTz$1(); //add all the city names by themselves const cities = Object.keys(zones$1).reduce((h, k) => { @@ -566,6 +564,11 @@ // try our best to reconcile the timzone to this given string const lookupTz = (str, zones) => { if (!str) { + // guard if Intl response is unsupported (#397) + if (!zones.hasOwnProperty(local)) { + console.warn(`Unrecognized IANA id '${local}'. Setting fallback tz to UTC.`); + local = 'utc'; + } return local } if (typeof str !== 'string') { @@ -878,7 +881,7 @@ } }; //find the desired date by a increment/check while loop - const units$4 = { + const units$5 = { year: { valid: (n) => n > -4000 && n < 4000, walkTo: (s, n) => walk(s, n, 'getFullYear', 'year', null) @@ -938,7 +941,7 @@ }; const walkTo = (s, wants) => { - let keys = Object.keys(units$4); + let keys = Object.keys(units$5); let old = s.clone(); for (let i = 0; i < keys.length; i++) { let k = keys[i]; @@ -950,14 +953,14 @@ n = parseInt(n, 10); } //make-sure it's valid - if (!units$4[k].valid(n)) { + if (!units$5[k].valid(n)) { s.epoch = null; if (s.silent === false) { console.warn('invalid ' + k + ': ' + n); } return } - units$4[k].walkTo(s, n); + units$5[k].walkTo(s, n); } return }; @@ -1029,7 +1032,7 @@ function short$1() { return shortMonths } function long$1() { return longMonths } function mapping$3() { return buildMapping() } - function set$3(i18n) { + function set$5(i18n) { shortMonths = i18n.short || shortMonths; longMonths = i18n.long || longMonths; } @@ -1039,7 +1042,7 @@ if (!offset) { return s } - + offset = offset.trim().toLowerCase(); // according to ISO8601, tz could be hh:mm, hhmm or hh // so need few more steps before the calculation. let num = 0; @@ -1094,10 +1097,10 @@ // truncate any sub-millisecond values const parseMs = function (str = '') { str = String(str); - //js does not support sub-millisecond values + //js does not support sub-millisecond values // so truncate these - 2021-11-02T19:55:30.087772 if (str.length > 3) { - str = str.substr(0, 3); + str = str.substring(0, 3); } else if (str.length === 1) { // assume ms are zero-padded on the left // but maybe not on the right. @@ -1115,19 +1118,20 @@ //formal time format - 04:30.23 let arr = str.match(/([0-9]{1,2}):([0-9]{1,2}):?([0-9]{1,2})?[:\.]?([0-9]{1,4})?/); if (arr !== null) { + let [, h, m, sec, ms] = arr; //validate it a little - let h = Number(arr[1]); + h = Number(h); if (h < 0 || h > 24) { return s.startOf('day') } - let m = Number(arr[2]); //don't accept '5:3pm' + m = Number(m); //don't accept '5:3pm' if (arr[2].length < 2 || m < 0 || m > 59) { return s.startOf('day') } s = s.hour(h); s = s.minute(m); - s = s.seconds(arr[3] || 0); - s = s.millisecond(parseMs(arr[4])); + s = s.seconds(sec || 0); + s = s.millisecond(parseMs(ms)); //parse-out am/pm let ampm = str.match(/[\b0-9] ?(am|pm)\b/); if (ampm !== null && ampm[1]) { @@ -1344,19 +1348,21 @@ }, // 'Sun Mar 14 15:09:48 +0000 2021' { - reg: /^([a-z]+) ([0-9]{1,2})( [0-9:]+)?( \+[0-9]{4})?( [0-9]{4})?$/i, + reg: /^([a-z]+) ([0-9]{1,2}) ([0-9]{1,2}:[0-9]{2}:?[0-9]{0,2})( \+[0-9]{4})?( [0-9]{4})?$/i, parse: (s, arr) => { + let [, month, date, time, tz, year] = arr; let obj = { - year: parseYear(arr[5], s._today), - month: parseMonth(arr[1]), - date: toCardinal(arr[2] || '') + year: parseYear(year, s._today), + month: parseMonth(month), + date: toCardinal(date || '') }; if (validate$1(obj) === false) { s.epoch = null; return s } walkTo$1(s, obj); - s = parseTime$3(s, arr[3]); + s = parseOffset$2(s, tz); + s = parseTime$3(s, time); return s } } @@ -1572,7 +1578,6 @@ for (let i = 0; i < parsers.length; i++) { let m = input.match(parsers[i].reg); if (m) { - // console.log(parsers[i].reg) let res = parsers[i].parse(s, m, givenTz); if (res !== null && res.isValid()) { return res @@ -1665,7 +1670,7 @@ function short() { return shortDays } function long() { return longDays } - function set$2(i18n) { + function set$4(i18n) { shortDays = i18n.short || shortDays; longDays = i18n.long || longDays; } @@ -1690,7 +1695,7 @@ return titleCaseEnabled } - function set$1(val) { + function set$3(val) { titleCaseEnabled = val; } @@ -1783,6 +1788,7 @@ 'millisecond-pad': (s) => zeroPad(s.millisecond(), 3), ampm: (s) => s.ampm(), + AMPM: (s) => s.ampm().toUpperCase(), quarter: (s) => 'Q' + s.quarter(), season: (s) => s.season(), era: (s) => s.era(), @@ -1872,7 +1878,7 @@ let out = format[str](s) || ''; if (str !== 'json') { out = String(out); - if (str !== 'ampm') { + if (str.toLowerCase() !== 'ampm') { out = applyCaseFormat(out); } } @@ -1885,7 +1891,7 @@ fmt = fmt.toLowerCase().trim(); if (format.hasOwnProperty(fmt)) { let out = String(format[fmt](s)); - if (fmt !== 'ampm') { + if (fmt.toLowerCase() !== 'ampm') { return applyCaseFormat(out) } return out @@ -2078,7 +2084,7 @@ }; var unixFmt$1 = unixFmt; - const units$3 = ['year', 'season', 'quarter', 'month', 'week', 'day', 'quarterHour', 'hour', 'minute']; + const units$4 = ['year', 'season', 'quarter', 'month', 'week', 'day', 'quarterHour', 'hour', 'minute']; const doUnit = function (s, k) { let start = s.clone().startOf(k); @@ -2095,7 +2101,7 @@ return doUnit(s, unit) } let obj = {}; - units$3.forEach(k => { + units$4.forEach(k => { obj[k] = doUnit(s, k); }); return obj @@ -2191,6 +2197,10 @@ tmp = a.add(obj.months, 'month'); obj.months += diffOne$1(tmp, b, 'month'); + // there's always 4 quarters in a year... + obj.quarters = obj.years * 4; + obj.quarters += parseInt((obj.months % 12) / 3, 10); + // there's always atleast 52 weeks in a year.. // (month * 4) isn't as close obj.weeks = obj.years * 52; @@ -2263,16 +2273,16 @@ S the number of seconds. */ - const fmt = (n) => Math.abs(n) || 0; + const fmt$1 = (n) => Math.abs(n) || 0; const toISO = function (diff) { let iso = 'P'; - iso += fmt(diff.years) + 'Y'; - iso += fmt(diff.months) + 'M'; - iso += fmt(diff.days) + 'DT'; - iso += fmt(diff.hours) + 'H'; - iso += fmt(diff.minutes) + 'M'; - iso += fmt(diff.seconds) + 'S'; + iso += fmt$1(diff.years) + 'Y'; + iso += fmt$1(diff.months) + 'M'; + iso += fmt$1(diff.days) + 'DT'; + iso += fmt$1(diff.hours) + 'H'; + iso += fmt$1(diff.minutes) + 'M'; + iso += fmt$1(diff.seconds) + 'S'; return iso }; var toISO$1 = toISO; @@ -2310,7 +2320,73 @@ return diff } + let units$3 = { + second: 'second', + seconds: 'seconds', + minute: 'minute', + minutes: 'minutes', + hour: 'hour', + hours: 'hours', + day: 'day', + days: 'days', + month: 'month', + months: 'months', + year: 'year', + years: 'years', + }; + + function unitsString(unit) { + return units$3[unit] || ''; + } + + function set$2(i18n = {}) { + units$3 = { + second: i18n.second || units$3.second, + seconds: i18n.seconds || units$3.seconds, + minute: i18n.minute || units$3.minute, + minutes: i18n.minutes || units$3.minutes, + hour: i18n.hour || units$3.hour, + hours: i18n.hours || units$3.hours, + day: i18n.day || units$3.day, + days: i18n.days || units$3.days, + month: i18n.month || units$3.month, + months: i18n.months || units$3.months, + year: i18n.year || units$3.year, + years: i18n.years || units$3.years, + }; + } + + let past = 'past'; + let future = 'future'; + let present = 'present'; + let now = 'now'; + let almost = 'almost'; + let over = 'over'; + let pastDistance = (value) => `${value} ago`; + let futureDistance = (value) => `in ${value}`; + + function pastDistanceString(value) { return pastDistance(value) } + function futureDistanceString(value) { return futureDistance(value) } + function pastString() { return past } + function futureString() { return future } + function presentString() { return present } + function nowString() { return now } + function almostString() { return almost } + function overString() { return over } + + function set$1(i18n) { + pastDistance = i18n.pastDistance || pastDistance; + futureDistance = i18n.futureDistance || futureDistance; + past = i18n.past || past; + future = i18n.future || future; + present = i18n.present || present; + now = i18n.now || now; + almost = i18n.almost || almost; + over = i18n.over || over; + } + //our conceptual 'break-points' for each unit + const qualifiers = { months: { almost: 10, @@ -2337,9 +2413,9 @@ // Expects a plural unit arg function pluralize(value, unit) { if (value === 1) { - unit = unit.slice(0, -1); + return value + ' ' + unitsString(unit.slice(0, -1)) } - return value + ' ' + unit + return value + ' ' + unitsString(unit) } const toSoft = function (diff) { @@ -2366,12 +2442,13 @@ const nextValue = Math.abs(diff[nextUnit]); if (nextValue > qualifiers[nextUnit].almost) { rounded = pluralize(value + 1, unit); - qualified = 'almost ' + rounded; + qualified = almostString() + ' ' + rounded; } else if (nextValue > qualifiers[nextUnit].over) { - qualified = 'over ' + englishValue; + qualified = overString() + ' ' + englishValue; } } }); + return { qualified, rounded, abbreviated, englishValues } }; var soften = toSoft; @@ -2386,16 +2463,16 @@ if (isNow === true) { return { diff, - rounded: 'now', - qualified: 'now', - precise: 'now', + rounded: nowString(), + qualified: nowString(), + precise: nowString(), abbreviated: [], iso: 'P0Y0M0DT0H0M0S', - direction: 'present', + direction: presentString(), } } let precise; - let direction = 'future'; + let direction = futureString(); let { rounded, qualified, englishValues, abbreviated } = soften(diff); @@ -2403,14 +2480,14 @@ precise = englishValues.splice(0, 2).join(', '); //handle before/after logic if (start.isAfter(end) === true) { - rounded += ' ago'; - qualified += ' ago'; - precise += ' ago'; - direction = 'past'; + rounded = pastDistanceString(rounded); + qualified = pastDistanceString(qualified); + precise = pastDistanceString(precise); + direction = pastString(); } else { - rounded = 'in ' + rounded; - qualified = 'in ' + qualified; - precise = 'in ' + precise; + rounded = futureDistanceString(rounded); + qualified = futureDistanceString(qualified); + precise = futureDistanceString(precise); } // https://en.wikipedia.org/wiki/ISO_8601#Durations // P[n]Y[n]M[n]DT[n]H[n]M[n]S @@ -2459,6 +2536,12 @@ ]; const units$2 = { + second: (s) => { + walkTo$1(s, { + millisecond: 0 + }); + return s + }, minute: (s) => { walkTo$1(s, { second: 0, @@ -2677,12 +2760,17 @@ return dst.split('->') }; + //iana codes are case-sensitive, technically const titleCase = str => { str = str[0].toUpperCase() + str.substr(1); - str = str.replace(/\/gmt/, '/GMT'); - str = str.replace(/[\/_]([a-z])/gi, s => { + str = str.replace(/[\/_-]([a-z])/gi, s => { return s.toUpperCase() }); + str = str.replace(/_(of|es)_/i, (s) => s.toLowerCase()); + str = str.replace(/\/gmt/i, '/GMT'); + str = str.replace(/\/Dumontdurville$/i, '/DumontDUrville'); + str = str.replace(/\/Mcmurdo$/i, '/McMurdo'); + str = str.replace(/\/Port-au-prince$/i, '/Port-au-Prince'); return str }; @@ -3194,7 +3282,11 @@ if (n <= 0) { n = 0; } else if (n >= 365) { - n = 364; + if (isLeapYear(s.year())) { + n = 365; + } else { + n = 364; + } } s = s.startOf('year'); s = s.add(n, 'day'); @@ -3337,15 +3429,15 @@ dayTime: function (str, goFwd) { if (str !== undefined) { const times = { - morning: '7:00am', - breakfast: '7:00am', - noon: '12:00am', - lunch: '12:00pm', - afternoon: '2:00pm', - evening: '6:00pm', - dinner: '6:00pm', - night: '11:00pm', - midnight: '23:59pm' + morning: '7:00', + breakfast: '7:00', + noon: '12:00', + lunch: '12:00', + afternoon: '14:00', + evening: '18:00', + dinner: '18:00', + night: '23:00', + midnight: '00:00' }; let s = this.clone(); str = str || ''; @@ -3497,9 +3589,10 @@ tmp = clearMinutes(tmp); tmp = tmp.day('monday'); //don't go into last-year - if (tmp.monthName() === 'december' && tmp.date() >= 28) { + if (tmp.month() === 11 && tmp.date() >= 25) { tmp = tmp.add(1, 'week'); } + // is first monday the 1st? let toAdd = 1; if (tmp.date() === 1) { @@ -3514,6 +3607,7 @@ //speed it up, if we can let i = 0; let skipWeeks = this.month() * 4; + // console.log(ms.week+ " "+ skipWeeks); tmp.epoch += ms.week * skipWeeks; i += skipWeeks; for (; i <= 52; i++) { @@ -3574,7 +3668,7 @@ if (this.hemisphere() === 'South') { hem = 'south'; } - if (input !== undefined) { + if (input !== undefined) { // setter let s = this.clone(); for (let i = 0; i < seasons$1[hem].length; i++) { if (input === seasons$1[hem][i][0]) { @@ -3591,7 +3685,7 @@ return seasons$1[hem][i][0] } } - return 'winter' + return hem === 'north' ? 'winter' : 'summer' }, //the year number @@ -4105,25 +4199,37 @@ const addMethods = SpaceTime => { const methods = { - i18n: data => { + i18n: function (data) { //change the day names if (isObject(data.days)) { - set$2(data.days); + set$4(data.days); } //change the month names if (isObject(data.months)) { - set$3(data.months); + set$5(data.months); } - // change the the display style of the month / day names + //change the display style of the month / day names if (isBoolean(data.useTitleCase)) { - set$1(data.useTitleCase); + set$3(data.useTitleCase); } //change am and pm strings if (isObject(data.ampm)) { set(data.ampm); } + + //change distance strings + if(isObject(data.distance)){ + set$1(data.distance); + } + + //change units strings + if(isObject(data.units)){ + set$2(data.units); + } + + return this } }; @@ -4136,19 +4242,19 @@ var i18nFns = addMethods; let timezones$1 = zones$1; - //fake timezone-support, for fakers (es5 class) + // fake timezone-support, for fakers (es5 class) const SpaceTime = function (input, tz, options = {}) { - //the holy moment + // the holy moment this.epoch = null; - //the shift for the given timezone + // the shift for the given timezone this.tz = findTz(tz, timezones$1); - //whether to output warnings to console + // whether to output warnings to console this.silent = typeof options.silent !== 'undefined' ? options.silent : true; // favour british interpretation of 02/02/2018, etc this.british = options.dmy || options.british; - //does the week start on sunday, or monday: - this._weekStart = 1; //default to monday + // does the week start on sunday, or monday: + this._weekStart = 1; // default to monday if (options.weekStart !== undefined) { this._weekStart = options.weekStart; } @@ -4163,23 +4269,23 @@ // writable: true, // value: parsers // }) - //add getter/setters + // add getter/setters Object.defineProperty(this, 'd', { - //return a js date object + // return a js date object get: function () { let offset = quickOffset$1(this); - //every computer is somewhere- get this computer's built-in offset + // every computer is somewhere- get this computer's built-in offset let bias = new Date(this.epoch).getTimezoneOffset() || 0; - //movement + // movement let shift = bias + offset * 60; //in minutes shift = shift * 60 * 1000; //in ms - //remove this computer's offset + // remove this computer's offset let epoch = this.epoch + shift; let d = new Date(epoch); return d } }); - //add this data on the object, to allow adding new timezones + // add this data on the object, to allow adding new timezones Object.defineProperty(this, 'timezones', { get: () => timezones$1, set: (obj) => { @@ -4187,12 +4293,15 @@ return obj } }); - //parse the various formats + // parse the various formats let tmp = handleInput(this, input); this.epoch = tmp.epoch; + if (tmp.tz) { + this.tz = tmp.tz; + } }; - //(add instance methods to prototype) + // (add instance methods to prototype) Object.keys(methods$5).forEach((k) => { SpaceTime.prototype[k] = methods$5[k]; }); @@ -4222,7 +4331,7 @@ return new Date(this.epoch) }; - //append more methods + // append more methods queryFns(SpaceTime); addFns(SpaceTime); sameFns(SpaceTime); @@ -4269,7 +4378,7 @@ }; var whereIts$1 = whereIts; - var version$1 = '7.1.4'; + var version$1 = '7.6.0'; const main = (input, tz, options) => new Spacetime(input, tz, options); @@ -6445,10 +6554,10 @@ console.log(`\n==== '${parts.doc.text()}' =====`); // eslint-disable-line Object.keys(parts).forEach(k => { if (k !== 'doc' && parts[k]) { - console.log(k, parts[k]);// eslint-disable-line + console.log(k, parts[k]); // eslint-disable-line } }); - parts.doc.debug();// allow + parts.doc.debug(); // allow } }; @@ -7307,8 +7416,16 @@ }; var toJSON$1 = toJSON; - const api$3 = function (View) { + const quickDate = function (view, str) { + let tmp = view.fromText(str); + let found = parseDates(tmp, view.opts)[0]; + if (!found || !found.start || !found.start.d) { + return null + } + return found.start.d + }; + const api$3 = function (View) { class Dates extends View { constructor(document, pointer, groups, opts = {}) { super(document, pointer, groups); @@ -7318,7 +7435,7 @@ get(n) { let all = []; - this.forEach((m) => { + this.forEach(m => { parseDates(m, this.opts).forEach(res => { all.push(toJSON$1(res)); }); @@ -7332,7 +7449,7 @@ json(opts = {}) { return this.map(m => { let json = m.toView().json(opts)[0] || {}; - if (opts && opts.dates !== true) { + if (opts && opts.dates !== false) { let parsed = parseDates(m, this.opts); json.dates = toJSON$1(parsed[0]); } @@ -7340,6 +7457,7 @@ }, []) } + /** replace date terms with a formatted date */ format(fmt) { let found = this; let res = found.map(m => { @@ -7359,6 +7477,31 @@ }); return new Dates(this.document, res.pointer, null, this.opts) } + + /** return only dates occuring before a given date */ + isBefore(iso) { + let pivot = quickDate(this, iso); + return this.filter(m => { + let obj = parseDates(m, this.opts)[0] || {}; + return obj.start && obj.start.d && obj.start.d.isBefore(pivot) + }) + } + /** return only dates occuring after a given date */ + isAfter(iso) { + let pivot = quickDate(this, iso); + return this.filter(m => { + let obj = parseDates(m, this.opts)[0] || {}; + return obj.start && obj.start.d && obj.start.d.isAfter(pivot) + }) + } + /** return only dates occuring after a given date */ + isSame(unit, iso) { + let pivot = quickDate(this, iso); + return this.filter(m => { + let obj = parseDates(m, this.opts)[0] || {}; + return obj.start && obj.start.d && obj.start.d.isSame(pivot, unit) + }) + } } View.prototype.dates = function (opts) { @@ -7432,17 +7575,16 @@ } let s = spacetime.now().time(res.result); return { - 'time': res.result, + time: res.result, '24h': s.format('time-24'), hour: s.hour(), - minute: s.minute() + minute: s.minute(), } }; const getNth = (doc, n) => (typeof n === 'number' ? doc.eq(n) : doc); const api$2 = function (View) { - class Times extends View { constructor(document, pointer, groups, opts) { super(document, pointer, groups); @@ -7477,7 +7619,7 @@ json(opts = {}) { return this.map(m => { let json = m.toView().json(opts)[0] || {}; - if (opts && opts.times !== true) { + if (opts && opts.time !== false) { json.time = parse$2(m); } return json @@ -7577,7 +7719,7 @@ json(opts = {}) { return this.map(m => { let json = m.toView().json(opts)[0] || {}; - if (opts && opts.times !== true) { + if (opts && opts.duration !== false) { json.duration = parse$1(m); } return json @@ -7586,7 +7728,7 @@ /** easy getter for the time */ get(options) { let arr = []; - this.forEach((doc) => { + this.forEach(doc => { let res = parse$1(doc); arr.push(res); }); @@ -7966,11 +8108,11 @@ { match: 'in? #Value to #Value #Duration time?', tag: 'Date', reason: '6-to-8-years' }, //two years old { match: '#Value #Duration old', unTag: 'Date', reason: 'val-years-old' }, - // + // { match: `${preps}? ${thisNext} ${seasons}`, tag: 'Date', reason: 'thisNext-season' }, - // + // { match: `the? ${sections} of ${seasons}`, tag: 'Date', reason: 'section-season' }, - // + // { match: `${seasons} ${preps}? #Cardinal`, tag: 'Date', reason: 'season-year' }, //june the 5th { match: '#Date the? #Ordinal', tag: 'Date', reason: 'correction' }, @@ -8005,7 +8147,6 @@ // mid-august { match: `[${sections}] #Date`, group: 0, tag: 'Date', reason: 'mid-sept' }, - //june 5 to 7th { match: '#Month #Value to #Value of? #Year?', tag: 'Date', reason: 'june 5 to 7th' }, //5 to 7th june @@ -8046,19 +8187,34 @@ //between x and y { match: '(between|from) #Date', tag: 'Date', reason: 'between x and y' }, { match: '(to|until|upto) #Date', tag: 'Date', reason: 'between x and y2' }, - { match: '#Date and #Date', tag: 'Date', reason: 'between x and y3' }, + { match: 'between #Date+ and #Date+', tag: 'Date', reason: 'between x and y3' }, + { match: '#Month and #Month #Year', tag: 'Date', reason: 'x and y4' }, //day after next { match: 'the? #Date after next one?', tag: 'Date', reason: 'day after next' }, //approximately... { match: '(about|approx|approximately|around) #Date', tag: 'Date', reason: 'approximately june' }, // until june - { match: '(by|until|on|in|at|during|over|every|each|due) the? #Date', ifNo: '#PhrasalVerb', tag: 'Date', reason: 'until june' }, + { + match: '(by|until|on|in|at|during|over|every|each|due) the? #Date', + ifNo: '#PhrasalVerb', + tag: 'Date', + reason: 'until june', + }, // until last june - { match: '(by|until|after|before|during|on|in|following|since) (next|this|last)? #Date', ifNo: '#PhrasalVerb', tag: 'Date', reason: 'until last june' }, + { + match: '(by|until|after|before|during|on|in|following|since) (next|this|last)? #Date', + ifNo: '#PhrasalVerb', + tag: 'Date', + reason: 'until last june', + }, //next september - { match: 'this? (last|next|past|this|previous|current|upcoming|coming|the) #Date', tag: 'Date', reason: 'next september' }, + { + match: 'this? (last|next|past|this|previous|current|upcoming|coming|the) #Date', + tag: 'Date', + reason: 'next september', + }, //starting this june { match: '(starting|beginning|ending) #Date', tag: 'Date', reason: 'starting this june' }, //start of june @@ -8077,9 +8233,17 @@ // 3 in the morning { match: '#Value (in|at) the? (morning|evening|night|nighttime)', tag: 'Time', reason: '3 in the morning' }, // ten to seven - { match: '(5|10|15|20|five|ten|fifteen|quarter|twenty|half) (after|past) #Cardinal', tag: 'Time', reason: 'ten to seven' }, //add check for 1 to 1 etc. + { + match: '(5|10|15|20|five|ten|fifteen|quarter|twenty|half) (after|past) #Cardinal', + tag: 'Time', + reason: 'ten to seven', + }, //add check for 1 to 1 etc. // at 10 past - { match: '(at|by|before) (5|10|15|20|five|ten|fifteen|twenty|quarter|half) (after|past|to)', tag: 'Time', reason: 'at-20-past' }, + { + match: '(at|by|before) (5|10|15|20|five|ten|fifteen|twenty|quarter|half) (after|past|to)', + tag: 'Time', + reason: 'at-20-past', + }, // iso (2020-03-02T00:00:00.000Z) // { match: '/^[0-9]{4}[:-][0-9]{2}[:-][0-9]{2}T[0-9]/', tag: 'Time', reason: 'iso-time-tag' }, // tuesday at 4 @@ -8094,7 +8258,12 @@ { match: '#Time [(sharp|on the dot)]', group: 0, tag: 'Time', reason: '4pm sharp' }, // around four thirty - { match: '(at|around|near|#Date) [#Cardinal (thirty|fifteen) (am|pm)?]', group: 0, tag: 'Time', reason: 'around four thirty' }, + { + match: '(at|around|near|#Date) [#Cardinal (thirty|fifteen) (am|pm)?]', + group: 0, + tag: 'Time', + reason: 'around four thirty', + }, // four thirty am { match: '#Cardinal (thirty|fifteen) (am|pm)', tag: 'Time', reason: 'four thirty am' }, // four thirty tomorrow @@ -8102,9 +8271,12 @@ //anytime around 3 { match: '(anytime|sometime) (before|after|near) [#Cardinal]', group: 0, tag: 'Time', reason: 'antime-after-3' }, - //'two days before'/ 'nine weeks frow now' - { match: '(#Cardinal|a|an) #Duration (before|after|ago|from|hence|back)', tag: 'DateShift', reason: 'nine weeks frow now' }, + { + match: '(#Cardinal|a|an) #Duration (before|after|ago|from|hence|back)', + tag: 'DateShift', + reason: 'nine weeks frow now', + }, // in two weeks { match: 'in (around|about|maybe|perhaps)? #Cardinal #Duration', tag: 'DateShift', reason: 'in two weeks' }, { match: 'in (a|an) #Duration', tag: 'DateShift', reason: 'in a week' }, @@ -8162,8 +8334,6 @@ { match: '#Ordinal quarter of? #Year', unTag: 'Fraction' }, // a month from now { match: '(from|by|before) now', unTag: 'Time', tag: 'Date' }, - - ]; var matches$1 = matches; @@ -8683,23 +8853,57 @@ ]; - var version = '3.4.2'; + var version = '3.5.0'; + + /* eslint-disable no-console */ + + const fmt = iso => (iso ? spacetime(iso).format('{nice-day} {year}') : '-'); - // import matches from './compute/matches.js' + // https://stackoverflow.com/questions/9781218/how-to-change-node-jss-console-font-color + const reset = '\x1b[0m'; + + //cheaper than requiring chalk + const cli = { + magenta: str => '\x1b[35m' + str + reset, + cyan: str => '\x1b[36m' + str + reset, + yellow: str => '\x1b[33m' + str + reset, + dim: str => '\x1b[2m' + str + reset, + i: str => '\x1b[3m' + str + reset, + }; + + const debug = function (view) { + view.dates().forEach(m => { + let res = m.dates().get()[0]; + + console.log('\n────────'); + m.debug('highlight'); + + let msg = ''; + if (res && res.start) { + msg = ' ' + cli.magenta(fmt(res.start)); + } + if (res && res.end) { + msg += cli.dim(' → ') + cli.cyan(fmt(res.end)); + } + console.log(msg + '\n'); + }); + }; + var debug$1 = debug; var plugin = { tags, words, compute: compute$1, api: api$1, - mutate: (world) => { + mutate: world => { + // add our regexes world.model.two.regexText = world.model.two.regexText || []; world.model.two.regexText = world.model.two.regexText.concat(regex); - // net = net || methods.one.buildNet(matches, world) - // world.model.two.matches = world.model.two.matches.concat(matches) + // add our debug('dates') method + world.methods.one.debug.dates = debug$1; }, hooks: ['dates'], - version + version, }; return plugin; diff --git a/plugins/dates/builds/compromise-dates.min.js b/plugins/dates/builds/compromise-dates.min.js index e6216c4b1..ce373469b 100644 --- a/plugins/dates/builds/compromise-dates.min.js +++ b/plugins/dates/builds/compromise-dates.min.js @@ -1 +1 @@ -var e,t;e=this,t=function(){var e=function(e){let t=null;return e.has("^(between|within) #Date")?e:(e.has("#Month")&&(t=e.match("[#Month #Value] and? #Month",0).ifNo("@hasDash$"),t.found&&(e=e.splitAfter(t)),t=e.match("[#Value #Month] and? #Value #Month",0),t.found&&(e=e.splitAfter(t)),t=e.match("^[#Month] and? #Month #Ordinal?$",0),t.found&&(e=e.splitAfter(t)),t=e.match("[#Month #Value] #Month",0).ifNo("@hasDash$"),t.found&&(e=e.splitAfter(t))),e.has("#WeekDay")&&(t=e.match("^[#WeekDay] and? #WeekDay$",0).ifNo("@hasDash$"),t.found&&(e=e.splitAfter(t)),t=e.match("#WeekDay #WeekDay and? #WeekDay"),t.found&&(e=e.splitOn("#WeekDay")),t=e.match("[#WeekDay] (and|or|this|next)? #WeekDay",0).ifNo("@hasDash$"),t.found&&(e=e.splitAfter("#WeekDay"))),t=e.match("(this|next) #Duration [(today|tomorrow|yesterday)]",0),t.found&&(e=e.splitBefore(t)),t=e.match("[(today|tomorrow|yesterday)] #Value #Month",0),t.found&&(e=e.splitAfter(t)),t=e.match("[(today|tomorrow|yesterday)] (today|tomorrow|yesterday|#WeekDay)",0).ifNo("@hasDash$"),t.found&&(e=e.splitAfter(t)),e=e.not("^and"))},t=function(t){let a=t.match("#Date+");a=a.filter((e=>{let t=e.has("^#Duration+$")||e.has("^#Value #Duration+$");return!(!0!==t||!e.has("(#FinancialQuarter|quarter)"))||!1===t}));let n=a.match("[#Cardinal #Duration (in|on|this|next|during|for)] #Date",0);return n.found&&(a=a.not(n)),n=a.match("[#Cardinal #Duration] #WeekDay",0),n.found&&(a=a.not(n)),n=a.match("#Date [for #Value #Duration]$",0),n.found&&(a=a.not(n)),n=a.match("[#Cardinal #Duration] #Date",0),n.found&&!a.has("#Cardinal #Duration] (ago|from|before|after|back)")&&(a=a.not(n)),n=a.match("for #Cardinal #Duration"),n.found&&(a=a.not(n)),a=a.notIf("^one (#WeekDay|#Month)$"),a=e(a),a=a.notIf("(#Money|#Percentage)"),a=a.notIf("^per #Duration"),a};const a={second:!0,minute:!0,hour:!0,day:!0,week:!0,weekend:!0,month:!0,season:!0,quarter:!0,year:!0},n={wk:"week",min:"minute",sec:"second",weekend:"week"},r=function(e){let t=e.match("#Duration").text("normal");return t=t.replace(/s$/,""),n.hasOwnProperty(t)&&(t=n[t]),t};var i=function(e){let t={},n=e.none(),i=e.match("#DateShift+");if(!1===i.found)return{res:t,m:n};if(i.match("#Cardinal #Duration").forEach((e=>{let n=e.match("#Cardinal").numbers().get()[0];if(n&&"number"==typeof n){let i=r(e);!0===a[i]&&(t[i]=n)}})),!0===i.has("(before|ago|hence|back)$")&&Object.keys(t).forEach((e=>t[e]*=-1)),n=i.match("#Cardinal #Duration"),i=i.not(n),n=i.match("[#Duration] [(after|before)]"),n.found){let e=n.groups("unit").text("reduced"),a=n.groups("dir").text("reduced");"after"===a?t[e]=1:"before"===a&&(t[e]=-1)}if(n=i.match("half (a|an) [#Duration]",0),n.found){let e=r(n);t[e]=.5}return n=e.match("#DateShift+"),{result:t,m:n}};const o={minute:!0};var s=function(e){let t=e.match("[#Value] [#Duration+] (of|in)");if(t.found){let e=t.groups(),a=e.num.numbers().get()[0],n=e.unit.text("reduced"),r={unit:n,num:Number(a)||0};return o[n]||(r.num-=1),{result:r,m:t}}if(t=e.match("[(first|initial|last|final)] [#Duration+] (of|in)"),t.found){let e=t.groups(),a=e.dir.text("reduced");return"initial"===a&&(a="first"),"final"===a&&(a="last"),{result:{unit:e.unit.text("reduced"),dir:a},m:t}}return{result:null,m:e.none()}};const u=(e,t,a)=>{const[n,r]=e.split("/"),[i,o]=r.split(":");return Date.UTC(a,n-1,i,o)-36e5*t};var d=(e,t,a,n,r)=>{const i=new Date(e).getUTCFullYear(),o=u(t,r,i),s=u(a,n,i);return e>=o&&e{let t=e.timezones[e.tz];if(void 0===t)return console.warn("Warning: couldn't find timezone "+e.tz),0;if(void 0===t.dst)return t.offset;let a=t.offset,n=t.offset+1;"n"===t.hem&&(n=a-1);let r=t.dst.split("->");return!0===d(e.epoch,r[0],r[1],a,n)?a:n},h={"9|s":"2/dili,2/jayapura","9|n":"2/chita,2/khandyga,2/pyongyang,2/seoul,2/tokyo,11/palau,japan,rok","9.5|s|04/03:03->10/02:02":"4/adelaide,4/broken_hill,4/south,4/yancowinna","9.5|s":"4/darwin,4/north","8|s|03/08:01->10/04:00":"12/casey","8|s":"2/kuala_lumpur,2/makassar,2/singapore,4/perth,2/ujung_pandang,4/west,singapore","8|n":"2/brunei,2/choibalsan,2/hong_kong,2/irkutsk,2/kuching,2/macau,2/manila,2/shanghai,2/taipei,2/ulaanbaatar,2/chongqing,2/chungking,2/harbin,2/macao,2/ulan_bator,hongkong,prc,roc","8.75|s":"4/eucla","7|s":"12/davis,2/jakarta,9/christmas","7|n":"2/bangkok,2/barnaul,2/hovd,2/krasnoyarsk,2/novokuznetsk,2/novosibirsk,2/phnom_penh,2/pontianak,2/ho_chi_minh,2/tomsk,2/vientiane,2/saigon","6|s":"12/vostok","6|n":"2/almaty,2/bishkek,2/dhaka,2/omsk,2/qyzylorda,2/qostanay,2/thimphu,2/urumqi,9/chagos,2/dacca,2/kashgar,2/thimbu","6.5|n":"2/yangon,9/cocos,2/rangoon","5|s":"12/mawson,9/kerguelen","5|n":"2/aqtau,2/aqtobe,2/ashgabat,2/atyrau,2/dushanbe,2/karachi,2/oral,2/samarkand,2/tashkent,2/yekaterinburg,9/maldives,2/ashkhabad","5.75|n":"2/katmandu,2/kathmandu","5.5|n":"2/kolkata,2/colombo,2/calcutta","4|s":"9/reunion","4|n":"2/baku,2/dubai,2/muscat,2/tbilisi,2/yerevan,8/astrakhan,8/samara,8/saratov,8/ulyanovsk,8/volgograd,2/volgograd,9/mahe,9/mauritius","4.5|n|03/22:00->09/21:24":"2/tehran,iran","4.5|n":"2/kabul","3|s":"12/syowa,9/antananarivo","3|n|03/27:03->10/30:04":"2/famagusta,2/nicosia,8/athens,8/bucharest,8/helsinki,8/kiev,8/mariehamn,8/riga,8/sofia,8/tallinn,8/uzhgorod,8/vilnius,8/zaporozhye,8/nicosia","3|n|03/27:02->10/30:03":"8/chisinau,8/tiraspol","3|n|03/27:00->10/29:24":"2/beirut","3|n|03/27:00->10/28:01":"2/gaza,2/hebron","3|n|03/25:02->10/30:02":"2/jerusalem,2/tel_aviv,israel","3|n|03/25:00->10/27:24":"2/damascus","3|n|02/25:00->10/28:01":"2/amman","3|n":"0/addis_ababa,0/asmara,0/asmera,0/dar_es_salaam,0/djibouti,0/juba,0/kampala,0/mogadishu,0/nairobi,2/aden,2/baghdad,2/bahrain,2/kuwait,2/qatar,2/riyadh,8/istanbul,8/kirov,8/minsk,8/moscow,8/simferopol,9/comoro,9/mayotte,2/istanbul,turkey,w-su","2|s|03/27:02->10/30:02":"12/troll","2|s":"0/gaborone,0/harare,0/johannesburg,0/lubumbashi,0/lusaka,0/maputo,0/maseru,0/mbabane","2|n|03/27:02->10/30:03":"0/ceuta,arctic/longyearbyen,8/amsterdam,8/andorra,8/belgrade,8/berlin,8/bratislava,8/brussels,8/budapest,8/busingen,8/copenhagen,8/gibraltar,8/ljubljana,8/luxembourg,8/madrid,8/malta,8/monaco,8/oslo,8/paris,8/podgorica,8/prague,8/rome,8/san_marino,8/sarajevo,8/skopje,8/stockholm,8/tirane,8/vaduz,8/vatican,8/vienna,8/warsaw,8/zagreb,8/zurich,3/jan_mayen,poland","2|n":"0/blantyre,0/bujumbura,0/cairo,0/khartoum,0/kigali,0/tripoli,8/kaliningrad,egypt,libya","1|s":"0/brazzaville,0/kinshasa,0/luanda,0/windhoek","1|n|03/27:03->05/08:02":"0/casablanca,0/el_aaiun","1|n|03/27:01->10/30:02":"3/canary,3/faroe,3/madeira,8/dublin,8/guernsey,8/isle_of_man,8/jersey,8/lisbon,8/london,3/faeroe,eire,8/belfast,gb-eire,gb,portugal","1|n":"0/algiers,0/bangui,0/douala,0/lagos,0/libreville,0/malabo,0/ndjamena,0/niamey,0/porto-novo,0/tunis","14|n":"11/kiritimati","13|s|04/04:04->09/26:03":"11/apia","13|s|01/15:02->11/05:03":"11/tongatapu","13|n":"11/enderbury,11/fakaofo","12|s|04/03:03->09/25:02":"12/mcmurdo,11/auckland,12/south_pole,nz","12|s|01/17:03->11/14:02":"11/fiji","12|n":"2/anadyr,2/kamchatka,2/srednekolymsk,11/funafuti,11/kwajalein,11/majuro,11/nauru,11/tarawa,11/wake,11/wallis,kwajalein","12.75|s|04/03:03->04/03:02":"11/chatham,nz-chat","11|s|04/03:03->10/02:02":"12/macquarie","11|s":"11/bougainville","11|n":"2/magadan,2/sakhalin,11/efate,11/guadalcanal,11/kosrae,11/noumea,11/pohnpei,11/ponape","11.5|n|04/03:03->10/02:02":"11/norfolk","10|s|04/03:03->10/02:02":"4/currie,4/hobart,4/melbourne,4/sydney,4/act,4/canberra,4/nsw,4/tasmania,4/victoria","10|s":"12/dumontdurville,4/brisbane,4/lindeman,11/port_moresby,4/queensland","10|n":"2/ust-nera,2/vladivostok,2/yakutsk,11/guam,11/saipan,11/chuuk,11/truk,11/yap","10.5|s|04/03:01->10/02:02":"4/lord_howe,4/lhi","0|n|03/27:00->10/30:01":"1/scoresbysund,3/azores","0|n":"0/abidjan,0/accra,0/bamako,0/banjul,0/bissau,0/conakry,0/dakar,0/freetown,0/lome,0/monrovia,0/nouakchott,0/ouagadougou,0/sao_tome,1/danmarkshavn,3/reykjavik,3/st_helena,13/gmt,13/utc,0/timbuktu,13/greenwich,13/uct,13/universal,13/zulu,gmt-0,gmt+0,gmt0,greenwich,iceland,uct,universal,utc,zulu","-9|n|03/13:02->11/06:02":"1/adak,1/atka,us/aleutian","-9|n":"11/gambier","-9.5|n":"11/marquesas","-8|n|03/13:02->11/06:02":"1/anchorage,1/juneau,1/metlakatla,1/nome,1/sitka,1/yakutat,us/alaska","-8|n":"11/pitcairn","-7|n|03/13:02->11/06:02":"1/los_angeles,1/santa_isabel,1/tijuana,1/vancouver,1/ensenada,6/pacific,10/bajanorte,us/pacific-new,us/pacific","-7|n|03/08:02->11/01:01":"1/dawson,1/whitehorse,6/yukon","-7|n":"1/creston,1/dawson_creek,1/fort_nelson,1/hermosillo,1/phoenix,us/arizona","-6|s|04/02:22->09/03:22":"11/easter,7/easterisland","-6|n|04/03:02->10/30:02":"1/chihuahua,1/mazatlan,10/bajasur","-6|n|03/13:02->11/06:02":"1/boise,1/cambridge_bay,1/denver,1/edmonton,1/inuvik,1/ojinaga,1/yellowknife,1/shiprock,6/mountain,navajo,us/mountain","-6|n":"1/belize,1/costa_rica,1/el_salvador,1/guatemala,1/managua,1/regina,1/swift_current,1/tegucigalpa,11/galapagos,6/east-saskatchewan,6/saskatchewan","-5|s":"1/lima,1/rio_branco,1/porto_acre,5/acre","-5|n|04/03:02->10/30:02":"1/bahia_banderas,1/merida,1/mexico_city,1/monterrey,10/general","-5|n|03/13:02->11/06:02":"1/chicago,1/matamoros,1/menominee,1/rainy_river,1/rankin_inlet,1/resolute,1/winnipeg,1/indiana/knox,1/indiana/tell_city,1/north_dakota/beulah,1/north_dakota/center,1/north_dakota/new_salem,1/knox_in,6/central,us/central,us/indiana-starke","-5|n|03/12:03->11/05:01":"1/north_dakota","-5|n":"1/bogota,1/cancun,1/cayman,1/coral_harbour,1/eirunepe,1/guayaquil,1/jamaica,1/panama,1/atikokan,jamaica","-4|s|05/13:23->08/13:01":"12/palmer","-4|s|04/02:24->09/04:00":"1/santiago,7/continental","-4|s|03/26:24->10/02:00":"1/asuncion","-4|s|02/16:24->11/03:00":"1/campo_grande,1/cuiaba","-4|s":"1/la_paz,1/manaus,5/west","-4|n|03/13:02->11/06:02":"1/detroit,1/grand_turk,1/indianapolis,1/iqaluit,1/louisville,1/montreal,1/nassau,1/new_york,1/nipigon,1/pangnirtung,1/port-au-prince,1/thunder_bay,1/toronto,1/indiana/marengo,1/indiana/petersburg,1/indiana/vevay,1/indiana/vincennes,1/indiana/winamac,1/kentucky/monticello,1/fort_wayne,1/indiana/indianapolis,1/kentucky/louisville,6/eastern,us/east-indiana,us/eastern,us/michigan","-4|n|03/13:00->11/06:01":"1/havana,cuba","-4|n|03/12:03->11/05:01":"1/indiana,1/kentucky","-4|n":"1/anguilla,1/antigua,1/aruba,1/barbados,1/blanc-sablon,1/boa_vista,1/caracas,1/curacao,1/dominica,1/grenada,1/guadeloupe,1/guyana,1/kralendijk,1/lower_princes,1/marigot,1/martinique,1/montserrat,1/port_of_spain,1/porto_velho,1/puerto_rico,1/santo_domingo,1/st_barthelemy,1/st_kitts,1/st_lucia,1/st_thomas,1/st_vincent,1/tortola,1/virgin","-3|s":"1/argentina,1/buenos_aires,1/catamarca,1/cordoba,1/fortaleza,1/jujuy,1/mendoza,1/montevideo,1/punta_arenas,1/sao_paulo,12/rothera,3/stanley,1/argentina/la_rioja,1/argentina/rio_gallegos,1/argentina/salta,1/argentina/san_juan,1/argentina/san_luis,1/argentina/tucuman,1/argentina/ushuaia,1/argentina/comodrivadavia,1/argentina/buenos_aires,1/argentina/catamarca,1/argentina/cordoba,1/argentina/jujuy,1/argentina/mendoza,1/argentina/rosario,1/rosario,5/east","-3|n|03/13:02->11/06:02":"1/glace_bay,1/goose_bay,1/halifax,1/moncton,1/thule,3/bermuda,6/atlantic","-3|n":"1/araguaina,1/bahia,1/belem,1/cayenne,1/maceio,1/paramaribo,1/recife,1/santarem","-2|n|03/26:22->10/29:23":"1/nuuk,1/godthab","-2|n|03/13:02->11/06:02":"1/miquelon","-2|n":"1/noronha,3/south_georgia,5/denoronha","-2.5|n|03/13:02->11/06:02":"1/st_johns,6/newfoundland","-1|n":"3/cape_verde","-11|n":"11/midway,11/niue,11/pago_pago,11/samoa,us/samoa","-10|n":"11/honolulu,11/johnston,11/rarotonga,11/tahiti,us/hawaii"},m=["africa","america","asia","atlantic","australia","brazil","canada","chile","europe","indian","mexico","pacific","antarctica","etc"];let c={};Object.keys(h).forEach((e=>{let t=e.split("|"),a={offset:Number(t[0]),hem:t[1]};t[2]&&(a.dst=t[2]),h[e].split(",").forEach((e=>{e=e.replace(/(^[0-9]+)\//,((e,t)=>(t=Number(t),m[t]+"/"))),c[e]=a}))})),c.utc={offset:0,hem:"n"};for(let e=-14;e<=14;e+=.5){let t=e;t>0&&(t="+"+t);let a="etc/gmt"+t;c[a]={offset:-1*e,hem:"n"},a="utc/gmt"+t,c[a]={offset:-1*e,hem:"n"}}var f=c,y=()=>{let e=(()=>{if("undefined"==typeof Intl||void 0===Intl.DateTimeFormat)return null;let e=Intl.DateTimeFormat();if(void 0===e||void 0===e.resolvedOptions)return null;let t=e.resolvedOptions().timeZone;return t?t.toLowerCase():null})();return null===e?"utc":e};const p=/(\-?[0-9]+)h(rs)?/i,g=/(\-?[0-9]+)/,w=/utc([\-+]?[0-9]+)/i,b=/gmt([\-+]?[0-9]+)/i,k=function(e){return(e=Number(e))>=-13&&e<=13?"etc/gmt"+(e=((e*=-1)>0?"+":"")+e):null};var D=function(e){let t=e.match(p);if(null!==t)return k(t[1]);if(t=e.match(w),null!==t)return k(t[1]);if(t=e.match(b),null!==t){let e=-1*Number(t[1]);return k(e)}return t=e.match(g),null!==t?k(t[1]):null};const v=y(),x=Object.keys(f).reduce(((e,t)=>{let a=t.split("/")[1]||"";return a=a.replace(/_/g," "),e[a]=t,e}),{});var O=(e,t)=>{if(!e)return v;"string"!=typeof e&&console.error("Timezone must be a string - recieved: '",e,"'\n");let a=e.trim();if(a=a.toLowerCase(),!0===t.hasOwnProperty(a))return a;if(a=(e=>(e=(e=(e=(e=(e=e.replace(/ time/g,"")).replace(/ (standard|daylight|summer)/g,"")).replace(/\b(east|west|north|south)ern/g,"$1")).replace(/\b(africa|america|australia)n/g,"$1")).replace(/\beuropean/g,"europe")).replace(/\islands/g,"island"))(a),!0===t.hasOwnProperty(a))return a;if(!0===x.hasOwnProperty(a))return x[a];if(!0===/[0-9]/.test(a)){let e=D(a);if(e)return e}throw new Error("Spacetime: Cannot find timezone named: '"+e+"'. Please enter an IANA timezone id.")};function M(e){return e%4==0&&e%100!=0||e%400==0}function _(e){return"[object Date]"===Object.prototype.toString.call(e)&&!isNaN(e.valueOf())}function j(e){return"[object Object]"===Object.prototype.toString.call(e)}function $(e,t=2){return(e+="").length>=t?e:new Array(t-e.length+1).join("0")+e}function T(e){let t=e%10,a=e%100;return 1===t&&11!==a?e+"st":2===t&&12!==a?e+"nd":3===t&&13!==a?e+"rd":e+"th"}function z(e){return e=(e=String(e)).replace(/([0-9])(st|nd|rd|th)$/i,"$1"),parseInt(e,10)}function V(e=""){return"day"===(e=(e=(e=(e=e.toLowerCase().trim()).replace(/ies$/,"y")).replace(/s$/,"")).replace(/-/g,""))||"days"===e?"date":"min"===e||"mins"===e?"minute":e}function q(e){return"number"==typeof e?e:_(e)?e.getTime():e.epoch?e.epoch:null}function S(e,t){return!1===j(e)?t.clone().set(e):e}function C(e,t=""){const a=e>0?"+":"-",n=Math.abs(e);return`${a}${$(parseInt(""+n,10))}${t}${$(n%1*60)}`}const A={year:(new Date).getFullYear(),month:0,date:1};var N={parseArray:(e,t,a)=>{if(0===t.length)return e;let n=["year","month","date","hour","minute","second","millisecond"];for(let r=0;r{if(0===Object.keys(t).length)return e;t=Object.assign({},A,a,t);let n=Object.keys(t);for(let r=0;r0&&t<25e8&&!1===e.silent&&(console.warn(" - Warning: You are setting the date to January 1970."),console.warn(" - did input seconds instead of milliseconds?")),e.epoch=t,e}};const E=function(e){return e.epoch=Date.now(),Object.keys(e._today||{}).forEach((t=>{"function"==typeof e[t]&&(e=e[t](e._today[t]))})),e},P={now:e=>E(e),today:e=>E(e),tonight:e=>e=(e=E(e)).hour(18),tomorrow:e=>e=(e=(e=E(e)).add(1,"day")).startOf("day"),yesterday:e=>e=(e=(e=E(e)).subtract(1,"day")).startOf("day"),christmas:e=>{let t=E(e).year();return e=e.set([t,11,25,18,0,0])},"new years":e=>{let t=E(e).year();return e=e.set([t,11,31,18,0,0])}};P["new years eve"]=P["new years"];var Y=P,H=function(e){return e=(e=(e=(e=e.replace(/\b(mon|tues?|wed|wednes|thur?s?|fri|sat|satur|sun)(day)?\b/i,"")).replace(/([0-9])(th|rd|st|nd)/,"$1")).replace(/,/g,"")).replace(/ +/g," ").trim()};let B={millisecond:1,second:1e3,minute:6e4,hour:36e5,day:864e5};B.date=B.day,B.month=25488e5,B.week=6048e5,B.year=3154e7,Object.keys(B).forEach((e=>{B[e+"s"]=B[e]}));var L=B;const I=(e,t,a,n,r)=>{let i=e.d[a]();if(i===t)return;let o=null===r?null:e.d[r](),s=e.epoch,u=t-i;e.epoch+=L[n]*u,"day"===n&&Math.abs(u)>28&&t<28&&(e.epoch+=L.hour),null!==r&&o!==e.d[r]()&&(e.epoch=s);const d=L[n]/2;for(;e.d[a]()t;)e.epoch-=d;null!==r&&o!==e.d[r]()&&(e.epoch=s)},W={year:{valid:e=>e>-4e3&&e<4e3,walkTo:(e,t)=>I(e,t,"getFullYear","year",null)},month:{valid:e=>e>=0&&e<=11,walkTo:(e,t)=>{let a=e.d,n=a.getMonth(),r=e.epoch,i=a.getFullYear();if(n===t)return;let o=t-n;for(e.epoch+=L.day*(28*o),i!==e.d.getFullYear()&&(e.epoch=r);e.d.getMonth()t;)e.epoch-=L.day}},date:{valid:e=>e>0&&e<=31,walkTo:(e,t)=>I(e,t,"getDate","day","getMonth")},hour:{valid:e=>e>=0&&e<24,walkTo:(e,t)=>I(e,t,"getHours","hour","getDate")},minute:{valid:e=>e>=0&&e<60,walkTo:(e,t)=>I(e,t,"getMinutes","minute","getHours")},second:{valid:e=>e>=0&&e<60,walkTo:(e,t)=>{e.epoch=e.seconds(t).epoch}},millisecond:{valid:e=>e>=0&&e<1e3,walkTo:(e,t)=>{e.epoch=e.milliseconds(t).epoch}}};var F=(e,t)=>{let a=Object.keys(W),n=e.clone();for(let r=0;r{if(!t)return e;let a=0;if(/^[\+-]?[0-9]{2}:[0-9]{2}$/.test(t)&&(!0===/:00/.test(t)&&(t=t.replace(/:00/,"")),!0===/:30/.test(t)&&(t=t.replace(/:30/,".5"))),/^[\+-]?[0-9]{4}$/.test(t)&&(t=t.replace(/30$/,".5")),a=parseFloat(t),Math.abs(a)>100&&(a/=100),0===a||"Z"===t||"z"===t)return e.tz="etc/gmt",e;a*=-1,a>=0&&(a="+"+a);let n="etc/gmt"+a;return e.timezones[n]&&(e.tz=n),e},Z=(e,t="")=>{let a=(t=t.replace(/^\s+/,"").toLowerCase()).match(/([0-9]{1,2}):([0-9]{1,2}):?([0-9]{1,2})?[:\.]?([0-9]{1,4})?/);if(null!==a){let n=Number(a[1]);if(n<0||n>24)return e.startOf("day");let r=Number(a[2]);if(a[2].length<2||r<0||r>59)return e.startOf("day");e=(e=(e=(e=e.hour(n)).minute(r)).seconds(a[3]||0)).millisecond(function(e=""){return(e=String(e)).length>3?e=e.substr(0,3):1===e.length?e+="00":2===e.length&&(e+="0"),Number(e)||0}(a[4]));let i=t.match(/[\b0-9] ?(am|pm)\b/);return null!==i&&i[1]&&(e=e.ampm(i[1])),e}if(a=t.match(/([0-9]+) ?(am|pm)/),null!==a&&a[1]){let t=Number(a[1]);return t>12||t<1?e.startOf("day"):e=(e=(e=e.hour(a[1]||0)).ampm(a[2])).startOf("hour")}return e=e.startOf("day")};let X=G();const ee=e=>{if(!0!==J.hasOwnProperty(e.month))return!1;if(1===e.month)return!!(M(e.year)&&e.date<=29)||e.date<=28;let t=J[e.month]||0;return e.date<=t},te=(e="",t)=>{if(e=e.trim(),!0===/^'[0-9][0-9]$/.test(e)){let t=Number(e.replace(/'/,""));return t>50?1900+t:2e3+t}let a=parseInt(e,10);return!a&&t&&(a=t.year),a=a||(new Date).getFullYear(),a},ae=function(e){return"sept"===(e=e.toLowerCase().trim())?X.sep:X[e]};var ne=[{reg:/^([0-9]{1,2})[\-\/.]([0-9]{1,2})[\-\/.]?([0-9]{4})?( [0-9]{1,2}:[0-9]{2}:?[0-9]{0,2}? ?(am|pm|gmt))?$/i,parse:(e,t)=>{let a=parseInt(t[1],10)-1,n=parseInt(t[2],10);(e.british||a>=12)&&(n=parseInt(t[1],10),a=parseInt(t[2],10)-1);let r={date:n,month:a,year:te(t[3],e._today)||(new Date).getFullYear()};return!1===ee(r)?(e.epoch=null,e):(F(e,r),e=Z(e,t[4]))}},{reg:/^([a-z]+)[\-\/\. ]([0-9]{1,2})[\-\/\. ]?([0-9]{4}|'[0-9]{2})?( [0-9]{1,2}(:[0-9]{0,2})?(:[0-9]{0,3})? ?(am|pm)?)?$/i,parse:(e,t)=>{let a={year:te(t[3],e._today),month:ae(t[1]),date:z(t[2]||"")};return!1===ee(a)?(e.epoch=null,e):(F(e,a),e=Z(e,t[4]))}},{reg:/^([a-z]+) ([0-9]{1,2})( [0-9]{4})?( ([0-9:]+( ?am| ?pm| ?gmt)?))?$/i,parse:(e,t)=>{let a={year:te(t[3],e._today),month:ae(t[1]),date:z(t[2]||"")};return!1===ee(a)?(e.epoch=null,e):(F(e,a),e=Z(e,t[4]))}},{reg:/^([a-z]+) ([0-9]{1,2})( [0-9:]+)?( \+[0-9]{4})?( [0-9]{4})?$/i,parse:(e,t)=>{let a={year:te(t[5],e._today),month:ae(t[1]),date:z(t[2]||"")};return!1===ee(a)?(e.epoch=null,e):(F(e,a),e=Z(e,t[3]))}}],re=[{reg:/^([0-9]{4})[\-\/]([0-9]{2})$/i,parse:(e,t)=>{let a={year:t[1],month:parseInt(t[2],10)-1,date:1};return!1===ee(a)?(e.epoch=null,e):(F(e,a),e=Z(e,t[4]))}},{reg:/^([a-z]+) ([0-9]{4})$/i,parse:(e,t)=>{let a={year:te(t[2],e._today),month:ae(t[1]),date:e._today.date||1};return!1===ee(a)?(e.epoch=null,e):(F(e,a),e=Z(e,t[4]))}},{reg:/^(q[0-9])( of)?( [0-9]{4})?/i,parse:(e,t)=>{let a=t[1]||"";e=e.quarter(a);let n=t[3]||"";return n&&(n=n.trim(),e=e.year(n)),e}},{reg:/^(spring|summer|winter|fall|autumn)( of)?( [0-9]{4})?/i,parse:(e,t)=>{let a=t[1]||"";e=e.season(a);let n=t[3]||"";return n&&(n=n.trim(),e=e.year(n)),e}},{reg:/^[0-9,]+ ?b\.?c\.?$/i,parse:(e,t)=>{let a=t[0]||"";a=a.replace(/^([0-9,]+) ?b\.?c\.?$/i,"-$1");let n=new Date,r={year:parseInt(a.trim(),10),month:n.getMonth(),date:n.getDate()};return!1===ee(r)?(e.epoch=null,e):(F(e,r),e=Z(e))}},{reg:/^[0-9,]+ ?(a\.?d\.?|c\.?e\.?)$/i,parse:(e,t)=>{let a=t[0]||"";a=a.replace(/,/g,"");let n=new Date,r={year:parseInt(a.trim(),10),month:n.getMonth(),date:n.getDate()};return!1===ee(r)?(e.epoch=null,e):(F(e,r),e=Z(e))}},{reg:/^[0-9]{4}( ?a\.?d\.?)?$/i,parse:(e,t)=>{let a=e._today;a.month&&!a.date&&(a.date=1);let n=new Date,r={year:te(t[0],a),month:a.month||n.getMonth(),date:a.date||n.getDate()};return!1===ee(r)?(e.epoch=null,e):(F(e,r),e=Z(e))}}],ie=[].concat([{reg:/^(\-?0?0?[0-9]{3,4})-([0-9]{1,2})-([0-9]{1,2})[T| ]([0-9.:]+)(Z|[0-9\-\+:]+)?$/i,parse:(e,t)=>{let a={year:t[1],month:parseInt(t[2],10)-1,date:t[3]};return!1===ee(a)?(e.epoch=null,e):(U(e,t[5]),F(e,a),e=Z(e,t[4]))}},{reg:/^([0-9]{4})[\-\/\. ]([0-9]{1,2})[\-\/\. ]([0-9]{1,2})( [0-9]{1,2}(:[0-9]{0,2})?(:[0-9]{0,3})? ?(am|pm)?)?$/i,parse:(e,t)=>{let a={year:t[1],month:parseInt(t[2],10)-1,date:parseInt(t[3],10)};return a.month>=12&&(a.date=parseInt(t[2],10),a.month=parseInt(t[3],10)-1),!1===ee(a)?(e.epoch=null,e):(F(e,a),e=Z(e,t[4]))}},{reg:/^([0-9]{4})[\-\/\. ]([a-z]+)[\-\/\. ]([0-9]{1,2})( [0-9]{1,2}(:[0-9]{0,2})?(:[0-9]{0,3})? ?(am|pm)?)?$/i,parse:(e,t)=>{let a={year:te(t[1],e._today),month:ae(t[2]),date:z(t[3]||"")};return!1===ee(a)?(e.epoch=null,e):(F(e,a),e=Z(e,t[4]))}}],ne,[{reg:/^([0-9]{1,2})[\-\/]([a-z]+)[\-\/]?([0-9]{4})?$/i,parse:(e,t)=>{let a={year:te(t[3],e._today),month:ae(t[2]),date:z(t[1]||"")};return!1===ee(a)?(e.epoch=null,e):(F(e,a),e=Z(e,t[4]))}},{reg:/^([0-9]{1,2})( [a-z]+)( [0-9]{4}| '[0-9]{2})? ?([0-9]{1,2}:[0-9]{2}:?[0-9]{0,2}? ?(am|pm|gmt))?$/i,parse:(e,t)=>{let a={year:te(t[3],e._today),month:ae(t[2]),date:z(t[1])};return a.month&&!1!==ee(a)?(F(e,a),e=Z(e,t[4])):(e.epoch=null,e)}},{reg:/^([0-9]{1,2})[\. -/]([a-z]+)[\. -/]([0-9]{4})?( [0-9]{1,2}(:[0-9]{0,2})?(:[0-9]{0,3})? ?(am|pm)?)?$/i,parse:(e,t)=>{let a={date:Number(t[1]),month:ae(t[2]),year:Number(t[3])};return!1===ee(a)?(e.epoch=null,e):(F(e,a),e=e.startOf("day"),e=Z(e,t[4]))}}],re),oe=function(e,t,a){for(let n=0;n{let a=e._today||le;if("number"==typeof t)return de(e,t);if(e.epoch=Date.now(),e._today&&j(e._today)&&Object.keys(e._today).length>0){let t=ue(e,a,le);t.isValid()&&(e.epoch=t.epoch)}return null==t||""===t?e:!0===_(t)?(e.epoch=t.getTime(),e):!0===function(e){return"[object Array]"===Object.prototype.toString.call(e)}(t)?e=se(e,t,a):!0===j(t)?t.epoch?(e.epoch=t.epoch,e.tz=t.tz,e):e=ue(e,t,a):"string"!=typeof t?e:(t=H(t),!0===Y.hasOwnProperty(t)?e=Y[t](e):oe(e,t))};let me=["sun","mon","tue","wed","thu","fri","sat"],ce=["sunday","monday","tuesday","wednesday","thursday","friday","saturday"];function fe(){return me}function ye(){return ce}const pe={mo:1,tu:2,we:3,th:4,fr:5,sa:6,su:7,tues:2,weds:3,wedn:3,thur:4,thurs:4};let ge=!0;var we=e=>{let t=e.timezone().current.offset;return t?C(t,":"):"Z"};const be=e=>ge?function(e){return e?e[0].toUpperCase()+e.substr(1):""}(e):e,ke={day:e=>be(e.dayName()),"day-short":e=>be(fe()[e.day()]),"day-number":e=>e.day(),"day-ordinal":e=>T(e.day()),"day-pad":e=>$(e.day()),date:e=>e.date(),"date-ordinal":e=>T(e.date()),"date-pad":e=>$(e.date()),month:e=>be(e.monthName()),"month-short":e=>be(R()[e.month()]),"month-number":e=>e.month(),"month-ordinal":e=>T(e.month()),"month-pad":e=>$(e.month()),"iso-month":e=>$(e.month()+1),year:e=>{let t=e.year();return t>0?t:(t=Math.abs(t),t+" BC")},"year-short":e=>{let t=e.year();return t>0?`'${String(e.year()).substr(2,4)}`:(t=Math.abs(t),t+" BC")},"iso-year":e=>{let t=e.year(),a=t<0,n=$(Math.abs(t),4);return a&&(n=$(n,6),n="-"+n),n},time:e=>e.time(),"time-24":e=>`${e.hour24()}:${$(e.minute())}`,hour:e=>e.hour12(),"hour-pad":e=>$(e.hour12()),"hour-24":e=>e.hour24(),"hour-24-pad":e=>$(e.hour24()),minute:e=>e.minute(),"minute-pad":e=>$(e.minute()),second:e=>e.second(),"second-pad":e=>$(e.second()),millisecond:e=>e.millisecond(),"millisecond-pad":e=>$(e.millisecond(),3),ampm:e=>e.ampm(),quarter:e=>"Q"+e.quarter(),season:e=>e.season(),era:e=>e.era(),json:e=>e.json(),timezone:e=>e.timezone().name,offset:e=>we(e),numeric:e=>`${e.year()}/${$(e.month()+1)}/${$(e.date())}`,"numeric-us":e=>`${$(e.month()+1)}/${$(e.date())}/${e.year()}`,"numeric-uk":e=>`${$(e.date())}/${$(e.month()+1)}/${e.year()}`,"mm/dd":e=>`${$(e.month()+1)}/${$(e.date())}`,iso:e=>`${e.format("iso-year")}-${$(e.month()+1)}-${$(e.date())}T${$(e.h24())}:${$(e.minute())}:${$(e.second())}.${$(e.millisecond(),3)}${we(e)}`,"iso-short":e=>{let t=$(e.month()+1),a=$(e.date());var n;return`${(n=e.year())>=0?$(n,4):"-"+$(n=Math.abs(n),4)}-${t}-${a}`},"iso-utc":e=>new Date(e.epoch).toISOString(),nice:e=>`${R()[e.month()]} ${T(e.date())}, ${e.time()}`,"nice-24":e=>`${R()[e.month()]} ${T(e.date())}, ${e.hour24()}:${$(e.minute())}`,"nice-year":e=>`${R()[e.month()]} ${T(e.date())}, ${e.year()}`,"nice-day":e=>`${fe()[e.day()]} ${be(R()[e.month()])} ${T(e.date())}`,"nice-full":e=>`${e.dayName()} ${be(e.monthName())} ${T(e.date())}, ${e.time()}`,"nice-full-24":e=>`${e.dayName()} ${be(e.monthName())} ${T(e.date())}, ${e.hour24()}:${$(e.minute())}`},De={"day-name":"day","month-name":"month","iso 8601":"iso","time-h24":"time-24","time-12":"time","time-h12":"time",tz:"timezone","day-num":"day-number","month-num":"month-number","month-iso":"iso-month","year-iso":"iso-year","nice-short":"nice","nice-short-24":"nice-24",mdy:"numeric-us",dmy:"numeric-uk",ymd:"numeric","yyyy/mm/dd":"numeric","mm/dd/yyyy":"numeric-us","dd/mm/yyyy":"numeric-us","little-endian":"numeric-uk","big-endian":"numeric","day-nice":"nice-day"};Object.keys(De).forEach((e=>ke[e]=ke[De[e]]));var ve=(e,t="")=>{if(!0!==e.isValid())return"";if(ke.hasOwnProperty(t)){let a=ke[t](e)||"";return"json"!==t&&(a=String(a),"ampm"!==t&&(a=be(a))),a}if(-1!==t.indexOf("{")){let a=/\{(.+?)\}/g;return t=t.replace(a,((t,a)=>{if(a=a.toLowerCase().trim(),ke.hasOwnProperty(a)){let t=String(ke[a](e));return"ampm"!==a?be(t):t}return""})),t}return e.format("iso-short")};const xe={G:e=>e.era(),GG:e=>e.era(),GGG:e=>e.era(),GGGG:e=>"AD"===e.era()?"Anno Domini":"Before Christ",y:e=>e.year(),yy:e=>$(Number(String(e.year()).substr(2,4))),yyy:e=>e.year(),yyyy:e=>e.year(),yyyyy:e=>"0"+e.year(),Q:e=>e.quarter(),QQ:e=>e.quarter(),QQQ:e=>e.quarter(),QQQQ:e=>e.quarter(),M:e=>e.month()+1,MM:e=>$(e.month()+1),MMM:e=>e.format("month-short"),MMMM:e=>e.format("month"),w:e=>e.week(),ww:e=>$(e.week()),d:e=>e.date(),dd:e=>$(e.date()),D:e=>e.dayOfYear(),DD:e=>$(e.dayOfYear()),DDD:e=>$(e.dayOfYear(),3),E:e=>e.format("day-short"),EE:e=>e.format("day-short"),EEE:e=>e.format("day-short"),EEEE:e=>e.format("day"),EEEEE:e=>e.format("day")[0],e:e=>e.day(),ee:e=>e.day(),eee:e=>e.format("day-short"),eeee:e=>e.format("day"),eeeee:e=>e.format("day")[0],a:e=>e.ampm().toUpperCase(),aa:e=>e.ampm().toUpperCase(),aaa:e=>e.ampm().toUpperCase(),aaaa:e=>e.ampm().toUpperCase(),h:e=>e.h12(),hh:e=>$(e.h12()),H:e=>e.hour(),HH:e=>$(e.hour()),m:e=>e.minute(),mm:e=>$(e.minute()),s:e=>e.second(),ss:e=>$(e.second()),SSS:e=>$(e.millisecond(),3),A:e=>e.epoch-e.startOf("day").epoch,z:e=>e.timezone().name,zz:e=>e.timezone().name,zzz:e=>e.timezone().name,zzzz:e=>e.timezone().name,Z:e=>C(e.timezone().current.offset),ZZ:e=>C(e.timezone().current.offset),ZZZ:e=>C(e.timezone().current.offset),ZZZZ:e=>C(e.timezone().current.offset,":")},Oe=(e,t,a)=>{let n=e,r=t;for(let i=0;i{let a=t.split("");return a=function(e){for(let t=0;te))}(a),a=function(e){for(let t=0;te))).map((e=>("''"===e&&(e="'"),e)))}(a),a.reduce(((t,a)=>(void 0!==xe[a]?t+=xe[a](e)||"":(/^'.{1,}'$/.test(a)&&(a=a.replace(/'/g,"")),t+=a),t)),"")};const _e=["year","season","quarter","month","week","day","quarterHour","hour","minute"],je=function(e,t){let a=e.clone().startOf(t),n=e.clone().endOf(t).epoch-a.epoch,r=(e.epoch-a.epoch)/n;return parseFloat(r.toFixed(2))};var $e=(e,t)=>{if(t)return t=V(t),je(e,t);let a={};return _e.forEach((t=>{a[t]=je(e,t)})),a},Te=(e,t)=>{let a=e.progress();return"quarterhour"===(t=V(t))&&(t="quarterHour"),void 0!==a[t]?(a[t]>.5&&(e=e.add(1,t)),e=e.startOf(t)):!1===e.silent&&console.warn("no known unit '"+t+"'"),e};const ze=(e,t,a)=>{let n=0;for(e=e.clone();e.isBefore(t);)e=e.add(1,a),n+=1;return e.isAfter(t,a)&&(n-=1),n};var Ve=(e,t,a)=>e.isBefore(t)?ze(e,t,a):-1*ze(t,e,a),qe=function(e,t){let a=t.epoch-e.epoch,n={milliseconds:a,seconds:parseInt(a/1e3,10)};n.minutes=parseInt(n.seconds/60,10),n.hours=parseInt(n.minutes/60,10);let r=e.clone();return n.years=((e,t)=>{let a=t.year()-e.year();return(e=e.year(t.year())).isAfter(t)&&(a-=1),a})(r,t),r=e.add(n.years,"year"),n.months=12*n.years,r=e.add(n.months,"month"),n.months+=Ve(r,t,"month"),n.weeks=52*n.years,r=e.add(n.weeks,"week"),n.weeks+=Ve(r,t,"week"),n.days=7*n.weeks,r=e.add(n.days,"day"),n.days+=Ve(r,t,"day"),n},Se=function(e,t,a){t=S(t,e);let n=!1;if(e.isAfter(t)){let a=e;e=t,t=a,n=!0}let r=qe(e,t);return n&&(r=function(e){return Object.keys(e).forEach((t=>{e[t]*=-1})),e}(r)),a?(a=V(a),!0!==/s$/.test(a)&&(a+="s"),"dates"===a&&(a="days"),r[a]):r};const Ce=e=>Math.abs(e)||0;var Ae=function(e){let t="P";return t+=Ce(e.years)+"Y",t+=Ce(e.months)+"M",t+=Ce(e.days)+"DT",t+=Ce(e.hours)+"H",t+=Ce(e.minutes)+"M",t+=Ce(e.seconds)+"S",t};const Ne={months:{almost:10,over:4},days:{almost:25,over:10},hours:{almost:20,over:8},minutes:{almost:50,over:20},seconds:{almost:50,over:20}};function Ee(e,t){return 1===e&&(t=t.slice(0,-1)),e+" "+t}var Pe=function(e){let t=null,a=null,n=[],r=[];return Object.keys(e).forEach(((i,o,s)=>{const u=Math.abs(e[i]);if(0===u)return;n.push(u+i[0]);const d=Ee(u,i);if(r.push(d),!t){if(t=a=d,o>4)return;const n=s[o+1],r=Math.abs(e[n]);r>Ne[n].almost?(t=Ee(u+1,i),a="almost "+t):r>Ne[n].over&&(a="over "+d)}})),{qualified:a,rounded:t,abbreviated:n,englishValues:r}},Ye=(e,t)=>{const a=function(e,t){const a=e.isBefore(t),n=a?t:e;let r=a?e:t;r=r.clone();const i={years:0,months:0,days:0,hours:0,minutes:0,seconds:0};return Object.keys(i).forEach((e=>{if(r.isSame(n,e))return;let t=r.diff(n,e);r=r.add(t,e),i[e]=t})),a&&Object.keys(i).forEach((e=>{0!==i[e]&&(i[e]*=-1)})),i}(e,t=S(t,e));if(!0===Object.keys(a).every((e=>!a[e])))return{diff:a,rounded:"now",qualified:"now",precise:"now",abbreviated:[],iso:"P0Y0M0DT0H0M0S",direction:"present"};let n,r="future",{rounded:i,qualified:o,englishValues:s,abbreviated:u}=Pe(a);n=s.splice(0,2).join(", "),!0===e.isAfter(t)?(i+=" ago",o+=" ago",n+=" ago",r="past"):(i="in "+i,o="in "+o,n="in "+n);let d=Ae(a);return{diff:a,rounded:i,qualified:o,precise:n,abbreviated:u,iso:d,direction:r}},He={north:[["spring",2,1],["summer",5,1],["fall",8,1],["autumn",8,1],["winter",11,1]],south:[["fall",2,1],["autumn",2,1],["winter",5,1],["spring",8,1],["summer",11,1]]},Be=[null,[0,1],[3,1],[6,1],[9,1]];const Le={minute:e=>(F(e,{second:0,millisecond:0}),e),quarterhour:e=>{let t=e.minutes();return e=t>=45?e.minutes(45):t>=30?e.minutes(30):t>=15?e.minutes(15):e.minutes(0),F(e,{second:0,millisecond:0}),e},hour:e=>(F(e,{minute:0,second:0,millisecond:0}),e),day:e=>(F(e,{hour:0,minute:0,second:0,millisecond:0}),e),week:e=>{let t=e.clone();return(e=e.day(e._weekStart)).isAfter(t)&&(e=e.subtract(1,"week")),F(e,{hour:0,minute:0,second:0,millisecond:0}),e},month:e=>(F(e,{date:1,hour:0,minute:0,second:0,millisecond:0}),e),quarter:e=>{let t=e.quarter();return Be[t]&&F(e,{month:Be[t][0],date:Be[t][1],hour:0,minute:0,second:0,millisecond:0}),e},season:e=>{let t=e.season(),a="north";"South"===e.hemisphere()&&(a="south");for(let n=0;n(F(e,{month:0,date:1,hour:0,minute:0,second:0,millisecond:0}),e),decade:e=>{let t=(e=e.startOf("year")).year(),a=10*parseInt(t/10,10);return e=e.year(a)},century:e=>{let t=(e=e.startOf("year")).year(),a=100*parseInt(t/100,10);return e=e.year(a)}};Le.date=Le.day;var Ie=function(e,t,a){if(!t||!a)return[];if(t=V(t),a=e.clone().set(a),e.isAfter(a)){let t=e;e=a,a=t}let n=e.clone();!function(e){return!!fe().find((t=>t===e))||!!ye().find((t=>t===e))}(t)?n.startOf(t).isBefore(e)&&(n=n.next(t)):(n=n.next(t),t="week");let r=[];for(;n.isBefore(a);)r.push(n),n=n.add(1,t);return r},We=e=>{let t=e.timezones,a=e.tz;if(!1===t.hasOwnProperty(a)&&(a=O(e.tz,t)),null===a)return!1===e.silent&&console.warn("Warn: could not find given or local timezone - '"+e.tz+"'"),{current:{epochShift:0}};let n=t[a],r={name:(i=a,i=(i=(i=i[0].toUpperCase()+i.substr(1)).replace(/\/gmt/,"/GMT")).replace(/[\/_]([a-z])/gi,(e=>e.toUpperCase()))),hasDst:Boolean(n.dst),default_offset:n.offset,hemisphere:"s"===n.hem?"South":"North",current:{}};var i,o;if(r.hasDst){let e=(o=n.dst)?o.split("->"):[];r.change={start:e[0],back:e[1]}}let s=n.offset,u=s;return!0===r.hasDst&&(u="North"===r.hemisphere?s-1:n.offset+1),!1===r.hasDst?(r.current.offset=s,r.current.isDST=!1):!0===d(e.epoch,r.change.start,r.change.back,s,u)?(r.current.offset=s,r.current.isDST="North"===r.hemisphere):(r.current.offset=u,r.current.isDST="South"===r.hemisphere),r};const Fe=["century","decade","year","month","date","day","hour","minute","second","millisecond"],Je={set:function(e,t){let a=this.clone();return a=he(a,e),t&&(this.tz=O(t)),a},timezone:function(){return We(this)},isDST:function(){return We(this).current.isDST},hasDST:function(){return We(this).hasDst},offset:function(){return 60*We(this).current.offset},hemisphere:function(){return We(this).hemisphere},format:function(e){return ve(this,e)},unixFmt:function(e){return Me(this,e)},startOf:function(e){return((e,t)=>{let a=e.clone();return t=V(t),Le[t]?Le[t](a):"summer"===t||"winter"===t?(a=a.season(t),Le.season(a)):a})(this,e)},endOf:function(e){return((e,t)=>{let a=e.clone();return t=V(t),Le[t]?(a=Le[t](a),a=a.add(1,t),a=a.subtract(1,"millisecond"),a):a})(this,e)},leapYear:function(){return M(this.year())},progress:function(e){return $e(this,e)},nearest:function(e){return Te(this,e)},diff:function(e,t){return Se(this,e,t)},since:function(e){return e||(e=this.clone().set()),Ye(this,e)},next:function(e){return this.add(1,e).startOf(e)},last:function(e){return this.subtract(1,e).startOf(e)},isValid:function(){return!(!this.epoch&&0!==this.epoch||isNaN(this.d.getTime()))},goto:function(e){let t=this.clone();return t.tz=O(e,t.timezones),t},every:function(e,t){if("object"==typeof e&&"string"==typeof t){let a=t;t=e,e=a}return Ie(this,e,t)},isAwake:function(){let e=this.hour();return!(e<8||e>22)},isAsleep:function(){return!this.isAwake()},daysInMonth:function(){switch(this.month()){case 0:case 2:case 4:case 6:case 7:case 9:case 11:return 31;case 1:return this.leapYear()?29:28;case 3:case 5:case 8:case 10:return 30;default:throw new Error("Invalid Month state.")}},log:function(){return console.log(""),console.log(ve(this,"nice-short")),this},logYear:function(){return console.log(""),console.log(ve(this,"full-short")),this},json:function(){return Fe.reduce(((e,t)=>(e[t]=this[t](),e)),{})},debug:function(){let e=this.timezone(),t=this.format("MM")+" "+this.format("date-ordinal")+" "+this.year();return t+="\n - "+this.format("time"),console.log("\n\n",t+"\n - "+e.name+" ("+e.current.offset+")"),this},from:function(e){return(e=this.clone().set(e)).since(this)},fromNow:function(){return this.clone().set(Date.now()).since(this)},weekStart:function(e){if("number"==typeof e)return this._weekStart=e,this;if("string"==typeof e){e=e.toLowerCase().trim();let t=fe().indexOf(e);-1===t&&(t=ye().indexOf(e)),-1===t&&(t=1),this._weekStart=t}else console.warn("Spacetime Error: Cannot understand .weekStart() input:",e);return this}};Je.inDST=Je.isDST,Je.round=Je.nearest,Je.each=Je.every;var Ke=Je;const Qe=e=>("string"==typeof e&&(e=parseInt(e,10)),e),Re=["year","month","date","hour","minute","second","millisecond"],Ge=(e,t,a)=>{let n=Re.indexOf(a),r=Re.slice(n,Re.length);for(let a=0;a=24?t=24:t<0&&(t=0);let n=e.clone(),r=e.hour()-t,i=r*L.hour;return e.epoch-=i,e.date()!==n.date()&&(e=n.clone(),r>1&&(r-=1),r<1&&(r+=1),i=r*L.hour,e.epoch-=i),F(e,{hour:t}),Ge(e,n,"minute"),(e=Ue(e,n,a,"day")).epoch},et=function(e,t){return"string"==typeof t&&/^'[0-9]{2}$/.test(t)&&(t=t.replace(/'/,"").trim(),t=(t=Number(t))>30?1900+t:2e3+t),t=Qe(t),F(e,{year:t}),e.epoch};let tt="am",at="pm";const nt={millisecond:function(e){if(void 0!==e){let t=this.clone();return t.epoch=function(e,t){t=Qe(t);let a=e.millisecond()-t;return e.epoch-a}(t,e),t}return this.d.getMilliseconds()},second:function(e,t){if(void 0!==e){let a=this.clone();return a.epoch=function(e,t,a){t=Qe(t);let n=e.clone(),r=(e.second()-t)*L.second;return e.epoch=e.epoch-r,(e=Ue(e,n,a,"minute")).epoch}(a,e,t),a}return this.d.getSeconds()},minute:function(e,t){if(void 0!==e){let a=this.clone();return a.epoch=Ze(a,e,t),a}return this.d.getMinutes()},hour:function(e,t){let a=this.d;if(void 0!==e){let a=this.clone();return a.epoch=Xe(a,e,t),a}return a.getHours()},hourFloat:function(e,t){if(void 0!==e){let a=this.clone(),n=e%1;n*=60;let r=parseInt(e,10);return a.epoch=Xe(a,r,t),a.epoch=Ze(a,n,t),a}let a=this.d,n=a.getHours(),r=a.getMinutes();return r/=60,n+r},hour12:function(e,t){let a=this.d;if(void 0!==e){let a=this.clone(),n=(e=""+e).match(/^([0-9]+)(am|pm)$/);if(n){let e=parseInt(n[1],10);"pm"===n[2]&&(e+=12),a.epoch=Xe(a,e,t)}return a}let n=a.getHours();return n>12&&(n-=12),0===n&&(n=12),n},time:function(e,t){if(void 0!==e){let a=this.clone();return e=e.toLowerCase().trim(),a.epoch=function(e,t,a){let n=t.match(/([0-9]{1,2})[:h]([0-9]{1,2})(:[0-9]{1,2})? ?(am|pm)?/);if(!n){if(n=t.match(/([0-9]{1,2}) ?(am|pm)/),!n)return e.epoch;n.splice(2,0,"0"),n.splice(3,0,"")}let r=!1,i=parseInt(n[1],10),o=parseInt(n[2],10);o>=60&&(o=59),i>12&&(r=!0),!1===r&&("am"===n[4]&&12===i&&(i=0),"pm"===n[4]&&i<12&&(i+=12)),n[3]=n[3]||"",n[3]=n[3].replace(/:/,"");let s=parseInt(n[3],10)||0,u=e.clone();return e=(e=(e=(e=e.hour(i)).minute(o)).second(s)).millisecond(0),(e=Ue(e,u,a,"day")).epoch}(a,e,t),a}return`${this.h12()}:${$(this.minute())}${this.ampm()}`},ampm:function(e,t){let a=tt,n=this.hour();if(n>=12&&(a=at),"string"!=typeof e)return a;let r=this.clone();return e=e.toLowerCase().trim(),n>=12&&"am"===e?(n-=12,r.hour(n,t)):n<12&&"pm"===e?(n+=12,r.hour(n,t)):r},dayTime:function(e,t){if(void 0!==e){const a={morning:"7:00am",breakfast:"7:00am",noon:"12:00am",lunch:"12:00pm",afternoon:"2:00pm",evening:"6:00pm",dinner:"6:00pm",night:"11:00pm",midnight:"23:59pm"};let n=this.clone();return e=(e=e||"").toLowerCase(),!0===a.hasOwnProperty(e)&&(n=n.time(a[e],t)),n}let a=this.hour();return a<6?"night":a<12?"morning":a<17?"afternoon":a<22?"evening":"night"},iso:function(e){return void 0!==e?this.set(e):this.format("iso")}};var rt=nt;const it={date:function(e,t){if(void 0!==e){let a=this.clone();return(e=parseInt(e,10))&&(a.epoch=function(e,t,a){if((t=Qe(t))>28){let a=e.month(),n=J[a];1===a&&29===t&&M(e.year())&&(n=29),t>n&&(t=n)}t<=0&&(t=1);let n=e.clone();return F(e,{date:t}),(e=Ue(e,n,a,"month")).epoch}(a,e,t)),a}return this.d.getDate()},day:function(e,t){if(void 0===e)return this.d.getDay();let a=this.clone(),n=e;"string"==typeof e&&(e=e.toLowerCase(),pe.hasOwnProperty(e)?n=pe[e]:(n=fe().indexOf(e),-1===n&&(n=ye().indexOf(e))));let r=this.d.getDay()-n;!0===t&&r>0&&(r-=7),!1===t&&r<0&&(r+=7);let i=this.subtract(r,"days");return F(i,{hour:a.hour(),minute:a.minute(),second:a.second()}),i},dayName:function(e,t){if(void 0===e)return ye()[this.day()];let a=this.clone();return a=a.day(e,t),a}};var ot=it;const st=e=>e=(e=(e=e.minute(0)).second(0)).millisecond(1),ut={dayOfYear:function(e,t){if(void 0!==e){let a=this.clone();return a.epoch=function(e,t,a){t=Qe(t);let n=e.clone();return(t-=1)<=0?t=0:t>=365&&(t=364),e=(e=e.startOf("year")).add(t,"day"),Ge(e,n,"hour"),(e=Ue(e,n,a,"year")).epoch}(a,e,t),a}let a,n=0,r=this.d.getMonth();for(let e=1;e<=r;e++)a=new Date,a.setDate(1),a.setFullYear(this.d.getFullYear()),a.setHours(1),a.setMinutes(1),a.setMonth(e),a.setHours(-2),n+=a.getDate();return n+this.d.getDate()},week:function(e,t){if(void 0!==e){let a=this.clone();return a.epoch=function(e,t,a){let n=e.clone();return t=Qe(t),"december"===(e=(e=(e=e.month(0)).date(1)).day("monday")).monthName()&&e.date()>=28&&(e=e.add(1,"week")),t-=1,e=e.add(t,"weeks"),(e=Ue(e,n,a,"year")).epoch}(this,e,t),a=st(a),a}let a=this.clone();a=a.month(0),a=a.date(1),a=st(a),a=a.day("monday"),"december"===a.monthName()&&a.date()>=28&&(a=a.add(1,"week"));let n=1;1===a.date()&&(n=0),a=a.minus(1,"second");const r=this.epoch;if(a.epoch>r)return 1;let i=0,o=4*this.month();for(a.epoch+=L.week*o,i+=o;i<=52;i++){if(a.epoch>r)return i+n;a=a.add(1,"week")}return 52},month:function(e,t){if(void 0!==e){let a=this.clone();return a.epoch=function(e,t,a){"string"==typeof t&&("sept"===t&&(t="sep"),t=G()[t.toLowerCase()]),(t=Qe(t))>=12&&(t=11),t<=0&&(t=0);let n=e.date();n>J[t]&&(n=J[t]);let r=e.clone();return F(e,{month:t,d:n}),(e=Ue(e,r,a,"year")).epoch}(a,e,t),a}return this.d.getMonth()},monthName:function(e,t){if(void 0!==e){let a=this.clone();return a=a.month(e,t),a}return Q[this.month()]},quarter:function(e,t){if(void 0!==e&&("string"==typeof e&&(e=e.replace(/^q/i,""),e=parseInt(e,10)),Be[e])){let a=this.clone(),n=Be[e][0];return a=a.month(n,t),a=a.date(1,t),a=a.startOf("day"),a}let a=this.d.getMonth();for(let e=1;e=He[a][e][1]&&n0&&(t.epoch=et(t,-1*a)),"ad"===e&&a<0&&(t.epoch=et(t,-1*a)),t}return this.d.getFullYear()<0?"BC":"AD"},decade:function(e){if(void 0!==e){if(!(e=(e=(e=String(e)).replace(/([0-9])'?s$/,"$1")).replace(/([0-9])(th|rd|st|nd)/,"$1")))return console.warn("Spacetime: Invalid decade input"),this;2===e.length&&/[0-9][0-9]/.test(e)&&(e="19"+e);let t=Number(e);return isNaN(t)?this:(t=10*Math.floor(t/10),this.year(t))}return this.startOf("decade").year()},century:function(e){if(void 0!==e){"string"==typeof e&&(e=(e=(e=e.replace(/([0-9])(th|rd|st|nd)/,"$1")).replace(/([0-9]+) ?(b\.?c\.?|a\.?d\.?)/i,((e,t,a)=>(a.match(/b\.?c\.?/i)&&(t="-"+t),t)))).replace(/c$/,""));let t=Number(e);return isNaN(e)?(console.warn("Spacetime: Invalid century input"),this):(0===t&&(t=1),t=t>=0?100*(t-1):100*(t+1),this.year(t))}let t=this.startOf("century").year();return t=Math.floor(t/100),t<0?t-1:t+1},millenium:function(e){if(void 0!==e){if("string"==typeof e&&(e=e.replace(/([0-9])(th|rd|st|nd)/,"$1"),e=Number(e),isNaN(e)))return console.warn("Spacetime: Invalid millenium input"),this;e>0&&(e-=1);let t=1e3*e;return 0===t&&(t=1),this.year(t)}let t=Math.floor(this.year()/1e3);return t>=0&&(t+=1),t}};var dt=ut;const lt=Object.assign({},rt,ot,dt);lt.milliseconds=lt.millisecond,lt.seconds=lt.second,lt.minutes=lt.minute,lt.hours=lt.hour,lt.hour24=lt.hour,lt.h12=lt.hour12,lt.h24=lt.hour24,lt.days=lt.day;var ht=e=>{Object.keys(lt).forEach((t=>{e.prototype[t]=lt[t]}))};const mt=function(e,t){return 1===e&&M(t)?29:J[e]},ct=(e,t)=>{if(e.month>0){let a=parseInt(e.month/12,10);e.year=t.year()+a,e.month=e.month%12}else if(e.month<0){let a=Math.abs(e.month),n=parseInt(a/12,10);a%12!=0&&(n+=1),e.year=t.year()-n,e.month=e.month%12,e.month=e.month+12,12===e.month&&(e.month=0)}return e},ft=(e,t,a)=>{let n=t.year(),r=t.month(),i=mt(r,n);for(;a>i;)a-=i,r+=1,r>=12&&(r-=12,n+=1),i=mt(r,n);return e.month=r,e.date=a,e},yt=(e,t,a)=>{e.year=t.year(),e.month=t.month();let n=t.date();for(e.date=n-Math.abs(a);e.date<1;){e.month-=1,e.month<0&&(e.month=11,e.year-=1);let t=mt(e.month,e.year);e.date+=t}return e},pt=["millisecond","second","minute","hour","date","month"];let gt={second:pt.slice(0,1),minute:pt.slice(0,2),quarterhour:pt.slice(0,2),hour:pt.slice(0,3),date:pt.slice(0,4),month:pt.slice(0,4),quarter:pt.slice(0,4),season:pt.slice(0,4),year:pt,decade:pt,century:pt};gt.week=gt.hour,gt.season=gt.date,gt.quarter=gt.date;const wt={year:!0,quarter:!0,season:!0,month:!0,week:!0,date:!0},bt={month:!0,quarter:!0,season:!0,year:!0};var kt=e=>{e.prototype.add=function(e,t){let a=this.clone();if(!t||0===e)return a;let n=this.clone();if("millisecond"===(t=V(t)))return a.epoch+=e,a;"fortnight"===t&&(e*=2,t="week"),L[t]?a.epoch+=L[t]*e:"week"===t||"weekend"===t?a.epoch+=L.day*(7*e):"quarter"===t||"season"===t?a.epoch+=L.month*(3*e):"quarterhour"===t&&(a.epoch+=15*L.minute*e);let r={};if(gt[t]&>[t].forEach((e=>{r[e]=n[e]()})),wt[t]){const e=n.timezone().current.offset-a.timezone().current.offset;a.epoch+=3600*e*1e3}if("month"===t&&(r.month=n.month()+e,r=ct(r,n)),"week"===t){let t=n.date()+7*e;t<=28&&t>1&&(r.date=t)}if("weekend"===t&&"saturday"!==a.dayName())a=a.day("saturday",!0);else if("date"===t){if(e<0)r=yt(r,n,e);else{let t=n.date()+e;r=ft(r,n,t)}0!==e&&n.isSame(a,"day")&&(r.date=n.date()+e)}else if("quarter"===t){if(r.month=n.month()+3*e,r.year=n.year(),r.month<0){let e=Math.floor(r.month/12),t=r.month+12*Math.abs(e);r.month=t,r.year+=e}else if(r.month>=12){let e=Math.floor(r.month/12);r.month=r.month%12,r.year+=e}r.date=n.date()}else if("year"===t){let t=n.year()+e,r=a.year();if(rt){let t=Math.floor(e/4)||1;a.epoch+=L.day*t}}else"decade"===t?r.year=a.year()+10:"century"===t&&(r.year=a.year()+100);if(bt[t]){let e=J[r.month];r.date=n.date(),r.date>e&&(r.date=e)}return Object.keys(r).length>1&&F(a,r),a},e.prototype.subtract=function(e,t){return this.clone().add(-1*e,t)},e.prototype.minus=e.prototype.subtract,e.prototype.plus=e.prototype.add};const Dt={millisecond:e=>e.epoch,second:e=>[e.year(),e.month(),e.date(),e.hour(),e.minute(),e.second()].join("-"),minute:e=>[e.year(),e.month(),e.date(),e.hour(),e.minute()].join("-"),hour:e=>[e.year(),e.month(),e.date(),e.hour()].join("-"),day:e=>[e.year(),e.month(),e.date()].join("-"),week:e=>[e.year(),e.week()].join("-"),month:e=>[e.year(),e.month()].join("-"),quarter:e=>[e.year(),e.quarter()].join("-"),year:e=>e.year()};Dt.date=Dt.day;var vt=e=>{e.prototype.isSame=function(t,a,n=!0){let r=this;if(!a)return null;if("string"==typeof t&&"object"==typeof a){let e=t;t=a,a=e}return"string"!=typeof t&&"number"!=typeof t||(t=new e(t,this.timezone.name)),a=a.replace(/s$/,""),!0===n&&r.tz!==t.tz&&((t=t.clone()).tz=r.tz),Dt[a]?Dt[a](r)===Dt[a](t):null}},xt=e=>{const t={isAfter:function(e){let t=q(e=S(e,this));return null===t?null:this.epoch>t},isBefore:function(e){let t=q(e=S(e,this));return null===t?null:this.epoch{e.prototype[a]=t[a]}))},Ot=e=>{const t={i18n:e=>{var t,a,n;j(e.days)&&(t=e.days,me=t.short||me,ce=t.long||ce),j(e.months)&&function(e){K=e.short||K,Q=e.long||Q}(e.months),n=e.useTitleCase,"[object Boolean]"===Object.prototype.toString.call(n)&&(a=e.useTitleCase,ge=a),j(e.ampm)&&function(e){tt=e.am||tt,at=e.pm||at}(e.ampm)}};Object.keys(t).forEach((a=>{e.prototype[a]=t[a]}))};let Mt=f;const _t=function(e,t,a={}){this.epoch=null,this.tz=O(t,Mt),this.silent=void 0===a.silent||a.silent,this.british=a.dmy||a.british,this._weekStart=1,void 0!==a.weekStart&&(this._weekStart=a.weekStart),this._today={},void 0!==a.today&&(this._today=a.today),Object.defineProperty(this,"d",{get:function(){let e=l(this),t=(new Date(this.epoch).getTimezoneOffset()||0)+60*e;t=60*t*1e3;let a=this.epoch+t;return new Date(a)}}),Object.defineProperty(this,"timezones",{get:()=>Mt,set:e=>(Mt=e,e)});let n=he(this,e);this.epoch=n.epoch};Object.keys(Ke).forEach((e=>{_t.prototype[e]=Ke[e]})),_t.prototype.clone=function(){return new _t(this.epoch,this.tz,{silent:this.silent,weekStart:this._weekStart,today:this._today,parsers:this.parsers})},_t.prototype.toLocalDate=function(){return this.toNativeDate()},_t.prototype.toNativeDate=function(){return new Date(this.epoch)},ht(_t),kt(_t),vt(_t),xt(_t),Ot(_t);var jt=_t,$t=(e,t)=>{let a=new jt(null),n=new jt(null);a=a.time(e),n=t?n.time(t):a.add(59,"minutes");let r=a.hour(),i=n.hour();return Object.keys(a.timezones).filter((e=>{if(-1===e.indexOf("/"))return!1;let t=new jt(null,e),o=t.hour();return o>=r&&o<=i&&!(o===r&&t.minute()n.minute())}))};const Tt=(e,t,a)=>new jt(e,t,a),zt=function(e){let t=e._today||{};return Object.keys(t).forEach((a=>{e=e[a](t[a])})),e};Tt.now=(e,t)=>{let a=new jt((new Date).getTime(),e,t);return a=zt(a),a},Tt.today=(e,t)=>{let a=new jt((new Date).getTime(),e,t);return a=zt(a),a.startOf("day")},Tt.tomorrow=(e,t)=>{let a=new jt((new Date).getTime(),e,t);return a=zt(a),a.add(1,"day").startOf("day")},Tt.yesterday=(e,t)=>{let a=new jt((new Date).getTime(),e,t);return a=zt(a),a.subtract(1,"day").startOf("day")},Tt.extend=function(e={}){return Object.keys(e).forEach((t=>{jt.prototype[t]=e[t]})),this},Tt.timezones=function(){return(new jt).timezones},Tt.max=function(e,t){let a=new jt(null,e,t);return a.epoch=864e13,a},Tt.min=function(e,t){let a=new jt(null,e,t);return a.epoch=-864e13,a},Tt.whereIts=$t,Tt.version="7.1.4",Tt.plugin=Tt.extend;var Vt=Tt;const qt={daybreak:"7:00am",breakfast:"8:00am",morning:"9:00am",noon:"12:00pm",midday:"12:00pm",afternoon:"2:00pm",lunchtime:"12:00pm",evening:"6:00pm",dinnertime:"6:00pm",night:"8:00pm",eod:"10:00pm",midnight:"12:00am",am:"9:00am",pm:"5:00pm","early day":"8:00am","late at night":"11:00pm"},St={quarter:15,half:30},Ct=function(e){let t=e.time("6:00am");return e.isBefore(t)?e.ampm("pm"):e};var At=function(e,t){let a=e.match("(at|by|for|before|this|after)? #Time+");a=a.not("^(at|by|for|before|this|after)"),a=a.not("sharp"),a=a.not("on the dot");let n=Vt.now(t.timezone),r=n.clone(),i=a.not("in the").text("reduced");if(i=i.replace(/^@/,""),qt.hasOwnProperty(i))return{result:qt[i],m:a};let o=a.match("^#Cardinal oclock (am|pm)?");if(o.found&&(n=n.hour(o.text("reduced")),n=n.startOf("hour"),n.isValid()&&!n.isEqual(r))){let e=o.match("(am|pm)");return n=e.found?n.ampm(e.text("reduced")):Ct(n),{result:n.time(),m:o}}if(o=a.match("(half|quarter|25|20|15|10|5) (past|after|to) #Cardinal"),o.found&&(n=function(e,t){let a=e.match("#Cardinal$"),n=e.not(a).match("(half|quarter|25|20|15|10|5)");a=a.text("reduced");let r=n.text("reduced");St.hasOwnProperty(r)&&(r=St[r]);let i=e.has("to");return t=(t=t.hour(a)).startOf("hour"),a<6&&(t=t.ampm("pm")),i?t.subtract(r,"minutes"):t.add(r,"minutes")}(o,n),n.isValid()&&!n.isEqual(r)))return n=Ct(n),{result:n.time(),m:o};if(o=a.match("[(half|quarter|25|20|15|10|5)] (past|after)"),o.found){let e=o.groups("min").text("reduced"),a=Vt(t.today);if(St.hasOwnProperty(e)&&(e=St[e]),a=a.next("hour").startOf("hour").minute(e),a.isValid()&&!a.isEqual(r))return{result:a.time(),m:o}}if(o=a.match("[(half|quarter|25|20|15|10|5)] to"),o.found){let e=o.groups("min").text("reduced"),a=Vt(t.today);if(St.hasOwnProperty(e)&&(e=St[e]),a=a.next("hour").startOf("hour").minus(e,"minutes"),a.isValid()&&!a.isEqual(r))return{result:a.time(),m:o}}if(o=a.match("[