@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.
- package/LICENSE +661 -0
- package/README.md +5 -0
- package/dist/copyright_dialog/index.d.ts +17 -0
- package/dist/copyright_dialog/index.d.ts.map +1 -0
- package/dist/copyright_dialog/index.js +148 -0
- package/dist/copyright_dialog/index.js.map +1 -0
- package/dist/copyright_dialog/templates.d.ts +13 -0
- package/dist/copyright_dialog/templates.d.ts.map +1 -0
- package/dist/copyright_dialog/templates.js +48 -0
- package/dist/copyright_dialog/templates.js.map +1 -0
- package/dist/database.d.ts +9 -0
- package/dist/database.d.ts.map +1 -0
- package/dist/database.js +51 -0
- package/dist/database.js.map +1 -0
- package/dist/edit_dialog/index.d.ts +24 -0
- package/dist/edit_dialog/index.d.ts.map +1 -0
- package/dist/edit_dialog/index.js +186 -0
- package/dist/edit_dialog/index.js.map +1 -0
- package/dist/edit_dialog/model.d.ts +20 -0
- package/dist/edit_dialog/model.d.ts.map +1 -0
- package/dist/edit_dialog/model.js +153 -0
- package/dist/edit_dialog/model.js.map +1 -0
- package/dist/edit_dialog/templates.d.ts +5 -0
- package/dist/edit_dialog/templates.d.ts.map +1 -0
- package/dist/edit_dialog/templates.js +33 -0
- package/dist/edit_dialog/templates.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/overview/categories.d.ts +8 -0
- package/dist/overview/categories.d.ts.map +1 -0
- package/dist/overview/categories.js +94 -0
- package/dist/overview/categories.js.map +1 -0
- package/dist/overview/index.d.ts +39 -0
- package/dist/overview/index.d.ts.map +1 -0
- package/dist/overview/index.js +358 -0
- package/dist/overview/index.js.map +1 -0
- package/dist/overview/menu.d.ts +48 -0
- package/dist/overview/menu.d.ts.map +1 -0
- package/dist/overview/menu.js +67 -0
- package/dist/overview/menu.js.map +1 -0
- package/dist/overview/templates.d.ts +4 -0
- package/dist/overview/templates.d.ts.map +1 -0
- package/dist/overview/templates.js +24 -0
- package/dist/overview/templates.js.map +1 -0
- package/dist/selection_dialog/index.d.ts +19 -0
- package/dist/selection_dialog/index.d.ts.map +1 -0
- package/dist/selection_dialog/index.js +149 -0
- package/dist/selection_dialog/index.js.map +1 -0
- package/package.json +53 -0
- package/src/copyright_dialog/index.js +179 -0
- package/src/copyright_dialog/templates.js +56 -0
- package/src/database.js +57 -0
- package/src/edit_dialog/index.js +222 -0
- package/src/edit_dialog/model.js +184 -0
- package/src/edit_dialog/templates.js +41 -0
- package/src/index.js +4 -0
- package/src/overview/categories.js +115 -0
- package/src/overview/index.js +435 -0
- package/src/overview/menu.js +75 -0
- package/src/overview/templates.js +30 -0
- 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":"AAAA,OAAO,EAAC,MAAM,EAAE,kBAAkB,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAC,MAAM,WAAW,CAAA;AAExF,MAAM,OAAO,oBAAoB;IAC7B,YAAY,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,IAAI;QACzC,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,IAAI,CAAC,WAAW,GAAG,WAAW,CAAA;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA,CAAC,sBAAsB;QACzC,IAAI,CAAC,KAAK,GAAG,UAAU,CAAA,CAAC,4DAA4D;QACpF,IAAI,CAAC,MAAM,GAAG,EAAE,CAAA,CAAC,6BAA6B;IAClD,CAAC;IAED,MAAM;QACF,OAAO,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,SAAS,KAAK,IAAI,CAAA;IAC7C,CAAC;IAED,IAAI;QACA,OAAO,CAAC,GAAG,CACP,kCAAkC,EAClC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,MAAM,EACnC,aAAa,EACb,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,MAAM,CAC1C,CAAA;QACD,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE;YACrD,OAAO;gBACH,KAAK;gBACL,EAAE,EAAE,UAAU;aACjB,CAAA;QACL,CAAC,CAAC,CAAA;QACF,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE;YAC/C,IAAI,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC5B,OAAM;YACV,CAAC;YACD,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC;gBACb,KAAK;gBACL,EAAE,EAAE,MAAM;aACb,CAAC,CAAA;QACN,CAAC,CAAC,CAAA;QACF,OAAO,CAAC,GAAG,CAAC,yBAAyB,EAAE,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;QAC1D,MAAM,OAAO,GAAG,EAAE,CAAA;QAClB,MAAM,CAAC,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,EAAE;YAC5B,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC;gBAC7B,OAAO,CAAC,IAAI,CAAC;oBACT,IAAI,EAAE,OAAO,CAAC,eAAe,CAAC;oBAC9B,IAAI,EAAE,aAAa;oBACnB,KAAK,EAAE,GAAG,EAAE;wBACR,MAAM,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,CAAC,EAAC,eAAe,EAAC,EAAE,EAAE;4BAChD,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,EAAE;gCAC1B,CAAC,CAAC,IAAI,CAAC,OAAO;gCACd,CAAC,CAAC,IAAI,CAAC,WAAW,CAAA;4BACtB,MAAM,WAAW,GAAG,IAAI,eAAe,CACnC,QAAQ,EACR,KAAK,EACL,IAAI,CAAC,IAAI,CACZ,CAAA;4BAED,OAAO,CACH,WAAW,CAAC,IAAI,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE;gCAC9B,OAAO,CAAC,GAAG,CACP,gCAAgC,EAChC,OAAO,CACV,CAAA;gCACD,IAAI,CAAC,KAAK,GAAG,OAAO,CAAA;gCACpB,wCAAwC;gCACxC,wCAAwC;gCACxC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,MAAM,EAAE;oCACtB,CAAC,CAAC,UAAU;oCACZ,CAAC,CAAC,MAAM,CAAA;gCACZ,OAAO,CAAC,GAAG,CACP,gCAAgC,CACnC,CAAA;gCACD,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAA;gCACxB,OAAO,CAAC,GAAG,CAAC,+BAA+B,CAAC,CAAA;gCAC5C,OAAO,IAAI,CAAC,IAAI,EAAE,CAAA;4BACtB,CAAC,CAAC,CACL,CAAA;wBACL,CAAC,CAAC,CAAA;oBACN,CAAC;iBACJ,CAAC,CAAA;YACN,CAAC;YAED,OAAO,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,OAAO,CAAC,WAAW,CAAC;gBAC1B,OAAO,EAAE,SAAS;gBAClB,KAAK,EAAE,GAAG,EAAE;oBACR,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAA;oBACxB,OAAO,CAAC,EAAC,EAAE,EAAE,IAAI,CAAC,KAAK,EAAE,EAAE,EAAE,IAAI,CAAC,KAAK,EAAC,CAAC,CAAA;gBAC7C,CAAC;aACJ,CAAC,CAAA;YAEF,OAAO,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,QAAQ;gBACd,KAAK,EAAE,GAAG,EAAE;oBACR,IAAI,CAAC,WAAW,CAAC,KAAK,EAAE,CAAA;oBACxB,OAAO,CAAC,aAAa,EAAE,CAAC,CAAA;gBAC5B,CAAC;aACJ,CAAC,CAAA;QACN,CAAC,CAAC,CAAA;QACF,IAAI,CAAC,WAAW,GAAG,IAAI,MAAM,CAAC;YAC1B,OAAO;YACP,KAAK,EAAE,GAAG;YACV,IAAI,EAAE,2CAA2C;YACjD,KAAK,EAAE,OAAO,CAAC,QAAQ,CAAC;YACxB,EAAE,EAAE,qBAAqB;SAC5B,CAAC,CAAA;QACF,IAAI,CAAC,WAAW,CAAC,IAAI,EAAE,CAAA;QACvB,IAAI,CAAC,SAAS,EAAE,CAAA;QAChB,IAAI,CAAC,WAAW,CAAC,YAAY,EAAE,CAAA;QAC/B,OAAO,CAAC,CAAA;IACZ,CAAC;IAED,SAAS;QACL,mCAAmC;QACnC,MAAM,OAAO,GAAG,QAAQ,CAAC,aAAa,CAAC,OAAO,CAAC,CAAA;QAC/C,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,eAAe,CAAC,CAAA;QACtC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,UAAU,CAAC,CAAA;QACjC,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,aAAa,CAChD,2BAA2B,CAC9B,CAAA;QACD,IAAI,CAAC,SAAS,GAAG,EAAE,CAAA;QACnB,IAAI,CAAC,WAAW,CAAC,OAAO,CAAC,CAAA;QAEzB,MAAM,WAAW,GACb,IAAI,CAAC,KAAK,KAAK,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC,CAAA;QAE/D,IAAI,CAAC,cAAc,GAAG,IAAI,kBAAkB,CAAC;YACzC,GAAG,EAAE,IAAI;YACT,OAAO,EAAE,CAAC,eAAe,EAAE,UAAU,CAAC;YACtC,OAAO,EAAE;gBACL;oBACI,MAAM,EAAE,CAAC;oBACT,MAAM,EAAE,IAAI;iBACf;gBACD;oBACI,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;oBACd,IAAI,EAAE,QAAQ;iBACjB;gBACD;oBACI,MAAM,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;oBACd,QAAQ,EAAE,KAAK;iBAClB;aACJ;YACD,IAAI,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;YAC1D,QAAQ,EAAE,CAAC;YACX,QAAQ,EAAE,KAAK;YACf,WAAW;YACX,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE;gBACJ,MAAM,EAAE,OAAO,CAAC,qBAAqB,CAAC,EAAE,yCAAyC;gBACjF,SAAS,EAAE,OAAO,CAAC,iBAAiB,CAAC,EAAE,sDAAsD;gBAC7F,WAAW,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC,+BAA+B;aACpE;YACD,QAAQ,EAAE,QAAQ,CAAC,EAAE;gBACjB,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;oBAClB,MAAM,CAAC,EAAE,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;oBAC/C,IAAI,CAAC,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAA;oBAChC,IAAI,CAAC,KAAK,GAAG,EAAE,CAAA;gBACnB,CAAC;qBAAM,CAAC;oBACJ,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;gBACtB,CAAC;YACL,CAAC;SACJ,CAAC,CAAA;QACF,IAAI,CAAC,cAAc,CAAC,IAAI,EAAE,CAAA;QAC1B,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,cAAc,CAAC,KAAK,CAAA;IAC1C,CAAC;IAED,cAAc,CAAC,KAAK;QAChB,OAAO;YACH,GAAG,KAAK,CAAC,EAAE,IAAI,KAAK,CAAC,KAAK,CAAC,EAAE,EAAE;YAC/B,KAAK,CAAC,KAAK,CAAC,SAAS,KAAK,SAAS;gBAC/B,CAAC,CAAC,aAAa,KAAK,CAAC,KAAK,CAAC,KAAK,4CAA4C;gBAC5E,CAAC,CAAC,aAAa,KAAK,CAAC,KAAK,CAAC,SAAS,4CAA4C;YACpF,UAAU,CAAC,KAAK,CAAC,KAAK,CAAC,KAAK,CAAC;SAChC,CAAA;IACL,CAAC;CACJ"}
|
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>`
|
package/src/database.js
ADDED
|
@@ -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
|
+
}
|