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.Element;
019: import com.google.gwt.user.client.ui.RichTextArea.FontSize;
020:
021: /**
022: * Safari rich text platform implementation.
023: */
024: public class RichTextAreaImplSafari extends RichTextAreaImplStandard {
025:
026: private static final String[] sizeNumberCSSValues = new String[] {
027: "medium", "xx-small", "x-small", "small", "medium",
028: "large", "x-large", "xx-large" };
029:
030: private static int webKitVersion = getWebKitVersion();
031:
032: /**
033: * WebKit v420 began suppporting full rich text editing.
034: */
035: private static boolean extendedEditingSupported = (webKitVersion >= 420);
036:
037: /**
038: * WebKit v420 changed BackColor to HiliteColor.
039: */
040: private static boolean useHiliteColor = (webKitVersion >= 420);
041:
042: /**
043: * WebKit version up to *and including* 420 require CSS font-size values
044: * (e.g. 'medium', 'x-large') rather than size numbers. All subsequent
045: * versions use size numbers like other browsers.
046: */
047: private static boolean oldSchoolSizeValues = (webKitVersion <= 420);
048:
049: private static native int getWebKitVersion() /*-{
050: var exp = / AppleWebKit\/([\d]+)/;
051: var result = exp.exec(navigator.userAgent);
052: if (result) {
053: var version = parseInt(result[1]);
054: if (version) {
055: return version;
056: }
057: }
058:
059: // Intentionally conservative fallback.
060: return 0;
061: }-*/;;
062:
063: @Override
064: public Element createElement() {
065: return super .createElement();
066: }
067:
068: @Override
069: public native boolean isBold() /*-{
070: return !!this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.__gwt_isBold;
071: }-*/;
072:
073: @Override
074: public boolean isExtendedEditingSupported() {
075: return extendedEditingSupported;
076: }
077:
078: @Override
079: public native boolean isItalic() /*-{
080: return !!this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.__gwt_isItalic;
081: }-*/;
082:
083: @Override
084: public native boolean isUnderlined() /*-{
085: return !!this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.__gwt_isUnderlined;
086: }-*/;
087:
088: @Override
089: public void setBackColor(String color) {
090: if (useHiliteColor) {
091: execCommand("HiliteColor", color);
092: } else {
093: super .setBackColor(color);
094: }
095: }
096:
097: @Override
098: public native void setFocus(boolean focused) /*-{
099: // Safari needs the *iframe* focused, not its window.
100: var elem = this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
101: if (focused) {
102: elem.focus();
103: if (elem.__gwt_restoreSelection) {
104: elem.__gwt_restoreSelection();
105: }
106: } else {
107: elem.blur();
108: }
109: }-*/;
110:
111: @Override
112: public void setFontSize(FontSize fontSize) {
113: if (oldSchoolSizeValues) {
114: // Safari2 only accepts css-style 'small, medium, large, etc' values.
115: // Setting these doesn't seem to hurt Safari3.
116: int number = fontSize.getNumber();
117: if ((number >= 0) && (number <= 7)) {
118: execCommand("FontSize", sizeNumberCSSValues[number]);
119: }
120: } else {
121: super .setFontSize(fontSize);
122: }
123: }
124:
125: @Override
126: protected native String getTextImpl() /*-{
127: return this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.innerText;
128: }-*/;
129:
130: @Override
131: protected native void hookEvents() /*-{
132: var elem = this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
133: var wnd = elem.contentWindow;
134: var doc = wnd.document;
135:
136: // Create an expando on the element to hold the selection state.
137: elem.__gwt_selection = { baseOffset:0, extentOffset:0, baseNode:null,
138: extentNode:null };
139:
140: // A function for restoring the selection state.
141: elem.__gwt_restoreSelection = function() {
142: var sel = elem.__gwt_selection;
143:
144: // wnd.getSelection is not defined if the iframe isn't attached.
145: if (wnd.getSelection) {
146: wnd.getSelection().setBaseAndExtent(sel.baseNode, sel.baseOffset,
147: sel.extentNode, sel.extentOffset);
148: }
149: };
150:
151: // Generic event dispatcher. Also stores selection state.
152: elem.__gwt_handler = function(evt) {
153: // Store the editor's selection state.
154: var s = wnd.getSelection();
155: elem.__gwt_selection = {
156: baseOffset:s.baseOffset,
157: extentOffset:s.extentOffset,
158:
159: baseNode:s.baseNode,
160: extentNode:s.extentNode
161: };
162:
163: // Hang on to bold/italic/underlined states.
164: elem.__gwt_isBold = doc.queryCommandState('Bold');
165: elem.__gwt_isItalic = doc.queryCommandState('Italic');
166: elem.__gwt_isUnderlined = doc.queryCommandState('Underline');
167:
168: // Dispatch the event.
169: if (elem.__listener) {
170: elem.__listener.@com.google.gwt.user.client.ui.RichTextArea::onBrowserEvent(Lcom/google/gwt/user/client/Event;)(evt);
171: }
172: };
173:
174: wnd.addEventListener('keydown', elem.__gwt_handler, true);
175: wnd.addEventListener('keyup', elem.__gwt_handler, true);
176: wnd.addEventListener('keypress', elem.__gwt_handler, true);
177: wnd.addEventListener('mousedown', elem.__gwt_handler, true);
178: wnd.addEventListener('mouseup', elem.__gwt_handler, true);
179: wnd.addEventListener('mousemove', elem.__gwt_handler, true);
180: wnd.addEventListener('mouseover', elem.__gwt_handler, true);
181: wnd.addEventListener('mouseout', elem.__gwt_handler, true);
182: wnd.addEventListener('click', elem.__gwt_handler, true);
183:
184: // Focus/blur event handlers. For some reason, [add|remove]eventListener()
185: // doesn't work on the iframe element (at least not for focus/blur). Don't
186: // dispatch through the normal handler method, as some of the querying we do
187: // there interferes with focus.
188: elem.onfocus = function(evt) {
189: if (elem.__listener) {
190: elem.__listener.@com.google.gwt.user.client.ui.RichTextArea::onBrowserEvent(Lcom/google/gwt/user/client/Event;)(evt);
191: }
192: };
193:
194: elem.onblur = function(evt) {
195: if (elem.__listener) {
196: elem.__listener.@com.google.gwt.user.client.ui.RichTextArea::onBrowserEvent(Lcom/google/gwt/user/client/Event;)(evt);
197: }
198: };
199: }-*/;
200:
201: @Override
202: protected native void setTextImpl(String text) /*-{
203: this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem.contentWindow.document.body.innerText = text;
204: }-*/;
205:
206: @Override
207: protected native void unhookEvents() /*-{
208: var elem = this.@com.google.gwt.user.client.ui.impl.RichTextAreaImpl::elem;
209: var wnd = elem.contentWindow;
210:
211: wnd.removeEventListener('keydown', elem.__gwt_handler, true);
212: wnd.removeEventListener('keyup', elem.__gwt_handler, true);
213: wnd.removeEventListener('keypress', elem.__gwt_handler, true);
214: wnd.removeEventListener('mousedown', elem.__gwt_handler, true);
215: wnd.removeEventListener('mouseup', elem.__gwt_handler, true);
216: wnd.removeEventListener('mousemove', elem.__gwt_handler, true);
217: wnd.removeEventListener('mouseover', elem.__gwt_handler, true);
218: wnd.removeEventListener('mouseout', elem.__gwt_handler, true);
219: wnd.removeEventListener('click', elem.__gwt_handler, true);
220:
221: elem.__gwt_restoreSelection = null;
222: elem.__gwt_handler = null;
223:
224: elem.onfocus = null;
225: elem.onblur = null;
226: }-*/;
227: }
|