001package org.gwtbootstrap3.extras.summernote.client.ui.base;
002
003/*
004 * #%L
005 * GwtBootstrap3
006 * %%
007 * Copyright (C) 2016 GwtBootstrap3
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 * 
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 * 
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import org.gwtbootstrap3.client.ui.html.Div;
024import org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers;
025import org.gwtbootstrap3.extras.summernote.client.event.SummernoteBlurEvent;
026import org.gwtbootstrap3.extras.summernote.client.event.SummernoteBlurHandler;
027import org.gwtbootstrap3.extras.summernote.client.event.SummernoteChangeEvent;
028import org.gwtbootstrap3.extras.summernote.client.event.SummernoteChangeHandler;
029import org.gwtbootstrap3.extras.summernote.client.event.SummernoteEnterEvent;
030import org.gwtbootstrap3.extras.summernote.client.event.SummernoteEnterHandler;
031import org.gwtbootstrap3.extras.summernote.client.event.SummernoteFocusEvent;
032import org.gwtbootstrap3.extras.summernote.client.event.SummernoteFocusHandler;
033import org.gwtbootstrap3.extras.summernote.client.event.SummernoteImageUploadEvent;
034import org.gwtbootstrap3.extras.summernote.client.event.SummernoteImageUploadEvent.ImageFile;
035import org.gwtbootstrap3.extras.summernote.client.event.SummernoteImageUploadHandler;
036import org.gwtbootstrap3.extras.summernote.client.event.SummernoteInitEvent;
037import org.gwtbootstrap3.extras.summernote.client.event.SummernoteInitHandler;
038import org.gwtbootstrap3.extras.summernote.client.event.SummernoteKeyDownEvent;
039import org.gwtbootstrap3.extras.summernote.client.event.SummernoteKeyDownHandler;
040import org.gwtbootstrap3.extras.summernote.client.event.SummernoteKeyUpEvent;
041import org.gwtbootstrap3.extras.summernote.client.event.SummernoteKeyUpHandler;
042import org.gwtbootstrap3.extras.summernote.client.event.SummernotePasteEvent;
043import org.gwtbootstrap3.extras.summernote.client.event.SummernotePasteHandler;
044
045import com.google.gwt.core.client.JavaScriptObject;
046import com.google.gwt.core.client.JsArray;
047import com.google.gwt.core.client.JsArrayString;
048import com.google.gwt.core.client.ScriptInjector;
049import com.google.gwt.dom.client.Element;
050import com.google.gwt.event.shared.HandlerRegistration;
051import com.google.gwt.user.client.ui.HasEnabled;
052import com.google.gwt.user.client.ui.UIObject;
053
054/**
055 * Wrapper for the Summernote WYSIWYG Editor
056 * <p/>
057 * See: http://summernote.org/
058 *
059 * @author Xiaodong Sun
060 */
061public class SummernoteBase extends Div implements HasAllSummernoteHandlers, HasEnabled {
062
063    /**
064     * Language; defaults to {@link SummernoteLanguage#EN_US}
065     */
066    private SummernoteLanguage language = SummernoteLanguage.EN_US;
067
068    /**
069     * Initialize options
070     */
071    private SummernoteOptions options = SummernoteOptions.newOptions();
072
073    /**
074     * Enabled/Disabled state
075     */
076    private boolean enabled = true;
077
078    private boolean hasInitHandler = false;
079    private boolean hasEnterHandler = false;
080    private boolean hasFocusHandler = false;
081    private boolean hasBlurHandler = false;
082    private boolean hasKeyUpHandler = false;
083    private boolean hasKeyDownHandler = false;
084    private boolean hasPasteHandler = false;
085    private boolean hasUploadImageHandler = false;
086    private boolean hasChangeHandler = false;
087
088    /**
089     *
090     */
091    public SummernoteBase() {}
092
093    /**
094     * Sets the default height of the editor (in pixel).<br>
095     * <br>
096     * <b>Note</b>: DO NOT renamed this method to <em>setHeight</em>
097     * to avoid UiBinder name clash with {@link UIObject#setHeight(String)}.
098     *
099     * @param height
100     */
101    public void setDefaultHeight(final int height) {
102        options.setHeight(height);
103    }
104
105    /**
106     * Sets the maximum height of the editor (in pixel).
107     *
108     * @param maxHeight
109     */
110    public void setMaxHeight(final int maxHeight) {
111        options.setMaxHeight(maxHeight);
112    }
113
114    /**
115     * Sets the minimum height of the editor (in pixel).
116     *
117     * @param minHeight
118     */
119    public void setMinHeight(final int minHeight) {
120        options.setMinHeight(minHeight);
121    }
122
123    /**
124     * If <code>false</code> the toolbar will be hidden.<br>
125     * <br>
126     * Defaults to <code>true</code>.
127     *
128     * @param showToolbar
129     */
130    public void setShowToolbar(final boolean showToolbar) {
131        options.setShowToolbar(showToolbar);
132    }
133
134    /**
135     * Customizes the toolbar.<br>
136     * <br>
137     * Example:
138     * <pre>
139     * summernote.setToolbar(new Toolbar()
140     *     .addGroup(ToolbarButton.OL, ToolbarButton.BOLD)
141     *     .addGroup(ToolbarButton.HELP));
142     * </pre>
143     *
144     * @param toolbar
145     */
146    public void setToolbar(final Toolbar toolbar) {
147        options.setToolbar(toolbar);
148    }
149
150    /**
151     * Set the focus of the editor.
152     *
153     * @param focus if <code>true</code>, focus on the editor
154     */
155    public void setHasFocus(final boolean focus) {
156        options.setFocus(focus);
157    }
158
159    /**
160     * Set placeholder of the editor.
161     *
162     * @param placeholder placeholder of the editor
163     */
164    public void setPlaceholder(final String placeholder) {
165        options.setPlaceholder(placeholder);
166    }
167
168    /**
169     * Set customized font names.
170     *
171     * @param fontNames customized font names
172     * @see SummernoteFontName
173     */
174    public void setFontNames(final SummernoteFontName... fontNames) {
175        JsArrayString array = JavaScriptObject.createArray().cast();
176        for (SummernoteFontName fontName : fontNames) {
177            array.push(fontName.getName());
178        }
179        options.setFontNames(array);
180    }
181
182    /**
183     * Set a list for Web fonts to be ignored. <br>
184     * <br>
185     * Summernote tests font in fontNames before adding them to drop-down.
186     * This is problem while using Web fonts. It’s not easy picking up
187     * nice time to check availabilities of Web fonts.
188     *
189     * @param fontNames
190     */
191    public void setFontNamesIgnoreCheck(final SummernoteFontName... fontNames) {
192        JsArrayString array = JavaScriptObject.createArray().cast();
193        for (SummernoteFontName fontName : fontNames) {
194            array.push(fontName.getName());
195        }
196        options.setFontNamesIgnoreCheck(array);
197    }
198
199    /**
200     * Set the air mode of the editor. Air-mode gives clearer interface with
201     * hidden toolbar. To reveal toolbar, select a text where you want to
202     * shape up.<br>
203     * <br>
204     * Defaults to <code>false</code>.
205     *
206     * @param airMode if <code>true</code>, the air mode is turn on
207    */
208    public void setAirMode(final boolean airMode) {
209        options.setAirMode(airMode);
210    }
211
212    /**
213     * Set <code>false</code> to disable custom shortcuts.<br>
214     * <br>
215     * Defaults to <code>true</code>.
216     *
217     * @param shortcuts if <code>false</code>, disable custom shortcuts
218     */
219    public void setShortcuts(final boolean shortcuts) {
220        options.setShortcuts(shortcuts);
221    }
222
223    /**
224     * Set <code>true</code> to place dialogs in &lt;body&gt;
225     * rather than in the editor.<br>
226     * <br>
227     * Defaults to <code>false</code>.
228     *
229     * @param dialogsInBody if <code>true</code>, place dialogs in &lt;body&gt;
230     */
231    public void setDialogsInBody(final boolean dialogsInBody) {
232        options.setDialogsInBody(dialogsInBody);
233    }
234
235    /**
236     * Set <code>true</code> to turn on dialogs fading effect
237     * when showing or hiding.<br>
238     * <br>
239     * Defaults to <code>false</code>.
240     *
241     * @param dialogsFade if <code>true</code>, turn on dialogs fading effect
242     */
243    public void setDialogsFade(final boolean dialogsFade) {
244        options.setDialogsFade(dialogsFade);
245    }
246
247    /**
248     * Set <code>true</code> to disable drag and drop.<br>
249     * <br>
250     * Defaults to <code>false</code>.
251     *
252     * @param disableDragAndDrop if <code>true</code>, disable drag and drop
253     */
254    public void setDisableDragAndDrop(final boolean disableDragAndDrop) {
255        options.setDisableDragAndDrop(disableDragAndDrop);
256    }
257
258    /**
259     * Summernote support hint (autocomplete) feature. You can define custom hint
260     * with options.
261     *
262     * @param matchRegexp
263     * @param hintHandler
264     */
265    public void setHint(String matchRegexp, HintHandler hintHandler) {
266        options.setHint(matchRegexp, hintHandler);
267    }
268
269    /**
270     * Set the editor language.
271     *
272     * @param language supported editor language
273     */
274    public void setLanguage(final SummernoteLanguage language) {
275        options.setLanguage(language);
276        this.language = language;
277    }
278
279    /**
280     * Returns the editor language.
281     *
282     * @return
283     */
284    public SummernoteLanguage getLanguage() {
285        return language;
286    }
287
288    @Override
289    public HandlerRegistration addSummernoteInitHandler(final SummernoteInitHandler handler) {
290        hasInitHandler = true;
291        return addHandler(handler, SummernoteInitEvent.getType());
292    }
293
294    @Override
295    public HandlerRegistration addSummernoteEnterHandler(final SummernoteEnterHandler handler) {
296        hasEnterHandler = true;
297        return addHandler(handler, SummernoteEnterEvent.getType());
298    }
299
300    @Override
301    public HandlerRegistration addSummernoteFocusHandler(final SummernoteFocusHandler handler) {
302        hasFocusHandler = true;
303        return addHandler(handler, SummernoteFocusEvent.getType());
304    }
305
306    @Override
307    public HandlerRegistration addSummernoteBlurHandler(final SummernoteBlurHandler handler) {
308        hasBlurHandler = true;
309        return addHandler(handler, SummernoteBlurEvent.getType());
310    }
311
312    @Override
313    public HandlerRegistration addSummernoteKeyUpHandler(final SummernoteKeyUpHandler handler) {
314        hasKeyUpHandler = true;
315        return addHandler(handler, SummernoteKeyUpEvent.getType());
316    }
317
318    @Override
319    public HandlerRegistration addSummernoteKeyDownHandler(final SummernoteKeyDownHandler handler) {
320        hasKeyDownHandler = true;
321        return addHandler(handler, SummernoteKeyDownEvent.getType());
322    }
323
324    @Override
325    public HandlerRegistration addSummernotePasteHandler(final SummernotePasteHandler handler) {
326        hasPasteHandler = true;
327        return addHandler(handler, SummernotePasteEvent.getType());
328    }
329
330    @Override
331    public HandlerRegistration addSummernoteImageUploadHandler(final SummernoteImageUploadHandler handler) {
332        hasUploadImageHandler = true;
333        return addHandler(handler, SummernoteImageUploadEvent.getType());
334    }
335
336    @Override
337    public HandlerRegistration addSummernoteChangeHandler(final SummernoteChangeHandler handler) {
338        hasChangeHandler = true;
339        return addHandler(handler, SummernoteChangeEvent.getType());
340    }
341
342    /**
343     * Gets the HTML code generated from the editor
344     *
345     * @return generated code
346     */
347    public String getCode() {
348        if (isAttached()) {
349            return getCode(getElement());
350        }
351        return getElement().getInnerHTML();
352    }
353
354    /**
355     * Sets the given HTML code to the editor.
356     *
357     * @param code
358     */
359    public void setCode(final String code) {
360        if (isAttached()) {
361            setCode(getElement(), code);
362        } else {
363            getElement().setInnerHTML(code);
364        }
365    }
366
367    /**
368     * Returns <code>true</code> if the content is empty.<br>
369     * <br>
370     * Editing area needs <code>&lt;p&gt;&lt;br&gt;&lt;/p&gt;</code></code>
371     * for focus, even if contents is empty. So summernote supports this method
372     * for helping to check contents is empty.
373     *
374     * @return <code>true</code> if the editor is empty
375     */
376    public boolean isEmpty() {
377        if (isAttached()) {
378            return isEmpty(getElement());
379        }
380        return getElement().getInnerHTML().isEmpty();
381    }
382
383    /**
384     * Removes all contents and restores the editable instance
385     * to an <code>_emptyPara_</code>: &lt;p&gt;&lt;br&gt;&lt;/p&gt;
386     */
387    @Override
388    public void clear() {
389        if (isAttached()) {
390            command(getElement(), "empty");
391        } else {
392            super.clear();
393            getElement().removeAllChildren();
394        }
395    }
396
397    @Override
398    public void setEnabled(boolean enabled) {
399        this.enabled = enabled;
400        if (isAttached()) {
401            command(getElement(), enabled ? "enable" : "disable");
402        }
403    }
404
405    @Override
406    public boolean isEnabled() {
407        return enabled;
408    }
409
410    /**
411     * Clear editor contents and remove all stored history.
412     */
413    public void reset() {
414        if (isAttached()) {
415            command(getElement(), "reset");
416        } else {
417            clear();
418        }
419    }
420
421    /**
422     * Call this when updating options to ensure everything is up to date
423     */
424    public void reconfigure() {
425        destroy(getElement());
426        initialize();
427    }
428
429    private void initialize() {
430        // Inject the language JS is necessary
431        if (language.getJs() != null) {
432            ScriptInjector.fromString(language.getJs().getText())
433                .setWindow(ScriptInjector.TOP_WINDOW).inject();
434        }
435        // Initialize
436        initialize(getElement(), options);
437        // Enable/Disable editor
438        setEnabled(enabled);
439    }
440
441    @Override
442    protected void onLoad() {
443        super.onLoad();
444
445        // Initialize
446        initialize();
447    }
448
449    @Override
450    protected void onUnload() {
451        super.onUnload();
452
453        // Destroy
454        destroy(getElement());
455    }
456
457    /**
458     * Inserts the given images to the editor.<br>
459     * <br>
460     * This method should be used only when you customize
461     * the image upload handler.
462     *
463     * @param images
464     */
465    public void insertImages(JsArray<ImageFile> images) {
466        insertImages(getElement(), images);
467    }
468
469    private native void initialize(Element e, SummernoteOptions options) /*-{
470        var target = this;
471        options.callbacks = {};
472        if (this.@org.gwtbootstrap3.extras.summernote.client.ui.base.SummernoteBase::hasInitHandler) {
473            options.callbacks.onInit = function() {
474                @org.gwtbootstrap3.extras.summernote.client.event.SummernoteInitEvent::fire(Lorg/gwtbootstrap3/extras/summernote/client/event/HasSummernoteInitHandlers;)(target);
475            };
476        }
477        if (this.@org.gwtbootstrap3.extras.summernote.client.ui.base.SummernoteBase::hasEnterHandler) {
478            options.callbacks.onEnter = function () {
479                @org.gwtbootstrap3.extras.summernote.client.event.SummernoteEnterEvent::fire(Lorg/gwtbootstrap3/extras/summernote/client/event/HasSummernoteEnterHandlers;)(target);
480            };
481        }
482        if (this.@org.gwtbootstrap3.extras.summernote.client.ui.base.SummernoteBase::hasFocusHandler) {
483            options.callbacks.onFocus = function() {
484                @org.gwtbootstrap3.extras.summernote.client.event.SummernoteFocusEvent::fire(Lorg/gwtbootstrap3/extras/summernote/client/event/HasSummernoteFocusHandlers;)(target);
485            };
486        }
487        if (this.@org.gwtbootstrap3.extras.summernote.client.ui.base.SummernoteBase::hasBlurHandler) {
488            options.callbacks.onBlur = function() {
489                @org.gwtbootstrap3.extras.summernote.client.event.SummernoteBlurEvent::fire(Lorg/gwtbootstrap3/extras/summernote/client/event/HasSummernoteBlurHandlers;)(target);
490            };
491        }
492        if (this.@org.gwtbootstrap3.extras.summernote.client.ui.base.SummernoteBase::hasKeyUpHandler) {
493            options.callbacks.onKeyup = function(e) {
494                @org.gwtbootstrap3.extras.summernote.client.event.SummernoteKeyUpEvent::fire(Lorg/gwtbootstrap3/extras/summernote/client/event/HasSummernoteKeyUpHandlers;Lcom/google/gwt/dom/client/NativeEvent;)(target, e.originalEvent);
495            };
496        }
497        if (this.@org.gwtbootstrap3.extras.summernote.client.ui.base.SummernoteBase::hasKeyDownHandler) {
498            options.callbacks.onKeydown = function(e) {
499                @org.gwtbootstrap3.extras.summernote.client.event.SummernoteKeyDownEvent::fire(Lorg/gwtbootstrap3/extras/summernote/client/event/HasSummernoteKeyDownHandlers;Lcom/google/gwt/dom/client/NativeEvent;)(target, e.originalEvent);
500            };
501        }
502        if (this.@org.gwtbootstrap3.extras.summernote.client.ui.base.SummernoteBase::hasUploadImageHandler) {
503            options.callbacks.onImageUpload = function(files) {
504                @org.gwtbootstrap3.extras.summernote.client.event.SummernoteImageUploadEvent::fire(Lorg/gwtbootstrap3/extras/summernote/client/event/HasSummernoteImageUploadHandlers;Lcom/google/gwt/core/client/JsArray;)(target, files);
505            };
506        }
507        if (this.@org.gwtbootstrap3.extras.summernote.client.ui.base.SummernoteBase::hasPasteHandler) {
508            options.callbacks.onPaste = function() {
509                @org.gwtbootstrap3.extras.summernote.client.event.SummernotePasteEvent::fire(Lorg/gwtbootstrap3/extras/summernote/client/event/HasSummernotePasteHandlers;)(target);
510            };
511        }
512        if (this.@org.gwtbootstrap3.extras.summernote.client.ui.base.SummernoteBase::hasChangeHandler) {
513            options.callbacks.onChange = function() {
514                @org.gwtbootstrap3.extras.summernote.client.event.SummernoteChangeEvent::fire(Lorg/gwtbootstrap3/extras/summernote/client/event/HasSummernoteChangeHandlers;)(target);
515            };
516        }
517        $wnd.jQuery(e).summernote(options);
518    }-*/;
519
520    private native void destroy(Element e) /*-{
521        $wnd.jQuery(e).summernote('destroy');
522        $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers::SUMMERNOTE_INIT_EVENT);
523        $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers::SUMMERNOTE_ENTER_EVENT);
524        $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers::SUMMERNOTE_FOCUS_EVENT);
525        $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers::SUMMERNOTE_BLUR_EVENT);
526        $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers::SUMMERNOTE_KEYUP_EVENT);
527        $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers::SUMMERNOTE_KEYDOWN_EVENT);
528        $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers::SUMMERNOTE_PASTE_EVENT);
529        $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers::SUMMERNOTE_IMAGE_UPLOAD_EVENT);
530        $wnd.jQuery(e).off(@org.gwtbootstrap3.extras.summernote.client.event.HasAllSummernoteHandlers::SUMMERNOTE_CHANGE_EVENT);
531    }-*/;
532
533    private native void setCode(Element e, String code) /*-{
534        $wnd.jQuery(e).summernote('code', code);
535    }-*/;
536
537    private native String getCode(Element e)/*-{
538        return $wnd.jQuery(e).summernote('code');
539    }-*/;
540
541    private native boolean isEmpty(Element e)/*-{
542        return $wnd.jQuery(e).summernote('isEmpty');
543    }-*/;
544
545    private native void command(Element e, String command)/*-{
546        $wnd.jQuery(e).summernote(command);
547    }-*/;
548
549    private native void insertImages(Element e, JsArray<ImageFile> images) /*-{
550        $wnd.jQuery(e).summernote('insertImages', images);
551    }-*/;
552}