All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.alkacon.acacia.client.widgets.TinyMCEWidget Maven / Gradle / Ivy

Go to download

A GWT based resource editor. This component is used as a part of OpenCms, an enterprise-ready, easy to use website content management system based on Java and XML technology. Offering a complete set of features, OpenCms helps content managers worldwide to create and maintain beautiful websites fast and efficiently.

There is a newer version: 2.1
Show newest version
/*
 * This library is part of the Acacia Editor -
 * an open source inline and form based content editor for GWT.
 *
 * Copyright (c) Alkacon Software GmbH (http://www.alkacon.com)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * For further information about Alkacon Software, please see the
 * company website: http://www.alkacon.com
 * 
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

package com.alkacon.acacia.client.widgets;

import com.alkacon.acacia.client.EditorBase;
import com.alkacon.acacia.client.css.I_LayoutBundle;
import com.alkacon.geranium.client.util.DomUtil;
import com.alkacon.geranium.client.util.DomUtil.Style;
import com.alkacon.geranium.client.util.PositionBean;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Document;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.DomEvent;
import com.google.gwt.event.logical.shared.HasResizeHandlers;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.user.client.DOM;
import com.google.gwt.user.client.Element;
import com.google.gwt.user.client.Timer;

/**
 * This class is used to start TinyMCE for editing the content of an element.

* * After constructing the instance, the actual editor is opened using the init() method, and destroyed with the close() * method. While the editor is opened, the edited contents can be accessed using the methods of the HasValue interface. */ public final class TinyMCEWidget extends A_EditWidget implements HasResizeHandlers { /** Use as option to disallow any HTML or formatting the content. */ public static final String NO_HTML_EDIT = "no_html_edit"; /** The minimum editor height. */ private static final int MIN_EDITOR_HEIGHT = 70; /** The toolbar container css class name. */ private static final String TOOLBAR_CONTAINER = I_LayoutBundle.INSTANCE.form().tinymceToolbarContainer(); /** A flag which indicates whether the editor is currently active. */ protected boolean m_active; /** The current content. */ protected String m_currentContent; /** The TinyMCE editor instance. */ protected JavaScriptObject m_editor; /** The DOM ID of the editable element. */ protected String m_id; /** The original HTML content of the editable element. */ protected String m_originalContent; /** The maximal width of the widget. */ protected int m_width; /** The editor height to set. */ int m_editorHeight; /** The element to store the widget content in. */ private Element m_contentElement; /** Indicates the value has been set from external, not from within the widget. */ private boolean m_externalValueChange; /** Indicating if the widget has been attached yet. */ private boolean m_hasBeenAttached; /** Flag indicating the editor has been initialized. */ private boolean m_initialized; /** Flag indicating if in line editing is used. */ private boolean m_inline; /** The editor options. */ private Object m_options; /** The in line editing toolbar container. */ private Element m_toolbarContainer; /** * Creates a new instance for the given element. Use this constructor for in line editing.

* * @param element the DOM element * @param options the tinyMCE editor options to extend the default settings */ public TinyMCEWidget(Element element, Object options) { this(element, options, true); } /** * Creates a new instance with the given options. Use this constructor for form based editing.

* * @param options the tinyMCE editor options to extend the default settings */ public TinyMCEWidget(Object options) { this(DOM.createDiv(), options, false); } /** * Constructor.

* * @param element the DOM element * @param options the tinyMCE editor options to extend the default settings * @param inline flag indicating if in line editing is used */ private TinyMCEWidget(Element element, Object options, boolean inline) { super(element); m_originalContent = ""; m_options = options; m_active = true; m_inline = inline; if (m_inline) { m_contentElement = element; } else { // using a child DIV as content element m_contentElement = getElement().appendChild(DOM.createDiv()); } } /** * @see com.google.gwt.event.logical.shared.HasResizeHandlers#addResizeHandler(com.google.gwt.event.logical.shared.ResizeHandler) */ public HandlerRegistration addResizeHandler(ResizeHandler handler) { return addHandler(handler, ResizeEvent.getType()); } /** * @see com.alkacon.acacia.client.widgets.A_EditWidget#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) */ @Override public HandlerRegistration addValueChangeHandler(ValueChangeHandler handler) { return addHandler(handler, ValueChangeEvent.getType()); } /** * Gets the main editable element.

* * @return the editable element */ public Element getMainElement() { return m_contentElement; } /** * @see com.google.gwt.user.client.ui.HasValue#getValue() */ @Override public String getValue() { if (m_editor != null) { return getContent().trim(); } return m_originalContent.trim(); } /** * @see com.alkacon.acacia.client.widgets.I_EditWidget#isActive() */ public boolean isActive() { return m_active; } /** * @see com.alkacon.acacia.client.widgets.I_EditWidget#setActive(boolean) */ public void setActive(boolean active) { if (m_active == active) { return; } m_active = active; if (m_editor != null) { if (m_active) { getElement().removeClassName(I_LayoutBundle.INSTANCE.form().inActive()); fireValueChange(true); } else { getElement().addClassName(I_LayoutBundle.INSTANCE.form().inActive()); } } } /** * @see com.alkacon.acacia.client.widgets.I_EditWidget#setName(java.lang.String) */ public void setName(String name) { // no input field so nothing to do } /** * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object) */ public void setValue(String value) { setValue(value, false); } /** * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object, boolean) */ public void setValue(String value, boolean fireEvents) { if (value != null) { value = value.trim(); } setPreviousValue(value); if (m_editor == null) { // editor has not been initialized yet m_originalContent = value; } else { m_externalValueChange = true; setContent(value); } if (fireEvents) { fireValueChange(true); } } /** * Checks whether the necessary Javascript libraries are available by accessing them. */ protected native void checkLibraries() /*-{ // fail early if tinymce is not available var w = $wnd; var init = w.tinyMCE.init; }-*/; /** * Gives an element an id if it doesn't already have an id, and then returns the element's id.

* * @param element the element for which we want to add the id * * @return the id */ protected String ensureId(Element element) { String id = element.getId(); if ((id == null) || "".equals(id)) { id = Document.get().createUniqueId(); element.setId(id); } return id; } /** * Returns the editor parent element.

* * @return the editor parent element */ protected Element getEditorParentElement() { String parentId = m_id + "_parent"; Element result = getElementById(parentId); return result; } /** * Gets an element by its id.

* * @param id the id * @return the element with the given id */ protected native Element getElementById(String id) /*-{ return $doc.getElementById(id); }-*/; /** * Gets the toolbar element.

* * @return the toolbar element */ protected Element getToolbarElement() { String toolbarId = m_id + "_external"; Element result = getElementById(toolbarId); return result; } /** * Checks if the main element contains the current text selection.

* * @return true if the main element contains the current text selection */ protected boolean shouldReceiveFocus() { return m_inline && EditorBase.shouldFocusOnInlineEdit(getElement()); } /** * Returns if the widget is used in inline mode.

* * @return true if the widget is used in inline mode */ protected boolean isInline() { return m_inline; } /** * @see com.google.gwt.user.client.ui.FocusWidget#onAttach() */ @Override protected void onAttach() { super.onAttach(); if (!m_hasBeenAttached) { m_hasBeenAttached = true; Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { if (isAttached()) { m_editorHeight = calculateEditorHeight(); m_id = ensureId(getMainElement()); m_width = calculateWidth(); checkLibraries(); if (isInline()) { if (DomUtil.getCurrentStyleInt(getElement(), Style.zIndex) < 1) { getElement().getStyle().setZIndex(1); } addDomHandler(new ClickHandler() { public void onClick(ClickEvent event) { // prevent event propagation while editing inline, to avoid following links in ancestor nodes event.stopPropagation(); event.preventDefault(); } }, ClickEvent.getType()); } initNative(); if (!m_active) { getElement().addClassName(I_LayoutBundle.INSTANCE.form().inActive()); } } else { resetAtachedFlag(); } } }); } } /** * @see com.google.gwt.user.client.ui.Widget#onDetach() */ @Override protected void onDetach() { detachEditor(); if (m_toolbarContainer != null) { m_toolbarContainer.removeFromParent(); m_toolbarContainer = null; } super.onDetach(); } /** * Propagates the a focus event.

*/ protected void propagateFocusEvent() { NativeEvent nativeEvent = Document.get().createFocusEvent(); DomEvent.fireNativeEvent(nativeEvent, this, getElement()); } /** * Propagates a native mouse event.

* * @param eventType the mouse event type * @param eventSource the event source */ protected native void propagateMouseEvent(String eventType, Element eventSource) /*-{ var doc = $wnd.document; var event; if (doc.createEvent) { event = doc.createEvent("MouseEvents"); event.initEvent(eventType, true, true); eventSource.dispatchEvent(event); } else { eventSource.fireEvent("on" + eventType); } }-*/; /** * Sets focus to the editor. Use only when in line editing.

*/ protected native void refocusInlineEditor() /*-{ var elem = $wnd.document .getElementById([email protected]::m_id); elem.blur(); elem.focus(); }-*/; /** * Removes the editor instance.

*/ protected native void removeEditor() /*-{ var editor = [email protected]::m_editor; editor.remove(); }-*/; /** * Schedules to reset the focus to the main element.

*/ protected void scheduleRefocus() { // this needs to be delayed a bit, otherwise the toolbar is not rendered properly Timer focusTimer = new Timer() { @Override public void run() { refocusInlineEditor(); } }; focusTimer.schedule(150); } /** * Sets the main content of the element which is inline editable.

* * @param html the new content html */ protected native void setMainElementContent(String html) /*-{ var instance = this; var elementId = [email protected]::m_id; var mainElement = $wnd.document.getElementById(elementId); mainElement.innerHTML = html; }-*/; /** * Calculates the needed editor height.

* * @return the calculated editor height */ int calculateEditorHeight() { int result = getElement().getOffsetHeight() + 30; return result > MIN_EDITOR_HEIGHT ? result : MIN_EDITOR_HEIGHT; } /** * Calculates the widget width.

* * @return the widget width */ int calculateWidth() { int result; if (m_inline && DomUtil.getCurrentStyle(getElement(), Style.display).equals("inline")) { com.google.gwt.dom.client.Element parentBlock = getElement().getParentElement(); while (DomUtil.getCurrentStyle(parentBlock, Style.display).equals("inline")) { parentBlock = parentBlock.getParentElement(); } result = parentBlock.getOffsetWidth(); } else { result = getElement().getOffsetWidth(); } return result - 2; } /** * Initializes the TinyMCE instance. */ native void initNative() /*-{ var self = this; var needsRefocus = [email protected]::shouldReceiveFocus()(); var elementId = [email protected]::m_id; var mainElement = $wnd.document.getElementById(elementId); var editorHeight = [email protected]::m_editorHeight + "px"; var fireChange = function() { [email protected]::fireChangeFromNative()(); }; var options = [email protected]::m_options; if (options != null && options.editorHeight) { editorHeight = options.editorHeight; delete options.editorHeight; } // default options: var defaults; if (@com.alkacon.acacia.client.widgets.TinyMCEWidget::NO_HTML_EDIT == options) { // disallow any formatting defaults = { selector : mainElement.tagName+"#"+ elementId, entity_encoding : "raw", mode : "exact", plugins : "paste", paste_as_text: true, toolbar : "undo,redo", menubar : false, toolbar_items_size : 'small' }; options = null; } else { defaults = { elements : elementId, relative_urls : false, remove_script_host : false, entity_encoding : "raw", skin_variant : 'ocms', mode : "exact", theme : "modern", plugins : "autolink,lists,pagebreak,layer,table,save,hr,image,link,emoticons,spellchecker,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,noneditable,visualchars,nonbreaking,template,wordcount,advlist", paste_as_text: true, menubar : false, toolbar_items_size : 'small' }; } if ([email protected]::m_inline) { [email protected]::m_currentContent = mainElement.innerHTML; defaults.inline = true; defaults.width = [email protected]::m_width; var toolbarContainer = $wnd.document.createElement("div"); toolbarContainer.className = @com.alkacon.acacia.client.widgets.TinyMCEWidget::TOOLBAR_CONTAINER; toolbarContainer.innerHTML = "

"; $wnd.document.body.appendChild(toolbarContainer); [email protected]::m_toolbarContainer = toolbarContainer; defaults.fixed_toolbar_container = "#" + elementId + "_toolbarContainer"; } else { [email protected]::m_currentContent = [email protected]::m_originalContent; defaults.autoresize_min_height = 100; defaults.autoresize_max_height = editorHeight; defaults.width = '100%'; defaults.resize = 'both'; } // extend the defaults with any given options if (options != null) { var vie = @com.alkacon.vie.client.Vie::getInstance()(); vie.jQuery.extend(defaults, options); if ([email protected]::m_inline) { delete defaults.content_css; } else { // enable autoresize defaults.plugins = "autoresize," + defaults.plugins; } } // add the setup function defaults.setup = function(ed) { [email protected]::m_editor = ed; ed.on('SetContent', fireChange); ed.on('change', fireChange); ed.on('KeyDown', fireChange); ed .on( 'LoadContent', function() { if ([email protected]::m_inline) { // firing resize event on resize of the editor iframe ed.dom .bind( ed.getWin(), 'resize', function() { [email protected]::fireResizeEvent()(); }); var content = [email protected]::m_originalContent; if (content != null) { ed.setContent(content); } // ensure the body height is set to 'auto', otherwise the autoresize plugin will not work ed.getDoc().body.style.height = 'auto'; } [email protected]::m_initialized = true; }); if ([email protected]::m_inline) { ed .on( 'Click', function(event) { [email protected]::propagateFocusEvent()(); }); ed .on( 'activate', function(event) { [email protected]::propagateFocusEvent()(); }); ed .on( 'focus', function(event) { [email protected]::propagateFocusEvent()(); }); } else { if (needsRefocus) { ed .on( 'init', function() { [email protected]::scheduleRefocus()(); }); } ed .on( 'focus', function(event) { [email protected]::resetToolbarContainerPosition()(); }); } }; // set default z-index for overlay ui components var cssConstants = @com.alkacon.acacia.client.css.I_LayoutBundle::[email protected]_LayoutBundle::constants()()[email protected]_ConstantsBundle::css()(); $wnd.tinymce.ui.FloatPanel.zIndex = [email protected]_ConstantsBundle.I_ConstantsCss::zIndexPopup()(); // initialize tinyMCE $wnd.tinymce.init(defaults); }-*/; /** * Resets the attached flag.

*/ void resetAtachedFlag() { m_hasBeenAttached = false; } /** * Removes the editor.

*/ private native void detachEditor() /*-{ var ed = [email protected]::m_editor; if (ed != null) { ed.remove(); } // in IE somehow the whole document will be selected, empty the selection to resolve that if ($wnd.document.selection != null) { $wnd.document.selection.empty(); } }-*/; /** * Used to fire the value changed event from native code.

*/ private void fireChangeFromNative() { // skip firing the change event, if the external flag is set if (m_initialized && !m_externalValueChange) { Scheduler.get().scheduleDeferred(new ScheduledCommand() { public void execute() { try { fireValueChange(false); } catch (Throwable t) { // this may happen when returning from full screen mode, nothing to be done } } }); } // reset the external flag m_externalValueChange = false; } /** * Fires the resize event.

*/ private void fireResizeEvent() { ResizeEvent.fire(this, getOffsetWidth(), getOffsetHeight()); } /** * Returns the editor content.

* * @return the editor content */ private native String getContent() /*-{ var editor = [email protected]::m_editor; return editor.getContent(); }-*/; /** * Resets the in line editing toolbar position.

*/ private void resetToolbarContainerPosition() { if (m_toolbarContainer != null) { PositionBean position = PositionBean.generatePositionInfo(m_contentElement); m_toolbarContainer.getStyle().setTop(position.getTop() - 5, Unit.PX); m_toolbarContainer.getStyle().setLeft(position.getLeft(), Unit.PX); } } /** * Sets the content of the TinyMCE editor.

* * @param newContent the new content */ private native void setContent(String newContent) /*-{ var editor = [email protected]::m_editor; editor.setContent(newContent); }-*/; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy