diff --git a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j index fe17ec2c8..3fa5546bb 100644 --- a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j +++ b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j @@ -20,92 +20,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -/* - * THIS DOCUMENTATION STOLEN DIRECTLY FROM GOOGLE CLOSURE (licensed under Apache 2) - * - * Different web browsers have very different keyboard event handling. Most - * importantly is that only certain browsers repeat keydown events: - * IE, Opera, FF/Win32, and Safari 3 repeat keydown events. - * FF/Mac and Safari 2 do not. - * - * For the purposes of this code, "Safari 3" means WebKit 525+, when WebKit - * decided that they should try to match IE's key handling behavior. - * Safari 3.0.4, which shipped with Leopard (WebKit 523), has the - * Safari 2 behavior. - * - * Firefox, Safari, Opera prevent on keypress - * - * IE prevents on keydown - * - * Firefox does not fire keypress for shift, ctrl, alt - * Firefox does fire keydown for shift, ctrl, alt, meta - * Firefox does not repeat keydown for shift, ctrl, alt, meta - * - * Firefox does not fire keypress for up and down in an input - * - * Opera fires keypress for shift, ctrl, alt, meta - * Opera does not repeat keypress for shift, ctrl, alt, meta - * - * Safari 2 and 3 do not fire keypress for shift, ctrl, alt - * Safari 2 does not fire keydown for shift, ctrl, alt - * Safari 3 *does* fire keydown for shift, ctrl, alt - * - * IE provides the keycode for keyup/down events and the charcode (in the - * keycode field) for keypress. - * - * Mozilla provides the keycode for keyup/down and the charcode for keypress - * unless it's a non text modifying key in which case the keycode is provided. - * - * Safari 3 provides the keycode and charcode for all events. - * - * Opera provides the keycode for keyup/down event and either the charcode or - * the keycode (in the keycode field) for keypress events. - * - * Firefox x11 doesn't fire keydown events if a another key is already held down - * until the first key is released. This can cause a key event to be fired with - * a keyCode for the first key and a charCode for the second key. - * - * Safari 2 in keypress (not supported) - * - * charCode keyCode which - * ENTER: 13 13 13 - * F1: 63236 63236 63236 - * F8: 63243 63243 63243 - * ... - * p: 112 112 112 - * P: 80 80 80 - * - * Firefox, keypress: - * - * charCode keyCode which - * ENTER: 0 13 13 - * F1: 0 112 0 - * F8: 0 119 0 - * ... - * p: 112 0 112 - * P: 80 0 80 - * - * Opera, Mac+Win32, keypress: - * - * charCode keyCode which - * ENTER: undefined 13 13 - * F1: undefined 112 0 - * F8: undefined 119 0 - * ... - * p: undefined 112 112 - * P: undefined 80 80 - * - * IE7, keydown - * - * charCode keyCode which - * ENTER: undefined 13 undefined - * F1: undefined 112 undefined - * F8: undefined 119 undefined - * ... - * p: undefined 80 undefined - * P: undefined 80 undefined - */ - @import @import @import @@ -151,6 +65,29 @@ var KeyCodesToPrevent = {}, }, KeyCodesToUnicodeMap = {}; +// New map from event.key to our internal Unicode function keys. +// This is more reliable than mapping from keyCode. +var KeyToUnicodeMapFromKey = { + "Backspace": CPDeleteCharacter, + "Delete": CPDeleteFunctionKey, + "Tab": CPTabCharacter, + "Enter": CPCarriageReturnCharacter, + "Escape": CPEscapeFunctionKey, + "PageUp": CPPageUpFunctionKey, + "PageDown": CPPageDownFunctionKey, + "ArrowLeft": CPLeftArrowFunctionKey, + "ArrowUp": CPUpArrowFunctionKey, + "ArrowRight": CPRightArrowFunctionKey, + "ArrowDown": CPDownArrowFunctionKey, + "Home": CPHomeFunctionKey, + "End": CPEndFunctionKey +}; + +// Map F-keys dynamically. Assumes CP_F1_KEY (0xF704) to CP_F12_KEY (0xF70F) are defined elsewhere. +for (var i = 1; i <= 12; i++) + KeyToUnicodeMapFromKey['F' + i] = 0xF703 + i; + + KeyCodesToPrevent[CPKeyCodes.A] = YES; KeyCodesToAllow[CPKeyCodes.F1] = YES; @@ -690,6 +627,7 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio timestamp = [CPEvent currentTimestamp], sourceElement = aDOMEvent.target || aDOMEvent.srcElement, windowNumber = [[CPApp keyWindow] windowNumber], + eventKey = aDOMEvent.key, modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | @@ -697,22 +635,14 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio (_capsLockActive ? CPAlphaShiftKeyMask : 0); // With a few exceptions, all key events are blocked from propagating to - // the browser. Here the following exceptions are being allowed: - // - // - All keys pressed along with a ctrl or cmd key _unless_ they are in - // one of the two blacklists. - // - Any key listed in the whitelist. - // - // The ctrl/cmd keys are used for browser hotkeys as are the keys listed in - // the whitelist (F1-F12 at the time of writing). - // - // If a key is listed in both the blacklist and whitelist, the blacklist is - // checked first. The key will be blocked from propagating in that case. - + // the browser. We check against blacklists and whitelists. StopDOMEventPropagation = YES; + // Use event.key for checking character keys. This is more reliable across keyboard layouts. + var charToTest = (eventKey && eventKey.length === 1) ? eventKey.toLowerCase() : null; + // Make sure it is not in the blacklists. - if (!(CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] || KeyCodesToPrevent[aDOMEvent.keyCode])) + if (!((charToTest && CharacterKeysToPrevent[charToTest]) || KeyCodesToPrevent[aDOMEvent.keyCode])) { // It is not in the blacklist, let it through if the ctrl/cmd key is // also down or it's in the whitelist. @@ -726,7 +656,7 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio switch (aDOMEvent.type) { case "keydown": - // Grab and store the keycode now since it is correct and consistent at this point. + // Grab and store the keycode for compatibility with other parts of the system. if (aDOMEvent.keyCode in MozKeyCodeToKeyCodeMap) _keyCode = MozKeyCodeToKeyCodeMap[aDOMEvent.keyCode]; else @@ -734,62 +664,68 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio var characters; - // Handle key codes for which String.fromCharCode won't work. - // Refs #1036: In Internet Explorer, both 'which' and 'charCode' are undefined for special keys. - if (aDOMEvent.which === 0 || aDOMEvent.charCode === 0 || (aDOMEvent.which === undefined && aDOMEvent.charCode === undefined)) - characters = KeyCodesToUnicodeMap[_keyCode]; - - // The problem with keyCode is that this property refers to keys on the keyboard and not to characters - // This is why String.fromCharCode does not always work in more recent versions of Firefox - // E.g. pressing a '#' on a German keyboard gives you a charCode of 163, which refers to '£' and not '#' - // The property key works fine, though. From there we can get the actual character more robustly. - // Therefore we prefer key over keyCode whenever possible - - if (!characters) - characters = (aDOMEvent.key && aDOMEvent.key.length == 1) ? aDOMEvent.key.toLowerCase() : String.fromCharCode(_keyCode).toLowerCase(); - - overrideCharacters = (modifierFlags & CPShiftKeyMask || _capsLockActive) ? characters.toUpperCase() : characters; - - // check for caps lock state - if (_keyCode === CPKeyCodes.CAPS_LOCK) + // Determine characters using modern event.key property first. + if (eventKey) { - _capsLockActive = YES; - - // Make sure the caps lock flag is set in modifierFlags - modifierFlags |= CPAlphaShiftKeyMask; + if (eventKey.length === 1) + characters = eventKey; // Printable character, already correctly cased. + else + characters = String.fromCharCode(KeyToUnicodeMapFromKey[eventKey]); // Special key. } - if ([ModifierKeyCodes containsObject:_keyCode]) + // Fallback for older browsers or unhandled keys. + if (!characters) { - // A modifier key will never fire keypress. We don't need to do any other processing so we just fire it here and break. + if (aDOMEvent.charCode === 0) // Keydown for non-printable keys have charCode === 0 + characters = KeyCodesToUnicodeMap[_keyCode]; + if (!characters) + characters = String.fromCharCode(_keyCode); + } + + // Set characters for the event. event.key is already cased correctly for printable keys. + if (eventKey && eventKey.length === 1) + { + overrideCharacters = eventKey; + charactersIgnoringModifiers = eventKey.toLowerCase(); + } + else + { + charactersIgnoringModifiers = (characters || "").toLowerCase(); + overrideCharacters = (modifierFlags & CPShiftKeyMask || _capsLockActive) ? (characters || "").toUpperCase() : charactersIgnoringModifiers; + } + + // Handle caps lock state. + if (eventKey === "CapsLock") + { + // This is a simplification; getModifierState("CapsLock") would be ideal but is not used here to avoid further changes. + _capsLockActive = !_capsLockActive; + modifierFlags = (modifierFlags & ~CPAlphaShiftKeyMask) | (_capsLockActive ? CPAlphaShiftKeyMask : 0); + } + + var isModifier = (eventKey === "Control" || eventKey === "Shift" || eventKey === "Alt" || eventKey === "Meta" || eventKey === "CapsLock"); + if (isModifier) + { + // A modifier key will never fire keypress. We fire a CPFlagsChanged event and break. event = [CPEvent keyEventWithType:CPFlagsChanged location:location modifierFlags:modifierFlags timestamp:timestamp windowNumber:windowNumber context:nil characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:_keyCode]; - break; } else if (modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) { - //we are simply going to skip all keypress events that use cmd/ctrl key - //this lets us be consistent in all browsers and send on the keydown - //which means we can cancel the event early enough, but only if sendEvent needs to + // Let Cmd/Ctrl combinations be sent on keydown to allow for early cancellation. } - else if (CPKeyCodes.firesKeyPressEvent(_keyCode, aDOMEvent.key, _lastKey, aDOMEvent.shiftKey, aDOMEvent.ctrlKey, aDOMEvent.altKey)) + else if (CPKeyCodes.firesKeyPressEvent(_keyCode, eventKey, _lastKey, aDOMEvent.shiftKey, aDOMEvent.ctrlKey, aDOMEvent.altKey)) { - // this branch is taken by events which fire keydown, keypress, and keyup. - // this is the only time we'll ALLOW character keys to propagate (needed for text fields) + // This branch is for keys that fire a `keypress` event. + // We allow propagation to let the browser handle input in text fields. StopDOMEventPropagation = NO; break; } - else - { - //this branch is taken by "remedial" key events - // In this state we continue to keypress and send the CPEvent - } case "keypress": - // we unconditionally break on keypress events with modifiers, - // because we forced the event to be sent on the keydown + // We unconditionally break on keypress events with modifiers, + // as we forced the event to be sent on the keydown. if (aDOMEvent.type === "keypress" && (modifierFlags & (CPControlKeyMask | CPCommandKeyMask))) break; @@ -800,15 +736,20 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio _lastKey = keyCode; _charCodes[keyCode] = charCode; + // Use the character determined during keydown if available. var characters = overrideCharacters; - // Is this a special key? - if (!characters && (aDOMEvent.which === 0 || aDOMEvent.charCode === 0)) - characters = KeyCodesToUnicodeMap[charCode]; - if (!characters) - characters = String.fromCharCode(charCode); + { + // Fallback for printable keys on keypress. + if (eventKey && eventKey.length === 1) + characters = eventKey; + else if (charCode !== 0) + characters = String.fromCharCode(charCode); + else + characters = KeyCodesToUnicodeMap[keyCode] || ""; + } - charactersIgnoringModifiers = characters.toLowerCase(); // FIXME: This isn't correct. It SHOULD include Shift. + charactersIgnoringModifiers = characters.toLowerCase(); // Safari won't send proper capitalization during cmd-key events if (!overrideCharacters && (modifierFlags & CPCommandKeyMask) && ((modifierFlags & CPShiftKeyMask) || _capsLockActive)) @@ -817,7 +758,6 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags timestamp:timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:charCode]; - break; case "keyup": @@ -828,26 +768,26 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio _lastKey = -1; _charCodes[keyCode] = nil; - // check for caps lock state - if (keyCode === CPKeyCodes.CAPS_LOCK) + if (eventKey === "CapsLock") { - _capsLockActive = NO; - - // Make sure the caps lock flag is cleared in modifierFlags - modifierFlags &= ~CPAlphaShiftKeyMask; + // The state was handled on keydown. Nothing to do for state change on keyup for a toggle key. } - if ([ModifierKeyCodes containsObject:keyCode]) + var isModifier = (eventKey === "Control" || eventKey === "Shift" || eventKey === "Alt" || eventKey === "Meta" || eventKey === "CapsLock"); + if (isModifier) { - // A modifier key will never fire keypress. We don't need to do any other processing so we just fire it here and break. event = [CPEvent keyEventWithType:CPFlagsChanged location:location modifierFlags:modifierFlags timestamp:timestamp windowNumber:windowNumber context:nil - characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:_keyCode]; - + characters:nil charactersIgnoringModifiers:nil isARepeat:NO keyCode:keyCode]; break; } - var characters = KeyCodesToUnicodeMap[charCode] || String.fromCharCode(charCode); + var characters; + if (eventKey && eventKey.length === 1) + characters = eventKey; + else + characters = KeyCodesToUnicodeMap[keyCode] || String.fromCharCode(charCode) || ""; + charactersIgnoringModifiers = characters.toLowerCase(); if (!(modifierFlags & CPShiftKeyMask) && (modifierFlags & CPCommandKeyMask) && !_capsLockActive) @@ -856,7 +796,6 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio event = [CPEvent keyEventWithType:CPKeyUp location:location modifierFlags:modifierFlags timestamp: timestamp windowNumber:windowNumber context:nil characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:NO keyCode:keyCode]; - break; } @@ -868,7 +807,6 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio if (event && ![_platformPasteboard windowShouldSuppressKeyEvent]) { [CPApp sendEvent:event]; - [_platformPasteboard windowDidSendKeyEvent:event]; } @@ -1225,7 +1163,7 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio return; } } - + // cancel other touch cases preventively if (aDOMEvent.preventDefault) @@ -1908,3 +1846,13 @@ function CPWindowObjectList() return windowObjects; } + +function CPWindowList() +{ + var windowObjectList = CPWindowObjectList(); + + return [windowObjectList arrayByApplyingBlock:function(windowObject) + { + return [windowObject windowNumber]; + }]; +} diff --git a/dist/cappuccino/bin/flatten b/dist/cappuccino/bin/flatten index 80b9ecf8e..1df033252 100755 --- a/dist/cappuccino/bin/flatten +++ b/dist/cappuccino/bin/flatten @@ -233,7 +233,7 @@ ObjectiveJFlattener.prototype.serializeFunctions = function() { var functionString = executable._function.toString().replace(", require, exports, module, system, print, window", ""); // HACK var relative = this.rootPath.relative(path).toString(); - this.functionsBuffer.push("ObjectiveJ.FileExecutable._cacheFunction(new CFURL("+JSON.stringify(relative)+", baseURL),\n"+functionString+");"); + this.functionsBuffer.push("ObjectiveJ.StaticResource._cacheFunction(new CFURL("+JSON.stringify(relative)+", baseURL),\n"+functionString+");"); } var bundle = this.context.global.CFBundle.bundleContainingURL(path); diff --git a/dist/cappuccino/bin/fontinfo b/dist/cappuccino/bin/fontinfo index 1a91ba1c8..f1ff06158 100755 Binary files a/dist/cappuccino/bin/fontinfo and b/dist/cappuccino/bin/fontinfo differ diff --git a/dist/cappuccino/bin/imagesize b/dist/cappuccino/bin/imagesize new file mode 100755 index 000000000..5d67a1ce0 Binary files /dev/null and b/dist/cappuccino/bin/imagesize differ diff --git a/dist/cappuccino/bin/objj2objcskeleton b/dist/cappuccino/bin/objj2objcskeleton index 51ecab38d..b7972dbb3 100755 --- a/dist/cappuccino/bin/objj2objcskeleton +++ b/dist/cappuccino/bin/objj2objcskeleton @@ -4,11 +4,14 @@ var fs = require("fs"), acorn = require("objj-parser"), - walk = require("acorn-walk"), + walk = require("objj-parser/util/walk"), stream = ObjectiveJ.term; + debugger; + function main(args) { + debugger; args.shift(); if (args.length < 1) @@ -37,8 +40,6 @@ function raise(pos, message) throw syntaxError; } -function ignore(_node, _st, _c) {} - var errors = [], xcc = walk.make( { @@ -114,18 +115,7 @@ var errors = [], else raise(node.loc.start, "Action methods must have exactly one parameter"); } - }, - TypeDefStatement: ignore, - ClassStatement: ignore, - MessageSendExpression: ignore, - GlobalStatement: ignore, - ProtocolDeclarationStatement: ignore, - ArrayLiteral: ignore, - Reference: ignore, - DictionaryLiteral: ignore, - Dereference: ignore, - ImportStatement: ignore, - SelectorLiteralExpression: ignore + } } ); @@ -164,7 +154,7 @@ function parser(args) outputHeaderURL = new CFURL([outputDirectory stringByAppendingPathComponent:baseFilenameWithNoExtension + ".h"]), outputImplementationURL = new CFURL([outputDirectory stringByAppendingPathComponent:baseFilenameWithNoExtension + ".m"]), source = fs.readFileSync(sourcePath, { encoding: "utf8" }), - tokens = acorn.parse(source, { locations:true, sourceFile:sourcePath, ecmaVersion: 2022 }), + tokens = acorn.parse(source, { locations:true, sourceFile:sourcePath }), classesInformation = [], ObjectiveCSource = "", ObjectiveCHeader = "",