001/* 002 * #%L 003 * GwtMaterial 004 * %% 005 * Copyright (C) 2015 - 2017 GwtMaterialDesign 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package gwt.material.design.addins.client.richeditor; 021 022import com.google.gwt.dom.client.Document; 023import com.google.gwt.dom.client.Element; 024import com.google.gwt.event.dom.client.BlurEvent; 025import com.google.gwt.event.dom.client.FocusEvent; 026import com.google.gwt.event.dom.client.KeyDownEvent; 027import com.google.gwt.event.dom.client.KeyUpEvent; 028import com.google.gwt.event.logical.shared.HasValueChangeHandlers; 029import com.google.gwt.event.logical.shared.ValueChangeEvent; 030import com.google.gwt.event.shared.HandlerRegistration; 031import com.google.gwt.safehtml.shared.SafeHtmlUtils; 032import com.google.gwt.user.client.ui.HasHTML; 033import gwt.material.design.addins.client.MaterialAddins; 034import gwt.material.design.addins.client.base.constants.AddinsCssName; 035import gwt.material.design.addins.client.richeditor.base.HasPasteHandlers; 036import gwt.material.design.addins.client.richeditor.base.ToolBarManager; 037import gwt.material.design.addins.client.richeditor.base.constants.RichEditorEvents; 038import gwt.material.design.addins.client.richeditor.base.constants.ToolbarButton; 039import gwt.material.design.addins.client.richeditor.events.PasteEvent; 040import gwt.material.design.addins.client.richeditor.js.JsRichEditor; 041import gwt.material.design.addins.client.richeditor.js.JsRichEditorOptions; 042import gwt.material.design.client.MaterialDesignBase; 043import gwt.material.design.client.base.AbstractValueWidget; 044import gwt.material.design.client.base.HasPlaceholder; 045import gwt.material.design.client.base.JsLoader; 046import gwt.material.design.client.ui.MaterialModal; 047import gwt.material.design.client.ui.MaterialModalContent; 048import gwt.material.design.jquery.client.api.JQueryElement; 049 050import static gwt.material.design.addins.client.richeditor.js.JsRichEditor.$; 051 052//@formatter:off 053 054/** 055 * Provides a great Rich Editor with amazing options built with Material Design Look and Feel 056 * <p> 057 * <h3>XML Namespace Declaration</h3> 058 * <pre> 059 * {@code 060 * xmlns:ma='urn:import:gwt.material.design.addins.client' 061 * } 062 * </pre> 063 * <p> 064 * <h3>UiBinder Usage:</h3> 065 * <pre> 066 * {@code 067 * <ma:MaterialRichEditor placeholder="Type anything in here..."/> 068 * } 069 * </pre> 070 * 071 * @author kevzlou7979 072 * @see <a href="http://gwtmaterialdesign.github.io/gwt-material-demo/#richeditor">Material Rich Editor</a> 073 * @see <a href="https://github.com/Cerealkillerway/materialNote">1.2.1</a> 074 */ 075//@formatter:on 076public class MaterialRichEditor extends AbstractValueWidget<String> implements JsLoader, HasValueChangeHandlers<String>, HasPasteHandlers, HasPlaceholder, HasHTML { 077 078 static { 079 if (MaterialAddins.isDebug()) { 080 MaterialDesignBase.injectDebugJs(MaterialRichEditorDebugClientBundle.INSTANCE.richEditorDebugJs()); 081 MaterialDesignBase.injectCss(MaterialRichEditorDebugClientBundle.INSTANCE.richEditorDebugCss()); 082 } else { 083 MaterialDesignBase.injectJs(MaterialRichEditorClientBundle.INSTANCE.richEditorJs()); 084 MaterialDesignBase.injectCss(MaterialRichEditorClientBundle.INSTANCE.richEditorCss()); 085 } 086 } 087 088 089 private String html; 090 private ToolBarManager manager = new ToolBarManager(); 091 private boolean toggleFullScreen = true; 092 private JsRichEditorOptions options = JsRichEditorOptions.create(); 093 094 private HandlerRegistration handlerRegistration; 095 096 public MaterialRichEditor() { 097 super(Document.get().createDivElement(), AddinsCssName.EDITOR); 098 } 099 100 public MaterialRichEditor(String placeholder) { 101 this(); 102 setPlaceholder(placeholder); 103 } 104 105 public MaterialRichEditor(String placeholder, String value) { 106 this(placeholder); 107 setValue(value); 108 } 109 110 @Override 111 protected void onLoad() { 112 super.onLoad(); 113 114 load(); 115 116 setHTML(html); 117 } 118 119 @Override 120 public void load() { 121 122 JsRichEditor jsRichEditor = $(getElement()); 123 124 options.toolbar = manager.getToolbars(); 125 options.placeholder = getPlaceholder(); 126 options.height = getHeight(); 127 128 jsRichEditor.materialnote(options); 129 130 // Events 131 jsRichEditor.on(RichEditorEvents.MATERIALNOTE_BLUR, event -> { 132 fireEvent(new BlurEvent() {}); 133 return true; 134 }); 135 jsRichEditor.on(RichEditorEvents.MATERIALNOTE_FOCUS, event -> { 136 fireEvent(new FocusEvent() {}); 137 return true; 138 }); 139 jsRichEditor.on(RichEditorEvents.MATERIALNOTE_KEYUP, event -> { 140 fireEvent(new KeyUpEvent() {}); 141 return true; 142 }); 143 jsRichEditor.on(RichEditorEvents.MATERIALNOTE_KEYDOWN, event -> { 144 fireEvent(new KeyDownEvent() {}); 145 return true; 146 }); 147 jsRichEditor.on(RichEditorEvents.MATERIALNOTE_PASTE, event -> { 148 fireEvent(new PasteEvent() {}); 149 return true; 150 }); 151 jsRichEditor.on(RichEditorEvents.MATERIALNOTE_CHANGE, event -> { 152 ValueChangeEvent.fire(MaterialRichEditor.this, getHTMLCode(getElement())); 153 return true; 154 }); 155 156 checkContainer(); 157 } 158 159 @Override 160 protected void onUnload() { 161 super.onUnload(); 162 163 unload(); 164 } 165 166 @Override 167 public void unload() { 168 JsRichEditor jsRichEditor = $(getElement()); 169 jsRichEditor.off(RichEditorEvents.MATERIALNOTE_BLUR); 170 jsRichEditor.off(RichEditorEvents.MATERIALNOTE_FOCUS); 171 jsRichEditor.off(RichEditorEvents.MATERIALNOTE_KEYUP); 172 jsRichEditor.off(RichEditorEvents.MATERIALNOTE_KEYDOWN); 173 jsRichEditor.off(RichEditorEvents.MATERIALNOTE_PASTE); 174 jsRichEditor.off(RichEditorEvents.MATERIALNOTE_CHANGE); 175 jsRichEditor.destroy(); 176 } 177 178 @Override 179 public void reload() { 180 unload(); 181 load(); 182 } 183 184 public ToolbarButton[] getStyleOptions() { 185 return manager.getStyleOptions(); 186 } 187 188 public void setStyleOptions(ToolbarButton... styleOptions) { 189 manager.setStyleOptions(styleOptions); 190 } 191 192 public ToolbarButton[] getFontOptions() { 193 return manager.getFontOptions(); 194 } 195 196 public void setFontOptions(ToolbarButton... fontOptions) { 197 manager.setFontOptions(fontOptions); 198 } 199 200 public ToolbarButton[] getColorOptions() { 201 return manager.getColorOptions(); 202 } 203 204 public void setColorOptions(ToolbarButton... colorOptions) { 205 manager.setColorOptions(colorOptions); 206 } 207 208 public ToolbarButton[] getUndoOptions() { 209 return manager.getUndoOptions(); 210 } 211 212 public void setUndoOptions(ToolbarButton... undoOptions) { 213 manager.setUndoOptions(undoOptions); 214 } 215 216 public ToolbarButton[] getCkMediaOptions() { 217 return manager.getCkMediaOptions(); 218 } 219 220 public void setCkMediaOptions(ToolbarButton... ckMediaOptions) { 221 manager.setCkMediaOptions(ckMediaOptions); 222 } 223 224 public ToolbarButton[] getMiscOptions() { 225 return manager.getMiscOptions(); 226 } 227 228 public void setMiscOptions(ToolbarButton... miscOptions) { 229 manager.setMiscOptions(miscOptions); 230 } 231 232 public ToolbarButton[] getParaOptions() { 233 return manager.getParaOptions(); 234 } 235 236 public void setParaOptions(ToolbarButton... paraOptions) { 237 manager.setParaOptions(paraOptions); 238 } 239 240 public ToolbarButton[] getHeightOptions() { 241 return manager.getHeightOptions(); 242 } 243 244 public void setHeightOptions(ToolbarButton... heightOptions) { 245 manager.setHeightOptions(heightOptions); 246 } 247 248 protected void checkContainer() { 249 if (getParent() instanceof MaterialModal) { 250 MaterialModal modal = (MaterialModal) getParent(); 251 adjustFullScreen(modal); 252 adjustNestedModals(modal); 253 } else if (getParent() instanceof MaterialModalContent) { 254 MaterialModal modal = (MaterialModal) getParent().getParent(); 255 adjustFullScreen(modal); 256 adjustNestedModals(modal); 257 } 258 } 259 260 protected void adjustNestedModals(MaterialModal modal) { 261 registerHandler(modal.addOpenHandler(openEvent -> modal.setDepth(9999))); 262 } 263 264 protected void adjustFullScreen(MaterialModal modal) { 265 getEditor().find("div[data-event='fullscreen']").off("click").on("click", (e, param1) -> { 266 modal.setFullscreen(toggleFullScreen); 267 if (toggleFullScreen) { 268 toggleFullScreen = false; 269 } else { 270 toggleFullScreen = true; 271 } 272 return true; 273 }); 274 } 275 276 public JQueryElement getEditor() { 277 return $(getElement()).next(".note-editor"); 278 } 279 280 /** 281 * Insert custom text inside the note zone. 282 */ 283 public void insertText(String text) { 284 insertText(getElement(), text); 285 } 286 287 /** 288 * Insert custom text inside the note zone. 289 */ 290 protected void insertText(Element e, String text) { 291 $(e).materialnote("insertText", SafeHtmlUtils.fromString(text).asString()); 292 } 293 294 /** 295 * Insert custom HTML inside the note zone. 296 */ 297 public void pasteHTML(String html) { 298 pasteHTML(getElement(), html); 299 } 300 301 /** 302 * Insert custom HTML inside the note zone. 303 */ 304 protected void pasteHTML(Element e, String html) { 305 $(e).materialnote("pasteHTML", html); 306 } 307 308 /** 309 * Reset the Rich Editor component 310 */ 311 public void reset() { 312 $(getElement()).materialnote("reset"); 313 } 314 315 @Override 316 public void clear() { 317 super.clear(); 318 reset(); 319 } 320 321 public boolean isAirMode() { 322 return options.airMode; 323 } 324 325 public void setAirMode(boolean airMode) { 326 options.airMode = airMode; 327 } 328 329 /** 330 * Check if the dnd for rich editor is enabled / disabled 331 */ 332 public boolean isDisableDragAndDrop() { 333 return options.disableDragAndDrop; 334 } 335 336 /** 337 * If true, disable the ability to drag and drop items to rich editor 338 */ 339 public void setDisableDragAndDrop(boolean disableDragAndDrop) { 340 options.disableDragAndDrop = disableDragAndDrop; 341 } 342 343 @Override 344 public String getPlaceholder() { 345 return options.placeholder; 346 } 347 348 @Override 349 public void setPlaceholder(String placeholder) { 350 options.placeholder = placeholder; 351 } 352 353 public String getHeight() { 354 String height = getElement().getStyle().getHeight(); 355 if (height == null || height.isEmpty()) { 356 height = "550px"; 357 } 358 return height; 359 } 360 361 @Override 362 public String getHTML() { 363 return getHTMLCode(getElement()); 364 } 365 366 @Override 367 public void setHTML(final String html) { 368 this.html = html; 369 370 if (handlerRegistration != null) { 371 handlerRegistration.removeHandler(); 372 handlerRegistration = null; 373 } 374 375 if (!isAttached() && html != null) { 376 handlerRegistration = registerHandler(addAttachHandler(e -> setHTMLCode(getElement(), html))); 377 } else { 378 setHTMLCode(getElement(), html); 379 } 380 } 381 382 @Override 383 public String getText() { 384 return getHTML(); 385 } 386 387 @Override 388 public void setText(String text) { 389 setHTML(text); 390 } 391 392 protected String getHTMLCode(Element e) { 393 return $(e).code(); 394 } 395 396 protected void setHTMLCode(Element e, String html) { 397 $(e).code(html); 398 } 399 400 @Override 401 public String getValue() { 402 return getHTML(); 403 } 404 405 @Override 406 public void setValue(String value, boolean fireEvents) { 407 setHTML(value); 408 } 409 410 @Override 411 public HandlerRegistration addPasteHandler(final PasteEvent.PasteHandler handler) { 412 return addHandler(handler, PasteEvent.TYPE); 413 } 414}