API  0.9.10
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
31 CPWebKitBrowserEngine = 1 << 4; // Safari + Chrome
32 CPBlinkBrowserEngine = 1 << 5; // Recent Chrome
33 
34 // Operating Systems
38 
39 // Features
41 
45 
48 // 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.
50 // window.clipboardData exists and can be read and written to at any time using window.clipboardData.getData/setData.
54 
56 
59 
62 
64 
65 // Internet explorer does not allow dynamically changing the type of an input element
68 
70 
71 // element.style.font can be set for an element not in the DOM.
73 
74 // Input elements have 1 px of extra padding on the left regardless of padding setting.
77 
79 
81 
83 
84 /*
85  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
86  */
88 
89 // The paste event is only sent if an input or textarea has focus.
91 // Redirecting the focus of the browser on keydown to an input for Cmd-V or Ctrl-V makes the paste fail.
93 
94 /*
95  Safari calculates incorrect text size unless you set the canvas font even if it is already set
96  You can see the bug after disabling the workaround and opening any panel while typing.
97  You can use the font panel in the manual test for CPTextView.
98  Look out for a displaced cursor, i.e. after typing letters of small width, such as the 'i'.
99  https://bugs.webkit.org/show_bug.cgi?id=150224
100  */
102 
103 
104 var USER_AGENT = "",
109 
110 // default these features to true
111 PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = YES;
112 PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = YES;
113 PLATFORM_FEATURES[CPAltEnterTextAreaFeature] = YES;
114 
115 if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
116  USER_AGENT = window.navigator.userAgent;
117 
118 // Opera
119 if (typeof window !== "undefined" && window.opera)
120 {
122 
123  PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
124 }
125 
126 // Internet Explorer
127 else if (typeof window !== "undefined" && (window.attachEvent || (!(window.ActiveXObject) && "ActiveXObject" in window))) // Must follow Opera check.
128 {
130 
131  // Features we can only be sure of with IE (no known independent tests)
132  PLATFORM_FEATURES[CPVMLFeature] = YES;
133  PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
134  PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
135 
136  PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = YES;
137 
138  PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = NO;
139 
140  // Tested in Internet Explore 8 and 9.
141  PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = NO;
142 
143  // IE allows free clipboard access.
144  PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = YES;
145 }
146 
147 // Safari + Chrome (WebKit and Blink)
148 else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
149 {
151 
152  // Features we can only be sure of with WebKit (no known independent tests)
153  PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
154  PLATFORM_FEATURES[CPHTMLContentEditableFeature] = YES;
155 
156  PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
157  PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = NO;
158  PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
159 
160  var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
161  versionEnd = USER_AGENT.indexOf(" ", versionStart),
162  versionString = USER_AGENT.substring(versionStart, versionEnd),
163  versionDivision = versionString.indexOf('.'),
164  majorVersion = parseInt(versionString.substring(0, versionDivision)),
165  minorVersion = parseInt(versionString.substr(versionDivision + 1));
166 
167  if ((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
169 
170  // FIXME this is a terrible hack to get around this bug:
171  // https://bugs.webkit.org/show_bug.cgi?id=21548
172  if (![CPPlatform isBrowser])
173  PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
174 
175  if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
177 
178  // This is supposedly fixed in webkit r123603. Seems to work in Chrome 21 but not Safari 6.0.
179  if (majorVersion < 537)
180  PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
181 
182  if (USER_AGENT.indexOf("Chrome") === CPNotFound)
183  {
184  PLATFORM_FEATURES[CPSOPDisabledFromFileURLs] = YES;
185  PLATFORM_FEATURES[CPHTMLDragAndDropFeature] = YES;
186  // https://bugs.webkit.org/show_bug.cgi?id=75891
188  // https://bugs.webkit.org/show_bug.cgi?id=39689
191  }
192  else if ((window.chrome || (window.Intl && Intl.v8BreakIterator)) && 'CSS' in window)
194 
195  // Assume this bug was introduced around Safari 5.1/Chrome 16. This could probably be tighter.
196  if (majorVersion > 533)
198 }
199 
200 // KHTML
201 else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
202 {
204 }
205 
206 // Gecko
207 else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
208 {
210 
211  PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
212 
213  var index = USER_AGENT.indexOf("Firefox"),
214  version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
215 
216  if (version >= 3.0)
217  PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
218 
219  if (version < 3.0)
220  PLATFORM_FEATURES[CPJavaScriptMouseWheelValues_8_15] = YES;
221 
222  // Some day this might be fixed and should be version prefixed. No known fixed version yet.
223  PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
224 
225  PLATFORM_FEATURES[CPAltEnterTextAreaFeature] = NO;
226  // This was supposed to be added in Firefox 22, but when testing with the latest beta as of 2013-06-14
227  // it does not seem to work. It seems to exhibit the CPJavaScriptPasteRequiresEditableTarget problem,
228  // and in addition doesn't seem to work with our native copy code either.
229  /*if (version >= 22.0)
230  {
231  PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
232  // TODO File a bug at https://bugzilla.mozilla.org/. In other browsers, one can return "false" from the
233  // beforepaste event to indicate a paste should be enabled even that the DOMEvent.target is not editable.
234  PLATFORM_BUGS |= CPJavaScriptPasteRequiresEditableTarget;
235  }*/
236 }
237 
238 // Feature-specific checks
239 if (typeof document != "undefined")
240 {
241  var canvasElement = document.createElement("canvas");
242 
243  // Detect canvas support
244  if (canvasElement && canvasElement.getContext)
245  {
246  PLATFORM_FEATURES[CPHTMLCanvasFeature] = YES;
247 
248  // Any browser that supports canvas supports CSS opacity
249  PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = NO;
250 
251  // Detect canvas setTransform/transform support
252  var context = document.createElement("canvas").getContext("2d");
253 
254  if (context && context.setTransform && context.transform)
255  PLATFORM_FEATURES[CPJavaScriptCanvasTransformFeature] = YES;
256  }
257 
258  var DOMElement = document.createElement("div");
259 
260  // Detect whether we have innerText or textContent (or neither)
261  if (DOMElement.innerText != undefined)
262  PLATFORM_FEATURES[CPJavaScriptInnerTextFeature] = YES;
263  else if (DOMElement.textContent != undefined)
264  PLATFORM_FEATURES[CPJavaScriptTextContentFeature] = YES;
265 
266  var DOMInputElement = document.createElement("input");
267 
268  if ("oninput" in DOMInputElement)
269  PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
270  else if (typeof DOMInputElement.setAttribute === "function")
271  {
272  DOMInputElement.setAttribute("oninput", "return;");
273 
274  if (typeof DOMInputElement.oninput === "function")
275  PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
276  }
277 
278  // Detect FileAPI support
279  if (typeof DOMInputElement.setAttribute === "function")
280  {
281  DOMInputElement.setAttribute("type", "file");
282  PLATFORM_FEATURES[CPFileAPIFeature] = !!DOMInputElement["files"];
283  }
284  else
285  PLATFORM_FEATURES[CPFileAPIFeature] = NO;
286 }
287 
288 function CPFeatureIsCompatible(aFeature)
289 {
290  return !!PLATFORM_FEATURES[aFeature];
291 }
292 
293 function CPPlatformHasBug(aBug)
294 {
295  return PLATFORM_BUGS & aBug;
296 }
297 
298 function CPBrowserIsEngine(anEngine)
299 {
300  return PLATFORM_ENGINE & anEngine;
301 }
302 
303 function CPBrowserIsOperatingSystem(anOperatingSystem)
304 {
305  return OPERATING_SYSTEM === anOperatingSystem;
306 }
307 
309 
310 if (USER_AGENT.indexOf("Mac") !== -1)
311 {
313 
315 
316  CPUndoKeyEquivalent = @"z";
317  CPRedoKeyEquivalent = @"Z";
318 
321 }
322 else
323 {
324  if (USER_AGENT.indexOf("Windows") !== -1)
326 
328 
331 
334 }
335 
339 function CPSetPlatformFeature(aFeature, aBool)
340 {
341  PLATFORM_FEATURES[aFeature] = aBool;
342 }
343 
353 function CPBrowserStyleProperty(aProperty)
354 {
355  var lowerProperty = aProperty.toLowerCase();
356 
357  if (PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] === undefined)
358  {
359  var r = nil;
360 
361 #if PLATFORM(DOM)
362  var testElement = document.createElement('div');
363 
364  switch (lowerProperty)
365  {
366  case 'transitionend':
367  var candidates = {
368  'WebkitTransition' : 'webkitTransitionEnd',
369  'MozTransition' : 'transitionend',
370  'OTransition' : 'oTransitionEnd',
371  'msTransition' : 'MSTransitionEnd',
372  'transition' : 'transitionend'
373  };
374 
375  r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transition']] || nil;
376  break;
377 
378  case 'transformorigin':
379 
380  var candidates = {
381  'WebkitTransform' : 'WebkitTransformOrigin',
382  'MozTransform' : 'MozTransformOrigin',
383  'OTransform' : 'OTransformOrigin',
384  'msTransform' : 'MSTransformOrigin',
385  'transform' : 'transformOrigin'
386  };
387 
388  r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transform']] || nil;
389  break;
390 
391  case 'animationend':
392  var candidates = {
393  'WebkitAnimation' : 'webkitAnimationEnd',
394  'MozAnimation' : 'animationend',
395  'OAnimation' : 'oAnimationEnd',
396  'msAnimation' : 'MSAnimationEnd',
397  'animation' : 'animationend'
398  };
399 
400  r = candidates[PLATFORM_STYLE_JS_PROPERTIES['animation']] || nil;
401  break;
402 
403  default:
404  var prefixes = ["Webkit", "Moz", "O", "ms"],
405  strippedProperty = aProperty.split('-').join(' '),
406  capProperty = [strippedProperty capitalizedString].split(' ').join('');
407 
408  for (var i = 0; i < prefixes.length; i++)
409  {
410  // First check if the property is already valid without being formatted, otherwise try the capitalized property
411  if (prefixes[i] + aProperty in testElement.style)
412  {
413  r = prefixes[i] + aProperty;
414  break;
415  }
416  else if (prefixes[i] + capProperty in testElement.style)
417  {
418  r = prefixes[i] + capProperty;
419  break;
420  }
421  }
422 
423  if (!r && lowerProperty in testElement.style)
424  r = lowerProperty;
425 
426  break;
427  }
428 #endif
429 
430  PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] = r;
431  }
432 
433  return PLATFORM_STYLE_JS_PROPERTIES[lowerProperty];
434 }
435 
436 function CPBrowserCSSProperty(aProperty)
437 {
438  var browserProperty = CPBrowserStyleProperty(aProperty);
439 
440  if (!browserProperty)
441  return nil;
442 
443  var prefixes = {
444  'Webkit': '-webkit-',
445  'Moz': '-moz-',
446  'O': '-o-',
447  'ms': '-ms-'
448  };
449 
450  for (var prefix in prefixes)
451  {
452  if (browserProperty.substring(0, prefix.length) == prefix)
453  {
454  var browserPropertyWithoutPrefix = browserProperty.substring(prefix.length),
455  parts = browserPropertyWithoutPrefix.match(/[A-Z][a-z]+/g);
456 
457  // If there were any capitalized words in the browserProperty, insert a "-" between each one
458  if (parts && parts.length > 0)
459  browserPropertyWithoutPrefix = parts.join("-");
460 
461  return prefixes[prefix] + browserPropertyWithoutPrefix.toLowerCase();
462  }
463  }
464 
465  var parts = browserProperty.match(/[A-Z][a-z]+/g);
466 
467  if (parts && parts.length > 0)
468  browserProperty = parts.join("-");
469 
470  return browserProperty.toLowerCase();
471 }
472 
474 {
475  return context.webkitBackingStorePixelRatio ||
476  context.mozBackingStorePixelRatio ||
477  context.msBackingStorePixelRatio ||
478  context.oBackingStorePixelRatio ||
479  context.backingStorePixelRatio || 1;
480 }
CPHTMLDragAndDropFeature
function CPBrowserCSSProperty(aProperty)
CPCSSRGBAFeature
CPWebKitBrowserEngine
CPOtherOperatingSystem
CPOperaBrowserEngine
CPGeckoBrowserEngine
CPJavaScriptNegativeMouseWheelValues
function CPBrowserBackingStorePixelRatio(context)
CPJavaScriptInnerTextFeature
CPRedoKeyEquivalentModifierMask
CPUnknownBrowserEngine
CPInputOnInputEventFeature
CPJavaScriptPasteRequiresEditableTarget
CPJavaScriptClipboardEventsFeature
CPCSSAnimationFeature
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
CPTextSizingAlwaysNeedsSetFontBug
function CPBrowserIsOperatingSystem(anOperatingSystem)
var PLATFORM_FEATURES
CPPlatformActionKeyMask
function CPBrowserStyleProperty(aProperty)
function CPSetPlatformFeature(aFeature, aBool)
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
CPBlinkBrowserEngine
CPCanvasParentDrawErrorsOnMovementBug
CPInput1PxLeftPadding