001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.user.client.ui.impl;
017:
018: import com.google.gwt.user.client.DOM;
019: import com.google.gwt.user.client.Element;
020: import com.google.gwt.user.client.ui.RichTextArea;
021: import com.google.gwt.user.client.ui.RichTextArea.FontSize;
022: import com.google.gwt.user.client.ui.RichTextArea.Justification;
023:
024: /**
025: * Basic rich text platform implementation.
026: */
027: public abstract class RichTextAreaImplStandard extends RichTextAreaImpl
028: implements RichTextArea.BasicFormatter,
029: RichTextArea.ExtendedFormatter {
030:
031: /**
032: * Holds a cached copy of any user setHTML/setText actions until the real
033: * text area is fully initialized. Becomes <code>null</code> after init.
034: */
035: private Element beforeInitPlaceholder = DOM.createDiv();
036:
037: @Override
038: public native Element createElement() /*-{
039: return $doc.createElement('iframe');
040: }-*/;
041:
042: public void createLink(String url) {
043: execCommand("CreateLink", url);
044: }
045:
046: public String getBackColor() {
047: return queryCommandValue("BackColor");
048: }
049:
050: public String getForeColor() {
051: return queryCommandValue("ForeColor");
052: }
053:
054: @Override
055: public final String getHTML() {
056: return beforeInitPlaceholder == null ? getHTMLImpl() : DOM
057: .getInnerHTML(beforeInitPlaceholder);
058: }
059:
060: @Override
061: public final String getText() {
062: return beforeInitPlaceholder == null ? getTextImpl() : DOM
063: .getInnerText(beforeInitPlaceholder);
064: }
065:
066: @Override
067: public native void initElement() /*-{
068: // Most browsers don't like setting designMode until slightly _after_
069: // the iframe becomes attached to the DOM. Any non-zero timeout will do
070: // just fine.
071: var _this = this;
072: setTimeout(function() {
073: // Turn on design mode.
074: _this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.designMode = 'On';
075:
076: // Send notification that the iframe has reached design mode.
077: _this.@com.google.gwt.user.client.ui.impl.RichTextAreaImplStandard::onElementInitialized()();
078: }, 1);
079: }-*/;
080:
081: public void insertHorizontalRule() {
082: execCommand("InsertHorizontalRule", null);
083: }
084:
085: public void insertImage(String url) {
086: execCommand("InsertImage", url);
087: }
088:
089: public void insertOrderedList() {
090: execCommand("InsertOrderedList", null);
091: }
092:
093: public void insertUnorderedList() {
094: execCommand("InsertUnorderedList", null);
095: }
096:
097: @Override
098: public boolean isBasicEditingSupported() {
099: return true;
100: }
101:
102: public boolean isBold() {
103: return queryCommandState("Bold");
104: }
105:
106: @Override
107: public boolean isExtendedEditingSupported() {
108: return true;
109: }
110:
111: public boolean isItalic() {
112: return queryCommandState("Italic");
113: }
114:
115: public boolean isStrikethrough() {
116: return queryCommandState("Strikethrough");
117: }
118:
119: public boolean isSubscript() {
120: return queryCommandState("Subscript");
121: }
122:
123: public boolean isSuperscript() {
124: return queryCommandState("Superscript");
125: }
126:
127: public boolean isUnderlined() {
128: return queryCommandState("Underline");
129: }
130:
131: public void leftIndent() {
132: execCommand("Outdent", null);
133: }
134:
135: public void removeFormat() {
136: execCommand("RemoveFormat", null);
137: }
138:
139: public void removeLink() {
140: execCommand("Unlink", "false");
141: }
142:
143: public void rightIndent() {
144: execCommand("Indent", null);
145: }
146:
147: public void selectAll() {
148: execCommand("SelectAll", null);
149: }
150:
151: public void setBackColor(String color) {
152: execCommand("BackColor", color);
153: }
154:
155: @Override
156: public native void setFocus(boolean focused) /*-{
157: if (focused) {
158: this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.focus();
159: } else {
160: this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.blur();
161: }
162: }-*/;
163:
164: public void setFontName(String name) {
165: execCommand("FontName", name);
166: }
167:
168: public void setFontSize(FontSize fontSize) {
169: execCommand("FontSize", Integer.toString(fontSize.getNumber()));
170: }
171:
172: public void setForeColor(String color) {
173: execCommand("ForeColor", color);
174: }
175:
176: @Override
177: public final void setHTML(String html) {
178: if (beforeInitPlaceholder == null) {
179: setHTMLImpl(html);
180: } else {
181: DOM.setInnerHTML(beforeInitPlaceholder, html);
182: }
183: }
184:
185: public void setJustification(Justification justification) {
186: if (justification == Justification.CENTER) {
187: execCommand("JustifyCenter", null);
188: } else if (justification == Justification.LEFT) {
189: execCommand("JustifyLeft", null);
190: } else if (justification == Justification.RIGHT) {
191: execCommand("JustifyRight", null);
192: }
193: }
194:
195: @Override
196: public final void setText(String text) {
197: if (beforeInitPlaceholder == null) {
198: setTextImpl(text);
199: } else {
200: DOM.setInnerText(beforeInitPlaceholder, text);
201: }
202: }
203:
204: public void toggleBold() {
205: execCommand("Bold", "false");
206: }
207:
208: public void toggleItalic() {
209: execCommand("Italic", "false");
210: }
211:
212: public void toggleStrikethrough() {
213: execCommand("Strikethrough", "false");
214: }
215:
216: public void toggleSubscript() {
217: execCommand("Subscript", "false");
218: }
219:
220: public void toggleSuperscript() {
221: execCommand("Superscript", "false");
222: }
223:
224: public void toggleUnderline() {
225: execCommand("Underline", "False");
226: }
227:
228: @Override
229: public void uninitElement() {
230: // Unhook all custom event handlers when the element is detached.
231: unhookEvents();
232:
233: // Recreate the placeholder element and store the iframe's contents in it.
234: // This is necessary because some browsers will wipe the iframe's contents
235: // when it is removed from the DOM.
236: String html = getHTML();
237: beforeInitPlaceholder = DOM.createDiv();
238: DOM.setInnerHTML(beforeInitPlaceholder, html);
239: }
240:
241: protected native String getHTMLImpl() /*-{
242: return this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.innerHTML;
243: }-*/;
244:
245: protected native String getTextImpl() /*-{
246: return this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.textContent;
247: }-*/;
248:
249: @Override
250: protected native void hookEvents() /*-{
251: var elem = this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
252: var wnd = elem.contentWindow;
253:
254: elem.__gwt_handler = function(evt) {
255: if (elem.__listener) {
256: elem.__listener.@com.google.gwt.user.client.ui.RichTextArea::onBrowserEvent(Lcom/google/gwt/user/client/Event;)(evt);
257: }
258: };
259:
260: elem.__gwt_focusHandler = function(evt) {
261: if (elem.__gwt_isFocused) {
262: return;
263: }
264:
265: elem.__gwt_isFocused = true;
266: elem.__gwt_handler(evt);
267: };
268:
269: elem.__gwt_blurHandler = function(evt) {
270: if (!elem.__gwt_isFocused) {
271: return;
272: }
273:
274: elem.__gwt_isFocused = false;
275: elem.__gwt_handler(evt);
276: };
277:
278: wnd.addEventListener('keydown', elem.__gwt_handler, true);
279: wnd.addEventListener('keyup', elem.__gwt_handler, true);
280: wnd.addEventListener('keypress', elem.__gwt_handler, true);
281: wnd.addEventListener('mousedown', elem.__gwt_handler, true);
282: wnd.addEventListener('mouseup', elem.__gwt_handler, true);
283: wnd.addEventListener('mousemove', elem.__gwt_handler, true);
284: wnd.addEventListener('mouseover', elem.__gwt_handler, true);
285: wnd.addEventListener('mouseout', elem.__gwt_handler, true);
286: wnd.addEventListener('click', elem.__gwt_handler, true);
287:
288: wnd.addEventListener('focus', elem.__gwt_focusHandler, true);
289: wnd.addEventListener('blur', elem.__gwt_blurHandler, true);
290: }-*/;
291:
292: @Override
293: protected void onElementInitialized() {
294: super .onElementInitialized();
295:
296: // When the iframe is ready, ensure cached content is set.
297: if (beforeInitPlaceholder != null) {
298: setHTMLImpl(DOM.getInnerHTML(beforeInitPlaceholder));
299: beforeInitPlaceholder = null;
300: }
301: }
302:
303: protected native void setHTMLImpl(String html) /*-{
304: this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.innerHTML = html;
305: }-*/;
306:
307: protected native void setTextImpl(String text) /*-{
308: this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.textContent = text;
309: }-*/;
310:
311: protected native void unhookEvents() /*-{
312: var elem = this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
313: var wnd = elem.contentWindow;
314:
315: wnd.removeEventListener('keydown', elem.__gwt_handler, true);
316: wnd.removeEventListener('keyup', elem.__gwt_handler, true);
317: wnd.removeEventListener('keypress', elem.__gwt_handler, true);
318: wnd.removeEventListener('mousedown', elem.__gwt_handler, true);
319: wnd.removeEventListener('mouseup', elem.__gwt_handler, true);
320: wnd.removeEventListener('mousemove', elem.__gwt_handler, true);
321: wnd.removeEventListener('mouseover', elem.__gwt_handler, true);
322: wnd.removeEventListener('mouseout', elem.__gwt_handler, true);
323: wnd.removeEventListener('click', elem.__gwt_handler, true);
324:
325: wnd.removeEventListener('focus', elem.__gwt_focusHandler, true);
326: wnd.removeEventListener('blur', elem.__gwt_blurHandler, true);
327:
328: elem.__gwt_handler = null;
329: elem.__gwt_focusHandler = null;
330: elem.__gwt_blurHandler = null;
331: }-*/;
332:
333: void execCommand(String cmd, String param) {
334: if (isRichEditingActive(elem)) {
335: // When executing a command, focus the iframe first, since some commands
336: // don't take properly when it's not focused.
337: setFocus(true);
338: execCommandAssumingFocus(cmd, param);
339: }
340: }
341:
342: native void execCommandAssumingFocus(String cmd, String param) /*-{
343: this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.execCommand(cmd, false, param);
344: }-*/;
345:
346: native boolean isRichEditingActive(Element e) /*-{
347: return ((e.contentWindow.document.designMode).toUpperCase()) == 'ON';
348: }-*/;
349:
350: boolean queryCommandState(String cmd) {
351: if (isRichEditingActive(elem)) {
352: // When executing a command, focus the iframe first, since some commands
353: // don't take properly when it's not focused.
354: setFocus(true);
355: return queryCommandStateAssumingFocus(cmd);
356: } else {
357: return false;
358: }
359: }
360:
361: native boolean queryCommandStateAssumingFocus(String cmd) /*-{
362: return !!this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.queryCommandState(cmd);
363: }-*/;
364:
365: String queryCommandValue(String cmd) {
366: // When executing a command, focus the iframe first, since some commands
367: // don't take properly when it's not focused.
368: setFocus(true);
369: return queryCommandValueAssumingFocus(cmd);
370: }
371:
372: native String queryCommandValueAssumingFocus(String cmd) /*-{
373: return this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.queryCommandValue(cmd);
374: }-*/;
375: }
|