com.ait.toolkit.editors.ckeditor.client.CKEditor.java Source code

Java tutorial

Introduction

Here is the source code for com.ait.toolkit.editors.ckeditor.client.CKEditor.java

Source

/**
 * This program 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 3 of the License, or
 * (at your option) any later version.
 * 
 * This program 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 General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */
package com.ait.toolkit.editors.ckeditor.client;

import com.ait.toolkit.editors.ckeditor.client.event.HasInstanceReadyHandlers;
import com.ait.toolkit.editors.ckeditor.client.event.HasSaveHandlers;
import com.ait.toolkit.editors.ckeditor.client.event.InstanceReadyEvent;
import com.ait.toolkit.editors.ckeditor.client.event.InstanceReadyHandler;
import com.ait.toolkit.editors.ckeditor.client.event.SaveEvent;
import com.ait.toolkit.editors.ckeditor.client.event.SaveHandler;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.dom.client.DivElement;
import com.google.gwt.dom.client.Node;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.HasValueChangeHandlers;
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.Event;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FormPanel;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HTMLPanel;
import com.google.gwt.user.client.ui.HasAlignment;
import com.google.gwt.user.client.ui.HasHTML;
import com.google.gwt.user.client.ui.HasText;
import com.google.gwt.user.client.ui.ScrollPanel;
import com.google.gwt.user.client.ui.SimplePanel;
import com.google.gwt.user.client.ui.TextArea;

/**
 * This class provides a CKEdtior as a Widget
 * 
 * @author Damien Picard <damien.picard@axeiya.com>
 * @author Emmanuel COQUELIN <emmanuel.coquelin@axeiya.com>
 */
public class CKEditor extends Composite implements HasSaveHandlers<CKEditor>, HasValueChangeHandlers<String>,
        ClickHandler, HasAlignment, HasHTML, HasText, HasInstanceReadyHandlers {

    /**
     * Used for catching Save event
     * 
     * @param o
     * @return
     */
    private static native String getParentClassname(JavaScriptObject o) /*-{
                                                                        var classname = o.parentNode.getAttribute("class");
                                                                        if (classname == null)
                                                                        return o.parentNode.className;
                                                                        return classname;
                                                                        }-*/;

    private static class AutoSaveTimer extends Timer {

        protected CKEditor editor;

        public AutoSaveTimer(CKEditor editor) {
            this.editor = editor;
        }

        @Override
        public void run() {
            editor.save();
        }

        public void delay() {
            this.cancel();
            if (editor.config.getAutoSaveLatencyInMillis() > 0) {
                this.schedule(editor.config.getAutoSaveLatencyInMillis());
            }
        }

    }

    protected String name;
    protected JavaScriptObject editor;
    protected TextArea textArea;
    protected Element baseTextArea;
    protected JavaScriptObject dataProcessor;
    protected CKConfig config;
    protected boolean replaced = false;
    protected boolean textWaitingForAttachment = false;
    protected String waitingText;
    protected boolean waitingForDisabling = false;
    protected boolean disabled = false;
    protected Element div;
    protected Node ckEditorNode;
    protected HTML disabledHTML;

    protected AutoSaveTimer autoSaveTimer = new AutoSaveTimer(this);

    protected boolean focused = false;
    protected HorizontalAlignmentConstant hAlign = null;

    protected VerticalAlignmentConstant vAlign = null;

    /**
     * Creates an editor with the CKConfig.basic configuration. By default, the
     * CKEditor is enabled in hosted mode ; if not, the CKEditor is replaced by
     * a simple TextArea
     */
    public CKEditor() {
        this(CKConfig.basic);
    }

    /**
     * Creates an editor with the given configuration. By default, the CKEditor
     * is enabled in hosted mode ; if not, the CKEditor is replaced by a simple
     * TextArea
     * 
     * @param config
     *            The configuration
     */
    public CKEditor(CKConfig config) {
        super();
        this.config = config;
        initCKEditor();
    }

    /**
     * Add an instanceReady handler.
     * <p>
     * Fired when the CKEDITOR instance is completely created, fully initialized
     * and ready for interaction.
     * 
     * @param handler
     *            the instanceReady handler
     * @return {@link HandlerRegistration} used to remove this handler
     */
    @Override
    public HandlerRegistration addInstanceReadyHandler(InstanceReadyHandler handler) {
        return addHandler(handler, InstanceReadyEvent.TYPE);
    }

    private native void bindInstanceReadyEvent() /*-{
                                                 var selfJ = this;
                                                 var editor = this.@com.ait.toolkit.editors.ckeditor.client.CKEditor::editor;
                                                 editor
                                                 .on(
                                                 'instanceReady',
                                                 function() {
                                                 @com.ait.toolkit.editors.ckeditor.client.event.InstanceReadyEvent::fire(Lcom/ait/toolkit/editors/ckeditor/client/event/HasInstanceReadyHandlers;Lcom/ait/toolkit/editors/ckeditor/client/CKEditor;)(selfJ, selfJ);
                                                 });
                                                 }-*/;

    private native void bindChangeEvent() /*-{
                                          var selfJ = this;
                                          var editor = this.@com.ait.toolkit.editors.ckeditor.client.CKEditor::editor;
                                          var timer = selfJ.@com.ait.toolkit.editors.ckeditor.client.CKEditor::autoSaveTimer;
                                          editor.on('change', function() {
                                          @com.google.gwt.event.logical.shared.ValueChangeEvent::fire(Lcom/google/gwt/event/logical/shared/HasValueChangeHandlers;Ljava/lang/Object;)(selfJ, editor.getData());
                                          timer.@com.ait.toolkit.editors.ckeditor.client.CKEditor.AutoSaveTimer::delay()();
                                          });
                                          }-*/;

    /**
     * Fired when the content of the editor is changed.
     * 
     * Due to performance reasons, it is not verified if the content really
     * changed. The editor instead watches several editing actions that usually
     * result in changes. This event may thus in some cases be fired when no
     * changes happen or may even get fired twice.
     * 
     * If it is important not to get the change event too often, you should
     * compare the previous and the current editor content inside the event
     * listener.
     */
    @Override
    public HandlerRegistration addSaveHandler(SaveHandler<CKEditor> handler) {
        return addHandler(handler, SaveEvent.getType());
    }

    @Override
    public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) {
        return this.addHandler(handler, ValueChangeEvent.getType());
    }

    private native void destroyInstance()/*-{
                                         var editor = this.@com.ait.toolkit.editors.ckeditor.client.CKEditor::editor;
                                         if (editor) {
                                         editor.destroy();
                                         }
                                         }-*/;

    /**
     * {@link #getHTML()}
     * 
     * @return
     */
    public String getData() {
        return getHTML();
    }

    @Override
    public HorizontalAlignmentConstant getHorizontalAlignment() {
        return hAlign;
    }

    /**
     * Returns the editor text
     * 
     * @return the editor text
     */
    public String getHTML() {
        if (replaced)
            return getNativeHTML();
        else {
            return waitingText;
        }
    }

    private native String getNativeHTML() /*-{
                                          var e = this.@com.ait.toolkit.editors.ckeditor.client.CKEditor::editor;
                                          return e.getData();
                                          }-*/;

    public native JavaScriptObject getSelection() /*-{
                                                  var e = this.@com.ait.toolkit.editors.ckeditor.client.CKEditor::editor;
                                                  return e.getSelection();
                                                  }-*/;

    /**
     * Use getHTML() instead. Returns the editor text
     * 
     * @return the editor text
     */
    @Deprecated
    public String getText() {
        return getNativeHTML();
    }

    @Override
    public VerticalAlignmentConstant getVerticalAlignment() {
        return vAlign;
    }

    /**
     * Initialize the editor
     */
    private void initCKEditor() {
        div = DOM.createDiv();
        baseTextArea = DOM.createTextArea();
        name = HTMLPanel.createUniqueId();
        div.appendChild(baseTextArea);
        DOM.setElementAttribute(baseTextArea, "name", name);
        this.sinkEvents(Event.ONCLICK | Event.KEYEVENTS);

        if (config.isUsingFormPanel()) {
            FormPanel form = new FormPanel();
            Button submit = new Button();
            submit.addClickHandler(this);
            submit.getElement().setAttribute("name", "submit");
            submit.setVisible(false);
            // .getElement().setAttribute("style", "visibility:hidden");

            form.getElement().appendChild(div);
            form.add(submit);
            initWidget(form);
        } else {
            SimplePanel simplePanel = new SimplePanel();
            simplePanel.getElement().appendChild(div);
            initWidget(simplePanel);
        }
    }

    /**
     * Replace the text Area by a CKEditor Instance
     */
    protected void initInstance() {
        if (!replaced && !disabled) {
            replaced = true;
            replaceTextArea(baseTextArea, this.config.getConfigObject());

            if (textWaitingForAttachment) {
                textWaitingForAttachment = false;
                setHTML(waitingText);
            }

            if (hAlign != null) {
                setHorizontalAlignment(hAlign);
            }

            if (vAlign != null) {
                setVerticalAlignment(vAlign);
            }

            if (this.config.isFocusOnStartup()) {
                this.focused = true;
                setAddFocusOnLoad(focused);
            }

            if (waitingForDisabling) {
                this.waitingForDisabling = false;
                setEnabled(this.disabled);
            }

            bindInstanceReadyEvent();
            bindChangeEvent();
            /*
             * if (config.getBreakLineChars() != null) {
             * setNativeBreakLineChars(config.getBreakLineChars()); }
             * 
             * if (config.getSelfClosingEnd() != null) {
             * setNativeSelfClosingEnd(config.getSelfClosingEnd()); }
             */
        }

    }

    public boolean isDisabled() {
        return disabled;
    }

    @Override
    public void onClick(ClickEvent event) {
        if (event.getRelativeElement().getAttribute("name").equals("submit")) {
            event.stopPropagation();
            save();
        }
    }

    /**
     * Dispatch a save event
     */
    protected void save() {
        SaveEvent.fire(this, this, this.getHTML());
    }

    @Override
    protected void onLoad() {
        initInstance();
    }

    private native void replaceTextArea(Object o, JavaScriptObject config) /*-{
                                                                           this.@com.ait.toolkit.editors.ckeditor.client.CKEditor::editor = $wnd.CKEDITOR
                                                                           .replace(o, config);
                                                                           }-*/;

    private native void setAddFocusOnLoad(boolean focus)/*-{
                                                        var e = this.@com.ait.toolkit.editors.ckeditor.client.CKEditor::editor;
                                                            
                                                        e.on('dataReady', function(ev) {
                                                            
                                                        if (focus) {
                                                        e.focus();
                                                        var lastc = e.document.getBody().getLast();
                                                        e.getSelection().selectElement(lastc);
                                                        var range = e.getSelection().getRanges()[0];
                                                        if (range != null) {
                                                        range.collapse(false);
                                                        range.setStart(lastc, range.startOffset);
                                                        try {
                                                        range.setEnd(lastc, range.endOffset);
                                                        } catch (err) {
                                                        }
                                                        range.select();
                                                        }
                                                        }
                                                            
                                                        });
                                                        }-*/;

    /**
     * {@link #setHTML(String)}
     * 
     * @param data
     */
    public void setData(String data) {
        setHTML(data);
    }

    /**
     * Use to disable CKEditor's instance
     * 
     * @param disabled
     */
    public void setEnabled(boolean enabled) {
        // FIXME : rework this part to remove the !
        boolean disabled = !enabled;

        if (this.disabled != disabled) {
            this.disabled = disabled;

            if (disabled) {
                ScrollPanel scroll = new ScrollPanel();
                disabledHTML = new HTML();
                disabledHTML.setStyleName("GWTCKEditor-Disabled");
                scroll.setWidget(disabledHTML);

                if (config.getWidth() != null)
                    scroll.setWidth(config.getWidth());

                if (config.getHeight() != null)
                    scroll.setHeight(config.getHeight());

                String htmlString = new String();

                if (replaced) {
                    htmlString = getHTML();
                } else {
                    htmlString = waitingText;
                }

                DivElement divElement = DivElement.as(this.getElement().getFirstChildElement());
                Node node = divElement.getFirstChild();
                while (node != null) {
                    if (node.getNodeType() == Node.ELEMENT_NODE) {
                        com.google.gwt.dom.client.Element element = com.google.gwt.dom.client.Element.as(node);
                        if (element.getTagName().equalsIgnoreCase("textarea")) {
                            destroyInstance();
                            replaced = false;
                            divElement.removeChild(node);
                            ckEditorNode = node;
                        }
                    }
                    node = node.getNextSibling();
                }
                disabledHTML.setHTML(htmlString);
                div.appendChild(scroll.getElement());

            } else {
                if (ckEditorNode != null) {
                    DivElement divElement = DivElement.as(this.getElement().getFirstChildElement());
                    Node node = divElement.getFirstChild();
                    while (node != null) {
                        if (node.getNodeType() == Node.ELEMENT_NODE) {
                            com.google.gwt.dom.client.Element element = com.google.gwt.dom.client.Element.as(node);
                            if (element.getTagName().equalsIgnoreCase("div")) {
                                divElement.removeChild(node);

                            }
                        }
                        node = node.getNextSibling();
                    }
                    div.appendChild(baseTextArea);
                    initInstance();

                }
            }
        }

    }

    /**
     * Set the focus natively if ckEditor is attached, alerts you if it's not
     * the case.
     * 
     * @param focus
     */
    public void setFocus(boolean focus) {
        if (replaced == true) {
            setNativeFocus(focus);
        } else {
            Window.alert("You can't set the focus on startup with the method setFocus(boolean focus).\n"
                    + "If you want to add focus to your instance on startup, use the config object\n"
                    + "with the method setFocusOnStartup(boolean focus) instead.");
        }
    }

    /**
     * If you want to set the height, you must do so with the configuration
     * object before instanciating
     */
    @Deprecated
    @Override
    public void setHeight(String height) {
        super.setHeight(height);
    }

    @Override
    public void setHorizontalAlignment(HorizontalAlignmentConstant align) {
        this.hAlign = align;
        if (replaced)
            this.getElement().getParentElement().setAttribute("align", align.getTextAlignString());
    }

    /**
     * Set the editor's html
     * 
     * @param html
     *            The html string to set
     */
    public void setHTML(String html) {
        if (replaced)
            setNativeHTML(html);
        else {
            waitingText = html;
            textWaitingForAttachment = true;
        }
    }

    private native void setNativeFocus(boolean focus)/*-{
                                                     if (focus) {
                                                     var e = this.@com.ait.toolkit.editors.ckeditor.client.CKEditor::editor;
                                                     if (e) {
                                                     e.focus();
                                                         
                                                     var lastc = e.document.getBody().getLast();
                                                     e.getSelection().selectElement(lastc);
                                                     var range = e.getSelection().getRanges()[0];
                                                     range.collapse(false);
                                                     range.setStart(lastc, range.startOffset);
                                                     try {
                                                     range.setEnd(lastc, range.endOffset);
                                                     } catch (err) {
                                                     }
                                                     range.select();
                                                     }
                                                     }
                                                     }-*/;

    private native void setNativeHTML(String html) /*-{
                                                   var e = this.@com.ait.toolkit.editors.ckeditor.client.CKEditor::editor;
                                                   e.setData(html);
                                                   }-*/;

    /**
     * Use setHtml(String html) instead. Set the editor text
     * 
     * @param text
     *            The text to set
     */
    @Deprecated
    public void setText(String text) {
        if (replaced)
            setNativeHTML(text);
        else {
            waitingText = text;
            textWaitingForAttachment = true;
        }
    }

    @Override
    public void setVerticalAlignment(VerticalAlignmentConstant align) {
        this.vAlign = align;
        if (replaced)
            this.getElement().getParentElement().setAttribute("style",
                    "vertical-align:" + align.getVerticalAlignString());

    }

    /**
     * If you want to set the width, you must do so with the configuration
     * object before instanciating
     */
    @Deprecated
    @Override
    public void setWidth(String width) {
        super.setWidth(width);
    }
}