summaryrefslogtreecommitdiff
path: root/web_widget_ckeditor/static/src/js
diff options
context:
space:
mode:
authorRafi Zadanly <zadanlyr@gmail.com>2023-03-08 11:04:08 +0700
committerRafi Zadanly <zadanlyr@gmail.com>2023-03-08 11:04:08 +0700
commit870a9a59efefb7ce6ff2f9dc2b952416247a9953 (patch)
tree5d4d92fba0a70d92fd45ec173662fc71beb68c73 /web_widget_ckeditor/static/src/js
parentaefbbe76d0cb228d6928180991f59829d3ef2139 (diff)
add widget ckeditor
Diffstat (limited to 'web_widget_ckeditor/static/src/js')
-rw-r--r--web_widget_ckeditor/static/src/js/field_ckeditor.js330
-rw-r--r--web_widget_ckeditor/static/src/js/field_html_override.js15
2 files changed, 345 insertions, 0 deletions
diff --git a/web_widget_ckeditor/static/src/js/field_ckeditor.js b/web_widget_ckeditor/static/src/js/field_ckeditor.js
new file mode 100644
index 00000000..0f212abe
--- /dev/null
+++ b/web_widget_ckeditor/static/src/js/field_ckeditor.js
@@ -0,0 +1,330 @@
+/*
+ Copyright 2021 Camptocamp SA (https://www.camptocamp.com).
+ @author Iván Todorovich <ivan.todorovich@camptocamp.com>
+ License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+*/
+odoo.define("web_widget_ckeditor.field_ckeditor", function (require) {
+ "use strict";
+
+ const core = require("web.core");
+ const session = require("web.session");
+ const config = require("web.config");
+ const ajax = require("web.ajax");
+ const rpc = require("web.rpc");
+ const basic_fields = require("web.basic_fields");
+ const field_registry = require("web.field_registry");
+ const _lt = core._lt;
+ const TranslatableFieldMixin = basic_fields.TranslatableFieldMixin;
+
+ // Load configuration for the editor
+ const getCKEditorConfigPromise = rpc.query({
+ model: "ir.config_parameter",
+ method: "get_web_widget_ckeditor_config",
+ });
+
+ // Load CKEditor localization files
+ async function loadCKEditorLanguageSource(languageCode) {
+ if (languageCode === "en") {
+ return;
+ }
+ const languageURL = `/web_widget_ckeditor/static/lib/ckeditor/build/translations/${languageCode}.js`;
+ try {
+ ajax.loadJS(languageURL);
+ } catch (error) {
+ console.warn("Unable to load CKEditor language: ", languageCode);
+ }
+ }
+ const CKEditorLanguageCode = session.user_context.lang.split("_")[0];
+ const loadCKEditorLanguagePromise = loadCKEditorLanguageSource(
+ CKEditorLanguageCode
+ );
+
+ const FieldHtmlCKEditor = basic_fields.DebouncedField.extend(
+ TranslatableFieldMixin,
+ {
+ description: _lt("Html (CKEditor)"),
+ className: "oe_form_field oe_form_field_html oe_form_field_html_ckeditor",
+ supportedFieldTypes: ["html"],
+
+ /**
+ * @override
+ */
+ willStart: function () {
+ return Promise.all([
+ this._super.apply(this, arguments),
+ loadCKEditorLanguagePromise,
+ ]);
+ },
+
+ /**
+ * @override
+ */
+ destroy: function () {
+ if (this.ckeditor) {
+ this.ckeditor.destroy();
+ this.ckeditor = undefined;
+ }
+ return this._super();
+ },
+
+ // --------------------------------------------------------------------------
+ // Public
+ // --------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ activate: function () {
+ if (this.ckeditor) {
+ this.ckeditor.focus();
+ return true;
+ }
+ },
+ /**
+ * This function is similar to the one found in core's web_editor.FieldHtml.
+ *
+ * @override
+ */
+ isSet: function () {
+ // Removing spaces & html spaces
+ const value =
+ this.value &&
+ this.value.split("&nbsp;").join("").replace(/\s/g, "");
+ return (
+ value &&
+ value !== "<p></p>" &&
+ value !== "<p><br></p>" &&
+ value.match(/\S/)
+ );
+ },
+ /**
+ * This function is similar to the one found in core's web_editor.FieldHtml.
+ *
+ * @override
+ */
+ getFocusableElement: function () {
+ return this.$target || $();
+ },
+ /**
+ * Do not re-render this field if it was the origin of the onchange call.
+ * This function is similar to the one found in core's web_editor.FieldHtml.
+ *
+ * @override
+ */
+ reset: function (record, event) {
+ this._reset(record, event);
+ const value = this._textToHtml(this.value);
+ if (!event || event.target !== this) {
+ if (this.mode === "edit") {
+ this.ckeditor.setData(value);
+ } else {
+ this.$content.html(value);
+ }
+ }
+ return Promise.resolve();
+ },
+
+ // --------------------------------------------------------------------------
+ // Private
+ // --------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ _getValue: function () {
+ if (this.mode === "edit" && this.ckeditor) {
+ return this.ckeditor.getData();
+ }
+ return this.$target.val();
+ },
+ /**
+ * Gets the CKEditor toolbar items configuration.
+ * If not found, returns the default configuration.
+ */
+ _getCKEditorToolbarItems: async function () {
+ try {
+ const ckconfig = await getCKEditorConfigPromise;
+ if (ckconfig.toolbar) {
+ return ckconfig.toolbar.split(/[\s,]+/).filter((item) => item);
+ }
+ } catch (error) {
+ console.warn(
+ "Unable to use CKEditor toolbar configuration: ",
+ error
+ );
+ console.warn(
+ "Please check the value for ir.config_parameter 'web_widget_ckeditor.toolbar' is correct"
+ );
+ console.warn("Using default toolbar configuration");
+ }
+ return [
+ "heading",
+ "|",
+ "bold",
+ "italic",
+ "underline",
+ "removeFormat",
+ "|",
+ "fontSize",
+ "fontColor",
+ "fontBackgroundColor",
+ "|",
+ "bulletedList",
+ "numberedList",
+ "alignment",
+ "|",
+ "outdent",
+ "indent",
+ "pagebreak",
+ "|",
+ "link",
+ "imageUpload",
+ "blockQuote",
+ "insertTable",
+ "|",
+ "undo",
+ "redo",
+ ];
+ },
+ /**
+ * Gets the CKEditor configuration.
+ * See for details:
+ * https://ckeditor.com/docs/ckeditor5/latest/api/module_core_editor_editorconfig-EditorConfig.html
+ *
+ * @returns EditorConfig
+ */
+ _getCKEditorConfig: async function () {
+ const res = {
+ toolbar: {
+ items: await this._getCKEditorToolbarItems(),
+ shouldNotGroupWhenFull: true,
+ },
+ language: CKEditorLanguageCode,
+ image: {
+ toolbar: [
+ "imageTextAlternative",
+ "imageStyle:inline",
+ "imageStyle:block",
+ "imageStyle:side",
+ "linkImage",
+ ],
+ },
+ table: {
+ contentToolbar: [
+ "tableColumn",
+ "tableRow",
+ "mergeTableCells",
+ "tableCellProperties",
+ "tableProperties",
+ ],
+ },
+ };
+ if (config.isDebug()) {
+ res.toolbar.items.push("sourceEditing");
+ }
+ return res;
+ },
+ /**
+ * Create the CKEditor instance with the target (this.$target)
+ * then add the editable content (this.$content).
+ *
+ * @private
+ * @returns {$.Promise}
+ */
+ _createCKEditorIntance: async function () {
+ const editorConfig = await this._getCKEditorConfig();
+ this.ckeditor = await window.ClassicEditor.create(
+ this.$target.get(0),
+ editorConfig
+ );
+ // Register event hooks
+ this.ckeditor.on("change", () => this._onChange());
+ this.ckeditor.ui.focusTracker.on(
+ "change:isFocused",
+ (ev, name, isFocused) => (isFocused ? null : this._onChange())
+ );
+ this._onLoadCKEditor();
+ },
+ /**
+ * @override
+ */
+ _renderEdit: function () {
+ const value = this._textToHtml(this.value);
+ this.$target = $("<textarea>").val(value).hide();
+ this.$target.appendTo(this.$el);
+ return this._createCKEditorIntance();
+ },
+ /**
+ * @override
+ */
+ _renderReadonly: function () {
+ const value = this._textToHtml(this.value);
+ this.$el.empty();
+ this.$content = $('<div class="o_readonly"/>').html(value);
+ this.$content.appendTo(this.$el);
+ },
+ /**
+ * This function is similar to the one found in core's web_editor.FieldHtml.
+ *
+ * @private
+ * @param {String} text
+ * @returns {String} the text converted to html
+ */
+ _textToHtml: function (text) {
+ let value = text || "";
+ try {
+ // Crashes if text isn't html
+ $(text)[0].innerHTML; // eslint-disable-line
+ } catch (e) {
+ if (value.match(/^\s*$/)) {
+ value = "<p><br/></p>";
+ } else {
+ value =
+ "<p>" +
+ value.split(/<br\/?>/).join("<br/></p><p>") +
+ "</p>";
+ value = value
+ .replace(/<p><\/p>/g, "")
+ .replace("<p><p>", "<p>")
+ .replace("<p><p ", "<p ")
+ .replace("</p></p>", "</p>");
+ }
+ }
+ return value;
+ },
+
+ // --------------------------------------------------------------------------
+ // Handler
+ // --------------------------------------------------------------------------
+
+ /**
+ * Method called when the CKEditor instance is loaded.
+ *
+ * @private
+ */
+ _onLoadCKEditor: function () {
+ const $button = this._renderTranslateButton();
+ $button.css({
+ "font-size": "15px",
+ position: "absolute",
+ right: "+5px",
+ top: "+5px",
+ });
+ this.$el.append($button);
+ },
+ /**
+ * Method called when ckeditor triggers a change.
+ *
+ * @private
+ * @param {OdooEvent} ev
+ */
+ _onChange: function () {
+ this._doDebouncedAction.apply(this, arguments);
+ },
+ }
+ );
+
+ field_registry.add("ckeditor", FieldHtmlCKEditor);
+
+ return FieldHtmlCKEditor;
+});
diff --git a/web_widget_ckeditor/static/src/js/field_html_override.js b/web_widget_ckeditor/static/src/js/field_html_override.js
new file mode 100644
index 00000000..aa9d42bc
--- /dev/null
+++ b/web_widget_ckeditor/static/src/js/field_html_override.js
@@ -0,0 +1,15 @@
+/*
+ Copyright 2021 Camptocamp SA (https://www.camptocamp.com).
+ @author Iván Todorovich <ivan.todorovich@camptocamp.com>
+ License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+*/
+odoo.define("web_widget_ckeditor.field_html_override", function (require) {
+ "use strict";
+
+ const FieldHtml = require("web_editor.field.html");
+ const FieldHtmlCKEditor = require("web_widget_ckeditor.field_ckeditor");
+ const field_registry = require("web.field_registry");
+
+ field_registry.add("html_odoo", FieldHtml);
+ field_registry.add("html", FieldHtmlCKEditor);
+});