Skeleton of image plugin for editor.js
parent
ce1700b2b8
commit
50600de813
|
|
@ -10,10 +10,40 @@
|
||||||
"integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==",
|
"integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"@editorjs/editorjs": {
|
||||||
|
"version": "2.22.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.22.2.tgz",
|
||||||
|
"integrity": "sha512-rPCv7Z5LZebreQaaL4DZuWzoVGEqwB+P7BF1dsefGQNBmLyeLF412topeW2b6e+g4l1oQ7t75kCOACNTEyYYIA==",
|
||||||
|
"requires": {
|
||||||
|
"codex-notifier": "^1.1.2",
|
||||||
|
"codex-tooltip": "^1.0.2",
|
||||||
|
"nanoid": "^3.1.22"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@editorjs/header": {
|
||||||
|
"version": "2.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@editorjs/header/-/header-2.6.1.tgz",
|
||||||
|
"integrity": "sha512-EsnyVFv5uThpU9tbQ/dUPFCQoa/sBFy2n+9tN3wOXJGx7sjea4fdcacJ2UYhO+7pCgZ+aSgmMOyGLYHUFbchvA=="
|
||||||
|
},
|
||||||
|
"@editorjs/nested-list": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@editorjs/nested-list/-/nested-list-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-NumQfEivI29lOAuDMyVhn+VXUDGvWUPJMkjgKlUYRbnwgnPL4tK007+UzoVPLxv/f6lPOqeKcApvCj/MfskPNw=="
|
||||||
|
},
|
||||||
|
"@editorjs/quote": {
|
||||||
|
"version": "2.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@editorjs/quote/-/quote-2.4.0.tgz",
|
||||||
|
"integrity": "sha512-IWOBWjL2ngPP63GcIAltyD9kc7OVZFma4kS+T5JRHvKKDspYsnmrxsbRmCPc+coZQzqPxXHkiOZuNMdmGX/Y3w=="
|
||||||
|
},
|
||||||
|
"@editorjs/table": {
|
||||||
|
"version": "2.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@editorjs/table/-/table-2.0.1.tgz",
|
||||||
|
"integrity": "sha512-PB8VM+GPRwGy7IlF+WrEQw2A2c36xEXBnYIvf2VGNJo8A7PjYHtuWrlyHHCnGpY4lHXYnavZ/U8pKAfXv86XjA=="
|
||||||
|
},
|
||||||
"@tiptap/core": {
|
"@tiptap/core": {
|
||||||
"version": "2.0.0-beta.102",
|
"version": "2.0.0-beta.104",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.102.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.0.0-beta.104.tgz",
|
||||||
"integrity": "sha512-ykSAyYfyb14xiYWQ6mTaa+GF6j5dQvSDgeXQDNcy8xvBjZHm1g+51D0jm9FF/dsrY0rEps5h8yX883h7MaGFHA==",
|
"integrity": "sha512-eGmHljAgGFcIFHTykBaxOCUOeGJoZOJOwa3il7ci2GxrrMyvhvU421mv5njFoHxAiNQJEgdu+kWitDGYuxbixg==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/prosemirror-commands": "^1.0.4",
|
"@types/prosemirror-commands": "^1.0.4",
|
||||||
"@types/prosemirror-inputrules": "^1.0.4",
|
"@types/prosemirror-inputrules": "^1.0.4",
|
||||||
|
|
@ -22,7 +52,7 @@
|
||||||
"@types/prosemirror-schema-list": "^1.0.3",
|
"@types/prosemirror-schema-list": "^1.0.3",
|
||||||
"@types/prosemirror-state": "^1.2.7",
|
"@types/prosemirror-state": "^1.2.7",
|
||||||
"@types/prosemirror-transform": "^1.1.4",
|
"@types/prosemirror-transform": "^1.1.4",
|
||||||
"@types/prosemirror-view": "^1.17.2",
|
"@types/prosemirror-view": "^1.19.0",
|
||||||
"prosemirror-commands": "^1.1.10",
|
"prosemirror-commands": "^1.1.10",
|
||||||
"prosemirror-inputrules": "^1.1.3",
|
"prosemirror-inputrules": "^1.1.3",
|
||||||
"prosemirror-keymap": "^1.1.3",
|
"prosemirror-keymap": "^1.1.3",
|
||||||
|
|
@ -30,7 +60,7 @@
|
||||||
"prosemirror-schema-list": "^1.1.5",
|
"prosemirror-schema-list": "^1.1.5",
|
||||||
"prosemirror-state": "^1.3.4",
|
"prosemirror-state": "^1.3.4",
|
||||||
"prosemirror-transform": "^1.3.2",
|
"prosemirror-transform": "^1.3.2",
|
||||||
"prosemirror-view": "^1.19.3"
|
"prosemirror-view": "^1.20.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@tiptap/extension-blockquote": {
|
"@tiptap/extension-blockquote": {
|
||||||
|
|
@ -60,9 +90,9 @@
|
||||||
"integrity": "sha512-Kakg/RMiVrxjzIkLVDXtbCzRh/9W8dgSG04IhMZNOI8N9vWn8Z78jdUyxEEDTcL/JyWWcMxn9AsJw2U5ajO3pA=="
|
"integrity": "sha512-Kakg/RMiVrxjzIkLVDXtbCzRh/9W8dgSG04IhMZNOI8N9vWn8Z78jdUyxEEDTcL/JyWWcMxn9AsJw2U5ajO3pA=="
|
||||||
},
|
},
|
||||||
"@tiptap/extension-code-block": {
|
"@tiptap/extension-code-block": {
|
||||||
"version": "2.0.0-beta.17",
|
"version": "2.0.0-beta.18",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.17.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.0.0-beta.18.tgz",
|
||||||
"integrity": "sha512-u3RY991mXtjuw+trVaDwbAhuPPlU8l6kS4rXIxWJ5W/sNElbmfHLVu7RP++YwM8KOQrCrQl8TJbZTEIekMw61w==",
|
"integrity": "sha512-E2gz7ovl9nXLZzheqLyN3hi7A10fCaodDn4DvIl4wiEbKZpF7WFBNeb+FQetWNay9UWNeDO94SCX9+rT9H+yHA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"prosemirror-inputrules": "^1.1.3"
|
"prosemirror-inputrules": "^1.1.3"
|
||||||
}
|
}
|
||||||
|
|
@ -91,9 +121,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@tiptap/extension-hard-break": {
|
"@tiptap/extension-hard-break": {
|
||||||
"version": "2.0.0-beta.15",
|
"version": "2.0.0-beta.16",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.15.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.0.0-beta.16.tgz",
|
||||||
"integrity": "sha512-MS7MjGOtKtC1bVNAShwCetFRuk8nPr/j18OOzKChNrJFrZXWNJrid3dUojwDLqCraYdzSTmiOmMgU+yoUe/gnw=="
|
"integrity": "sha512-vRw8OIJlvlr17Y7mtJGL/dWbotX9fjgmA/zYqL//UIXQjp1FWW5JMh5E1Z5+jlJpGWjsWGH8fHpGSM2JCZVPRw=="
|
||||||
},
|
},
|
||||||
"@tiptap/extension-heading": {
|
"@tiptap/extension-heading": {
|
||||||
"version": "2.0.0-beta.15",
|
"version": "2.0.0-beta.15",
|
||||||
|
|
@ -131,9 +161,9 @@
|
||||||
"integrity": "sha512-t6xwEqP+d5443Ul2Jvqz9kXb3ro7bA7yY9HA0vskm3120WxxHW9jxgxZN+82Ot5Tm7nXOAlsN6vuqnt4idnxZQ=="
|
"integrity": "sha512-t6xwEqP+d5443Ul2Jvqz9kXb3ro7bA7yY9HA0vskm3120WxxHW9jxgxZN+82Ot5Tm7nXOAlsN6vuqnt4idnxZQ=="
|
||||||
},
|
},
|
||||||
"@tiptap/extension-ordered-list": {
|
"@tiptap/extension-ordered-list": {
|
||||||
"version": "2.0.0-beta.15",
|
"version": "2.0.0-beta.16",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.15.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.0.0-beta.16.tgz",
|
||||||
"integrity": "sha512-j9Xh8CYtV+C/wrTXEWN+U7NJIQ/cQrjta80Mm2hFiE2KDtFNkpsPqG6UBoky04EPFphR5xDUsO1nCT7T7Tei5A==",
|
"integrity": "sha512-3n0h5FBfQqBrN/zqF/Ngoyd1bZxeIRLwWI7ak4KulpvOg5V/yw3sw5CSxr2f13ZI9AgGaTq8yOsTYs9dkCCnsQ==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"prosemirror-inputrules": "^1.1.3"
|
"prosemirror-inputrules": "^1.1.3"
|
||||||
}
|
}
|
||||||
|
|
@ -159,12 +189,12 @@
|
||||||
"integrity": "sha512-rTQCnSnloSf6UN1y3zhu6j41MxrcCVWm5JIPX8VEt60WsOXJLAc/YJHLYi2FWhh/Psq8k78sPrmZbjYUrj3Dkw=="
|
"integrity": "sha512-rTQCnSnloSf6UN1y3zhu6j41MxrcCVWm5JIPX8VEt60WsOXJLAc/YJHLYi2FWhh/Psq8k78sPrmZbjYUrj3Dkw=="
|
||||||
},
|
},
|
||||||
"@tiptap/extension-table": {
|
"@tiptap/extension-table": {
|
||||||
"version": "2.0.0-beta.29",
|
"version": "2.0.0-beta.30",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.0.0-beta.29.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/extension-table/-/extension-table-2.0.0-beta.30.tgz",
|
||||||
"integrity": "sha512-2yK4dZboe7+KQoJeM0p6v7xx+G/yKDWbbtDtYRnFecD2Oiz1u44DeMKGzVqPXcMYWxmCnAeUK0kcbmwnPYyBTg==",
|
"integrity": "sha512-s6+HRo3sFv7SUprsUAAF27hg7inITpzl78If3XdrpscuzTVuRmd7GsFnY+aZGPVikekwCMjp/0klE92P4A7w0w==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"prosemirror-tables": "^1.1.1",
|
"prosemirror-tables": "^1.1.1",
|
||||||
"prosemirror-view": "^1.19.3"
|
"prosemirror-view": "^1.20.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@tiptap/extension-table-cell": {
|
"@tiptap/extension-table-cell": {
|
||||||
|
|
@ -188,31 +218,36 @@
|
||||||
"integrity": "sha512-0EtAwuRldCAoFaL/iXgkRepEeOd55rPg5N4FQUN1xTwZT7PDofukP0DG/2jff/Uj17x4uTaJAa9qlFWuNnDvjw=="
|
"integrity": "sha512-0EtAwuRldCAoFaL/iXgkRepEeOd55rPg5N4FQUN1xTwZT7PDofukP0DG/2jff/Uj17x4uTaJAa9qlFWuNnDvjw=="
|
||||||
},
|
},
|
||||||
"@tiptap/starter-kit": {
|
"@tiptap/starter-kit": {
|
||||||
"version": "2.0.0-beta.101",
|
"version": "2.0.0-beta.103",
|
||||||
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.101.tgz",
|
"resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.0.0-beta.103.tgz",
|
||||||
"integrity": "sha512-1SoVrPe2JMPo5/Je0NprZWN8G0q0EOTpoVHQ/+VwthectJBz1TyCuNOqoR1WNL9II6Fj9AZelyhPWacw7vM27A==",
|
"integrity": "sha512-Kmd7hLCc2JmmMijc6ng7u+OQBJYZBQJKznjgiUSuwJhAkROVdjgx7mz8p2J+tl81QpM4yHVBpj4W9Al2kiFRng==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@tiptap/core": "^2.0.0-beta.102",
|
"@tiptap/core": "^2.0.0-beta.104",
|
||||||
"@tiptap/extension-blockquote": "^2.0.0-beta.15",
|
"@tiptap/extension-blockquote": "^2.0.0-beta.15",
|
||||||
"@tiptap/extension-bold": "^2.0.0-beta.15",
|
"@tiptap/extension-bold": "^2.0.0-beta.15",
|
||||||
"@tiptap/extension-bullet-list": "^2.0.0-beta.15",
|
"@tiptap/extension-bullet-list": "^2.0.0-beta.15",
|
||||||
"@tiptap/extension-code": "^2.0.0-beta.16",
|
"@tiptap/extension-code": "^2.0.0-beta.16",
|
||||||
"@tiptap/extension-code-block": "^2.0.0-beta.17",
|
"@tiptap/extension-code-block": "^2.0.0-beta.18",
|
||||||
"@tiptap/extension-document": "^2.0.0-beta.13",
|
"@tiptap/extension-document": "^2.0.0-beta.13",
|
||||||
"@tiptap/extension-dropcursor": "^2.0.0-beta.19",
|
"@tiptap/extension-dropcursor": "^2.0.0-beta.19",
|
||||||
"@tiptap/extension-gapcursor": "^2.0.0-beta.19",
|
"@tiptap/extension-gapcursor": "^2.0.0-beta.19",
|
||||||
"@tiptap/extension-hard-break": "^2.0.0-beta.15",
|
"@tiptap/extension-hard-break": "^2.0.0-beta.16",
|
||||||
"@tiptap/extension-heading": "^2.0.0-beta.15",
|
"@tiptap/extension-heading": "^2.0.0-beta.15",
|
||||||
"@tiptap/extension-history": "^2.0.0-beta.16",
|
"@tiptap/extension-history": "^2.0.0-beta.16",
|
||||||
"@tiptap/extension-horizontal-rule": "^2.0.0-beta.19",
|
"@tiptap/extension-horizontal-rule": "^2.0.0-beta.19",
|
||||||
"@tiptap/extension-italic": "^2.0.0-beta.15",
|
"@tiptap/extension-italic": "^2.0.0-beta.15",
|
||||||
"@tiptap/extension-list-item": "^2.0.0-beta.14",
|
"@tiptap/extension-list-item": "^2.0.0-beta.14",
|
||||||
"@tiptap/extension-ordered-list": "^2.0.0-beta.15",
|
"@tiptap/extension-ordered-list": "^2.0.0-beta.16",
|
||||||
"@tiptap/extension-paragraph": "^2.0.0-beta.17",
|
"@tiptap/extension-paragraph": "^2.0.0-beta.17",
|
||||||
"@tiptap/extension-strike": "^2.0.0-beta.17",
|
"@tiptap/extension-strike": "^2.0.0-beta.17",
|
||||||
"@tiptap/extension-text": "^2.0.0-beta.13"
|
"@tiptap/extension-text": "^2.0.0-beta.13"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@types/editorjs__header": {
|
||||||
|
"version": "2.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/editorjs__header/-/editorjs__header-2.6.0.tgz",
|
||||||
|
"integrity": "sha512-J9TyO/BjNVddi+syyXpvMRMtVz5Z62pwmFKynWsgP+wnJYdWF8ABqgomokIsAvuEwH5NHa/YxsTltYcPPGCRRQ=="
|
||||||
|
},
|
||||||
"@types/eslint": {
|
"@types/eslint": {
|
||||||
"version": "7.28.0",
|
"version": "7.28.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-7.28.0.tgz",
|
||||||
|
|
@ -358,9 +393,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"@types/prosemirror-view": {
|
"@types/prosemirror-view": {
|
||||||
"version": "1.18.0",
|
"version": "1.19.0",
|
||||||
"resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.18.0.tgz",
|
"resolved": "https://registry.npmjs.org/@types/prosemirror-view/-/prosemirror-view-1.19.0.tgz",
|
||||||
"integrity": "sha512-7NBy7qIV/ig49ThfkrIJrvW8E+HwumMgmpopUTYJlKwOh/fQ6SVUG/RtdnAIBLD+4uK0R2SMObbGZm06x6OwbA==",
|
"integrity": "sha512-Y8OX9L+Yni0HgXAN9wcNSf61IId13uqpURnRC5WkmCOlVDsr35vfGjj+tcaQL4dZzblsu3bRkXI/c0oGXp+xgw==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/prosemirror-model": "*",
|
"@types/prosemirror-model": "*",
|
||||||
"@types/prosemirror-state": "*",
|
"@types/prosemirror-state": "*",
|
||||||
|
|
@ -726,6 +761,16 @@
|
||||||
"shallow-clone": "^3.0.0"
|
"shallow-clone": "^3.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"codex-notifier": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/codex-notifier/-/codex-notifier-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg=="
|
||||||
|
},
|
||||||
|
"codex-tooltip": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.2.tgz",
|
||||||
|
"integrity": "sha512-oC+Bu5X/zyhbPydgMSLWKoM/+vkJMqaLWu3Dt/jZgXS3MWK23INwC5DMBrVXZSufAFk0i0SUni38k9rLMyZn/w=="
|
||||||
|
},
|
||||||
"color-convert": {
|
"color-convert": {
|
||||||
"version": "1.9.3",
|
"version": "1.9.3",
|
||||||
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
|
||||||
|
|
@ -781,6 +826,11 @@
|
||||||
"object-keys": "^1.0.12"
|
"object-keys": "^1.0.12"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"editorjs-latex": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/editorjs-latex/-/editorjs-latex-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-HIfVZFy4CJpLQk6xgNUmftxzTo5p4POFFEnW3cPUAKoqcL9FhLPr3vm8Yll+qirHla1b8okNmyw8lPIfUtXLaw=="
|
||||||
|
},
|
||||||
"electron-to-chromium": {
|
"electron-to-chromium": {
|
||||||
"version": "1.3.830",
|
"version": "1.3.830",
|
||||||
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.830.tgz",
|
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.830.tgz",
|
||||||
|
|
@ -1416,6 +1466,11 @@
|
||||||
"brace-expansion": "^1.1.7"
|
"brace-expansion": "^1.1.7"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"nanoid": {
|
||||||
|
"version": "3.1.25",
|
||||||
|
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.25.tgz",
|
||||||
|
"integrity": "sha512-rdwtIXaXCLFAQbnfqDRnI6jaRHp9fTcYBjtFKE8eezcZ7LuLjhUaQGNeMXf1HmRoCH32CLz6XwX0TtxEOS/A3Q=="
|
||||||
|
},
|
||||||
"neo-async": {
|
"neo-async": {
|
||||||
"version": "2.6.2",
|
"version": "2.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
"resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz",
|
||||||
|
|
@ -1734,9 +1789,9 @@
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"prosemirror-view": {
|
"prosemirror-view": {
|
||||||
"version": "1.20.0",
|
"version": "1.20.1",
|
||||||
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.20.0.tgz",
|
"resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.20.1.tgz",
|
||||||
"integrity": "sha512-OqU/bHUIiJhpyb2ytX4fLalYAJJOyZ0k5H0AibP/WPsdHq9CqmJFU676gO+N8WWhR5tVz1NxsqMZgEBy5Lc6GQ==",
|
"integrity": "sha512-djWORhy3a706mUH4A2dgEEV0IPZqQd1tFyz/ZVHJNoqhSgq82FwG6dq7uqHeUB2KdVSNfI2yc3rwfqlC/ll2pA==",
|
||||||
"requires": {
|
"requires": {
|
||||||
"prosemirror-model": "^1.14.3",
|
"prosemirror-model": "^1.14.3",
|
||||||
"prosemirror-state": "^1.0.0",
|
"prosemirror-state": "^1.0.0",
|
||||||
|
|
|
||||||
|
|
@ -22,17 +22,24 @@
|
||||||
"webpack-cli": "^4.8.0"
|
"webpack-cli": "^4.8.0"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@tiptap/core": "^2.0.0-beta.102",
|
"@editorjs/editorjs": "^2.22.2",
|
||||||
|
"@editorjs/header": "^2.6.1",
|
||||||
|
"@editorjs/nested-list": "^1.0.2",
|
||||||
|
"@editorjs/quote": "^2.4.0",
|
||||||
|
"@editorjs/table": "^2.0.1",
|
||||||
|
"@tiptap/core": "^2.0.0-beta.103",
|
||||||
"@tiptap/extension-subscript": "^2.0.0-beta.4",
|
"@tiptap/extension-subscript": "^2.0.0-beta.4",
|
||||||
"@tiptap/extension-superscript": "^2.0.0-beta.4",
|
"@tiptap/extension-superscript": "^2.0.0-beta.4",
|
||||||
"@tiptap/extension-table": "^2.0.0-beta.29",
|
"@tiptap/extension-table": "^2.0.0-beta.30",
|
||||||
"@tiptap/extension-table-cell": "^2.0.0-beta.14",
|
"@tiptap/extension-table-cell": "^2.0.0-beta.14",
|
||||||
"@tiptap/extension-table-header": "^2.0.0-beta.16",
|
"@tiptap/extension-table-header": "^2.0.0-beta.16",
|
||||||
"@tiptap/extension-table-row": "^2.0.0-beta.14",
|
"@tiptap/extension-table-row": "^2.0.0-beta.14",
|
||||||
"@tiptap/starter-kit": "^2.0.0-beta.101",
|
"@tiptap/starter-kit": "^2.0.0-beta.102",
|
||||||
|
"@types/editorjs__header": "^2.6.0",
|
||||||
"acorn": "^8.4.1",
|
"acorn": "^8.4.1",
|
||||||
"bootstrap": "^4.6.0",
|
"bootstrap": "^4.6.0",
|
||||||
"bootstrap-icons": "^1.5.0",
|
"bootstrap-icons": "^1.5.0",
|
||||||
|
"editorjs-latex": "^1.0.0",
|
||||||
"jquery": "^3.6.0",
|
"jquery": "^3.6.0",
|
||||||
"popper.js": "^1.16.1",
|
"popper.js": "^1.16.1",
|
||||||
"sortablejs": "^1.14.0"
|
"sortablejs": "^1.14.0"
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,659 @@
|
||||||
|
<!DOCTYPE html [<!ENTITY times '×'>]>
|
||||||
|
<html
|
||||||
|
xmlns="http://www.w3.org/1999/xhtml"
|
||||||
|
xmlns:bootstrap="http://xmlns.jcp.org/jsf/composite/components/bootstrap"
|
||||||
|
xmlns:cc="http://xmlns.jcp.org/jsf/composite"
|
||||||
|
xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"
|
||||||
|
xmlns:ui="http://xmlns.jcp.org/jsf/facelets"
|
||||||
|
>
|
||||||
|
<cc:interface
|
||||||
|
shortDescription="A editor component for HTML texts using the TipTap editor. To use this component you have also to include the cms-editor.js file into the page."
|
||||||
|
>
|
||||||
|
<!-- <cc:attribute
|
||||||
|
name="addMethod"
|
||||||
|
required="true"
|
||||||
|
shortDescription="URL of teh endpoint for adding localized values."
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="addButtonLabel"
|
||||||
|
default="Add"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the add button"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="addDialogCancelLabel"
|
||||||
|
default="#{CmsAdminMessages['cancel_button.label']}"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the cancel and close buttons."
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="addDialogLocaleSelectHelp"
|
||||||
|
default="The locale of the value of add"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Help text for the locale select field"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="addDialogLocaleSelectLabel"
|
||||||
|
default="#{CmsAdminMessages['locale_select.label']}"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the locale select field"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="addDialogSubmitLabel"
|
||||||
|
default="#{CmsAdminMessages['locale_add.label']}"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the submit button"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="addDialogTitle"
|
||||||
|
default="#{CmsAdminMessages['locale_add.dialog.title']}"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Title for the dialog."
|
||||||
|
type="String"
|
||||||
|
/>-->
|
||||||
|
<cc:attribute
|
||||||
|
name="backUrl"
|
||||||
|
required="true"
|
||||||
|
shortDescription="The back URL."
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="canEdit"
|
||||||
|
default="true"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Can the current user edit the text?"
|
||||||
|
type="boolean"
|
||||||
|
/>
|
||||||
|
<!-- <cc:attribute
|
||||||
|
name="editButtonLabel"
|
||||||
|
default="#{CmsAdminMessages['edit_button.label']}"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the edit button"
|
||||||
|
type="String"
|
||||||
|
/>-->
|
||||||
|
<cc:attribute
|
||||||
|
name="editDialogCancelLabel"
|
||||||
|
default="#{CmsAdminMessages['cancel_button.label']}"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the cancel and close button of the edit dialog"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="editDialogSubmitLabel"
|
||||||
|
default="#{CmsAdminMessages['save_button.label']}"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the submit button of the edit dialog"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="editDialogValueHelp"
|
||||||
|
default="Value to update"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Help text for the value field"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="editDialogValueLabel"
|
||||||
|
default="Value"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the value field"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="editDialogTitle"
|
||||||
|
default="#{CmsAdminMessages['text.edit.dialog.title']}"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Title for the edit dialog"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="editMethod"
|
||||||
|
required="true"
|
||||||
|
shortDescription="URL of the endpoint for editing/updating a value. The value of the locale to update is added after the provided URL, eg. /de for updating the german value."
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<!-- <cc:attribute
|
||||||
|
name="hasUnusedLocales"
|
||||||
|
required="true"
|
||||||
|
shortDescription="Are there unused locales? This will usually be an expression pointing to some method. The result must resolve to boolean."
|
||||||
|
type="boolean"
|
||||||
|
/>-->
|
||||||
|
<cc:attribute
|
||||||
|
name="headingLevel"
|
||||||
|
default="2"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Level of the heading used for the component. Also determines the heading levels used for other parts of the component."
|
||||||
|
type="int"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="editorId"
|
||||||
|
required="true"
|
||||||
|
shortDescription="ID for the editor. Also used as prefix to generate IDs for some subcomponents"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<!-- <cc:attribute
|
||||||
|
name="emptyText"
|
||||||
|
default="#{text.editor.no_localized_values}"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Text shown if the localized has no values yet."
|
||||||
|
type="String"
|
||||||
|
/>-->
|
||||||
|
<cc:attribute
|
||||||
|
name="messageSaveFailed"
|
||||||
|
default="Failed to save."
|
||||||
|
required="false"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="messageSaveSuccessful"
|
||||||
|
default="Saved sucessfully."
|
||||||
|
required="false"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="messageVariantLoadFailed"
|
||||||
|
default="Failed to load variant."
|
||||||
|
required="false"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="mode"
|
||||||
|
default="full"
|
||||||
|
shortDescription="The editor mode. Use 'full' to enable all options including adding images etc. Use 'textonly' for a minimal version that only supports text editing, but does not allow insertation of images etc."
|
||||||
|
required="false"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="objectIdentifier"
|
||||||
|
required="true"
|
||||||
|
shortDescription="Identifier of the object to which the localized string belongs"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="selectedLocale"
|
||||||
|
required="true"
|
||||||
|
shortDescription="The selected locale."
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<!-- <cc:attribute
|
||||||
|
name="removeButtonLabel"
|
||||||
|
default="Remove"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the remove button"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="removeDialogCancelLabel"
|
||||||
|
default="Cancel"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the cancel and close buttons of the remove dialog"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="removeDialogSubmitLabel"
|
||||||
|
default="Remove localized value"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Label for the submit button of the remove dialog (removes the localized value)"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="removeDialogText"
|
||||||
|
default="Are you sure to remove the following localized value?"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Text for the remove dialog"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="removeDialogTitle"
|
||||||
|
default="Remove localized value"
|
||||||
|
required="false"
|
||||||
|
shortDescription="The title of the remove dialog"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="removeMethod"
|
||||||
|
required="true"
|
||||||
|
shortDescription="URL of the endpoint for remvoving a value. The locale of the value to remove is added to the end of the URL. Eg. /de for removing the value for the locale de."
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="tableActionsHeading"
|
||||||
|
default="Actions"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Heading for the action columns"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="tableLocaleHeading"
|
||||||
|
default="Locale"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Heading for the locale column"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="tableWordCountHeading"
|
||||||
|
default="Words"
|
||||||
|
required="false"
|
||||||
|
shortDescription="Heading for the word count column"
|
||||||
|
type="String"
|
||||||
|
/>-->
|
||||||
|
<cc:attribute
|
||||||
|
name="title"
|
||||||
|
required="true"
|
||||||
|
shortDescription="Title/Heading of the editor widget"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<!-- <cc:attribute
|
||||||
|
name="unusedLocales"
|
||||||
|
required="true"
|
||||||
|
shortDescription="A collection of the unused locales of the edited localized string"
|
||||||
|
type="java.util.Collection"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="variants"
|
||||||
|
required="true"
|
||||||
|
shortDescription="Info about the available variants. Must be a List of CmsEditorLocaleVariantRow objects."
|
||||||
|
type="java.util.List"
|
||||||
|
/>-->
|
||||||
|
<cc:attribute
|
||||||
|
name="variantUrl"
|
||||||
|
required="true"
|
||||||
|
shortDescription="URL of the endpoint for retrieving a variant. The locale of the variant to retrieve is appended as last token."
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<!-- <cc:attribute name="viewButtonLabel" default="View" type="String" />
|
||||||
|
<cc:attribute name="viewDialogTitle" default="View" type="String" />
|
||||||
|
<cc:attribute
|
||||||
|
name="viewDialogEditButtonLabel"
|
||||||
|
default="Edit"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="viewDialogCloseButtonLabel"
|
||||||
|
default="Close"
|
||||||
|
type="String"
|
||||||
|
/>
|
||||||
|
<cc:attribute
|
||||||
|
name="wordCountUrl"
|
||||||
|
required="true"
|
||||||
|
shortDescription="URL of the endpoint for retrieving the wordcount of a variant. The locale of the variant to retrieve is appended as last token."
|
||||||
|
type="String"
|
||||||
|
/>-->
|
||||||
|
</cc:interface>
|
||||||
|
<cc:implementation>
|
||||||
|
<div
|
||||||
|
class="cms-editor"
|
||||||
|
data-locale="#{cc.attrs.selectedLocale}"
|
||||||
|
data-save-url="#{cc.attrs.editMethod}/#{variant.locale}"
|
||||||
|
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
||||||
|
id="#{cc.attrs.editorId}"
|
||||||
|
>
|
||||||
|
<c:choose>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 1}">
|
||||||
|
<h1>#{cc.attrs.title}</h1>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 2}">
|
||||||
|
<h2>#{cc.attrs.title}</h2>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 3}">
|
||||||
|
<h3>#{cc.attrs.title}</h3>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 4}">
|
||||||
|
<h4>#{cc.attrs.title}</h4>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 5}">
|
||||||
|
<h5>#{cc.attrs.title}</h5>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 6}">
|
||||||
|
<h6>#{cc.attrs.title}</h6>
|
||||||
|
</c:when>
|
||||||
|
<c:otherwise>
|
||||||
|
<div>#{cc.attrs.title}</div>
|
||||||
|
</c:otherwise>
|
||||||
|
</c:choose>
|
||||||
|
<div class="cms-editor-messages">
|
||||||
|
<template id="cms-editor-msg-canvas-element-not-found">
|
||||||
|
<div class="alert alert alert-danger" role="alert">
|
||||||
|
#{CmsAdminMessages['cms_editor.internal_error']}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template id="cms-editor-msg-save-failed">
|
||||||
|
<div class="alert alert alert-danger" role="alert">
|
||||||
|
#{cc.attrs.messageSaveFailed}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template id="cms-editor-msg-save-successful">
|
||||||
|
<div class="alert alert-success" role="alert">
|
||||||
|
#{cc.attrs.messageSaveSuccessful}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<template id="cms-editor-msg-variant-load-failed">
|
||||||
|
<div class="alert alert-warning" role="alert">
|
||||||
|
#{cc.attrs.messageVariantLoadFailed}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
<c:choose>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 1}">
|
||||||
|
<h2 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h2>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 2}">
|
||||||
|
<h3 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h3>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 3}">
|
||||||
|
<h4 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h4>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 4}">
|
||||||
|
<h5 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h5>
|
||||||
|
</c:when>
|
||||||
|
<c:when test="#{cc.attrs.headingLevel == 5}">
|
||||||
|
<h6 id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</h6>
|
||||||
|
</c:when>
|
||||||
|
<c:otherwise>
|
||||||
|
<div id="#{cc.attrs.editorId}-edit-dialog-title">
|
||||||
|
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
||||||
|
</div>
|
||||||
|
</c:otherwise>
|
||||||
|
</c:choose>
|
||||||
|
<div
|
||||||
|
class="cms-tiptap-editor"
|
||||||
|
data-locale="#{cc.attrs.selectedLocale}"
|
||||||
|
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
||||||
|
>
|
||||||
|
<div class="cms-tiptap-editor-buttons mb-1">
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-textformatting">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-emph"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.emph']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="type-italic" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-strong-emph"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.strong_emph']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="type-bold" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-code"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.code']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="code" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-strikethrough"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.strikethrough']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="type-strikethrough" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-subscript"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.subscript']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">x<sub>n</sub></span>
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-superscript"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.superscript']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">x<sup>n</sup></span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-headings">
|
||||||
|
<ui:repeat begin="1" end="6" var="level">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-h#{level}"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.h'.concat(level)]}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<span aria-hidden="true">H#{level}</span>
|
||||||
|
</button>
|
||||||
|
</ui:repeat>
|
||||||
|
</div>
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-paragraphs">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-paragraph"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.paragraph']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="type" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-blockquote"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.blockquote']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="blockquote-left" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-codeblock"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.codeblock']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="code-square" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-lists">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-ul"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.ul']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="list-ul" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-ol"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.ol']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="list-ol" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-table">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table"
|
||||||
|
data-target="#insert-table-dialog"
|
||||||
|
data-toggle="modal"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="table" />
|
||||||
|
</button>
|
||||||
|
<div
|
||||||
|
aria-hidden="true"
|
||||||
|
aria-labelledby="insert-table-dialog-title"
|
||||||
|
class="modal fade cms-editor-insert-table-dialog"
|
||||||
|
id="insert-table-dialog"
|
||||||
|
tabindex="-1"
|
||||||
|
>
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<form class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h4
|
||||||
|
class="modal-title"
|
||||||
|
id="insert-table-dialog-title"
|
||||||
|
>
|
||||||
|
#{CmsAdminMessages['cms_editor.dialogs.insert_table.title']}
|
||||||
|
</h4>
|
||||||
|
<button
|
||||||
|
aria-label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.close']}"
|
||||||
|
class="close"
|
||||||
|
data-dismiss="modal"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<bootstrap:svgIcon icon="x" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
<bootstrap:formGroupNumber
|
||||||
|
help="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.rows.help']}"
|
||||||
|
inputId="rows"
|
||||||
|
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.rows.label']}"
|
||||||
|
name="rows"
|
||||||
|
/>
|
||||||
|
<bootstrap:formGroupNumber
|
||||||
|
help="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.cols.help']}"
|
||||||
|
inputId="cols"
|
||||||
|
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.cols.label']}"
|
||||||
|
name="cols"
|
||||||
|
/>
|
||||||
|
<bootstrap:formCheck
|
||||||
|
checked="true"
|
||||||
|
inputId="headerRow"
|
||||||
|
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.header_row.label']}"
|
||||||
|
name="headerRow"
|
||||||
|
value="true"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button
|
||||||
|
class="
|
||||||
|
btn btn-warning
|
||||||
|
cms-editor-cancel-button
|
||||||
|
"
|
||||||
|
data-dismiss="modal"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.close']}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-success"
|
||||||
|
data-dismiss="modal"
|
||||||
|
data-backdrop="false"
|
||||||
|
type="submit"
|
||||||
|
>
|
||||||
|
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.submit']}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-remove-table"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.remove_table']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-remove.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table-row-before"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_row_before']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-add-row-before.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table-row-after"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_row_after']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-add-row-after.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-table-remove-row"
|
||||||
|
title="#{CmsAdminMessages['cms_editor.buttons.remove_table_row']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-remove-row.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table-column-before"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_column_before']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-add-column-before.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-table-column-after"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_column_after']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-add-column-after.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-remove-table-column"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.remove_table_column']}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-remove-column.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-toggle-table-header-row"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.toggle_header_row']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-toggle-header-row.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-toggle-table-header-column"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.toggle_header_column']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-toggle-header-column.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-merge-table-cells"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.merge-table-cells']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-merge-cells.svg" />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-split-table-cell"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.split-table-cell']}"
|
||||||
|
type="button">
|
||||||
|
<img src="#{request.contextPath}/icons/cms-editor/table-split-cells.svg" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-media">
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-dark tiptap-insert-image"
|
||||||
|
title="#{CmsAdminMessage['cms_editor.buttons.insert_image']}"
|
||||||
|
type="button">
|
||||||
|
<bootstrap:svgIcon icon="image" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="cms-tiptap-editor-canvas border"
|
||||||
|
data-locale="#{cc.attrs.selectedLocale}"
|
||||||
|
data-variant-url="#{cc.attrs.variantUrl}"
|
||||||
|
></div>
|
||||||
|
<div id="html-preview"></div>
|
||||||
|
<div class="mt-3">
|
||||||
|
<a
|
||||||
|
class="btn btn-warning cms-editor-cancel-button"
|
||||||
|
href="#{backUrl}"
|
||||||
|
>
|
||||||
|
#{cc.attrs.editDialogCancelLabel}
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
class="btn btn-success cms-editor-save-button"
|
||||||
|
disabled="#{cc.attrs.canEdit ? '' : 'disabled'}"
|
||||||
|
type="button"
|
||||||
|
>
|
||||||
|
#{cc.attrs.editDialogSubmitLabel}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</cc:implementation>
|
||||||
|
</html>
|
||||||
|
|
@ -293,358 +293,22 @@
|
||||||
data-save-url="#{cc.attrs.editMethod}/#{variant.locale}"
|
data-save-url="#{cc.attrs.editMethod}/#{variant.locale}"
|
||||||
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
||||||
id="#{cc.attrs.editorId}"
|
id="#{cc.attrs.editorId}"
|
||||||
>
|
></div>
|
||||||
<c:choose>
|
<div id="editor-preview"></div>
|
||||||
<c:when test="#{cc.attrs.headingLevel == 1}">
|
<div class="mt-3">
|
||||||
<h1>#{cc.attrs.title}</h1>
|
<a
|
||||||
</c:when>
|
class="btn btn-warning cms-editor-cancel-button"
|
||||||
<c:when test="#{cc.attrs.headingLevel == 2}">
|
href="#{backUrl}"
|
||||||
<h2>#{cc.attrs.title}</h2>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 3}">
|
|
||||||
<h3>#{cc.attrs.title}</h3>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 4}">
|
|
||||||
<h4>#{cc.attrs.title}</h4>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 5}">
|
|
||||||
<h5>#{cc.attrs.title}</h5>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 6}">
|
|
||||||
<h6>#{cc.attrs.title}</h6>
|
|
||||||
</c:when>
|
|
||||||
<c:otherwise>
|
|
||||||
<div>#{cc.attrs.title}</div>
|
|
||||||
</c:otherwise>
|
|
||||||
</c:choose>
|
|
||||||
<div class="cms-editor-messages">
|
|
||||||
<template id="cms-editor-msg-canvas-element-not-found">
|
|
||||||
<div class="alert alert alert-danger" role="alert">
|
|
||||||
#{CmsAdminMessages['cms_editor.internal_error']}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template id="cms-editor-msg-save-failed">
|
|
||||||
<div class="alert alert alert-danger" role="alert">
|
|
||||||
#{cc.attrs.messageSaveFailed}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template id="cms-editor-msg-save-successful">
|
|
||||||
<div class="alert alert-success" role="alert">
|
|
||||||
#{cc.attrs.messageSaveSuccessful}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
<template id="cms-editor-msg-variant-load-failed">
|
|
||||||
<div class="alert alert-warning" role="alert">
|
|
||||||
#{cc.attrs.messageVariantLoadFailed}
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
<c:choose>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 1}">
|
|
||||||
<h2 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h2>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 2}">
|
|
||||||
<h3 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h3>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 3}">
|
|
||||||
<h4 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h4>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 4}">
|
|
||||||
<h5 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h5>
|
|
||||||
</c:when>
|
|
||||||
<c:when test="#{cc.attrs.headingLevel == 5}">
|
|
||||||
<h6 id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</h6>
|
|
||||||
</c:when>
|
|
||||||
<c:otherwise>
|
|
||||||
<div id="#{cc.attrs.editorId}-edit-dialog-title">
|
|
||||||
#{cc.attrs.editDialogTitle} #{cc.attrs.selectedLocale}
|
|
||||||
</div>
|
|
||||||
</c:otherwise>
|
|
||||||
</c:choose>
|
|
||||||
<div
|
|
||||||
class="cms-tiptap-editor"
|
|
||||||
data-locale="#{cc.attrs.selectedLocale}"
|
|
||||||
data-variant-url="#{cc.attrs.variantUrl}/#{cc.attrs.selectedLocale}"
|
|
||||||
>
|
>
|
||||||
<div class="cms-tiptap-editor-buttons mb-1">
|
#{cc.attrs.editDialogCancelLabel}
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-textformatting">
|
</a>
|
||||||
<button
|
<button
|
||||||
class="btn btn-outline-dark tiptap-emph"
|
class="btn btn-success cms-editor-save-button"
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.emph']}"
|
disabled="#{cc.attrs.canEdit ? '' : 'disabled'}"
|
||||||
type="button"
|
type="button"
|
||||||
>
|
>
|
||||||
<bootstrap:svgIcon icon="type-italic" />
|
#{cc.attrs.editDialogSubmitLabel}
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-strong-emph"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.strong_emph']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="type-bold" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-code"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.code']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="code" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-strikethrough"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.strikethrough']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="type-strikethrough" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-subscript"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.subscript']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">x<sub>n</sub></span>
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-superscript"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.superscript']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">x<sup>n</sup></span>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-headings">
|
|
||||||
<ui:repeat begin="1" end="6" var="level">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-h#{level}"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.h'.concat(level)]}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<span aria-hidden="true">H#{level}</span>
|
|
||||||
</button>
|
|
||||||
</ui:repeat>
|
|
||||||
</div>
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-paragraphs">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-paragraph"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.paragraph']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="type" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-blockquote"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.blockquote']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="blockquote-left" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-codeblock"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.codeblock']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="code-square" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-lists">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-ul"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.ul']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="list-ul" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-ol"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.ol']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="list-ol" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="px-2 cms-tiptap-editor-button-row cms-tiptap-editor-table">
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table"
|
|
||||||
data-target="#insert-table-dialog"
|
|
||||||
data-toggle="modal"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="table" />
|
|
||||||
</button>
|
|
||||||
<div
|
|
||||||
aria-hidden="true"
|
|
||||||
aria-labelledby="insert-table-dialog-title"
|
|
||||||
class="modal fade cms-editor-insert-table-dialog"
|
|
||||||
id="insert-table-dialog"
|
|
||||||
tabindex="-1"
|
|
||||||
>
|
|
||||||
<div class="modal-dialog">
|
|
||||||
<form class="modal-content">
|
|
||||||
<div class="modal-header">
|
|
||||||
<h4
|
|
||||||
class="modal-title"
|
|
||||||
id="insert-table-dialog-title"
|
|
||||||
>
|
|
||||||
#{CmsAdminMessages['cms_editor.dialogs.insert_table.title']}
|
|
||||||
</h4>
|
|
||||||
<button
|
|
||||||
aria-label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.close']}"
|
|
||||||
class="close"
|
|
||||||
data-dismiss="modal"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<bootstrap:svgIcon icon="x" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="modal-body">
|
|
||||||
<bootstrap:formGroupNumber
|
|
||||||
help="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.rows.help']}"
|
|
||||||
inputId="rows"
|
|
||||||
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.rows.label']}"
|
|
||||||
name="rows"
|
|
||||||
/>
|
|
||||||
<bootstrap:formGroupNumber
|
|
||||||
help="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.cols.help']}"
|
|
||||||
inputId="cols"
|
|
||||||
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.cols.label']}"
|
|
||||||
name="cols"
|
|
||||||
/>
|
|
||||||
<bootstrap:formCheck
|
|
||||||
checked="true"
|
|
||||||
inputId="headerRow"
|
|
||||||
label="#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.header_row.label']}"
|
|
||||||
name="headerRow"
|
|
||||||
value="true"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="modal-footer">
|
|
||||||
<button
|
|
||||||
class="
|
|
||||||
btn btn-warning
|
|
||||||
cms-editor-cancel-button
|
|
||||||
"
|
|
||||||
data-dismiss="modal"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.close']}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-success"
|
|
||||||
data-dismiss="modal"
|
|
||||||
data-backdrop="false"
|
|
||||||
type="submit"
|
|
||||||
>
|
|
||||||
#{CmsDefaultStepsMessageBundle['cms_editor.dialogs.insert_table.submit']}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-remove-table"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.remove_table']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-remove.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table-row-before"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_row_before']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-add-row-before.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table-row-after"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_row_after']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-add-row-after.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-table-remove-row"
|
|
||||||
title="#{CmsAdminMessages['cms_editor.buttons.remove_table_row']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-remove-row.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table-column-before"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_column_before']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-add-column-before.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-insert-table-column-after"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.insert_table_column_after']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-add-column-after.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-remove-table-column"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.remove_table_column']}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-remove-column.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-toggle-table-header-row"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.toggle_header_row']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-toggle-header-row.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-toggle-table-header-column"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.toggle_header_column']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-toggle-header-column.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-merge-table-cells"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.merge-table-cells']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-merge-cells.svg" />
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-outline-dark tiptap-split-table-cell"
|
|
||||||
title="#{CmsAdminMessage['cms_editor.buttons.split-table-cell']}"
|
|
||||||
type="button">
|
|
||||||
<img src="#{request.contextPath}/icons/cms-editor/table-split-cells.svg" />
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="cms-tiptap-editor-canvas border"
|
|
||||||
data-locale="#{cc.attrs.selectedLocale}"
|
|
||||||
data-variant-url="#{cc.attrs.variantUrl}"
|
|
||||||
></div>
|
|
||||||
<div class="mt-3">
|
|
||||||
<a
|
|
||||||
class="btn btn-warning cms-editor-cancel-button"
|
|
||||||
href="#{backUrl}"
|
|
||||||
>
|
|
||||||
#{cc.attrs.editDialogCancelLabel}
|
|
||||||
</a>
|
|
||||||
<button
|
|
||||||
class="btn btn-success cms-editor-save-button"
|
|
||||||
disabled="#{cc.attrs.canEdit ? '' : 'disabled'}"
|
|
||||||
type="button"
|
|
||||||
>
|
|
||||||
#{cc.attrs.editDialogSubmitLabel}
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</cc:implementation>
|
</cc:implementation>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
|
|
@ -30,11 +30,37 @@
|
||||||
/>
|
/>
|
||||||
</c:if>
|
</c:if>
|
||||||
|
|
||||||
|
<button type="button" class="btn btn-primary" data-toggle="modal" data-target="#exampleModal">
|
||||||
|
Launch demo modal
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<!-- Modal -->
|
||||||
|
<div class="modal fade" id="exampleModal" tabindex="-1" aria-labelledby="exampleModalLabel" aria-hidden="true">
|
||||||
|
<div class="modal-dialog">
|
||||||
|
<div class="modal-content">
|
||||||
|
<div class="modal-header">
|
||||||
|
<h5 class="modal-title" id="exampleModalLabel">Modal title</h5>
|
||||||
|
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||||
|
<span aria-hidden="true">×</span>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<div class="modal-body">
|
||||||
|
...
|
||||||
|
</div>
|
||||||
|
<div class="modal-footer">
|
||||||
|
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
|
||||||
|
<button type="button" class="btn btn-primary">Save changes</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</ui:define>
|
</ui:define>
|
||||||
|
|
||||||
<ui:define name="scripts">
|
<ui:define name="scripts">
|
||||||
<script src="#{request.contextPath}/assets/@content-sections/article-text-step.js"></script>
|
<script src="#{request.contextPath}/assets/@content-sections/article-text-step.js"></script>
|
||||||
</ui:define>
|
</ui:define>
|
||||||
|
|
||||||
</ui:composition>
|
</ui:composition>
|
||||||
</html>
|
</html>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import EditorJS from "@editorjs/editorjs";
|
||||||
|
import Header from "@editorjs/header";
|
||||||
|
import NestedList from "@editorjs/nested-list";
|
||||||
|
import Quote from "@editorjs/quote";
|
||||||
|
import Table from "@editorjs/table";
|
||||||
|
import { CmsImage } from "./ccm-cms-editorjs-image/ccm-cms-editorjs-image";
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", event => {
|
||||||
|
const editor = new EditorJS({
|
||||||
|
data: {
|
||||||
|
blocks: [
|
||||||
|
{
|
||||||
|
type: "header",
|
||||||
|
data: {
|
||||||
|
text: "Editor.js test",
|
||||||
|
level: 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "paragraph",
|
||||||
|
data: {
|
||||||
|
text: "Lorem ipsum"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
holder: "cms-article-text-editor",
|
||||||
|
tools: {
|
||||||
|
image: {
|
||||||
|
class: CmsImage,
|
||||||
|
inlineToolbar: true
|
||||||
|
},
|
||||||
|
header: Header,
|
||||||
|
list: {
|
||||||
|
class: NestedList,
|
||||||
|
inlineToolbar: true
|
||||||
|
},
|
||||||
|
quote: Quote,
|
||||||
|
table: Table
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const saveButton = document.querySelector(".cms-editor-save-button");
|
||||||
|
if (saveButton) {
|
||||||
|
saveButton.addEventListener("click", event => {
|
||||||
|
event.preventDefault();
|
||||||
|
const preview = document.querySelector("#editor-preview");
|
||||||
|
if (preview) {
|
||||||
|
editor.save().then(outputData => {
|
||||||
|
preview.innerHTML = JSON.stringify(outputData);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
@ -1,28 +0,0 @@
|
||||||
import { CmsEditorBuilder, CmsEditor } from "./cms-editor";
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", event => {
|
|
||||||
const editorElem = document.querySelector("#cms-article-text-editor");
|
|
||||||
|
|
||||||
if (editorElem) {
|
|
||||||
const saveUrl = editorElem.getAttribute("data-save-url");
|
|
||||||
const variantUrl = editorElem.getAttribute("data-variant-url");
|
|
||||||
|
|
||||||
if (!saveUrl) {
|
|
||||||
console.error("saveUrl is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!variantUrl) {
|
|
||||||
console.error("variantUrl is null");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const builder = new CmsEditorBuilder(
|
|
||||||
editorElem as HTMLElement,
|
|
||||||
saveUrl,
|
|
||||||
variantUrl
|
|
||||||
);
|
|
||||||
|
|
||||||
builder.buildEditor();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
// import { CmsEditorBuilder, CmsEditor } from "./cms-editor";
|
||||||
|
|
||||||
|
// const editorElem = document.querySelector("#cms-article-text-editor");
|
||||||
|
|
||||||
|
// if (editorElem) {
|
||||||
|
// const saveUrl = editorElem.getAttribute("data-save-url");
|
||||||
|
// const variantUrl = editorElem.getAttribute("data-variant-url");
|
||||||
|
|
||||||
|
// if (!saveUrl) {
|
||||||
|
// console.error("saveUrl is null");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// if (!variantUrl) {
|
||||||
|
// console.error("variantUrl is null");
|
||||||
|
// return;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// const builder = new CmsEditorBuilder(
|
||||||
|
// editorElem as HTMLElement,
|
||||||
|
// saveUrl,
|
||||||
|
// variantUrl
|
||||||
|
// );
|
||||||
|
|
||||||
|
// builder.buildEditor();
|
||||||
|
// }
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,131 @@
|
||||||
|
import * as jquery from "jquery";
|
||||||
|
|
||||||
|
export class CmsImage {
|
||||||
|
static get toolbox() {
|
||||||
|
return {
|
||||||
|
title: "Image",
|
||||||
|
icon: "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-image\" viewBox=\"0 0 16 16\"><path d=\"M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z\"/><path d=\"M2.002 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2h-12zm12 1a1 1 0 0 1 1 1v6.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3a1 1 0 0 1 1-1h12z\"/></svg>"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public render() {
|
||||||
|
const figureElem = document.createElement("figure");
|
||||||
|
|
||||||
|
const imgElem = document.createElement("img");
|
||||||
|
imgElem.src = "data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-image\" viewBox=\"0 0 16 16\"><path d=\"M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z\"/><path d=\"M2.002 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2h-12zm12 1a1 1 0 0 1 1 1v6.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3a1 1 0 0 1 1-1h12z\"/></svg>";
|
||||||
|
imgElem.setAttribute("style", "width: 100%");
|
||||||
|
figureElem.appendChild(imgElem);
|
||||||
|
|
||||||
|
const captionElem = document.createElement("figcaption");
|
||||||
|
captionElem.contentEditable = "true";
|
||||||
|
captionElem.innerHTML = "<i>Caption placeholder</i>"
|
||||||
|
figureElem.appendChild(captionElem);
|
||||||
|
|
||||||
|
return figureElem;
|
||||||
|
}
|
||||||
|
|
||||||
|
public renderSettings() {
|
||||||
|
const updateImageButton = document.createElement("button");
|
||||||
|
updateImageButton.title = "Update image";
|
||||||
|
updateImageButton.classList.add("cdx-settings-button");
|
||||||
|
updateImageButton.innerHTML = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-image\" viewBox=\"0 0 16 16\"><path d=\"M6.002 5.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0z\"/><path d=\"M2.002 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2h-12zm12 1a1 1 0 0 1 1 1v6.5l-3.777-1.947a.5.5 0 0 0-.577.093l-3.71 3.71-2.66-1.772a.5.5 0 0 0-.63.062L1.002 12V3a1 1 0 0 1 1-1h12z\"/></svg>";
|
||||||
|
updateImageButton.addEventListener("click", (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
this.updateImage();
|
||||||
|
});
|
||||||
|
|
||||||
|
const settings = document.createElement("div");
|
||||||
|
settings.appendChild(updateImageButton);
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public save(blockContent: any) {
|
||||||
|
return {
|
||||||
|
url: blockContent.value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private updateImage() {
|
||||||
|
(jquery("#exampleModal") as any).modal("toggle");
|
||||||
|
|
||||||
|
const modalElem = document.createElement("div");
|
||||||
|
modalElem.classList.add("modal");
|
||||||
|
modalElem.classList.add("fade");
|
||||||
|
modalElem.id = "cms-editor-image-dialog";
|
||||||
|
modalElem.setAttribute("aria-labelledby", "cms-editor-image-dialog-title");
|
||||||
|
// modalElem.setAttribute("data-show", "true");
|
||||||
|
|
||||||
|
const modalDialogElem = document.createElement("div");
|
||||||
|
modalDialogElem.classList.add("modal-dialog");
|
||||||
|
modalElem.appendChild(modalDialogElem);
|
||||||
|
|
||||||
|
const modalContentElem = document.createElement("div");
|
||||||
|
modalContentElem.classList.add("modal-content");
|
||||||
|
modalDialogElem.appendChild(modalContentElem);
|
||||||
|
|
||||||
|
const modalHeaderElem = document.createElement("div");
|
||||||
|
modalHeaderElem.classList.add("modal-header");
|
||||||
|
modalContentElem.appendChild(modalHeaderElem);
|
||||||
|
|
||||||
|
const modalTitleElem = document.createElement("h3");
|
||||||
|
modalTitleElem.classList.add("modal-title");
|
||||||
|
modalTitleElem.id = "cms-editor-image-dialog-title";
|
||||||
|
modalTitleElem.textContent = "Update image";
|
||||||
|
modalHeaderElem.appendChild(modalTitleElem);
|
||||||
|
|
||||||
|
const modalHeaderCloseButton = document.createElement("button");
|
||||||
|
modalHeaderCloseButton.classList.add("close");
|
||||||
|
modalHeaderCloseButton.setAttribute("aria-label", "Cancel");
|
||||||
|
modalHeaderCloseButton.type = "button";
|
||||||
|
modalHeaderCloseButton.innerHTML = "<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" fill=\"currentColor\" class=\"bi bi-x-circle\" viewBox=\"0 0 16 16\"><path d=\"M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z\"/><path d=\"M4.646 4.646a.5.5 0 0 1 .708 0L8 7.293l2.646-2.647a.5.5 0 0 1 .708.708L8.707 8l2.647 2.646a.5.5 0 0 1-.708.708L8 8.707l-2.646 2.647a.5.5 0 0 1-.708-.708L7.293 8 4.646 5.354a.5.5 0 0 1 0-.708z\"/></svg>";
|
||||||
|
modalHeaderElem.appendChild(modalHeaderCloseButton);
|
||||||
|
|
||||||
|
const modalBodyElem = document.createElement("div");
|
||||||
|
modalBodyElem.classList.add("modal-body");
|
||||||
|
modalBodyElem.innerHTML = "<i>Update image placeholder</i>";
|
||||||
|
modalContentElem.appendChild(modalBodyElem);
|
||||||
|
|
||||||
|
const modalFooterElem = document.createElement("div");
|
||||||
|
const cancelButtonElem = document.createElement("button");
|
||||||
|
cancelButtonElem.classList.add("btn");
|
||||||
|
cancelButtonElem.classList.add("btn-warning");
|
||||||
|
cancelButtonElem.type = "button";
|
||||||
|
const saveButtonElem = document.createElement("button");
|
||||||
|
saveButtonElem.classList.add("btn");
|
||||||
|
saveButtonElem.classList.add("btn-success");
|
||||||
|
modalFooterElem.appendChild(cancelButtonElem);
|
||||||
|
modalFooterElem.appendChild(saveButtonElem);
|
||||||
|
modalContentElem.appendChild(modalFooterElem);
|
||||||
|
|
||||||
|
modalHeaderCloseButton.addEventListener("click", event => {
|
||||||
|
event.preventDefault();
|
||||||
|
const bodyElem = document.querySelector("body");
|
||||||
|
bodyElem?.removeChild(modalElem);
|
||||||
|
});
|
||||||
|
|
||||||
|
cancelButtonElem.addEventListener("click", event => {
|
||||||
|
event.preventDefault();
|
||||||
|
const bodyElem = document.querySelector("body");
|
||||||
|
bodyElem?.removeChild(modalElem);
|
||||||
|
});
|
||||||
|
|
||||||
|
saveButtonElem.addEventListener("click", event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
console.log("updating image...");
|
||||||
|
|
||||||
|
const bodyElem = document.querySelector("body");
|
||||||
|
bodyElem?.removeChild(modalElem);
|
||||||
|
});
|
||||||
|
|
||||||
|
const bodyElem = document.querySelector("body");
|
||||||
|
bodyElem?.appendChild(modalElem);
|
||||||
|
|
||||||
|
|
||||||
|
console.dir(jquery);
|
||||||
|
|
||||||
|
(jquery("#cms-editor-image-dialog") as any).modal();
|
||||||
|
(jquery("#cms-editor-image-dialog") as any).modal("toggle");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,808 @@
|
||||||
|
import "bootstrap";
|
||||||
|
import * as $ from "jquery";
|
||||||
|
import { ChainedCommands, Editor, Node, mergeAttributes } from "@tiptap/core";
|
||||||
|
import Gapcursor from "@tiptap/extension-gapcursor";
|
||||||
|
import StarterKit from "@tiptap/starter-kit";
|
||||||
|
import Subscript from "@tiptap/extension-subscript";
|
||||||
|
import Superscript from "@tiptap/extension-superscript";
|
||||||
|
import Table from "@tiptap/extension-table";
|
||||||
|
import TableRow from "@tiptap/extension-table-row";
|
||||||
|
import TableCell from "@tiptap/extension-table-cell";
|
||||||
|
import TableHeader from "@tiptap/extension-table-header";
|
||||||
|
|
||||||
|
import CcmImage from "./cms-editor/image";
|
||||||
|
|
||||||
|
const BUTTONS: CmsEditorButton[] = [
|
||||||
|
{
|
||||||
|
selector: ".tiptap-emph",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().toggleItalic().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleItalic()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-strong-emph",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().toggleBold().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBold()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-code",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().toggleCode().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleCode()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-strikethrough",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().toggleStrike().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleStrike()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-subscript",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleSubscript()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleSubscript()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-superscript",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleSuperscript()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleSuperscript()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h1",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 1 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 1 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h2",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 2 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 2 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h3",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 3 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 3 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h5",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 5 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 5 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-h6",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 6 })
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeading({ level: 6 })
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-paragraph",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().clearNodes().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.clearNodes()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-blockquote",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBlockquote()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBlockquote()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-codeblock",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleCodeBlock()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleCodeBlock()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-ul",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBulletList()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleBulletList()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-ol",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleOrderedList()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleOrderedList()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".cms-editor-insert-table-dialog",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.insertTable()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".cms-editor-insert-table-dialog .btn-success",
|
||||||
|
command: cmsEditor => {
|
||||||
|
const dialog = cmsEditor
|
||||||
|
.getEditorElem()
|
||||||
|
.querySelector(".cms-editor-insert-table-dialog");
|
||||||
|
if (!dialog) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const rowsInput = dialog.querySelector(
|
||||||
|
"input#rows"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const colsInput = dialog.querySelector(
|
||||||
|
"input#cols"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
const headerRowInput = dialog.querySelector(
|
||||||
|
"input#headerRow"
|
||||||
|
) as HTMLInputElement;
|
||||||
|
console.log(`rowsInput = ${rowsInput}`);
|
||||||
|
console.log(`colsInput = ${colsInput}`);
|
||||||
|
console.log(`headerRowInput = ${headerRowInput}`);
|
||||||
|
const rows = parseInt(rowsInput.value, 10);
|
||||||
|
const cols = parseInt(colsInput.value, 10);
|
||||||
|
const headerRow = JSON.parse(headerRowInput.value) as Boolean;
|
||||||
|
const insertTableDialog = $("#insert-table-dialog") as any;
|
||||||
|
insertTableDialog.modal("hide");
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.insertTable({
|
||||||
|
// allowTableNodeSelection: true,
|
||||||
|
// cellMinWidth: 150,
|
||||||
|
cols: cols,
|
||||||
|
// headerRow: headerRow,
|
||||||
|
// resizable: true,
|
||||||
|
rows: rows
|
||||||
|
})
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-insert-table-row-before",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().addRowBefore().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addRowBefore()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-insert-table-row-after",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().addRowAfter().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addRowAfter()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-insert-table-column-before",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addColumnBefore()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addColumnBefore()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-insert-table-column-after",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().addColumnAfter().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.addColumnAfter()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-remove-table-row",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().deleteRow().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.deleteRow()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-remove-table-column",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().deleteColumn().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.deleteColumn()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-remove-table",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().deleteTable().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.deleteTable()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-toggle-table-header-row",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeaderRow()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeaderRow()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-toggle-table-header-column",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeaderColumn()
|
||||||
|
.run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.toggleHeaderColumn()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-merge-table-cells",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().mergeCells().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.mergeCells()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-split-table-cell",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().splitCell().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor
|
||||||
|
.getEditor()
|
||||||
|
.can()
|
||||||
|
.chain()
|
||||||
|
.focus()
|
||||||
|
.splitCell()
|
||||||
|
.run();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: ".tiptap-insert-image",
|
||||||
|
command: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().chain().focus().addCcmImage().run();
|
||||||
|
},
|
||||||
|
can: cmsEditor => {
|
||||||
|
return cmsEditor.getEditor().can().chain().focus().addCcmImage().run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// {
|
||||||
|
// selector: "",
|
||||||
|
// command: cmsEditor => {},
|
||||||
|
// can: cmsEditor => {}
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// selector: "",
|
||||||
|
// command: cmsEditor => {},
|
||||||
|
// can: cmsEditor => {}
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// selector: "",
|
||||||
|
// command: cmsEditor => {},
|
||||||
|
// can: cmsEditor => {}
|
||||||
|
// }
|
||||||
|
];
|
||||||
|
|
||||||
|
class CmsEditor {
|
||||||
|
private editor: Editor;
|
||||||
|
private editorElem: HTMLElement;
|
||||||
|
private saveUrl: string;
|
||||||
|
|
||||||
|
public constructor(
|
||||||
|
editor: Editor,
|
||||||
|
editorElem: HTMLElement,
|
||||||
|
saveUrl: string
|
||||||
|
) {
|
||||||
|
this.editor = editor;
|
||||||
|
this.editorElem = editorElem;
|
||||||
|
this.saveUrl = saveUrl;
|
||||||
|
|
||||||
|
console.log("initializing editor buttons");
|
||||||
|
const buttonsElem = editorElem.querySelector(
|
||||||
|
".cms-tiptap-editor-buttons"
|
||||||
|
);
|
||||||
|
if (buttonsElem) {
|
||||||
|
for (const button of BUTTONS) {
|
||||||
|
const buttonElem = buttonsElem.querySelector(button.selector);
|
||||||
|
if (buttonElem) {
|
||||||
|
buttonElem.addEventListener("click", event => {
|
||||||
|
event.preventDefault();
|
||||||
|
button.command(this);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.error("editorButtonsElem not found.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
editor.on("selectionUpdate", ({ editor }: { editor: Editor }) => {
|
||||||
|
console.log(`checkButton - this.editorElem = ${this.editorElem}`);
|
||||||
|
const buttonsElem = editorElem.querySelector(
|
||||||
|
".cms-tiptap-editor-buttons"
|
||||||
|
);
|
||||||
|
if (!buttonsElem) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
for (const button of BUTTONS) {
|
||||||
|
const elem = buttonsElem.querySelector(button.selector);
|
||||||
|
if (elem) {
|
||||||
|
const buttonElem = elem as HTMLButtonElement;
|
||||||
|
if (button.can(this)) {
|
||||||
|
buttonElem.removeAttribute("disabled");
|
||||||
|
} else {
|
||||||
|
buttonElem.setAttribute("disabled", "disabled");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`editorElem = ${editorElem}`);
|
||||||
|
|
||||||
|
const saveButton = editorElem.querySelector(".cms-editor-save-button");
|
||||||
|
saveButton?.addEventListener("click", event => this.save(event));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async save(event: Event) {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
const params = new URLSearchParams();
|
||||||
|
params.append("value", this.editor.getHTML());
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(this.saveUrl, {
|
||||||
|
method: "POST",
|
||||||
|
credentials: "include",
|
||||||
|
headers: {
|
||||||
|
"Content-Type": "application/x-www-form-urlencoded"
|
||||||
|
},
|
||||||
|
body: params
|
||||||
|
});
|
||||||
|
if (response.ok) {
|
||||||
|
} else {
|
||||||
|
this.showSaveFailedMessage(
|
||||||
|
response.status,
|
||||||
|
response.statusText
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showSaveFailedErrMessage(error as string);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showSaveFailedMessage(status: number, statusText: string) {
|
||||||
|
this.showMessage("#cms-editor-msg-save-failed");
|
||||||
|
console.error(
|
||||||
|
`Failed to save text. Status: ${statusText}. Status Text: ${statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showSaveFailedErrMessage(error: string) {
|
||||||
|
this.showMessage("#cms-editor-msg-save-failed");
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showMessage(messageId: string) {
|
||||||
|
const template = this.editorElem.querySelector(
|
||||||
|
messageId
|
||||||
|
) as HTMLTemplateElement;
|
||||||
|
const message = template.content.cloneNode(true);
|
||||||
|
this.editorElem.querySelector(".cms-editor-messages")?.append(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEditor(): Editor {
|
||||||
|
return this.editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public getEditorElem(): HTMLElement {
|
||||||
|
return this.editorElem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class CmsEditorBuilder {
|
||||||
|
private editorElem: HTMLElement;
|
||||||
|
private saveUrl: string;
|
||||||
|
private variantUrl: string;
|
||||||
|
|
||||||
|
constructor(editorElem: HTMLElement, saveUrl: string, variantUrl: string) {
|
||||||
|
this.editorElem = editorElem;
|
||||||
|
this.saveUrl = saveUrl;
|
||||||
|
this.variantUrl = variantUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async buildEditor(): Promise<CmsEditor> {
|
||||||
|
console.log("Build CMS Editor.");
|
||||||
|
const canvasElement = this.editorElem.querySelector(
|
||||||
|
".cms-tiptap-editor-canvas"
|
||||||
|
);
|
||||||
|
if (!canvasElement) {
|
||||||
|
this.showMessage("#cms-editor-msg-canvas-element-not-found");
|
||||||
|
console.error("canvasElem not found.");
|
||||||
|
throw "canvasElem not found.";
|
||||||
|
}
|
||||||
|
|
||||||
|
const variant = await this.fetchVariant(this.variantUrl);
|
||||||
|
|
||||||
|
const editor: Editor = new Editor({
|
||||||
|
element: canvasElement,
|
||||||
|
extensions: [
|
||||||
|
Gapcursor,
|
||||||
|
CcmImage,
|
||||||
|
StarterKit,
|
||||||
|
Subscript,
|
||||||
|
Superscript,
|
||||||
|
Table.configure({
|
||||||
|
allowTableNodeSelection: true,
|
||||||
|
cellMinWidth: 100,
|
||||||
|
handleWidth: 25,
|
||||||
|
resizable: true
|
||||||
|
}),
|
||||||
|
TableRow,
|
||||||
|
TableHeader,
|
||||||
|
TableCell
|
||||||
|
],
|
||||||
|
content: variant,
|
||||||
|
onUpdate({editor}) {
|
||||||
|
const preview = document.querySelector("#html-preview");
|
||||||
|
if (preview) {
|
||||||
|
preview.innerHTML = editor.getHTML();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return new CmsEditor(editor, this.editorElem, this.saveUrl);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected async fetchVariant(variantUrl: string): Promise<string> {
|
||||||
|
try {
|
||||||
|
const response = await fetch(variantUrl, {
|
||||||
|
method: "GET",
|
||||||
|
credentials: "include"
|
||||||
|
});
|
||||||
|
|
||||||
|
if (response.ok) {
|
||||||
|
return await response.text();
|
||||||
|
} else {
|
||||||
|
this.showLoadVariantFailedMessage(
|
||||||
|
response.status,
|
||||||
|
response.statusText
|
||||||
|
);
|
||||||
|
throw `Failed to load variant. Status: ${response.status}, Status Text: ${response.statusText}`;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
this.showLoadVariantFailedErrorMessage(error as string);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showLoadVariantFailedMessage(status: number, statusText: string) {
|
||||||
|
this.showMessage("#cms-editor-msg-variant-load-failed");
|
||||||
|
console.error(
|
||||||
|
`Failed to load variant: HTTP Status: ${status}, statusText: ${statusText}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showLoadVariantFailedErrorMessage(error: string) {
|
||||||
|
this.showMessage("#cms-editor-msg-variant-load-failed");
|
||||||
|
console.error(`Failed to load variant: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected showMessage(messageId: string) {
|
||||||
|
const template = this.editorElem.querySelector(
|
||||||
|
messageId
|
||||||
|
) as HTMLTemplateElement;
|
||||||
|
const message = template.content.cloneNode(true);
|
||||||
|
this.editorElem.querySelector(".cms-editor-messages")?.append(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CmsEditorParameters {
|
||||||
|
editorElem: HTMLElement;
|
||||||
|
variantUrl: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CmsEditorButton {
|
||||||
|
selector: string;
|
||||||
|
command: (cmsEditor: CmsEditor) => boolean;
|
||||||
|
can: (cmsEditor: CmsEditor) => boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface EditorParam {
|
||||||
|
editor: Editor;
|
||||||
|
}
|
||||||
|
|
||||||
|
export { CmsEditor, CmsEditorBuilder, CmsEditorParameters };
|
||||||
|
|
@ -1,795 +1,2 @@
|
||||||
import "bootstrap";
|
//import EditorJS from '@editorjs/editorjs';
|
||||||
import * as $ from "jquery";
|
|
||||||
import { ChainedCommands, Editor } from "@tiptap/core";
|
|
||||||
import Gapcursor from "@tiptap/extension-gapcursor";
|
|
||||||
import StarterKit from "@tiptap/starter-kit";
|
|
||||||
import Subscript from "@tiptap/extension-subscript";
|
|
||||||
import Superscript from "@tiptap/extension-superscript";
|
|
||||||
import Table from "@tiptap/extension-table";
|
|
||||||
import TableRow from "@tiptap/extension-table-row";
|
|
||||||
import TableCell from "@tiptap/extension-table-cell";
|
|
||||||
import TableHeader from "@tiptap/extension-table-header";
|
|
||||||
|
|
||||||
const BUTTONS: CmsEditorButton[] = [
|
|
||||||
{
|
|
||||||
selector: ".tiptap-emph",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().toggleItalic().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleItalic()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-strong-emph",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().toggleBold().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBold()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-code",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().toggleCode().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleCode()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-strikethrough",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().toggleStrike().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleStrike()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-subscript",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleSubscript()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleSubscript()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-superscript",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleSuperscript()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleSuperscript()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h1",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 1 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 1 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h2",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 2 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 2 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h3",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 3 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 3 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h5",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 5 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 5 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-h6",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 6 })
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeading({ level: 6 })
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-paragraph",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().clearNodes().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.clearNodes()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-blockquote",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBlockquote()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBlockquote()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-codeblock",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleCodeBlock()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleCodeBlock()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-ul",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBulletList()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleBulletList()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-ol",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleOrderedList()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleOrderedList()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".cms-editor-insert-table-dialog",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return true;
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.insertTable()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".cms-editor-insert-table-dialog .btn-success",
|
|
||||||
command: cmsEditor => {
|
|
||||||
const dialog = cmsEditor
|
|
||||||
.getEditorElem()
|
|
||||||
.querySelector(".cms-editor-insert-table-dialog");
|
|
||||||
if (!dialog) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const rowsInput = dialog.querySelector(
|
|
||||||
"input#rows"
|
|
||||||
) as HTMLInputElement;
|
|
||||||
const colsInput = dialog.querySelector(
|
|
||||||
"input#cols"
|
|
||||||
) as HTMLInputElement;
|
|
||||||
const headerRowInput = dialog.querySelector(
|
|
||||||
"input#headerRow"
|
|
||||||
) as HTMLInputElement;
|
|
||||||
console.log(`rowsInput = ${rowsInput}`);
|
|
||||||
console.log(`colsInput = ${colsInput}`);
|
|
||||||
console.log(`headerRowInput = ${headerRowInput}`);
|
|
||||||
const rows = parseInt(rowsInput.value, 10);
|
|
||||||
const cols = parseInt(colsInput.value, 10);
|
|
||||||
const headerRow = JSON.parse(headerRowInput.value) as Boolean;
|
|
||||||
const insertTableDialog = $("#insert-table-dialog") as any;
|
|
||||||
insertTableDialog.modal("hide");
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.insertTable({
|
|
||||||
// allowTableNodeSelection: true,
|
|
||||||
// cellMinWidth: 150,
|
|
||||||
cols: cols,
|
|
||||||
// headerRow: headerRow,
|
|
||||||
// resizable: true,
|
|
||||||
rows: rows
|
|
||||||
})
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-insert-table-row-before",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().addRowBefore().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addRowBefore()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-insert-table-row-after",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().addRowAfter().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addRowAfter()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-insert-table-column-before",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addColumnBefore()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addColumnBefore()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-insert-table-column-after",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().addColumnAfter().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.addColumnAfter()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-remove-table-row",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().deleteRow().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.deleteRow()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-remove-table-column",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().deleteColumn().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.deleteColumn()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-remove-table",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().deleteTable().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.deleteTable()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-toggle-table-header-row",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeaderRow()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeaderRow()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-toggle-table-header-column",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeaderColumn()
|
|
||||||
.run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.toggleHeaderColumn()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-merge-table-cells",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().mergeCells().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.mergeCells()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
selector: ".tiptap-split-table-cell",
|
|
||||||
command: cmsEditor => {
|
|
||||||
return cmsEditor.getEditor().chain().focus().splitCell().run();
|
|
||||||
},
|
|
||||||
can: cmsEditor => {
|
|
||||||
return cmsEditor
|
|
||||||
.getEditor()
|
|
||||||
.can()
|
|
||||||
.chain()
|
|
||||||
.focus()
|
|
||||||
.splitCell()
|
|
||||||
.run();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// {
|
|
||||||
// selector: "",
|
|
||||||
// command: cmsEditor => {},
|
|
||||||
// can: cmsEditor => {}
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// selector: "",
|
|
||||||
// command: cmsEditor => {},
|
|
||||||
// can: cmsEditor => {}
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// selector: "",
|
|
||||||
// command: cmsEditor => {},
|
|
||||||
// can: cmsEditor => {}
|
|
||||||
// },
|
|
||||||
// {
|
|
||||||
// selector: "",
|
|
||||||
// command: cmsEditor => {},
|
|
||||||
// can: cmsEditor => {}
|
|
||||||
// }
|
|
||||||
];
|
|
||||||
|
|
||||||
class CmsEditor {
|
|
||||||
private editor: Editor;
|
|
||||||
private editorElem: HTMLElement;
|
|
||||||
private saveUrl: string;
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
editor: Editor,
|
|
||||||
editorElem: HTMLElement,
|
|
||||||
saveUrl: string
|
|
||||||
) {
|
|
||||||
this.editor = editor;
|
|
||||||
this.editorElem = editorElem;
|
|
||||||
this.saveUrl = saveUrl;
|
|
||||||
|
|
||||||
console.log("initializing editor buttons");
|
|
||||||
const buttonsElem = editorElem.querySelector(
|
|
||||||
".cms-tiptap-editor-buttons"
|
|
||||||
);
|
|
||||||
if (buttonsElem) {
|
|
||||||
for (const button of BUTTONS) {
|
|
||||||
const buttonElem = buttonsElem.querySelector(button.selector);
|
|
||||||
if (buttonElem) {
|
|
||||||
buttonElem.addEventListener("click", event => {
|
|
||||||
event.preventDefault();
|
|
||||||
button.command(this);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
console.error("editorButtonsElem not found.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
editor.on("selectionUpdate", ({ editor }: { editor: Editor }) => {
|
|
||||||
console.log(`checkButton - this.editorElem = ${this.editorElem}`);
|
|
||||||
const buttonsElem = editorElem.querySelector(
|
|
||||||
".cms-tiptap-editor-buttons"
|
|
||||||
);
|
|
||||||
if (!buttonsElem) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
for (const button of BUTTONS) {
|
|
||||||
const elem = buttonsElem.querySelector(button.selector);
|
|
||||||
if (elem) {
|
|
||||||
const buttonElem = elem as HTMLButtonElement;
|
|
||||||
if (button.can(this)) {
|
|
||||||
buttonElem.removeAttribute("disabled");
|
|
||||||
} else {
|
|
||||||
buttonElem.setAttribute("disabled", "disabled");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log(`editorElem = ${editorElem}`);
|
|
||||||
|
|
||||||
const saveButton = editorElem.querySelector(".cms-editor-save-button");
|
|
||||||
saveButton?.addEventListener("click", event => this.save(event));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async save(event: Event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
const params = new URLSearchParams();
|
|
||||||
params.append("value", this.editor.getHTML());
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(this.saveUrl, {
|
|
||||||
method: "POST",
|
|
||||||
credentials: "include",
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/x-www-form-urlencoded"
|
|
||||||
},
|
|
||||||
body: params
|
|
||||||
});
|
|
||||||
if (response.ok) {
|
|
||||||
} else {
|
|
||||||
this.showSaveFailedMessage(
|
|
||||||
response.status,
|
|
||||||
response.statusText
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.showSaveFailedErrMessage(error as string);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showSaveFailedMessage(status: number, statusText: string) {
|
|
||||||
this.showMessage("#cms-editor-msg-save-failed");
|
|
||||||
console.error(
|
|
||||||
`Failed to save text. Status: ${statusText}. Status Text: ${statusText}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showSaveFailedErrMessage(error: string) {
|
|
||||||
this.showMessage("#cms-editor-msg-save-failed");
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showMessage(messageId: string) {
|
|
||||||
const template = this.editorElem.querySelector(
|
|
||||||
messageId
|
|
||||||
) as HTMLTemplateElement;
|
|
||||||
const message = template.content.cloneNode(true);
|
|
||||||
this.editorElem.querySelector(".cms-editor-messages")?.append(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public getEditor(): Editor {
|
|
||||||
return this.editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public getEditorElem(): HTMLElement {
|
|
||||||
return this.editorElem;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class CmsEditorBuilder {
|
|
||||||
private editorElem: HTMLElement;
|
|
||||||
private saveUrl: string;
|
|
||||||
private variantUrl: string;
|
|
||||||
|
|
||||||
constructor(editorElem: HTMLElement, saveUrl: string, variantUrl: string) {
|
|
||||||
this.editorElem = editorElem;
|
|
||||||
this.saveUrl = saveUrl;
|
|
||||||
this.variantUrl = variantUrl;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async buildEditor(): Promise<CmsEditor> {
|
|
||||||
console.log("Build CMS Editor.");
|
|
||||||
const canvasElement = this.editorElem.querySelector(
|
|
||||||
".cms-tiptap-editor-canvas"
|
|
||||||
);
|
|
||||||
if (!canvasElement) {
|
|
||||||
this.showMessage("#cms-editor-msg-canvas-element-not-found");
|
|
||||||
console.error("canvasElem not found.");
|
|
||||||
throw "canvasElem not found.";
|
|
||||||
}
|
|
||||||
|
|
||||||
const variant = await this.fetchVariant(this.variantUrl);
|
|
||||||
|
|
||||||
const editor: Editor = new Editor({
|
|
||||||
element: canvasElement,
|
|
||||||
extensions: [
|
|
||||||
Gapcursor,
|
|
||||||
StarterKit,
|
|
||||||
Subscript,
|
|
||||||
Superscript,
|
|
||||||
Table.configure({
|
|
||||||
allowTableNodeSelection: true,
|
|
||||||
cellMinWidth: 100,
|
|
||||||
handleWidth: 25,
|
|
||||||
resizable: true
|
|
||||||
}),
|
|
||||||
TableRow,
|
|
||||||
TableHeader,
|
|
||||||
TableCell
|
|
||||||
],
|
|
||||||
content: variant
|
|
||||||
});
|
|
||||||
|
|
||||||
return new CmsEditor(editor, this.editorElem, this.saveUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async fetchVariant(variantUrl: string): Promise<string> {
|
|
||||||
try {
|
|
||||||
const response = await fetch(variantUrl, {
|
|
||||||
method: "GET",
|
|
||||||
credentials: "include"
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
return await response.text();
|
|
||||||
} else {
|
|
||||||
this.showLoadVariantFailedMessage(
|
|
||||||
response.status,
|
|
||||||
response.statusText
|
|
||||||
);
|
|
||||||
throw `Failed to load variant. Status: ${response.status}, Status Text: ${response.statusText}`;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
this.showLoadVariantFailedErrorMessage(error as string);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showLoadVariantFailedMessage(status: number, statusText: string) {
|
|
||||||
this.showMessage("#cms-editor-msg-variant-load-failed");
|
|
||||||
console.error(
|
|
||||||
`Failed to load variant: HTTP Status: ${status}, statusText: ${statusText}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showLoadVariantFailedErrorMessage(error: string) {
|
|
||||||
this.showMessage("#cms-editor-msg-variant-load-failed");
|
|
||||||
console.error(`Failed to load variant: ${error}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected showMessage(messageId: string) {
|
|
||||||
const template = this.editorElem.querySelector(
|
|
||||||
messageId
|
|
||||||
) as HTMLTemplateElement;
|
|
||||||
const message = template.content.cloneNode(true);
|
|
||||||
this.editorElem.querySelector(".cms-editor-messages")?.append(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CmsEditorParameters {
|
|
||||||
editorElem: HTMLElement;
|
|
||||||
variantUrl: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface CmsEditorButton {
|
|
||||||
selector: string;
|
|
||||||
command: (cmsEditor: CmsEditor) => boolean;
|
|
||||||
can: (cmsEditor: CmsEditor) => boolean;
|
|
||||||
}
|
|
||||||
|
|
||||||
interface EditorParam {
|
|
||||||
editor: Editor;
|
|
||||||
}
|
|
||||||
|
|
||||||
export { CmsEditor, CmsEditorBuilder, CmsEditorParameters };
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
import { Node, mergeAttributes } from "@tiptap/core";
|
||||||
|
import { insertContent } from "@tiptap/core/dist/packages/core/src/extensions/commands";
|
||||||
|
|
||||||
|
export interface CcmImageOptions {
|
||||||
|
HTMLAttributes: Record<string, any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
declare module "@tiptap/core" {
|
||||||
|
interface Commands<ReturnType> {
|
||||||
|
ccmImage: {
|
||||||
|
addCcmImage: () => ReturnType;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const CcmImage = Node.create<CcmImageOptions>({
|
||||||
|
name: "ccmImage",
|
||||||
|
|
||||||
|
// priority: 1000,
|
||||||
|
|
||||||
|
defaultOptions: {
|
||||||
|
HTMLAttributes: {},
|
||||||
|
},
|
||||||
|
|
||||||
|
group: "block",
|
||||||
|
|
||||||
|
content: "inline*",
|
||||||
|
|
||||||
|
parseHTML() {
|
||||||
|
return [
|
||||||
|
{ tag: "figure"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
|
||||||
|
renderHTML({ HTMLAttributes }) {
|
||||||
|
return ["node-view", mergeAttributes(HTMLAttributes)];
|
||||||
|
// return [
|
||||||
|
// "figure",
|
||||||
|
// mergeAttributes(this.options.HTMLAttributes, HTMLAttributes),
|
||||||
|
// ["figcaption", HTMLAttributes, 0],
|
||||||
|
// // ["img", HTMLAttributes, 0]
|
||||||
|
// ]
|
||||||
|
},
|
||||||
|
|
||||||
|
addCommands() {
|
||||||
|
return {
|
||||||
|
addCcmImage: () => ({ commands }) => {
|
||||||
|
return commands.setNode("ccmImage");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// addKeyboardShortcut() {
|
||||||
|
// return {
|
||||||
|
// "Mod-Alt-i": () => this.editor.commands.addCcmImage()
|
||||||
|
// }
|
||||||
|
// },
|
||||||
|
|
||||||
|
addNodeView() {
|
||||||
|
return () => {
|
||||||
|
const dom = document.createElement("figure");
|
||||||
|
dom.classList.add("ccm-image");
|
||||||
|
const caption = document.createElement("figcaption") as HTMLElement;
|
||||||
|
caption.classList.add("label");
|
||||||
|
caption.innerHTML = "Node View";
|
||||||
|
caption.contentEditable = "true";
|
||||||
|
|
||||||
|
const content = document.createElement("div");
|
||||||
|
content.classList.add("content");
|
||||||
|
|
||||||
|
dom.append(caption, content);
|
||||||
|
|
||||||
|
return {
|
||||||
|
dom,
|
||||||
|
contentDom: content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
import { CcmImage } from "./image";
|
||||||
|
|
||||||
|
export * from "./image";
|
||||||
|
|
||||||
|
export default CcmImage;
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
"outDir": "target/generated-resources/assets/@content-sections",
|
"outDir": "target/generated-resources/assets/@content-sections",
|
||||||
"sourceMap": true,
|
"sourceMap": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
|
"target": "ES5"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/main/typescript/**/*"
|
"src/main/typescript/**/*"
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@ module.exports = {
|
||||||
devtool: "source-map",
|
devtool: "source-map",
|
||||||
entry: {
|
entry: {
|
||||||
"cms-admin": "./src/main/typescript/content-sections/cms-admin.ts",
|
"cms-admin": "./src/main/typescript/content-sections/cms-admin.ts",
|
||||||
"article-text-step": "./src/main/typescript/content-sections/article-text-step.ts"
|
"article-text-step": "./src/main/typescript/content-sections/article-text-step.js"
|
||||||
},
|
},
|
||||||
output: {
|
output: {
|
||||||
filename: "[name].js",
|
filename: "[name].js",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue