diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j index dd71a604c..9f970dae6 100644 --- a/AppKit/CPApplication.j +++ b/AppKit/CPApplication.j @@ -29,6 +29,8 @@ @import "CPDocumentController.j" @import "CPThemeBlend.j" @import "CPCibLoading.j" +@import "CPPlatform.j" + var CPMainCibFile = @"CPMainCibFile", CPMainCibFileHumanFriendly = @"Main cib file base name"; @@ -809,7 +811,7 @@ var _CPAppBootstrapperActions = nil; + (void)actions { - return [@selector(loadDefaultTheme), @selector(loadMainCibFile)]; + return [@selector(bootstrapPlatform), @selector(loadDefaultTheme), @selector(loadMainCibFile)]; } + (void)performActions @@ -828,6 +830,11 @@ var _CPAppBootstrapperActions = nil; [CPApp run]; } ++ (BOOL)bootstrapPlatform +{ + return [CPPlatform bootstrap]; +} + + (BOOL)loadDefaultTheme { var blend = [[CPThemeBlend alloc] initWithContentsOfURL:[[CPBundle bundleForClass:[CPApplication class]] pathForResource:@"Aristo.blend"]]; diff --git a/AppKit/CPDragServer.j b/AppKit/CPDragServer.j index f39524a61..60a23f06e 100644 --- a/AppKit/CPDragServer.j +++ b/AppKit/CPDragServer.j @@ -26,6 +26,7 @@ @import #import "CoreGraphics/CGGeometry.h" +#import "Platform/Platform.h" #define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window]) @@ -111,7 +112,7 @@ var CPDragServerUpdateDragging = function(anEvent) bridgeLocation = [[anEvent window] convertBaseToBridge:location]; // We have to convert base to bridge since the drag event comes from the source window, not the drag window. - var draggingDestination = [[CPDOMWindowBridge sharedDOMWindowBridge] _dragHitTest:bridgeLocation pasteboard:CPDragServerPasteboard]; + var draggingDestination = [[CPPlatformWindow primaryPlatformWindow] _dragHitTest:bridgeLocation pasteboard:CPDragServerPasteboard]; CPDragServerLocation = [DRAGGING_WINDOW(draggingDestination) convertBridgeToBase:bridgeLocation]; @@ -241,14 +242,21 @@ var CPDragServerUpdateDragging = function(anEvent) - (void)dragView:(CPView)aView fromWindow:(CPWindow)aWindow at:(CGPoint)viewLocation offset:(CGSize)mouseOffset event:(CPEvent)anEvent pasteboard:(CPPasteboard)aPasteboard source:(id)aSourceObject slideBack:(BOOL)slideBack { var eventLocation = [anEvent locationInWindow]; - + CPDragServerView = aView; CPDragServerSource = aSourceObject; CPDragServerWindow = aWindow; CPDragServerOffset = CPPointMake(eventLocation.x - viewLocation.x, eventLocation.y - viewLocation.y); CPDragServerPasteboard = [CPPasteboard pasteboardWithName:CPDragPboard];//aPasteboard; - [_dragWindow setFrameSize:CGSizeMakeCopy([[CPDOMWindowBridge sharedDOMWindowBridge] frame].size)]; +#if PLATFORM(BROWSER) + var platformWindow = [aWindow platformWindow]; + + // FIXME: We should just have the window be the size of the view and move the window around. + [_dragWindow setPlatformWindow:platformWindow]; + [_dragWindow setFrameSize:[platformWindow contentBounds].size]; +#endif + [_dragWindow orderFront:self]; [aView setFrameOrigin:viewLocation]; @@ -318,7 +326,7 @@ var CPDragServerUpdateDragging = function(anEvent) return nil; // We don't need to do this because the only place this gets called -// -_dragHitTest: in CPDOMWindowBridge does this already. Perhaps to +// -_dragHitTest: in CPPlatformWindow does this already. Perhaps to // be safe? // if (![self containsPoint:aPoint]) // return nil; diff --git a/AppKit/CPFlashView.j b/AppKit/CPFlashView.j index 874614b7a..5d3f983a4 100644 --- a/AppKit/CPFlashView.j +++ b/AppKit/CPFlashView.j @@ -20,10 +20,10 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import "CPDOMWindowBridge.j" @import "CPFlashMovie.j" @import "CPView.j" + /*! @ingroup appkit */ @@ -96,17 +96,17 @@ - (void)mouseDragged:(CPEvent)anEvent { - [[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES]; + [[[self window] platformWindow] _propagateCurrentDOMEvent:YES]; } - (void)mouseDown:(CPEvent)anEvent { - [[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES]; + [[[self window] platformWindow] _propagateCurrentDOMEvent:YES]; } - (void)mouseUp:(CPEvent)anEvent { - [[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES]; + [[[self window] platformWindow] _propagateCurrentDOMEvent:YES]; } @end diff --git a/AppKit/CPMenu.j b/AppKit/CPMenu.j index 79ca0e9d2..8e7ab43e1 100644 --- a/AppKit/CPMenu.j +++ b/AppKit/CPMenu.j @@ -119,7 +119,7 @@ var _CPMenuBarVisible = NO, // FIXME: There must be a better way to do this. #if PLATFORM(DOM) - [[CPDOMWindowBridge sharedDOMWindowBridge] _bridgeResizeEvent:nil]; + [[CPPlatformWindow primaryPlatformWindow] resizeEvent:nil]; #endif } @@ -1078,7 +1078,7 @@ var STICKY_TIME_INTERVAL = 500, { _unconstrainedFrame = CGRectMakeCopy([self frame]); - var screenBounds = CGRectInset([[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds], 5.0, 5.0), + var screenBounds = CGRectInset([[self platformWindow] contentBounds], 5.0, 5.0), constrainedFrame = CGRectIntersection(_unconstrainedFrame, screenBounds), menuViewOrigin = [self convertBaseToBridge:CGPointMake(LEFT_MARGIN, TOP_MARGIN)]; @@ -1158,7 +1158,7 @@ var STICKY_TIME_INTERVAL = 500, if (type == CPPeriodic) { - var constrainedBounds = CGRectInset([[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds], 5.0, 5.0); + var constrainedBounds = CGRectInset([[self platformWindow] contentBounds], 5.0, 5.0); if (_scrollingState == _CPMenuWindowScrollingStateUp) { @@ -1447,9 +1447,9 @@ var _CPMenuBarWindowBackgroundColor = nil, - (id)init { - var bridgeWidth = CGRectGetWidth([[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds]); + var platformWindowWidth = CGRectGetWidth([[CPPlatformWindow primaryPlatformWindow] contentBounds]); - self = [super initWithContentRect:CGRectMake(0.0, 0.0, bridgeWidth, MENUBAR_HEIGHT) styleMask:CPBorderlessWindowMask]; + self = [super initWithContentRect:CGRectMake(0.0, 0.0, platformWindowWidth, MENUBAR_HEIGHT) styleMask:CPBorderlessWindowMask]; if (self) { diff --git a/AppKit/CPStringDrawing.j b/AppKit/CPStringDrawing.j index 61b4fda70..dec7bde1c 100644 --- a/AppKit/CPStringDrawing.j +++ b/AppKit/CPStringDrawing.j @@ -22,13 +22,9 @@ @import -#include "CoreGraphics/CGGeometry.h" -#include "Platform/Platform.h" +@import "CPPlatformString.j" -var CPStringReferenceElement = nil, - CPStringDefaultFont = nil; - @implementation CPString (CPStringDrawing) /*! @@ -46,75 +42,7 @@ var CPStringReferenceElement = nil, - (CGSize)sizeWithFont:(CPFont)aFont inWidth:(float)aWidth { -#if PLATFORM(DOM) - if (!CPStringReferenceElement) - { - CPStringReferenceElement = document.createElement("span"); - - var style = CPStringReferenceElement.style; - - style.position = "absolute"; - style.whiteSpace = "pre"; - style.visibility = "visible"; - style.padding = "0px"; - style.margin = "0px"; - - style.left = "-100000px"; - style.top = "-100000px"; - style.zIndex = "10000"; - style.background = "red"; - - document.getElementsByTagName("body")[0].appendChild(CPStringReferenceElement); - } - - if (!aFont) - { - if (!CPStringDefaultFont) - CPStringDefaultFont = [CPFont systemFontOfSize:12.0]; - - aFont = CPStringDefaultFont; - } - - var style = CPStringReferenceElement.style; - - if (aWidth === NULL) - { - style.width = ""; - style.whiteSpace = "pre"; - } - - else - { - style.width = ROUND(aWidth) + "px"; - - if (document.attachEvent) - style.wordWrap = "break-word"; - - else - { - style.whiteSpace = "-o-pre-wrap"; - style.whiteSpace = "-pre-wrap"; - style.whiteSpace = "-moz-pre-wrap"; - style.whiteSpace = "pre-wrap"; - } - } - - style.font = [aFont cssString]; - - if (CPFeatureIsCompatible(CPJavascriptInnerTextFeature)) - CPStringReferenceElement.innerText = self; - - else if (CPFeatureIsCompatible(CPJavascriptTextContentFeature)) - CPStringReferenceElement.textContent = self; - - return _CGSizeMake(CPStringReferenceElement.clientWidth, CPStringReferenceElement.clientHeight); -#endif - return _CGSizeMakeZero(); -} - -+ (void)_resetSize -{ - CPStringReferenceElement = nil; + return [CPPlatformString sizeOfString:self withFont:aFont forWidth:aWidth]; } @end diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j index 24ee21442..c80b975b1 100644 --- a/AppKit/CPTextField.j +++ b/AppKit/CPTextField.j @@ -597,7 +597,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder"); //post CPControlTextDidBeginEditingNotification [self textDidBeginEditing:[CPNotification notificationWithName:CPControlTextDidBeginEditingNotification object:self userInfo:nil]]; - [[CPDOMWindowBridge sharedDOMWindowBridge] _propagateCurrentDOMEvent:YES]; + [[[self window] platformWindow] _propagateCurrentDOMEvent:YES]; CPTextFieldInputIsActive = YES; diff --git a/AppKit/CPView.j b/AppKit/CPView.j index 0121e9f6b..289e69fe3 100644 --- a/AppKit/CPView.j +++ b/AppKit/CPView.j @@ -970,7 +970,7 @@ var DOMElementPrototype = nil, { _fullScreenModeState = _CPViewFullScreenModeStateMake(self); - var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPDOMWindowBridge sharedDOMWindowBridge] contentBounds] styleMask:CPBorderlessWindowMask]; + var fullScreenWindow = [[CPWindow alloc] initWithContentRect:[[CPPlatformWindow primaryPlatformWindow] contentBounds] styleMask:CPBorderlessWindowMask]; [fullScreenWindow setLevel:CPScreenSaverWindowLevel]; [fullScreenWindow setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j index 48a9968c6..86a9f5dff 100644 --- a/AppKit/CPWindow/CPWindow.j +++ b/AppKit/CPWindow/CPWindow.j @@ -27,6 +27,7 @@ @import "CGGeometry.j" @import "CPAnimation.j" @import "CPResponder.j" +@import "CPPlatformWindow.j" #include "../Platform/Platform.h" #include "../Platform/DOM/CPDOMDisplayServer.h" @@ -235,6 +236,8 @@ var CPWindowSaveImage = nil, */ @implementation CPWindow : CPResponder { + CPPlatformWindow _platformWindow; + int _windowNumber; unsigned _styleMask; CGRect _frame; @@ -289,7 +292,7 @@ var CPWindowSaveImage = nil, #if PLATFORM(DOM) DOMElement _DOMElement; #endif - CPDOMWindowBridge _bridge; + unsigned _autoresizingMask; BOOL _delegateRespondsToWindowWillReturnUndoManagerSelector; @@ -328,37 +331,13 @@ CPTexturedBackgroundWindowMask @return the initialized window */ - (id)initWithContentRect:(CGRect)aContentRect styleMask:(unsigned int)aStyleMask -{ -#if PLATFORM(DOM) - return [self initWithContentRect:aContentRect styleMask:aStyleMask bridge:[CPDOMWindowBridge sharedDOMWindowBridge]]; -#else - return [self initWithContentRect:aContentRect styleMask:aStyleMask bridge:nil]; -#endif -} - -/*! - Initializes the window. The method also takes a style bit mask made up - of any of the following values: -
-CPBorderlessWindowMask
-CPTitledWindowMask
-CPClosableWindowMask
-CPMiniaturizableWindowMask
-CPResizableWindowMask
-CPTexturedBackgroundWindowMask
-
- @param aContentRect the size and location of the window in screen space - @param aStyleMask a style mask - @param aBridge a DOM-Window bridge object - @return the initialized window - @ignore -*/ -- (id)initWithContentRect:(CGRect)aContentRect styleMask:(unsigned int)aStyleMask bridge:(CPDOMWindowBridge)aBridge { self = [super init]; if (self) { + [self setPlatformWindow:[CPPlatformWindow primaryPlatformWindow]]; + _isFullBridge = NO; _registeredDraggedTypes = [CPSet set]; _registeredDraggedTypesArray = []; @@ -402,8 +381,6 @@ CPTexturedBackgroundWindowMask CPDOMDisplayServerAppendChild(_DOMElement, _windowView._DOMElement); #endif - [self setBridge:aBridge]; - [self setNextResponder:CPApp]; [self setHasShadow:aStyleMask !== CPBorderlessWindowMask]; @@ -418,6 +395,17 @@ CPTexturedBackgroundWindowMask return self; } +- (CPPlatformWindow)platformWindow +{ + return _platformWindow; +} + +- (void)setPlatformWindow:(CPPlatformWindow)aPlatformWindow +{ + // FIXME: already visible. + _platformWindow = aPlatformWindow; +} + /*! @ignore */ @@ -503,7 +491,7 @@ CPTexturedBackgroundWindowMask [self setLevel:CPBackgroundWindowLevel]; [self setHasShadow:NO]; [self setAutoresizingMask:CPWindowWidthSizable | CPWindowHeightSizable]; - [self setFrame:[_bridge visibleFrame]]; + [self setFrame:[_platformWindow usableContentFrame]]; } else { @@ -649,7 +637,8 @@ CPTexturedBackgroundWindowMask */ - (void)orderFront:(id)aSender { - [_bridge order:CPWindowAbove window:self relativeTo:nil]; + [_platformWindow orderFront:self]; + [_platformWindow order:CPWindowAbove window:self relativeTo:nil]; } /* @@ -659,7 +648,7 @@ CPTexturedBackgroundWindowMask */ - (void)orderBack:(id)aSender { - //[_bridge order:CPWindowBelow + //[_platformWindow order:CPWindowBelow } /*! @@ -671,7 +660,7 @@ CPTexturedBackgroundWindowMask if ([_delegate respondsToSelector:@selector(windowWillClose:)]) [_delegate windowWillClose:self]; - [_bridge order:CPWindowOut window:self relativeTo:nil]; + [_platformWindow order:CPWindowOut window:self relativeTo:nil]; if ([CPApp keyWindow] == self) { @@ -688,7 +677,7 @@ CPTexturedBackgroundWindowMask */ - (void)orderWindow:(CPWindowOrderingMode)aPlace relativeTo:(int)otherWindowNumber { - [_bridge order:aPlace window:self relativeTo:CPApp._windows[otherWindowNumber]]; + [_platformWindow order:aPlace window:self relativeTo:CPApp._windows[otherWindowNumber]]; } /*! @@ -1153,9 +1142,9 @@ CPTexturedBackgroundWindowMask - (void)center { var size = [self frame].size, - bridgeSize = [_bridge contentBounds].size; + containerSize = [_platformWindow contentBounds].size; - [self setFrameOrigin:CGPointMake((bridgeSize.width - size.width) / 2.0, (bridgeSize.height - size.height) / 2.0)]; + [self setFrameOrigin:CGPointMake((containerSize.width - size.width) / 2.0, (containerSize.height - size.height) / 2.0)]; } /*! @@ -1640,7 +1629,7 @@ CPTexturedBackgroundWindowMask [_windowView noteToolbarChanged]; if (_isFullBridge) - newFrame = [_bridge visibleFrame]; + newFrame = [_platformWindow usableContentFrame]; else { newFrame = CGRectMakeCopy([self frame]); @@ -1707,7 +1696,7 @@ CPTexturedBackgroundWindowMask [self _setAttachedSheetFrameOrigin]; // Place this window above ourselves. - [_bridge order:CPWindowAbove window:aSheet relativeTo:self]; + [_platformWindow order:CPWindowAbove window:aSheet relativeTo:self]; } /*! @@ -1928,39 +1917,18 @@ var keyViewComparator = function(a, b, context) @implementation CPWindow (BridgeSupport) -/* - Sets the DOM-Window bridge for this window. - @ignore -*/ -- (void)setBridge:(CPDOMWindowBridge)aBridge -{ - if (_bridge == aBridge) - return; - - if (_bridge) - { - [self orderOut:self]; - // FIXME: If the bridge changes, then we have to recreate all of our subviews' DOM Elements. - } - - _bridge = aBridge; - - if ([self isFullBridge]) - [self setFrame:[aBridge contentBounds]]; -} - /* @ignore */ - (void)resizeWithOldBridgeSize:(CGSize)aSize { if ([self isFullBridge]) - return [self setFrame:[_bridge visibleFrame]]; + return [self setFrame:[_platformWindow usableContentFrame]]; if (_autoresizingMask == CPWindowNotSizable) return; - var frame = [_bridge contentBounds], + var frame = [_platformWindow contentBounds], newFrame = CGRectMakeCopy(_frame), dX = (CGRectGetWidth(frame) - aSize.width) / (((_autoresizingMask & CPWindowMinXMargin) ? 1 : 0) + (_autoresizingMask & CPWindowWidthSizable ? 1 : 0) + (_autoresizingMask & CPWindowMaxXMargin ? 1 : 0)), @@ -2118,5 +2086,4 @@ function _CPWindowFullBridgeSessionMake(aWindowView, aContentRect, hasShadow, aL @import "_CPBorderlessWindowView.j" @import "_CPBorderlessBridgeWindowView.j" @import "CPDragServer.j" -@import "CPDOMWindowBridge.j" @import "CPView.j" diff --git a/AppKit/CPWindow/_CPWindowView.j b/AppKit/CPWindow/_CPWindowView.j index 7bbfaf22b..fe2d1ca4d 100644 --- a/AppKit/CPWindow/_CPWindowView.j +++ b/AppKit/CPWindow/_CPWindowView.j @@ -145,7 +145,7 @@ var _CPWindowViewResizeIndicatorImage = nil; var visibleFrame = _cachedScreenFrame; if (!visibleFrame) - visibleFrame = [[CPDOMWindowBridge sharedDOMWindowBridge] visibleFrame]; + visibleFrame = [[CPPlatformWindow primaryPlatformWindow] usableContentFrame]; var restrictedPoint = CGPointMake(0, 0); @@ -167,7 +167,7 @@ var _CPWindowViewResizeIndicatorImage = nil; else if (type === CPLeftMouseDown) { _mouseDraggedPoint = [[self window] convertBaseToBridge:[anEvent locationInWindow]]; - _cachedScreenFrame = [[CPDOMWindowBridge sharedDOMWindowBridge] visibleFrame]; + _cachedScreenFrame = [[CPPlatformWindow primaryPlatformWindow] usableContentFrame]; } else if (type === CPLeftMouseDragged) { diff --git a/AppKit/Platform/CPPlatform.j b/AppKit/Platform/CPPlatform.j new file mode 100644 index 000000000..8a75b53ab --- /dev/null +++ b/AppKit/Platform/CPPlatform.j @@ -0,0 +1,27 @@ + +@import + +#include "Platform.h" + + +@implementation CPPlatform : CPObject +{ +} + ++ (void)bootstrap +{ +#if PLATFORM(DOM) + var body = document.getElementsByTagName("body")[0]; + + body.innerHTML = ""; // Get rid of anything that might be lingering in the body element. + body.style.overflow = "hidden"; + + if (document.documentElement) + document.documentElement.style.overflow = "hidden"; +#endif + + [CPPlatformString bootstrap]; + [CPPlatformWindow setPrimaryPlatformWindow:[[CPPlatformWindow alloc] _init]]; +} + +@end diff --git a/AppKit/Platform/CPPlatformString.j b/AppKit/Platform/CPPlatformString.j new file mode 100644 index 000000000..24e91d152 --- /dev/null +++ b/AppKit/Platform/CPPlatformString.j @@ -0,0 +1,104 @@ + + +@import + +#include "../CoreGraphics/CGGeometry.h" +#include "Platform.h" + + +#if PLATFORM(DOM) +var DOMIFrameElement = nil, + DOMSpanElement = nil, + DefaultFont = nil; +#endif + +@implementation CPPlatformString : CPObject +{ +} + ++ (void)bootstrap +{ +#if PLATFORM(DOM) + DOMIFrameElement = document.createElement("iframe"); + + DOMIFrameElement.name = name = "iframe_" + FLOOR(RAND() * 10000); + DOMIFrameElement.style.position = "absolute"; + DOMIFrameElement.style.left = "-100px"; + DOMIFrameElement.style.top = "-100px"; + DOMIFrameElement.style.width = "1px"; + DOMIFrameElement.style.height = "1px"; + DOMIFrameElement.style.borderWidth = "0px"; + DOMIFrameElement.style.background = "blue"; + DOMIFrameElement.style.overflow = "hidden"; + DOMIFrameElement.style.zIndex = 100000000000; + + document.getElementsByTagName("body")[0].appendChild(DOMIFrameElement); + + var DOMIFrameDocument = (DOMIFrameElement.contentDocument || DOMIFrameElement.contentWindow.document); + + DOMIFrameDocument.write(""); + DOMIFrameDocument.close(); + + DOMSpanElement = DOMIFrameDocument.createElement("span"); + + DOMSpanElement.style.position = "absolute"; + DOMSpanElement.style.whiteSpace = "pre"; + DOMSpanElement.style.visibility = "visible"; + DOMSpanElement.style.padding = "0px"; + DOMSpanElement.style.margin = "0px"; + DOMSpanElement.style.background = "red"; + + DOMIFrameDocument.getElementsByTagName("body")[0].appendChild(DOMSpanElement); +#endif +} + ++ (CGSize)sizeOfString:(CPString)aString withFont:(CPFont)aFont forWidth:(float)aWidth +{ +#if PLATFORM(DOM) + if (!aFont) + { + if (!DefaultFont) + DefaultFont = [CPFont systemFontOfSize:12.0]; + + aFont = DefaultFont; + } + + var style = DOMSpanElement.style; + + if (aWidth === NULL) + { + style.width = ""; + style.whiteSpace = "pre"; + } + + else + { + style.width = ROUND(aWidth) + "px"; + + if (document.attachEvent) + style.wordWrap = "break-word"; + + else + { + style.whiteSpace = "-o-pre-wrap"; + style.whiteSpace = "-pre-wrap"; + style.whiteSpace = "-moz-pre-wrap"; + style.whiteSpace = "pre-wrap"; + } + } + + style.font = [aFont cssString]; + + if (CPFeatureIsCompatible(CPJavascriptInnerTextFeature)) + DOMSpanElement.innerText = aString; + + else if (CPFeatureIsCompatible(CPJavascriptTextContentFeature)) + DOMSpanElement.textContent = aString; + + return _CGSizeMake(DOMSpanElement.clientWidth, DOMSpanElement.clientHeight); +#else + return _CGSizeMakeZero(); +#endif +} + +@end diff --git a/AppKit/Platform/CPPlatformWindow.j b/AppKit/Platform/CPPlatformWindow.j new file mode 100644 index 000000000..1aec29a90 --- /dev/null +++ b/AppKit/Platform/CPPlatformWindow.j @@ -0,0 +1,168 @@ + +@import + +#import "Platform.h" +#import "../CoreGraphics/CGGeometry.h" + + +var PrimaryPlatformWindow = NULL; + +@implementation CPPlatformWindow : CPObject +{ + CGRect _contentRect; + +#if PLATFORM(DOM) + DOMWindow _DOMWindow; + + DOMElement _DOMBodyElement; + DOMElement _DOMFocusElement; + + CPArray _windowLevels; + CPDictionary _windowLayers; + + BOOL _mouseIsDown; + CPWindow _mouseDownWindow; + CPTimeInterval _lastMouseUp; + CPTimeInterval _lastMouseDown; + + Object _charCodes; + unsigned _keyCode; + + BOOL _DOMEventMode; + + // Native Pasteboard Support + DOMElement _DOMPasteboardElement; + CPEvent _pasteboardKeyDownEvent; + + CPString _overriddenEventType; +#endif +} + ++ (CPPlatformWindow)primaryPlatformWindow +{ + return PrimaryPlatformWindow; +} + ++ (void)setPrimaryPlatformWindow:(CPPlatformWindow)aPlatformWindow +{ + PrimaryPlatformWindow = aPlatformWindow; +} + +- (id)initWithContentRect:(CGRect)aRect +{ + self = [super init]; + + if (self) + { + _contentRect = _CGRectMakeCopy(aRect); + +#if PLATFORM(DOM) + _windowLevels = []; + _windowLayers = [CPDictionary dictionary]; + + _charCodes = {}; +#endif + } + + return self; +} + +- (id)init +{ + return [self initWithContentRect:_CGRectMake(0.0, 0.0, 400.0, 500.0)]; +} + +- (CGRect)contentRect +{ + return _CGRectMakeCopy(_contentRect); +} + +- (CGRect)contentBounds +{ + var contentBounds = [self contentRect]; + + contentBounds.origin = _CGPointMakeZero(); + + return contentBounds; +} + +- (void)usableContentFrame +{ + var frame = [self contentBounds]; + + frame.origin = CGPointMakeZero(); + + if ([CPMenu menuBarVisible]) + { + var menuBarHeight = [[CPApp mainMenu] menuBarHeight]; + + frame.origin.y += menuBarHeight; + frame.size.height -= menuBarHeight; + } + + return frame; +} + +- (void)setContentRect:(CGRect)aRect +{ + if (!aRect || _CGRectEqualToRect(_contentRect, aRect)) + return; + + [self setContentOrigin:aRect.origin]; + [self setContentSize:aRect.size]; +} + +- (void)setContentOrigin:(CGPoint)aPoint +{ + var origin = _contentRect.origin; + + if (!aPoint || _CGPointEqualToPoint(origin, aPoint)) + return; + + origin.x = aPoint.x; + origin.y = aPoint.y; + + [self updateNativeContentOrigin]; +} + +- (void)setContentSize:(CGSize)aSize +{ + var size = _contentRect.size; + + if (!aSize || _CGSizeEqualToSize(size, aSize)) + return; + + var delta = _CGSizeMake(aSize.width - size.width, aSize.height - size.height); + + size.width = aSize.width; + size.height = aSize.height; + + [self updateNativeContentSize]; +} + +- (void)updateFromNativeContentRect +{ + [self setContentRect:[self nativeContentRect]]; +} +/* +- (BOOL)isVisible +{ + return NO; +} + +/* ++ (BOOL)supportsMultipleWindows +{ +#if PLATFORM(BROWSER) + return YES; +#else + return NO; +#endif +} +*/ + +@end + +#if PLATFORM(BROWSER) +@import "CPPlatformWindow+DOM.j" +#endif diff --git a/AppKit/Platform/DOM/CPDOMWindowBridge.j b/AppKit/Platform/DOM/CPDOMWindowBridge.j deleted file mode 100644 index 6b6c57d29..000000000 --- a/AppKit/Platform/DOM/CPDOMWindowBridge.j +++ /dev/null @@ -1,1060 +0,0 @@ -/* - * CPDOMWindowBridge.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 -@import - -@import "CPEvent.j" -@import "CPCompatibility.j" - -@import "CPDOMWindowLayer.j" - -#import "../../CoreGraphics/CGGeometry.h" - - -CPSharedDOMWindowBridge = nil; - -var ExcludedDOMElements = []; - -// Define up here so compressor knows about em. -var CPDOMWindowGetFrame, - CPDOMEventGetClickCount, - CPDOMEventStop; - -@implementation CPDOMWindowBridge : CPObject -{ - CPArray _orderedWindows; - CPWindow _mouseDownWindow; - - DOMWindow _DOMWindow; - DOMElement _DOMBodyElement; - DOMElement _DOMFocusElement; - - CPArray _windowLevels; - CPDictionary _windowLayers; - - CPRect _frame; - CPRect _contentBounds; - - BOOL _mouseIsDown; - CPTimeInterval _lastMouseUp; - CPTimeInterval _lastMouseDown; - - JSObject _charCodes; - unsigned _keyCode; - - BOOL _DOMEventMode; - - // Native Pasteboard Support - DOMElement _DOMPasteboardElement; - CPEvent _pasteboardKeyDownEvent; - - CPString _overriddenEventType; -} - -/*! - Returns the shared DOMWindowBridge. -*/ -+ (id)sharedDOMWindowBridge -{ - if (!CPSharedDOMWindowBridge) - CPSharedDOMWindowBridge = [[CPDOMWindowBridge alloc] _initWithDOMWindow:window]; - - return CPSharedDOMWindowBridge; -} - -- (id)initWithFrame:(CPRect)aFrame -{ - alert("unimplemented"); -} - -/* @ignore */ -- (id)_initWithDOMWindow:(DOMWindow)aDOMWindow -{ - self = [super init]; - - if (self) - { - _DOMWindow = aDOMWindow; - - _windowLevels = []; - _windowLayers = [CPDictionary dictionary]; - - // Do this before getting the frame of the window, because if not it will be wrong in IE. - _DOMBodyElement = document.getElementsByTagName("body")[0]; - _DOMBodyElement.innerHTML = ""; // Get rid of anything that might be lingering in the body element. - _DOMBodyElement.style.overflow = "hidden"; - _DOMBodyElement.style.webkitTouchCallout = "none"; - - [CPString _resetSize]; - - if (document.documentElement) - document.documentElement.style.overflow = "hidden"; - - _frame = CPDOMWindowGetFrame(_DOMWindow); - _contentBounds = CGRectMake(0.0, 0.0, CPRectGetWidth(_frame), CPRectGetHeight(_frame)); - - _DOMFocusElement = document.createElement("input"); - _DOMFocusElement.style.position = "absolute"; - _DOMFocusElement.style.zIndex = "-1000"; - _DOMFocusElement.style.opacity = "0"; - _DOMFocusElement.style.filter = "alpha(opacity=0)"; - _DOMBodyElement.appendChild(_DOMFocusElement); - - // Create Native Pasteboard handler. - _DOMPasteboardElement = document.createElement("input"); - _DOMPasteboardElement.style.position = "absolute"; - _DOMPasteboardElement.style.top = "-10000px"; - _DOMPasteboardElement.style.zIndex = "99"; - - _DOMBodyElement.appendChild(_DOMPasteboardElement); - - // Make sure the pastboard element is blurred. - _DOMPasteboardElement.blur(); - - _charCodes = {}; - - // - var theClass = [self class], - - keyEventSelector = @selector(_bridgeKeyEvent:), - keyEventImplementation = class_getMethodImplementation(theClass, keyEventSelector), - keyEventCallback = function (anEvent) { keyEventImplementation(self, nil, anEvent); }, - - mouseEventSelector = @selector(_bridgeMouseEvent:), - mouseEventImplementation = class_getMethodImplementation(theClass, mouseEventSelector), - mouseEventCallback = function (anEvent) { mouseEventImplementation(self, nil, anEvent); }, - - scrollEventSelector = @selector(_bridgeScrollEvent:), - scrollEventImplementation = class_getMethodImplementation(theClass, scrollEventSelector), - scrollEventCallback = function (anEvent) { scrollEventImplementation(self, nil, anEvent); }, - - resizeEventSelector = @selector(_bridgeResizeEvent:), - resizeEventImplementation = class_getMethodImplementation(theClass, resizeEventSelector), - resizeEventCallback = function (anEvent) { resizeEventImplementation(self, nil, anEvent); }, - - touchEventSelector = @selector(_bridgeTouchEvent:), - touchEventImplementation = class_getMethodImplementation(theClass, touchEventSelector), - touchEventCallback = function (anEvent) { touchEventImplementation(self, nil, anEvent); }, - - theDocument = _DOMWindow.document; - - if (document.addEventListener) - { - _DOMWindow.addEventListener("resize", resizeEventCallback, NO); - - theDocument.addEventListener(CPDOMEventMouseUp, mouseEventCallback, NO); - theDocument.addEventListener(CPDOMEventMouseDown, mouseEventCallback, NO); - theDocument.addEventListener(CPDOMEventMouseMoved, mouseEventCallback, NO); - - theDocument.addEventListener(CPDOMEventKeyUp, keyEventCallback, NO); - theDocument.addEventListener(CPDOMEventKeyDown, keyEventCallback, NO); - theDocument.addEventListener(CPDOMEventKeyPress, keyEventCallback, NO); - - - theDocument.addEventListener(CPDOMEventTouchStart, touchEventCallback, NO); - theDocument.addEventListener(CPDOMEventTouchEnd, touchEventCallback, NO); - theDocument.addEventListener(CPDOMEventTouchMove, touchEventCallback, NO); - theDocument.addEventListener(CPDOMEventTouchCancel, touchEventCallback, NO); - - //FIXME: does firefox really need a different value? - _DOMWindow.addEventListener("DOMMouseScroll", scrollEventCallback, NO); - _DOMWindow.addEventListener(CPDOMEventScrollWheel, scrollEventCallback, NO); - } - else if(document.attachEvent) - { - _DOMWindow.attachEvent("onresize", resizeEventCallback); - - theDocument.attachEvent("on" + CPDOMEventMouseUp, mouseEventCallback); - theDocument.attachEvent("on" + CPDOMEventMouseDown, mouseEventCallback); - theDocument.attachEvent("on" + CPDOMEventMouseMoved, mouseEventCallback); - theDocument.attachEvent("on" + CPDOMEventDoubleClick, mouseEventCallback); - - theDocument.attachEvent("on" + CPDOMEventKeyUp, keyEventCallback); - theDocument.attachEvent("on" + CPDOMEventKeyDown, keyEventCallback); - theDocument.attachEvent("on" + CPDOMEventKeyPress, keyEventCallback); - - _DOMWindow.onmousewheel = scrollEventCallback; - theDocument.onmousewheel = scrollEventCallback; - - theDocument.body.ondrag = function () { return NO; }; - theDocument.body.onselectstart = function () { return window.event.srcElement == _DOMPasteboardElement; }; - } - - ExcludedDOMElements["INPUT"] = YES; - ExcludedDOMElements["SELECT"] = YES; - ExcludedDOMElements["TEXTAREA"] = YES; - ExcludedDOMElements["OPTION"] = YES; - } - - return self; -} - -- (CPRect)frame -{ - return CGRectMakeCopy(_frame); -} - -- (CGRect)visibleFrame -{ - var frame = [self frame]; - - frame.origin = CGPointMakeZero(); - - if ([CPMenu menuBarVisible]) - { - var menuBarHeight = [[CPApp mainMenu] menuBarHeight]; - - frame.origin.y += menuBarHeight; - frame.size.height -= menuBarHeight; - } - - return frame; -} - -- (CPRect)contentBounds -{ - return CPRectCreateCopy(_contentBounds); -} - -- (CPArray)orderedWindowsAtLevel:(int)aLevel -{ - var layer = [self layerAtLevel:aLevel create:NO]; - - if (!layer) - return []; - - return [layer orderedWindows]; -} - -- (CPDOMWindowLayer)layerAtLevel:(int)aLevel create:(BOOL)aFlag -{ - var layer = [_windowLayers objectForKey:aLevel]; - - // If the layer doesn't currently exist, and the create flag is true, - // create the layer. - if (!layer && aFlag) - { - layer = [[CPDOMWindowLayer alloc] initWithLevel:aLevel]; - - [_windowLayers setObject:layer forKey:aLevel]; - - // Find the nearest layer. This is similar to a binary search, - // only we know we won't find the value. - var low = 0, - high = _windowLevels.length - 1, - middle; - - while (low <= high) - { - middle = FLOOR((low + high) / 2); - - if (_windowLevels[middle] > aLevel) - high = middle - 1; - else - low = middle + 1; - } - - [_windowLevels insertObject:aLevel atIndex:_windowLevels[middle] > aLevel ? middle : middle + 1]; - layer._DOMElement.style.zIndex = aLevel; - _DOMBodyElement.appendChild(layer._DOMElement); - } - - return layer; -} - -- (void)order:(CPWindowOrderingMode)aPlace window:(CPWindow)aWindow relativeTo:(CPWindow)otherWindow -{ - // Grab the appropriate level for the layer, and create it if - // necessary (if we are not simply removing the window). - var layer = [self layerAtLevel:[aWindow level] create:aPlace != CPWindowOut]; - - // Ignore otherWindow, simply remove this window from it's level. - // If layer is nil, this will be a no-op. - if (aPlace == CPWindowOut) - return [layer removeWindow:aWindow]; - - // Place the window at the appropriate index. - [layer insertWindow:aWindow atIndex:(otherWindow ? (aPlace == CPWindowAbove ? otherWindow._index + 1 : otherWindow._index) : CPNotFound)]; -} - -/* @ignore */ -- (id)_dragHitTest:(CPPoint)aPoint pasteboard:(CPPasteboard)aPasteboard -{ - var levels = _windowLevels, - layers = _windowLayers, - levelCount = levels.length; - - while (levelCount--) - { - // Skip any windows above or at the dragging level. - if (levels[levelCount] >= CPDraggingWindowLevel) - continue; - - var windows = [layers objectForKey:levels[levelCount]]._windows, - windowCount = windows.length; - - while (windowCount--) - { - var theWindow = windows[windowCount]; - - if ([theWindow containsPoint:aPoint]) - return [theWindow _dragHitTest:aPoint pasteboard:aPasteboard]; - } - } - - return nil; -} - -/* @ignore */ -- (void)_propagateCurrentDOMEvent:(BOOL)aFlag -{ - StopDOMEventPropagation = !aFlag; -} - -- (CPWindow)hitTest:(CPPoint)location -{ - var levels = _windowLevels, - layers = _windowLayers, - levelCount = levels.length, - theWindow = nil; - - while (levelCount-- && !theWindow) - { - var windows = [layers objectForKey:levels[levelCount]]._windows, - windowCount = windows.length; - - while (windowCount-- && !theWindow) - { - var candidateWindow = windows[windowCount]; - - if (!candidateWindow._ignoresMouseEvents && [candidateWindow containsPoint:location]) - theWindow = candidateWindow; - } - } - - return theWindow; -} - -@end - -var CPDOMWindowGetFrame = function(_DOMWindow) -{ - var frame = nil;//CGRectMakeZero(); - - // We will rarely be able to get all this information, but we do the best we can: - if (_DOMWindow.outerWidth) - frame = CGRectMake(0, 0, _DOMWindow.outerWidth, _DOMWindow.outerHeight); - - else /*if(self.outerWidth)*/ - frame = CGRectMake(0, 0, -1, -1); - - if (window.screenTop) - frame.origin = CGPointMake(_DOMWindow.screenLeft, _DOMWindow.screenTop, 0); - - else if (window.screenX) - frame.origin = CGPointMake(_DOMWindow.screenX, _DOMWindow.screenY, 0); - - // Safari, Mozilla, Firefox, and Opera - if (_DOMWindow.innerWidth) - frame.size = CGSizeMake(_DOMWindow.innerWidth, _DOMWindow.innerHeight); - - // Internet Explorer 6 in Strict Mode - else if (document.documentElement && document.documentElement.clientWidth) - frame.size = CGSizeMake(_DOMWindow.document.documentElement.clientWidth, _DOMWindow.document.documentElement.clientHeight); - - // Internet Explorer X - else - frame.size = CGSizeMake(_DOMWindow.document.body.clientWidth, _DOMWindow.document.body.clientHeight); - - return frame; -} - -//right now we hard code q, w, r and t as keys to propogate -//these aren't normal keycodes, they are with modifier key codes -//might be mac only, we should investigate futher later. -var KeyCodesToPrevent = {}, - CharacterKeysToPrevent = {}, - KeyCodesWithoutKeyPressEvents = { '8':1, '9':1, '16':1, '37':1, '38':1, '39':1, '40':1, '46':1, '33':1, '34':1 }; - -var CTRL_KEY_CODE = 17; - -@implementation CPDOMWindowBridge (Events) - -/*! - When using command (mac) or control (windows), keys are propagated to the browser by default. - To prevent a character key from propagating (to prevent its default action, and instead use it - in your own application), use these methods. These methods are additive -- the list builds until you clear it. - - @param characters a list of characters to stop propagating keypresses to the browser. -*/ -- (void)preventCharacterKeysFromPropagating:(CPArray)characters -{ - for(var i=characters.length; i>0; i--) - CharacterKeysToPrevent[""+characters[i-1].toLowerCase()] = YES; -} - -/*! - @param character a character to stop propagating keypresses to the browser. -*/ -- (void)preventCharacterKeyFromPropagating:(CPString)character -{ - CharacterKeysToPrevent[character.toLowerCase()] = YES; -} - -/*! - Clear the list of characters for which we are not sending keypresses to the browser. -*/ -- (void)clearCharacterKeysToPreventFromPropagating -{ - CharacterKeysToPrevent = {}; -} - -/*! - Prevent these keyCodes from sending their keypresses to the browser. - @param keyCodes an array of keycodes to prevent propagation. -*/ -- (void)preventKeyCodesFromPropagating:(CPArray)keyCodes -{ - for(var i=keyCodes.length; i>0; i--) - KeyCodesToPrevent[keyCodes[i-1]] = YES; -} - -/*! - Prevent this keyCode from sending its key events to the browser. - @param keyCode a keycode to prevent propagation. -*/ -- (void)preventKeyCodeFromPropagating:(CPString)keyCode -{ - KeyCodesToPrevent[keyCode] = YES; -} - -/*! - Clear the list of keyCodes for which we are not sending keypresses to the browser. -*/ -- (void)clearKeyCodesToPreventFromPropagating -{ - KeyCodesToPrevent = {}; -} - -/* @ignore */ -- (void)_bridgeMouseEvent:(DOMEvent)aDOMEvent -{ - var theType = _overriddenEventType || aDOMEvent.type; - - // IE's event order is down, up, up, dblclick, so we have create these events artificially. - if (theType === CPDOMEventDoubleClick) - { - _overriddenEventType = CPDOMEventMouseDown; - [self _bridgeMouseEvent:aDOMEvent]; - - _overriddenEventType = CPDOMEventMouseUp; - [self _bridgeMouseEvent:aDOMEvent]; - - _overriddenEventType = nil; - - return; - } - -#ifndef DEBUG - try - { -#endif - var event, - location = _CGPointMake(aDOMEvent.clientX, aDOMEvent.clientY), - timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), - sourceElement = (aDOMEvent.target || aDOMEvent.srcElement), - windowNumber = 0, - modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | - (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | - (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | - (aDOMEvent.metaKey ? CPCommandKeyMask : 0); - - StopDOMEventPropagation = YES; - - if (_mouseDownWindow) - windowNumber = [_mouseDownWindow windowNumber]; - else - { - var theWindow = [self hitTest:location]; - - if ((aDOMEvent.type === CPDOMEventMouseDown) && theWindow) - _mouseDownWindow = theWindow; - - windowNumber = [theWindow windowNumber]; - } - - if (windowNumber) - { - var windowFrame = CPApp._windows[windowNumber]._frame; - - location.x -= _CGRectGetMinX(windowFrame); - location.y -= _CGRectGetMinY(windowFrame); - } - - switch (theType) - { - case CPDOMEventMouseUp: if(_mouseIsDown) - { - event = _CPEventFromNativeMouseEvent(aDOMEvent, CPLeftMouseUp, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseUp, timestamp, location), 0); - - _mouseIsDown = NO; - _lastMouseUp = event; - _mouseDownWindow = nil; - } - - if(_DOMEventMode) - { - _DOMEventMode = NO; - return; - } - - break; - - case CPDOMEventMouseDown: if (ExcludedDOMElements[sourceElement.tagName] && sourceElement != _DOMFocusElement) - { - _DOMEventMode = YES; - _mouseIsDown = YES; - - //fake a down and up event so that event tracking mode will work correctly - [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseDown location:location modifierFlags:modifierFlags - timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 - clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]]; - - [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseUp location:location modifierFlags:modifierFlags - timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 - clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]]; - - return; - } - - event = _CPEventFromNativeMouseEvent(aDOMEvent, CPLeftMouseDown, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseDown, timestamp, location), 0); - - _mouseIsDown = YES; - _lastMouseDown = event; - - break; - - case CPDOMEventMouseMoved: if (_DOMEventMode) - return; - - event = _CPEventFromNativeMouseEvent(aDOMEvent, _mouseIsDown ? CPLeftMouseDragged : CPMouseMoved, location, modifierFlags, timestamp, windowNumber, nil, -1, 1, 0); - - break; - } - - if (event) - { - event._DOMEvent = aDOMEvent; - - [CPApp sendEvent:event]; - } - - if (StopDOMEventPropagation) - CPDOMEventStop(aDOMEvent); - - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; -#ifndef DEBUG - } - catch (anException) - { - objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); - } -#endif -} - -/* @ignore */ -- (void)_bridgeKeyEvent:(DOMEvent)aDOMEvent -{ -#ifndef DEBUG - try - { -#endif - var event, - timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), - sourceElement = (aDOMEvent.target || aDOMEvent.srcElement), - windowNumber = [[CPApp keyWindow] windowNumber], - modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | - (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | - (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | - (aDOMEvent.metaKey ? CPCommandKeyMask : 0); - - if (ExcludedDOMElements[sourceElement.tagName] && sourceElement != _DOMFocusElement && sourceElement != _DOMPasteboardElement) - return; - - //We want to stop propagation if this is a command key AND this character or keycode has been added to our blacklist - StopDOMEventPropagation = !(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) || - CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] || - KeyCodesToPrevent[aDOMEvent.keyCode]; - - var isNativePasteEvent = NO, - isNativeCopyOrCutEvent = NO; - - switch (aDOMEvent.type) - { - case CPDOMEventKeyDown: // Grab and store the keycode now since it is correct and consistent at this point. - _keyCode = aDOMEvent.keyCode; - - var characters = String.fromCharCode(_keyCode).toLowerCase(); - - // If this could be a native PASTE event, then we need to further examine it before - // sending a CPEvent. Select our element to see if anything gets pasted in it. - if (characters == "v" && (modifierFlags & CPPlatformActionKeyMask)) - { - _DOMPasteboardElement.select(); - _DOMPasteboardElement.value = ""; - - isNativePasteEvent = YES; - } - - // Normally we return now because we let keypress send the actual CPEvent keyDown event, since we don't have - // a complete set of information yet. - - // However, of this could be a native COPY event, we need to let the normal event-process take place so it - // can capture our internal Cappuccino pasteboard. - else if ((characters == "c" || characters == "x") && (modifierFlags & CPPlatformActionKeyMask)) - isNativeCopyOrCutEvent = YES; - - // Also, certain browsers (IE and Safari), have broken keyboard supportwhere they don't send keypresses for certain events. - // So, allow the keypress event to handle the event if we are not a browser with broken (remedial) key support... - else if (!CPFeatureIsCompatible(CPJavascriptRemedialKeySupport)) - return; - - // Or, if this is not one of those special keycodes, and also not a ctrl+event - else if (!KeyCodesWithoutKeyPressEvents[_keyCode] && (_keyCode == CTRL_KEY_CODE || !(modifierFlags & CPControlKeyMask))) - return; - - // If this is in fact our broke state, continue to keypress and send the keydown. - case CPDOMEventKeyPress: - // If the source of this event is our pasteboard element, then simply let it continue - // as normal, so that the paste event can successfully complete. - if ((aDOMEvent.target || aDOMEvent.srcElement) == _DOMPasteboardElement) - return; - - var keyCode = _keyCode, - charCode = aDOMEvent.keyCode || aDOMEvent.charCode, - isARepeat = (_charCodes[keyCode] != nil); - - _charCodes[keyCode] = charCode; - - var characters = String.fromCharCode(charCode), - charactersIgnoringModifiers = characters.toLowerCase(); - - event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags - timestamp:timestamp windowNumber:windowNumber context:nil - characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:keyCode]; - - if (isNativePasteEvent) - { - _pasteboardKeyDownEvent = event; - - window.setNativeTimeout(function () { [self _checkPasteboardElement] }, 0); - - return; - } - - break; - - case CPDOMEventKeyUp: var keyCode = aDOMEvent.keyCode, - charCode = _charCodes[keyCode]; - - _charCodes[keyCode] = nil; - - var characters = String.fromCharCode(charCode), - charactersIgnoringModifiers = characters.toLowerCase(); - - if (!(modifierFlags & CPShiftKeyMask)) - characters = charactersIgnoringModifiers; - - event = [CPEvent keyEventWithType:CPKeyUp location:location modifierFlags:modifierFlags - timestamp: timestamp windowNumber:windowNumber context:nil - characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:NO keyCode:keyCode]; - break; - } - - if (event) - { - event._DOMEvent = aDOMEvent; - - [CPApp sendEvent:event]; - - if (isNativeCopyOrCutEvent) - { - var pasteboard = [CPPasteboard generalPasteboard], - types = [pasteboard types]; - - // If this is a native copy event, then check if the pasteboard has anything in it. - if (types.length) - { - if ([types indexOfObjectIdenticalTo:CPStringPboardType] != CPNotFound) - _DOMPasteboardElement.value = [pasteboard stringForType:CPStringPboardType]; - else - _DOMPasteboardElement.value = [pasteboard _generateStateUID]; - - _DOMPasteboardElement.select(); - - window.setNativeTimeout(function() { [self _clearPasteboardElement]; }, 0); - } - - return; - } - } - - if (StopDOMEventPropagation) - CPDOMEventStop(aDOMEvent); - - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; -#ifndef DEBUG - } - catch (anException) - { - objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); - } -#endif -} - -/* @ignore */ -- (void)_bridgeScrollEvent:(DOMEvent)aDOMEvent -{ - if(!aDOMEvent) - aDOMEvent = window.event; - -#ifndef DEBUG - try - { -#endif - if (CPFeatureIsCompatible(CPJavaScriptMouseWheelValues_8_15)) - { - var x = 0.0, - y = 0.0, - element = aDOMEvent.target; - - while (element.nodeType !== 1) - element = element.parentNode; - - if (element.offsetParent) - { - do - { - x += element.offsetLeft; - y += element.offsetTop; - - } while (element = element.offsetParent); - } - - var location = _CGPointMake((x + ((aDOMEvent.clientX - 8) / 15)), (y + ((aDOMEvent.clientY - 8) / 15)));} - else - var location = _CGPointMake(aDOMEvent.clientX, aDOMEvent.clientY); - - var deltaX = 0.0, - deltaY = 0.0, - windowNumber = 0, - timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), - modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | - (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | - (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | - (aDOMEvent.metaKey ? CPCommandKeyMask : 0); - - StopDOMEventPropagation = YES; - - windowNumber = [[self hitTest:location] windowNumber]; - - if (!windowNumber) - return; - - var windowFrame = CPApp._windows[windowNumber]._frame; - - location.x -= CGRectGetMinX(windowFrame); - location.y -= CGRectGetMinY(windowFrame); - - if(typeof aDOMEvent.wheelDeltaX != "undefined") - { - deltaX = aDOMEvent.wheelDeltaX / 120.0; - deltaY = aDOMEvent.wheelDeltaY / 120.0; - } - - else if (aDOMEvent.wheelDelta) - deltaY = aDOMEvent.wheelDelta / 120.0; - - else if (aDOMEvent.detail) - deltaY = -aDOMEvent.detail / 3.0; - - else - return; - - if(!CPFeatureIsCompatible(CPJavaScriptNegativeMouseWheelValues)) - { - deltaX = -deltaX; - deltaY = -deltaY; - } - - var event = [CPEvent mouseEventWithType:CPScrollWheel location:location modifierFlags:modifierFlags - timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0 ]; - - event._DOMEvent = aDOMEvent; - event._deltaX = deltaX; - event._deltaY = deltaY; - - [CPApp sendEvent:event]; - - if (StopDOMEventPropagation) - CPDOMEventStop(aDOMEvent); - - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; -#ifndef DEBUG - } - catch (anException) - { - objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); - } -#endif -} - -/* @ignore */ -- (void)_bridgeResizeEvent:(DOMEvent)aDOMEvent -{ -#ifndef DEBUG - try - { -#endif - // FIXME: This is not the right way to do this. - // We should pay attention to mouse down and mouse up in conjunction with this. - //window.liveResize = YES; - - var oldSize = _frame.size; - - // window.liveResize = YES? - _frame = CPDOMWindowGetFrame(_DOMWindow); - _contentBounds.size = CGSizeCreateCopy(_frame.size); - - var levels = _windowLevels, - layers = _windowLayers, - levelCount = levels.length; - - while (levelCount--) - { - var windows = [layers objectForKey:levels[levelCount]]._windows, - windowCount = windows.length; - - while (windowCount--) - [windows[windowCount] resizeWithOldBridgeSize:oldSize]; - } - - //window.liveResize = NO; - - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; -#ifndef DEBUG - } - catch (anException) - { - objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); - } -#endif -} - -// DOM event properties used in mouse bridge code - -// type -// clientX -// clientY -// timestamp -// target/srcElement -// shiftKey,ctrlKey,altKey,metaKey - -/* @ignore */ -- (void)_bridgeTouchEvent:(DOMEvent)aDOMEvent -{ -#ifndef DEBUG - try - { -#endif - if (aDOMEvent.touches && (aDOMEvent.touches.length == 1 || (aDOMEvent.touches.length == 0 && aDOMEvent.changedTouches.length == 1))) - { - var newEvent = {}; - - switch(aDOMEvent.type) - { - case CPDOMEventTouchStart: newEvent.type = CPDOMEventMouseDown; - break; - case CPDOMEventTouchEnd: newEvent.type = CPDOMEventMouseUp; - break; - case CPDOMEventTouchMove: newEvent.type = CPDOMEventMouseMoved; - break; - case CPDOMEventTouchCancel: newEvent.type = CPDOMEventMouseUp; - break; - } - - var touch = aDOMEvent.touches.length ? aDOMEvent.touches[0] : aDOMEvent.changedTouches[0]; - - newEvent.clientX = touch.clientX; - newEvent.clientY = touch.clientY; - - newEvent.timestamp = aDOMEvent.timestamp; - newEvent.target = aDOMEvent.target; - - newEvent.shiftKey = newEvent.ctrlKey = newEvent.altKey = newEvent.metaKey = false; - - newEvent.preventDefault = function(){if(aDOMEvent.preventDefault) aDOMEvent.preventDefault()}; - newEvent.stopPropagation = function(){if(aDOMEvent.stopPropagation) aDOMEvent.stopPropagation()}; - - [self _bridgeMouseEvent:newEvent]; - - return; - } - else - { - if (aDOMEvent.preventDefault) - aDOMEvent.preventDefault(); - - if (aDOMEvent.stopPropagation) - aDOMEvent.stopPropagation(); - } -#ifndef DEBUG - } - catch (anException) - { - objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); - } -#endif - // handle touch cases specifically -} - -/* @ignore */ -- (void)_checkPasteboardElement -{ -#ifndef DEBUG - try - { -#endif - var value = _DOMPasteboardElement.value; - - if ([value length]) - { - var pasteboard = [CPPasteboard generalPasteboard]; - - if ([pasteboard _stateUID] != value) - { - [pasteboard declareTypes:[CPStringPboardType] owner:self]; - - [pasteboard setString:value forType:CPStringPboardType]; - } - } - - [self _clearPasteboardElement]; - - [CPApp sendEvent:_pasteboardKeyDownEvent]; - - _pasteboardKeyDownEvent = nil; - - [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; -#ifndef DEBUG - } - catch (anException) - { - objj_exception_report(anException, {path:@"CPDOMWindowBridge.j"}); - } -#endif -} - -/* @ignore */ -- (void)_clearPasteboardElement -{ - _DOMPasteboardElement.value = ""; - _DOMPasteboardElement.blur(); -} - -@end - -var CLICK_SPACE_DELTA = 5.0, - CLICK_TIME_DELTA = (typeof document != "undefined" && document.addEventListener) ? 350.0 : 1000.0; - -var CPDOMEventGetClickCount = function(aComparisonEvent, aTimestamp, aLocation) -{ - if (!aComparisonEvent) - return 1; - - var comparisonLocation = [aComparisonEvent locationInWindow]; - - return (aTimestamp - [aComparisonEvent timestamp] < CLICK_TIME_DELTA && - ABS(comparisonLocation.x - aLocation.x) < CLICK_SPACE_DELTA && - ABS(comparisonLocation.y - aLocation.y) < CLICK_SPACE_DELTA) ? [aComparisonEvent clickCount] + 1 : 1; -} - -var CPDOMEventStop = function(aDOMEvent) -{ - // IE Model - aDOMEvent.cancelBubble = true; - aDOMEvent.returnValue = false; - - // W3C Model - if (aDOMEvent.preventDefault) - aDOMEvent.preventDefault(); - - if (aDOMEvent.stopPropagation) - aDOMEvent.stopPropagation(); - - if (aDOMEvent.type === CPDOMEventMouseDown) - { - CPSharedDOMWindowBridge._DOMFocusElement.focus(); - CPSharedDOMWindowBridge._DOMFocusElement.blur(); - } -} - -function CPWindowObjectList() -{ - var bridge = [CPDOMWindowBridge sharedDOMWindowBridge], - levels = bridge._windowLevels, - layers = bridge._windowLayers, - levelCount = levels.length, - windowObjects = []; - - while (levelCount--) - { - var windows = [layers objectForKey:levels[levelCount]]._windows, - windowCount = windows.length; - - while (windowCount--) - windowObjects.push(windows[windowCount]); - } - - return windowObjects; -} - -function CPWindowList() -{ - var bridge = [CPDOMWindowBridge sharedDOMWindowBridge], - levels = bridge._windowLevels, - layers = bridge._windowLayers, - levelCount = levels.length, - windowNumbers = []; - - while (levelCount--) - { - var windows = [layers objectForKey:levels[levelCount]]._windows, - windowCount = windows.length; - - while (windowCount--) - windowNumbers.push([windows[windowCount] windowNumber]); - } - - return windowNumbers; -} diff --git a/AppKit/Platform/DOM/CPDOMWindowLayer.j b/AppKit/Platform/DOM/CPDOMWindowLayer.j index bb5c5c882..3d6f3dede 100644 --- a/AppKit/Platform/DOM/CPDOMWindowLayer.j +++ b/AppKit/Platform/DOM/CPDOMWindowLayer.j @@ -115,7 +115,7 @@ aWindow._isVisible = YES; if ([aWindow isFullBridge]) - [aWindow setFrame:[aWindow._bridge visibleFrame]]; + [aWindow setFrame:[aWindow._platformWindow usableContentFrame]]; } } diff --git a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j new file mode 100644 index 000000000..4630ae210 --- /dev/null +++ b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j @@ -0,0 +1,1031 @@ +/* + * _CPDOMWindow.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 +@import + +@import "CPEvent.j" +@import "CPCompatibility.j" + +@import "CPDOMWindowLayer.j" + +@import "CPPlatformWindow.j" + +#import "../../CoreGraphics/CGGeometry.h" + + +var DoubleClick = "dblclick", + MouseDown = "mousedown", + MouseUp = "mouseup", + MouseMove = "mousemove", + MouseDrag = "mousedrag", + KeyUp = "keyup", + KeyDown = "keydown", + KeyPress = "keypress", + Copy = "copy", + Paste = "paste", + Resize = "resize", + ScrollWheel = "mousewheel", + TouchStart = "touchstart", + TouchMove = "touchmove", + TouchEnd = "touchend", + TouchCancel = "touchcancel"; + +var ExcludedDOMElements = []; + +ExcludedDOMElements["INPUT"] = YES; +ExcludedDOMElements["SELECT"] = YES; +ExcludedDOMElements["TEXTAREA"] = YES; +ExcludedDOMElements["OPTION"] = YES; + +// Define up here so compressor knows about em. +var CPDOMEventGetClickCount, + CPDOMEventStop; + +//right now we hard code q, w, r and t as keys to propogate +//these aren't normal keycodes, they are with modifier key codes +//might be mac only, we should investigate futher later. +var KeyCodesToPrevent = {}, + CharacterKeysToPrevent = {}, + KeyCodesWithoutKeyPressEvents = { '8':1, '9':1, '16':1, '37':1, '38':1, '39':1, '40':1, '46':1, '33':1, '34':1 }; + +var CTRL_KEY_CODE = 17; + +@implementation CPPlatformWindow (DOM) + +- (id)_init +{ + self = [super init]; + + if (self) + { + _DOMWindow = window; + _contentRect = _CGRectMakeZero(); + + [self registerDOMWindow]; + [self updateFromNativeContentRect]; + + _charCodes = {}; + + _windowLevels = []; + _windowLayers = [CPDictionary dictionary]; + } + + return self; +} + +- (CGRect)nativeContentRect +{ + if (!_DOMWindow) + return [self contentRect]; + + var contentRect = _CGRectMakeZero(); + + if (window.screenTop) + contentRect.origin = _CGPointMake(_DOMWindow.screenLeft, _DOMWindow.screenTop); + + else if (window.screenX) + contentRect.origin = _CGPointMake(_DOMWindow.screenX, _DOMWindow.screenY); + + // Safari, Mozilla, Firefox, and Opera + if (_DOMWindow.innerWidth) + contentRect.size = _CGSizeMake(_DOMWindow.innerWidth, _DOMWindow.innerHeight); + + // Internet Explorer 6 in Strict Mode + else if (document.documentElement && document.documentElement.clientWidth) + contentRect.size = _CGSizeMake(_DOMWindow.document.documentElement.clientWidth, _DOMWindow.document.documentElement.clientHeight); + + // Internet Explorer X + else + contentRect.size = _CGSizeMake(_DOMWindow.document.body.clientWidth, _DOMWindow.document.body.clientHeight); + + return contentRect; +} + +- (void)updateNativeContentOrigin +{ + if (!_DOMWindow) + return; + + var origin = [self contentRect].origin, + nativeOrigin = [self nativeContentRect].origin; + + _DOMWindow.moveBy(origin.x - nativeOrigin.x, origin.y - nativeOrigin.y); +} + +- (void)updateNativeContentSize +{ + if (!_DOMWindow) + return; + + var size = [self contentRect].size, + nativeSize = [self nativeContentRect].size; + + _DOMWindow.resizeBy(size.width - nativeSize.width, size.height - nativeSize.height); +} + +- (void)orderBack:(id)aSender +{ + if (_DOMWindow) + _DOMWindow.blur(); +} + +- (void)registerDOMWindow +{ + var theDocument = _DOMWindow.document; + + _DOMBodyElement = theDocument.getElementsByTagName("body")[0]; + + _DOMBodyElement.webkitTouchCallout = "none"; + + _DOMFocusElement = theDocument.createElement("input"); + + _DOMFocusElement.style.position = "absolute"; + _DOMFocusElement.style.zIndex = "-1000"; + _DOMFocusElement.style.opacity = "0"; + _DOMFocusElement.style.filter = "alpha(opacity=0)"; + + _DOMBodyElement.appendChild(_DOMFocusElement); + + // Create Native Pasteboard handler. + _DOMPasteboardElement = theDocument.createElement("input"); + + _DOMPasteboardElement.style.position = "absolute"; + _DOMPasteboardElement.style.top = "-10000px"; + _DOMPasteboardElement.style.zIndex = "99"; + + _DOMBodyElement.appendChild(_DOMPasteboardElement); + + // Make sure the pastboard element is blurred. + _DOMPasteboardElement.blur(); + + var theClass = [self class], + + resizeEventSelector = @selector(resizeEvent:), + resizeEventImplementation = class_getMethodImplementation(theClass, resizeEventSelector), + resizeEventCallback = function (anEvent) { resizeEventImplementation(self, nil, anEvent); }, + + keyEventSelector = @selector(keyEvent:), + keyEventImplementation = class_getMethodImplementation(theClass, keyEventSelector), + keyEventCallback = function (anEvent) { keyEventImplementation(self, nil, anEvent); }, + + mouseEventSelector = @selector(mouseEvent:), + mouseEventImplementation = class_getMethodImplementation(theClass, mouseEventSelector), + mouseEventCallback = function (anEvent) { mouseEventImplementation(self, nil, anEvent); }, + + scrollEventSelector = @selector(scrollEvent:), + scrollEventImplementation = class_getMethodImplementation(theClass, scrollEventSelector), + scrollEventCallback = function (anEvent) { scrollEventImplementation(self, nil, anEvent); }, + + touchEventSelector = @selector(touchEvent:), + touchEventImplementation = class_getMethodImplementation(theClass, touchEventSelector), + touchEventCallback = function (anEvent) { touchEventImplementation(self, nil, anEvent); }; + + if (theDocument.addEventListener) + { + theDocument.addEventListener("mouseup", mouseEventCallback, NO); + theDocument.addEventListener("mousedown", mouseEventCallback, NO); + theDocument.addEventListener("mousemove", mouseEventCallback, NO); + + theDocument.addEventListener("keyup", keyEventCallback, NO); + theDocument.addEventListener("keydown", keyEventCallback, NO); + theDocument.addEventListener("keypress", keyEventCallback, NO); + + theDocument.addEventListener("touchstart", touchEventCallback, NO); + theDocument.addEventListener("touchend", touchEventCallback, NO); + theDocument.addEventListener("touchmove", touchEventCallback, NO); + theDocument.addEventListener("touchcancel", touchEventCallback, NO); + + _DOMWindow.addEventListener("DOMMouseScroll", scrollEventCallback, NO); + _DOMWindow.addEventListener("mousewheel", scrollEventCallback, NO); + + _DOMWindow.addEventListener("resize", resizeEventCallback, NO); + + _DOMWindow.addEventListener("beforeunload", function() + { + [self updateFromNativeContentRect]; + + theDocument.removeEventListener("mouseup", mouseEventCallback, NO); + theDocument.removeEventListener("mousedown", mouseEventCallback, NO); + theDocument.removeEventListener("mousemove", mouseEventCallback, NO); + + theDocument.removeEventListener("keyup", keyEventCallback, NO); + theDocument.removeEventListener("keydown", keyEventCallback, NO); + theDocument.removeEventListener("keypress", keyEventCallback, NO); + + theDocument.removeEventListener("touchstart", touchEventCallback, NO); + theDocument.removeEventListener("touchend", touchEventCallback, NO); + theDocument.removeEventListener("touchmove", touchEventCallback, NO); + + _DOMWindow.removeEventListener("resize", resizeEventCallback, NO); + + //FIXME: does firefox really need a different value? + _DOMWindow.removeEventListener("DOMMouseScroll", scrollEventCallback, NO); + _DOMWindow.removeEventListener("mousewheel", scrollEventCallback, NO); + + //_DOMWindow.removeEventListener("beforeunload", this, NO); + + self._DOMWindow = nil; + }, NO); + } + else + { + theDocument.attachEvent("onmouseup", mouseEventCallback); + theDocument.attachEvent("onmousedown", mouseEventCallback); + theDocument.attachEvent("onmousemove", mouseEventCallback); + theDocument.attachEvent("ondblclick", mouseEventCallback); + + theDocument.attachEvent("onkeyup", keyEventCallback); + theDocument.attachEvent("onkeydown", keyEventCallback); + theDocument.attachEvent("onkeypress", keyEventCallback); + + _DOMWindow.attachEvent("onresize", resizeEventCallback); + + _DOMWindow.onmousewheel = scrollEventCallback; + theDocument.onmousewheel = scrollEventCallback; + + theDocument.body.ondrag = function () { return NO; }; + theDocument.body.onselectstart = function () { return _DOMWindow.event.srcElement === _DOMPasteboardElement; }; + + _DOMWindow.attachEvent("onbeforeunload", function() + { + [self updateFromNativeContentRect]; + + theDocument.removeEvent("onmouseup", mouseEventCallback); + theDocument.removeEvent("onmousedown", mouseEventCallback); + theDocument.removeEvent("onmousemove", mouseEventCallback); + theDocument.removeEvent("ondblclick", mouseEventCallback); + + theDocument.removeEvent("onkeyup", keyEventCallback); + theDocument.removeEvent("onkeydown", keyEventCallback); + theDocument.removeEvent("onkeypress", keyEventCallback); + + _DOMWindow.removeEvent("onresize", resizeEventCallback); + + _DOMWindow.onmousewheel = NULL; + theDocument.onmousewheel = NULL; + + theDocument.body.ondrag = NULL; + theDocument.body.onselectstart = NULL; + + //_DOMWindow.removeEvent("beforeunload", this); + + self._DOMWindow = nil; + }, NO); + } +} + +- (BOOL)isVisible +{ + return _DOMWindow !== NULL; +} + +- (void)orderFront:(id)aSender +{ + if (_DOMWindow) + return _DOMWindow.focus(); + + _DOMWindow = window.open("", "", "menubar=no,location=no,resizable=yes,scrollbars=no,status=no,left=" + _CGRectGetMinX(_contentRect) + ",top=" + _CGRectGetMinY(_contentRect) + ",width=" + _CGRectGetWidth(_contentRect) + ",height=" + _CGRectGetHeight(_contentRect)); + + [self registerDOMWindow]; +} + +- (void)orderOut:(id)aSender +{ + _DOMWindow.close(); +} + +- (void)keyEvent:(DOMEvent)aDOMEvent +{ + var event, + timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), + sourceElement = (aDOMEvent.target || aDOMEvent.srcElement), + windowNumber = [[CPApp keyWindow] windowNumber], + modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | + (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | + (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | + (aDOMEvent.metaKey ? CPCommandKeyMask : 0); + + if (ExcludedDOMElements[sourceElement.tagName] && sourceElement != _DOMFocusElement && sourceElement != _DOMPasteboardElement) + return; + + //We want to stop propagation if this is a command key AND this character or keycode has been added to our blacklist + StopDOMEventPropagation = !(modifierFlags & (CPControlKeyMask | CPCommandKeyMask)) || + CharacterKeysToPrevent[String.fromCharCode(aDOMEvent.keyCode || aDOMEvent.charCode).toLowerCase()] || + KeyCodesToPrevent[aDOMEvent.keyCode]; + + var isNativePasteEvent = NO, + isNativeCopyOrCutEvent = NO; + + switch (aDOMEvent.type) + { + case "keydown": // Grab and store the keycode now since it is correct and consistent at this point. + _keyCode = aDOMEvent.keyCode; + + var characters = String.fromCharCode(_keyCode).toLowerCase(); + + // If this could be a native PASTE event, then we need to further examine it before + // sending a CPEvent. Select our element to see if anything gets pasted in it. + if (characters == "v" && (modifierFlags & CPPlatformActionKeyMask)) + { + _DOMPasteboardElement.select(); + _DOMPasteboardElement.value = ""; + + isNativePasteEvent = YES; + } + + // Normally we return now because we let keypress send the actual CPEvent keyDown event, since we don't have + // a complete set of information yet. + + // However, of this could be a native COPY event, we need to let the normal event-process take place so it + // can capture our internal Cappuccino pasteboard. + else if ((characters == "c" || characters == "x") && (modifierFlags & CPPlatformActionKeyMask)) + isNativeCopyOrCutEvent = YES; + + // Also, certain browsers (IE and Safari), have broken keyboard supportwhere they don't send keypresses for certain events. + // So, allow the keypress event to handle the event if we are not a browser with broken (remedial) key support... + else if (!CPFeatureIsCompatible(CPJavascriptRemedialKeySupport)) + return; + + // Or, if this is not one of those special keycodes, and also not a ctrl+event + else if (!KeyCodesWithoutKeyPressEvents[_keyCode] && (_keyCode == CTRL_KEY_CODE || !(modifierFlags & CPControlKeyMask))) + return; + + // If this is in fact our broke state, continue to keypress and send the keydown. + case "keypress": // If the source of this event is our pasteboard element, then simply let it continue + // as normal, so that the paste event can successfully complete. + if ((aDOMEvent.target || aDOMEvent.srcElement) == _DOMPasteboardElement) + return; + + var keyCode = _keyCode, + charCode = aDOMEvent.keyCode || aDOMEvent.charCode, + isARepeat = (_charCodes[keyCode] != nil); + + _charCodes[keyCode] = charCode; + + var characters = String.fromCharCode(charCode), + charactersIgnoringModifiers = characters.toLowerCase(); + + event = [CPEvent keyEventWithType:CPKeyDown location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil + characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:isARepeat keyCode:keyCode]; + + if (isNativePasteEvent) + { + _pasteboardKeyDownEvent = event; + + window.setNativeTimeout(function () { [self _checkPasteboardElement] }, 0); + + return; + } + + break; + + case "keyup": var keyCode = aDOMEvent.keyCode, + charCode = _charCodes[keyCode]; + + _charCodes[keyCode] = nil; + + var characters = String.fromCharCode(charCode), + charactersIgnoringModifiers = characters.toLowerCase(); + + if (!(modifierFlags & CPShiftKeyMask)) + characters = charactersIgnoringModifiers; + + event = [CPEvent keyEventWithType:CPKeyUp location:location modifierFlags:modifierFlags + timestamp: timestamp windowNumber:windowNumber context:nil + characters:characters charactersIgnoringModifiers:charactersIgnoringModifiers isARepeat:NO keyCode:keyCode]; + break; + } + + if (event) + { + event._DOMEvent = aDOMEvent; + + [CPApp sendEvent:event]; + + if (isNativeCopyOrCutEvent) + { + var pasteboard = [CPPasteboard generalPasteboard], + types = [pasteboard types]; + + // If this is a native copy event, then check if the pasteboard has anything in it. + if (types.length) + { + if ([types indexOfObjectIdenticalTo:CPStringPboardType] != CPNotFound) + _DOMPasteboardElement.value = [pasteboard stringForType:CPStringPboardType]; + else + _DOMPasteboardElement.value = [pasteboard _generateStateUID]; + + _DOMPasteboardElement.select(); + + window.setNativeTimeout(function() { [self _clearPasteboardElement]; }, 0); + } + + return; + } + } + + if (StopDOMEventPropagation) + CPDOMEventStop(aDOMEvent, self); + + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; + +} + +- (void)scrollEvent:(DOMEvent)aDOMEvent +{ + if(!aDOMEvent) + aDOMEvent = window.event; + + if (CPFeatureIsCompatible(CPJavaScriptMouseWheelValues_8_15)) + { + var x = 0.0, + y = 0.0, + element = aDOMEvent.target; + + while (element.nodeType !== 1) + element = element.parentNode; + + if (element.offsetParent) + { + do + { + x += element.offsetLeft; + y += element.offsetTop; + + } while (element = element.offsetParent); + } + + var location = _CGPointMake((x + ((aDOMEvent.clientX - 8) / 15)), (y + ((aDOMEvent.clientY - 8) / 15))); + } + else + var location = _CGPointMake(aDOMEvent.clientX, aDOMEvent.clientY); + + var deltaX = 0.0, + deltaY = 0.0, + windowNumber = 0, + timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), + modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | + (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | + (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | + (aDOMEvent.metaKey ? CPCommandKeyMask : 0); + + StopDOMEventPropagation = YES; + + windowNumber = [[self hitTest:location] windowNumber]; + + if (!windowNumber) + return; + + var windowFrame = CPApp._windows[windowNumber]._frame; + + location.x -= CGRectGetMinX(windowFrame); + location.y -= CGRectGetMinY(windowFrame); + + if(typeof aDOMEvent.wheelDeltaX != "undefined") + { + deltaX = aDOMEvent.wheelDeltaX / 120.0; + deltaY = aDOMEvent.wheelDeltaY / 120.0; + } + + else if (aDOMEvent.wheelDelta) + deltaY = aDOMEvent.wheelDelta / 120.0; + + else if (aDOMEvent.detail) + deltaY = -aDOMEvent.detail / 3.0; + + else + return; + + if(!CPFeatureIsCompatible(CPJavaScriptNegativeMouseWheelValues)) + { + deltaX = -deltaX; + deltaY = -deltaY; + } + + var event = [CPEvent mouseEventWithType:CPScrollWheel location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 clickCount:1 pressure:0 ]; + + event._DOMEvent = aDOMEvent; + event._deltaX = deltaX; + event._deltaY = deltaY; + + [CPApp sendEvent:event]; + + if (StopDOMEventPropagation) + CPDOMEventStop(aDOMEvent, self); + + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; +} + +- (void)resizeEvent:(DOMEvent)aDOMEvent +{ + // FIXME: This is not the right way to do this. + // We should pay attention to mouse down and mouse up in conjunction with this. + //window.liveResize = YES; + + var oldSize = [self contentRect].size; + + [self updateFromNativeContentRect]; + + var levels = _windowLevels, + layers = _windowLayers, + levelCount = levels.length; + + while (levelCount--) + { + var windows = [layers objectForKey:levels[levelCount]]._windows, + windowCount = windows.length; + + while (windowCount--) + [windows[windowCount] resizeWithOldBridgeSize:oldSize]; + } + + //window.liveResize = NO; + + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; +} + +- (void)touchEvent:(DOMEvent)aDOMEvent +{ + if (aDOMEvent.touches && (aDOMEvent.touches.length == 1 || (aDOMEvent.touches.length == 0 && aDOMEvent.changedTouches.length == 1))) + { + var newEvent = {}; + + switch(aDOMEvent.type) + { + case CPDOMEventTouchStart: newEvent.type = CPDOMEventMouseDown; + break; + case CPDOMEventTouchEnd: newEvent.type = CPDOMEventMouseUp; + break; + case CPDOMEventTouchMove: newEvent.type = CPDOMEventMouseMoved; + break; + case CPDOMEventTouchCancel: newEvent.type = CPDOMEventMouseUp; + break; + } + + var touch = aDOMEvent.touches.length ? aDOMEvent.touches[0] : aDOMEvent.changedTouches[0]; + + newEvent.clientX = touch.clientX; + newEvent.clientY = touch.clientY; + + newEvent.timestamp = aDOMEvent.timestamp; + newEvent.target = aDOMEvent.target; + + newEvent.shiftKey = newEvent.ctrlKey = newEvent.altKey = newEvent.metaKey = false; + + newEvent.preventDefault = function(){if(aDOMEvent.preventDefault) aDOMEvent.preventDefault()}; + newEvent.stopPropagation = function(){if(aDOMEvent.stopPropagation) aDOMEvent.stopPropagation()}; + + [self _bridgeMouseEvent:newEvent]; + + return; + } + else + { + if (aDOMEvent.preventDefault) + aDOMEvent.preventDefault(); + + if (aDOMEvent.stopPropagation) + aDOMEvent.stopPropagation(); + } + // handle touch cases specifically +} + +- (void)mouseEvent:(DOMEvent)aDOMEvent +{ + var type = _overriddenEventType || aDOMEvent.type; + + // IE's event order is down, up, up, dblclick, so we have create these events artificially. + if (type === CPDOMEventDoubleClick) + { + _overriddenEventType = CPDOMEventMouseDown; + [self _bridgeMouseEvent:aDOMEvent]; + + _overriddenEventType = CPDOMEventMouseUp; + [self _bridgeMouseEvent:aDOMEvent]; + + _overriddenEventType = nil; + + return; + } + + var event, + location = _CGPointMake(aDOMEvent.clientX, aDOMEvent.clientY), + timestamp = aDOMEvent.timeStamp ? aDOMEvent.timeStamp : new Date(), + sourceElement = (aDOMEvent.target || aDOMEvent.srcElement), + windowNumber = 0, + modifierFlags = (aDOMEvent.shiftKey ? CPShiftKeyMask : 0) | + (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | + (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | + (aDOMEvent.metaKey ? CPCommandKeyMask : 0); + + StopDOMEventPropagation = YES; + + if (_mouseDownWindow) + windowNumber = [_mouseDownWindow windowNumber]; + + else + { + var theWindow = [self hitTest:location]; + + if ((aDOMEvent.type === CPDOMEventMouseDown) && theWindow) + _mouseDownWindow = theWindow; + + windowNumber = [theWindow windowNumber]; + } + + if (windowNumber) + { + var windowFrame = CPApp._windows[windowNumber]._frame; + + location.x -= _CGRectGetMinX(windowFrame); + location.y -= _CGRectGetMinY(windowFrame); + } + + if (type === "mouseup") + { + if(_mouseIsDown) + { + event = _CPEventFromNativeMouseEvent(aDOMEvent, CPLeftMouseUp, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseUp, timestamp, location), 0); + + _mouseIsDown = NO; + _lastMouseUp = event; + _mouseDownWindow = nil; + } + + if(_DOMEventMode) + { + _DOMEventMode = NO; + return; + } + } + + else if (type === "mousedown") + { + if (ExcludedDOMElements[sourceElement.tagName] && sourceElement != _DOMFocusElement) + { + _DOMEventMode = YES; + _mouseIsDown = YES; + + //fake a down and up event so that event tracking mode will work correctly + [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseDown location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 + clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]]; + + [CPApp sendEvent:[CPEvent mouseEventWithType:CPLeftMouseUp location:location modifierFlags:modifierFlags + timestamp:timestamp windowNumber:windowNumber context:nil eventNumber:-1 + clickCount:CPDOMEventGetClickCount(_lastMouseDown, timestamp, location) pressure:0]]; + + return; + } + + event = _CPEventFromNativeMouseEvent(aDOMEvent, CPLeftMouseDown, location, modifierFlags, timestamp, windowNumber, nil, -1, CPDOMEventGetClickCount(_lastMouseDown, timestamp, location), 0); + + _mouseIsDown = YES; + _lastMouseDown = event; + } + + else // if (type === "mousemove") + { + if (_DOMEventMode) + return; + + event = _CPEventFromNativeMouseEvent(aDOMEvent, _mouseIsDown ? CPLeftMouseDragged : CPMouseMoved, location, modifierFlags, timestamp, windowNumber, nil, -1, 1, 0); + } + + if (event) + { + event._DOMEvent = aDOMEvent; + + [CPApp sendEvent:event]; + } + + if (StopDOMEventPropagation) + CPDOMEventStop(aDOMEvent, self); + + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; +} + + (CPArray)orderedWindowsAtLevel:(int)aLevel +{ + var layer = [self layerAtLevel:aLevel create:NO]; + + if (!layer) + return []; + + return [layer orderedWindows]; +} + +- (CPDOMWindowLayer)layerAtLevel:(int)aLevel create:(BOOL)aFlag +{ + var layer = [_windowLayers objectForKey:aLevel]; + + // If the layer doesn't currently exist, and the create flag is true, + // create the layer. + if (!layer && aFlag) + { + layer = [[CPDOMWindowLayer alloc] initWithLevel:aLevel]; + + [_windowLayers setObject:layer forKey:aLevel]; + + // Find the nearest layer. This is similar to a binary search, + // only we know we won't find the value. + var low = 0, + high = _windowLevels.length - 1, + middle; + + while (low <= high) + { + middle = FLOOR((low + high) / 2); + + if (_windowLevels[middle] > aLevel) + high = middle - 1; + else + low = middle + 1; + } + + [_windowLevels insertObject:aLevel atIndex:_windowLevels[middle] > aLevel ? middle : middle + 1]; + layer._DOMElement.style.zIndex = aLevel; + _DOMBodyElement.appendChild(layer._DOMElement); + } + + return layer; +} + +- (void)order:(CPWindowOrderingMode)aPlace window:(CPWindow)aWindow relativeTo:(CPWindow)otherWindow +{ + // Grab the appropriate level for the layer, and create it if + // necessary (if we are not simply removing the window). + var layer = [self layerAtLevel:[aWindow level] create:aPlace != CPWindowOut]; + + // Ignore otherWindow, simply remove this window from it's level. + // If layer is nil, this will be a no-op. + if (aPlace == CPWindowOut) + return [layer removeWindow:aWindow]; + + // Place the window at the appropriate index. + [layer insertWindow:aWindow atIndex:(otherWindow ? (aPlace == CPWindowAbove ? otherWindow._index + 1 : otherWindow._index) : CPNotFound)]; +} + +/* @ignore */ +- (id)_dragHitTest:(CPPoint)aPoint pasteboard:(CPPasteboard)aPasteboard +{ + var levels = _windowLevels, + layers = _windowLayers, + levelCount = levels.length; + + while (levelCount--) + { + // Skip any windows above or at the dragging level. + if (levels[levelCount] >= CPDraggingWindowLevel) + continue; + + var windows = [layers objectForKey:levels[levelCount]]._windows, + windowCount = windows.length; + + while (windowCount--) + { + var theWindow = windows[windowCount]; + + if ([theWindow containsPoint:aPoint]) + return [theWindow _dragHitTest:aPoint pasteboard:aPasteboard]; + } + } + + return nil; +} + +/* @ignore */ +- (void)_propagateCurrentDOMEvent:(BOOL)aFlag +{ + StopDOMEventPropagation = !aFlag; +} + +- (CPWindow)hitTest:(CPPoint)location +{ + var levels = _windowLevels, + layers = _windowLayers, + levelCount = levels.length, + theWindow = nil; + + while (levelCount-- && !theWindow) + { + var windows = [layers objectForKey:levels[levelCount]]._windows, + windowCount = windows.length; + + while (windowCount-- && !theWindow) + { + var candidateWindow = windows[windowCount]; + + if (!candidateWindow._ignoresMouseEvents && [candidateWindow containsPoint:location]) + theWindow = candidateWindow; + } + } + + return theWindow; +} + +- (void)_checkPasteboardElement +{ + var value = _DOMPasteboardElement.value; + + if ([value length]) + { + var pasteboard = [CPPasteboard generalPasteboard]; + + if ([pasteboard _stateUID] != value) + { + [pasteboard declareTypes:[CPStringPboardType] owner:self]; + + [pasteboard setString:value forType:CPStringPboardType]; + } + } + + [self _clearPasteboardElement]; + + [CPApp sendEvent:_pasteboardKeyDownEvent]; + + _pasteboardKeyDownEvent = nil; + + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; +} + +- (void)_clearPasteboardElement +{ + _DOMPasteboardElement.value = ""; + _DOMPasteboardElement.blur(); +} + +/*! + When using command (mac) or control (windows), keys are propagated to the browser by default. + To prevent a character key from propagating (to prevent its default action, and instead use it + in your own application), use these methods. These methods are additive -- the list builds until you clear it. + + @param characters a list of characters to stop propagating keypresses to the browser. +*/ ++ (void)preventCharacterKeysFromPropagating:(CPArray)characters +{ + for(var i=characters.length; i>0; i--) + CharacterKeysToPrevent[""+characters[i-1].toLowerCase()] = YES; +} + +/*! + @param character a character to stop propagating keypresses to the browser. +*/ ++ (void)preventCharacterKeyFromPropagating:(CPString)character +{ + CharacterKeysToPrevent[character.toLowerCase()] = YES; +} + +/*! + Clear the list of characters for which we are not sending keypresses to the browser. +*/ ++ (void)clearCharacterKeysToPreventFromPropagating +{ + CharacterKeysToPrevent = {}; +} + +/*! + Prevent these keyCodes from sending their keypresses to the browser. + @param keyCodes an array of keycodes to prevent propagation. +*/ ++ (void)preventKeyCodesFromPropagating:(CPArray)keyCodes +{ + for(var i=keyCodes.length; i>0; i--) + KeyCodesToPrevent[keyCodes[i-1]] = YES; +} + +/*! + Prevent this keyCode from sending its key events to the browser. + @param keyCode a keycode to prevent propagation. +*/ ++ (void)preventKeyCodeFromPropagating:(CPString)keyCode +{ + KeyCodesToPrevent[keyCode] = YES; +} + +/*! + Clear the list of keyCodes for which we are not sending keypresses to the browser. +*/ ++ (void)clearKeyCodesToPreventFromPropagating +{ + KeyCodesToPrevent = {}; +} + +@end + +var CPEventClass = [CPEvent class]; + +var _CPEventFromNativeMouseEvent = function(aNativeEvent, anEventType, aPoint, modifierFlags, aTimestamp, aWindowNumber, aGraphicsContext, anEventNumber, aClickCount, aPressure) +{ + aNativeEvent.isa = CPEventClass; + + aNativeEvent._type = anEventType; + aNativeEvent._location = aPoint; + aNativeEvent._modifierFlags = modifierFlags; + aNativeEvent._timestamp = aTimestamp; + aNativeEvent._windowNumber = aWindowNumber; + aNativeEvent._window = nil; + aNativeEvent._context = aGraphicsContext; + aNativeEvent._eventNumber = anEventNumber; + aNativeEvent._clickCount = aClickCount; + aNativeEvent._pressure = aPressure; + + return aNativeEvent; +} + +var CLICK_SPACE_DELTA = 5.0, + CLICK_TIME_DELTA = (typeof document != "undefined" && document.addEventListener) ? 350.0 : 1000.0; + +var CPDOMEventGetClickCount = function(aComparisonEvent, aTimestamp, aLocation) +{ + if (!aComparisonEvent) + return 1; + + var comparisonLocation = [aComparisonEvent locationInWindow]; + + return (aTimestamp - [aComparisonEvent timestamp] < CLICK_TIME_DELTA && + ABS(comparisonLocation.x - aLocation.x) < CLICK_SPACE_DELTA && + ABS(comparisonLocation.y - aLocation.y) < CLICK_SPACE_DELTA) ? [aComparisonEvent clickCount] + 1 : 1; +} + +var CPDOMEventStop = function(aDOMEvent, aPlatformWindow) +{ + // IE Model + aDOMEvent.cancelBubble = true; + aDOMEvent.returnValue = false; + + // W3C Model + if (aDOMEvent.preventDefault) + aDOMEvent.preventDefault(); + + if (aDOMEvent.stopPropagation) + aDOMEvent.stopPropagation(); + + if (aDOMEvent.type === CPDOMEventMouseDown) + { + aPlatformWindow._DOMFocusElement.focus(); + aPlatformWindow._DOMFocusElement.blur(); + } +} + +function CPWindowObjectList() +{ + var platformWindow = [CPPlatformWindow primaryPlatformWindow], + levels = platformWindow._windowLevels, + layers = platformWindow._windowLayers, + levelCount = levels.length, + windowObjects = []; + + while (levelCount--) + { + var windows = [layers objectForKey:levels[levelCount]]._windows, + windowCount = windows.length; + + while (windowCount--) + windowObjects.push(windows[windowCount]); + } + + return windowObjects; +} + +function CPWindowList() +{ + var platformWindow = [CPPlatformWindow primaryPlatformWindow], + levels = platformWindow._windowLevels, + layers = platformWindow._windowLayers, + levelCount = levels.length, + windowNumbers = []; + + while (levelCount--) + { + var windows = [layers objectForKey:levels[levelCount]]._windows, + windowCount = windows.length; + + while (windowCount--) + windowNumbers.push([windows[windowCount] windowNumber]); + } + + return windowNumbers; +}