
com.alkacon.acacia.client.widgets.TinyMCEWidget Maven / Gradle / Ivy
Show all versions of acacia-editor-client Show documentation
/*
* 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.css.I_LayoutBundle;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
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.Display;
import com.google.gwt.dom.client.Style.Unit;
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;
/**
* 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 {
/** The minimum editor height. */
private static final int MIN_EDITOR_HEIGHT = 70;
/** 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;
/** Indicating if the widget has been attached yet. */
private boolean m_hasBeenAttached;
/** Flag indicating the editor has been initialized. */
private boolean m_initialized;
/** The editor options. */
private JavaScriptObject m_options;
/**
* Creates a new instance for the given element.
*
* @param element the DOM element
* @param options the tinyMCE editor options to extend the default settings
*/
public TinyMCEWidget(Element element, JavaScriptObject options) {
super(element);
m_originalContent = "";
m_options = options;
m_active = true;
}
/**
* Constructor.
*
* @param options the tinyMCE editor options to extend the default settings
*/
public TinyMCEWidget(JavaScriptObject options) {
this(DOM.createDiv(), options);
}
/**
* @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 native Element getMainElement() /*-{
var elementId = [email protected]::m_id;
var mainElement = $wnd.document.getElementById(elementId);
return mainElement;
}-*/;
/**
* @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) {
getEditorParentElement().removeClassName(I_LayoutBundle.INSTANCE.form().inActive());
// getElement().focus();
fireValueChange(true);
} else {
getEditorParentElement().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 {
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;
}
/**
* Fixes the styling of the editor widget.
*/
protected void fixStyles() {
// it may take some time until the editor has been initialized, repeat until layout fix can be applied
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
/**
* @see com.google.gwt.core.client.Scheduler.RepeatingCommand#execute()
*/
public boolean execute() {
Element parent = getEditorParentElement();
if (parent != null) {
String cssClass = getMainElement().getClassName();
if ((cssClass != null) && (cssClass.trim().length() > 0)) {
parent.addClassName(cssClass);
}
parent.getStyle().setDisplay(Display.BLOCK);
getEditorTableElement().getStyle().setWidth(100, Unit.PCT);
return false;
}
return true;
}
}, 100);
}
/**
* Returns the editor parent element.
*
* @return the editor parent element
*/
protected native Element getEditorParentElement() /*-{
var elementId = [email protected]::m_id;
var parentId = elementId + "_parent";
return $doc.getElementById(parentId);
}-*/;
/**
* Returns the editor table element.
*
* @return the editor table element
*/
protected native Element getEditorTableElement() /*-{
var elementId = [email protected]::m_id;
var tableId = elementId + "_tbl";
return $doc.getElementById(tableId);
}-*/;
/**
* Returns the editor parent element.
*
* @return the editor parent element
*/
protected native int getFrameContentHeight() /*-{
var elementId = [email protected]::m_id;
var parentId = elementId + "_parent";
return $doc.getElementById(parentId);
}-*/;
/**
* Gets the toolbar element.
*
* @return the toolbar element
*/
protected native Element getToolbarElement() /*-{
var elementId = [email protected]::m_id;
var toolbarId = elementId + "_external";
return $doc.getElementById(toolbarId);
}-*/;
/**
* @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(getElement());
m_width = getElement().getOffsetWidth() - 2;
checkLibraries();
initNative();
if (!m_active) {
Element parent = getEditorParentElement();
if (parent != null) {
parent.addClassName(I_LayoutBundle.INSTANCE.form().inActive());
}
}
} else {
resetAtachedFlag();
}
}
});
}
}
/**
* Propagates the a focus event.
*/
protected void propagateFocusEvent() {
NativeEvent nativeEvent = Document.get().createFocusEvent();
DomEvent.fireNativeEvent(nativeEvent, this, 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);
}
}-*/;
/**
* Removes the editor instance.
*/
protected native void removeEditor() /*-{
var editor = [email protected]::m_editor;
editor.remove();
}-*/;
/**
* 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;
}
/**
* Initializes the TinyMCE instance.
*/
native void initNative() /*-{
var self = this;
var elementId = [email protected]::m_id;
var iframeId = elementId + "_ifr";
var mainElement = $wnd.document.getElementById(elementId);
var editorHeight = [email protected]::m_editorHeight
+ "px";
[email protected]::m_currentContent = [email protected]::m_originalContent;
var fireChange = function() {
[email protected]::fireChangeFromNative()();
};
// default options:
var defaults = {
onchange_callback : fireChange,
theme_advanced_resize_horizontal : true,
theme_advanced_resizing_max_width : [email protected]::m_width,
relative_urls : false,
remove_script_host : false,
entity_encoding : "raw",
skin_variant : 'ocms',
mode : "exact",
theme : "advanced",
plugins : "autolink,lists,pagebreak,style,layer,table,save,advhr,advimage,advlink,emotions,iespell,inlinepopups,insertdatetime,preview,media,searchreplace,print,contextmenu,paste,directionality,noneditable,visualchars,nonbreaking,xhtmlxtras,template,wordcount,advlist",
theme_advanced_toolbar_location : "external",
theme_advanced_toolbar_align : "right",
theme_advanced_statusbar_location : "bottom",
width : '100%',
theme_advanced_resizing : true,
theme_advanced_resizing_use_cookie : false
};
var options = [email protected]::m_options;
if (options != null && options.editorHeight) {
editorHeight = options.editorHeight + "px";
delete options.editorHeight;
}
// extend the defaults with any given options
if (options != null) {
var vie = @com.alkacon.vie.client.Vie::getInstance()();
vie.jQuery.extend(defaults, options);
}
// add the setup function
defaults.setup = function(ed) {
[email protected]::m_editor = ed;
ed.onSetContent.add(fireChange);
ed.onKeyDown.add(fireChange);
ed.onLoadContent
.add(function() {
$wnd.document.getElementById(iframeId).style.minHeight = editorHeight;
// 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);
}
[email protected]::m_initialized = true;
});
ed.onClick
.add(function(event) {
if ([email protected]::isActive()()) {
// this may be the case if the mouse-down event has not been triggered correctly yet (IE),
// trigger activation through new mouse-down
[email protected]::propagateMouseEvent(Ljava/lang/String;Lcom/google/gwt/user/client/Element;)('mousedown', mainElement);
}
[email protected]::propagateMouseEvent(Ljava/lang/String;Lcom/google/gwt/user/client/Element;)('click', mainElement);
});
};
// add event handlers
defaults.init_instance_callback = function(ed) {
$wnd.tinyMCE.dom.Event
.add(
ed.getWin(),
'focus',
function(event) {
[email protected]::propagateFocusEvent()();
});
$wnd.tinyMCE.dom.Event
.add(
ed.getWin(),
'mousedown',
function(event) {
[email protected]::propagateMouseEvent(Ljava/lang/String;Lcom/google/gwt/user/client/Element;)('mousedown', mainElement);
});
$wnd.tinyMCE.dom.Event
.add(
ed.getWin(),
'mouseup',
function(event) {
[email protected]::propagateMouseEvent(Ljava/lang/String;Lcom/google/gwt/user/client/Element;)('mouseup', mainElement);
});
// $wnd.tinyMCE.dom.Event
// .add(
// ed.getWin(),
// 'mousemove',
// function(event) {
// [email protected]::propagateMouseEvent(Ljava/lang/String;Lcom/google/gwt/user/client/Element;)('mousemove', mainElement);
// });
};
// set the edited element id
defaults.elements = [email protected]::m_id;
// initialize tinyMCE
$wnd.tinyMCE.init(defaults);
[email protected]::fixStyles()();
}-*/;
/**
* Resets the attached flag.
*/
void resetAtachedFlag() {
m_hasBeenAttached = false;
}
/**
* Used to fire the value changed event from native code.
*/
private void fireChangeFromNative() {
if (m_initialized) {
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
}
}
});
}
}
/**
* 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();
}-*/;
/**
* 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);
}-*/;
}