1 // Copyright (C) 2010-2015 Yozons, Inc. 2 // CKEditor for Vaadin - Widget linkage for using CKEditor within a Vaadin application. 3 // 4 // This software is released under the Apache License 2.0 <http://www.apache.org/licenses/LICENSE-2.0.html> 5 // 6 // This software was originally based on the Vaadin incubator component TinyMCEEditor written by Matti Tahvonen. 7 // 8 package org.vaadin.openesignforms.ckeditor.widgetset.client.ui; 9 10 import java.util.ArrayList; 11 import java.util.List; 12 13 import com.google.gwt.core.client.GWT; 14 import com.google.gwt.core.client.JavaScriptObject; 15 import com.google.gwt.core.client.Scheduler; 16 import com.google.gwt.core.client.Scheduler.RepeatingCommand; 17 import com.google.gwt.core.client.Scheduler.ScheduledCommand; 18 import com.google.gwt.dom.client.Document; 19 import com.google.gwt.dom.client.ScriptElement; 20 21 /** 22 * GWT wrapper for CKEDITOR for use by our Vaadin-based CKEditorService. 23 */ 24 public class CKEditorService { 25 26 private static boolean libraryLoadInited = false; 27 private static boolean libraryLoaded = false; 28 private static List<ScheduledCommand> afterLoadedStack = new ArrayList<ScheduledCommand>(); 29 30 public static synchronized void loadLibrary(ScheduledCommand afterLoad) { 31 if (! libraryLoadInited) { 32 libraryLoadInited = true; 33 String url = GWT.getModuleBaseURL() + "ckeditor/ckeditor.js"; 34 ScriptElement se = Document.get().createScriptElement(); 35 se.setSrc(url); 36 se.setType("text/javascript"); 37 Document.get().getElementsByTagName("head").getItem(0).appendChild(se); 38 Scheduler.get().scheduleFixedDelay(new RepeatingCommand() { 39 @Override 40 public boolean execute() { 41 if (libraryReady()) { 42 reduceBlurDelay(); 43 for (ScheduledCommand sc: afterLoadedStack) { 44 sc.execute(); 45 } 46 libraryLoaded = true; 47 return false; 48 } 49 return true; 50 } 51 }, 50); 52 } 53 if (libraryLoaded) { 54 afterLoad.execute(); 55 } else { 56 afterLoadedStack.add(afterLoad); 57 } 58 } 59 60 public static native boolean libraryReady() 61 /*-{ 62 if($wnd.CKEDITOR) { 63 return true; 64 } 65 return false; 66 }-*/; 67 68 /** 69 * Use this method to load editor to given identifier. 70 * 71 * @param id the string DOM <div> 'id' attribute value for the element you want to replace with CKEditor 72 * @param listener the CKEditorService.CKEditorListener will get notified when the editor instance is ready, changed, etc. 73 * @param jsInPageConfig the String possible custom "in page" configuration; note that this must be an expected JSON for the CKEDITOR in page config. 74 * sent "as is" without any real syntax or security testing, so be sure you know it's valid and not malicious, 75 * such as: <code>{toolbar : 'Basic', language : 'en'}</code> 76 */ 77 public static native JavaScriptObject loadEditor(String id, CKEditorService.CKEditorListener listener, String jsInPageConfig, int compWidth, int compHeight) 78 /*-{ 79 // Build our inPageConfig object based on the JSON jsInPageConfig sent to us. 80 var inPageConfig = @org.vaadin.openesignforms.ckeditor.widgetset.client.ui.CKEditorService::convertJavaScriptStringToObject(Ljava/lang/String;)(jsInPageConfig); 81 82 var myEditor; 83 84 if (inPageConfig == null) { 85 inPageConfig = new Object; 86 inPageConfig.width = compWidth; 87 inPageConfig.height = compHeight; 88 } else { 89 if (!inPageConfig.width) inPageConfig.width = compWidth; 90 if (!inPageConfig.height) inPageConfig.height = compHeight; 91 } 92 93 myEditor = $wnd.CKEDITOR.appendTo( id, inPageConfig ); 94 95 // The 'listener' passed to us is used as 'listenerData' for the callback. 96 myEditor.on( 'instanceReady', function( ev ) { 97 ev.listenerData.@org.vaadin.openesignforms.ckeditor.widgetset.client.ui.CKEditorService.CKEditorListener::onInstanceReady()(); 98 }, null, listener); 99 100 return myEditor; 101 102 }-*/; 103 104 public native static String version() 105 /*-{ 106 return $wnd.CKEDITOR.version; 107 }-*/; 108 109 // This is a hack attempt to resolve issues with Vaadin when the CKEditorTextField widget is set with BLUR and FOCUS listeners. 110 // In particular, the Safari browser could not deal well with PASTE, right clicking in a table cell, etc. 111 // because those operations resulted in BLUR then FOCUS events in rapid succession, causing the UI to update. 112 // But the 200 value is too long and we find that often the button acts faster than the BLUR can fire from CKEditor 113 // so Vaadin doesn't get the latest contents. 114 // Even though CKEditor 4.2 introduced a change event, it doesn't appear to fire if you stay in SOURCE mode, which many people do use. 115 public native static void reduceBlurDelay() 116 /*-{ 117 $wnd.CKEDITOR.focusManager._.blurDelay = 20; // the default is 200 even if the documentation says it's only 100 118 }-*/; 119 120 /** 121 * Returns a javascript CKEDITOR.editor instance for given id. 122 * 123 * @param id the String id of the editor instance 124 * @return the overlay for CKEDITOR.editor or null if not yet initialized 125 */ 126 public native static CKEditor get(String id) 127 /*-{ 128 return $wnd.CKEDITOR.instances[ id ]; 129 }-*/; 130 131 // TODO: Never tested yet 132 public native static void addStylesSet(String name, String jsStyles) 133 /*-{ 134 var styles = @org.vaadin.openesignforms.ckeditor.widgetset.client.ui.CKEditorService::convertJavaScriptStringToObject(Ljava/lang/String;)(jsStyles); 135 $wnd.CKEDITOR.addStylesSet(name,styles); 136 }-*/; 137 138 // TODO: Never tested yet 139 public native static void addTemplates(String name, String jsDefinition) 140 /*-{ 141 var definition = @org.vaadin.openesignforms.ckeditor.widgetset.client.ui.CKEditorService::convertJavaScriptStringToObject(Ljava/lang/String;)(jsDefinition); 142 $wnd.CKEDITOR.addTemplates(name,definition); 143 }-*/; 144 145 public native static JavaScriptObject convertJavaScriptStringToObject(String jsString) 146 /*-{ 147 try { 148 return eval('('+jsString+')'); 149 } catch (e) { 150 alert('convertJavaScriptStringToObject() INVALID JAVASCRIPT: ' + jsString); 151 return {}; 152 } 153 }-*/; 154 155 156 /** 157 * An interface for the VCKEditorTextField to get events from the CKEditor. 158 */ 159 public interface CKEditorListener { 160 public void onInstanceReady(); 161 public void onChange(); 162 public void onSelectionChange(); 163 public void onModeChange(String mode); 164 public void onDataReady(); 165 public void onBlur(); 166 public void onFocus(); 167 public void onSave(); 168 } 169 170 }