@fiduswriter/image-manager 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +5 -0
  3. package/dist/copyright_dialog/index.d.ts +17 -0
  4. package/dist/copyright_dialog/index.d.ts.map +1 -0
  5. package/dist/copyright_dialog/index.js +148 -0
  6. package/dist/copyright_dialog/index.js.map +1 -0
  7. package/dist/copyright_dialog/templates.d.ts +13 -0
  8. package/dist/copyright_dialog/templates.d.ts.map +1 -0
  9. package/dist/copyright_dialog/templates.js +48 -0
  10. package/dist/copyright_dialog/templates.js.map +1 -0
  11. package/dist/database.d.ts +9 -0
  12. package/dist/database.d.ts.map +1 -0
  13. package/dist/database.js +51 -0
  14. package/dist/database.js.map +1 -0
  15. package/dist/edit_dialog/index.d.ts +24 -0
  16. package/dist/edit_dialog/index.d.ts.map +1 -0
  17. package/dist/edit_dialog/index.js +186 -0
  18. package/dist/edit_dialog/index.js.map +1 -0
  19. package/dist/edit_dialog/model.d.ts +20 -0
  20. package/dist/edit_dialog/model.d.ts.map +1 -0
  21. package/dist/edit_dialog/model.js +153 -0
  22. package/dist/edit_dialog/model.js.map +1 -0
  23. package/dist/edit_dialog/templates.d.ts +5 -0
  24. package/dist/edit_dialog/templates.d.ts.map +1 -0
  25. package/dist/edit_dialog/templates.js +33 -0
  26. package/dist/edit_dialog/templates.js.map +1 -0
  27. package/dist/index.d.ts +5 -0
  28. package/dist/index.d.ts.map +1 -0
  29. package/dist/index.js +5 -0
  30. package/dist/index.js.map +1 -0
  31. package/dist/overview/categories.d.ts +8 -0
  32. package/dist/overview/categories.d.ts.map +1 -0
  33. package/dist/overview/categories.js +94 -0
  34. package/dist/overview/categories.js.map +1 -0
  35. package/dist/overview/index.d.ts +39 -0
  36. package/dist/overview/index.d.ts.map +1 -0
  37. package/dist/overview/index.js +358 -0
  38. package/dist/overview/index.js.map +1 -0
  39. package/dist/overview/menu.d.ts +48 -0
  40. package/dist/overview/menu.d.ts.map +1 -0
  41. package/dist/overview/menu.js +67 -0
  42. package/dist/overview/menu.js.map +1 -0
  43. package/dist/overview/templates.d.ts +4 -0
  44. package/dist/overview/templates.d.ts.map +1 -0
  45. package/dist/overview/templates.js +24 -0
  46. package/dist/overview/templates.js.map +1 -0
  47. package/dist/selection_dialog/index.d.ts +19 -0
  48. package/dist/selection_dialog/index.d.ts.map +1 -0
  49. package/dist/selection_dialog/index.js +149 -0
  50. package/dist/selection_dialog/index.js.map +1 -0
  51. package/package.json +53 -0
  52. package/src/copyright_dialog/index.js +179 -0
  53. package/src/copyright_dialog/templates.js +56 -0
  54. package/src/database.js +57 -0
  55. package/src/edit_dialog/index.js +222 -0
  56. package/src/edit_dialog/model.js +184 -0
  57. package/src/edit_dialog/templates.js +41 -0
  58. package/src/index.js +4 -0
  59. package/src/overview/categories.js +115 -0
  60. package/src/overview/index.js +435 -0
  61. package/src/overview/menu.js +75 -0
  62. package/src/overview/templates.js +30 -0
  63. package/src/selection_dialog/index.js +176 -0
@@ -0,0 +1,149 @@
1
+ import { Dialog, SelectionDataTable, cancelPromise, escapeText, gettext } from "fwtoolkit";
2
+ export class ImageSelectionDialog {
3
+ constructor(imageDB, userImageDB, imgId, page) {
4
+ this.imageDB = imageDB;
5
+ this.userImageDB = userImageDB;
6
+ this.page = page;
7
+ this.imgId = imgId; // a preselected image
8
+ this.imgDb = "document"; // the preselection image will always come from the document
9
+ this.images = []; // images from both databases
10
+ }
11
+ isE2EE() {
12
+ return this.page.e2ee?.encrypted === true;
13
+ }
14
+ init() {
15
+ console.log("DEBUG selection init: docImages=", Object.keys(this.imageDB.db).length, "userImages=", Object.keys(this.userImageDB.db).length);
16
+ this.images = Object.values(this.imageDB.db).map(image => {
17
+ return {
18
+ image,
19
+ db: "document"
20
+ };
21
+ });
22
+ Object.values(this.userImageDB.db).forEach(image => {
23
+ if (this.imageDB.db[image.id]) {
24
+ return;
25
+ }
26
+ this.images.push({
27
+ image,
28
+ db: "user"
29
+ });
30
+ });
31
+ console.log("DEBUG selection images=", this.images.length);
32
+ const buttons = [];
33
+ const p = new Promise(resolve => {
34
+ if (!this.page.app.isOffline()) {
35
+ buttons.push({
36
+ text: gettext("Add new image"),
37
+ icon: "plus-circle",
38
+ click: () => {
39
+ import("../edit_dialog").then(({ ImageEditDialog }) => {
40
+ const targetDB = this.isE2EE()
41
+ ? this.imageDB
42
+ : this.userImageDB;
43
+ const imageUpload = new ImageEditDialog(targetDB, false, this.page);
44
+ resolve(imageUpload.init().then(imageId => {
45
+ console.log("DEBUG upload resolved imageId=", imageId);
46
+ this.imgId = imageId;
47
+ // For E2EE docs the image goes straight
48
+ // into the document DB, not the user's.
49
+ this.imgDb = this.isE2EE()
50
+ ? "document"
51
+ : "user";
52
+ console.log("DEBUG closing selection dialog");
53
+ this.imageDialog.close();
54
+ console.log("DEBUG reinit selection dialog");
55
+ return this.init();
56
+ }));
57
+ });
58
+ }
59
+ });
60
+ }
61
+ buttons.push({
62
+ text: gettext("Use image"),
63
+ classes: "fw-dark",
64
+ click: () => {
65
+ this.imageDialog.close();
66
+ resolve({ id: this.imgId, db: this.imgDb });
67
+ }
68
+ });
69
+ buttons.push({
70
+ type: "cancel",
71
+ click: () => {
72
+ this.imageDialog.close();
73
+ resolve(cancelPromise());
74
+ }
75
+ });
76
+ });
77
+ this.imageDialog = new Dialog({
78
+ buttons,
79
+ width: 300,
80
+ body: '<div class="image-selection-table"></div>',
81
+ title: gettext("Images"),
82
+ id: "select-image-dialog"
83
+ });
84
+ this.imageDialog.open();
85
+ this.initTable();
86
+ this.imageDialog.centerDialog();
87
+ return p;
88
+ }
89
+ initTable() {
90
+ /* Initialize the overview table */
91
+ const tableEl = document.createElement("table");
92
+ tableEl.classList.add("fw-data-table");
93
+ tableEl.classList.add("fw-small");
94
+ const host = this.imageDialog.dialogEl.querySelector("div.image-selection-table");
95
+ host.innerHTML = "";
96
+ host.appendChild(tableEl);
97
+ const selectedIds = this.imgId === false ? [] : [`${this.imgDb}-${this.imgId}`];
98
+ this.selectionTable = new SelectionDataTable({
99
+ dom: host,
100
+ classes: ["fw-data-table", "fw-small"],
101
+ columns: [
102
+ {
103
+ select: 0,
104
+ hidden: true
105
+ },
106
+ {
107
+ select: [0, 2],
108
+ type: "string"
109
+ },
110
+ {
111
+ select: [1, 3],
112
+ sortable: false
113
+ }
114
+ ],
115
+ data: this.images.map(image => this.createTableRow(image)),
116
+ idColumn: 0,
117
+ multiple: false,
118
+ selectedIds,
119
+ scrollY: "270px",
120
+ labels: {
121
+ noRows: gettext("No images available"), // Message shown when there are no images
122
+ noResults: gettext("No images found"), // Message shown when no images are found after search
123
+ placeholder: gettext("Search...") // placeholder for search field
124
+ },
125
+ onChange: selected => {
126
+ if (selected.length) {
127
+ const [db, id] = String(selected[0]).split("-");
128
+ this.imgId = Number.parseInt(id);
129
+ this.imgDb = db;
130
+ }
131
+ else {
132
+ this.imgId = false;
133
+ }
134
+ }
135
+ });
136
+ this.selectionTable.init();
137
+ this.table = this.selectionTable.table;
138
+ }
139
+ createTableRow(image) {
140
+ return [
141
+ `${image.db}-${image.image.id}`,
142
+ image.image.thumbnail === undefined
143
+ ? `<img src="${image.image.image}" style="max-heigth:30px;max-width:30px;">`
144
+ : `<img src="${image.image.thumbnail}" style="max-heigth:30px;max-width:30px;">`,
145
+ escapeText(image.image.title)
146
+ ];
147
+ }
148
+ }
149
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/selection_dialog/index.js"],"names":[],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@fiduswriter/image-manager",
3
+ "version": "0.1.0",
4
+ "description": "Fidus Writer image/media manager",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "ssh://git@codeberg.org/fiduswriter/fiduswriter-image-manager-js.git"
8
+ },
9
+ "license": "AGPL-3.0",
10
+ "author": "Johannes Wilm",
11
+ "type": "module",
12
+ "main": "./dist/index.js",
13
+ "types": "./dist/index.d.ts",
14
+ "exports": {
15
+ ".": {
16
+ "types": "./dist/index.d.ts",
17
+ "default": "./dist/index.js"
18
+ },
19
+ "./*": {
20
+ "types": "./dist/*.d.ts",
21
+ "default": "./dist/*.js"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist/",
26
+ "src/",
27
+ "README.md",
28
+ "LICENSE"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "prepare": "npm run build",
33
+ "prepublishOnly": "npm run build",
34
+ "typecheck": "tsc --noEmit",
35
+ "lint": "eslint src",
36
+ "lint:fix": "eslint src --fix",
37
+ "format": "prettier --write .",
38
+ "format:check": "prettier --check ."
39
+ },
40
+ "dependencies": {
41
+ "@fiduswriter/common": "^0.1.4",
42
+ "cropperjs": "^1.6.2",
43
+ "fwtoolkit": "^0.1.9"
44
+ },
45
+ "devDependencies": {
46
+ "@eslint/js": "^10.0.1",
47
+ "eslint": "^10.6.0",
48
+ "globals": "^15.15.0",
49
+ "prettier": "^3.5.3",
50
+ "typescript": "^6.0.3",
51
+ "typescript-eslint": "^8.62.0"
52
+ }
53
+ }
@@ -0,0 +1,179 @@
1
+ import {edtfParse} from "bibliojson"
2
+ import deepEqual from "fast-deep-equal"
3
+ import {Dialog, InputList, TypeSwitch, escapeText} from "fwtoolkit"
4
+ import {
5
+ copyrightTemplate,
6
+ licenseInputTemplate,
7
+ licenseSelectTemplate
8
+ } from "./templates"
9
+
10
+ export const LICENSE_URLS = [
11
+ ["CC BY 4.0", "https://creativecommons.org/licenses/by/4.0/"],
12
+ ["CC BY-SA 4.0", "https://creativecommons.org/licenses/by-sa/4.0/"],
13
+ ["CC BY-ND 4.0", "https://creativecommons.org/licenses/by-nd/4.0/"],
14
+ ["CC BY-NC 4.0", "https://creativecommons.org/licenses/by-nc/4.0/"],
15
+ ["CC BY-NC-SA 4.0", "https://creativecommons.org/licenses/by-nc-sa/4.0/"],
16
+ ["CC BY-NC-ND 4.0", "https://creativecommons.org/licenses/by-nc-nd/4.0/"],
17
+ ["CC0", "https://creativecommons.org/publicdomain/zero/1.0/"]
18
+ ]
19
+
20
+ function getLicenseTitle(url) {
21
+ const license = LICENSE_URLS.find(license => license[1] === url)
22
+ return license ? license[0] : ""
23
+ }
24
+
25
+ export class CopyrightDialog {
26
+ constructor(copyright) {
27
+ this.copyright = copyright
28
+ this.origCopyright = copyright
29
+ this.dialog = false
30
+ }
31
+
32
+ getCurrentValue() {
33
+ this.copyright = {}
34
+ const holder = this.dialog.dialogEl.querySelector(".holder").value
35
+ this.copyright.holder = holder.length ? holder : false
36
+ const year = this.dialog.dialogEl.querySelector(".year").value
37
+ this.copyright.year = year.length
38
+ ? Math.max(0, Math.min(Number.parseInt(year) || 0, 2100))
39
+ : false
40
+ this.copyright.freeToRead = this.dialog.dialogEl.querySelector(
41
+ ".free-to-read:checked"
42
+ )
43
+ ? true
44
+ : false
45
+ const licenseStartDates = Array.from(
46
+ this.dialog.dialogEl.querySelectorAll(".license-start")
47
+ ).map(el => el.value)
48
+ this.copyright.licenses = this.licensesList.values
49
+ .map((license, index) => {
50
+ if (!license.url.length) {
51
+ return false
52
+ }
53
+ const returnValue = {
54
+ url: license.url,
55
+ title: license.title
56
+ }
57
+ const startDate = edtfParse(licenseStartDates[index])
58
+ if (
59
+ startDate.valid &&
60
+ (startDate.type === "Date" ||
61
+ startDate.type === "YearMonth" ||
62
+ startDate.type === "Year") &&
63
+ !startDate.uncertain &&
64
+ !startDate.approximate
65
+ ) {
66
+ returnValue.start = startDate.cleanedString
67
+ }
68
+ return returnValue
69
+ })
70
+ .filter(license => license)
71
+ }
72
+
73
+ init() {
74
+ return new Promise(resolve => {
75
+ const buttons = []
76
+ buttons.push({
77
+ text: gettext("Change"),
78
+ classes: "fw-dark",
79
+ click: () => {
80
+ this.dialog.close()
81
+ this.getCurrentValue()
82
+ if (deepEqual(this.copyright, this.origCopyright)) {
83
+ // No change.
84
+ resolve(false)
85
+ }
86
+ resolve(this.copyright)
87
+ }
88
+ })
89
+
90
+ buttons.push({
91
+ type: "cancel"
92
+ })
93
+
94
+ this.dialog = new Dialog({
95
+ width: 940,
96
+ height: 300,
97
+ id: "configure-copyright",
98
+ title: gettext("Set copyright information"),
99
+ body: copyrightTemplate(this.copyright),
100
+ buttons
101
+ })
102
+
103
+ this.dialog.open()
104
+ this.bind()
105
+ })
106
+ }
107
+
108
+ bind() {
109
+ this.licensesList = new InputList({
110
+ dom: this.dialog.dialogEl.querySelector(".copyright-licenses-list"),
111
+ initialValues: this.copyright.licenses || [],
112
+ emptyValue: {url: "", title: "", start: false},
113
+ renderItem: license => ({
114
+ html: `<div class="copyright-license-switch"></div>
115
+ <div class="field-part field-part-small">
116
+ <input type="text" class="license-start" value="${license.start ? escapeText(license.start) : ""}" placeholder="${gettext("Start date")}">
117
+ </div>`,
118
+ bind: el => {
119
+ const licenseContainer = el.closest("tr")
120
+ const startInput =
121
+ licenseContainer.querySelector(".license-start")
122
+ if (license.start) {
123
+ startInput.value = license.start
124
+ }
125
+
126
+ const mode =
127
+ license.url === "" ||
128
+ LICENSE_URLS.find(
129
+ licenseUrl => licenseUrl[1] === license.url
130
+ )
131
+ ? 1
132
+ : 2
133
+ new TypeSwitch({
134
+ dom: el.querySelector(".copyright-license-switch"),
135
+ label1: gettext("From list"),
136
+ label2: gettext("Custom"),
137
+ initialMode: mode,
138
+ render1: () =>
139
+ licenseSelectTemplate({url: license.url}),
140
+ render2: () =>
141
+ licenseInputTemplate({
142
+ url: license.url,
143
+ title: license.title
144
+ }),
145
+ onChange: () => {
146
+ // Restore focus to the license input after switching.
147
+ const focusable = el.querySelector(
148
+ ".fw-type-switch-input-inner input, .fw-type-switch-input-inner select"
149
+ )
150
+ if (focusable) {
151
+ focusable.focus()
152
+ }
153
+ }
154
+ })
155
+ }
156
+ }),
157
+ getValue: el => {
158
+ const licenseInput = el.querySelector(
159
+ ".fw-type-switch-input-inner"
160
+ )
161
+ const selectEl = licenseInput.querySelector("select.license")
162
+ let url, title
163
+ if (selectEl) {
164
+ url = selectEl.value
165
+ title = getLicenseTitle(url)
166
+ } else {
167
+ url = licenseInput.querySelector("input.license").value
168
+ title = licenseInput.querySelector(
169
+ "input.license-title"
170
+ ).value
171
+ }
172
+ const start =
173
+ el.closest("tr").querySelector(".license-start").value ||
174
+ false
175
+ return {url, title, start}
176
+ }
177
+ })
178
+ }
179
+ }
@@ -0,0 +1,56 @@
1
+ import {escapeText} from "fwtoolkit"
2
+ import {LICENSE_URLS} from "./index"
3
+
4
+ export const licenseSelectTemplate = ({url}) =>
5
+ `<select class="license">
6
+ <option value=""></option>
7
+ ${LICENSE_URLS.map(
8
+ licenseUrl =>
9
+ `<option value="${licenseUrl[1]}"${url === licenseUrl[1] ? " selected" : ""}>${licenseUrl[0]}</option>`
10
+ ).join("")}
11
+ </select>
12
+ <div class="fw-select-arrow fa fa-caret-down"></div>`
13
+
14
+ export const licenseInputTemplate = ({url, title}) =>
15
+ `<div class="field-part field-part-huge">
16
+ <input type='text' class='license' value="${escapeText(url)}" placeholder="${gettext("License URL")}">
17
+ </div>
18
+ <div class="field-part field-part-huge">
19
+ <input type='text' class='license-title' value="${escapeText(title)}" placeholder="${gettext("License Title")}">
20
+ </div>`
21
+
22
+ export const copyrightTemplate = ({holder, year, freeToRead}) =>
23
+ `<table class="fw-dialog-table">
24
+ <tbody>
25
+ <tr>
26
+ <th><h4 class="fw-tablerow-title fw-wtooltip">
27
+ ${gettext("Copyright holder")}
28
+ <span class="fw-tooltip">${gettext("If the work is not in the public domain, specify who the copyright holder is.")}</span>
29
+ </h4></th>
30
+ <td class="fw-entry-field"><input type="text" class="holder" value="${holder ? escapeText(holder) : ""}"></td>
31
+ </tr>
32
+ <tr>
33
+ <th><h4 class="fw-tablerow-title fw-wtooltip">
34
+ ${gettext("Copyright year")}
35
+ <span class="fw-tooltip">${gettext("If the work is not in the public domain, specify the year of the copyright.")}</span>
36
+ </h4></th>
37
+ <td class="fw-entry-field"><input type="number" class="year" min=0 max=2100 value="${year ? year : ""}"></td>
38
+ </tr>
39
+ <tr>
40
+ <th><h4 class="fw-tablerow-title fw-wtooltip">
41
+ ${gettext("Available to read for free?")}
42
+ <span class="fw-tooltip">${gettext("Specify whether the work can be accessed without paying a fee.")}</span>
43
+ </h4></th>
44
+ <td class="fw-entry-field"><input type="checkbox" class="free-to-read"${freeToRead ? " checked" : ""}></td>
45
+ </tr>
46
+ <tr>
47
+ <th><h4 class="fw-tablerow-title fw-wtooltip">
48
+ ${gettext("License(s)")}
49
+ <span class="fw-tooltip">${gettext('List any licenses the work is available under. If the license only applies from a given date, please specify the date in the ISO8601 format (such as "2012-10-15").')}</span>
50
+ </h4></th>
51
+ <td class="fw-entry-field licenses">
52
+ <div class="copyright-licenses-list"></div>
53
+ </td>
54
+ </tr>
55
+ </tbody>
56
+ </table>`
@@ -0,0 +1,57 @@
1
+ import {activateWait, addAlert, deactivateWait, gettext, postJson} from "fwtoolkit"
2
+
3
+ /* A class that holds information about images uploaded by the user. */
4
+
5
+ export class ImageDB {
6
+ constructor(app) {
7
+ this.app = app
8
+ this.db = {}
9
+ this.cats = []
10
+ }
11
+
12
+ getDB() {
13
+ this.db = {}
14
+ this.cats = []
15
+
16
+ activateWait()
17
+
18
+ return postJson("/api/usermedia/images/").then(({json}) => {
19
+ this.cats = json.imageCategories
20
+ json.images.forEach(image => {
21
+ this.db[image.id] = image
22
+ })
23
+ deactivateWait()
24
+ return
25
+ })
26
+ }
27
+
28
+ saveImage(imageData) {
29
+ activateWait()
30
+ const {image, ...jsonData} = imageData
31
+
32
+ return postJson("/api/usermedia/save/", jsonData, image ? {image} : {})
33
+ .then(({json}) => {
34
+ deactivateWait()
35
+ if (Object.keys(json.errormsg).length) {
36
+ return Promise.reject(new Error(json.errormsg.error))
37
+ } else {
38
+ this.db[json.values.id] = json.values
39
+ return json.values.id
40
+ }
41
+ })
42
+ .catch(error => {
43
+ if (error.status === 413) {
44
+ addAlert(
45
+ "error",
46
+ `${gettext("Image is larger than the maximum permitted size")}${this.app.settings?.MEDIA_MAX_SIZE ? `: ${Number.parseInt(this.app.settings.MEDIA_MAX_SIZE / 1000000)}MB` : "."}`
47
+ )
48
+ } else if (error.message) {
49
+ addAlert("error", gettext(error.message))
50
+ } else {
51
+ addAlert("error", gettext(error.statusText))
52
+ }
53
+ deactivateWait()
54
+ throw error
55
+ })
56
+ }
57
+ }