001/* 002 * This library is part of OpenCms - 003 * the Open Source Content Management System 004 * 005 * Copyright (C) Alkacon Software (http://www.alkacon.com) 006 * 007 * This library is free software; you can redistribute it and/or 008 * modify it under the terms of the GNU Lesser General Public 009 * License as published by the Free Software Foundation; either 010 * version 2.1 of the License, or (at your option) any later version. 011 * 012 * This library is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * For further information about Alkacon Software, please see the 018 * company website: http://www.alkacon.com 019 * 020 * For further information about OpenCms, please see the 021 * project website: http://www.opencms.org 022 * 023 * You should have received a copy of the GNU Lesser General Public 024 * License along with this library; if not, write to the Free Software 025 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 026 */ 027 028package org.opencms.acacia.client.widgets; 029 030import org.opencms.acacia.client.CmsEditorBase; 031import org.opencms.acacia.client.css.I_CmsLayoutBundle; 032import org.opencms.gwt.client.CmsCoreProvider; 033import org.opencms.gwt.client.util.CmsDomUtil; 034import org.opencms.gwt.client.util.CmsDomUtil.Style; 035 036import com.google.gwt.core.client.JavaScriptObject; 037import com.google.gwt.core.client.Scheduler; 038import com.google.gwt.core.client.Scheduler.ScheduledCommand; 039import com.google.gwt.dom.client.Document; 040import com.google.gwt.dom.client.Element; 041import com.google.gwt.dom.client.NativeEvent; 042import com.google.gwt.event.dom.client.ClickEvent; 043import com.google.gwt.event.dom.client.ClickHandler; 044import com.google.gwt.event.dom.client.DomEvent; 045import com.google.gwt.event.logical.shared.HasResizeHandlers; 046import com.google.gwt.event.logical.shared.ResizeEvent; 047import com.google.gwt.event.logical.shared.ResizeHandler; 048import com.google.gwt.event.logical.shared.ValueChangeEvent; 049import com.google.gwt.event.logical.shared.ValueChangeHandler; 050import com.google.gwt.event.shared.HandlerRegistration; 051import com.google.gwt.user.client.DOM; 052 053/** 054 * This class is used to start TinyMCE for editing the content of an element.<p> 055 * 056 * After constructing the instance, the actual editor is opened using the init() method, and destroyed with the close() 057 * method. While the editor is opened, the edited contents can be accessed using the methods of the HasValue interface. 058 */ 059public final class CmsTinyMCEWidget extends A_CmsEditWidget implements HasResizeHandlers, I_CmsHasDisplayDirection { 060 061 /** Use as option to disallow any HTML or formatting the content. */ 062 public static final String NO_HTML_EDIT = "no_html_edit"; 063 064 /** The disabled style element id. */ 065 private static final String DISABLED_STYLE_ID = "editorDisabledStyle"; 066 067 /** The minimum editor height. */ 068 private static final int MIN_EDITOR_HEIGHT = 70; 069 070 /** A flag which indicates whether the editor is currently active. */ 071 protected boolean m_active; 072 073 /** The current content. */ 074 protected String m_currentContent; 075 076 /** The TinyMCE editor instance. */ 077 protected JavaScriptObject m_editor; 078 079 /** The DOM ID of the editable element. */ 080 protected String m_id; 081 082 /** The original HTML content of the editable element. */ 083 protected String m_originalContent; 084 085 /** The maximal width of the widget. */ 086 protected int m_width; 087 088 /** The editor height to set. */ 089 int m_editorHeight; 090 091 /** Flag indicating the editor has been initialized. */ 092 boolean m_initialized; 093 094 /** The element to store the widget content in. */ 095 private Element m_contentElement; 096 097 /** Indicates the value has been set from external, not from within the widget. */ 098 private boolean m_externalValueChange; 099 100 /** Indicating if the widget has been attached yet. */ 101 private boolean m_hasBeenAttached; 102 103 /** Flag indicating if in line editing is used. */ 104 private boolean m_inline; 105 106 /** The editor options. */ 107 private Object m_options; 108 109 /** 110 * Creates a new instance for the given element. Use this constructor for in line editing.<p> 111 * 112 * @param element the DOM element 113 * @param options the tinyMCE editor options to extend the default settings 114 */ 115 public CmsTinyMCEWidget(Element element, Object options) { 116 117 this(element, options, true); 118 } 119 120 /** 121 * Creates a new instance with the given options. Use this constructor for form based editing.<p> 122 * 123 * @param options the tinyMCE editor options to extend the default settings 124 */ 125 public CmsTinyMCEWidget(Object options) { 126 127 this(DOM.createDiv(), options, false); 128 } 129 130 /** 131 * Constructor.<p> 132 * 133 * @param element the DOM element 134 * @param options the tinyMCE editor options to extend the default settings 135 * @param inline flag indicating if in line editing is used 136 */ 137 private CmsTinyMCEWidget(Element element, Object options, boolean inline) { 138 139 super(element); 140 m_originalContent = ""; 141 m_options = options; 142 m_active = true; 143 m_inline = inline; 144 if (m_inline) { 145 m_contentElement = element; 146 } else { 147 // using a child DIV as content element 148 m_contentElement = getElement().appendChild(DOM.createDiv()); 149 } 150 } 151 152 /** 153 * Returns the disabled text color.<p> 154 * 155 * @return the disabled text color 156 */ 157 private static String getDisabledTextColor() { 158 159 return I_CmsLayoutBundle.INSTANCE.constants().css().textColorDisabled(); 160 } 161 162 /** 163 * @see com.google.gwt.event.logical.shared.HasResizeHandlers#addResizeHandler(com.google.gwt.event.logical.shared.ResizeHandler) 164 */ 165 public HandlerRegistration addResizeHandler(ResizeHandler handler) { 166 167 return addHandler(handler, ResizeEvent.getType()); 168 } 169 170 /** 171 * @see org.opencms.acacia.client.widgets.A_CmsEditWidget#addValueChangeHandler(com.google.gwt.event.logical.shared.ValueChangeHandler) 172 */ 173 @Override 174 public HandlerRegistration addValueChangeHandler(ValueChangeHandler<String> handler) { 175 176 return addHandler(handler, ValueChangeEvent.getType()); 177 } 178 179 /** 180 * @see org.opencms.acacia.client.widgets.I_CmsHasDisplayDirection#getDisplayingDirection() 181 */ 182 public Direction getDisplayingDirection() { 183 184 return Direction.above; 185 } 186 187 /** 188 * Gets the main editable element.<p> 189 * 190 * @return the editable element 191 */ 192 public Element getMainElement() { 193 194 return m_contentElement; 195 } 196 197 /** 198 * @see com.google.gwt.user.client.ui.HasValue#getValue() 199 */ 200 @Override 201 public String getValue() { 202 203 if (m_editor != null) { 204 return getContent().trim(); 205 } 206 return m_originalContent.trim(); 207 } 208 209 /** 210 * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#isActive() 211 */ 212 public boolean isActive() { 213 214 return m_active; 215 } 216 217 /** 218 * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#setActive(boolean) 219 */ 220 public void setActive(boolean active) { 221 222 if (m_active == active) { 223 return; 224 } 225 m_active = active; 226 if (m_editor != null) { 227 if (m_active) { 228 getElement().removeClassName(I_CmsLayoutBundle.INSTANCE.form().inActive()); 229 removeEditorDisabledStyle(); 230 fireValueChange(true); 231 } else { 232 getElement().addClassName(I_CmsLayoutBundle.INSTANCE.form().inActive()); 233 setEditorDisabledStyle(); 234 } 235 } 236 } 237 238 /** 239 * @see org.opencms.acacia.client.widgets.I_CmsEditWidget#setName(java.lang.String) 240 */ 241 public void setName(String name) { 242 243 // no input field so nothing to do 244 245 } 246 247 /** 248 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object) 249 */ 250 public void setValue(String value) { 251 252 setValue(value, false); 253 } 254 255 /** 256 * @see com.google.gwt.user.client.ui.HasValue#setValue(java.lang.Object, boolean) 257 */ 258 public void setValue(String value, boolean fireEvents) { 259 260 if (value != null) { 261 value = value.trim(); 262 } 263 setPreviousValue(value); 264 if (m_editor == null) { 265 // editor has not been initialized yet 266 m_originalContent = value; 267 } else { 268 m_externalValueChange = true; 269 setContent(value); 270 } 271 if (fireEvents) { 272 fireValueChange(true); 273 } 274 } 275 276 /** 277 * Checks whether the necessary Javascript libraries are available by accessing them. 278 */ 279 protected native void checkLibraries() /*-{ 280 // fail early if tinymce is not available 281 var w = $wnd; 282 var init = w.tinyMCE.init; 283 }-*/; 284 285 /** 286 * Gives an element an id if it doesn't already have an id, and then returns the element's id.<p> 287 * 288 * @param element the element for which we want to add the id 289 * 290 * @return the id 291 */ 292 protected String ensureId(Element element) { 293 294 String id = element.getId(); 295 if ((id == null) || "".equals(id)) { 296 id = Document.get().createUniqueId(); 297 element.setId(id); 298 } 299 return id; 300 } 301 302 /** 303 * Returns the editor parent element.<p> 304 * 305 * @return the editor parent element 306 */ 307 protected Element getEditorParentElement() { 308 309 String parentId = m_id + "_parent"; 310 Element result = getElementById(parentId); 311 return result; 312 } 313 314 /** 315 * Gets an element by its id.<p> 316 * 317 * @param id the id 318 * @return the element with the given id 319 */ 320 protected native Element getElementById(String id) /*-{ 321 return $doc.getElementById(id); 322 }-*/; 323 324 /** 325 * Gets the toolbar element.<p> 326 * 327 * @return the toolbar element 328 */ 329 protected Element getToolbarElement() { 330 331 String toolbarId = m_id + "_external"; 332 Element result = getElementById(toolbarId); 333 return result; 334 } 335 336 /** 337 * Returns if the widget is used in inline mode.<p> 338 * 339 * @return <code>true</code> if the widget is used in inline mode 340 */ 341 protected boolean isInline() { 342 343 return m_inline; 344 } 345 346 /** 347 * @see com.google.gwt.user.client.ui.FocusWidget#onAttach() 348 */ 349 @Override 350 protected void onAttach() { 351 352 super.onAttach(); 353 if (!m_hasBeenAttached) { 354 m_hasBeenAttached = true; 355 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 356 357 public void execute() { 358 359 if (isAttached()) { 360 m_editorHeight = calculateEditorHeight(); 361 m_id = ensureId(getMainElement()); 362 m_width = calculateWidth(); 363 checkLibraries(); 364 if (isInline()) { 365 if (CmsDomUtil.getCurrentStyleInt(getElement(), Style.zIndex) < 1) { 366 getElement().getStyle().setZIndex(1); 367 } 368 addDomHandler(new ClickHandler() { 369 370 public void onClick(ClickEvent event) { 371 372 // prevent event propagation while editing inline, to avoid following links in ancestor nodes 373 event.stopPropagation(); 374 event.preventDefault(); 375 } 376 }, ClickEvent.getType()); 377 } 378 initNative(CmsCoreProvider.get().getWpLanguage()); 379 if (!m_active) { 380 getElement().addClassName(I_CmsLayoutBundle.INSTANCE.form().inActive()); 381 } 382 } else { 383 resetAtachedFlag(); 384 } 385 } 386 }); 387 } 388 } 389 390 /** 391 * @see com.google.gwt.user.client.ui.Widget#onDetach() 392 */ 393 @Override 394 protected void onDetach() { 395 396 try { 397 detachEditor(); 398 } catch (Throwable t) { 399 // may happen in rare cases, can be ignored 400 } 401 super.onDetach(); 402 } 403 404 /** 405 * Propagates the a focus event.<p> 406 */ 407 protected void propagateFocusEvent() { 408 409 if (m_initialized) { 410 NativeEvent nativeEvent = Document.get().createFocusEvent(); 411 DomEvent.fireNativeEvent(nativeEvent, this, getElement()); 412 } 413 } 414 415 /** 416 * Propagates a native mouse event.<p> 417 * 418 * @param eventType the mouse event type 419 * @param eventSource the event source 420 */ 421 protected native void propagateMouseEvent(String eventType, Element eventSource) /*-{ 422 var doc = $wnd.document; 423 var event; 424 if (doc.createEvent) { 425 event = doc.createEvent("MouseEvents"); 426 event.initEvent(eventType, true, true); 427 eventSource.dispatchEvent(event); 428 } else { 429 eventSource.fireEvent("on" + eventType); 430 } 431 }-*/; 432 433 /** 434 * Removes the editor instance.<p> 435 */ 436 protected native void removeEditor() /*-{ 437 var editor = this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_editor; 438 editor.remove(); 439 }-*/; 440 441 /** 442 * Sets the main content of the element which is inline editable.<p> 443 * 444 * @param html the new content html 445 */ 446 protected native void setMainElementContent(String html) /*-{ 447 var instance = this; 448 var elementId = instance.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_id; 449 var mainElement = $wnd.document.getElementById(elementId); 450 mainElement.innerHTML = html; 451 }-*/; 452 453 /** 454 * Checks if the main element contains the current text selection.<p> 455 * 456 * @return <code>true</code> if the main element contains the current text selection 457 */ 458 protected boolean shouldReceiveFocus() { 459 460 return m_inline && CmsEditorBase.shouldFocusOnInlineEdit(getElement()); 461 } 462 463 /** 464 * Calculates the needed editor height.<p> 465 * 466 * @return the calculated editor height 467 */ 468 int calculateEditorHeight() { 469 470 int result = getElement().getOffsetHeight() + 30; 471 return result > MIN_EDITOR_HEIGHT ? result : MIN_EDITOR_HEIGHT; 472 } 473 474 /** 475 * Calculates the widget width.<p> 476 * 477 * @return the widget width 478 */ 479 int calculateWidth() { 480 481 int result; 482 if (m_inline && CmsDomUtil.getCurrentStyle(getElement(), Style.display).equals("inline")) { 483 com.google.gwt.dom.client.Element parentBlock = getElement().getParentElement(); 484 while (CmsDomUtil.getCurrentStyle(parentBlock, Style.display).equals("inline")) { 485 parentBlock = parentBlock.getParentElement(); 486 } 487 result = parentBlock.getOffsetWidth(); 488 } else { 489 result = getElement().getOffsetWidth(); 490 } 491 return result - 2; 492 } 493 494 /** 495 * Initializes the TinyMCE instance. 496 * 497 * @param locale the UI locale 498 */ 499 native void initNative(String locale) /*-{ 500 501 function merge() { 502 var result = {}, length = arguments.length; 503 for (i = 0; i < length; i++) { 504 for (key in arguments[i]) { 505 if (arguments[i].hasOwnProperty(key)) { 506 result[key] = arguments[i][key]; 507 } 508 } 509 } 510 return result; 511 } 512 513 514 var languageMap = { "it": "it_IT", "cs": "cs_CZ", "ru": "ru_RU", "zh": "zh_CN"}; 515 var translatedLanguage = languageMap[locale]; 516 if (translatedLanguage) { 517 locale = translatedLanguage; 518 } 519 520 521 522 var self = this; 523 var needsRefocus = self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::shouldReceiveFocus()(); 524 var elementId = self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_id; 525 var mainElement = $wnd.document.getElementById(elementId); 526 var editorHeight = self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_editorHeight; 527 528 var fireChange = function() { 529 self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::fireChangeFromNative()(); 530 }; 531 var options = this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_options; 532 if (options != null && options.editorHeight) { 533 editorHeight = options.editorHeight; 534 delete options.editorHeight; 535 } 536 // default options: 537 var defaults; 538 if (@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::NO_HTML_EDIT == options) { 539 // disallow any formatting 540 defaults = { 541 selector : mainElement.tagName + "#" + elementId, 542 entity_encoding : "raw", 543 mode : "exact", 544 theme : "silver", 545 plugins : "paste", 546 paste_as_text : true, 547 toolbar : "undo redo", 548 menubar : false, 549 forced_root_block : false 550 }; 551 options = null; 552 } else { 553 defaults = { 554 selector : mainElement.tagName + "#" + elementId, 555 relative_urls : false, 556 remove_script_host : false, 557 entity_encoding : "raw", 558 skin_variant : 'ocms', 559 mode : "exact", 560 theme : "silver", 561 plugins : "autolink lists pagebreak table save codemirror hr image link emoticons spellchecker insertdatetime preview media searchreplace print paste directionality noneditable visualchars nonbreaking template wordcount advlist", 562 paste_as_text : true, 563 menubar : false, 564 }; 565 } 566 if (this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_inline) { 567 self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_currentContent = mainElement.innerHTML; 568 defaults.inline = true; 569 defaults.width = this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_width; 570 } else { 571 self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_currentContent = self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_originalContent; 572 defaults.min_height = 100; 573 defaults.max_height = editorHeight; 574 defaults.width = '100%'; 575 defaults.resize = 'both'; 576 } 577 // extend the defaults with any given options 578 if (options != null) { 579 if (options.style_formats) { 580 // tinymce performs a type test for arrays wich fails in case the array was not created in the same window context 581 var formats = new $wnd.Array(); 582 for (var i = 0; i < options.style_formats.length; i++) { 583 formats[i] = options.style_formats[i]; 584 } 585 options.style_formats = formats; 586 } 587 defaults = merge(defaults, options); 588 } 589 if (this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_inline) { 590 delete defaults.content_css; 591 } else { 592 // enable autoresize 593 defaults.plugins = "autoresize " + defaults.plugins; 594 } 595 if (needsRefocus) { 596 defaults.auto_focus = elementId; 597 } 598 599 // add the setup function 600 defaults.setup = function(ed) { 601 self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_editor = ed; 602 ed.on('SetContent', fireChange); 603 ed.on('change', fireChange); 604 ed.on('KeyDown', fireChange); 605 ed 606 .on( 607 'LoadContent', 608 function() { 609 if (!self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_inline) { 610 // firing resize event on resize of the editor iframe 611 ed.dom 612 .bind( 613 ed.getWin(), 614 'resize', 615 function() { 616 self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::fireResizeEvent()(); 617 }); 618 var content = self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_originalContent; 619 if (content != null) { 620 ed.setContent(content); 621 } 622 } 623 }); 624 ed 625 .on( 626 'init', 627 function() { 628 self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::scheduleInitializationDone()(); 629 }); 630 631 if (!self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_inline) { 632 633 ed 634 .on( 635 'Click', 636 function(event) { 637 self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::propagateFocusEvent()(); 638 }); 639 ed 640 .on( 641 'activate', 642 function(event) { 643 self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::propagateFocusEvent()(); 644 }); 645 ed 646 .on( 647 'focus', 648 function(event) { 649 self.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::propagateFocusEvent()(); 650 }); 651 } 652 }; 653 654 // initialize tinyMCE 655 defaults.language = locale; 656 $wnd.tinymce.init(defaults); 657 }-*/; 658 659 /** 660 * Removes the disabled editor styling.<p> 661 */ 662 native void removeEditorDisabledStyle()/*-{ 663 var ed = this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_editor; 664 var styleEl = ed 665 .getDoc() 666 .getElementById( 667 @org.opencms.acacia.client.widgets.CmsTinyMCEWidget::DISABLED_STYLE_ID); 668 if (styleEl != null) { 669 ed.getDoc().head.removeChild(styleEl); 670 } 671 }-*/; 672 673 /** 674 * Resets the attached flag.<p> 675 */ 676 void resetAtachedFlag() { 677 678 m_hasBeenAttached = false; 679 } 680 681 /** 682 * Scheduling to set the initialized flag.<p> 683 */ 684 void scheduleInitializationDone() { 685 686 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 687 688 public void execute() { 689 690 m_initialized = true; 691 if (m_active) { 692 removeEditorDisabledStyle(); 693 } else { 694 setEditorDisabledStyle(); 695 } 696 } 697 }); 698 } 699 700 /** 701 * Sets the editor disabled styling.<p> 702 */ 703 native void setEditorDisabledStyle()/*-{ 704 var ed = this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_editor; 705 if (ed 706 .getDoc() 707 .getElementById( 708 @org.opencms.acacia.client.widgets.CmsTinyMCEWidget::DISABLED_STYLE_ID) == null) { 709 var styleEl = ed.getDoc().createElement("style"); 710 styleEl 711 .setAttribute("id", 712 @org.opencms.acacia.client.widgets.CmsTinyMCEWidget::DISABLED_STYLE_ID); 713 var styleText = ed 714 .getDoc() 715 .createTextNode( 716 "body, body *{ color: " 717 + @org.opencms.acacia.client.widgets.CmsTinyMCEWidget::getDisabledTextColor()() 718 + " !important;}"); 719 styleEl.appendChild(styleText); 720 ed.getDoc().head.appendChild(styleEl); 721 } 722 }-*/; 723 724 /** 725 * Removes the editor.<p> 726 */ 727 private native void detachEditor() /*-{ 728 729 var ed = this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_editor; 730 if (ed != null) { 731 ed.remove(); 732 } 733 // in IE somehow the whole document will be selected, empty the selection to resolve that 734 if ($wnd.document.selection != null) { 735 $wnd.document.selection.empty(); 736 } 737 }-*/; 738 739 /** 740 * Used to fire the value changed event from native code.<p> 741 */ 742 private void fireChangeFromNative() { 743 744 // skip firing the change event, if the external flag is set 745 if (m_initialized && !m_externalValueChange && m_active) { 746 Scheduler.get().scheduleDeferred(new ScheduledCommand() { 747 748 public void execute() { 749 750 try { 751 fireValueChange(false); 752 } catch (Throwable t) { 753 // this may happen when returning from full screen mode, nothing to be done 754 } 755 } 756 }); 757 } 758 // reset the external flag 759 m_externalValueChange = false; 760 } 761 762 /** 763 * Fires the resize event.<p> 764 */ 765 private void fireResizeEvent() { 766 767 ResizeEvent.fire(this, getOffsetWidth(), getOffsetHeight()); 768 } 769 770 /** 771 * Returns the editor content.<p> 772 * 773 * @return the editor content 774 */ 775 private native String getContent() /*-{ 776 var editor = this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_editor; 777 return editor.getContent(); 778 }-*/; 779 780 /** 781 * Sets the content of the TinyMCE editor.<p> 782 * 783 * @param newContent the new content 784 */ 785 private native void setContent(String newContent) /*-{ 786 var editor = this.@org.opencms.acacia.client.widgets.CmsTinyMCEWidget::m_editor; 787 editor.setContent(newContent); 788 }-*/; 789 790}