API  0.9.9
CPCompatibility.j
Go to the documentation of this file.
1 /*
2  * CPCompatibility.j
3  * AppKit
4  *
5  * Created by Francisco Tolmasky.
6  * Copyright 2008, 280 North, Inc.
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21  */
22 
23 
24 
25 // Browser Engines
32 
33 // Operating Systems
37 
38 // Features
40 
44 
47 // In onpaste, oncopy and oncut events, the event has an event.clipboardData from which the current pasteboard contents can be read with event.clipboardData.getData.
49 // window.clipboardData exists and can be read and written to at any time using window.clipboardData.getData/setData.
53 
55 
58 
61 
63 
64 // Internet explorer does not allow dynamically changing the type of an input element
67 
69 
70 // element.style.font can be set for an element not in the DOM.
72 
73 // Input elements have 1 px of extra padding on the left regardless of padding setting.
76 
78 
80 
81 
82 
83 /*
84  When an absolutely positioned div (CPView) with an absolutely positioned canvas in it (CPView with drawRect:) moves things on top of the canvas (subviews) don't redraw correctly. E.g. if you have a bunch of text fields in a CPBox in a sheet which animates in, some of the text fields might not be visible because the CPBox has a canvas at the bottom and the box moved form offscreen to onscreen. This bug is probably very related: https://bugs.webkit.org/show_bug.cgi?id=67203
85 */
87 
88 // The paste event is only sent if an input or textarea has focus.
90 // Redirecting the focus of the browser on keydown to an input for Cmd-V or Ctrl-V makes the paste fail.
92 
93 
94 var USER_AGENT = "",
99 
100 // default these features to true
101 PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = YES;
102 PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = YES;
103 PLATFORM_FEATURES[CPAltEnterTextAreaFeature] = YES;
104 
105 if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
106  USER_AGENT = window.navigator.userAgent;
107 
108 // Opera
109 if (typeof window !== "undefined" && window.opera)
110 {
112 
113  PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
114 }
115 
116 // Internet Explorer
117 else if (typeof window !== "undefined" && window.attachEvent) // Must follow Opera check.
118 {
120 
121  // Features we can only be sure of with IE (no known independent tests)
122  PLATFORM_FEATURES[CPVMLFeature] = YES;
123  PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
124  PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
125 
126  PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = YES;
127 
128  PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = NO;
129 
130  // Tested in Internet Explore 8 and 9.
131  PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = NO;
132 
133  // IE allows free clipboard access.
134  PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = YES;
135 }
136 
137 // WebKit
138 else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
139 {
141 
142  // Features we can only be sure of with WebKit (no known independent tests)
143  PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
144  PLATFORM_FEATURES[CPHTMLContentEditableFeature] = YES;
145 
146  PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
147  PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = NO;
148  PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
149 
150  var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
151  versionEnd = USER_AGENT.indexOf(" ", versionStart),
152  versionString = USER_AGENT.substring(versionStart, versionEnd),
153  versionDivision = versionString.indexOf('.'),
154  majorVersion = parseInt(versionString.substring(0, versionDivision)),
155  minorVersion = parseInt(versionString.substr(versionDivision + 1));
156 
157  if ((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
159 
160  // FIXME this is a terrible hack to get around this bug:
161  // https://bugs.webkit.org/show_bug.cgi?id=21548
162  if (![CPPlatform isBrowser])
163  PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
164 
165  if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
167 
168  // This is supposedly fixed in webkit r123603. Seems to work in Chrome 21 but not Safari 6.0.
169  if (majorVersion < 537)
170  PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
171 
172  if (USER_AGENT.indexOf("Chrome") === CPNotFound)
173  {
174  PLATFORM_FEATURES[CPSOPDisabledFromFileURLs] = YES;
175  PLATFORM_FEATURES[CPHTMLDragAndDropFeature] = YES;
176  // https://bugs.webkit.org/show_bug.cgi?id=75891
178  // https://bugs.webkit.org/show_bug.cgi?id=39689
180  }
181 
182  // Assume this bug was introduced around Safari 5.1/Chrome 16. This could probably be tighter.
183  if (majorVersion > 533)
185 }
186 
187 // KHTML
188 else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
189 {
191 }
192 
193 // Gecko
194 else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
195 {
197 
198  PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
199 
200  var index = USER_AGENT.indexOf("Firefox"),
201  version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
202 
203  if (version >= 3.0)
204  PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
205 
206  if (version < 3.0)
207  PLATFORM_FEATURES[CPJavaScriptMouseWheelValues_8_15] = YES;
208 
209  // Some day this might be fixed and should be version prefixed. No known fixed version yet.
210  PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
211 
212  PLATFORM_FEATURES[CPAltEnterTextAreaFeature] = NO;
213  // This was supposed to be added in Firefox 22, but when testing with the latest beta as of 2013-06-14
214  // it does not seem to work. It seems to exhibit the CPJavaScriptPasteRequiresEditableTarget problem,
215  // and in addition doesn't seem to work with our native copy code either.
216  /*if (version >= 22.0)
217  {
218  PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
219  // TODO File a bug at https://bugzilla.mozilla.org/. In other browsers, one can return "false" from the
220  // beforepaste event to indicate a paste should be enabled even that the DOMEvent.target is not editable.
221  PLATFORM_BUGS |= CPJavaScriptPasteRequiresEditableTarget;
222  }*/
223 }
224 
225 // Feature-specific checks
226 if (typeof document != "undefined")
227 {
228  var canvasElement = document.createElement("canvas");
229 
230  // Detect canvas support
231  if (canvasElement && canvasElement.getContext)
232  {
233  PLATFORM_FEATURES[CPHTMLCanvasFeature] = YES;
234 
235  // Any browser that supports canvas supports CSS opacity
236  PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = NO;
237 
238  // Detect canvas setTransform/transform support
239  var context = document.createElement("canvas").getContext("2d");
240 
241  if (context && context.setTransform && context.transform)
242  PLATFORM_FEATURES[CPJavaScriptCanvasTransformFeature] = YES;
243  }
244 
245  var DOMElement = document.createElement("div");
246 
247  // Detect whether we have innerText or textContent (or neither)
248  if (DOMElement.innerText != undefined)
249  PLATFORM_FEATURES[CPJavaScriptInnerTextFeature] = YES;
250  else if (DOMElement.textContent != undefined)
251  PLATFORM_FEATURES[CPJavaScriptTextContentFeature] = YES;
252 
253  var DOMInputElement = document.createElement("input");
254 
255  if ("oninput" in DOMInputElement)
256  PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
257  else if (typeof DOMInputElement.setAttribute === "function")
258  {
259  DOMInputElement.setAttribute("oninput", "return;");
260 
261  if (typeof DOMInputElement.oninput === "function")
262  PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
263  }
264 
265  // Detect FileAPI support
266  if (typeof DOMInputElement.setAttribute === "function")
267  {
268  DOMInputElement.setAttribute("type", "file");
269  PLATFORM_FEATURES[CPFileAPIFeature] = !!DOMInputElement["files"];
270  }
271  else
272  PLATFORM_FEATURES[CPFileAPIFeature] = NO;
273 }
274 
275 function CPFeatureIsCompatible(aFeature)
276 {
277  return !!PLATFORM_FEATURES[aFeature];
278 }
279 
280 function CPPlatformHasBug(aBug)
281 {
282  return PLATFORM_BUGS & aBug;
283 }
284 
285 function CPBrowserIsEngine(anEngine)
286 {
287  return PLATFORM_ENGINE === anEngine;
288 }
289 
290 function CPBrowserIsOperatingSystem(anOperatingSystem)
291 {
292  return OPERATING_SYSTEM === anOperatingSystem;
293 }
294 
296 
297 if (USER_AGENT.indexOf("Mac") !== -1)
298 {
300 
302 
303  CPUndoKeyEquivalent = @"z";
304  CPRedoKeyEquivalent = @"Z";
305 
308 }
309 else
310 {
311  if (USER_AGENT.indexOf("Windows") !== -1)
313 
315 
318 
321 }
322 
332 function CPBrowserStyleProperty(aProperty)
333 {
334  var lowerProperty = aProperty.toLowerCase();
335 
336  if (PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] === undefined)
337  {
338  var r = nil;
339 
340 #if PLATFORM(DOM)
341  var testElement = document.createElement('div');
342 
343  switch (lowerProperty)
344  {
345  case 'transitionend':
346  var candidates = {
347  'WebkitTransition' : 'webkitTransitionEnd',
348  'MozTransition' : 'transitionend',
349  'OTransition' : 'oTransitionEnd',
350  'msTransition' : 'MSTransitionEnd',
351  'transition' : 'transitionend'
352  };
353 
354  r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transition']] || nil;
355  break;
356 
357  case 'transformorigin':
358 
359  var candidates = {
360  'WebkitTransform' : 'WebkitTransformOrigin',
361  'MozTransform' : 'MozTransformOrigin',
362  'OTransform' : 'OTransformOrigin',
363  'msTransform' : 'MSTransformOrigin',
364  'transform' : 'transformOrigin'
365  };
366 
367  r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transform']] || nil;
368  break;
369 
370  default:
371  var prefixes = ["Webkit", "Moz", "O", "ms"],
372  strippedProperty = aProperty.split('-').join(' '),
373  capProperty = [strippedProperty capitalizedString].split(' ').join('');
374 
375  for (var i = 0; i < prefixes.length; i++)
376  {
377  // First check if the property is already valid without being formatted, otherwise try the capitalized property
378  if (prefixes[i] + aProperty in testElement.style)
379  {
380  r = prefixes[i] + aProperty;
381  break;
382  }
383  else if (prefixes[i] + capProperty in testElement.style)
384  {
385  r = prefixes[i] + capProperty;
386  break;
387  }
388  }
389 
390  if (!r && lowerProperty in testElement.style)
391  r = lowerProperty;
392 
393  break;
394  }
395 #endif
396 
397  PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] = r;
398  }
399 
400  return PLATFORM_STYLE_JS_PROPERTIES[lowerProperty];
401 }
402 
403 function CPBrowserCSSProperty(aProperty)
404 {
405  var browserProperty = CPBrowserStyleProperty(aProperty);
406 
407  if (!browserProperty)
408  return nil;
409 
410  var prefixes = {
411  'Webkit': '-webkit-',
412  'Moz': '-moz-',
413  'O': '-o-',
414  'ms': '-ms-'
415  };
416 
417  for (var prefix in prefixes)
418  {
419  if (browserProperty.substring(0, prefix.length) == prefix)
420  {
421  var browserPropertyWithoutPrefix = browserProperty.substring(prefix.length),
422  parts = browserPropertyWithoutPrefix.match(/[A-Z][a-z]+/g);
423 
424  // If there were any capitalized words in the browserProperty, insert a "-" between each one
425  if (parts && parts.length > 0)
426  browserPropertyWithoutPrefix = parts.join("-");
427 
428  return prefixes[prefix] + browserPropertyWithoutPrefix.toLowerCase();
429  }
430  }
431 
432  var parts = browserProperty.match(/[A-Z][a-z]+/g);
433 
434  if (parts && parts.length > 0)
435  browserProperty = parts.join("-");
436 
437  return browserProperty.toLowerCase();
438 }
439 
441 {
442  return context.webkitBackingStorePixelRatio ||
443  context.mozBackingStorePixelRatio ||
444  context.msBackingStorePixelRatio ||
445  context.oBackingStorePixelRatio ||
446  context.backingStorePixelRatio || 1;
447 }
CPHTMLDragAndDropFeature
function CPBrowserCSSProperty(aProperty)
CPCSSRGBAFeature
CPWebKitBrowserEngine
CPOtherOperatingSystem
CPOperaBrowserEngine
CPGeckoBrowserEngine
CPJavaScriptNegativeMouseWheelValues
function CPBrowserBackingStorePixelRatio(context)
CPJavaScriptInnerTextFeature
CPRedoKeyEquivalentModifierMask
CPUnknownBrowserEngine
CPInputOnInputEventFeature
CPJavaScriptPasteRequiresEditableTarget
CPJavaScriptClipboardEventsFeature
CPJavaScriptClipboardAccessFeature
CPRedoKeyEquivalent
CPAltEnterTextAreaFeature
CPJavaScriptTextContentFeature
var PLATFORM_STYLE_JS_PROPERTIES
CPJavaScriptCanvasDrawFeature
CPInternetExplorerBrowserEngine
var PLATFORM_ENGINE
CPCommandKeyMask
OPERATING_SYSTEM
function CPFeatureIsCompatible(aFeature)
CPKHTMLBrowserEngine
var Z
Definition: CGContextVML.j:63
CPWindowsOperatingSystem
CPInputSetFontOutsideOfDOM
CPFileAPIFeature
CPJavaScriptMouseWheelValues_8_15
CPHTML5DragAndDropSourceYOffBy1
CPHTMLCanvasFeature
function CPBrowserIsOperatingSystem(anOperatingSystem)
var PLATFORM_FEATURES
CPPlatformActionKeyMask
function CPBrowserStyleProperty(aProperty)
CPJavaScriptShadowFeature
CPJavaScriptCanvasTransformFeature
CPNotFound
Definition: CPObjJRuntime.j:62
CPInputTypeCanBeChangedFeature
CPJavaScriptPasteCantRefocus
CPHTMLContentEditableFeature
CPMacOperatingSystem
CPVMLFeature
CPJavaScriptRemedialKeySupport
var PLATFORM_BUGS
CPOpacityRequiresFilterFeature
CPControlKeyMask
function CPBrowserIsEngine(anEngine)
CPUndoKeyEquivalentModifierMask
function CPPlatformHasBug(aBug)
CPSOPDisabledFromFileURLs
CPUndoKeyEquivalent
var USER_AGENT
CPCanvasParentDrawErrorsOnMovementBug
CPInput1PxLeftPadding