mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-08-25 04:57:03 +00:00
450 lines
16 KiB
Plaintext
450 lines
16 KiB
Plaintext
/*
|
|
* CPCompatibility.j
|
|
* AppKit
|
|
*
|
|
* Created by Francisco Tolmasky.
|
|
* Copyright 2008, 280 North, Inc.
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
@import "CPEvent_Constants.j"
|
|
@import "CPPlatform.j"
|
|
|
|
|
|
// Browser Engines
|
|
CPUnknownBrowserEngine = 0;
|
|
CPGeckoBrowserEngine = 1;
|
|
CPInternetExplorerBrowserEngine = 2;
|
|
CPKHTMLBrowserEngine = 3;
|
|
CPOperaBrowserEngine = 4;
|
|
CPWebKitBrowserEngine = 5;
|
|
|
|
// Operating Systems
|
|
CPMacOperatingSystem = 0;
|
|
CPWindowsOperatingSystem = 1;
|
|
CPOtherOperatingSystem = 2;
|
|
|
|
// Features
|
|
CPCSSRGBAFeature = 5;
|
|
|
|
CPHTMLCanvasFeature = 6;
|
|
CPHTMLContentEditableFeature = 7;
|
|
CPHTMLDragAndDropFeature = 8;
|
|
|
|
CPJavaScriptInnerTextFeature = 9;
|
|
CPJavaScriptTextContentFeature = 10;
|
|
// 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.
|
|
CPJavaScriptClipboardEventsFeature = 11;
|
|
// window.clipboardData exists and can be read and written to at any time using window.clipboardData.getData/setData.
|
|
CPJavaScriptClipboardAccessFeature = 12;
|
|
CPJavaScriptCanvasDrawFeature = 13;
|
|
CPJavaScriptCanvasTransformFeature = 14;
|
|
|
|
CPVMLFeature = 15;
|
|
|
|
CPJavaScriptRemedialKeySupport = 16;
|
|
CPJavaScriptShadowFeature = 20;
|
|
|
|
CPJavaScriptNegativeMouseWheelValues = 22;
|
|
CPJavaScriptMouseWheelValues_8_15 = 23;
|
|
|
|
CPOpacityRequiresFilterFeature = 24;
|
|
|
|
// Internet explorer does not allow dynamically changing the type of an input element
|
|
CPInputTypeCanBeChangedFeature = 25;
|
|
CPHTML5DragAndDropSourceYOffBy1 = 26;
|
|
|
|
CPSOPDisabledFromFileURLs = 27;
|
|
|
|
// element.style.font can be set for an element not in the DOM.
|
|
CPInputSetFontOutsideOfDOM = 28;
|
|
|
|
// Input elements have 1 px of extra padding on the left regardless of padding setting.
|
|
CPInput1PxLeftPadding = 29;
|
|
CPInputOnInputEventFeature = 30;
|
|
|
|
CPFileAPIFeature = 31;
|
|
|
|
CPAltEnterTextAreaFeature = 32;
|
|
|
|
|
|
|
|
/*
|
|
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
|
|
*/
|
|
CPCanvasParentDrawErrorsOnMovementBug = 1 << 0;
|
|
|
|
// The paste event is only sent if an input or textarea has focus.
|
|
CPJavaScriptPasteRequiresEditableTarget = 1 << 1;
|
|
// Redirecting the focus of the browser on keydown to an input for Cmd-V or Ctrl-V makes the paste fail.
|
|
CPJavaScriptPasteCantRefocus = 1 << 2;
|
|
|
|
|
|
var USER_AGENT = "",
|
|
PLATFORM_ENGINE = CPUnknownBrowserEngine,
|
|
PLATFORM_FEATURES = [],
|
|
PLATFORM_BUGS = 0,
|
|
PLATFORM_STYLE_JS_PROPERTIES = {};
|
|
|
|
// default these features to true
|
|
PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = YES;
|
|
PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = YES;
|
|
PLATFORM_FEATURES[CPAltEnterTextAreaFeature] = YES;
|
|
|
|
if (typeof window !== "undefined" && typeof window.navigator !== "undefined")
|
|
USER_AGENT = window.navigator.userAgent;
|
|
|
|
// Opera
|
|
if (typeof window !== "undefined" && window.opera)
|
|
{
|
|
PLATFORM_ENGINE = CPOperaBrowserEngine;
|
|
|
|
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
|
}
|
|
|
|
// Internet Explorer
|
|
else if (typeof window !== "undefined" && window.attachEvent) // Must follow Opera check.
|
|
{
|
|
PLATFORM_ENGINE = CPInternetExplorerBrowserEngine;
|
|
|
|
// Features we can only be sure of with IE (no known independent tests)
|
|
PLATFORM_FEATURES[CPVMLFeature] = YES;
|
|
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
|
|
PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
|
|
|
|
PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = YES;
|
|
|
|
PLATFORM_FEATURES[CPInputTypeCanBeChangedFeature] = NO;
|
|
|
|
// Tested in Internet Explore 8 and 9.
|
|
PLATFORM_FEATURES[CPInputSetFontOutsideOfDOM] = NO;
|
|
|
|
// IE allows free clipboard access.
|
|
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = YES;
|
|
}
|
|
|
|
// WebKit
|
|
else if (USER_AGENT.indexOf("AppleWebKit/") != -1)
|
|
{
|
|
PLATFORM_ENGINE = CPWebKitBrowserEngine;
|
|
|
|
// Features we can only be sure of with WebKit (no known independent tests)
|
|
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
|
|
PLATFORM_FEATURES[CPHTMLContentEditableFeature] = YES;
|
|
|
|
PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
|
|
PLATFORM_FEATURES[CPJavaScriptClipboardAccessFeature] = NO;
|
|
PLATFORM_FEATURES[CPJavaScriptShadowFeature] = YES;
|
|
|
|
var versionStart = USER_AGENT.indexOf("AppleWebKit/") + "AppleWebKit/".length,
|
|
versionEnd = USER_AGENT.indexOf(" ", versionStart),
|
|
versionString = USER_AGENT.substring(versionStart, versionEnd),
|
|
versionDivision = versionString.indexOf('.'),
|
|
majorVersion = parseInt(versionString.substring(0, versionDivision)),
|
|
minorVersion = parseInt(versionString.substr(versionDivision + 1));
|
|
|
|
if ((USER_AGENT.indexOf("Safari") !== CPNotFound && (majorVersion > 525 || (majorVersion === 525 && minorVersion > 14))) || USER_AGENT.indexOf("Chrome") !== CPNotFound)
|
|
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
|
|
|
|
// FIXME this is a terrible hack to get around this bug:
|
|
// https://bugs.webkit.org/show_bug.cgi?id=21548
|
|
if (![CPPlatform isBrowser])
|
|
PLATFORM_FEATURES[CPJavaScriptRemedialKeySupport] = YES;
|
|
|
|
if (majorVersion < 532 || (majorVersion === 532 && minorVersion < 6))
|
|
PLATFORM_FEATURES[CPHTML5DragAndDropSourceYOffBy1] = YES;
|
|
|
|
// This is supposedly fixed in webkit r123603. Seems to work in Chrome 21 but not Safari 6.0.
|
|
if (majorVersion < 537)
|
|
PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
|
|
|
|
if (USER_AGENT.indexOf("Chrome") === CPNotFound)
|
|
{
|
|
PLATFORM_FEATURES[CPSOPDisabledFromFileURLs] = YES;
|
|
PLATFORM_FEATURES[CPHTMLDragAndDropFeature] = YES;
|
|
// https://bugs.webkit.org/show_bug.cgi?id=75891
|
|
PLATFORM_BUGS |= CPJavaScriptPasteRequiresEditableTarget;
|
|
// https://bugs.webkit.org/show_bug.cgi?id=39689
|
|
PLATFORM_BUGS |= CPJavaScriptPasteCantRefocus;
|
|
}
|
|
|
|
// Assume this bug was introduced around Safari 5.1/Chrome 16. This could probably be tighter.
|
|
if (majorVersion > 533)
|
|
PLATFORM_BUGS |= CPCanvasParentDrawErrorsOnMovementBug;
|
|
}
|
|
|
|
// KHTML
|
|
else if (USER_AGENT.indexOf("KHTML") != -1) // Must follow WebKit check.
|
|
{
|
|
PLATFORM_ENGINE = CPKHTMLBrowserEngine;
|
|
}
|
|
|
|
// Gecko
|
|
else if (USER_AGENT.indexOf("Gecko") !== -1) // Must follow KHTML check.
|
|
{
|
|
PLATFORM_ENGINE = CPGeckoBrowserEngine;
|
|
|
|
PLATFORM_FEATURES[CPJavaScriptCanvasDrawFeature] = YES;
|
|
|
|
var index = USER_AGENT.indexOf("Firefox"),
|
|
version = (index === -1) ? 2.0 : parseFloat(USER_AGENT.substring(index + "Firefox".length + 1));
|
|
|
|
if (version >= 3.0)
|
|
PLATFORM_FEATURES[CPCSSRGBAFeature] = YES;
|
|
|
|
if (version < 3.0)
|
|
PLATFORM_FEATURES[CPJavaScriptMouseWheelValues_8_15] = YES;
|
|
|
|
// Some day this might be fixed and should be version prefixed. No known fixed version yet.
|
|
PLATFORM_FEATURES[CPInput1PxLeftPadding] = YES;
|
|
|
|
PLATFORM_FEATURES[CPAltEnterTextAreaFeature] = NO;
|
|
// This was supposed to be added in Firefox 22, but when testing with the latest beta as of 2013-06-14
|
|
// it does not seem to work. It seems to exhibit the CPJavaScriptPasteRequiresEditableTarget problem,
|
|
// and in addition doesn't seem to work with our native copy code either.
|
|
/*if (version >= 22.0)
|
|
{
|
|
PLATFORM_FEATURES[CPJavaScriptClipboardEventsFeature] = YES;
|
|
// TODO File a bug at https://bugzilla.mozilla.org/. In other browsers, one can return "false" from the
|
|
// beforepaste event to indicate a paste should be enabled even that the DOMEvent.target is not editable.
|
|
PLATFORM_BUGS |= CPJavaScriptPasteRequiresEditableTarget;
|
|
}*/
|
|
}
|
|
|
|
// Feature-specific checks
|
|
if (typeof document != "undefined")
|
|
{
|
|
var canvasElement = document.createElement("canvas");
|
|
|
|
// Detect canvas support
|
|
if (canvasElement && canvasElement.getContext)
|
|
{
|
|
PLATFORM_FEATURES[CPHTMLCanvasFeature] = YES;
|
|
|
|
// Any browser that supports canvas supports CSS opacity
|
|
PLATFORM_FEATURES[CPOpacityRequiresFilterFeature] = NO;
|
|
|
|
// Detect canvas setTransform/transform support
|
|
var context = document.createElement("canvas").getContext("2d");
|
|
|
|
if (context && context.setTransform && context.transform)
|
|
PLATFORM_FEATURES[CPJavaScriptCanvasTransformFeature] = YES;
|
|
}
|
|
|
|
var DOMElement = document.createElement("div");
|
|
|
|
// Detect whether we have innerText or textContent (or neither)
|
|
if (DOMElement.innerText != undefined)
|
|
PLATFORM_FEATURES[CPJavaScriptInnerTextFeature] = YES;
|
|
else if (DOMElement.textContent != undefined)
|
|
PLATFORM_FEATURES[CPJavaScriptTextContentFeature] = YES;
|
|
|
|
var DOMInputElement = document.createElement("input");
|
|
|
|
if ("oninput" in DOMInputElement)
|
|
PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
|
|
else if (typeof DOMInputElement.setAttribute === "function")
|
|
{
|
|
DOMInputElement.setAttribute("oninput", "return;");
|
|
|
|
if (typeof DOMInputElement.oninput === "function")
|
|
PLATFORM_FEATURES[CPInputOnInputEventFeature] = YES;
|
|
}
|
|
|
|
// Detect FileAPI support
|
|
if (typeof DOMInputElement.setAttribute === "function")
|
|
{
|
|
DOMInputElement.setAttribute("type", "file");
|
|
PLATFORM_FEATURES[CPFileAPIFeature] = !!DOMInputElement["files"];
|
|
}
|
|
else
|
|
PLATFORM_FEATURES[CPFileAPIFeature] = NO;
|
|
}
|
|
|
|
function CPFeatureIsCompatible(aFeature)
|
|
{
|
|
return !!PLATFORM_FEATURES[aFeature];
|
|
}
|
|
|
|
function CPPlatformHasBug(aBug)
|
|
{
|
|
return PLATFORM_BUGS & aBug;
|
|
}
|
|
|
|
function CPBrowserIsEngine(anEngine)
|
|
{
|
|
return PLATFORM_ENGINE === anEngine;
|
|
}
|
|
|
|
function CPBrowserIsOperatingSystem(anOperatingSystem)
|
|
{
|
|
return OPERATING_SYSTEM === anOperatingSystem;
|
|
}
|
|
|
|
OPERATING_SYSTEM = CPOtherOperatingSystem;
|
|
|
|
if (USER_AGENT.indexOf("Mac") !== -1)
|
|
{
|
|
OPERATING_SYSTEM = CPMacOperatingSystem;
|
|
|
|
CPPlatformActionKeyMask = CPCommandKeyMask;
|
|
|
|
CPUndoKeyEquivalent = @"z";
|
|
CPRedoKeyEquivalent = @"Z";
|
|
|
|
CPUndoKeyEquivalentModifierMask = CPCommandKeyMask;
|
|
CPRedoKeyEquivalentModifierMask = CPCommandKeyMask;
|
|
}
|
|
else
|
|
{
|
|
if (USER_AGENT.indexOf("Windows") !== -1)
|
|
OPERATING_SYSTEM = CPWindowsOperatingSystem;
|
|
|
|
CPPlatformActionKeyMask = CPControlKeyMask;
|
|
|
|
CPUndoKeyEquivalent = @"z";
|
|
CPRedoKeyEquivalent = @"y";
|
|
|
|
CPUndoKeyEquivalentModifierMask = CPControlKeyMask;
|
|
CPRedoKeyEquivalentModifierMask = CPControlKeyMask;
|
|
}
|
|
|
|
/*!
|
|
Return the properly prefixed JS property for the given name. E.g. in a webkit browser,
|
|
CPBrowserStyleProperty('transition') -> WebkitTransition
|
|
|
|
While technically not a style property, style related event handler names are also supported.
|
|
CPBrowserStyleProperty('transitionend') -> 'webkitTransitionEnd'
|
|
|
|
CSS is only available in platform(dom), so don't rely too heavily on it.
|
|
*/
|
|
function CPBrowserStyleProperty(aProperty)
|
|
{
|
|
var lowerProperty = aProperty.toLowerCase();
|
|
|
|
if (PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] === undefined)
|
|
{
|
|
var r = nil;
|
|
|
|
#if PLATFORM(DOM)
|
|
var testElement = document.createElement('div');
|
|
|
|
switch (lowerProperty)
|
|
{
|
|
case 'transitionend':
|
|
var candidates = {
|
|
'WebkitTransition' : 'webkitTransitionEnd',
|
|
'MozTransition' : 'transitionend',
|
|
'OTransition' : 'oTransitionEnd',
|
|
'msTransition' : 'MSTransitionEnd',
|
|
'transition' : 'transitionend'
|
|
};
|
|
|
|
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transition']] || nil;
|
|
break;
|
|
|
|
case 'transformorigin':
|
|
|
|
var candidates = {
|
|
'WebkitTransform' : 'WebkitTransformOrigin',
|
|
'MozTransform' : 'MozTransformOrigin',
|
|
'OTransform' : 'OTransformOrigin',
|
|
'msTransform' : 'MSTransformOrigin',
|
|
'transform' : 'transformOrigin'
|
|
};
|
|
|
|
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transform']] || nil;
|
|
break;
|
|
|
|
default:
|
|
var prefixes = ["Webkit", "Moz", "O", "ms"],
|
|
strippedProperty = aProperty.split('-').join(' '),
|
|
capProperty = [strippedProperty capitalizedString].split(' ').join('');
|
|
|
|
for (var i = 0; i < prefixes.length; i++)
|
|
{
|
|
// First check if the property is already valid without being formatted, otherwise try the capitalized property
|
|
if (prefixes[i] + aProperty in testElement.style)
|
|
{
|
|
r = prefixes[i] + aProperty;
|
|
break;
|
|
}
|
|
else if (prefixes[i] + capProperty in testElement.style)
|
|
{
|
|
r = prefixes[i] + capProperty;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!r && lowerProperty in testElement.style)
|
|
r = lowerProperty;
|
|
|
|
break;
|
|
}
|
|
#endif
|
|
|
|
PLATFORM_STYLE_JS_PROPERTIES[lowerProperty] = r;
|
|
}
|
|
|
|
return PLATFORM_STYLE_JS_PROPERTIES[lowerProperty];
|
|
}
|
|
|
|
function CPBrowserCSSProperty(aProperty)
|
|
{
|
|
var browserProperty = CPBrowserStyleProperty(aProperty);
|
|
|
|
if (!browserProperty)
|
|
return nil;
|
|
|
|
var prefixes = {
|
|
'Webkit': '-webkit-',
|
|
'Moz': '-moz-',
|
|
'O': '-o-',
|
|
'ms': '-ms-'
|
|
};
|
|
|
|
for (var prefix in prefixes)
|
|
{
|
|
if (browserProperty.substring(0, prefix.length) == prefix)
|
|
{
|
|
var browserPropertyWithoutPrefix = browserProperty.substring(prefix.length),
|
|
parts = browserPropertyWithoutPrefix.match(/[A-Z][a-z]+/g);
|
|
|
|
// If there were any capitalized words in the browserProperty, insert a "-" between each one
|
|
if (parts && parts.length > 0)
|
|
browserPropertyWithoutPrefix = parts.join("-");
|
|
|
|
return prefixes[prefix] + browserPropertyWithoutPrefix.toLowerCase();
|
|
}
|
|
}
|
|
|
|
var parts = browserProperty.match(/[A-Z][a-z]+/g);
|
|
|
|
if (parts && parts.length > 0)
|
|
browserProperty = parts.join("-");
|
|
|
|
return browserProperty.toLowerCase();
|
|
}
|
|
|
|
function CPBrowserBackingStorePixelRatio(context)
|
|
{
|
|
return context.webkitBackingStorePixelRatio ||
|
|
context.mozBackingStorePixelRatio ||
|
|
context.msBackingStorePixelRatio ||
|
|
context.oBackingStorePixelRatio ||
|
|
context.backingStorePixelRatio || 1;
|
|
}
|