add: resize image

This commit is contained in:
xiangyu 2022-10-24 11:30:34 +08:00
parent 781cea3361
commit ef7071266a
7 changed files with 224 additions and 51 deletions

View File

@ -112,6 +112,36 @@ async function main() {
} s.`
);
await esbuild
.build({
entryPoints: ["src/editor/editorScript.ts"],
bundle: true,
outfile: path.join(buildDir, "temp/editorScript.js"),
// minify: true,
target: ["firefox60"],
})
.catch(() => process.exit(1));
const optionsScript = {
files: [path.join(buildDir, "addon/chrome/content/scripts/index.js")],
from: [/__placeholder:editorScript.js__/g],
to: [
fs
.readFileSync(path.join(buildDir, "temp/editorScript.js"))
.toString()
.replace(/\\/g, "\\\\"),
],
countMatches: true,
};
_ = replace.sync(optionsScript);
console.log(
"[Build] Run replace in ",
_.filter((f) => f.hasChanged).map(
(f) => `${f.file} : ${f.numReplacements} / ${f.numMatches}`
)
);
const optionsAddon = {
files: [
path.join(buildDir, "**/*.rdf"),

View File

@ -32,6 +32,7 @@
"compressing": "^1.5.1",
"crypto-js": "^4.1.1",
"esbuild": "^0.14.34",
"prosemirror-transform": "^1.7.0",
"replace-in-file": "^6.3.2",
"seedrandom": "^3.0.5",
"tree-model": "^1.0.7",

View File

@ -26,6 +26,15 @@ class EditorController extends AddonBase {
await this.editorPromise.promise;
}
injectScripts(_window: Window) {
if (!_window.document.getElementById("betternotes-script")) {
const messageScript = _window.document.createElement("script");
messageScript.id = "betternotes-script";
messageScript.innerHTML = `__placeholder:editorScript.js__`;
_window.document.head.append(messageScript);
}
}
recordEditor(instance: Zotero.EditorInstance) {
this.editorHistory.push({
instance: instance,

135
src/editor/editorScript.ts Normal file
View File

@ -0,0 +1,135 @@
// TODO: Move this somewhere else
const { Fragment, Slice } = require("prosemirror-model");
const { Step, StepResult } = require("prosemirror-transform");
class SetAttrsStep extends Step {
// :: (number, Object | null)
constructor(pos, attrs) {
super();
this.pos = pos;
this.attrs = attrs;
}
apply(doc) {
let target = doc.nodeAt(this.pos);
if (!target) return StepResult.fail("No node at given position");
let newNode = target.type.create(this.attrs, Fragment.emtpy, target.marks);
let slice = new Slice(Fragment.from(newNode), 0, target.isLeaf ? 0 : 1);
return StepResult.fromReplace(doc, this.pos, this.pos + 1, slice);
}
invert(doc) {
let target = doc.nodeAt(this.pos);
return new SetAttrsStep(this.pos, target ? target.attrs : null);
}
map(mapping) {
let pos = mapping.mapResult(this.pos, 1);
return pos.deleted ? null : new SetAttrsStep(pos.pos, this.attrs);
}
toJSON() {
return { stepType: "setAttrs", pos: this.pos, attrs: this.attrs };
}
static fromJSON(schema, json) {
if (
typeof json.pos != "number" ||
(json.attrs != null && typeof json.attrs != "object")
)
throw new RangeError("Invalid input for SetAttrsStep.fromJSON");
return new SetAttrsStep(json.pos, json.attrs);
}
}
// @ts-ignore
window.SetAttrsStep = SetAttrsStep;
// @ts-ignore
window.updateImageDimensions = function (
nodeID,
width,
height,
state,
dispatch
) {
let { tr } = state;
console.log(nodeID, width, height, state, dispatch);
state.doc.descendants((node, pos) => {
if (node.type.name === "image" && node.attrs.nodeID === nodeID) {
tr.step(new SetAttrsStep(pos, { ...node.attrs, width, height }));
tr.setMeta("addToHistory", false);
tr.setMeta("system", true);
dispatch(tr);
return false;
}
});
};
window.addEventListener(
"message",
async (e) => {
if (e.data.type === "exportPDF") {
console.log("exportPDF");
const container = document.getElementById(
"editor-container"
) as HTMLElement;
container.style.display = "none";
const fullPageStyle = document.createElement("style");
fullPageStyle.innerHTML =
"@page { margin: 0; } @media print{ body { height : auto; -webkit-print-color-adjust: exact; color-adjust: exact; }}";
document.body.append(fullPageStyle);
let t = 0;
let imageFlag = false;
while (!imageFlag && t < 500) {
await new Promise(function (resolve) {
setTimeout(resolve, 10);
});
imageFlag = !Array.prototype.find.call(
document.querySelectorAll("img"),
(e) => !e.getAttribute("src") || e.style.display === "none"
);
t += 1;
}
const editNode = document.querySelector(".primary-editor") as HTMLElement;
const printNode = editNode.cloneNode(true) as HTMLElement;
printNode.style.padding = "20px";
document.body.append(printNode);
let printFlag = false;
window.onafterprint = (e) => {
console.log("Print Dialog Closed..");
printFlag = true;
document.title = "Printed";
};
window.onmouseover = (e) => {
if (printFlag) {
document.title = "Printed";
printNode.remove();
container.style.removeProperty("display");
}
};
document.title = (printNode?.firstChild as HTMLElement).innerText;
console.log(document.title);
window.print();
} else if (e.data.type === "resizeImage") {
console.log("resizeImage");
// @ts-ignore
window.updateImageDimensions(
// @ts-ignore
_currentEditorInstance._editorCore.view.state.selection.node.attrs
.nodeID,
e.data.width,
undefined,
// @ts-ignore
_currentEditorInstance._editorCore.view.state,
// @ts-ignore
_currentEditorInstance._editorCore.view.dispatch
);
}
},
false
);

View File

@ -324,57 +324,7 @@ class EditorViews extends AddonBase {
_window.document.body.append(style);
}
if (!_window.document.getElementById("betternotes-script")) {
const messageScript = _window.document.createElement("script");
messageScript.id = "betternotes-script";
messageScript.innerHTML = `
window.addEventListener('message', async (e)=>{
if(e.data.type === "exportPDF"){
console.log("exportPDF");
const container = document.getElementById("editor-container");
container.style.display = "none";
const fullPageStyle = document.createElement("style");
fullPageStyle.innerHTML =
"@page { margin: 0; } @media print{ body { height : auto; -webkit-print-color-adjust: exact; color-adjust: exact; }}";
document.body.append(fullPageStyle);
let t = 0;
let imageFlag = false;
while(!imageFlag && t < 500){
await new Promise(function (resolve) {
setTimeout(resolve, 10);
});
imageFlag = !Array.prototype.find.call(document.querySelectorAll('img'), e=>(!e.getAttribute('src') || e.style.display === 'none'));
t += 1;
}
const editNode = document.querySelector(".primary-editor");
const printNode = editNode.cloneNode(true);
printNode.style.padding = "20px";
document.body.append(printNode);
let printFlag = false;
window.onafterprint = (e) => {
console.log('Print Dialog Closed..');
printFlag = true;
document.title = "Printed";
};
window.onmouseover = (e) => {
if (printFlag) {
document.title = "Printed";
printNode.remove();
container.style.removeProperty('display');
}
};
document.title = printNode.firstChild.innerText;
console.log(document.title);
window.print();
}
}, false)
`;
_window.document.head.append(messageScript);
}
this._Addon.EditorController.injectScripts(_window);
const moreDropdown: HTMLElement = Array.prototype.filter.call(
_window.document.querySelectorAll(".more-dropdown"),
@ -898,6 +848,13 @@ class EditorViews extends AddonBase {
return res;
};
const checkImageSelected = () => {
return (
(instance._iframeWindow as any).wrappedJSObject._currentEditorInstance
._editorCore.view.state.selection.node?.type?.name === "image"
);
};
const elementOptions: XULElementOptions = {
tag: "fragment",
subElementOptions: [
@ -907,6 +864,43 @@ class EditorViews extends AddonBase {
checkExistanceParent: instance._popup,
ignoreIfExists: true,
},
{
tag: "menuitem",
id: "menupopup-resizeImage",
checkExistanceParent: instance._popup,
ignoreIfExists: true,
attributes: [["label", "Resize Image"]],
customCheck: checkImageSelected,
listeners: [
[
"command",
(e) => {
const newWidth = parseFloat(
prompt(
"Enter new width (px):",
(instance._iframeWindow as any).wrappedJSObject
._currentEditorInstance._editorCore.view.state.selection
.node?.attrs?.width
)
);
if (newWidth && newWidth > 10) {
instance._iframeWindow.postMessage(
{ type: "resizeImage", width: newWidth },
"*"
);
}
},
undefined,
],
],
},
{
tag: "menuseparator",
id: "menupopup-resizeimagesplitter",
checkExistanceParent: instance._popup,
customCheck: checkImageSelected,
ignoreIfExists: true,
},
{
tag: "menuitem",
id: "menupopup-copylink",

View File

@ -386,6 +386,9 @@ class ZoteroViews extends AddonBase {
doc.querySelector(`#${options.id}`).remove();
}
}
if (options.customCheck && !options.customCheck()) {
return undefined;
}
const element = createElement();
if (options.id) {
element.id = options.id;

1
typing/global.d.ts vendored
View File

@ -19,5 +19,6 @@ declare interface XULElementOptions {
checkExistanceParent?: HTMLElement;
ignoreIfExists?: boolean;
removeIfExists?: boolean;
customCheck?: () => boolean;
subElementOptions?: Array<XULElementOptions>;
}