diff --git a/.DS_Store b/.DS_Store
index 62b60b80..a67cca33 100644
Binary files a/.DS_Store and b/.DS_Store differ
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 00000000..d495dc20
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,17 @@
+{
+ "trailingComma": "es5",
+ "tabWidth": 2,
+ "semi": false,
+ "singleQuote": true,
+ "jsxSingleQuote": true,
+ "overrides": [
+ {
+ "files": [
+ "**/*.mjs"
+ ],
+ "options": {
+ "singleQuote": true
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..a5aabc7b
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "editor.tabSize": 2,
+ "editor.defaultFormatter": "esbenp.prettier-vscode",
+ "editor.formatOnSave": true
+}
diff --git a/package-lock.json b/package-lock.json
index ebbffd34..ff970314 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -47,7 +47,8 @@
"semver": "^7.6.0",
"uuid": "^8.1.0",
"w3c-xmlserializer": "^2.0.0",
- "xml-formatter": "^2.5.0"
+ "xml-formatter": "^2.5.0",
+ "xpath": "^0.0.34"
},
"devDependencies": {
"@babel/core": "^7.24.6",
@@ -478,9 +479,9 @@
}
},
"node_modules/@babel/helper-validator-identifier": {
- "version": "7.24.6",
- "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.24.6.tgz",
- "integrity": "sha512-4yA7s865JHaqUdRbnaxarZREuPTHrjpDT+pXoAZ1yhyo6uFnIEpS8VMu16siFOHDpZNKYv5BObhsB//ycbICyw==",
+ "version": "7.25.9",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz",
+ "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==",
"engines": {
"node": ">=6.9.0"
}
@@ -4088,82 +4089,30 @@
"integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww=="
},
"node_modules/@eslint-community/eslint-utils": {
- "version": "4.4.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
- "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz",
+ "integrity": "sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA==",
"dependencies": {
- "eslint-visitor-keys": "^3.3.0"
+ "eslint-visitor-keys": "^3.4.3"
},
"engines": {
"node": "^12.22.0 || ^14.17.0 || >=16.0.0"
},
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
"peerDependencies": {
"eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
"node_modules/@eslint-community/regexpp": {
- "version": "4.10.0",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
- "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+ "version": "4.12.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz",
+ "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==",
"engines": {
"node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
- "node_modules/@eslint/eslintrc": {
- "version": "2.1.4",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
- "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
- "dependencies": {
- "ajv": "^6.12.4",
- "debug": "^4.3.2",
- "espree": "^9.6.0",
- "globals": "^13.19.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.1.0",
- "minimatch": "^3.1.2",
- "strip-json-comments": "^3.1.1"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
- "dependencies": {
- "type-fest": "^0.20.2"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@eslint/js": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
- "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- }
- },
"node_modules/@gar/promisify": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz",
@@ -4171,11 +4120,12 @@
"dev": true
},
"node_modules/@humanwhocodes/config-array": {
- "version": "0.11.14",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
- "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+ "version": "0.13.0",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz",
+ "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==",
+ "deprecated": "Use @eslint/config-array instead",
"dependencies": {
- "@humanwhocodes/object-schema": "^2.0.2",
+ "@humanwhocodes/object-schema": "^2.0.3",
"debug": "^4.3.1",
"minimatch": "^3.0.5"
},
@@ -4198,7 +4148,8 @@
"node_modules/@humanwhocodes/object-schema": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz",
- "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA=="
+ "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==",
+ "deprecated": "Use @eslint/object-schema instead"
},
"node_modules/@isaacs/cliui": {
"version": "8.0.2",
@@ -5723,20 +5674,6 @@
"url": "https://github.com/sponsors/rsimon"
}
},
- "node_modules/@recogito/recogito-client-core/node_modules/react": {
- "version": "16.14.0",
- "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
- "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/@recogito/recogito-client-core/node_modules/react-autosize-textarea": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/react-autosize-textarea/-/react-autosize-textarea-7.1.0.tgz",
@@ -5751,36 +5688,11 @@
"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0"
}
},
- "node_modules/@recogito/recogito-client-core/node_modules/react-dom": {
- "version": "16.14.0",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
- "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==",
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "scheduler": "^0.19.1"
- },
- "peerDependencies": {
- "react": "^16.14.0"
- }
- },
"node_modules/@recogito/recogito-client-core/node_modules/regenerator-runtime": {
"version": "0.13.11",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz",
"integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg=="
},
- "node_modules/@recogito/recogito-client-core/node_modules/scheduler": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
- "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1"
- }
- },
"node_modules/@redux-saga/core": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@redux-saga/core/-/core-1.3.0.tgz",
@@ -6478,16 +6390,6 @@
"csstype": "^3.0.2"
}
},
- "node_modules/@types/react-dom": {
- "version": "17.0.25",
- "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-17.0.25.tgz",
- "integrity": "sha512-urx7A7UxkZQmThYA4So0NelOVjx3V4rNFVJwp0WZlbIK5eM4rNJDiN3R/E9ix0MBh6kAEojk/9YL+Te6D9zHNA==",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@types/react": "^17"
- }
- },
"node_modules/@types/react-redux": {
"version": "7.1.33",
"resolved": "https://registry.npmjs.org/@types/react-redux/-/react-redux-7.1.33.tgz",
@@ -6900,9 +6802,9 @@
}
},
"node_modules/@ungap/structured-clone": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
- "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ=="
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.1.tgz",
+ "integrity": "sha512-fEzPV3hSkSMltkw152tJKNARhOupqbH96MZWyRjNaYZOMIzbrTeQDG+MTc6Mr2pgzFQzFxAfmhGDNP5QK++2ZA=="
},
"node_modules/@vercel/webpack-asset-relocator-loader": {
"version": "1.7.3",
@@ -7324,20 +7226,6 @@
"resolved": "https://registry.npmjs.org/openseadragon/-/openseadragon-2.4.2.tgz",
"integrity": "sha512-398KbZwRtOYA6OmeWRY4Q0737NTacQ9Q6whmr9Lp1MNQO3p0eBz5LIASRne+4gwequcSM1vcHcjfy3dIndQziw=="
},
- "node_modules/annotorious-openseadragon/node_modules/react": {
- "version": "16.14.0",
- "resolved": "https://registry.npmjs.org/react/-/react-16.14.0.tgz",
- "integrity": "sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g==",
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/annotorious-openseadragon/node_modules/react-autosize-textarea": {
"version": "7.1.0",
"resolved": "https://registry.npmjs.org/react-autosize-textarea/-/react-autosize-textarea-7.1.0.tgz",
@@ -7352,21 +7240,6 @@
"react-dom": "^0.14.0 || ^15.0.0 || ^16.0.0"
}
},
- "node_modules/annotorious-openseadragon/node_modules/react-dom": {
- "version": "16.14.0",
- "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.14.0.tgz",
- "integrity": "sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw==",
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1",
- "prop-types": "^15.6.2",
- "scheduler": "^0.19.1"
- },
- "peerDependencies": {
- "react": "^16.14.0"
- }
- },
"node_modules/annotorious-openseadragon/node_modules/react-select": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/react-select/-/react-select-3.2.0.tgz",
@@ -7386,16 +7259,6 @@
"react-dom": "^16.8.0 || ^17.0.0"
}
},
- "node_modules/annotorious-openseadragon/node_modules/scheduler": {
- "version": "0.19.1",
- "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz",
- "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==",
- "peer": true,
- "dependencies": {
- "loose-envify": "^1.1.0",
- "object-assign": "^4.1.1"
- }
- },
"node_modules/ansi-escapes": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-5.0.0.tgz",
@@ -9463,11 +9326,11 @@
}
},
"node_modules/core-js-compat": {
- "version": "3.37.1",
- "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.37.1.tgz",
- "integrity": "sha512-9TNiImhKvQqSUkOvk/mMRZzOANTiEVC7WaBNhHcKM7x+/5E1l5NvsysR19zuDQScE8k+kfQXWRN3AtS/eOSHpg==",
+ "version": "3.39.0",
+ "resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.39.0.tgz",
+ "integrity": "sha512-VgEUx3VwlExr5no0tXlBt+silBvhTryPwCXRI2Id1PN8WTKu7MreethvddqOubrYxkFdv/RnYrqlv1sFNAUelw==",
"dependencies": {
- "browserslist": "^4.23.0"
+ "browserslist": "^4.24.2"
},
"funding": {
"type": "opencollective",
@@ -9516,9 +9379,9 @@
"dev": true
},
"node_modules/cross-spawn": {
- "version": "7.0.3",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
- "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
"dependencies": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
@@ -10119,11 +9982,11 @@
"integrity": "sha512-XRRe6Glud4rd/ZGQfiV1ruXSfbvfJedlV9Y6zOlP+2K04vBYiJEte6stfFkCP03aMnY5tsipamumUjL14fofug=="
},
"node_modules/debug": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
- "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz",
+ "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==",
"dependencies": {
- "ms": "2.1.2"
+ "ms": "^2.1.3"
},
"engines": {
"node": ">=6.0"
@@ -11593,60 +11456,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/eslint": {
- "version": "8.57.0",
- "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
- "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.2.0",
- "@eslint-community/regexpp": "^4.6.1",
- "@eslint/eslintrc": "^2.1.4",
- "@eslint/js": "8.57.0",
- "@humanwhocodes/config-array": "^0.11.14",
- "@humanwhocodes/module-importer": "^1.0.1",
- "@nodelib/fs.walk": "^1.2.8",
- "@ungap/structured-clone": "^1.2.0",
- "ajv": "^6.12.4",
- "chalk": "^4.0.0",
- "cross-spawn": "^7.0.2",
- "debug": "^4.3.2",
- "doctrine": "^3.0.0",
- "escape-string-regexp": "^4.0.0",
- "eslint-scope": "^7.2.2",
- "eslint-visitor-keys": "^3.4.3",
- "espree": "^9.6.1",
- "esquery": "^1.4.2",
- "esutils": "^2.0.2",
- "fast-deep-equal": "^3.1.3",
- "file-entry-cache": "^6.0.1",
- "find-up": "^5.0.0",
- "glob-parent": "^6.0.2",
- "globals": "^13.19.0",
- "graphemer": "^1.4.0",
- "ignore": "^5.2.0",
- "imurmurhash": "^0.1.4",
- "is-glob": "^4.0.0",
- "is-path-inside": "^3.0.3",
- "js-yaml": "^4.1.0",
- "json-stable-stringify-without-jsonify": "^1.0.1",
- "levn": "^0.4.1",
- "lodash.merge": "^4.6.2",
- "minimatch": "^3.1.2",
- "natural-compare": "^1.4.0",
- "optionator": "^0.9.3",
- "strip-ansi": "^6.0.1",
- "text-table": "^0.2.0"
- },
- "bin": {
- "eslint": "bin/eslint.js"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
"node_modules/eslint-config-react-app": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/eslint-config-react-app/-/eslint-config-react-app-7.0.1.tgz",
@@ -11934,21 +11743,6 @@
"eslint": "^7.5.0 || ^8.0.0"
}
},
- "node_modules/eslint-scope": {
- "version": "7.2.2",
- "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
- "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
- "dependencies": {
- "esrecurse": "^4.3.0",
- "estraverse": "^5.2.0"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
"node_modules/eslint-visitor-keys": {
"version": "3.4.3",
"resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
@@ -12059,47 +11853,6 @@
"url": "https://github.com/chalk/supports-color?sponsor=1"
}
},
- "node_modules/eslint/node_modules/globals": {
- "version": "13.24.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
- "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
- "dependencies": {
- "type-fest": "^0.20.2"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/eslint/node_modules/type-fest": {
- "version": "0.20.2",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
- "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/espree": {
- "version": "9.6.1",
- "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
- "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
- "dependencies": {
- "acorn": "^8.9.0",
- "acorn-jsx": "^5.3.2",
- "eslint-visitor-keys": "^3.4.1"
- },
- "engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
"node_modules/esprima": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
@@ -12113,9 +11866,9 @@
}
},
"node_modules/esquery": {
- "version": "1.5.0",
- "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
- "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz",
+ "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==",
"dependencies": {
"estraverse": "^5.1.0"
},
@@ -12493,17 +12246,6 @@
"pend": "~1.2.0"
}
},
- "node_modules/file-entry-cache": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
- "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
- "dependencies": {
- "flat-cache": "^3.0.4"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
- }
- },
"node_modules/file-loader": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz",
@@ -12783,23 +12525,10 @@
"flat": "cli.js"
}
},
- "node_modules/flat-cache": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
- "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
- "dependencies": {
- "flatted": "^3.2.9",
- "keyv": "^4.5.3",
- "rimraf": "^3.0.2"
- },
- "engines": {
- "node": "^10.12.0 || >=12.0.0"
- }
- },
"node_modules/flatted": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
- "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw=="
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.2.tgz",
+ "integrity": "sha512-AiwGJM8YcNOaobumgtng+6NHuOqC3A7MixFeDafM3X9cIUM+xUXoS5Vfgf+OihAYe20fxqNM9yPBXJzRtZ/4eA=="
},
"node_modules/flexsearch": {
"version": "0.7.43",
@@ -14058,9 +13787,9 @@
]
},
"node_modules/ignore": {
- "version": "5.3.1",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
- "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
+ "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==",
"engines": {
"node": ">= 4"
}
@@ -17834,9 +17563,9 @@
}
},
"node_modules/ms": {
- "version": "2.1.2",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
- "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
},
"node_modules/multicast-dns": {
"version": "7.2.5",
@@ -21005,15 +20734,6 @@
}
}
},
- "node_modules/react-refresh": {
- "version": "0.14.2",
- "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz",
- "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==",
- "peer": true,
- "engines": {
- "node": ">=0.10.0"
- }
- },
"node_modules/react-router": {
"version": "6.23.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.23.1.tgz",
@@ -21116,6 +20836,36 @@
}
}
},
+ "node_modules/react-scripts/node_modules/@eslint/eslintrc": {
+ "version": "2.1.4",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+ "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+ "dependencies": {
+ "ajv": "^6.12.4",
+ "debug": "^4.3.2",
+ "espree": "^9.6.0",
+ "globals": "^13.19.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.1.0",
+ "minimatch": "^3.1.2",
+ "strip-json-comments": "^3.1.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/react-scripts/node_modules/@eslint/js": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz",
+ "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==",
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ }
+ },
"node_modules/react-scripts/node_modules/babel-loader": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.3.0.tgz",
@@ -21142,6 +20892,103 @@
"node": ">=10"
}
},
+ "node_modules/react-scripts/node_modules/eslint": {
+ "version": "8.57.1",
+ "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz",
+ "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
+ "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.2.0",
+ "@eslint-community/regexpp": "^4.6.1",
+ "@eslint/eslintrc": "^2.1.4",
+ "@eslint/js": "8.57.1",
+ "@humanwhocodes/config-array": "^0.13.0",
+ "@humanwhocodes/module-importer": "^1.0.1",
+ "@nodelib/fs.walk": "^1.2.8",
+ "@ungap/structured-clone": "^1.2.0",
+ "ajv": "^6.12.4",
+ "chalk": "^4.0.0",
+ "cross-spawn": "^7.0.2",
+ "debug": "^4.3.2",
+ "doctrine": "^3.0.0",
+ "escape-string-regexp": "^4.0.0",
+ "eslint-scope": "^7.2.2",
+ "eslint-visitor-keys": "^3.4.3",
+ "espree": "^9.6.1",
+ "esquery": "^1.4.2",
+ "esutils": "^2.0.2",
+ "fast-deep-equal": "^3.1.3",
+ "file-entry-cache": "^6.0.1",
+ "find-up": "^5.0.0",
+ "glob-parent": "^6.0.2",
+ "globals": "^13.19.0",
+ "graphemer": "^1.4.0",
+ "ignore": "^5.2.0",
+ "imurmurhash": "^0.1.4",
+ "is-glob": "^4.0.0",
+ "is-path-inside": "^3.0.3",
+ "js-yaml": "^4.1.0",
+ "json-stable-stringify-without-jsonify": "^1.0.1",
+ "levn": "^0.4.1",
+ "lodash.merge": "^4.6.2",
+ "minimatch": "^3.1.2",
+ "natural-compare": "^1.4.0",
+ "optionator": "^0.9.3",
+ "strip-ansi": "^6.0.1",
+ "text-table": "^0.2.0"
+ },
+ "bin": {
+ "eslint": "bin/eslint.js"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/react-scripts/node_modules/eslint-scope": {
+ "version": "7.2.2",
+ "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+ "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+ "dependencies": {
+ "esrecurse": "^4.3.0",
+ "estraverse": "^5.2.0"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/react-scripts/node_modules/espree": {
+ "version": "9.6.1",
+ "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+ "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+ "dependencies": {
+ "acorn": "^8.9.0",
+ "acorn-jsx": "^5.3.2",
+ "eslint-visitor-keys": "^3.4.1"
+ },
+ "engines": {
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ }
+ },
+ "node_modules/react-scripts/node_modules/file-entry-cache": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+ "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+ "dependencies": {
+ "flat-cache": "^3.0.4"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
"node_modules/react-scripts/node_modules/find-cache-dir": {
"version": "3.3.2",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-3.3.2.tgz",
@@ -21158,6 +21005,33 @@
"url": "https://github.com/avajs/find-cache-dir?sponsor=1"
}
},
+ "node_modules/react-scripts/node_modules/flat-cache": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+ "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+ "dependencies": {
+ "flatted": "^3.2.9",
+ "keyv": "^4.5.3",
+ "rimraf": "^3.0.2"
+ },
+ "engines": {
+ "node": "^10.12.0 || >=12.0.0"
+ }
+ },
+ "node_modules/react-scripts/node_modules/globals": {
+ "version": "13.24.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+ "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+ "dependencies": {
+ "type-fest": "^0.20.2"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/react-scripts/node_modules/react-refresh": {
"version": "0.11.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
@@ -21183,6 +21057,17 @@
"url": "https://opencollective.com/webpack"
}
},
+ "node_modules/react-scripts/node_modules/type-fest": {
+ "version": "0.20.2",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+ "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
"node_modules/react-scroll": {
"version": "1.8.3",
"resolved": "https://registry.npmjs.org/react-scroll/-/react-scroll-1.8.3.tgz",
@@ -22202,9 +22087,9 @@
}
},
"node_modules/semver": {
- "version": "7.6.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.2.tgz",
- "integrity": "sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w==",
+ "version": "7.6.3",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz",
+ "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==",
"bin": {
"semver": "bin/semver.js"
},
@@ -22263,11 +22148,6 @@
"node": ">= 0.8"
}
},
- "node_modules/send/node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
- },
"node_modules/serialize-error": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
@@ -24124,9 +24004,9 @@
}
},
"node_modules/tslib": {
- "version": "2.6.2",
- "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
- "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q=="
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="
},
"node_modules/tsutils": {
"version": "3.21.0",
@@ -24170,7 +24050,7 @@
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz",
"integrity": "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==",
- "devOptional": true,
+ "dev": true,
"engines": {
"node": ">=10"
},
@@ -24273,19 +24153,6 @@
"is-typedarray": "^1.0.0"
}
},
- "node_modules/typescript": {
- "version": "4.9.5",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
- "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
- "peer": true,
- "bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
- },
- "engines": {
- "node": ">=4.2.0"
- }
- },
"node_modules/typescript-compare": {
"version": "0.0.2",
"resolved": "https://registry.npmjs.org/typescript-compare/-/typescript-compare-0.0.2.tgz",
@@ -25932,6 +25799,14 @@
"resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz",
"integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw=="
},
+ "node_modules/xpath": {
+ "version": "0.0.34",
+ "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.34.tgz",
+ "integrity": "sha512-FxF6+rkr1rNSQrhUNYrAFJpRXNzlDoMxeXN5qI84939ylEv3qqPFKa85Oxr6tDaJKqwW6KKyo2v26TSv3k6LeA==",
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
"node_modules/xtend": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
diff --git a/package.json b/package.json
index 857b0757..2bac4d11 100644
--- a/package.json
+++ b/package.json
@@ -83,9 +83,10 @@
"semver": "^7.6.0",
"uuid": "^8.1.0",
"w3c-xmlserializer": "^2.0.0",
- "xml-formatter": "^2.5.0"
+ "xml-formatter": "^2.5.0",
+ "xpath": "^0.0.34"
},
"eslintConfig": {
"extends": "react-app"
}
-}
+}
\ No newline at end of file
diff --git a/src/data/test-milestone.js b/src/data/test-milestone.js
new file mode 100644
index 00000000..b1ecf1c6
--- /dev/null
+++ b/src/data/test-milestone.js
@@ -0,0 +1,15 @@
+export const data = [
+ {
+ id: "83cd79d8-73fb-4217-b28a-f2b43f863b31",
+ path: "//text[@xml:id='text-1']/body[1]/div[1]/div[1]/lg[2]/l[1]::0 //text[@xml:id='text-1']/body[1]/div[1]/div[1]/lg[3]/l[4]::42", // My English Letter
+ author: "Admin",
+ annotation: "Person"
+ },
+ {
+ id: "uid-4b2ab81c-04c1-44a3-bc21-fd4d8e071545",
+ path: "//text[@xml:id='text-1']/body[1]/div[1]/p[2]::3 //text[@xml:id='text-1']/body[1]/div[1]/p[2]::21", // My English Letter
+ author: "Admin",
+ annotation: "Person"
+ }
+]
+
diff --git a/src/main-process/FairCopyApplication.js b/src/main-process/FairCopyApplication.js
index d52fd055..7b06d02e 100644
--- a/src/main-process/FairCopyApplication.js
+++ b/src/main-process/FairCopyApplication.js
@@ -9,7 +9,7 @@ const log = require('electron-log')
const { FairCopySession } = require('./FairCopySession')
const debugBaseDir = `${process.cwd()}/src`
-const distBaseDir = process.resourcesPath
+const distBaseDir = process.resourcesPath
class FairCopyApplication {
@@ -24,7 +24,7 @@ class FairCopyApplication {
this.baseDir = !app.isPackaged ? debugBaseDir : distBaseDir
this.config = this.getConfig()
-
+
this.mainMenu = new MainMenu(this)
this.initFileProtocol()
this.initIPC()
@@ -44,228 +44,231 @@ class FairCopyApplication {
const url = request.url
console.log(`handling request: ${url}`)
// handle editioncrafter asset requests
- if( url.startsWith('ec://ec')) {
+ if (url.startsWith('ec://ec')) {
const content = this.fairCopySession.requestEditionCrafterData(url)
return new Response(content, {
headers: { 'content-type': 'text/html' }
- })
- // handle local image resource requests
- // } else if( url.startsWith('file://images') ) {
+ })
+ // handle local image resource requests
+ // } else if( url.startsWith('file://images') ) {
// TODO ... need localPath
// this.fairCopySession.openImageResource(request.url)
// return net.fetch(pathToFileURL(localPath).toString())
- // } else {
+ // } else {
// TODO ignore any other reqs
}
})
}
initIPC() {
-
- ipcMain.on('closeProject', (event) => {
+
+ ipcMain.on('closeProject', (event) => {
this.closeProject()
this.exitApp()
})
- ipcMain.on('openWebpage', (event, url ) => {
+ ipcMain.on('openWebpage', (event, url) => {
shell.openExternal(url)
- })
-
- ipcMain.on('exitApp', (event) => {
- if( this.projectWindow ) {
- this.projectWindow.close()
+ })
+
+ ipcMain.on('exitApp', (event) => {
+ if (this.projectWindow) {
+ this.projectWindow.close()
} else {
- this.exitApp()
+ this.exitApp()
}
})
ipcMain.on('reopenProject', (event) => {
- this.fairCopySession.reopenProject()
+ this.fairCopySession.reopenProject()
})
-
- ipcMain.on('addResource', (event, resourceEntry, resourceData, resourceMap) => { this.fairCopySession.addResource(resourceEntry,resourceData,resourceMap) })
+
+ ipcMain.on('addResource', (event, resourceEntry, resourceData, resourceMap) => { this.fairCopySession.addResource(resourceEntry, resourceData, resourceMap) })
ipcMain.on('replaceTEIDocument', (event, resources) => { this.fairCopySession.replaceTEIDocument(resources) })
- ipcMain.on('replaceResource', (event, resource, parentEntry) => { this.fairCopySession.replaceResource(resource,parentEntry) })
+ ipcMain.on('replaceResource', (event, resource, parentEntry) => { this.fairCopySession.replaceResource(resource, parentEntry) })
+
+ ipcMain.on('removeResources', (event, resourceIDs) => {
+ this.fairCopySession.removeResources(resourceIDs)
- ipcMain.on('removeResources', (event, resourceIDs) => {
- this.fairCopySession.removeResources(resourceIDs)
-
// close any open image windows
- for( const resourceID of resourceIDs ) {
+ for (const resourceID of resourceIDs) {
const imageView = this.imageViews[resourceID]
- if( imageView ) {
+ if (imageView) {
imageView.close()
}
- delete this.imageViews[resourceID]
+ delete this.imageViews[resourceID]
}
})
- ipcMain.on('recoverResources', (event, resourceID) => {
- this.fairCopySession.recoverResources(resourceID)
+ ipcMain.on('recoverResources', (event, resourceID) => {
+ this.fairCopySession.recoverResources(resourceID)
})
- ipcMain.on('requestResourceView', (event, resourceViewRequest) => {
- this.fairCopySession.updateResourceView(resourceViewRequest)
+ ipcMain.on('requestResourceView', (event, resourceViewRequest) => {
+ this.fairCopySession.updateResourceView(resourceViewRequest)
})
-
- ipcMain.on('searchProject', (event, searchQuery) => {
- this.fairCopySession.searchProject(searchQuery)
+
+ ipcMain.on('searchProject', (event, searchQuery) => {
+ this.fairCopySession.searchProject(searchQuery)
})
- ipcMain.on('requestSave', (event, msgID, resourceID, resourceData) => {
- const ok = this.fairCopySession.saveResource(resourceID, resourceData, !!this.previewView)
- if( ok ) {
- const update = { resourceID, messageID: msgID, resourceContent: resourceData }
- this.sendToAllWindows('resourceContentUpdated', update )
+ ipcMain.on('requestSave', (event, msgID, resourceID, resourceData, annotationData) => {
+ const ok = this.fairCopySession.saveResource(resourceID, resourceData, !!this.previewView, annotationData)
+ if (ok) {
+ const update = { resourceID, messageID: msgID, resourceContent: resourceData }
+ this.sendToAllWindows('resourceContentUpdated', update)
}
})
- ipcMain.on('abandonResourceMap', (event, resourceID) => {
+ ipcMain.on('updateStandoffAnnotations', (event, resourceID, annotationData) => {
+ const ok = this.fairCopySession.saveAnnotations(resourceID, annotationData)
+ })
+ ipcMain.on('abandonResourceMap', (event, resourceID) => {
this.fairCopySession.abandonResourceMap(resourceID)
})
- ipcMain.on('updateResource', (event, resourceEntry) => {
- this.fairCopySession.updateResource(resourceEntry)
+ ipcMain.on('updateResource', (event, resourceEntry) => {
+ this.fairCopySession.updateResource(resourceEntry)
})
ipcMain.on('requestImageData', (event) => {
const paths = this.mainMenu.openAddImage()
- if( paths ) {
+ if (paths) {
this.processImageData(paths).then((imageData) => {
- this.sendToAllWindows('imagesOpened', imageData )
- })
+ this.sendToAllWindows('imagesOpened', imageData)
+ })
} else {
this.sendToAllWindows('imagesOpened', [])
}
})
-
+
// Main window events //////
- ipcMain.on('openResource', (event,resourceID) => {
+ ipcMain.on('openResource', (event, resourceID) => {
this.fairCopySession.openResource(resourceID)
})
- ipcMain.on('setResourceMap', (event, resourceMap, localID, parentID) => {
+ ipcMain.on('setResourceMap', (event, resourceMap, localID, parentID) => {
this.fairCopySession.setResourceMap(resourceMap, localID, parentID)
})
- ipcMain.on('importContinue', (event) => {
+ ipcMain.on('importContinue', (event) => {
this.fairCopySession.importContinue()
})
- ipcMain.on('importEnd', (event) => {
+ ipcMain.on('importEnd', (event) => {
this.fairCopySession.importEnd()
})
- ipcMain.on('checkIn', (event, userID, serverURL, projectID, checkInResources, message ) => {
+ ipcMain.on('checkIn', (event, userID, serverURL, projectID, checkInResources, message) => {
this.fairCopySession.checkIn(userID, serverURL, projectID, checkInResources, message)
})
- ipcMain.on('checkOut', (event, userID, serverURL, projectID, resourceIDs ) => {
+ ipcMain.on('checkOut', (event, userID, serverURL, projectID, resourceIDs) => {
this.fairCopySession.checkOut(userID, serverURL, projectID, resourceIDs)
})
- ipcMain.on('requestSaveConfig', (event,fairCopyConfig,lastAction) => { this.fairCopySession.saveFairCopyConfig(fairCopyConfig,lastAction) })
- ipcMain.on('checkInConfig', (event,fairCopyConfig,firstAction) => { this.fairCopySession.checkInConfig(fairCopyConfig,firstAction) })
- ipcMain.on('checkOutConfig', (event) => { this.fairCopySession.checkOutConfig() })
- ipcMain.on('requestExportConfig', (event,exportPath,fairCopyConfig) => { this.fairCopySession.exportFairCopyConfig(exportPath,fairCopyConfig) })
- ipcMain.on('updateProjectInfo', (event,projectInfo) => { this.fairCopySession.updateProjectInfo(projectInfo) })
-
- ipcMain.on('requestPaste', (event) => {
- if( this.mainWindow ) {
- this.mainWindow.webContents.paste()
+ ipcMain.on('requestSaveConfig', (event, fairCopyConfig, lastAction) => { this.fairCopySession.saveFairCopyConfig(fairCopyConfig, lastAction) })
+ ipcMain.on('checkInConfig', (event, fairCopyConfig, firstAction) => { this.fairCopySession.checkInConfig(fairCopyConfig, firstAction) })
+ ipcMain.on('checkOutConfig', (event) => { this.fairCopySession.checkOutConfig() })
+ ipcMain.on('requestExportConfig', (event, exportPath, fairCopyConfig) => { this.fairCopySession.exportFairCopyConfig(exportPath, fairCopyConfig) })
+ ipcMain.on('updateProjectInfo', (event, projectInfo) => { this.fairCopySession.updateProjectInfo(projectInfo) })
+
+ ipcMain.on('requestPaste', (event) => {
+ if (this.mainWindow) {
+ this.mainWindow.webContents.paste()
}
})
- ipcMain.on('requestImport', (event,options) => {
+ ipcMain.on('requestImport', (event, options) => {
const paths = this.mainMenu.openImport()
- if( paths ) {
- this.fairCopySession.importStart(paths,options)
+ if (paths) {
+ this.fairCopySession.importStart(paths, options)
}
})
- ipcMain.on('requestIIIFImport', (event, importList) => {
- this.fairCopySession.importIIIFStart(importList)
+ ipcMain.on('requestIIIFImport', (event, importList) => {
+ this.fairCopySession.importIIIFStart(importList)
})
- ipcMain.on('requestExport', (event, resourceEntries) => {
+ ipcMain.on('requestExport', (event, resourceEntries) => {
const paths = this.mainMenu.openExport()
const path = paths ? paths[0] : null
- if( path ) {
- this.fairCopySession.requestExport(resourceEntries,path)
+ if (path) {
+ this.fairCopySession.requestExport(resourceEntries, path)
}
})
- ipcMain.on('requestImageView', (event, imageViewInfo) => {
- this.createImageWindow(imageViewInfo).then( () => {
+ ipcMain.on('requestImageView', (event, imageViewInfo) => {
+ this.createImageWindow(imageViewInfo).then(() => {
log.info(`Opened image view.`)
})
})
- ipcMain.on('requestPreviewView', (event, previewData) => {
+ ipcMain.on('requestPreviewView', (event, previewData) => {
// if the preview window already exists, move it to the front
- if( this.previewView ) {
+ if (this.previewView) {
this.previewView.focus()
}
this.fairCopySession.requestPreviewView(previewData)
})
- ipcMain.on('selectedZones', (event, selectedZones) => {
- this.sendToAllWindows('selectedZones', selectedZones )
+ ipcMain.on('selectedZones', (event, selectedZones) => {
+ this.sendToAllWindows('selectedZones', selectedZones)
})
// Project Window events ///////
- ipcMain.on('requestNewPath', (event) => {
+ ipcMain.on('requestNewPath', (event) => {
const targetPath = this.mainMenu.selectPath()
this.projectWindow.webContents.send('pathSelected', targetPath)
})
- ipcMain.on('requestNewProject', (event, projectInfo) => {
- createProjectArchive({ ...projectInfo, defaultProjectCSS: this.config.defaultProjectCSS, generatedWith: this.config.version}, this.baseDir, () => {
+ ipcMain.on('requestNewProject', (event, projectInfo) => {
+ createProjectArchive({ ...projectInfo, defaultProjectCSS: this.config.defaultProjectCSS, generatedWith: this.config.version }, this.baseDir, () => {
this.openProject(projectInfo.filePath)
})
})
- ipcMain.on('requestFileOpen', (event) => {
- this.mainMenu.openFileMenu()
+ ipcMain.on('requestFileOpen', (event) => {
+ this.mainMenu.openFileMenu()
})
- ipcMain.on('requestProject', (event,targetFile) => {
- if( fs.existsSync(targetFile) ) {
+ ipcMain.on('requestProject', (event, targetFile) => {
+ if (fs.existsSync(targetFile)) {
this.openProject(targetFile)
}
})
-
+
}
async createMainWindow() {
- if( this.mainWindow ) {
- if( !this.mainWindow.isDestroyed() ) {
+ if (this.mainWindow) {
+ if (!this.mainWindow.isDestroyed()) {
this.mainWindow.destroy()
}
this.mainWindow = null
}
- const windowSize = this.config.devMode ? [1440,1200] : [1440,900]
- this.mainWindow = await this.createWindow('main_window', ...windowSize, true, '#fff', true, true )
+ const windowSize = this.config.devMode ? [1440, 1200] : [1440, 900]
+ this.mainWindow = await this.createWindow('main_window', ...windowSize, true, '#fff', true, true)
this.mainWindow.webContents.send('appConfig', this.config)
// let render window handle on close (without browser restrictions)
this.mainWindow.on('close', (event) => {
- if( !this.exiting ) {
+ if (!this.exiting) {
this.sendToMainWindow('requestExitApp')
event.preventDefault()
- }
+ }
})
}
async createProjectWindow() {
- this.projectWindow = await this.createWindow('project_window', 740, 570, true, '#E6DEF9', false )
+ this.projectWindow = await this.createWindow('project_window', 740, 570, true, '#E6DEF9', false)
this.projectWindow.webContents.send('appConfig', this.config)
- }
+ }
async createPreviewWindow(previewData) {
- if( !this.previewView ) {
- const windowSize = this.config.devMode ? [1440,1200] : [1440,900]
- this.previewView = await this.createWindow('preview_window', ...windowSize, true, '#fff', false, true )
- this.previewView.on('close', e => delete this.previewView )
+ if (!this.previewView) {
+ const windowSize = this.config.devMode ? [1440, 1200] : [1440, 900]
+ this.previewView = await this.createWindow('preview_window', ...windowSize, true, '#fff', false, true)
+ this.previewView.on('close', e => delete this.previewView)
this.previewView.webContents.send('previewViewOpened', previewData)
} else {
this.previewView.webContents.send('updatePreview', previewData)
@@ -273,42 +276,42 @@ class FairCopyApplication {
}
async createImageWindow(imageViewInfo) {
- const imageView = await this.createWindow('image_window', 1024, 768, true, '#fff', false )
- const {resourceID, xmlID} = imageViewInfo
-
+ const imageView = await this.createWindow('image_window', 1024, 768, true, '#fff', false)
+ const { resourceID, xmlID } = imageViewInfo
+
this.imageViews[resourceID] = imageView
imageView.on('close', (event) => {
delete this.imageViews[resourceID]
})
- this.fairCopySession.openResource( resourceID, xmlID )
+ this.fairCopySession.openResource(resourceID, xmlID)
}
exitApp() {
- if( !this.exiting ) {
+ if (!this.exiting) {
this.exiting = true
- for( const imageView of Object.values(this.imageViews) ) {
- if( !imageView.isDestroyed() ) {
+ for (const imageView of Object.values(this.imageViews)) {
+ if (!imageView.isDestroyed()) {
imageView.close()
}
}
this.imageViews = {}
- if( this.previewView ) {
+ if (this.previewView) {
this.previewView.close()
this.previewView = null
}
- if( this.returnToProjectWindow ) {
+ if (this.returnToProjectWindow) {
this.createProjectWindow().then(() => {
this.returnToProjectWindow = false
this.exiting = false
})
- }
+ }
- if( this.mainWindow ) {
+ if (this.mainWindow) {
this.mainWindow.close()
this.fairCopySession.closeProject()
}
@@ -317,9 +320,9 @@ class FairCopyApplication {
openProject(targetFile) {
this.createMainWindow().then(() => {
- if( this.projectWindow ) {
+ if (this.projectWindow) {
this.projectWindow.close()
- this.projectWindow = null
+ this.projectWindow = null
}
this.fairCopySession = new FairCopySession(this, targetFile)
this.mainMenu.updateMenu()
@@ -328,13 +331,13 @@ class FairCopyApplication {
}
closeProject() {
- if( this.mainWindow && !this.mainWindow.isDestroyed() ) {
+ if (this.mainWindow && !this.mainWindow.isDestroyed()) {
this.returnToProjectWindow = true
this.mainWindow.close()
this.fairCopySession.closeProject()
this.mainMenu.updateMenu()
}
- if( this.projectWindow && !this.projectWindow.isDestroyed()) {
+ if (this.projectWindow && !this.projectWindow.isDestroyed()) {
this.projectWindow.close()
}
}
@@ -349,14 +352,14 @@ class FairCopyApplication {
sendToAllWindows(message, params) {
this.sendToMainWindow(message, params)
- for( const imageView of Object.values(this.imageViews) ) {
+ for (const imageView of Object.values(this.imageViews)) {
imageView.webContents.send(message, params)
}
}
async processImageData(paths) {
const imageData = []
- for( const path of paths ) {
+ for (const path of paths) {
const image = await Jimp.read(path)
const mimeType = image.getMIME()
const width = image.bitmap.width
@@ -371,7 +374,7 @@ class FairCopyApplication {
return imageData
}
- async createWindow(windowName, width, height, resizable, backgroundColor, menuBar, devTools ) {
+ async createWindow(windowName, width, height, resizable, backgroundColor, menuBar, devTools) {
const webpackPreloadPath = getWebpackPreload(windowName)
@@ -382,9 +385,9 @@ class FairCopyApplication {
minWidth: 1024,
minHeight: 768,
webPreferences: {
- sandbox: false, // so we can use system clipboard
- preload: webpackPreloadPath,
- spellcheck: false
+ sandbox: false, // so we can use system clipboard
+ preload: webpackPreloadPath,
+ spellcheck: false
},
autoHideMenuBar: !menuBar,
@@ -393,13 +396,13 @@ class FairCopyApplication {
})
const webpackEntryURL = getWebpackEntry(windowName)
- await browserWindow.loadURL(webpackEntryURL)
+ await browserWindow.loadURL(webpackEntryURL)
log.info(`Opened window.`)
// Open the DevTools.
- if( !app.isPackaged && devTools )
- browserWindow.webContents.openDevTools({ mode: 'bottom'})
-
+ if (!app.isPackaged && devTools)
+ browserWindow.webContents.openDevTools({ mode: 'bottom' })
+
return browserWindow
}
}
diff --git a/src/main-process/FairCopySession.js b/src/main-process/FairCopySession.js
index 6287d708..a5a1c8eb 100644
--- a/src/main-process/FairCopySession.js
+++ b/src/main-process/FairCopySession.js
@@ -4,23 +4,24 @@ const log = require('electron-log')
const { RemoteProject } = require('./RemoteProject')
const { ProjectStore } = require('./ProjectStore')
const { createIDMapAuthority } = require('./IDMapAuthority')
+const { serializeStandoff } = require('../render/workers/serialize-standoff')
const initialRowsPerPage = 50
class FairCopySession {
- constructor( fairCopyApplication, targetFile ) {
+ constructor(fairCopyApplication, targetFile) {
this.fairCopyApplication = fairCopyApplication
this.projectStore = new ProjectStore(fairCopyApplication)
- this.projectStore.openProject(targetFile, this.onProjectOpened )
+ this.projectStore.openProject(targetFile, this.onProjectOpened)
this.remote = false
this.projectOpen = true
this.resourceViews = {
currentView: 'home',
- remote: {
+ remote: {
indexParentID: null,
parentEntry: null,
- currentPage: 1,
+ currentPage: 1,
rowsPerPage: initialRowsPerPage,
totalRows: 0,
nameFilter: null,
@@ -31,13 +32,13 @@ class FairCopySession {
home: {
indexParentID: null,
parentEntry: null,
- currentPage: 1,
+ currentPage: 1,
rowsPerPage: initialRowsPerPage,
totalRows: 0,
nameFilter: null,
orderBy: 'name',
order: 'ascending',
- loading: true
+ loading: true
}
}
}
@@ -49,15 +50,15 @@ class FairCopySession {
// id map authority tracks ids across processes and server
this.idMapAuthority = createIDMapAuthority(this.remote, idMap, (idMapData) => {
- this.fairCopyApplication.sendToAllWindows('IDMapUpdated', { idMapData } )
+ this.fairCopyApplication.sendToAllWindows('IDMapUpdated', { idMapData })
})
- this.fairCopyApplication.sendToMainWindow('projectOpened', projectData )
+ this.fairCopyApplication.sendToMainWindow('projectOpened', projectData)
// init remote project if this is one
- if( this.remote ) {
+ if (this.remote) {
const { userID, serverURL, projectID } = manifestData
- this.remoteProject = new RemoteProject(this, userID, serverURL, projectID )
+ this.remoteProject = new RemoteProject(this, userID, serverURL, projectID)
}
this.requestResourceView()
@@ -66,7 +67,7 @@ class FairCopySession {
closeProject() {
this.projectStore.close()
- if( this.remoteProject ) this.remoteProject.close()
+ if (this.remoteProject) this.remoteProject.close()
this.projectOpen = false
}
@@ -74,73 +75,73 @@ class FairCopySession {
this.remoteProject.open()
this.requestResourceView()
}
-
+
openImageResource(url) {
this.projectStore.openImageResource(url)
}
setResourceMap(resourceMap, localID, parentID) {
- this.idMapAuthority.setResourceMap( resourceMap, localID, parentID )
+ this.idMapAuthority.setResourceMap(resourceMap, localID, parentID)
}
- addResource(resourceEntry,resourceData,resourceMap) {
+ addResource(resourceEntry, resourceData, resourceMap) {
let idMap = null
- if( resourceMap ) {
+ if (resourceMap) {
const { localID } = resourceEntry
- if( resourceEntry.parentResource ) {
+ if (resourceEntry.parentResource) {
const { localID: parentID } = this.idMapAuthority.getLocalIDs(resourceEntry.parentResource)
- idMap = this.idMapAuthority.addResource(localID,parentID,resourceMap)
+ idMap = this.idMapAuthority.addResource(localID, parentID, resourceMap)
} else {
- idMap = this.idMapAuthority.addResource(localID,null,resourceMap)
+ idMap = this.idMapAuthority.addResource(localID, null, resourceMap)
}
-
- this.idMapAuthority.sendIDMapUpdate()
+
+ this.idMapAuthority.sendIDMapUpdate()
}
- this.projectStore.addResource(resourceEntry,resourceData,idMap)
+ this.projectStore.addResource(resourceEntry, resourceData, idMap)
}
- replaceTEIDocument( resources ) {
+ replaceTEIDocument(resources) {
const { resources: manifestResources } = this.projectStore.manifestData
- const teiDocResource = resources.find( r => r.resourceEntry.type == 'teidoc' )
- const teiDocLocalID = teiDocResource.resourceEntry.localID
- const existingTEIDoc = Object.values(manifestResources).find( r => r.localID == teiDocLocalID && r.type == 'teidoc' )
- const existingResourceMap = this.idMapAuthority.getResourceMapByLocalID(teiDocLocalID,null)
+ const teiDocResource = resources.find(r => r.resourceEntry.type == 'teidoc')
+ const teiDocLocalID = teiDocResource.resourceEntry.localID
+ const existingTEIDoc = Object.values(manifestResources).find(r => r.localID == teiDocLocalID && r.type == 'teidoc')
+ const existingResourceMap = this.idMapAuthority.getResourceMapByLocalID(teiDocLocalID, null)
- if( existingTEIDoc ) {
- for( const resource of resources ) {
- if( resource.resourceEntry.type !== 'teidoc') {
- if( !this.replaceResource(resource,existingTEIDoc) ) {
+ if (existingTEIDoc) {
+ for (const resource of resources) {
+ if (resource.resourceEntry.type !== 'teidoc') {
+ if (!this.replaceResource(resource, existingTEIDoc)) {
// don't continue if any sub resources fail
return
}
} else {
// acknowledge that we got the tei doc
- if(this.projectStore.importInProgress) {
+ if (this.projectStore.importInProgress) {
this.projectStore.importContinue()
}
}
}
const doomedIDs = []
- for( const childLocalID of Object.keys(existingResourceMap.ids) ) {
- const newerVersion = resources.find( r => r.resourceEntry.localID == childLocalID )
- if( !newerVersion ) {
+ for (const childLocalID of Object.keys(existingResourceMap.ids)) {
+ const newerVersion = resources.find(r => r.resourceEntry.localID == childLocalID)
+ if (!newerVersion) {
// if there wasn't a newer version of this resource, remove it
- doomedIDs.push( existingResourceMap.ids[childLocalID].resourceID )
+ doomedIDs.push(existingResourceMap.ids[childLocalID].resourceID)
}
}
- if( doomedIDs.length > 0 ) this.removeResources(doomedIDs)
+ if (doomedIDs.length > 0) this.removeResources(doomedIDs)
} else {
- if( !existingResourceMap ) {
+ if (!existingResourceMap) {
// add this teidoc and its resources as a new doc
- for( const resource of resources ) {
- const { resourceEntry, content, resourceMap } = resource
- this.addResource(resourceEntry, content, resourceMap )
+ for (const resource of resources) {
+ const { resourceEntry, content, resourceMap } = resource
+ this.addResource(resourceEntry, content, resourceMap)
}
} else {
- if(this.projectStore.importInProgress) {
+ if (this.projectStore.importInProgress) {
this.projectStore.importError(`${teiDocLocalID} is not checked out and could not be replaced.`)
- this.projectStore.importContinue()
+ this.projectStore.importContinue()
}
}
}
@@ -153,35 +154,35 @@ class FairCopySession {
// is there an existing resource with this id and parent? also set parentResource
let existingLocalResource = null
- if( parentEntry ) {
- existingLocalResource = Object.values(resources).find( r => r.localID == localID && parentEntry.id == r.parentResource )
+ if (parentEntry) {
+ existingLocalResource = Object.values(resources).find(r => r.localID == localID && parentEntry.id == r.parentResource)
resourceEntry.parentResource = parentEntry.id
} else {
- existingLocalResource = Object.values(resources).find( r => r.localID == localID && r.parentResource == null)
+ existingLocalResource = Object.values(resources).find(r => r.localID == localID && r.parentResource == null)
resourceEntry.parentResource = null
}
- if( existingLocalResource ) {
+ if (existingLocalResource) {
// save over top of the existing resource
- resourceEntry.id = existingLocalResource.id
+ resourceEntry.id = existingLocalResource.id
resourceMap.resourceID = existingLocalResource.id
const parentLocalID = parentEntry ? parentEntry.localID : null
this.setResourceMap(resourceMap, localID, parentLocalID)
- this.saveResource(resourceEntry.id,content,false)
+ this.saveResource(resourceEntry.id, content, false)
- if(this.projectStore.importInProgress) {
+ if (this.projectStore.importInProgress) {
this.projectStore.importContinue()
}
} else {
// otherwise, does it exist in the idMap?
const parentID = parentEntry ? parentEntry.localID : null
- const existingResourceMap = this.idMapAuthority.getResourceMapByLocalID(localID,parentID)
+ const existingResourceMap = this.idMapAuthority.getResourceMapByLocalID(localID, parentID)
// if not, just add this resource
- if( !existingResourceMap ) {
- if( parentEntry ) resourceEntry.parentResource = parentEntry.id
+ if (!existingResourceMap) {
+ if (parentEntry) resourceEntry.parentResource = parentEntry.id
this.addResource(resourceEntry, content, resourceMap)
} else {
- if(this.projectStore.importInProgress) {
+ if (this.projectStore.importInProgress) {
this.projectStore.importError(`${localID} is not checked out and could not be replaced.`)
this.projectStore.importContinue()
return false
@@ -194,18 +195,18 @@ class FairCopySession {
removeResources(resourceIDs) {
const { resources } = this.projectStore.manifestData
// use this map to generate unique ID list
- const doomedIDMap = {}
+ const doomedIDMap = {}
- for( const resourceID of resourceIDs ) {
+ for (const resourceID of resourceIDs) {
const resourceEntry = resources[resourceID]
- if( resourceEntry ) {
- if( resourceEntry.type === 'teidoc') {
+ if (resourceEntry) {
+ if (resourceEntry.type === 'teidoc') {
// also delete any checked out children
- for( const localResource of Object.values(resources) ) {
+ for (const localResource of Object.values(resources)) {
const { parentResource } = localResource
- if( localResource.type !== 'image' && parentResource === resourceEntry.id ) {
- doomedIDMap[localResource.id] = true
- }
+ if (localResource.type !== 'image' && parentResource === resourceEntry.id) {
+ doomedIDMap[localResource.id] = true
+ }
}
}
doomedIDMap[resourceEntry.id] = true
@@ -216,7 +217,7 @@ class FairCopySession {
const doomedIDs = Object.keys(doomedIDMap)
const idMap = this.idMapAuthority.removeResources(doomedIDs)
- this.projectStore.removeResources(doomedIDs,idMap)
+ this.projectStore.removeResources(doomedIDs, idMap)
this.requestResourceView()
}
@@ -224,40 +225,40 @@ class FairCopySession {
const { resources } = this.projectStore.manifestData
const recoveredIDMap = {}
- for( const resourceID of resourceIDs ) {
+ for (const resourceID of resourceIDs) {
const resourceEntry = resources[resourceID]
- if( resourceEntry && resourceEntry.deleted ) {
+ if (resourceEntry && resourceEntry.deleted) {
// also recover any checked out children
- if( resourceEntry.type === 'teidoc') {
- for( const localResource of Object.values(resources) ) {
+ if (resourceEntry.type === 'teidoc') {
+ for (const localResource of Object.values(resources)) {
const { parentResource } = localResource
- if( localResource.type !== 'image' && parentResource === resourceEntry.id ) {
- recoveredIDMap[localResource.id] = true
- }
+ if (localResource.type !== 'image' && parentResource === resourceEntry.id) {
+ recoveredIDMap[localResource.id] = true
+ }
}
}
recoveredIDMap[resourceEntry.id] = true
} else {
log.info(`Error recovering resource entry: ${resourceID}`)
- }
+ }
}
const recoveredIDs = Object.keys(recoveredIDMap)
- if( recoveredIDs.length > 0 ) {
+ if (recoveredIDs.length > 0) {
const idMap = this.idMapAuthority.recoverResources(recoveredIDs)
- this.projectStore.recoverResources(recoveredIDs,idMap)
- this.requestResourceView()
+ this.projectStore.recoverResources(recoveredIDs, idMap)
+ this.requestResourceView()
}
}
updateResourceView(resourceViewRequest) {
- if( resourceViewRequest ) {
- const { currentView, indexParentID, parentEntry } = resourceViewRequest
+ if (resourceViewRequest) {
+ const { currentView, indexParentID, parentEntry } = resourceViewRequest
const resourceView = this.resourceViews[currentView]
const currentPage = resourceViewRequest.currentPage !== undefined ? resourceViewRequest.currentPage : resourceView.currentPage
let nameFilter, order, orderBy
// carry over name filter and order values if switching views
- if( this.resourceViews.currentView !== currentView ) {
+ if (this.resourceViews.currentView !== currentView) {
nameFilter = resourceViewRequest.nameFilter !== undefined ? resourceViewRequest.nameFilter : this.resourceViews[currentView].nameFilter
order = resourceViewRequest.order !== undefined ? resourceViewRequest.order : this.resourceViews[currentView].order
orderBy = resourceViewRequest.orderBy !== undefined ? resourceViewRequest.orderBy : this.resourceViews[currentView].orderBy
@@ -267,29 +268,29 @@ class FairCopySession {
orderBy = resourceViewRequest.orderBy !== undefined ? resourceViewRequest.orderBy : resourceView.orderBy
}
this.resourceViews[currentView] = { ...resourceView, indexParentID, parentEntry, nameFilter, order, orderBy, currentPage }
- this.resourceViews.currentView = currentView
+ this.resourceViews.currentView = currentView
}
this.requestResourceView()
}
requestResourceView() {
- const {currentView} = this.resourceViews
+ const { currentView } = this.resourceViews
const resourceView = this.resourceViews[currentView]
const { indexParentID, currentPage, rowsPerPage } = resourceView
const { resources: localResources } = this.projectStore.manifestData
- if( currentView === 'remote' ) {
+ if (currentView === 'remote') {
// request resource data from server
this.remoteProject.requestResourceView(resourceView)
} else {
// respond right away from project store
resourceView.parentEntry = indexParentID ? localResources[indexParentID] : null
const { nameFilter, order, orderBy } = resourceView
- const sortedResources = Object.values(localResources).sort((a,b) => {
+ const sortedResources = Object.values(localResources).sort((a, b) => {
const valueA = a[orderBy].toUpperCase()
const valueB = b[orderBy].toUpperCase()
- if( valueA === valueB ) return 0
- if( order == 'ascending' ) {
+ if (valueA === valueB) return 0
+ if (order == 'ascending') {
return valueA > valueB ? 1 : -1
} else {
return valueA < valueB ? 1 : -1
@@ -297,35 +298,35 @@ class FairCopySession {
})
let resourceIndex = []
- for( const localResource of sortedResources ) {
+ for (const localResource of sortedResources) {
const { parentResource } = localResource
- if( localResource.type !== 'image' ) {
+ if (localResource.type !== 'image') {
// if this resource is a child of current parent OR
// if the parent is not checked out, display it at top level
- if( parentResource === indexParentID ||
- ( indexParentID === null && !localResources[parentResource] )) {
- if( indexParentID ) {
+ if (parentResource === indexParentID ||
+ (indexParentID === null && !localResources[parentResource])) {
+ if (indexParentID) {
// filter doesn't act on teidoc views
- resourceIndex.push(localResource)
- } else if( !nameFilter || localResource.name.includes(nameFilter) ) {
- resourceIndex.push(localResource)
+ resourceIndex.push(localResource)
+ } else if (!nameFilter || localResource.name.includes(nameFilter)) {
+ resourceIndex.push(localResource)
}
}
- }
+ }
}
// don't let currentPage be > page count
- let pageCount = Math.ceil(resourceIndex.length/rowsPerPage)
+ let pageCount = Math.ceil(resourceIndex.length / rowsPerPage)
pageCount = pageCount === 0 ? 1 : pageCount
const nextPage = currentPage > pageCount ? pageCount : currentPage
- const start = rowsPerPage * (nextPage-1)
+ const start = rowsPerPage * (nextPage - 1)
const end = start + rowsPerPage
resourceView.totalRows = resourceIndex.length
resourceView.currentPage = nextPage
resourceView.loading = false
this.resourceViews[currentView] = resourceView
- resourceIndex = resourceIndex.slice(start,end)
+ resourceIndex = resourceIndex.slice(start, end)
- this.fairCopyApplication.sendToAllWindows('resourceViewUpdate', { resourceViews: this.resourceViews, resourceIndex } )
+ this.fairCopyApplication.sendToAllWindows('resourceViewUpdate', { resourceViews: this.resourceViews, resourceIndex })
}
}
@@ -334,82 +335,104 @@ class FairCopySession {
const { indexParentID, rowsPerPage, currentPage, totalRows } = resourceView
// if parent isn't in remote response, must be local parent
- if( indexParentID !== null && !resourceView.parentEntry ) {
+ if (indexParentID !== null && !resourceView.parentEntry) {
resourceView.parentEntry = localResources[indexParentID]
}
- const resourceIndex = remoteResources.map( resourceEntry => {
+ const resourceIndex = remoteResources.map(resourceEntry => {
return localResources[resourceEntry.id] ? localResources[resourceEntry.id] : resourceEntry
})
// don't let currentPage be > page count
- let pageCount = Math.ceil(totalRows/rowsPerPage)
+ let pageCount = Math.ceil(totalRows / rowsPerPage)
pageCount = pageCount === 0 ? 1 : pageCount
resourceView.currentPage = currentPage > pageCount ? pageCount : currentPage
-
+
this.resourceViews.remote = resourceView
this.fairCopyApplication.sendToAllWindows('resourceViewUpdate', { resourceViews: this.resourceViews, resourceIndex })
}
-
+
searchProject(searchQuery) {
- this.projectStore.searchIndex.searchProject(searchQuery)
+ this.projectStore.searchIndex.searchProject(searchQuery)
}
- saveResource(resourceID, resourceData, updatePreview) {
+ saveResource(resourceID, resourceData, updatePreview, annotationData) {
const { resources } = this.projectStore.manifestData
const resourceEntry = resources[resourceID]
- if( resourceEntry ) {
+ if (resourceEntry) {
const { localID, parentID } = this.idMapAuthority.getLocalIDs(resourceID)
const idMap = this.idMapAuthority.commitResource(localID, parentID)
- this.projectStore.saveResource(resourceEntry, resourceData, idMap)
- if( updatePreview ) {
+ this.projectStore.saveResource(resourceEntry, resourceData, idMap)
+ if (updatePreview) {
this.requestPreviewView({ resourceEntry })
- }
- return true
+ }
+ return true
+ }
+ return false
+ }
+
+ saveAnnotations(resourceID, annotationData) {
+ const { resources } = this.projectStore.manifestData
+ const resourceEntry = resources[resourceID]
+ if (resourceEntry) {
+ // Find the standoff for this entry
+ const parent = resources[resourceEntry.parentResource]
+ let standoff
+ Object.keys(resources).forEach(key => {
+ const res = resources[key]
+ if (res.parentResource === parent.id && res.type === 'standOff') {
+ standoff = res
+ }
+ })
+
+ if (standoff) {
+ this.projectStore.saveAnnotationData(standoff, annotationData)
+ }
+ return true
}
return false
}
updateResource(resourceEntry) {
const { resources } = this.projectStore.manifestData
- if( resources[resourceEntry.id] ) {
+ if (resources[resourceEntry.id]) {
const currentLocalID = resources[resourceEntry.id].localID
const currentParentID = resources[resourceEntry.id].parentResource
resources[resourceEntry.id] = resourceEntry
- let idMap = null
- if( resourceEntry.parentResource !== currentParentID ) {
- idMap = this.idMapAuthority.moveResourceMap( resourceEntry.localID, currentLocalID, resourceEntry.parentResource, currentParentID )
+ let idMap = null
+ if (resourceEntry.parentResource !== currentParentID) {
+ idMap = this.idMapAuthority.moveResourceMap(resourceEntry.localID, currentLocalID, resourceEntry.parentResource, currentParentID)
}
- if( resourceEntry.localID !== currentLocalID ) {
- if( resourceEntry.parentResource ) {
+ if (resourceEntry.localID !== currentLocalID) {
+ if (resourceEntry.parentResource) {
const { localID: parentID } = this.idMapAuthority.getLocalIDs(resourceEntry.parentResource)
- idMap = this.idMapAuthority.changeID( resourceEntry.localID, currentLocalID, parentID )
+ idMap = this.idMapAuthority.changeID(resourceEntry.localID, currentLocalID, parentID)
} else {
- idMap = this.idMapAuthority.changeID( resourceEntry.localID, currentLocalID, null )
+ idMap = this.idMapAuthority.changeID(resourceEntry.localID, currentLocalID, null)
}
}
- if( idMap ) {
+ if (idMap) {
this.projectStore.saveIDMap(idMap)
- this.idMapAuthority.sendIDMapUpdate()
+ this.idMapAuthority.sendIDMapUpdate()
}
- this.projectStore.saveManifest()
- this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry )
- this.requestResourceView()
+ this.projectStore.saveManifest()
+ this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry)
+ this.requestResourceView()
} else {
log.info(`Error updating resource entry: ${resourceEntry.id}`)
}
}
abandonResourceMap(resourceID) {
- const { localID, parentID } = this.idMapAuthority.getLocalIDs( resourceID )
- this.idMapAuthority.abandonResourceMap(localID,parentID)
+ const { localID, parentID } = this.idMapAuthority.getLocalIDs(resourceID)
+ this.idMapAuthority.abandonResourceMap(localID, parentID)
this.idMapAuthority.sendIDMapUpdate()
}
- openResource(resourceID, xmlID=null ) {
- if( this.remote && !this.projectStore.manifestData.resources[resourceID]) {
+ openResource(resourceID, xmlID = null) {
+ if (this.remote && !this.projectStore.manifestData.resources[resourceID]) {
this.remoteProject.openResource(resourceID, xmlID)
} else {
this.projectStore.openResource(resourceID, xmlID)
@@ -417,32 +440,37 @@ class FairCopySession {
}
resourceOpened(resourceEntry, parentEntry, resource, xmlID) {
- if( this.remote && !parentEntry && resourceEntry.parentResource ) {
+ if (this.remote && !parentEntry && resourceEntry.parentResource) {
// parent is remote and resourceEntry is local or checked out
// go lookup the parent entry to complete this payload
this.remoteProject.getParentEntry(resourceEntry, resource, xmlID)
} else {
- if( xmlID ) {
+ if (xmlID) {
// at the moment, only image views use xmlID
- this.openImageView( { xmlID, resourceEntry, parentEntry, resource } )
+ this.openImageView({ xmlID, resourceEntry, parentEntry, resource })
} else {
- this.fairCopyApplication.sendToAllWindows('resourceOpened', { resourceEntry, parentEntry, resource } )
- log.info(`opened resourceID: ${resourceEntry.id}`)
+ this.fairCopyApplication.sendToAllWindows('resourceOpened', { resourceEntry, parentEntry, resource })
+ log.info(`opened resourceID: ${resourceEntry.id}`)
}
}
}
+
+ annotationDataReceived(data, parentResourceID) {
+ this.fairCopyApplication.sendToAllWindows('annotationDataReceived', { annotationData: data, parentResourceID })
+ }
+
// parent found, send complete resourceOpened message
parentFound(resourceEntry, parentEntry, resource, xmlID) {
- if( xmlID ) {
- this.openImageView( { xmlID, resourceEntry, parentEntry, resource } )
+ if (xmlID) {
+ this.openImageView({ xmlID, resourceEntry, parentEntry, resource })
} else {
- this.fairCopyApplication.sendToMainWindow('resourceOpened', { resourceEntry, parentEntry, resource } )
- log.info(`got parent, opened resourceID: ${resourceEntry.id}`)
+ this.fairCopyApplication.sendToMainWindow('resourceOpened', { resourceEntry, parentEntry, resource })
+ log.info(`got parent, opened resourceID: ${resourceEntry.id}`)
}
}
- openImageView( resourceData ) {
+ openImageView(resourceData) {
const { resourceEntry } = resourceData
const imageViewData = { ...resourceData }
@@ -450,8 +478,8 @@ class FairCopySession {
imageViewData.teiSchema = fs.readFileSync(`${baseDir}/tei-simple.json`).toString('utf-8')
imageViewData.idMap = JSON.stringify(this.idMapAuthority.idMap)
- // mix in remote project data if needed
- if( this.remote ) {
+ // mix in remote project data if needed
+ if (this.remote) {
const { userID, serverURL, permissions } = this.projectStore.manifestData
imageViewData.userID = userID
imageViewData.serverURL = serverURL
@@ -459,12 +487,12 @@ class FairCopySession {
imageViewData.permissions = permissions
}
const imageView = this.fairCopyApplication.imageViews[resourceEntry.id]
- imageView.webContents.send('imageViewOpened', imageViewData )
+ imageView.webContents.send('imageViewOpened', imageViewData)
this.projectStore.sendLocalResources()
}
- importStart(paths,options) {
- this.projectStore.importStart(paths,options)
+ importStart(paths, options) {
+ this.projectStore.importStart(paths, options)
}
importIIIFStart(importList) {
@@ -484,7 +512,7 @@ class FairCopySession {
const { resources } = this.projectStore.manifestData
const committedResources = []
- const createCommitEntry = ( resourceEntry ) => {
+ const createCommitEntry = (resourceEntry) => {
const { id, local, deleted, name, localID, parentResource: parentID, type } = resourceEntry
const resourceMap = !deleted ? this.idMapAuthority.getResourceMap(id) : null
const action = deleted ? 'destroy' : local ? 'create' : 'update'
@@ -498,16 +526,16 @@ class FairCopySession {
resourceType: type
}
}
-
+
const homeParentID = this.resourceViews.home.indexParentID
- for( const resourceID of checkInResources ) {
+ for (const resourceID of checkInResources) {
const resourceEntry = resources[resourceID]
// ignore resources that aren't in local manifest
- if( resourceEntry ) {
+ if (resourceEntry) {
const resourceCommitEntry = createCommitEntry(resourceEntry)
committedResources.push(resourceCommitEntry)
}
- if( resourceID === homeParentID ) {
+ if (resourceID === homeParentID) {
// if this got checked in, move to root
this.resourceViews.home.indexParentID = null
}
@@ -531,12 +559,12 @@ class FairCopySession {
checkOutConfig() {
this.remoteProject.checkOutConfig()
- }
+ }
- exportFairCopyConfig(exportPath,fairCopyConfig) {
- this.projectStore.exportFairCopyConfig(exportPath,fairCopyConfig)
+ exportFairCopyConfig(exportPath, fairCopyConfig) {
+ this.projectStore.exportFairCopyConfig(exportPath, fairCopyConfig)
}
-
+
updateProjectInfo(projectInfo) {
this.projectStore.updateProjectInfo(projectInfo)
// TODO if this is a remote project, send the latest name and description to server
@@ -547,8 +575,8 @@ class FairCopySession {
this.projectStore.requestPreviewView(previewData)
}
- requestExport(resourceEntries,path) {
- this.projectStore.requestExport(resourceEntries,path)
+ requestExport(resourceEntries, path) {
+ this.projectStore.requestExport(resourceEntries, path)
}
requestEditionCrafterData(url) {
diff --git a/src/main-process/ProjectStore.js b/src/main-process/ProjectStore.js
index 68993968..054b022f 100644
--- a/src/main-process/ProjectStore.js
+++ b/src/main-process/ProjectStore.js
@@ -8,6 +8,7 @@ const { SearchIndex } = require('./SearchIndex')
const { WorkerWindow } = require('./WorkerWindow')
const { EditionCrafterServer } = require('./EditionCrafterServer')
+
const manifestEntryName = 'faircopy-manifest.json'
const configSettingsEntryName = 'config-settings.json'
const idMapEntryName = 'id-map.json'
@@ -22,15 +23,15 @@ class ProjectStore {
this.editionCrafterServer = new EditionCrafterServer()
}
- initProjectArchiveWorker( baseDir, debug, projectFilePath ) {
- this.projectArchiveWorker = new WorkerWindow( baseDir, debug, 'project-archive', (msg) => {
+ initProjectArchiveWorker(baseDir, debug, projectFilePath) {
+ this.projectArchiveWorker = new WorkerWindow(baseDir, debug, 'project-archive', (msg) => {
const { messageType } = msg
- switch( messageType ) {
- case 'project-data':
+ switch (messageType) {
+ case 'project-data':
{
const { project } = msg
- this.loadProject(project)
+ this.loadProject(project)
}
break
case 'resource-data':
@@ -38,20 +39,26 @@ class ProjectStore {
const { resourceID, xmlID, resource } = msg
const resourceEntry = this.manifestData.resources[resourceID]
const parentEntry = this.manifestData.resources[resourceEntry.parentResource]
- this.fairCopyApplication.fairCopySession.resourceOpened( resourceEntry, parentEntry, resource, xmlID )
+ this.fairCopyApplication.fairCopySession.resourceOpened(resourceEntry, parentEntry, resource, xmlID)
+ }
+ break
+ case 'annotation-data':
+ {
+ const { data, parentResourceID } = msg
+ this.fairCopyApplication.fairCopySession.annotationDataReceived(data, parentResourceID)
}
break
case 'index-resource':
{
const { resourceID, resource } = msg
const resourceEntry = this.manifestData.resources[resourceID]
- this.searchIndex.indexResource( resourceID, resourceEntry.type, resource )
+ this.searchIndex.indexResource(resourceID, resourceEntry.type, resource)
}
break
case 'exported-resource':
{
const { error, path } = msg
- if( error ) {
+ if (error) {
log.info(`Error exporting resources to: ${path} ${error}`)
} else {
log.info(`Exported resources to: ${path}`)
@@ -61,19 +68,19 @@ class ProjectStore {
case 'preview-resource':
{
const { previewData, ecData, error } = msg
- if( error ) {
+ if (error) {
log.info(`Error previewing resource: ${error}`)
} else {
const teiDocumentID = previewData.resourceEntry.localID
- this.editionCrafterServer.addTEIDocument(teiDocumentID,ecData)
- this.fairCopyApplication.createPreviewWindow(previewData).then(() => {})
+ this.editionCrafterServer.addTEIDocument(teiDocumentID, ecData)
+ this.fairCopyApplication.createPreviewWindow(previewData).then(() => { })
}
}
- break
+ break
case 'cache-file-name':
{
const { cacheFile } = msg
- if( cacheFile ) this.fairCopyApplication.localFileProtocolCallback(decodeURIComponent(cacheFile))
+ if (cacheFile) this.fairCopyApplication.localFileProtocolCallback(decodeURIComponent(cacheFile))
}
break
case 'check-in-results':
@@ -85,37 +92,37 @@ class ProjectStore {
case 'check-out-results':
{
const { resources, error } = msg
- this.checkOutResults(resources,error)
+ this.checkOutResults(resources, error)
}
break
default:
throw new Error(`Unrecognized message type ${messageType} received from project archive: ${JSON.stringify(msg)}`)
}
})
-
+
return this.projectArchiveWorker.start({ projectFilePath, manifestEntryName, configSettingsEntryName, idMapEntryName })
}
openProject(projectFilePath, onProjectOpened) {
- const {baseDir} = this.fairCopyApplication
+ const { baseDir } = this.fairCopyApplication
this.onProjectOpened = onProjectOpened
const debug = !app.isPackaged
- this.initProjectArchiveWorker( baseDir, debug, projectFilePath ).then(() => {
+ this.initProjectArchiveWorker(baseDir, debug, projectFilePath).then(() => {
this.projectArchiveWorker.postMessage({ messageType: 'open' })
})
}
loadProject(project) {
const { baseDir, config: fairCopyAppConfig } = this.fairCopyApplication
-
- if( !project ) {
+
+ if (!project) {
log.error('Attempted to open invalid project file.')
return
}
const teiSchema = fs.readFileSync(`${baseDir}/tei-simple.json`).toString('utf-8')
const baseConfigJSON = fs.readFileSync(`${baseDir}/faircopy-config.json`).toString('utf-8')
- if( !teiSchema || !baseConfigJSON ) {
+ if (!teiSchema || !baseConfigJSON) {
log.info('Application data is missing or corrupted.')
return
}
@@ -124,7 +131,7 @@ class ProjectStore {
let { fairCopyManifest, fairCopyConfig, idMap, projectFilePath } = project
- if( !fairCopyManifest || !fairCopyConfig || !idMap ) {
+ if (!fairCopyManifest || !fairCopyConfig || !idMap) {
log.info('Project file is missing required entries.')
return
}
@@ -133,7 +140,7 @@ class ProjectStore {
this.manifestData = fairCopyManifest
const currentVersion = this.fairCopyApplication.config.version
- if( app.isPackaged && !compatibleProject(this.manifestData, currentVersion) ) {
+ if (app.isPackaged && !compatibleProject(this.manifestData, currentVersion)) {
log.info('Project file is incompatible.')
const incompatInfo = { projectFilePath, projectFileVersion: this.manifestData.generatedWith }
this.fairCopyApplication.sendToMainWindow('projectIncompatible', incompatInfo)
@@ -142,68 +149,68 @@ class ProjectStore {
// migrate the manifestData to latest version
this.manifestData = migrateManifestData(this.manifestData)
-
+
// if elements changed in config, migrate project config
- migrateConfig(this.manifestData.generatedWith,this.baseConfig,fairCopyConfig, fairCopyAppConfig)
+ migrateConfig(this.manifestData.generatedWith, this.baseConfig, fairCopyConfig, fairCopyAppConfig)
this.migratedConfig = fairCopyConfig
// clean up the idMap if there are no resources
idMap = this.cleanUpIDMap(idMap)
// apply any migrations to ID Map data
- idMap = migrateIDMap(this.manifestData.generatedWith,idMap,this.manifestData.resources)
+ idMap = migrateIDMap(this.manifestData.generatedWith, idMap, this.manifestData.resources)
// setup search index if local
- if( !this.manifestData.remote ) {
- this.searchIndex = new SearchIndex( teiSchema, this, (status) => {
- this.fairCopyApplication.sendToMainWindow('searchSystemStatus', status )
- })
+ if (!this.manifestData.remote) {
+ this.searchIndex = new SearchIndex(teiSchema, this, (status) => {
+ this.fairCopyApplication.sendToMainWindow('searchSystemStatus', status)
+ })
}
const projectData = { projectFilePath, fairCopyManifest: this.manifestData, teiSchema, fairCopyConfig, baseConfig: this.baseConfig, idMap }
- this.onProjectOpened( projectData )
+ this.onProjectOpened(projectData)
}
// clean up the idMap if there are no resources
cleanUpIDMap(idMap) {
const { resources } = this.manifestData
- if( Object.values(resources).length === 0 ) {
+ if (Object.values(resources).length === 0) {
return "{}"
} else {
return idMap
}
}
-
- async importStart(paths,options) {
+
+ async importStart(paths, options) {
this.importRunning(true)
const importList = []
- for( const path of paths ) {
+ for (const path of paths) {
const stats = await stat(path)
- if( stats.size <= maxImportFileSize ) {
- const data = await readFile(path, { encoding: 'utf-8'} )
- importList.push({ path, data, options })
+ if (stats.size <= maxImportFileSize) {
+ const data = await readFile(path, { encoding: 'utf-8' })
+ importList.push({ path, data, options })
} else {
- importList.push({ path, error: 'too-big'})
+ importList.push({ path, error: 'too-big' })
}
}
const importData = { command: 'start', importList }
- this.fairCopyApplication.sendToMainWindow('importData', importData )
+ this.fairCopyApplication.sendToMainWindow('importData', importData)
this.importContinue()
}
- importIIIFStart( importList ) {
+ importIIIFStart(importList) {
this.importRunning(true)
const importData = { command: 'start', importList }
- this.fairCopyApplication.sendToMainWindow('importData', importData )
+ this.fairCopyApplication.sendToMainWindow('importData', importData)
this.importContinue()
}
importContinue() {
- this.fairCopyApplication.sendToMainWindow('importData', { command: 'next' } )
+ this.fairCopyApplication.sendToMainWindow('importData', { command: 'next' })
}
importError(errorMessage) {
- this.fairCopyApplication.sendToMainWindow('importData', { command: 'error', errorMessage } )
+ this.fairCopyApplication.sendToMainWindow('importData', { command: 'error', errorMessage })
}
importEnd() {
@@ -216,10 +223,10 @@ class ProjectStore {
this.importInProgress = running
}
- requestExport(resourceEntries,path) {
+ requestExport(resourceEntries, path) {
const { resources: localEntries, remote, userID, serverURL, projectID } = this.manifestData
const projectData = { localEntries, remote, userID, serverURL, projectID }
- for( const resourceEntry of resourceEntries ) {
+ for (const resourceEntry of resourceEntries) {
this.projectArchiveWorker.postMessage({ messageType: 'request-export', resourceEntry, projectData, path })
}
}
@@ -232,92 +239,99 @@ class ProjectStore {
close() {
this.projectArchiveWorker.postMessage({ messageType: 'close' })
- if( this.searchIndex ) this.searchIndex.close()
+ if (this.searchIndex) this.searchIndex.close()
}
saveResource(resourceEntry, resourceData, idMap) {
const { id, type } = resourceEntry
this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
this.projectArchiveWorker.postMessage({ messageType: 'write-resource', resourceID: id, data: resourceData })
- if( this.searchIndex ) this.searchIndex.indexResource(id, type, resourceData )
+ if (this.searchIndex) this.searchIndex.indexResource(id, type, resourceData)
this.save()
}
-
- requestEditionCrafterData( url ) {
+
+ saveAnnotationData(standoffEntry, data) {
+ if (standoffEntry.local) {
+ this.projectArchiveWorker.postMessage({ messageType: 'save-annotation-data', resourceID: standoffEntry.id, data: data })
+ this.save()
+ }
+ }
+
+ requestEditionCrafterData(url) {
return this.editionCrafterServer.processRequest(url)
}
- requestIndex( resourceID ) {
+ requestIndex(resourceID) {
this.projectArchiveWorker.postMessage({ messageType: 'request-index', resourceID })
}
- addResource( resourceEntry, resourceData, idMap ) {
+ addResource(resourceEntry, resourceData, idMap) {
this.manifestData.resources[resourceEntry.id] = resourceEntry
- if( resourceEntry.type === 'image' ) {
+ if (resourceEntry.type === 'image') {
this.projectArchiveWorker.postMessage({ messageType: 'add-local-file', resourceID: resourceEntry.id, localFilePath: resourceData })
} else {
- if( idMap ) {
+ if (idMap) {
this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
}
this.projectArchiveWorker.postMessage({ messageType: 'write-resource', resourceID: resourceEntry.id, data: resourceData })
- if( this.searchIndex ) this.searchIndex.indexResource(resourceEntry.id, resourceEntry.type, resourceData )
+ if (this.searchIndex) this.searchIndex.indexResource(resourceEntry.id, resourceEntry.type, resourceData)
}
- if(this.importInProgress) {
+ if (this.importInProgress) {
this.importContinue()
} else {
this.saveManifest()
this.fairCopyApplication.fairCopySession.requestResourceView()
}
}
-
- removeResources(resourceIDs,idMap) {
- for( const resourceID of resourceIDs ) {
+
+ removeResources(resourceIDs, idMap) {
+ for (const resourceID of resourceIDs) {
const resourceEntry = this.manifestData.resources[resourceID]
-
+
// go through the manifest entries, if there are local images associated with this facs, delete them too
- if( resourceEntry.type === 'facs' ) {
- for( const entry of Object.values(this.manifestData.resources) ) {
- const {id, type, localID} = entry
- if( type === 'image' && localID.startsWith(resourceID) ) {
- delete this.manifestData.resources[id]
- this.projectArchiveWorker.postMessage({ messageType: 'remove-file', fileID: id })
+ if (resourceEntry.type === 'facs') {
+ for (const entry of Object.values(this.manifestData.resources)) {
+ const { id, type, localID } = entry
+ if (type === 'image' && localID.startsWith(resourceID)) {
+ delete this.manifestData.resources[id]
+ this.projectArchiveWorker.postMessage({ messageType: 'remove-file', fileID: id })
log.info(`Removed image resource from project: ${id}`)
}
}
} else {
// remove associated search index
- if( this.searchIndex ) this.searchIndex.removeIndex(resourceID)
+ if (this.searchIndex) this.searchIndex.removeIndex(resourceID)
}
-
- if( resourceEntry.local ) {
- delete this.manifestData.resources[resourceID]
- if( resourceEntry.type !== 'teidoc' ) {
+
+ if (resourceEntry.local) {
+ delete this.manifestData.resources[resourceID]
+ if (resourceEntry.type !== 'teidoc') {
this.projectArchiveWorker.postMessage({ messageType: 'remove-file', fileID: resourceID })
}
} else {
resourceEntry.deleted = true
- this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry )
+ this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry)
}
-
+
log.info(`Removed resource from project: ${resourceID}`)
}
- if( idMap ) {
- this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
+ if (idMap) {
+ this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
}
this.saveManifest()
}
- recoverResources( resourceIDs, idMap ) {
- for( const resourceID of resourceIDs ) {
- const resourceEntry = this.manifestData.resources[resourceID]
+ recoverResources(resourceIDs, idMap) {
+ for (const resourceID of resourceIDs) {
+ const resourceEntry = this.manifestData.resources[resourceID]
- if( resourceEntry ) {
+ if (resourceEntry) {
resourceEntry.deleted = false
- this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry )
- }
+ this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry)
+ }
}
- this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
+ this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
this.saveManifest()
}
@@ -333,15 +347,15 @@ class ProjectStore {
this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
}
- saveFairCopyConfig( fairCopyConfig, lastAction=null ) {
+ saveFairCopyConfig(fairCopyConfig, lastAction = null) {
this.migratedConfig = null
- if( lastAction ) {
+ if (lastAction) {
this.manifestData.configLastAction = lastAction
}
const configData = JSON.stringify(fairCopyConfig)
- if( configData && configData.length > 0 ) {
+ if (configData && configData.length > 0) {
this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: configSettingsEntryName, data: configData })
- this.saveManifest()
+ this.saveManifest()
} else {
log.error(`Cannot save empty FairCopy Config.`)
}
@@ -349,20 +363,20 @@ class ProjectStore {
save() {
// if there was a migration that hasn't been saved yet, save it now
- if( this.migratedConfig ) {
- this.saveFairCopyConfig( this.migratedConfig )
+ if (this.migratedConfig) {
+ this.saveFairCopyConfig(this.migratedConfig)
} else {
this.projectArchiveWorker.postMessage({ messageType: 'save' })
}
}
- exportFairCopyConfig( exportPath, fairCopyConfigJSONCompact ) {
+ exportFairCopyConfig(exportPath, fairCopyConfigJSONCompact) {
try {
const fairCopyConfig = JSON.parse(fairCopyConfigJSONCompact)
const fairCopyConfigJSON = JSON.stringify(fairCopyConfig, null, '\t')
- fs.writeFileSync(exportPath,fairCopyConfigJSON)
+ fs.writeFileSync(exportPath, fairCopyConfigJSON)
log.info(`Exported project config to: ${exportPath}`)
- } catch(e) {
+ } catch (e) {
log.error(`Error exporting project config: ${e}`)
}
}
@@ -370,7 +384,7 @@ class ProjectStore {
updateProjectInfo(projectInfo) {
const { name, description, permissions } = projectInfo
this.manifestData.projectName = name
- this.manifestData.description = description
+ this.manifestData.description = description
this.manifestData.permissions = permissions
this.saveManifest()
}
@@ -379,98 +393,101 @@ class ProjectStore {
this.projectArchiveWorker.postMessage({ messageType: 'check-in', userID, serverURL, projectID, committedResources, message })
}
- checkOut(userID, serverURL, projectID, resourceEntries ) {
+ checkOut(userID, serverURL, projectID, resourceEntries) {
this.projectArchiveWorker.postMessage({ messageType: 'check-out', userID, serverURL, projectID, resourceEntries })
}
openImageResource(requestURL) {
- const resourceID = requestURL.replace(`local://`, '').replace('..','')
+ const resourceID = requestURL.replace(`local://`, '').replace('..', '')
const resourceEntry = this.manifestData.resources[resourceID]
- if( resourceEntry ) {
+ if (resourceEntry) {
// create a file path to the temp dir
const ext = getExtensionForMIMEType(resourceEntry.mimeType)
const fileName = `${resourceID}.${ext}`
this.projectArchiveWorker.postMessage({ messageType: 'cache-resource', resourceID, fileName })
- }
+ }
}
- openResource(resourceID,xmlID) {
+ openResource(resourceID, xmlID) {
const resourceEntry = this.manifestData.resources[resourceID]
- if( resourceEntry ) {
+ if (resourceEntry) {
this.projectArchiveWorker.postMessage({ messageType: 'read-resource', resourceID, xmlID })
}
}
- checkOutResults(resources,error) {
+ checkOutResults(resources, error) {
const checkOutStatus = []
- for( const resource of Object.values(resources) ) {
+ for (const resource of Object.values(resources)) {
const { state, resourceEntry, parentEntry, content } = resource
- if( state === 'success') {
+ if (state === 'success') {
this.manifestData.resources[resourceEntry.id] = resourceEntry
- this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry )
- if( parentEntry ) this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', parentEntry )
- this.fairCopyApplication.sendToAllWindows('resourceContentUpdated', { resourceID: resourceEntry.id, messageID: 'check-out-messsage', resourceContent: content })
- }
+ this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry)
+ if (parentEntry) this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', parentEntry)
+ this.fairCopyApplication.sendToAllWindows('resourceContentUpdated', { resourceID: resourceEntry.id, messageID: 'check-out-messsage', resourceContent: content })
+ }
checkOutStatus.push({ state, resourceEntry })
}
const idMap = this.fairCopyApplication.fairCopySession.idMapAuthority.checkOut(resources)
this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
- this.saveManifest()
+ this.saveManifest()
- this.fairCopyApplication.sendToMainWindow('checkOutResults', checkOutStatus, error )
+ this.fairCopyApplication.sendToMainWindow('checkOutResults', checkOutStatus, error)
this.fairCopyApplication.fairCopySession.requestResourceView()
}
checkInResults(resourceStatus, error) {
const { userID } = this.manifestData
const resourceEntries = []
- for( const resourceID of Object.keys(resourceStatus) ) {
+ for (const resourceID of Object.keys(resourceStatus)) {
const resourceEntry = this.manifestData.resources[resourceID]
- if( !error ) {
+ if (!error) {
// remove remote resources from project file and manifest, update all windows
- this.projectArchiveWorker.postMessage({ messageType: 'remove-file', fileID: resourceID })
+ this.projectArchiveWorker.postMessage({ messageType: 'remove-file', fileID: resourceID })
resourceEntry.local = false
resourceEntry.lastAction = { action_type: 'check_in', user: { id: userID } }
- this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry )
- delete this.manifestData.resources[resourceID]
+ this.fairCopyApplication.sendToAllWindows('resourceEntryUpdated', resourceEntry)
+ delete this.manifestData.resources[resourceID]
}
resourceEntries.push(resourceEntry)
}
- if( !error ) {
- const idMap = this.fairCopyApplication.fairCopySession.idMapAuthority.checkIn(resourceEntries)
- this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
+ if (!error) {
+ const idMap = this.fairCopyApplication.fairCopySession.idMapAuthority.checkIn(resourceEntries)
+ this.projectArchiveWorker.postMessage({ messageType: 'write-file', fileID: idMapEntryName, data: idMap })
this.saveManifest()
this.fairCopyApplication.fairCopySession.requestResourceView()
}
- this.fairCopyApplication.sendToMainWindow('checkInResults', {resourceEntries, resourceStatus, error} )
+ this.fairCopyApplication.sendToMainWindow('checkInResults', { resourceEntries, resourceStatus, error })
}
// send a list of the local resources to main window
sendLocalResources() {
- const resourceEntries = Object.values( this.manifestData.resources )
-
+ const resourceEntries = Object.values(this.manifestData.resources)
const localResources = {}
- for( const resourceEntry of resourceEntries ) {
- if( resourceEntry.type !== 'image') {
+ for (const resourceEntry of resourceEntries) {
+ if (resourceEntry.type !== 'image') {
localResources[resourceEntry.id] = resourceEntry
}
+
+ if (resourceEntry.type === 'standOff') {
+ this.projectArchiveWorker.postMessage({ messageType: 'parse-standoff', resourceID: resourceEntry.id, parentResourceID: resourceEntry.parentResource })
+ }
}
- this.fairCopyApplication.sendToAllWindows('localResources', localResources )
+ this.fairCopyApplication.sendToAllWindows('localResources', localResources)
}
}
-function getExtensionForMIMEType( mimeType ) {
- switch(mimeType) {
+function getExtensionForMIMEType(mimeType) {
+ switch (mimeType) {
case 'image/png':
return 'png'
case 'image/jpeg':
return 'jpg'
case 'image/gif':
- return 'gif'
+ return 'gif'
default:
throw new Error(`Unknown MIMEType: ${mimeType}`)
- }
+ }
}
exports.ProjectStore = ProjectStore
diff --git a/src/main-process/RemoteProject.js b/src/main-process/RemoteProject.js
index 448ce051..1fff8f75 100644
--- a/src/main-process/RemoteProject.js
+++ b/src/main-process/RemoteProject.js
@@ -4,83 +4,89 @@ const { app } = require('electron')
class RemoteProject {
- constructor( fairCopySession, userID, serverURL, projectID ) {
+ constructor(fairCopySession, userID, serverURL, projectID) {
const { fairCopyApplication } = fairCopySession
- const {baseDir} = fairCopyApplication
+ const { baseDir } = fairCopyApplication
this.fairCopySession = fairCopySession
- this.initRemoteProjectWorker( baseDir, !app.isPackaged, userID, serverURL, projectID ).then(() => {
+ this.initRemoteProjectWorker(baseDir, !app.isPackaged, userID, serverURL, projectID).then(() => {
this.open()
})
}
- initRemoteProjectWorker( baseDir, debug, userID, serverURL, projectID ) {
- this.remoteProjectWorker = new WorkerWindow( baseDir, debug, 'remote-project', (msg) => {
+ initRemoteProjectWorker(baseDir, debug, userID, serverURL, projectID) {
+ this.remoteProjectWorker = new WorkerWindow(baseDir, debug, 'remote-project', (msg) => {
const { messageType } = msg
- switch( messageType ) {
+ switch (messageType) {
case 'resource-data':
{
const { resourceEntry, parentEntry, content, xmlID } = msg
- this.fairCopySession.resourceOpened( resourceEntry, parentEntry, content, xmlID )
+ this.fairCopySession.resourceOpened(resourceEntry, parentEntry, content, xmlID)
}
break
case 'got-parent':
- {
+ {
const { resourceEntry, parentEntry, content, xmlID } = msg
- this.fairCopySession.parentFound( resourceEntry, parentEntry, content, xmlID )
+ this.fairCopySession.parentFound(resourceEntry, parentEntry, content, xmlID)
}
break
case 'project-info-update':
{
const { projectInfo } = msg
const { fairCopyApplication } = this.fairCopySession
- fairCopyApplication.sendToAllWindows('updateProjectInfo', projectInfo )
+ fairCopyApplication.sendToAllWindows('updateProjectInfo', projectInfo)
}
break
case 'resource-view-update':
{
const { resourceView, remoteResources } = msg
- this.fairCopySession.sendResourceViewUpdate(resourceView,remoteResources)
+ this.fairCopySession.sendResourceViewUpdate(resourceView, remoteResources)
+ }
+ break
+ case 'parsed-annotation-data':
+ {
+ const { data, parentResourceID } = msg
+ this.fairCopySession.annotationDataReceived(data, parentResourceID)
}
- break
- case 'id-map-update':
- {
- const { idMapAuthority } = this.fairCopySession
- const { idMapData } = msg
- idMapAuthority.setBaseMap(idMapData)
- }
- break
+ break
+ case 'id-map-update':
+ {
+ const { idMapAuthority } = this.fairCopySession
+ const { idMapData } = msg
+ idMapAuthority.setBaseMap(idMapData)
+ }
+ break
case 'config-update':
- {
- const { config, configLastAction } = msg
- const { fairCopyApplication, projectStore } = this.fairCopySession
- const { baseConfig } = projectStore
- const { config: appConfig } = fairCopyApplication
- const { generatedWith } = projectStore.manifestData
- // make sure that the incoming config is migrated to the latest schema
- migrateConfig(generatedWith, baseConfig, config, appConfig )
- fairCopyApplication.sendToAllWindows('updateFairCopyConfig', {config, configLastAction} )
- }
- break
+ {
+ const { config, configLastAction } = msg
+ const { fairCopyApplication, projectStore } = this.fairCopySession
+ const { baseConfig } = projectStore
+ const { config: appConfig } = fairCopyApplication
+ const { generatedWith } = projectStore.manifestData
+ // make sure that the incoming config is migrated to the latest schema
+ migrateConfig(generatedWith, baseConfig, config, appConfig)
+ fairCopyApplication.sendToAllWindows('updateFairCopyConfig', { config, configLastAction })
+ }
+ break
case 'config-check-out-result':
const { status } = msg
const { fairCopyApplication } = this.fairCopySession
- fairCopyApplication.sendToAllWindows('fairCopyConfigCheckedOut', {status} )
- break
+ fairCopyApplication.sendToAllWindows('fairCopyConfigCheckedOut', { status })
+ break
case 'resources-updated':
- {
- const { resources } = msg
- // TODO determine which resources need to be updated
- // TODO what does this have to do with delete again?
- this.fairCopySession.requestResourceView()
- }
- break
+ {
+ const { resources } = msg
+ // TODO determine which resources need to be updated
+ // TODO what does this have to do with delete again?
+ this.fairCopySession.requestResourceView()
+ }
+ break
default:
throw new Error(`Unrecognized message type ${messageType} received from remote project: ${JSON.stringify(msg)}`)
}
})
-
- return this.remoteProjectWorker.start({userID, serverURL, projectID})
+
+ return this.remoteProjectWorker.start({ userID, serverURL, projectID })
}
open() {
diff --git a/src/main-process/WorkerWindow.js b/src/main-process/WorkerWindow.js
index c4e121bb..dfd7e8dd 100644
--- a/src/main-process/WorkerWindow.js
+++ b/src/main-process/WorkerWindow.js
@@ -9,16 +9,16 @@ class WorkerWindow {
this.messageHandler = messageHandler
}
- messageForwarder = (e,wid,msg) => {
+ messageForwarder = (e, wid, msg) => {
// if this message is for this worker, send to callback
- if( wid === this.workerID ) this.messageHandler(msg)
+ if (wid === this.workerID) this.messageHandler(msg)
}
- closeMessageHandler = (e,wid) => {
- if( wid === this.workerID ) this.close()
+ closeMessageHandler = (e, wid) => {
+ if (wid === this.workerID) this.close()
}
- async start( workerData ) {
+ async start(workerData) {
ipcMain.on('worker-window-message', this.messageForwarder)
ipcMain.on('close-worker-window', this.closeMessageHandler)
const webpackPreloadPath = getWebpackPreload('worker_window')
@@ -31,28 +31,28 @@ class WorkerWindow {
contextIsolation: false,
}
})
- if( this.debug )
- this.workerWindow.webContents.openDevTools({ mode: 'bottom'} )
+ if (this.debug)
+ this.workerWindow.webContents.openDevTools({ mode: 'bottom' })
this.workerWindow.on('closed', () => {
this.workerWindow = null
ipcMain.removeListener('worker-window-message', this.messageForwarder)
ipcMain.removeListener('close-worker-window', this.closeMessageHandler)
})
-
+
const webpackEntryURL = getWebpackEntry('worker_window')
await this.workerWindow.loadURL(webpackEntryURL);
- this.workerWindow.webContents.send('init', { workerID: this.workerID, workerData })
+ this.workerWindow.webContents.send('init', { workerID: this.workerID, workerData })
}
postMessage(messageData) {
- if( this.workerWindow && !this.workerWindow.isDestroyed() ) {
+ if (this.workerWindow && !this.workerWindow.isDestroyed()) {
this.workerWindow.webContents.send('message', messageData)
}
}
close() {
- if( this.workerWindow && !this.workerWindow.isDestroyed() ) {
+ if (this.workerWindow && !this.workerWindow.isDestroyed()) {
this.workerWindow.destroy()
}
}
diff --git a/src/render/components/main-window/MainWindow.jsx b/src/render/components/main-window/MainWindow.jsx
index 53c0a373..b5783ba2 100644
--- a/src/render/components/main-window/MainWindow.jsx
+++ b/src/render/components/main-window/MainWindow.jsx
@@ -1,5 +1,5 @@
import React, { Component } from 'react'
-import { debounce } from "debounce";
+import { debounce } from 'debounce'
import ProjectSidebar from './ProjectSidebar'
import AlertDialog from './dialogs/AlertDialog'
@@ -21,12 +21,16 @@ import ReleaseNotesDialog from './dialogs/ReleaseNotesDialog'
import EditorDraggingElement from './tei-editor/EditorDraggingElement'
import ImportTextsDialog from './dialogs/ImportTextsDialog'
import ImportConsoleDialog from './dialogs/ImportConsoleDialog'
-import { highlightSearchResults, scrollToSearchResult, searchResource } from '../../model/search'
+import {
+ highlightSearchResults,
+ scrollToSearchResult,
+ searchResource,
+} from '../../model/search'
import SearchDialog from './dialogs/SearchDialog'
import CheckInDialog from './dialogs/CheckInDialog'
import CheckOutDialog from './dialogs/CheckOutDialog'
import { bigRingSpinner } from '../common/ring-spinner'
-import {SplitPaneView} from '../common/SplitPaneView'
+import { SplitPaneView } from '../common/SplitPaneView'
const fairCopy = window.fairCopy
@@ -36,1034 +40,1467 @@ const resizeRefreshRate = 100
const resetSearchRate = 100
const initialRowsPerPage = 50
-const closePopUpState = { popupMenuOptions: null, popupMenuAnchorEl: null, popupMenuPlacement: null }
+const closePopUpState = {
+ popupMenuOptions: null,
+ popupMenuAnchorEl: null,
+ popupMenuPlacement: null,
+}
export default class MainWindow extends Component {
-
- constructor() {
- super()
-
- this.filterInitialState = { orderBy: 'name', order: 'ascending', nameFilter: null, rowsPerPage: initialRowsPerPage }
- this.state = {
- selectedResource: null,
- openResources: {},
- resourceViews: {
- currentView: 'home',
- remote: {
- indexParentID: null,
- parentEntry: null,
- totalRows: 0,
- currentPage: 1,
- ...this.filterInitialState,
- loading: true
- },
- home: {
- indexParentID: null,
- parentEntry: null,
- totalRows: 0,
- currentPage: 1,
- ...this.filterInitialState,
- loading: true
- }
- },
- allResourcesCheckmarked: false,
- resourceCheckmarks: {},
- resourceIndex: [],
- requestedResources: [],
- localResources: [],
- resourceBrowserOpen: true,
- alertDialogMode: 'closed',
- alertOptions: null,
- exitOnClose: false,
- editDialogMode: false,
- addImagesMode: false,
- releaseNotesMode: false,
- loginMode: false,
- draggingElementActive: false,
- dragInfo: null,
- editSurfaceInfoMode: false,
- moveResourceMode: false,
- checkInMode: false,
- checkInResources: [],
- editTEIDocDialogMode: false,
- moveResources: null,
- surfaceInfo: null,
- popupMenuOptions: null,
- popupMenuAnchorEl: null,
- popupMenuPlacement: null,
- alertMessage: null,
- iiifDialogMode: false,
- textImportDialogMode: false,
- checkOutMode: false,
- checkOutStatus: null,
- checkOutError: null,
- searchQuery: null,
- searchResults: {},
- searchFilterOptions: { active: false, elementName: '', attrQs: []},
- searchSelectionIndex: 0,
- searchFilterMode: false,
- searchEnabled: false,
- searchScope: 'file',
- showSearchBar: false,
- leftPaneWidth: initialLeftPaneWidth,
- rightPaneWidth: initialRightPaneWidth
- }
+ constructor() {
+ super()
+
+ this.filterInitialState = {
+ orderBy: 'name',
+ order: 'ascending',
+ nameFilter: null,
+ rowsPerPage: initialRowsPerPage,
}
-
- onResourceOpened = (event, resourceData) => {
- const { fairCopyProject } = this.props
- const { openResources, requestedResources } = this.state
- const { resourceEntry, parentEntry, resource } = resourceData
-
- // if this is a resource we asked for, then open it.
- // (resources can also be asked for by FacsDocument, maybe others in future)
- if( requestedResources.includes( resourceEntry.id ) ) {
- const nextRequestedResources = requestedResources.filter( r => r === resourceEntry.id )
- const doc = fairCopyProject.onResourceOpened(resourceEntry, parentEntry, resource)
- if( doc ) {
- const nextOpenResources = { ...openResources }
- nextOpenResources[resourceEntry.id] = doc
- this.setState( {
- ...this.state,
- openResources: nextOpenResources,
- requestedResources: nextRequestedResources
- })
- } else {
- this.setState( {
- ...this.state,
- requestedResources: nextRequestedResources
- })
- }
- }
+ this.state = {
+ selectedResource: null,
+ openResources: {},
+ resourceViews: {
+ currentView: 'home',
+ remote: {
+ indexParentID: null,
+ parentEntry: null,
+ totalRows: 0,
+ currentPage: 1,
+ ...this.filterInitialState,
+ loading: true,
+ },
+ home: {
+ indexParentID: null,
+ parentEntry: null,
+ totalRows: 0,
+ currentPage: 1,
+ ...this.filterInitialState,
+ loading: true,
+ },
+ },
+ allResourcesCheckmarked: false,
+ resourceCheckmarks: {},
+ resourceIndex: [],
+ requestedResources: [],
+ localResources: [],
+ annotationData: {},
+ resourceBrowserOpen: true,
+ alertDialogMode: 'closed',
+ alertOptions: null,
+ exitOnClose: false,
+ editDialogMode: false,
+ addImagesMode: false,
+ releaseNotesMode: false,
+ loginMode: false,
+ draggingElementActive: false,
+ dragInfo: null,
+ editSurfaceInfoMode: false,
+ moveResourceMode: false,
+ checkInMode: false,
+ checkInResources: [],
+ editTEIDocDialogMode: false,
+ moveResources: null,
+ surfaceInfo: null,
+ popupMenuOptions: null,
+ popupMenuAnchorEl: null,
+ popupMenuPlacement: null,
+ alertMessage: null,
+ iiifDialogMode: false,
+ textImportDialogMode: false,
+ checkOutMode: false,
+ checkOutStatus: null,
+ checkOutError: null,
+ searchQuery: null,
+ searchResults: {},
+ searchFilterOptions: { active: false, elementName: '', attrQs: [] },
+ searchSelectionIndex: 0,
+ searchFilterMode: false,
+ searchEnabled: false,
+ searchScope: 'file',
+ showSearchBar: false,
+ leftPaneWidth: initialLeftPaneWidth,
+ rightPaneWidth: initialRightPaneWidth,
}
-
- onRequestExitApp = () => this.requestExitApp()
- onSearchSystemStatus = (event, status ) => {
- if( status !== this.state.searchEnabled ) {
- this.setState({...this.state, searchEnabled: status })
- }
+ }
+
+ onResourceOpened = (event, resourceData) => {
+ const { fairCopyProject } = this.props
+ const { openResources, requestedResources, annotationData } = this.state
+ const { resourceEntry, parentEntry, resource } = resourceData
+
+ // if this is a resource we asked for, then open it.
+ // (resources can also be asked for by FacsDocument, maybe others in future)
+ if (requestedResources.includes(resourceEntry.id)) {
+ const nextRequestedResources = requestedResources.filter(
+ (r) => r === resourceEntry.id
+ )
+ const doc = fairCopyProject.onResourceOpened(
+ resourceEntry,
+ parentEntry,
+ resource,
+ annotationData[parentEntry.id]
+ )
+ if (doc) {
+ const nextOpenResources = { ...openResources }
+ nextOpenResources[resourceEntry.id] = doc
+ this.setState({
+ ...this.state,
+ openResources: nextOpenResources,
+ requestedResources: nextRequestedResources,
+ })
+ } else {
+ this.setState({
+ ...this.state,
+ requestedResources: nextRequestedResources,
+ })
+ }
}
-
- setResourceCheckmark = (resourceEntry, checked, stateUpdate=true) => {
- const { resourceCheckmarks } = this.state
- const nextCheckmarks = { ...resourceCheckmarks }
+ }
+
+ onAnnotationDataReceived = (event, info) => {
+ const { annotationData: data, parentResourceID } = info
+ const annotationData = { ...this.state.annotationData }
+ annotationData[parentResourceID] = data
+ this.setState({
+ ...this.state,
+ annotationData,
+ })
+ }
+
+ onRequestExitApp = () => this.requestExitApp()
+ onSearchSystemStatus = (event, status) => {
+ if (status !== this.state.searchEnabled) {
+ this.setState({ ...this.state, searchEnabled: status })
+ }
+ }
+
+ setResourceCheckmark = (resourceEntry, checked, stateUpdate = true) => {
+ const { resourceCheckmarks } = this.state
+ const nextCheckmarks = { ...resourceCheckmarks }
+ nextCheckmarks[resourceEntry.id] = checked ? resourceEntry : null
+ if (stateUpdate)
+ this.setState({ ...this.state, resourceCheckmarks: nextCheckmarks })
+ return nextCheckmarks
+ }
+
+ setAllCheckmarks = (checked, stateUpdate = true) => {
+ const { resourceCheckmarks, resourceIndex } = this.state
+ const nextCheckmarks = { ...resourceCheckmarks }
+ for (const resourceEntry of resourceIndex) {
+ if (resourceEntry.type !== 'header') {
nextCheckmarks[resourceEntry.id] = checked ? resourceEntry : null
- if(stateUpdate) this.setState({...this.state, resourceCheckmarks: nextCheckmarks })
- return nextCheckmarks
+ }
}
-
- setAllCheckmarks = ( checked, stateUpdate=true ) => {
- const { resourceCheckmarks, resourceIndex } = this.state
- const nextCheckmarks = { ...resourceCheckmarks }
- for( const resourceEntry of resourceIndex ) {
- if( resourceEntry.type !== 'header' ) {
- nextCheckmarks[resourceEntry.id] = checked ? resourceEntry : null
- }
- }
- if(stateUpdate) this.setState({...this.state, resourceCheckmarks: nextCheckmarks, allResourcesCheckmarked: checked })
- else return { resourceCheckmarks: nextCheckmarks, allResourcesCheckmarked: checked }
+ if (stateUpdate)
+ this.setState({
+ ...this.state,
+ resourceCheckmarks: nextCheckmarks,
+ allResourcesCheckmarked: checked,
+ })
+ else
+ return {
+ resourceCheckmarks: nextCheckmarks,
+ allResourcesCheckmarked: checked,
+ }
+ }
+
+ onResourceViewUpdate = (event, resourceData) => {
+ const { resourceViews } = this.state
+ const { currentView } = resourceViews
+ const resourceView = resourceViews[currentView]
+ const {
+ resourceViews: nextResourceViews,
+ resourceIndex: nextResourceIndex,
+ } = resourceData
+ const { currentView: nextCurrentView } = nextResourceViews
+ const nextResourceView = nextResourceViews[nextCurrentView]
+
+ // if the indexParentID or the currentView changed, clear checkmarks
+ if (
+ currentView !== nextCurrentView ||
+ resourceView.indexParentID !== nextResourceView.indexParentID
+ ) {
+ const checkmarkState = this.setAllCheckmarks(false, false)
+ this.setState({
+ ...this.state,
+ ...checkmarkState,
+ resourceViews: nextResourceViews,
+ resourceIndex: nextResourceIndex,
+ })
+ } else {
+ this.setState({
+ ...this.state,
+ resourceViews: nextResourceViews,
+ resourceIndex: nextResourceIndex,
+ })
}
-
- onResourceViewUpdate = (event, resourceData ) => {
- const { resourceViews } = this.state
- const { currentView } = resourceViews
- const resourceView = resourceViews[currentView]
- const { resourceViews: nextResourceViews, resourceIndex: nextResourceIndex } = resourceData
- const { currentView: nextCurrentView } = nextResourceViews
- const nextResourceView = nextResourceViews[nextCurrentView]
-
- // if the indexParentID or the currentView changed, clear checkmarks
- if( currentView !== nextCurrentView || resourceView.indexParentID !== nextResourceView.indexParentID ) {
- const checkmarkState = this.setAllCheckmarks(false,false)
- this.setState({...this.state, ...checkmarkState, resourceViews: nextResourceViews, resourceIndex: nextResourceIndex })
- } else {
- this.setState({...this.state, resourceViews: nextResourceViews, resourceIndex: nextResourceIndex })
- }
+ }
+
+ onCheckOutResults = (event, checkOutStatus, checkOutError) => {
+ this.setState({
+ ...this.state,
+ checkOutMode: true,
+ checkOutStatus,
+ checkOutError,
+ })
+ }
+
+ onResourceEntryUpdated = (e, resourceEntry) => {
+ const { fairCopyProject } = this.props
+ fairCopyProject.notifyListeners('resourceEntryUpdated', resourceEntry)
+ this.refreshWindow()
+ }
+
+ onResourceContentUpdated = (e, resourceUpdate) => {
+ const { fairCopyProject } = this.props
+ fairCopyProject.notifyListeners('resourceContentUpdated', resourceUpdate)
+ this.refreshWindow()
+ }
+
+ onUpdateProjectInfo = (e, projectInfo) => {
+ const { fairCopyProject } = this.props
+ fairCopyProject.updateProjectInfo(projectInfo)
+ }
+
+ componentDidMount() {
+ fairCopy.ipcRegisterCallback('resourceOpened', this.onResourceOpened)
+ fairCopy.ipcRegisterCallback(
+ 'resourceViewUpdate',
+ this.onResourceViewUpdate
+ )
+ fairCopy.ipcRegisterCallback('requestExitApp', this.onRequestExitApp)
+ fairCopy.ipcRegisterCallback(
+ 'searchSystemStatus',
+ this.onSearchSystemStatus
+ )
+ fairCopy.ipcRegisterCallback('checkOutResults', this.onCheckOutResults)
+ fairCopy.ipcRegisterCallback(
+ 'resourceEntryUpdated',
+ this.onResourceEntryUpdated
+ )
+ fairCopy.ipcRegisterCallback(
+ 'resourceContentUpdated',
+ this.onResourceContentUpdated
+ )
+ fairCopy.ipcRegisterCallback('updateProjectInfo', this.onUpdateProjectInfo)
+ fairCopy.ipcRegisterCallback('localResources', this.onLocalResources)
+ fairCopy.ipcRegisterCallback(
+ 'annotationDataReceived',
+ this.onAnnotationDataReceived
+ )
+ }
+
+ componentWillUnmount() {
+ fairCopy.ipcRemoveListener('resourceOpened', this.onResourceOpened)
+ fairCopy.ipcRemoveListener('resourceViewUpdate', this.onResourceViewUpdate)
+ fairCopy.ipcRemoveListener('requestExitApp', this.onRequestExitApp)
+ fairCopy.ipcRemoveListener('searchSystemStatus', this.onSearchSystemStatus)
+ fairCopy.ipcRemoveListener('checkOutResults', this.onCheckOutResults)
+ fairCopy.ipcRemoveListener(
+ 'resourceEntryUpdated',
+ this.onResourceEntryUpdated
+ )
+ fairCopy.ipcRemoveListener(
+ 'resourceContentUpdated',
+ this.onResourceContentUpdated
+ )
+ fairCopy.ipcRemoveListener('updateProjectInfo', this.onUpdateProjectInfo)
+ fairCopy.ipcRemoveListener('localResources', this.onLocalResources)
+ }
+
+ refreshWindow() {
+ const { selectedResource, openResources } = this.state
+
+ if (selectedResource) {
+ const resource = openResources[selectedResource]
+ if (resource instanceof TEIDocument && resource.editorView) {
+ this.setState({ ...this.state })
+ }
}
-
- onCheckOutResults = (event, checkOutStatus, checkOutError ) => {
- this.setState({ ...this.state, checkOutMode: true, checkOutStatus, checkOutError })
+ }
+
+ requestExitApp = () => {
+ const { openResources } = this.state
+ const resourceIDs = Object.keys(openResources)
+ if (resourceIDs.length > 0) {
+ this.closeResources(resourceIDs, true)
+ } else {
+ fairCopy.ipcSend('exitApp')
}
+ }
- onResourceEntryUpdated = (e, resourceEntry) => {
- const { fairCopyProject } = this.props
- fairCopyProject.notifyListeners( 'resourceEntryUpdated', resourceEntry)
- this.refreshWindow()
+ isResourceOpen(resourceEntries) {
+ const { openResources } = this.state
+ for (const resourceEntry of resourceEntries) {
+ if (openResources[resourceEntry.id]) {
+ return true
+ }
}
-
- onResourceContentUpdated = (e, resourceUpdate) => {
- const { fairCopyProject } = this.props
- fairCopyProject.notifyListeners( 'resourceContentUpdated', resourceUpdate)
- this.refreshWindow()
+ return false
+ }
+
+ selectTEIDoc(teiDocID) {
+ const { resourceViews, resourceIndex } = this.state
+ const { currentView } = resourceViews
+ const currentParentEntry = resourceViews[currentView].parentEntry
+
+ let teiDocEntry
+ if (currentParentEntry && teiDocID === currentParentEntry.id) {
+ teiDocEntry = currentParentEntry
+ } else {
+ teiDocEntry = resourceIndex.find(
+ (resourceEntry) =>
+ resourceEntry.id === teiDocID && resourceEntry.type === 'teidoc'
+ )
}
-
- onUpdateProjectInfo = ( e, projectInfo ) => {
- const { fairCopyProject } = this.props
- fairCopyProject.updateProjectInfo( projectInfo )
+ if (!teiDocEntry) return false
+
+ const indexParentID = teiDocEntry.id
+ const parentEntry = teiDocEntry
+ const currentPage = 1
+ const resourceViewRequest = {
+ currentView,
+ indexParentID,
+ parentEntry,
+ currentPage,
+ ...this.filterInitialState,
}
-
- componentDidMount() {
- fairCopy.ipcRegisterCallback('resourceOpened', this.onResourceOpened )
- fairCopy.ipcRegisterCallback('resourceViewUpdate', this.onResourceViewUpdate )
- fairCopy.ipcRegisterCallback('requestExitApp', this.onRequestExitApp )
- fairCopy.ipcRegisterCallback('searchSystemStatus', this.onSearchSystemStatus )
- fairCopy.ipcRegisterCallback('checkOutResults', this.onCheckOutResults )
- fairCopy.ipcRegisterCallback('resourceEntryUpdated', this.onResourceEntryUpdated )
- fairCopy.ipcRegisterCallback('resourceContentUpdated', this.onResourceContentUpdated )
- fairCopy.ipcRegisterCallback('updateProjectInfo', this.onUpdateProjectInfo )
- fairCopy.ipcRegisterCallback('localResources', this.onLocalResources )
+ fairCopy.ipcSend('requestResourceView', resourceViewRequest)
+
+ const nextResourceViews = { ...resourceViews }
+ const currentResourceView = resourceViews[currentView]
+ nextResourceViews[currentView] = {
+ ...currentResourceView,
+ indexParentID,
+ parentEntry,
+ currentPage,
+ loading: true,
}
- componentWillUnmount() {
- fairCopy.ipcRemoveListener('resourceOpened', this.onResourceOpened )
- fairCopy.ipcRemoveListener('resourceViewUpdate', this.onResourceViewUpdate )
- fairCopy.ipcRemoveListener('requestExitApp', this.onRequestExitApp )
- fairCopy.ipcRemoveListener('searchSystemStatus', this.onSearchSystemStatus )
- fairCopy.ipcRemoveListener('checkOutResults', this.onCheckOutResults )
- fairCopy.ipcRemoveListener('resourceEntryUpdated', this.onResourceEntryUpdated )
- fairCopy.ipcRemoveListener('resourceContentUpdated', this.onResourceContentUpdated )
- fairCopy.ipcRemoveListener('updateProjectInfo', this.onUpdateProjectInfo )
- fairCopy.ipcRemoveListener('localResources', this.onLocalResources )
+ const nextResourceIndex = currentView === 'home' ? resourceIndex : []
+
+ const checkmarkState = this.setAllCheckmarks(false, false)
+ const nextState = { ...this.state, ...checkmarkState }
+
+ this.setState({
+ ...nextState,
+ selectedResource: null,
+ resourceBrowserOpen: true,
+ resourceViews: nextResourceViews,
+ resourceIndex: nextResourceIndex,
+ ...closePopUpState,
+ })
+ return true
+ }
+
+ selectResources(resourceIDs) {
+ const { fairCopyProject } = this.props
+ const {
+ openResources,
+ selectedResource,
+ requestedResources,
+ resourceIndex,
+ } = this.state
+
+ let nextSelection = resourceIDs[0]
+
+ if (this.selectTEIDoc(nextSelection)) {
+ return
}
-
- refreshWindow() {
- const { selectedResource, openResources } = this.state
-
- if( selectedResource ) {
- const resource = openResources[selectedResource]
- if( resource instanceof TEIDocument && resource.editorView ) {
- this.setState({...this.state})
- }
- }
+ let change = selectedResource !== nextSelection
+ const nextRequestedResources = [...requestedResources]
+
+ for (const resourceID of resourceIDs) {
+ if (!openResources[resourceID]) {
+ nextRequestedResources.push(resourceID)
+ fairCopyProject.openResource(resourceID)
+ }
}
- requestExitApp = () => {
- const { openResources } = this.state
- const resourceIDs = Object.keys( openResources )
- if( resourceIDs.length > 0 ) {
- this.closeResources( resourceIDs, true)
- } else {
- fairCopy.ipcSend('exitApp')
- }
+ if (change) {
+ this.setState({
+ ...this.state,
+ selectedResource: nextSelection,
+ resourceBrowserOpen: false,
+ requestedResources: nextRequestedResources,
+ currentSubmenuID: 0,
+ searchSelectionIndex: 0,
+ popupMenuOptions: null,
+ popupMenuAnchorEl: null,
+ popupMenuPlacement: null,
+ })
+ const nextResource = openResources[nextSelection]
+ if (nextResource instanceof TEIDocument) {
+ const { searchQuery, searchResults } = this.state
+ this.refreshWhenReady(searchQuery, searchResults, false)
+ }
+ } else {
+ this.setState({
+ ...this.state,
+ resourceBrowserOpen: false,
+ requestedResources: nextRequestedResources,
+ popupMenuOptions: null,
+ popupMenuAnchorEl: null,
+ popupMenuPlacement: null,
+ })
}
-
- isResourceOpen( resourceEntries ) {
- const { openResources } = this.state
- for( const resourceEntry of resourceEntries ) {
- if( openResources[resourceEntry.id] ) {
- return true
- }
+ }
+
+ closeResources = (resourceIDs, exitOnClose = false, promptSave = true) => {
+ const { fairCopyProject } = this.props
+ const { openResources, selectedResource, resourceBrowserOpen } = this.state
+
+ if (promptSave) {
+ for (const resourceID of resourceIDs) {
+ const resource = openResources[resourceID]
+ if (resource && resource.changedSinceLastSave) {
+ const alertOptions = {
+ resource,
+ resourceIDs,
+ }
+ this.setState({
+ ...this.state,
+ exitOnClose,
+ alertDialogMode: 'confirmSave',
+ alertOptions,
+ })
+ return
}
- return false
+ }
}
- selectTEIDoc( teiDocID ) {
- const {resourceViews, resourceIndex} = this.state
- const {currentView} = resourceViews
- const currentParentEntry = resourceViews[currentView].parentEntry
-
- let teiDocEntry
- if( currentParentEntry && teiDocID === currentParentEntry.id ) {
- teiDocEntry = currentParentEntry
- } else {
- teiDocEntry = resourceIndex.find(resourceEntry => resourceEntry.id === teiDocID && resourceEntry.type === 'teidoc')
- }
- if(!teiDocEntry) return false
-
- const indexParentID = teiDocEntry.id
- const parentEntry = teiDocEntry
- const currentPage = 1
- const resourceViewRequest = { currentView, indexParentID, parentEntry, currentPage, ...this.filterInitialState }
- fairCopy.ipcSend('requestResourceView', resourceViewRequest )
-
- const nextResourceViews = { ...resourceViews }
- const currentResourceView = resourceViews[currentView]
- nextResourceViews[currentView] = { ...currentResourceView, indexParentID, parentEntry, currentPage, loading: true }
-
- const nextResourceIndex = currentView === 'home' ? resourceIndex : []
-
- const checkmarkState = this.setAllCheckmarks(false,false)
- const nextState = { ...this.state, ...checkmarkState }
-
- this.setState({
- ...nextState,
- selectedResource: null,
- resourceBrowserOpen: true,
- resourceViews: nextResourceViews,
- resourceIndex: nextResourceIndex,
- ...closePopUpState
- })
- return true
+ let nextResourceArr = []
+ for (const openResourceID of Object.keys(openResources)) {
+ if (!resourceIDs.find((id) => id === openResourceID)) {
+ // this id is not on the close list
+ nextResourceArr.push(openResources[openResourceID])
+ } else {
+ // closing this resource
+ fairCopyProject.onResourceClosed(openResources[openResourceID])
+ }
}
- selectResources(resourceIDs) {
- const { fairCopyProject } = this.props
- const { openResources, selectedResource, requestedResources, resourceIndex } = this.state
-
- let nextSelection = resourceIDs[0]
+ let nextSelection,
+ nextBrowserOpen,
+ nextResources = {}
+ if (nextResourceArr.length > 0) {
+ for (const resource of nextResourceArr) {
+ nextResources[resource.resourceID] = resource
+ }
+ nextSelection = resourceIDs.find((id) => id === selectedResource)
+ ? nextResourceArr[0].resourceID
+ : selectedResource
+ nextBrowserOpen = resourceBrowserOpen
+ } else {
+ nextSelection = null
+ nextBrowserOpen = true
+ }
- if( this.selectTEIDoc(nextSelection) ) {
- return
+ this.setState({
+ ...this.state,
+ selectedResource: nextSelection,
+ openResources: nextResources,
+ resourceBrowserOpen: nextBrowserOpen,
+ alertDialogMode: 'closed',
+ alertOptions: null,
+ popupMenuOptions: null,
+ popupMenuAnchorEl: null,
+ popupMenuPlacement: null,
+ })
+
+ if (exitOnClose) {
+ fairCopy.ipcSend('exitApp')
+ }
+ }
+
+ // a bit of a hack - need to refresh after it renders
+ refreshWhenReady(searchQuery, searchResults, openToSearchResult) {
+ setTimeout(() => {
+ const { selectedResource, openResources } = this.state
+ const resource = openResources[selectedResource]
+ if (resource && resource.getActiveView()) {
+ this.updateSearchResults(resource, searchQuery, searchResults)
+ resource.refreshView()
+ if (openToSearchResult) {
+ scrollToSearchResult(resource, 0)
}
- let change = (selectedResource !== nextSelection)
- const nextRequestedResources = [ ...requestedResources ]
-
- for( const resourceID of resourceIDs ) {
- if( !openResources[resourceID] ) {
- nextRequestedResources.push(resourceID)
- fairCopyProject.openResource(resourceID)
- }
+ } else {
+ this.refreshWhenReady(searchQuery, searchResults, openToSearchResult)
+ }
+ }, 60)
+ }
+
+ saveResources(resourceIDs) {
+ const { openResources, selectedResource } = this.state
+ for (const resourceID of resourceIDs) {
+ const resource = openResources[resourceID]
+ if (resource && resource.changedSinceLastSave) {
+ resource.save()
+ if (resource.resourceID === selectedResource) resource.refreshView()
+ }
+ }
+ }
+
+ checkInResources(checkInResources) {
+ this.setState({
+ ...this.state,
+ checkInMode: true,
+ checkInResources,
+ ...closePopUpState,
+ })
+ }
+
+ checkOutResources(resourceEntries) {
+ const { fairCopyProject } = this.props
+ const { userID, serverURL, projectID } = fairCopyProject
+ fairCopy.ipcSend('checkOut', userID, serverURL, projectID, resourceEntries)
+ }
+
+ onLocalResources = (event, localResources) => {
+ this.setState({ ...this.state, localResources })
+ }
+
+ onOpenPopupMenu = (
+ popupMenuOptions,
+ popupMenuAnchorEl,
+ popupMenuPlacement
+ ) => {
+ this.setState({
+ ...this.state,
+ popupMenuOptions,
+ popupMenuAnchorEl,
+ popupMenuPlacement,
+ })
+ }
+
+ onClosePopupMenu = () => {
+ this.setState({ ...this.state, ...closePopUpState })
+ }
+
+ onLogin = () => {
+ this.setState({ ...this.state, loginMode: true })
+ }
+
+ onLoggedIn = () => {
+ const { resourceViews } = this.state
+ const nextResourceViews = { ...resourceViews }
+ nextResourceViews['remote'].loading = true
+ this.setState({
+ ...this.state,
+ resourceViews: nextResourceViews,
+ loginMode: false,
+ })
+ fairCopy.ipcSend('reopenProject')
+ }
+
+ onEditResource = () => {
+ this.setState({ ...this.state, editDialogMode: true })
+ }
+
+ onImportResource = (importType) => {
+ if (importType === 'xml') {
+ this.setState({ ...this.state, textImportDialogMode: true })
+ } else {
+ this.setState({ ...this.state, iiifDialogMode: true })
+ }
+ }
+
+ onAddImages = () => {
+ this.setState({ ...this.state, addImagesMode: true })
+ }
+
+ onAlertMessage = (message) => {
+ this.setState({ ...this.state, alertMessage: message, ...closePopUpState })
+ }
+
+ onEditSurfaceInfo = (surfaceInfo) => {
+ this.setState({
+ ...this.state,
+ surfaceInfo: surfaceInfo,
+ editSurfaceInfoMode: true,
+ })
+ }
+
+ onDragElement = (elementID, clientOffset, startingPoint, dragTarget) => {
+ const dragInfo = { elementID, clientOffset, dragTarget, startingPoint }
+ this.setState({ ...this.state, draggingElementActive: true, dragInfo })
+ }
+
+ onResourceViewChange = (nextView) => {
+ const { resourceViews } = this.state
+ const { currentView } = resourceViews
+ const resourceView = resourceViews[currentView]
+ const { indexParentID, parentEntry } = resourceView
+ const resourceViewRequest = {
+ currentView,
+ indexParentID,
+ parentEntry,
+ ...nextView,
+ }
+ fairCopy.ipcSend('requestResourceView', resourceViewRequest)
+ const checkMarkState = this.setAllCheckmarks(false, false)
+ const nextResourceViews = { ...resourceViews }
+ nextResourceViews[currentView].loading = true
+ this.setState({
+ ...this.state,
+ ...checkMarkState,
+ resourceViews: nextResourceViews,
+ })
+ }
+
+ onResourceAction = (actionID, resourceIDs, resourceEntries) => {
+ // all actions that use nextState clear the checkmarks
+ const checkmarkState = this.setAllCheckmarks(false, false)
+ const nextState = { ...this.state, ...checkmarkState }
+
+ switch (actionID) {
+ case 'open':
+ this.selectResources(resourceIDs)
+ break
+ case 'open-search-result':
+ this.selectResources(resourceIDs, true)
+ break
+ case 'check-in':
+ {
+ // don't check in if there are unsaved files being committed
+ const { openResources } = this.state
+ for (const resourceID of resourceIDs) {
+ const openResource = openResources[resourceID]
+ if (openResource && openResource.changedSinceLastSave) {
+ this.onAlertMessage(
+ 'You must save all files that are being checked in.'
+ )
+ return
+ }
+ }
+ this.setState({
+ ...nextState,
+ checkInMode: true,
+ checkInResources: resourceIDs,
+ ...closePopUpState,
+ })
}
-
- if( change ) {
+ break
+ case 'check-out':
+ this.checkOutResources(resourceEntries)
+ this.setState({ ...nextState, ...closePopUpState })
+ break
+ case 'close':
+ this.closeResources(resourceIDs)
+ break
+ case 'remote':
+ {
+ const { resourceViews } = this.state
+ if (
+ resourceViews.currentView === 'home' &&
+ !resourceViews.home.loading
+ ) {
+ const nextResourceViews = { ...resourceViews }
+ nextResourceViews.currentView = 'remote'
+ nextResourceViews.remote.loading = true
+ const { indexParentID, parentEntry, currentPage } =
+ resourceViews.remote
+ const resourceViewRequest = {
+ currentView: 'remote',
+ indexParentID,
+ parentEntry,
+ currentPage,
+ ...this.filterInitialState,
+ }
+ fairCopy.ipcSend('requestResourceView', resourceViewRequest)
this.setState({
- ...this.state,
- selectedResource: nextSelection,
- resourceBrowserOpen: false,
- requestedResources: nextRequestedResources,
- currentSubmenuID: 0,
- searchSelectionIndex: 0,
- popupMenuOptions: null,
- popupMenuAnchorEl: null,
- popupMenuPlacement: null
-
+ ...nextState,
+ selectedResource: null,
+ resourceBrowserOpen: true,
+ resourceViews: nextResourceViews,
+ resourceIndex: [],
})
- const nextResource = openResources[nextSelection]
- if( nextResource instanceof TEIDocument ) {
- const { searchQuery, searchResults } = this.state
- this.refreshWhenReady(searchQuery, searchResults, false)
- }
- } else {
- this.setState( {
- ...this.state,
- resourceBrowserOpen: false,
- requestedResources: nextRequestedResources,
- popupMenuOptions: null,
- popupMenuAnchorEl: null,
- popupMenuPlacement: null
- })
+ }
}
- }
-
- closeResources = (resourceIDs,exitOnClose=false,promptSave=true) => {
- const { fairCopyProject } = this.props
- const { openResources, selectedResource, resourceBrowserOpen } = this.state
-
- if( promptSave ) {
- for( const resourceID of resourceIDs ) {
- const resource = openResources[resourceID]
- if( resource && resource.changedSinceLastSave ) {
- const alertOptions = {
- resource, resourceIDs
- }
- this.setState({ ...this.state, exitOnClose, alertDialogMode: 'confirmSave', alertOptions })
- return
- }
- }
- }
-
- let nextResourceArr = []
- for( const openResourceID of Object.keys(openResources) ) {
- if( !resourceIDs.find(id => id === openResourceID) ) {
- // this id is not on the close list
- nextResourceArr.push(openResources[openResourceID])
- } else {
- // closing this resource
- fairCopyProject.onResourceClosed(openResources[openResourceID])
+ break
+ case 'home':
+ {
+ const { resourceViews } = this.state
+ if (
+ resourceViews.currentView === 'remote' &&
+ !resourceViews.remote.loading
+ ) {
+ const { indexParentID, parentEntry, currentPage } =
+ resourceViews.home
+ const resourceViewRequest = {
+ currentView: 'home',
+ indexParentID,
+ parentEntry,
+ currentPage,
+ ...this.filterInitialState,
}
+ fairCopy.ipcSend('requestResourceView', resourceViewRequest)
+ const nextResourceViews = { ...resourceViews }
+ nextResourceViews.currentView = 'home'
+ nextResourceViews.home.loading = true
+ this.setState({
+ ...nextState,
+ selectedResource: null,
+ resourceBrowserOpen: true,
+ resourceViews: nextResourceViews,
+ })
+ }
}
-
- let nextSelection, nextBrowserOpen, nextResources = {}
- if( nextResourceArr.length > 0 ) {
- for( const resource of nextResourceArr ) {
- nextResources[resource.resourceID] = resource
- }
- nextSelection = resourceIDs.find(id => id === selectedResource) ? nextResourceArr[0].resourceID : selectedResource
- nextBrowserOpen = resourceBrowserOpen
+ break
+ case 'root':
+ {
+ const { resourceViews, resourceIndex } = this.state
+ const { currentView } = resourceViews
+ const currentResourceView = resourceViews[currentView]
+ const { currentPage } = currentResourceView
+ const resourceViewRequest = {
+ currentView,
+ indexParentID: null,
+ parentEntry: null,
+ currentPage,
+ }
+ fairCopy.ipcSend('requestResourceView', resourceViewRequest)
+ const nextResourceViews = { ...resourceViews }
+ const nextResourceIndex = currentView === 'home' ? resourceIndex : []
+ nextResourceViews[currentView] = {
+ ...currentResourceView,
+ indexParentID: null,
+ parentEntry: null,
+ loading: true,
+ }
+ this.setState({
+ ...nextState,
+ selectedResource: null,
+ resourceBrowserOpen: true,
+ resourceViews: nextResourceViews,
+ resourceIndex: nextResourceIndex,
+ })
+ }
+ break
+ case 'move':
+ if (this.isResourceOpen(resourceEntries)) {
+ this.onAlertMessage(
+ 'You must close open editor windows before moving a resource.'
+ )
+ } else if (resourceEntries.find((r) => r.type === 'teidoc')) {
+ this.onAlertMessage('Cannot move TEIDocument type resources.')
} else {
- nextSelection = null
- nextBrowserOpen = true
+ const { fairCopyProject } = this.props
+ const onMove = (movingItems, parentEntry) => {
+ fairCopyProject.moveResources(movingItems, parentEntry)
+ }
+ const moveResourceProps = {
+ resourceType: 'teidoc',
+ allowRoot: true,
+ movingItems: resourceEntries,
+ onMove,
+ }
+ this.setState({
+ ...nextState,
+ moveResourceMode: true,
+ moveResourceProps,
+ ...closePopUpState,
+ })
}
-
- this.setState( {
- ...this.state,
- selectedResource: nextSelection,
- openResources: nextResources,
- resourceBrowserOpen: nextBrowserOpen,
- alertDialogMode: 'closed',
- alertOptions: null,
- popupMenuOptions: null,
- popupMenuAnchorEl: null,
- popupMenuPlacement: null
- })
-
- if( exitOnClose ) {
- fairCopy.ipcSend('exitApp')
- }
- }
-
- // a bit of a hack - need to refresh after it renders
- refreshWhenReady( searchQuery, searchResults, openToSearchResult ) {
- setTimeout( () => {
- const { selectedResource, openResources } = this.state
- const resource = openResources[selectedResource]
- if( resource && resource.getActiveView() ) {
- this.updateSearchResults(resource, searchQuery, searchResults)
- resource.refreshView()
- if( openToSearchResult ) {
- scrollToSearchResult( resource, 0 )
- }
- } else {
- this.refreshWhenReady(searchQuery,searchResults, openToSearchResult)
- }
- }, 60 )
- }
-
- saveResources(resourceIDs) {
- const { openResources, selectedResource } = this.state
- for( const resourceID of resourceIDs ) {
- const resource = openResources[resourceID]
- if( resource && resource.changedSinceLastSave ) {
- resource.save()
- if( resource.resourceID === selectedResource ) resource.refreshView()
- }
+ break
+ case 'save':
+ this.saveResources(resourceIDs)
+ break
+ case 'delete':
+ {
+ const { fairCopyProject } = this.props
+ const { openResources } = this.state
+ const alertOptions = { resourceIDs, openResources }
+ if (fairCopyProject.areEditable(resourceEntries)) {
+ this.setState({
+ ...nextState,
+ alertDialogMode: 'confirmDelete',
+ alertOptions,
+ ...closePopUpState,
+ })
+ } else {
+ this.onAlertMessage(
+ 'To delete a resource, you must first check it out.'
+ )
+ }
}
- }
-
- checkInResources(checkInResources) {
- this.setState({...this.state, checkInMode: true, checkInResources, ...closePopUpState })
- }
-
- checkOutResources(resourceEntries) {
- const { fairCopyProject } = this.props
- const { userID, serverURL, projectID } = fairCopyProject
- fairCopy.ipcSend('checkOut', userID, serverURL, projectID, resourceEntries )
- }
-
- onLocalResources = (event,localResources) => {
- this.setState({...this.state, localResources })
- }
-
- onOpenPopupMenu = (popupMenuOptions, popupMenuAnchorEl, popupMenuPlacement ) => {
- this.setState({...this.state, popupMenuOptions, popupMenuAnchorEl, popupMenuPlacement })
- }
-
- onClosePopupMenu = () => {
- this.setState({...this.state, ...closePopUpState })
- }
-
- onLogin = () => {
- this.setState({...this.state, loginMode: true })
- }
-
- onLoggedIn = () => {
- const { resourceViews } = this.state
- const nextResourceViews = { ...resourceViews }
- nextResourceViews['remote'].loading = true
- this.setState( {...this.state, resourceViews: nextResourceViews, loginMode: false} )
- fairCopy.ipcSend('reopenProject')
- }
-
- onEditResource = () => {
- this.setState({...this.state, editDialogMode: true })
- }
-
- onImportResource = (importType) => {
- if( importType === 'xml' ) {
- this.setState({...this.state, textImportDialogMode: true })
- } else {
- this.setState({...this.state, iiifDialogMode: true })
+ break
+ case 'recover':
+ {
+ const { fairCopyProject } = this.props
+ fairCopyProject.recoverResources(resourceIDs)
+ this.setState({ ...nextState, ...closePopUpState })
}
+ break
+ case 'export':
+ fairCopy.ipcSend('requestExport', resourceEntries)
+ this.setState({ ...nextState, ...closePopUpState })
+ break
+ default:
+ console.error(`Unrecognized resource action id: ${actionID}`)
+ break
}
-
- onAddImages = () => {
- this.setState({...this.state, addImagesMode: true })
- }
-
- onAlertMessage = (message) => {
- this.setState({...this.state, alertMessage: message, ...closePopUpState })
- }
-
- onEditSurfaceInfo = (surfaceInfo) => {
- this.setState( {...this.state, surfaceInfo: surfaceInfo, editSurfaceInfoMode: true} )
+ }
+
+ onSearchResults = (
+ searchQuery,
+ searchResults,
+ popupMenuOptions,
+ searchBarEl
+ ) => {
+ const { selectedResource, openResources } = this.state
+
+ if (selectedResource) {
+ const resource = openResources[selectedResource]
+ this.updateSearchResults(resource, searchQuery, searchResults)
}
-
- onDragElement = (elementID, clientOffset, startingPoint, dragTarget) => {
- const dragInfo = { elementID, clientOffset, dragTarget, startingPoint }
- this.setState( {...this.state, draggingElementActive: true, dragInfo })
+ if (popupMenuOptions.length === 0) {
+ this.setState({
+ ...this.state,
+ searchQuery,
+ searchResults,
+ searchSelectionIndex: 0,
+ ...closePopUpState,
+ })
+ } else {
+ const popupMenuPlacement = { vertical: 'top', horizontal: 'left' }
+ this.setState({
+ ...this.state,
+ searchQuery,
+ searchResults,
+ searchSelectionIndex: 0,
+ popupMenuOptions,
+ popupMenuAnchorEl: searchBarEl,
+ popupMenuPlacement,
+ })
}
-
- onResourceViewChange = (nextView) => {
- const { resourceViews } = this.state
- const { currentView } = resourceViews
- const resourceView = resourceViews[currentView]
- const { indexParentID, parentEntry } = resourceView
- const resourceViewRequest = { currentView, indexParentID, parentEntry, ...nextView }
- fairCopy.ipcSend('requestResourceView', resourceViewRequest )
- const checkMarkState = this.setAllCheckmarks(false,false)
- const nextResourceViews = { ...resourceViews }
- nextResourceViews[currentView].loading = true
- this.setState({...this.state, ...checkMarkState, resourceViews: nextResourceViews })
+ }
+
+ onSearchFilter = () => {
+ this.setState({ ...this.state, searchFilterMode: true })
+ }
+
+ onResetSearch = () => {
+ const resetSearch = debounce(() => {
+ this.setState({
+ ...this.state,
+ searchQuery: '',
+ searchResults: {},
+ searchSelectionIndex: 0,
+ })
+ }, resetSearchRate)
+ resetSearch()
+ }
+
+ updateSearchFilter = (elementName, attrQs, active, open) => {
+ const { searchQuery, searchScope, selectedResource, openResources } =
+ this.state
+ const searchFilterOptions = { elementName, attrQs, active }
+ const query = searchQuery ? searchQuery.query : ''
+ const searchQ = { query, elementName, attrQs }
+
+ // run the search based on the new filter settings
+ if (searchScope === 'project') {
+ this.setState({
+ ...this.state,
+ searchQuery: searchQ,
+ searchFilterOptions,
+ searchFilterMode: open,
+ })
+ fairCopy.ipcSend('searchProject', searchQ)
+ } else {
+ const currentResource = selectedResource
+ ? openResources[selectedResource]
+ : null
+ if (currentResource) {
+ const searchResults = searchResource(currentResource, searchQ)
+ this.setState({
+ ...this.state,
+ searchQuery: searchQ,
+ searchFilterOptions,
+ searchFilterMode: open,
+ })
+ this.updateSearchResults(currentResource, searchQuery, searchResults)
+ }
}
-
- onResourceAction = (actionID, resourceIDs, resourceEntries) => {
- // all actions that use nextState clear the checkmarks
- const checkmarkState = this.setAllCheckmarks(false,false)
- const nextState = { ...this.state, ...checkmarkState }
-
- switch(actionID) {
- case 'open':
- this.selectResources(resourceIDs)
- break
- case 'open-search-result':
- this.selectResources(resourceIDs, true)
- break
- case 'check-in':
- {
- // don't check in if there are unsaved files being committed
- const { openResources } = this.state
- for( const resourceID of resourceIDs ) {
- const openResource = openResources[resourceID]
- if( openResource && openResource.changedSinceLastSave ) {
- this.onAlertMessage("You must save all files that are being checked in.")
- return
- }
- }
- this.setState({...nextState, checkInMode: true, checkInResources: resourceIDs, ...closePopUpState })
- }
- break
- case 'check-out':
- this.checkOutResources(resourceEntries)
- this.setState({...nextState, ...closePopUpState })
- break
- case 'close':
- this.closeResources(resourceIDs)
- break
- case 'remote':
- {
- const {resourceViews} = this.state
- if( resourceViews.currentView === 'home' && !resourceViews.home.loading ) {
- const nextResourceViews = { ...resourceViews }
- nextResourceViews.currentView = 'remote'
- nextResourceViews.remote.loading = true
- const { indexParentID, parentEntry, currentPage } = resourceViews.remote
- const resourceViewRequest = { currentView: 'remote', indexParentID, parentEntry, currentPage, ...this.filterInitialState }
- fairCopy.ipcSend('requestResourceView', resourceViewRequest )
- this.setState({...nextState, selectedResource: null, resourceBrowserOpen: true, resourceViews: nextResourceViews, resourceIndex: [] })
- }
- }
- break
- case 'home':
- {
- const {resourceViews} = this.state
- if( resourceViews.currentView === 'remote' && !resourceViews.remote.loading ) {
- const { indexParentID, parentEntry, currentPage } = resourceViews.home
- const resourceViewRequest = { currentView: 'home', indexParentID, parentEntry, currentPage, ...this.filterInitialState }
- fairCopy.ipcSend('requestResourceView', resourceViewRequest )
- const nextResourceViews = { ...resourceViews }
- nextResourceViews.currentView = 'home'
- nextResourceViews.home.loading = true
- this.setState({...nextState, selectedResource: null, resourceBrowserOpen: true, resourceViews: nextResourceViews })
- }
- }
- break
- case 'root':
- {
- const {resourceViews, resourceIndex } = this.state
- const {currentView} = resourceViews
- const currentResourceView = resourceViews[currentView]
- const { currentPage } = currentResourceView
- const resourceViewRequest = { currentView, indexParentID: null, parentEntry: null, currentPage }
- fairCopy.ipcSend('requestResourceView', resourceViewRequest )
- const nextResourceViews = { ...resourceViews }
- const nextResourceIndex = currentView === 'home' ? resourceIndex : []
- nextResourceViews[currentView] = { ...currentResourceView, indexParentID: null, parentEntry: null, loading: true }
- this.setState({...nextState, selectedResource: null, resourceBrowserOpen: true, resourceViews: nextResourceViews, resourceIndex: nextResourceIndex })
- }
- break
- case 'move':
- if( this.isResourceOpen( resourceEntries ) ) {
- this.onAlertMessage("You must close open editor windows before moving a resource.")
- } else if( resourceEntries.find( r => r.type === 'teidoc' ) ) {
- this.onAlertMessage("Cannot move TEIDocument type resources.")
- } else {
- const { fairCopyProject } = this.props
- const onMove = (movingItems, parentEntry)=>{ fairCopyProject.moveResources( movingItems, parentEntry ) }
- const moveResourceProps = { resourceType: 'teidoc', allowRoot: true, movingItems: resourceEntries, onMove }
- this.setState( {...nextState, moveResourceMode: true, moveResourceProps, ...closePopUpState} )
- }
- break
- case 'save':
- this.saveResources(resourceIDs)
- break
- case 'delete':
- {
- const { fairCopyProject } = this.props
- const { openResources } = this.state
- const alertOptions = { resourceIDs, openResources }
- if( fairCopyProject.areEditable( resourceEntries ) ) {
- this.setState({ ...nextState, alertDialogMode: 'confirmDelete', alertOptions, ...closePopUpState })
- } else {
- this.onAlertMessage("To delete a resource, you must first check it out.")
- }
- }
- break
- case 'recover':
- {
- const { fairCopyProject } = this.props
- fairCopyProject.recoverResources(resourceIDs)
- this.setState({...nextState, ...closePopUpState })
- }
- break
- case 'export':
- fairCopy.ipcSend('requestExport', resourceEntries)
- this.setState({...nextState, ...closePopUpState })
- break
- default:
- console.error(`Unrecognized resource action id: ${actionID}`)
- break
- }
+ }
+
+ updateSearchResults(resource, searchQuery, searchResults) {
+ const { resourceID } = resource
+ const resourceSearchResults = searchResults[resourceID]
+ ? searchResults[resourceID]
+ : -1
+ highlightSearchResults(resource, searchQuery, resourceSearchResults)
+ }
+
+ closeSearchBar() {
+ const { selectedResource, openResources } = this.state
+ if (selectedResource) {
+ const resource = openResources[selectedResource]
+ highlightSearchResults(resource, null, -1)
+ resource.getActiveView().focus()
}
-
- onSearchResults = ( searchQuery, searchResults, popupMenuOptions, searchBarEl) => {
- const { selectedResource, openResources } = this.state
-
- if( selectedResource ) {
- const resource = openResources[selectedResource]
- this.updateSearchResults(resource, searchQuery, searchResults)
+ this.setState({
+ ...this.state,
+ showSearchBar: false,
+ searchQuery: '',
+ searchResults: {},
+ searchSelectionIndex: 0,
+ ...closePopUpState,
+ })
+ }
+
+ toggleSearchScope() {
+ const { searchScope } = this.state
+ const nextScope = searchScope === 'file' ? 'project' : 'file'
+ this.setState({ ...this.state, searchScope: nextScope })
+ }
+
+ renderEditors() {
+ const {
+ openResources,
+ selectedResource,
+ leftPaneWidth,
+ resourceViews,
+ annotationData,
+ } = this.state
+ const { fairCopyProject, onProjectSettings } = this.props
+ const { currentView } = resourceViews
+
+ const editors = []
+ let visible = false
+ for (const resource of Object.values(openResources)) {
+ const hidden = selectedResource !== resource.resourceID
+ if (!hidden) visible = true
+ const key = `editor-${resource.resourceID}`
+ const { resourceEntry, parentEntry } = resource
+
+ const onSave = () => {
+ this.onResourceAction('save', [resource.resourceID])
+ }
+ const onConfirmDeleteImages = (alertOptions) => {
+ this.setState({
+ ...this.state,
+ alertDialogMode: 'confirmDeleteImages',
+ alertOptions,
+ })
+ }
+ const onMoveSurfaces = (facsDocument, surfaces, onMoved) => {
+ const onMove = (movingItems, parentEntry) => {
+ facsDocument.moveSurfaces(movingItems, parentEntry, onMoved)
}
- if( popupMenuOptions.length === 0 ) {
- this.setState({...this.state, searchQuery, searchResults, searchSelectionIndex: 0, ...closePopUpState })
- } else {
- const popupMenuPlacement = { vertical: 'top', horizontal: 'left' }
- this.setState({...this.state, searchQuery, searchResults, searchSelectionIndex: 0, popupMenuOptions, popupMenuAnchorEl: searchBarEl, popupMenuPlacement })
+ const moveResourceProps = {
+ resourceType: 'facs',
+ allowRoot: false,
+ movingItems: surfaces,
+ onMove,
+ onMoved,
}
- }
-
- onSearchFilter = () => {
- this.setState({...this.state, searchFilterMode: true })
- }
-
- onResetSearch = () => {
- const resetSearch = debounce(() => {
- this.setState({...this.state, searchQuery: '', searchResults: {}, searchSelectionIndex: 0 })
- }, resetSearchRate)
- resetSearch()
- }
-
- updateSearchFilter = ( elementName, attrQs, active, open ) => {
- const { searchQuery, searchScope, selectedResource, openResources } = this.state
- const searchFilterOptions = { elementName, attrQs, active }
- const query = searchQuery ? searchQuery.query : ""
- const searchQ = { query, elementName, attrQs }
-
- // run the search based on the new filter settings
- if( searchScope === 'project' ) {
- this.setState({...this.state, searchQuery: searchQ, searchFilterOptions, searchFilterMode: open })
- fairCopy.ipcSend('searchProject', searchQ)
+ this.setState({
+ ...this.state,
+ moveResourceMode: true,
+ moveResourceProps,
+ ...closePopUpState,
+ })
+ }
+ const onToggleSearchBar = (showSearchBar) => {
+ if (!showSearchBar) {
+ this.closeSearchBar()
} else {
- const currentResource = selectedResource ? openResources[selectedResource] : null
- if( currentResource ) {
- const searchResults = searchResource( currentResource, searchQ )
- this.setState({...this.state, searchQuery: searchQ, searchFilterOptions, searchFilterMode: open })
- this.updateSearchResults(currentResource, searchQuery, searchResults)
- }
+ this.setState({ ...this.state, showSearchBar: true })
}
+ }
+
+ // bump state to update sidebar
+ const onErrorCountChange = () => {
+ this.setState({ ...this.state })
+ }
+
+ if (resource instanceof TEIDocument) {
+ editors.push(
+