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