diff --git a/AppKit/CPAccordionView.j b/AppKit/CPAccordionView.j index c9bcf71a7..ec1798a67 100644 --- a/AppKit/CPAccordionView.j +++ b/AppKit/CPAccordionView.j @@ -30,7 +30,12 @@ #import "CoreGraphics/CGGeometry.h" - +/*! + @ingroup appkit + @class CPAccordionViewItem + +

A CPAccordionViewItem represents a single section of a CPAccordionView.

+*/ @implementation CPAccordionViewItem : CPObject { CPString _identifier @accessors(property=identifier); @@ -43,6 +48,10 @@ return [self initWithIdentifier:@""]; } +/*! + Initializes a new CPAccordionViewItem with the specified identifier. This identifier may + then be used in subsequent methods to provide abstract introspection of the CPAccordionView. +*/ - (id)initWithIdentifier:(CPString)anIdentifier { self = [super init]; @@ -55,6 +64,25 @@ @end +/*! + @ingroup appkit + @class CPAccordionView + +

CPAccordionView provides a container for CPAccordionViewItem objects and manages layout state + for all sublayout items.

+ + Example
+
+var myAccordionView = [[CPAccordionView alloc] initWithFrame:CGRectMakeZero()];
+var firstItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"firstSection"]];
+[firstItem setView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
+var secondItem = [[CPAccordionViewItem alloc] initWithIdentifier:@"secondSection"]];
+[secondItem setView:[[CPView alloc] initWithFrame:CGRectMakeZero()]];
+[myAccordionView addItem:firstItem];
+[myAccordionView addItem:secondItem];
+[myAccordionView setAutoresizingMask: CPViewWidthSizable | CPViewHeightSizable];
+  
+*/ @implementation CPAccordionView : CPView { CPInteger _dirtyItemIndex; @@ -64,7 +92,10 @@ CPMutableArray _itemViews; CPIndexSet _expandedItemIndexes; } - +/*! + Initializes the receiver for usage with the specified bounding rectangle + @return the initialized view +*/ - (id)initWithFrame:(CGRect)aFrame { self = [super initWithFrame:aFrame]; @@ -95,7 +126,11 @@ { return _items; } - +/*! + Append a CPAccordionViewItem to the receiver. Note that the CPAccordionViewItem must contain + a valid CPView derived component or a TypeError will be generated when the contents of the + ViewItem are disclosed. +*/ - (void)addItem:(CPAccordionViewItem)anItem { [self insertItem:anItem atIndex:_items.length]; diff --git a/AppKit/CPApplication.j b/AppKit/CPApplication.j index 56964473d..e23aab878 100644 --- a/AppKit/CPApplication.j +++ b/AppKit/CPApplication.j @@ -54,7 +54,7 @@ CPRunStoppedResponse = -1000; CPRunAbortedResponse = -1001; CPRunContinuesResponse = -1002; -/*! +/*! @ingroup appkit @class CPApplication @@ -62,7 +62,7 @@ CPRunContinuesResponse = -1002; Every GUI application has exactly one instance of CPApplication (or of a custom subclass of CPApplication). Your program's main() function can create that instance by calling the \c CPApplicationMain function. A simple example looks like this: - +
     function main(args, namedArgs)
     {
@@ -82,25 +82,25 @@ CPRunContinuesResponse  = -1002;
 @implementation CPApplication : CPResponder
 {
     CPArray                 _eventListeners;
-    
+
     CPEvent                 _currentEvent;
-    
+
     CPArray                 _windows;
     CPWindow                _keyWindow;
     CPWindow                _mainWindow;
     CPWindow                _previousKeyWindow;
     CPWindow                _previousMainWindow;
-    
+
     CPMenu                  _mainMenu;
     CPDocumentController    _documentController;
-    
+
     CPModalSession          _currentSession;
-    
+
     //
     id                      _delegate;
     BOOL                    _finishedLaunching;
     BOOL                    _isActive;
-    
+
     CPDictionary            _namedArgs;
     CPArray                 _args;
     CPString                _fullArgsString;
@@ -119,7 +119,7 @@ CPRunContinuesResponse  = -1002;
 {
     if (!CPApp)
         CPApp = [[CPApplication alloc] init];
-    
+
     return CPApp;
 }
 
@@ -131,73 +131,18 @@ CPRunContinuesResponse  = -1002;
 - (id)init
 {
     self = [super init];
-    
+
+    CPApp = self;
+
     if (self)
     {
         _eventListeners = [];
-        
+
         _windows = [];
-        
+
         [_windows addObject:nil];
-    
-        // FIXME: This should be read from the cib.
-        _mainMenu = [[CPMenu alloc] initWithTitle:@"MainMenu"];
-        
-        // FIXME: We should implement autoenabling.
-        [_mainMenu setAutoenablesItems:NO];
-
-        var bundle = [CPBundle bundleForClass:[CPApplication class]],
-            newMenuItem = [[CPMenuItem alloc] initWithTitle:@"New" action:@selector(newDocument:) keyEquivalent:@"n"];
-
-        [newMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/New.png"] size:CGSizeMake(16.0, 16.0)]];
-        [newMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/NewHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
-
-        [_mainMenu addItem:newMenuItem];
-        
-        var openMenuItem = [[CPMenuItem alloc] initWithTitle:@"Open" action:@selector(openDocument:) keyEquivalent:@"o"];
-        
-        [openMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Open.png"] size:CGSizeMake(16.0, 16.0)]];
-        [openMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/OpenHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];
-        
-        [_mainMenu addItem:openMenuItem];
-        
-        var saveMenu = [[CPMenu alloc] initWithTitle:@"Save"],
-            saveMenuItem = [[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:nil];
-        
-        [saveMenuItem setImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/Save.png"] size:CGSizeMake(16.0, 16.0)]];
-        [saveMenuItem setAlternateImage:[[CPImage alloc] initWithContentsOfFile:[bundle pathForResource:@"CPApplication/SaveHighlighted.png"] size:CGSizeMake(16.0, 16.0)]];        
-        
-        [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save" action:@selector(saveDocument:) keyEquivalent:@"s"]];
-        [saveMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Save As" action:@selector(saveDocumentAs:) keyEquivalent:nil]];
-        
-        [saveMenuItem setSubmenu:saveMenu];
-        
-        [_mainMenu addItem:saveMenuItem];
-        
-        var editMenuItem = [[CPMenuItem alloc] initWithTitle:@"Edit" action:nil keyEquivalent:nil],
-            editMenu = [[CPMenu alloc] initWithTitle:@"Edit"],
-            
-            undoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Undo" action:@selector(undo:) keyEquivalent:CPUndoKeyEquivalent],
-            redoMenuItem = [[CPMenuItem alloc] initWithTitle:@"Redo" action:@selector(redo:) keyEquivalent:CPRedoKeyEquivalent];
-
-        [undoMenuItem setKeyEquivalentModifierMask:CPUndoKeyEquivalentModifierMask];        
-        [redoMenuItem setKeyEquivalentModifierMask:CPRedoKeyEquivalentModifierMask];
-        
-        [editMenu addItem:undoMenuItem];
-        [editMenu addItem:redoMenuItem];
-        
-        [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Cut" action:@selector(cut:) keyEquivalent:@"x"]],
-        [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Copy" action:@selector(copy:) keyEquivalent:@"c"]],
-        [editMenu addItem:[[CPMenuItem alloc] initWithTitle:@"Paste" action:@selector(paste:) keyEquivalent:@"v"]];
-    
-        [editMenuItem setSubmenu:editMenu];
-        [editMenuItem setHidden:YES];
-        
-        [_mainMenu addItem:editMenuItem];
-        
-        [_mainMenu addItem:[CPMenuItem separatorItem]];
     }
-    
+
     return self;
 }
 
@@ -278,28 +223,28 @@ CPRunContinuesResponse  = -1002;
 
     // We also want to set the default cursor on the body, so that buttons and things don't have an iBeam
     [[CPCursor arrowCursor] set];
-    
+
     var bundle = [CPBundle mainBundle],
         types = [bundle objectForInfoDictionaryKey:@"CPBundleDocumentTypes"];
-        
+
     if ([types count] > 0)
         _documentController = [CPDocumentController sharedDocumentController];
-        
+
     var delegateClassName = [bundle objectForInfoDictionaryKey:@"CPApplicationDelegateClass"];
-    
+
     if (delegateClassName)
     {
         var delegateClass = objj_getClass(delegateClassName);
-        
+
         if (delegateClass)
             if ([_documentController class] == delegateClass)
                 [self setDelegate:_documentController];
             else
                 [self setDelegate:[[delegateClass alloc] init]];
     }
-    
+
     var defaultCenter = [CPNotificationCenter defaultCenter];
-    
+
     [defaultCenter
         postNotificationName:CPApplicationWillFinishLaunchingNotification
         object:self];
@@ -388,14 +333,14 @@ CPRunContinuesResponse  = -1002;
             versionLabel = [contentView viewWithTag:3],
             copyrightLabel = [contentView viewWithTag:4],
             standardPath = [[CPBundle bundleForClass:[self class]] pathForResource:@"standardApplicationIcon.png"];
-    
+
         // FIXME move this into the CIB eventually
         [applicationLabel setFont:[CPFont boldSystemFontOfSize:14.0]];
         [applicationLabel setAlignment:CPCenterTextAlignment];
         [versionLabel setAlignment:CPCenterTextAlignment];
         [copyrightLabel setAlignment:CPCenterTextAlignment];
 
-        [imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath 
+        [imageView setImage:applicationIcon || [[CPImage alloc] initWithContentsOfFile:standardPath
                                                                                   size:CGSizeMake(256, 256)]];
 
         [applicationLabel setStringValue:applicationTitle || ""];
@@ -499,10 +444,10 @@ CPRunContinuesResponse  = -1002;
         return;
         // raise exception;
     }
-    
+
     _currentSession._state = aCode;
     _currentSession = _currentSession._previous;
-    
+
 //    if (aCode == CPRunAbortedResponse)
         [self _removeRunModalLoop];
 }
@@ -511,12 +456,12 @@ CPRunContinuesResponse  = -1002;
 - (void)_removeRunModalLoop
 {
     var count = _eventListeners.length;
-    
+
     while (count--)
         if (_eventListeners[count]._callback === _CPRunModalLoop)
         {
             _eventListeners.splice(count, 1);
-            
+
             return;
         }
 }
@@ -554,12 +499,12 @@ CPRunContinuesResponse  = -1002;
 {
     aModalSession._previous = _currentSession;
     _currentSession = aModalSession;
-    
+
     var theWindow = aModalSession._window;
 
     [theWindow center];
     [theWindow makeKeyAndOrderFront:self];
-    
+
 //    [theWindow._bridge _obscureWindowsBelowModalWindow];
 
     [CPApp setCallback:_CPRunModalLoop forNextEventMatchingMask:CPAnyEventMask untilDate:nil inMode:0 dequeue:NO];
@@ -573,7 +518,7 @@ CPRunContinuesResponse  = -1002;
 {
     if (!_currentSession)
         return nil;
-    
+
     return _currentSession._window;
 }
 
@@ -618,7 +563,7 @@ CPRunContinuesResponse  = -1002;
     {
         if (_eventListeners[_eventListeners.length - 1]._mask & (1 << [anEvent type]))
             _eventListeners.pop()._callback(anEvent);
-        
+
         return;
     }
 
@@ -729,11 +674,11 @@ CPRunContinuesResponse  = -1002;
 
     if ([super tryToPerform:anAction with:anObject])
         return YES;
-    
+
     if([_delegate respondsToSelector:anAction])
     {
         [_delegate performSelector:anAction withObject:anObject];
-        
+
         return YES;
     }
 
@@ -753,9 +698,9 @@ CPRunContinuesResponse  = -1002;
 
     if (!target)
         return NO;
-    
+
     [target performSelector:anAction withObject:aSender];
-    
+
     return YES;
 }
 
@@ -774,10 +719,10 @@ CPRunContinuesResponse  = -1002;
 {
     if (!anAction)
         return nil;
-        
+
     if (aTarget)
         return aTarget;
-        
+
     return [self targetForAction:anAction];
 }
 
@@ -802,28 +747,28 @@ CPRunContinuesResponse  = -1002;
 {
     var responder = [aWindow firstResponder],
         checkWindow = YES;
-    
+
     while (responder)
     {
         if ([responder respondsToSelector:anAction])
             return responder;
-        
+
         if (responder == aWindow)
             checkWindow = NO;
-        
+
         responder = [responder nextResponder];
     }
-    
+
     if (checkWindow && [aWindow respondsToSelector:anAction])
         return aWindow;
-    
+
     var delegate = [aWindow delegate];
-    
+
     if ([delegate respondsToSelector:anAction])
         return delegate;
 
     var windowController = [aWindow windowController];
-    
+
     if ([windowController respondsToSelector:anAction])
         return windowController;
 
@@ -852,26 +797,26 @@ CPRunContinuesResponse  = -1002;
 {
     if (!anAction)
         return nil;
-    
+
     var target = [self _targetForWindow:[self keyWindow] action:anAction];
-    
+
     if (target)
         return target;
-        
+
     target = [self _targetForWindow:[self mainWindow] action:anAction];
-    
+
     if (target)
         return target;
-    
+
     if ([self respondsToSelector:anAction])
         return self;
-    
+
     if ([_delegate respondsToSelector:anAction])
         return _delegate;
-        
+
     if ([_documentController respondsToSelector:anAction])
         return _documentController;
-    
+
     return nil;
 }
 
@@ -901,14 +846,14 @@ CPRunContinuesResponse  = -1002;
     @param aContextInfo
 */
 - (void)beginSheet:(CPWindow)aSheet modalForWindow:(CPWindow)aWindow modalDelegate:(id)aModalDelegate didEndSelector:(SEL)aDidEndSelector contextInfo:(id)aContextInfo
-{    
+{
     var styleMask = [aSheet styleMask];
     if (!(styleMask & CPDocModalWindowMask))
     {
         [CPException raise:CPInternalInconsistencyException reason:@"Currently only CPDocModalWindowMask style mask is supported for attached sheets"];
         return;
     }
-    
+
     [aWindow orderFront:self];
     [aWindow _attachSheet:aSheet modalDelegate:aModalDelegate didEndSelector:aDidEndSelector contextInfo:aContextInfo];
 }
@@ -916,15 +861,15 @@ CPRunContinuesResponse  = -1002;
 - (void)endSheet:(CPWindow)sheet returnCode:(int)returnCode
 {
     var count = [_windows count];
-    
+
     while (--count >= 0)
     {
         var aWindow = [_windows objectAtIndex:count];
         var context = aWindow._sheetContext;
-    
+
         if (context != nil && context["sheet"] === sheet)
         {
-            context["returnCode"] = returnCode; 
+            context["returnCode"] = returnCode;
             [aWindow _detachSheetWindow];
             return;
         }
@@ -940,7 +885,7 @@ CPRunContinuesResponse  = -1002;
 {
     if(_fullArgsString !== window.location.hash)
         [self _reloadArguments];
-    
+
     return _args;
 }
 
@@ -950,28 +895,28 @@ CPRunContinuesResponse  = -1002;
     {
         _args = [];
         window.location.hash = @"#";
-        
+
         return;
     }
-    
+
     if([args class] != CPArray)
         args = [CPArray arrayWithObject:args];
-    
+
     _args = args;
-    
+
     var toEncode = [_args copy];
     for(var i=0, count = toEncode.length; i [self lastColumn] +1)
+        return [CPIndexSet indexSet];
+
     return [[self tableViewInColumn:column] selectedRowIndexes];
 }
 
@@ -551,8 +598,6 @@
 
     [[self tableViewInColumn:column] selectRowIndexes:indexSet byExtendingSelection:NO];
 
-    [self setNextResponder:[self tableViewInColumn:[self lastColumn]]];
-
     [self scrollColumnToVisible:column];
 
     if ([_delegate respondsToSelector:@selector(browserSelectionDidChange:)])
@@ -604,6 +649,8 @@
 
 @end
 
+var _CPBrowserResizeControlBackgroundImage = nil;
+
 @implementation _CPBrowserResizeControl : CPView
 {
     CGPoint     _mouseDownX;
@@ -612,6 +659,26 @@
     unsigned    _width;
 }
 
++ (CPImage)backgroundImage
+{
+    if (!_CPBrowserResizeControlBackgroundImage)
+    {
+        var path = [[CPBundle bundleForClass:[self class]] pathForResource:"browser-resize-control.png"];
+        _CPBrowserResizeControlBackgroundImage = [[CPImage alloc] initWithContentsOfFile:path
+                                                                                    size:CGSizeMake(15, 14)];
+    }
+
+    return _CPBrowserResizeControlBackgroundImage;
+}
+
+- (id)initWithFrame:(CGRect)aFrame
+{
+    if (self = [super initWithFrame:aFrame])
+        [self setBackgroundColor:[CPColor colorWithPatternImage:[[self class] backgroundImage]]];
+
+    return self;
+}
+
 - (void)mouseDown:(CPEvent)anEvent
 {
     _mouseDownX = [anEvent locationInWindow].x;
@@ -626,9 +693,11 @@
     [_browser setWidth:_width + deltaX ofColumn:_index];
 }
 
-@end
+- (void)mouseUp:(CPEvent)anEvent
+{
+}
 
-var _CPBrowserResizeControlBackgroundImage = nil;
+@end
 
 @implementation _CPBrowserScrollView : CPScrollView
 {
@@ -636,24 +705,11 @@ var _CPBrowserResizeControlBackgroundImage = nil;
     CPBrowser                _browser @accessors;
 }
 
-+ (CPImage)backgroundImage
-{
-    if (!_CPBrowserResizeControlBackgroundImage)
-    {
-        var path = [[CPBundle bundleForClass:[self class]] pathForResource:"browser-resize-control.png"];
-        _CPBrowserResizeControlBackgroundImage = [[CPImage alloc] initWithContentsOfFile:path
-                                                                                    size:CGSizeMake(15, 14)];
-    }
-
-    return _CPBrowserResizeControlBackgroundImage;
-}
-
 - (void)initWithFrame:(CGRect)aFrame
 {
     if (self = [super initWithFrame:aFrame])
     {
         _resizeControl = [[_CPBrowserResizeControl alloc] initWithFrame:CGRectMakeZero()];
-        [_resizeControl setBackgroundColor:[CPColor colorWithPatternImage:[[self class] backgroundImage]]];
         [self addSubview:_resizeControl];
     }
 
diff --git a/AppKit/CPButton.j b/AppKit/CPButton.j
index 8a602a607..57a105bd5 100644
--- a/AppKit/CPButton.j
+++ b/AppKit/CPButton.j
@@ -74,7 +74,7 @@ CPChangeBackgroundCellMask  = CPBackgroundButtonMask;
 
 CPButtonStateMixed  = CPThemeState("mixed");
 
-/*! 
+/*!
     @ingroup appkit
     @class CPButton
 
@@ -85,10 +85,10 @@ CPButtonStateMixed  = CPThemeState("mixed");
 @implementation CPButton : CPControl
 {
     BOOL                _allowsMixedState;
-    
+
     CPString            _title;
     CPString            _alternateTitle;
-    
+
     CPImage             _image;
     CPImage             _alternateImage;
 
@@ -131,7 +131,7 @@ CPButtonStateMixed  = CPThemeState("mixed");
 - (id)initWithFrame:(CGRect)aFrame
 {
     self = [super initWithFrame:aFrame];
-    
+
     if (self)
     {
         // Should we instead override the defaults?
@@ -139,13 +139,13 @@ CPButtonStateMixed  = CPThemeState("mixed");
         [self setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
         [self setValue:CPImageLeft forThemeAttribute:@"image-position"];
         [self setValue:CPScaleNone forThemeAttribute:@"image-scaling"];
-        
+
         _controlSize = CPRegularControlSize;
-        
+
 //        [self setBezelStyle:CPRoundRectBezelStyle];
         [self setBordered:YES];
     }
-    
+
     return self;
 }
 
@@ -248,9 +248,9 @@ CPButtonStateMixed  = CPThemeState("mixed");
 {
     if (_title === aTitle)
         return;
-    
+
     _title = aTitle;
-    
+
     [self setNeedsLayout];
     [self setNeedsDisplay:YES];
 }
@@ -264,7 +264,7 @@ CPButtonStateMixed  = CPThemeState("mixed");
 {
     if (_alternateTitle === aTitle)
         return;
-    
+
     _alternateTitle = aTitle;
 
     [self setNeedsLayout];
@@ -280,9 +280,9 @@ CPButtonStateMixed  = CPThemeState("mixed");
 {
     if (_image === anImage)
         return;
-    
+
     _image = anImage;
-    
+
     [self setNeedsLayout];
     [self setNeedsDisplay:YES];
 }
@@ -300,9 +300,9 @@ CPButtonStateMixed  = CPThemeState("mixed");
 {
     if (_alternateImage === anImage)
         return;
-    
+
     _alternateImage = anImage;
-    
+
     [self setNeedsLayout];
     [self setNeedsDisplay:YES];
 }
@@ -373,13 +373,13 @@ CPButtonStateMixed  = CPThemeState("mixed");
                                         [self setShowsStateBy:CPContentsCellMask];
                                         break;
 
-        case CPSwitchButton:            [CPException raise:CPInvalidArgumentException 
+        case CPSwitchButton:            [CPException raise:CPInvalidArgumentException
                                                     reason:"The CPSwitchButton type is not supported in Cappuccino, use the CPCheckBox class instead."];
 
-        case CPRadioButton:             [CPException raise:CPInvalidArgumentException 
+        case CPRadioButton:             [CPException raise:CPInvalidArgumentException
                                                     reason:"The CPRadioButton type is not supported in Cappuccino, use the CPRadio class instead."];
 
-        default:                        [CPException raise:CPInvalidArgumentException 
+        default:                        [CPException raise:CPInvalidArgumentException
                                                     reason:"Unknown button type."];
     }
 
@@ -395,7 +395,7 @@ CPButtonStateMixed  = CPThemeState("mixed");
 
     _imageDimsWhenDisabled = imageShouldDimWhenDisabled;
 
-    if (_imageDimsWhenDisabled)
+    if ([self hasThemeState:CPThemeStateDisabled])
     {
         [self setNeedsDisplay:YES];
         [self setNeedsLayout];
@@ -410,7 +410,7 @@ CPButtonStateMixed  = CPThemeState("mixed");
 - (BOOL)startTrackingAt:(CGPoint)aPoint
 {
     [self highlight:YES];
-    
+
     return [super startTrackingAt:aPoint];
 }
 
@@ -435,7 +435,7 @@ CPButtonStateMixed  = CPThemeState("mixed");
     bounds.origin.y += contentInset.top;
     bounds.size.width -= contentInset.left + contentInset.right;
     bounds.size.height -= contentInset.top + contentInset.bottom;
-    
+
     return bounds;
 }
 
@@ -483,10 +483,10 @@ CPButtonStateMixed  = CPThemeState("mixed");
 {
     if (aName === "bezel-view")
         return [self bezelRectForBounds:[self bounds]];
-    
+
     else if (aName === "content-view")
         return [self contentRectForBounds:[self bounds]];
-    
+
     return [super rectForEphemeralSubviewNamed:aName];
 }
 
@@ -497,7 +497,7 @@ CPButtonStateMixed  = CPThemeState("mixed");
         var view = [[CPView alloc] initWithFrame:_CGRectMakeZero()];
 
         [view setHitTests:NO];
-        
+
         return view;
     }
     else
@@ -530,6 +530,7 @@ CPButtonStateMixed  = CPThemeState("mixed");
         [contentView setTextShadowOffset:[self currentValueForThemeAttribute:@"text-shadow-offset"]];
         [contentView setImagePosition:[self currentValueForThemeAttribute:@"image-position"]];
         [contentView setImageScaling:[self currentValueForThemeAttribute:@"image-scaling"]];
+        [contentView setDimsImage:[self hasThemeState:CPThemeStateDisabled] && _imageDimsWhenDisabled];
     }
 }
 
@@ -569,11 +570,12 @@ CPButtonStateMixed  = CPThemeState("mixed");
 @end
 
 
-var CPButtonImageKey                = @"CPButtonImageKey",
-    CPButtonAlternateImageKey       = @"CPButtonAlternateImageKey",
-    CPButtonTitleKey                = @"CPButtonTitleKey",
-    CPButtonAlternateTitleKey       = @"CPButtonAlternateTitleKey",
-    CPButtonIsBorderedKey           = @"CPButtonIsBorderedKey";
+var CPButtonImageKey                    = @"CPButtonImageKey",
+    CPButtonAlternateImageKey           = @"CPButtonAlternateImageKey",
+    CPButtonTitleKey                    = @"CPButtonTitleKey",
+    CPButtonAlternateTitleKey           = @"CPButtonAlternateTitleKey",
+    CPButtonIsBorderedKey               = @"CPButtonIsBorderedKey",
+    CPButtonImageDimsWhenDisabledKey    = @"CPButtonImageDimsWhenDisabledKey";
 
 @implementation CPButton (CPCoding)
 
@@ -584,21 +586,23 @@ var CPButtonImageKey                = @"CPButtonImageKey",
 - (id)initWithCoder:(CPCoder)aCoder
 {
     self = [super initWithCoder:aCoder];
-    
+
     if (self)
     {
         _controlSize = CPRegularControlSize;
 
         [self setImage:[aCoder decodeObjectForKey:CPButtonImageKey]];
         [self setAlternateImage:[aCoder decodeObjectForKey:CPButtonAlternateImageKey]];
-        
+
         [self setTitle:[aCoder decodeObjectForKey:CPButtonTitleKey]];
         [self setAlternateTitle:[aCoder decodeObjectForKey:CPButtonAlternateTitleKey]];
-        
+
+        [self setImageDimsWhenDisabled:[aCoder decodeObjectForKey:CPButtonImageDimsWhenDisabledKey]];
+
         [self setNeedsLayout];
         [self setNeedsDisplay:YES];
     }
-    
+
     return self;
 }
 
@@ -615,6 +619,8 @@ var CPButtonImageKey                = @"CPButtonImageKey",
 
     [aCoder encodeObject:_title forKey:CPButtonTitleKey];
     [aCoder encodeObject:_alternateTitle forKey:CPButtonAlternateTitleKey];
+
+    [aCoder encodeObject:[self imageDimsWhenDisabled] forKey:CPButtonImageDimsWhenDisabledKey];
 }
 
 @end
diff --git a/AppKit/CPButtonBar.j b/AppKit/CPButtonBar.j
index daf2e9518..01aa1d833 100644
--- a/AppKit/CPButtonBar.j
+++ b/AppKit/CPButtonBar.j
@@ -14,8 +14,9 @@
 + (id)plusButton
 {
     var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
-        image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:self] pathForResource:@"plus_button.png"] size:CGSizeMake(11, 12)];
+        image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"plus_button.png"] size:CGSizeMake(11, 12)];
 
+    [button setBordered:NO];
     [button setImage:image];
     [button setImagePosition:CPImageOnly];
 
@@ -25,14 +26,30 @@
 + (id)minusButton
 {
     var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
-        image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:self] pathForResource:@"minus_button.png"] size:CGSizeMake(11, 4)];
+        image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"minus_button.png"] size:CGSizeMake(11, 4)];
 
+    [button setBordered:NO];
     [button setImage:image];
     [button setImagePosition:CPImageOnly];
 
     return button;
 }
 
++ (id)actionPopupButton
+{
+    var button = [[CPPopUpButton alloc] initWithFrame:CGRectMake(0, 0, 35, 25)],
+        image = [[CPImage alloc] initWithContentsOfFile:[[CPBundle bundleForClass:[CPButtonBar class]] pathForResource:@"action_button.png"] size:CGSizeMake(22, 14)];
+
+    [button addItemWithTitle:nil];
+    [[button lastItem] setImage:image];
+    [button setImagePosition:CPImageOnly];
+    [button setValue:CGInsetMake(0, 0, 0, 0) forThemeAttribute:"content-inset"];
+
+    [button setPullsDown:YES];
+
+    return button;
+}
+
 + (CPString)themeClass
 {
     return @"button-bar";
@@ -40,8 +57,8 @@
 
 + (id)themeAttributes
 {
-    return [CPDictionary dictionaryWithObjects:[CGInsetMake(0.0, 0.0, 0.0, 0.0), CGSizeMakeZero(), [CPNull null], [CPNull null], [CPNull null]]
-                                       forKeys:[@"resize-control-inset", @"resize-control-size", @"resize-control-color", @"bezel-color", @"button-bezel-color"]];
+    return [CPDictionary dictionaryWithObjects:[CGInsetMake(0.0, 0.0, 0.0, 0.0), CGSizeMakeZero(), [CPNull null], [CPNull null], [CPNull null], [CPNull null]]
+                                       forKeys:[@"resize-control-inset", @"resize-control-size", @"resize-control-color", @"bezel-color", @"button-bezel-color", @"button-text-color"]];
 }
 
 - (id)initWithFrame:(CGRect)aFrame
@@ -82,25 +99,8 @@
     _buttons = [CPArray arrayWithArray:buttons];
 
     for (var i = 0, count = [_buttons count]; i < count; i++)
-    {
-        var button = _buttons[i];
+        [_buttons[i] setBordered:YES];
 
-        var normalColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateNormal],
-            highlightedColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateHighlighted],
-            disabledColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateDisabled];
-
-        [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal|CPThemeStateBordered];    
-        [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted|CPThemeStateBordered];    
-        [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled|CPThemeStateBordered];    
-
-        // FIXME shouldn't need this
-        [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal|CPThemeStateBordered|CPPopUpButtonStatePullsDown];    
-        [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted|CPThemeStateBordered|CPPopUpButtonStatePullsDown];    
-        [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled|CPThemeStateBordered|CPPopUpButtonStatePullsDown];    
-
-        [button setBordered:YES];
-    }
-    
     [self setNeedsLayout];
 }
 
@@ -176,6 +176,11 @@
 {
     [self setBackgroundColor:[self currentValueForThemeAttribute:@"bezel-color"]];
 
+    var normalColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateNormal],
+        highlightedColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateHighlighted],
+        disabledColor = [self valueForThemeAttribute:@"button-bezel-color" inState:CPThemeStateDisabled],
+        textColor = [self valueForThemeAttribute:@"button-text-color" inState:CPThemeStateNormal];
+
     var buttonsNotHidden = [CPArray arrayWithArray:_buttons],
         count = [buttonsNotHidden count];
 
@@ -184,13 +189,23 @@
             [buttonsNotHidden removeObject:buttonsNotHidden[count]];
 
     var currentButtonOffset = _resizeControlIsLeftAligned ? CGRectGetMaxX([self bounds]) + 1 : -1,
-        height = CGRectGetHeight([self bounds]) - 1;
+        bounds = [self bounds],
+        height = CGRectGetHeight(bounds) - 1,
+        frameWidth = CGRectGetWidth(bounds),
+        resizeRect = _hasResizeControl ? [self rectForEphemeralSubviewNamed:"resize-control-view"] : CGRectMakeZero(),
+        resizeWidth = CGRectGetWidth(resizeRect),
+        availableWidth = frameWidth - resizeWidth - 1;
 
     for (var i = 0, count = [buttonsNotHidden count]; i < count; i++)
     {   
         var button = buttonsNotHidden[i],
             width = CGRectGetWidth([button frame]);
 
+        if (availableWidth > width)
+            availableWidth -=width;
+        else
+            break;
+
         if (_resizeControlIsLeftAligned)
         {
             [button setFrame:CGRectMake(currentButtonOffset - width, 1, width, height)];
@@ -202,6 +217,16 @@
             currentButtonOffset += width - 1;
         }
 
+        [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal|CPThemeStateBordered];    
+        [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted|CPThemeStateBordered];    
+        [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled|CPThemeStateBordered];    
+        [button setValue:textColor forThemeAttribute:@"text-color" inState:CPThemeStateBordered];    
+
+        // FIXME shouldn't need this
+        [button setValue:normalColor forThemeAttribute:@"bezel-color" inState:CPThemeStateNormal|CPThemeStateBordered|CPPopUpButtonStatePullsDown];    
+        [button setValue:highlightedColor forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted|CPThemeStateBordered|CPPopUpButtonStatePullsDown];    
+        [button setValue:disabledColor forThemeAttribute:@"bezel-color" inState:CPThemeStateDisabled|CPThemeStateBordered|CPPopUpButtonStatePullsDown];    
+
         [self addSubview:button];
     }
 
@@ -216,6 +241,12 @@
     }
 }
 
+- (void)setFrameSize:(CGSize)aSize
+{
+    [super setFrameSize:aSize];
+    [self setNeedsLayout];
+}
+
 @end
 
 var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
@@ -246,4 +277,3 @@ var CPButtonBarHasResizeControlKey = @"CPButtonBarHasResizeControlKey",
 }
 
 @end
-
diff --git a/AppKit/CPCheckBox.j b/AppKit/CPCheckBox.j
index 2c2852883..eec4de33b 100644
--- a/AppKit/CPCheckBox.j
+++ b/AppKit/CPCheckBox.j
@@ -61,4 +61,27 @@
     return self;
 }
 
+- (void)takeStateFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
+{
+    var count = objects.length,
+        value = [objects[0] valueForKeyPath:aKeyPath] ? CPOnState : CPOffState;
+
+    [self setAllowsMixedState:NO];
+    [self setState:value];
+
+    while (count-- > 1)
+    {
+        if (value !== ([objects[count] valueForKeyPath:aKeyPath] ? CPOnState : CPOffState))
+        {
+            [self setAllowsMixedState:YES];
+            [self setState:CPMixedState];
+        }
+    }
+}
+
+- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
+{
+    [self takeStateFromKeyPath:aKeyPath ofObjects:objects];
+}
+
 @end
diff --git a/AppKit/CPClipView.j b/AppKit/CPClipView.j
index f1dc67e10..16eabd711 100644
--- a/AppKit/CPClipView.j
+++ b/AppKit/CPClipView.j
@@ -190,17 +190,39 @@
 - (BOOL)autoscroll:(CPEvent)anEvent 
 {
     var bounds = [self bounds],
-        eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil];
+        eventLocation = [self convertPoint:[anEvent locationInWindow] fromView:nil],
+        superview = [self superview],
+        deltaX = 0,
+        deltaY = 0;
 
-    if (CPRectContainsPoint(bounds, eventLocation))
+    if (CGRectContainsPoint(bounds, eventLocation))
         return NO;
 
-    var newRect = CGRectMakeZero();
+    if (![superview isKindOfClass:[CPScrollView class]] || [superview hasVerticalScroller])
+    {
+        if (eventLocation.y < CGRectGetMinY(bounds))
+            deltaY = CGRectGetMinY(bounds) - eventLocation.y;
+        else if (eventLocation.y > CGRectGetMaxY(bounds))
+            deltaY = CGRectGetMaxY(bounds) - eventLocation.y;
+        if (deltaY < -bounds.size.height)
+            deltaY = -bounds.size.height;
+        if (deltaY > bounds.size.height)
+            deltaY = bounds.size.height;
+    }
 
-    newRect.origin = eventLocation;
-    newRect.size = CPSizeMake(10, 10);
+    if (![superview isKindOfClass:[CPScrollView class]] || [superview hasHorizontalScroller])
+    {
+        if (eventLocation.x < CGRectGetMinX(bounds))
+            deltaX = CGRectGetMinX(bounds) - eventLocation.x;
+        else if (eventLocation.x > CGRectGetMaxX(bounds))
+            deltaX = CGRectGetMaxX(bounds) - eventLocation.x;
+        if (deltaX < -bounds.size.width)
+            deltaX = -bounds.size.width;
+        if (deltaX > bounds.size.width)
+            deltaX = bounds.size.width;
+    }
 
-	return [_documentView scrollRectToVisible:newRect];
+	return [self scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
 }
 
 @end
diff --git a/AppKit/CPCollectionView.j b/AppKit/CPCollectionView.j
index cf91c32cd..77c4ca4d6 100644
--- a/AppKit/CPCollectionView.j
+++ b/AppKit/CPCollectionView.j
@@ -30,16 +30,16 @@
 @import "CPCollectionViewItem.j"
 
 
-/*! 
+/*!
     @ingroup appkit
     @class CPCollectionView
 
-    This class displays an array as a grid of objects, where each object is represented by a view. 
-    The view is controlled by creating a CPCollectionViewItem and specifying its view, then 
+    This class displays an array as a grid of objects, where each object is represented by a view.
+    The view is controlled by creating a CPCollectionViewItem and specifying its view, then
     setting that item as the collection view prototype.
-    
+
     @par Delegate Methods
-    
+
     @delegate -(void)collectionViewDidChangeSelection:(CPCollectionView)collectionView;
     Called when the selection in the collection view has changed.
     @param collectionView the collection view who's selection changed
@@ -67,35 +67,35 @@
 {
     CPArray                 _content;
     CPArray                 _items;
-    
+
     CPData                  _itemData;
     CPCollectionViewItem    _itemPrototype;
     CPCollectionViewItem    _itemForDragging;
     CPMutableArray          _cachedItems;
-    
+
     unsigned                _maxNumberOfRows;
     unsigned                _maxNumberOfColumns;
-    
+
     CGSize                  _minItemSize;
     CGSize                  _maxItemSize;
 
     CPArray                 _backgroundColors;
 
     float                   _tileWidth;
-    
+
     BOOL                    _isSelectable;
     BOOL                    _allowsMultipleSelection;
     BOOL                    _allowsEmptySelection;
     CPIndexSet              _selectionIndexes;
-    
+
     CGSize                  _itemSize;
-    
+
     float                   _horizontalMargin;
     float                   _verticalMargin;
-    
+
     unsigned                _numberOfRows;
     unsigned                _numberOfColumns;
-    
+
     id                      _delegate;
 
     CPEvent                 _mouseDownEvent;
@@ -104,14 +104,14 @@
 - (id)initWithFrame:(CGRect)aFrame
 {
     self = [super initWithFrame:aFrame];
-    
+
     if (self)
     {
         _items = [];
         _content = [];
-        
+
         _cachedItems = [];
-        
+
         _itemSize = CGSizeMakeZero();
         _minItemSize = CGSizeMakeZero();
         _maxItemSize = CGSizeMakeZero();
@@ -120,12 +120,12 @@
 
         _verticalMargin = 5.0;
         _tileWidth = -1.0;
-        
+
         _selectionIndexes = [CPIndexSet indexSet];
         _allowsEmptySelection = YES;
         _isSelectable = YES;
     }
-    
+
     return self;
 }
 
@@ -196,8 +196,8 @@
 
 // Setting the Content
 /*!
-    Sets the content of the collection view to the content in \c anArray. 
-    This array can be of any type, and each element will be passed to the \c -setRepresentedObject: method.  
+    Sets the content of the collection view to the content in \c anArray.
+    This array can be of any type, and each element will be passed to the \c -setRepresentedObject: method.
     It's the responsibility of your custom collection view item to interpret the object.
     @param anArray the content array
 */
@@ -205,9 +205,9 @@
 {
     if (_content == anArray)
         return;
-    
+
     _content = anArray;
-    
+
     [self reloadContent];
 }
 
@@ -236,13 +236,13 @@
 {
     if (_isSelectable == isSelectable)
         return;
-    
+
     _isSelectable = isSelectable;
-    
+
     if (!_isSelectable)
     {
         var index = CPNotFound;
-        
+
         while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
             [_items[index] setSelected:NO];
     }
@@ -299,19 +299,19 @@
 {
     if (_selectionIndexes == anIndexSet || !_isSelectable)
         return;
-    
+
     var index = CPNotFound;
-    
+
     while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
         [_items[index] setSelected:NO];
-    
+
     _selectionIndexes = anIndexSet;
-    
+
     var index = CPNotFound;
-    
+
     while ((index = [_selectionIndexes indexGreaterThanIndex:index]) != CPNotFound)
         [_items[index] setSelected:YES];
-    
+
     if ([_delegate respondsToSelector:@selector(collectionViewDidChangeSelection:)])
         [_delegate collectionViewDidChangeSelection:self]
 }
@@ -326,10 +326,10 @@
 
 /* @ignore */
 - (void)reloadContent
-{   
+{
     // Remove current views
     var count = _items.length;
-    
+
     while (count--)
     {
         [[_items[count] view] removeFromSuperview];
@@ -337,7 +337,7 @@
 
         _cachedItems.push(_items[count]);
     }
-    
+
     _items = [];
 
     if (!_itemPrototype || !_content)
@@ -350,7 +350,7 @@
     for (; index < count; ++index)
     {
         _items.push([self newItemForRepresentedObject:_content[index]]);
-    
+
         [self addSubview:[_items[index] view]];
     }
 
@@ -365,49 +365,49 @@
 - (void)tile
 {
     var width = CGRectGetWidth([self bounds]);
-        
+
     if (![_content count] || width == _tileWidth)
         return;
-        
-    // We try to fit as many views per row as possible.  Any remaining space is then 
+
+    // We try to fit as many views per row as possible.  Any remaining space is then
     // either proportioned out to the views (if their minSize != maxSize) or used as
     // margin
     var itemSize = CGSizeMakeCopy(_minItemSize);
-    
+
     _numberOfColumns = MAX(1.0, FLOOR(width / itemSize.width));
-    
+
     if (_maxNumberOfColumns > 0)
         _numberOfColumns = MIN(_maxNumberOfColumns, _numberOfColumns);
-            
+
     var remaining = width - _numberOfColumns * itemSize.width,
         itemsNeedSizeUpdate = NO;
-        
+
     if (remaining > 0 && itemSize.width < _maxItemSize.width)
         itemSize.width = MIN(_maxItemSize.width, itemSize.width + FLOOR(remaining / _numberOfColumns));
-    
+
     // When we ONE column and a non-integral width, the FLOORing above can cause the item width to be smaller than the total width.
     if (_maxNumberOfColumns == 1 && itemSize.width < _maxItemSize.width && itemSize.width < width)
         itemSize.width = MIN(_maxItemSize.width, width);
-    
+
     if (!CGSizeEqualToSize(_itemSize, itemSize))
     {
         _itemSize = itemSize;
         itemsNeedSizeUpdate = YES;
     }
-    
+
     var index = 0,
         count = _items.length;
-    
+
     if (_maxNumberOfColumns > 0 && _maxNumberOfRows > 0)
         count = MIN(count, _maxNumberOfColumns * _maxNumberOfRows);
-    
+
     _numberOfRows = CEIL(count / _numberOfColumns);
 
     _horizontalMargin = FLOOR((width - _numberOfColumns * itemSize.width) / (_numberOfColumns + 1));
-        
+
     var x = _horizontalMargin,
         y = -itemSize.height;
-    
+
     for (; index < count; ++index)
     {
         if (index % _numberOfColumns == 0)
@@ -415,19 +415,28 @@
             x = _horizontalMargin;
             y += _verticalMargin + itemSize.height;
         }
-        
+
         var view = [_items[index] view];
-        
+
         [view setFrameOrigin:CGPointMake(x, y)];
-        
+
         if (itemsNeedSizeUpdate)
             [view setFrameSize:_itemSize];
-            
+
         x += itemSize.width + _horizontalMargin;
     }
-    
+
+    var superview = [self superview],
+        proposedHeight =  y + itemSize.height + _verticalMargin;
+
+    if ([superview isKindOfClass:[CPClipView class]])
+    {
+        var superviewSize = [superview bounds].size;
+        proposedHeight = MAX(superviewSize.height, proposedHeight);
+    }
+
     _tileWidth = width;
-    [self setFrameSize:CGSizeMake(width, y + itemSize.height + _verticalMargin)];
+    [self setFrameSize:CGSizeMake(width, proposedHeight)];
     _tileWidth = -1.0;
 }
 
@@ -445,9 +454,9 @@
 {
     if (_maxNumberOfRows == aMaxNumberOfRows)
         return;
-    
+
     _maxNumberOfRows = aMaxNumberOfRows;
-    
+
     [self tile];
 }
 
@@ -467,9 +476,9 @@
 {
     if (_maxNumberOfColumns == aMaxNumberOfColumns)
         return;
-    
+
     _maxNumberOfColumns = aMaxNumberOfColumns;
-    
+
     [self tile];
 }
 
@@ -506,9 +515,9 @@
 {
     if (CGSizeEqualToSize(_minItemSize, aSize))
         return;
-    
+
     _minItemSize = CGSizeMakeCopy(aSize);
-    
+
     [self tile];
 }
 
@@ -528,9 +537,9 @@
 {
     if (CGSizeEqualToSize(_maxItemSize, aSize))
         return;
-    
+
     _maxItemSize = CGSizeMakeCopy(aSize);
-    
+
     [self tile];
 }
 
@@ -590,6 +599,14 @@
 
 - (void)mouseDragged:(CPEvent)anEvent
 {
+    var locationInWindow = [anEvent locationInWindow],
+        mouseDownLocationInWindow = [_mouseDownEvent locationInWindow];
+
+    // FIXME: This is because Safari's drag hysteresis is 3px x 3px
+    if ((ABS(locationInWindow.x - mouseDownLocationInWindow.x) < 3) &&
+        (ABS(locationInWindow.y - mouseDownLocationInWindow.y) < 3))
+        return;
+
     if (![_delegate respondsToSelector:@selector(collectionView:dragTypesForItemsAtIndexes:)])
         return;
 
@@ -648,9 +665,9 @@
 {
     if (_verticalMargin == aVerticalMargin)
         return;
-    
+
     _verticalMargin = aVerticalMargin;
-    
+
     [self tile];
 }
 
@@ -701,7 +718,7 @@
         count = [indexArray count];
 
     for (; index < count; ++index)
-        frame = CGRectUnion(frame, [self rectForItemAtIndex:indexArray[index]]);
+        frame = CGRectUnion(frame, [self frameForItemAtIndex:indexArray[index]]);
 
     return frame;
 }
@@ -721,7 +738,7 @@
 - (void)moveLeft:(id)sender
 {
     var index = [[self selectionIndexes] firstIndex];
-    if (index === CPNotFound) 
+    if (index === CPNotFound)
         index = [[self items] count];
 
     index = MAX(index - 1, 0);
@@ -749,7 +766,7 @@
 - (void)moveUp:(id)sender
 {
     var index = [[self selectionIndexes] firstIndex];
-    if (index == CPNotFound) 
+    if (index == CPNotFound)
         index = [[self items] count];
 
     index = MAX(0, index - [self numberOfColumns]);
@@ -821,20 +838,20 @@ var CPCollectionViewMinItemSizeKey      = @"CPCollectionViewMinItemSizeKey",
         _cachedItems = [];
 
         _itemSize = CGSizeMakeZero();
-        
+
         _minItemSize = [aCoder decodeSizeForKey:CPCollectionViewMinItemSizeKey] || CGSizeMakeZero();
         _maxItemSize = [aCoder decodeSizeForKey:CPCollectionViewMaxItemSizeKey] || CGSizeMakeZero();
-        
+
         _verticalMargin = [aCoder decodeFloatForKey:CPCollectionViewVerticalMarginKey];
-        
+
         _isSelectable = [aCoder decodeBoolForKey:CPCollectionViewSelectableKey];
 
         [self setBackgroundColors:[aCoder decodeObjectForKey:CPCollectionViewBackgroundColorsKey]];
-          
+
         _tileWidth = -1.0;
 
         _selectionIndexes = [CPIndexSet indexSet];
-        
+
         _allowsEmptySelection = YES;
     }
 
@@ -847,12 +864,12 @@ var CPCollectionViewMinItemSizeKey      = @"CPCollectionViewMinItemSizeKey",
 
     if (!CGSizeEqualToSize(_minItemSize, CGSizeMakeZero()))
       [aCoder encodeSize:_minItemSize forKey:CPCollectionViewMinItemSizeKey];
-    
+
     if (!CGSizeEqualToSize(_maxItemSize, CGSizeMakeZero()))
       [aCoder encodeSize:_maxItemSize forKey:CPCollectionViewMaxItemSizeKey];
-    
+
     [aCoder encodeBool:_isSelectable forKey:CPCollectionViewSelectableKey];
-    
+
     [aCoder encodeFloat:_verticalMargin forKey:CPCollectionViewVerticalMarginKey];
 
     [aCoder encodeObject:_backgroundColors forKey:CPCollectionViewBackgroundColorsKey];
diff --git a/AppKit/CPColorWell.j b/AppKit/CPColorWell.j
index 2c2586821..040e5c548 100644
--- a/AppKit/CPColorWell.j
+++ b/AppKit/CPColorWell.j
@@ -231,11 +231,17 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
 
 - (void)mouseDown:(CPEvent)anEvent
 {
+    if (![self isEnabled])
+        return;
+
     [self drawBezelWithHighlight:YES];
 }
 
 - (void)mouseDragged:(CPEvent)anEvent
 {
+    if (![self isEnabled])
+        return;
+
     [self drawBezelWithHighlight:CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil])];
 }
 
@@ -243,7 +249,7 @@ var _CPColorWellDidBecomeExclusiveNotification = @"_CPColorWellDidBecomeExclusiv
 {
     [self drawBezelWithHighlight:NO];
 
-    if (!CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]))
+    if (!CGRectContainsPoint([self bounds], [self convertPoint:[anEvent locationInWindow] fromView:nil]) || ![self isEnabled])
         return;
         
     [self activate:YES];
diff --git a/AppKit/CPCursor.j b/AppKit/CPCursor.j
index 95b1dc79a..45b3f1ab8 100755
--- a/AppKit/CPCursor.j
+++ b/AppKit/CPCursor.j
@@ -167,7 +167,9 @@ var currentCursor = nil,
 {
     if (CPBrowserIsEngine(CPWebKitBrowserEngine))
         return [CPCursor cursorWithCSSString:@"-webkit-grab"];
-    else if (CPBrowserIsEngine(CPGeckoBrowserEngine) || CPBrowserIsEngine(CPOperaBrowserEngine))
+    else if (CPBrowserIsEngine(CPGeckoBrowserEngine))
+        return [CPCursor cursorWithCSSString:@"-moz-grab"];
+    else if (CPBrowserIsEngine(CPOperaBrowserEngine))
         return [CPCursor cursorWithCSSString:@"move"];
 
     return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // WebKit only. move in FFMac|Opera 
@@ -177,8 +179,10 @@ var currentCursor = nil,
 {
     if (CPBrowserIsEngine(CPWebKitBrowserEngine))
         return [CPCursor cursorWithCSSString:@"-webkit-grabbing"];
+    else if (CPBrowserIsEngine(CPGeckoBrowserEngine))
+        return [CPCursor cursorWithCSSString:@"-moz-grabbing"];
 
-    return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // WebKit only 
+    return [CPCursor cursorWithImageNamed:CPStringFromSelector(_cmd)]; // WebKit || FF 
 }
 
 + (CPCursor)disappearingItemCursor
diff --git a/AppKit/CPDragServer.j b/AppKit/CPDragServer.j
index 06f8d2435..9469f331f 100644
--- a/AppKit/CPDragServer.j
+++ b/AppKit/CPDragServer.j
@@ -40,29 +40,10 @@ CPDragOperationEvery    = -1;
 
 #define DRAGGING_WINDOW(anObject) ([anObject isKindOfClass:[CPWindow class]] ? anObject : [anObject window])
 
-var    CPDragServerPreviousEvent      = nil,
-CPDragServerAutoscrollInterval = nil;
-/*
-var CPDragServerAutoscroll = function()
-{
-    [CPDragServerSource autoscroll:CPDragServerPreviousEvent];
-}
+var CPDragServerPreviousEvent = nil,
+    CPDragServerPeriodicUpdateInterval = 0.05;
 
-    if (CPDragServerAutoscrollInterval === nil)
-    {
-        if ([CPDragServerSource respondsToSelector:@selector(autoscroll:)])
-            CPDragServerAutoscrollInterval = setInterval(CPDragServerAutoscroll, 100);
-    }
-
-    CPDragServerPreviousEvent = anEvent;
-
-        if (CPDragServerAutoscrollInterval !== nil)
-            clearInterval(CPDragServerAutoscrollInterval);
-
-        CPDragServerAutoscrollInterval = nil;
-*/
-
-var CPSharedDragServer     = nil;
+var CPSharedDragServer = nil;
 
 var CPDragServerSource             = nil;
 var CPDragServerDraggingInfo       = nil;
@@ -150,10 +131,13 @@ var CPDraggingSource_draggedImage_movedTo_          = 1 << 0,
 
     CGPoint         _draggingLocation;
     id              _draggingDestination;
+    BOOL            _draggingDestinationWantsPeriodicUpdates;
 
     CGPoint         _startDragLocation;
     BOOL            _shouldSlideBack;
     unsigned        _dragOperation;
+
+    CPTimer         _draggingUpdateTimer;
 }
 
 /*
@@ -234,6 +218,9 @@ var CPDraggingSource_draggedImage_movedTo_          = 1 << 0,
 
 - (CPDragOperation)draggingUpdatedInPlatformWindow:(CPPlatformWindow)aPlatformWindow location:(CGPoint)aLocation
 {
+    [_draggingUpdateTimer invalidate];
+    _draggingUpdateTimer = nil;
+
     var dragOperation = CPDragOperationCopy;
     // We have to convert base to bridge since the drag event comes from the source window, not the drag window.
     var draggingDestination = [aPlatformWindow _dragHitTest:aLocation pasteboard:[CPDragServerDraggingInfo draggingPasteboard]];
@@ -243,25 +230,89 @@ var CPDraggingSource_draggedImage_movedTo_          = 1 << 0,
 
     if(draggingDestination !== _draggingDestination)
     {
-        if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingExited:)])
+        if ([_draggingDestination respondsToSelector:@selector(draggingExited:)])
             [_draggingDestination draggingExited:CPDragServerDraggingInfo];
 
         _draggingDestination = draggingDestination;
 
-        if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingEntered:)])
+        if ([_draggingDestination respondsToSelector:@selector(wantsPeriodicDraggingUpdates)])
+            _draggingDestinationWantsPeriodicUpdates = [_draggingDestination wantsPeriodicDraggingUpdates];
+        else
+            _draggingDestinationWantsPeriodicUpdates = YES;
+
+        if ([_draggingDestination respondsToSelector:@selector(draggingEntered:)])
             dragOperation = [_draggingDestination draggingEntered:CPDragServerDraggingInfo];
     }
-    else if (_draggingDestination && [_draggingDestination respondsToSelector:@selector(draggingUpdated:)])
+    else if ([_draggingDestination respondsToSelector:@selector(draggingUpdated:)])
         dragOperation = [_draggingDestination draggingUpdated:CPDragServerDraggingInfo];
 
     if (!_draggingDestination)
         dragOperation = CPDragOperationNone;
+    else
+    {
+        if (_draggingDestinationWantsPeriodicUpdates)
+            _draggingUpdateTimer = [CPTimer scheduledTimerWithTimeInterval:CPDragServerPeriodicUpdateInterval
+                                                                    target:self
+                                                                  selector:@selector(_sendPeriodicDraggingUpdate:)
+                                                                  userInfo:[CPDictionary dictionaryWithJSObject:{platformWindow:aPlatformWindow, location:aLocation}]
+                                                                   repeats:NO];
+
+        var scrollView = [_draggingDestination isKindOfClass:[CPView class]] ? [_draggingDestination enclosingScrollView] : nil;
+        if (scrollView)
+        {
+            var contentView = [scrollView contentView],
+                bounds = [contentView bounds],
+                insetBounds = CGRectInset(bounds, 10, 10)
+                eventLocation = [contentView convertPoint:_draggingLocation fromView:nil],
+                deltaX = 0,
+                deltaY = 0;
+
+            if (!CGRectContainsPoint(insetBounds, eventLocation))
+            {
+                if ([scrollView hasVerticalScroller])
+                {
+                    if (eventLocation.y < CGRectGetMinY(insetBounds))
+                        deltaY = CGRectGetMinY(insetBounds) - eventLocation.y;
+                    else if (eventLocation.y > CGRectGetMaxY(insetBounds))
+                        deltaY = CGRectGetMaxY(insetBounds) - eventLocation.y;
+                    if (deltaY < -insetBounds.size.height)
+                        deltaY = -insetBounds.size.height;
+                    if (deltaY > insetBounds.size.height)
+                        deltaY = insetBounds.size.height;
+                }
+
+                if ([scrollView hasHorizontalScroller])
+                {
+                    if (eventLocation.x < CGRectGetMinX(insetBounds))
+                        deltaX = CGRectGetMinX(insetBounds) - eventLocation.x;
+                    else if (eventLocation.x > CGRectGetMaxX(insetBounds))
+                        deltaX = CGRectGetMaxX(insetBounds) - eventLocation.x;
+                    if (deltaX < -insetBounds.size.width)
+                        deltaX = -insetBounds.size.width;
+                    if (deltaX > insetBounds.size.width)
+                        deltaX = insetBounds.size.width;
+                }
+
+                [contentView scrollToPoint:CGPointMake(bounds.origin.x - deltaX, bounds.origin.y - deltaY)];
+            }
+        }
+    }
 
     return dragOperation;
 }
 
+- (void)_sendPeriodicDraggingUpdate:(CPTimer)aTimer
+{
+    var userInfo = [aTimer userInfo];
+    _dragOperation = [self draggingUpdatedInPlatformWindow:[userInfo objectForKey:@"platformWindow"] 
+                                                  location:[userInfo objectForKey:@"location"]];
+}
+
 - (void)draggingEndedInPlatformWindow:(CPPlatformWindow)aPlatformWindow globalLocation:(CGPoint)aLocation operation:(CPDragOperation)anOperation
 {
+    [_draggingUpdateTimer invalidate];
+    _draggingUpdateTimer = nil;
+
     [_draggedView removeFromSuperview];
 
     if (![CPPlatform supportsDragAndDrop])
@@ -404,13 +455,25 @@ var CPDraggingSource_draggedImage_movedTo_          = 1 << 0,
         // Stop tracking events.
         return;
     }
-
-    [self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation];
-    _dragOperation = [self draggingUpdatedInPlatformWindow:platformWindow location:platformWindowLocation];
+    else if (type === CPKeyDown)
+    {
+        var keyCode = [anEvent keyCode];
+        if (keyCode === CPEscapeKeyCode)
+        {
+            _dragOperation = CPDragOperationNone;
+            [self draggingEndedInPlatformWindow:platformWindow globalLocation:CGPointMakeZero() operation:_dragOperation];
+            return;
+        }
+    }
+    else
+    {
+        [self draggingSourceUpdatedWithGlobalLocation:platformWindowLocation];
+        _dragOperation = [self draggingUpdatedInPlatformWindow:platformWindow location:platformWindowLocation];
+    }
 
     // If we're not a mouse up, then we're going to want to grab the next event.
     [CPApp setTarget:self selector:@selector(trackDragging:)
-        forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask
+        forNextEventMatchingMask:CPMouseMovedMask | CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPKeyDownMask
         untilDate:nil inMode:0 dequeue:NO];
 }
 
diff --git a/AppKit/CPImageView.j b/AppKit/CPImageView.j
index 234f74ceb..7d365f71f 100644
--- a/AppKit/CPImageView.j
+++ b/AppKit/CPImageView.j
@@ -365,7 +365,7 @@ var LEFT_SHADOW_INSET       = 3.0,
     return _isEditable;
 }
 
-- (void)performDragOperation:(CPDraggingInfo)aSender
+- (BOOL)performDragOperation:(CPDraggingInfo)aSender
 {
     var images = [CPKeyedUnarchiver unarchiveObjectWithData:[[aSender draggingPasteboard] dataForType:CPImagesPboardType]];
 
@@ -374,6 +374,8 @@ var LEFT_SHADOW_INSET       = 3.0,
         [self setImage:images[0]];
         [self sendAction:[self action] to:[self target]];
     }
+    
+    return YES;
 }
 
 @end
diff --git a/AppKit/CPMenu/CPMenu.j b/AppKit/CPMenu/CPMenu.j
index 1116437b7..687fb7045 100644
--- a/AppKit/CPMenu/CPMenu.j
+++ b/AppKit/CPMenu/CPMenu.j
@@ -747,6 +747,9 @@ var _CPMenuBarVisible               = NO,
     }
 
     // Show it.
+    if ([CPPlatform isBrowser])
+        [menuWindow setPlatformWindow:[[aView window] platformWindow]];
+
     [menuWindow orderFront:self];
 
     // Track it.
@@ -816,6 +819,9 @@ var _CPMenuBarVisible               = NO,
         [menuWindow setFrameOrigin:CGRectIntersection(unconstrainedFrame, constraintRect).origin];
     }
 
+    if ([CPPlatform isBrowser])
+        [menuWindow setPlatformWindow:[[aView window] platformWindow]];
+
     [menuWindow orderFront:self];
 
     [[_CPMenuManager sharedMenuManager]
diff --git a/AppKit/CPOutlineView.j b/AppKit/CPOutlineView.j
index 6711eba89..df90c688d 100644
--- a/AppKit/CPOutlineView.j
+++ b/AppKit/CPOutlineView.j
@@ -85,6 +85,8 @@ CPOutlineViewDropOnItemIndex = -1;
     
     BOOL            _shouldRetargetChildIndex;
     CPInteger       _retargedChildIndex;
+    CPTimer         _dragHoverTimer;
+    id              _dropItem;
 }
 
 - (id)initWithFrame:(CGRect)aFrame
@@ -108,6 +110,7 @@ CPOutlineViewDropOnItemIndex = -1;
         
         _retargedChildIndex = nil;
         _shouldRetargetChildIndex = NO;
+        _startHoverTime = nil;
 
         [self setIndentationPerLevel:16.0];
         [self setIndentationMarkerFollowsDataView:YES];
@@ -213,7 +216,7 @@ CPOutlineViewDropOnItemIndex = -1;
 - (void)expandItem:(id)anItem expandChildren:(BOOL)shouldExpandChildren
 {
     var itemInfo = null;
-    
+
     if (!anItem)
         itemInfo = _rootItemInfo;
     else
@@ -221,10 +224,16 @@ CPOutlineViewDropOnItemIndex = -1;
 
     if (!itemInfo)
         return;
-    
-    itemInfo.isExpanded = YES;
-    [self reloadItem:anItem reloadChildren:YES];
-    
+
+    // to prevent items which are already expanded from firing notifications
+    if (!itemInfo.isExpanded)
+    {
+        [self _noteItemWillExpand:anItem];
+        itemInfo.isExpanded = YES;
+        [self _noteItemDidExpand:anItem];
+        [self reloadItem:anItem reloadChildren:YES];
+    }
+
     if (shouldExpandChildren)
     {
         var children = itemInfo.children,
@@ -248,7 +257,9 @@ CPOutlineViewDropOnItemIndex = -1;
     if (!itemInfo.isExpanded)
         return;
 
+    [self _noteItemWillCollapse:anItem];
     itemInfo.isExpanded = NO;
+    [self _noteItemDidCollapse:anItem];
 
     [self reloadItem:anItem reloadChildren:YES];
 }
@@ -382,43 +393,14 @@ CPOutlineViewDropOnItemIndex = -1;
     return frame;
 }
 
-- (void)selectRowIndexes:(CPIndexSet)rows byExtendingSelection:(BOOL)shouldExtendSelection
+- (void)_performSelection:(BOOL)select forRow:(CPInteger)rowIndex context:(id)context
 {
-    // First un highlight the old disclosure controls   
-    var previousSelectedRows = [];
-    [[self selectedRowIndexes] getIndexes:previousSelectedRows maxCount:-1 inIndexRange:nil];
+    [super _performSelection:select forRow:rowIndex context:context];
     
-    var index = [previousSelectedRows count];
-    while (index--)
-    {
-        var rowIndex = previousSelectedRows[index],
-            item = [self itemAtRow:rowIndex];
+    var control = _disclosureControlsForRows[rowIndex],
+        selector = select ? @"setThemeState:" : @"unsetThemeState:";
         
-        if (![self isExpandable:item])
-            continue;
-        
-        var control = _disclosureControlsForRows[rowIndex];
-        [control setHighlighted:NO];
-    }
-    
-    [super selectRowIndexes:rows byExtendingSelection:shouldExtendSelection];
-    
-    // Now highlight the new disclosure controls
-    var selectedRows = [];
-        [rows getIndexes:selectedRows maxCount:-1 inIndexRange:nil];
-    
-        var index = [selectedRows count];
-    while (index--)
-    {
-        var rowIndex = selectedRows[index],
-            item = [self itemAtRow:rowIndex];
-        
-        if (![self isExpandable:item])
-            continue;
-        
-        var control = _disclosureControlsForRows[rowIndex];
-        [control setHighlighted:YES];
-    }
+    [control performSelector:CPSelectorFromString(selector) withObject:CPThemeStateSelected];
 }
 
 - (void)setDelegate:(id)aDelegate
@@ -453,6 +435,35 @@ CPOutlineViewDropOnItemIndex = -1;
                 removeObserver:_outlineViewDelegate
                           name:CPOutlineViewSelectionIsChangingNotification
                         object:self];
+
+
+
+        if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewItemWillExpand:)])
+            [defaultCenter
+                removeObserver:_outlineViewDelegate
+                          name:CPOutlineViewItemWillExpandNotification
+                        object:self];
+
+
+        if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewItemDidExpand:)])
+            [defaultCenter
+                removeObserver:_outlineViewDelegate
+                          name:CPOutlineViewItemDidExpandNotification
+                        object:self];
+
+
+        if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewItemWillCollapse:)])
+            [defaultCenter
+                removeObserver:_outlineViewDelegate
+                          name:CPOutlineViewItemWillCollapseNotification
+                        object:self];
+
+
+        if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewItemDidCollapse:)])
+            [defaultCenter
+                removeObserver:_outlineViewDelegate
+                          name:CPOutlineViewItemDidCollapseNotification
+                        object:self];
     }
 
     _outlineViewDelegate = aDelegate;
@@ -500,6 +511,36 @@ CPOutlineViewDropOnItemIndex = -1;
             selector:@selector(outlineViewSelectionIsChanging:)
             name:CPOutlineViewSelectionIsChangingNotification
             object:self];
+
+
+    if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewItemWillExpand:)])
+        [defaultCenter
+            addObserver:_outlineViewDelegate
+            selector:@selector(outlineViewItemWillExpand:)
+            name:CPOutlineViewItemWillExpandNotification
+            object:self];
+
+    if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewItemDidExpand:)])
+        [defaultCenter
+            addObserver:_outlineViewDelegate
+            selector:@selector(outlineViewItemDidExpand:)
+            name:CPOutlineViewItemDidExpandNotification
+            object:self];
+
+    if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewItemWillCollapse:)])
+        [defaultCenter
+            addObserver:_outlineViewDelegate
+            selector:@selector(outlineViewItemWillCollapse:)
+            name:CPOutlineViewItemWillCollapseNotification
+            object:self];
+
+    if ([_outlineViewDelegate respondsToSelector:@selector(outlineViewItemDidCollapse:)])
+        [defaultCenter
+            addObserver:_outlineViewDelegate
+            selector:@selector(outlineViewItemDidCollapse:)
+            name:CPOutlineViewItemDidCollapseNotification
+            object:self];
+
 }
 
 - (id)delegate
@@ -534,11 +575,48 @@ CPOutlineViewDropOnItemIndex = -1;
 
 - (void)setDropItem:(id)theItem dropChildIndex:(int)theIndex
 {
+    if (_dropItem !== theItem && theIndex < 0 && [self isExpandable:theItem] && ![self isItemExpanded:theItem])
+    {
+        if (_dragHoverTimer)
+            [_dragHoverTimer invalidate];
+
+        var autoExpandCallBack = function(){
+            if (_dropItem)
+            {
+                [_dropOperationFeedbackView blink];
+                [CPTimer scheduledTimerWithTimeInterval:.3 callback:objj_msgSend(self, "expandItem:", _dropItem) repeats:NO]; //[self expandItem:_dropItem];
+            }
+        }
+
+        _dragHoverTimer = [CPTimer scheduledTimerWithTimeInterval:.8 callback:autoExpandCallBack repeats:NO];
+    }
+
+    if (theIndex >= 0)
+    {
+        [_dragHoverTimer invalidate];
+        _dragHoverTimer = nil;
+    }
+
+    _dropItem = theItem;
     _retargetedItem = theItem;
     _shouldRetargetItem = YES;
     
     _retargedChildIndex = theIndex;
     _shouldRetargetChildIndex = YES;
+    
+    // set CPTableView's _retargetedDropRow based on retargetedItem and retargetedChildIndex
+    var retargetedItemInfo = (_retargetedItem !== nil) ? _itemInfosForItems[[_retargetedItem UID]] : _rootItemInfo,
+        retargetedChildItem = (_retargedChildIndex !== CPOutlineViewDropOnItemIndex) ? retargetedItemInfo.children[_retargedChildIndex] : _retargetedItem;
+
+    _retargetedDropRow = [self rowForItem:retargetedChildItem];
+}
+
+- (void)_draggingEnded
+{
+    [super _draggingEnded];
+    _dropItem = nil;
+    [_dragHoverTimer invalidate];
+    _dragHoverTimer = nil;
 }
 
 - (id)_parentItemForUpperRow:(int)theUpperRowIndex andLowerRow:(int)theLowerRowIndex atMouseOffset:(CPPoint)theOffset
@@ -619,6 +697,8 @@ CPOutlineViewDropOnItemIndex = -1;
         _disclosureControlsForRows[row] = control;
 
         [control setState:[self isItemExpanded:item] ? CPOnState : CPOffState];
+        var selector = [self isRowSelected:row] ? @"setThemeState:" : @"unsetThemeState:";
+        [control performSelector:CPSelectorFromString(selector) withObject:CPThemeStateSelected];
         [control setFrame:frame];
 
         [self addSubview:control];
@@ -709,6 +789,38 @@ CPOutlineViewDropOnItemIndex = -1;
                     userInfo:nil];
 }
 
+- (void)_noteItemWillExpand:(id)item
+{
+    [[CPNotificationCenter defaultCenter]
+        postNotificationName:CPOutlineViewItemWillExpandNotification
+                      object:self
+                    userInfo:[CPDictionary dictionaryWithObject:item forKey:"CPObject"]];
+}
+
+- (void)_noteItemDidExpand:(id)item
+{
+    [[CPNotificationCenter defaultCenter]
+        postNotificationName:CPOutlineViewItemDidExpandNotification
+                      object:self
+                    userInfo:[CPDictionary dictionaryWithObject:item forKey:"CPObject"]];
+}
+
+- (void)_noteItemWillCollapse:(id)item
+{
+    [[CPNotificationCenter defaultCenter]
+        postNotificationName:CPOutlineViewItemWillCollapseNotification
+                      object:self
+                    userInfo:[CPDictionary dictionaryWithObject:item forKey:"CPObject"]];
+}
+
+- (void)_noteItemDidCollapse:(id)item
+{
+    [[CPNotificationCenter defaultCenter]
+        postNotificationName:CPOutlineViewItemDidCollapseNotification
+                      object:self
+                    userInfo:[CPDictionary dictionaryWithObject:item forKey:"CPObject"]];
+}
+
 @end
 
 // FIX ME: We're using with() here because Safari fails if we use anOutlineView._itemInfosForItems or whatever... 
@@ -880,6 +992,13 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt
     return [_outlineView._outlineViewDataSource outlineView:_outlineView objectValueForTableColumn:aTableColumn byItem:_outlineView._itemsForRows[aRow]];
 }
 
+- (void)tableView:(CPTableView)aTableView setObjectValue:(id)aValue forTableColumn:(CPTableColumn)aColumn row:(CPInteger)aRow
+{
+    if (!(_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_setObjectValue_forTableColumn_byItem_))
+        return;
+    [_outlineView._outlineViewDataSource outlineView:_outlineView setObjectValue:aValue forTableColumn:aColumn byItem:_outlineView._itemsForRows[aRow]];
+}
+
 - (BOOL)tableView:(CPTableView)aTableColumn writeRowsWithIndexes:(CPIndexSet)theIndexes toPasteboard:(CPPasteboard)thePasteboard
 {
     if (!(_outlineView._implementedOutlineViewDataSourceMethods & CPOutlineViewDataSource_outlineView_writeItems_toPasteboard_))
@@ -1007,6 +1126,14 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt
     return YES;
 }
 
+- (BOOL)tableView:(CPTableView)aTableView shouldEditTableColumn:(CPTableColumn)aColumn row:(int)aRow
+{
+    if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_shouldEditTableColumn_item_))
+        return [_outlineView._outlineViewDelegate outlineView:_outlineView shouldEditTableColumn:aColumn item:[_outlineView itemAtRow:aRow]];
+
+    return NO;
+}
+
 - (float)tableView:(CPTableView)theTableView heightOfRow:(int)theRow
 {
     if ((_outlineView._implementedOutlineViewDelegateMethods & CPOutlineViewDelegate_outlineView_heightOfRowByItem_))
@@ -1080,8 +1207,10 @@ var _loadItemInfoForItem = function(/*CPOutlineView*/ anOutlineView, /*id*/ anIt
     CGContextAddLineToPoint(context, 0.0, 0.0);
 
     CGContextClosePath(context);
-
-    CGContextSetFillColor(context, [self isHighlighted] ? [CPColor whiteColor] : [CPColor grayColor]);
+    var isHighlighted = [self hasThemeState:CPThemeStateHighlighted];
+    var color = [self hasThemeState:CPThemeStateSelected] ? (isHighlighted ? [CPColor lightGrayColor] : [CPColor whiteColor]) : (isHighlighted ? [CPColor blackColor] : [CPColor grayColor]);
+    
+    CGContextSetFillColor(context, color);
     CGContextFillPath(context);
 }
 
diff --git a/AppKit/CPPopUpButton.j b/AppKit/CPPopUpButton.j
index 08a340626..b5ac0e1e1 100644
--- a/AppKit/CPPopUpButton.j
+++ b/AppKit/CPPopUpButton.j
@@ -32,7 +32,7 @@ var VISIBLE_MARGIN  = 7.0;
 
 CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 
-/*! 
+/*!
     @ingroup appkit
     @class CPPopUpButton
 
@@ -42,7 +42,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 {
     int         _selectedIndex;
     CPRectEdge  _preferredEdge;
-    
+
     CPMenu      _menu;
 }
 
@@ -60,21 +60,21 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 - (id)initWithFrame:(CGRect)aFrame pullsDown:(BOOL)shouldPullDown
 {
     self = [super initWithFrame:aFrame];
-    
+
     if (self)
     {
         _selectedIndex = CPNotFound;
         _preferredEdge = CPMaxYEdge;
-        
+
         [self setValue:CPImageLeft forThemeAttribute:@"image-position"];
         [self setValue:CPLeftTextAlignment forThemeAttribute:@"alignment"];
         [self setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
-        
+
         [self setMenu:[[CPMenu alloc] initWithTitle:@""]];
 
         [self setPullsDown:shouldPullDown];
     }
-    
+
     return self;
 }
 
@@ -145,7 +145,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 {
     var index = 0,
         count = [titles count];
-    
+
     for (; index < count; ++index)
         [self addItemWithTitle:titles[index]];
 }
@@ -159,11 +159,11 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 {
     var items = [self itemArray],
         count = [items count];
-    
+
     while (count--)
         if ([items[count] title] == aTitle)
             [self removeItemAtIndex:count];
-        
+
     [_menu insertItemWithTitle:aTitle action:NULL keyEquivalent:nil atIndex:anIndex];
 }
 
@@ -173,7 +173,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 - (void)removeAllItems
 {
     var count = [_menu numberOfItems];
-    
+
     while (count--)
         [_menu removeItemAtIndex:0];
 }
@@ -206,7 +206,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 {
     if (_selectedIndex < 0 || _selectedIndex > [self numberOfItems] - 1)
         return nil;
-    
+
     return [_menu itemAtIndex:_selectedIndex];
 }
 
@@ -253,15 +253,15 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 {
     if (_selectedIndex == anIndex)
         return;
-    
+
     if (_selectedIndex >= 0 && ![self pullsDown])
         [[self selectedItem] setState:CPOffState];
-    
+
     _selectedIndex = anIndex;
 
     if (_selectedIndex >= 0 && ![self pullsDown])
         [[self selectedItem] setState:CPOnState];
-    
+
     [self synchronizeTitleAndSelectedItem];
 }
 
@@ -310,7 +310,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
         return;
 
     var defaultCenter = [CPNotificationCenter defaultCenter];
-    
+
     if (_menu)
     {
         [defaultCenter
@@ -330,7 +330,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
     }
 
     _menu = aMenu;
-    
+
     if (_menu)
     {
         [defaultCenter
@@ -338,20 +338,20 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
               selector:@selector(menuDidAddItem:)
                   name:CPMenuDidAddItemNotification
                 object:_menu];
-    
+
         [defaultCenter
             addObserver:self
               selector:@selector(menuDidChangeItem:)
                   name:CPMenuDidChangeItemNotification
                 object:_menu];
-                
+
         [defaultCenter
             addObserver:self
               selector:@selector(menuDidRemoveItem:)
                   name:CPMenuDidRemoveItemNotification
                 object:_menu];
     }
-    
+
     [self synchronizeTitleAndSelectedItem];
 }
 
@@ -396,12 +396,13 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 {
     var titles = [],
         items = [self itemArray],
-        
         index = 0,
         count = [items count];
-    
+
     for (; index < count; ++index)
-        items.push([items[index] title]);
+        titles.push([items[index] title]);
+
+    return titles;
 }
 
 /*!
@@ -519,14 +520,14 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
     else
     {
         var index = [self indexOfItemWithTitle:aTitle];
-        
+
         if (index < 0)
         {
             [self addItemWithTitle:aTitle];
-            
+
             index = [self numberOfItems] - 1;
         }
-        
+
         [self selectItemAtIndex:index];
     }
 }
@@ -554,7 +555,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
     if ([self pullsDown])
     {
         var items = [_menu itemArray];
-        
+
         if ([items count] > 0)
             item = items[0];
     }
@@ -573,22 +574,22 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 - (void)menuDidAddItem:(CPNotification)aNotification
 {
     var index = [[aNotification userInfo] objectForKey:@"CPMenuItemIndex"];
-    
+
     if (_selectedIndex < 0)
         [self selectItemAtIndex:0];
-    
+
     else if (index == _selectedIndex)
         [self synchronizeTitleAndSelectedItem];
-        
+
     else if (index < _selectedIndex)
         ++_selectedIndex;
-        
+
     if (index == 0 && [self pullsDown])
     {
         var items = [_menu itemArray];
-        
+
         [items[0] setHidden:YES];
-        
+
         if (items.length > 0)
             [items[1] setHidden:NO];
     }
@@ -613,10 +614,10 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 
     if ([self pullsDown] && index != 0)
         return;
-    
+
     if (![self pullsDown] && index != _selectedIndex)
         return;
-    
+
     [self synchronizeTitleAndSelectedItem];
 }
 
@@ -627,7 +628,7 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
 - (void)menuDidRemoveItem:(CPNotification)aNotification
 {
     var numberOfItems = [self numberOfItems];
-    
+
     if (numberOfItems <= _selectedIndex && numberOfItems > 0)
         [self selectItemAtIndex:numberOfItems - 1];
     else
@@ -703,6 +704,23 @@ CPPopUpButtonStatePullsDown = CPThemeState("pulls-down");
     [self sendAction:[self action] to:[self target]];
 }
 
+- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
+{
+    var count = objects.length,
+        value = [objects[0] valueForKeyPath:aKeyPath];
+
+    [self selectItemWithTag:value];
+    [self setEnabled:YES];
+
+    while (count-- > 1)
+    {
+        if (value !== [objects[count] valueForKeyPath:aKeyPath])
+        {
+            [[self selectedItem] setState:CPOffState];
+        }
+    }
+}
+
 @end
 
 var CPPopUpButtonMenuKey            = @"CPPopUpButtonMenuKey",
@@ -720,7 +738,7 @@ var CPPopUpButtonMenuKey            = @"CPPopUpButtonMenuKey",
 - (id)initWithCoder:(CPCoder)aCoder
 {
     self = [super initWithCoder:aCoder];
-    
+
     if (self)
     {
         // Nothing is currently selected
@@ -729,7 +747,7 @@ var CPPopUpButtonMenuKey            = @"CPPopUpButtonMenuKey",
         [self setMenu:[aCoder decodeObjectForKey:CPPopUpButtonMenuKey]];
         [self selectItemAtIndex:[aCoder decodeObjectForKey:CPPopUpButtonSelectedIndexKey]];
     }
-    
+
     return self;
 }
 
@@ -741,7 +759,7 @@ var CPPopUpButtonMenuKey            = @"CPPopUpButtonMenuKey",
 - (void)encodeWithCoder:(CPCoder)aCoder
 {
     [super encodeWithCoder:aCoder];
-    
+
     [aCoder encodeObject:_menu forKey:CPPopUpButtonMenuKey];
     [aCoder encodeInt:_selectedIndex forKey:CPPopUpButtonSelectedIndexKey];
 }
diff --git a/AppKit/CPScrollView.j b/AppKit/CPScrollView.j
index 82badfea4..051195b66 100644
--- a/AppKit/CPScrollView.j
+++ b/AppKit/CPScrollView.j
@@ -27,7 +27,7 @@
 #include "CoreGraphics/CGGeometry.h"
 
 
-/*! 
+/*!
     @ingroup appkit
     @class CPScrollView
 
@@ -44,12 +44,12 @@
     BOOL        _hasVerticalScroller;
     BOOL        _hasHorizontalScroller;
     BOOL        _autohidesScrollers;
-    
+
     CPScroller  _verticalScroller;
     CPScroller  _horizontalScroller;
-    
+
     int         _recursionCount;
-    
+
     float       _verticalLineScroll;
     float       _verticalPageScroll;
     float       _horizontalLineScroll;
@@ -158,11 +158,11 @@
 
     if (_recursionCount > 5)
         return;
-    
+
     ++_recursionCount;
 
     var documentView = [self documentView];
-    
+
     if (!documentView)
     {
         if (_autohidesScrollers)
@@ -250,7 +250,7 @@
         [_verticalScroller setFloatValue:0.0];
         [_verticalScroller setKnobProportion:1.0];
     }
-    
+
     if (shouldShowHorizontalScroller)
     {
         [_horizontalScroller setFloatValue:(difference.width <= 0.0) ? 0.0 : scrollPoint.x / difference.width];
@@ -279,13 +279,13 @@
 {
     if (_horizontalScroller === aScroller)
         return;
-    
+
     [_horizontalScroller removeFromSuperview];
     [_horizontalScroller setTarget:nil];
     [_horizontalScroller setAction:nil];
-    
+
     _horizontalScroller = aScroller;
-    
+
     [_horizontalScroller setTarget:self];
     [_horizontalScroller setAction:@selector(_horizontalScrollerDidScroll:)];
 
@@ -339,13 +339,13 @@
 {
     if (_verticalScroller === aScroller)
         return;
-    
+
     [_verticalScroller removeFromSuperview];
     [_verticalScroller setTarget:nil];
     [_verticalScroller setAction:nil];
-    
+
     _verticalScroller = aScroller;
-    
+
     [_verticalScroller setTarget:self];
     [_verticalScroller setAction:@selector(_verticalScrollerDidScroll:)];
 
@@ -403,7 +403,7 @@
         return;
 
     _autohidesScrollers = autohidesScrollers;
-    
+
     [self reflectScrolledClipView:_contentView];
 }
 
@@ -445,9 +445,7 @@
 
 - (CPView)_headerView
 {
-    var headerClipViewSubviews = [_headerClipView subviews];
-
-    return [headerClipViewSubviews count] ? headerClipViewSubviews[0] : nil;
+    return [_headerClipView documentView];
 }
 
 - (CGRect)_cornerViewFrame
@@ -482,30 +480,30 @@
 /* @ignore */
 - (void)_verticalScrollerDidScroll:(CPScroller)aScroller
 {
-   var  value = [aScroller floatValue],
-        documentFrame = [[_contentView documentView] frame];
+    var value = [aScroller floatValue],
+        documentFrame = [[_contentView documentView] frame],
         contentBounds = [_contentView bounds];
 
     switch ([_verticalScroller hitPart])
     {
         case CPScrollerDecrementLine:   contentBounds.origin.y -= _verticalLineScroll;
                                         break;
-        
+
         case CPScrollerIncrementLine:   contentBounds.origin.y += _verticalLineScroll;
                                         break;
-           
+
         case CPScrollerDecrementPage:   contentBounds.origin.y -= _CGRectGetHeight(contentBounds) - _verticalPageScroll;
                                         break;
-        
+
         case CPScrollerIncrementPage:   contentBounds.origin.y += _CGRectGetHeight(contentBounds) - _verticalPageScroll;
                                         break;
-        
+
         case CPScrollerKnobSlot:
         case CPScrollerKnob:
                                         // We want integral bounds!
         default:                        contentBounds.origin.y = ROUND(value * (_CGRectGetHeight(documentFrame) - _CGRectGetHeight(contentBounds)));
     }
-    
+
     [_contentView scrollToPoint:contentBounds.origin];
 }
 
@@ -515,21 +513,21 @@
    var value = [aScroller floatValue],
        documentFrame = [[self documentView] frame],
        contentBounds = [_contentView bounds];
-        
+
     switch ([_horizontalScroller hitPart])
     {
         case CPScrollerDecrementLine:   contentBounds.origin.x -= _horizontalLineScroll;
                                         break;
-        
+
         case CPScrollerIncrementLine:   contentBounds.origin.x += _horizontalLineScroll;
                                         break;
-           
+
         case CPScrollerDecrementPage:   contentBounds.origin.x -= _CGRectGetWidth(contentBounds) - _horizontalPageScroll;
                                         break;
-        
+
         case CPScrollerIncrementPage:   contentBounds.origin.x += _CGRectGetWidth(contentBounds) - _horizontalPageScroll;
                                         break;
-        
+
         case CPScrollerKnobSlot:
         case CPScrollerKnob:
                                         // We want integral bounds!
@@ -670,15 +668,32 @@
 */
 - (void)scrollWheel:(CPEvent)anEvent
 {
-   var documentFrame = [[self documentView] frame],
-       contentBounds = [_contentView bounds];
+    [self _respondToScrollWheelEventWithDeltaX:[anEvent deltaX] * _horizontalLineScroll
+                                        deltaY:[anEvent deltaY] * _verticalLineScroll];
+}
+
+- (void)_respondToScrollWheelEventWithDeltaX:(float)deltaX deltaY:(float)deltaY
+{
+    var documentFrame = [[self documentView] frame],
+        contentBounds = [_contentView bounds],
+        contentFrame = [_contentView frame],
+        enclosingScrollView = [self enclosingScrollView],
+        extraX = 0,
+        extraY = 0;
 
     // We want integral bounds!
-    contentBounds.origin.x = ROUND(contentBounds.origin.x + [anEvent deltaX] * _horizontalLineScroll);
-    contentBounds.origin.y = ROUND(contentBounds.origin.y + [anEvent deltaY] * _verticalLineScroll);
+    contentBounds.origin.x = ROUND(contentBounds.origin.x + deltaX);
+    contentBounds.origin.y = ROUND(contentBounds.origin.y + deltaY);
 
-    [_contentView scrollToPoint:contentBounds.origin];
-    [_headerClipView scrollToPoint:CGPointMake(contentBounds.origin.x, 0.0)];
+    var constrainedOrigin = [_contentView constrainScrollPoint:CGPointCreateCopy(contentBounds.origin)];
+    extraX = ((contentBounds.origin.x - constrainedOrigin.x) / _horizontalLineScroll) * [enclosingScrollView horizontalLineScroll];
+    extraY = ((contentBounds.origin.y - constrainedOrigin.y) / _verticalLineScroll) * [enclosingScrollView verticalLineScroll];
+
+    [_contentView scrollToPoint:constrainedOrigin];
+    [_headerClipView scrollToPoint:CGPointMake(constrainedOrigin.x, 0.0)];
+
+    if (extraX || extraY)
+        [enclosingScrollView _respondToScrollWheelEventWithDeltaX:extraX deltaY:extraY];
 }
 
 - (void)keyDown:(CPEvent)anEvent
@@ -756,10 +771,16 @@ var CPScrollViewContentViewKey       = "CPScrollViewContentView",
 
         _horizontalLineScroll   = [aCoder decodeFloatForKey:CPScrollViewHLineScrollKey];
         _horizontalPageScroll   = [aCoder decodeFloatForKey:CPScrollViewHPageScrollKey];
-        
+
         _contentView            = [aCoder decodeObjectForKey:CPScrollViewContentViewKey];
         _headerClipView         = [aCoder decodeObjectForKey:CPScrollViewHeaderClipViewKey];
-        
+
+        if (!_headerClipView)
+        {
+            _headerClipView = [[CPClipView alloc] init];
+            [self addSubview:_headerClipView];
+        }
+
         _verticalScroller       = [aCoder decodeObjectForKey:CPScrollViewVScrollerKey];
         _horizontalScroller     = [aCoder decodeObjectForKey:CPScrollViewHScrollerKey];
 
@@ -772,25 +793,25 @@ var CPScrollViewContentViewKey       = "CPScrollViewContentView",
         // Do to the anything goes nature of decoding, our subviews may not exist yet, so layout at the end of the run loop when we're sure everything is in a correct state.
         [[CPRunLoop currentRunLoop] performSelector:@selector(reflectScrolledClipView:) target:self argument:_contentView order:0 modes:[CPDefaultRunLoopMode]];
     }
-    
+
     return self;
 }
 
 - (void)encodeWithCoder:(CPCoder)aCoder
 {
     [super encodeWithCoder:aCoder];
-    
+
     [aCoder encodeObject:_contentView           forKey:CPScrollViewContentViewKey];
     [aCoder encodeObject:_headerClipView        forKey:CPScrollViewHeaderClipViewKey];
 
     [aCoder encodeObject:_verticalScroller      forKey:CPScrollViewVScrollerKey];
     [aCoder encodeObject:_horizontalScroller    forKey:CPScrollViewHScrollerKey];
-    
+
     [aCoder encodeFloat:_verticalLineScroll     forKey:CPScrollViewVLineScrollKey];
     [aCoder encodeFloat:_verticalPageScroll     forKey:CPScrollViewVPageScrollKey];
     [aCoder encodeFloat:_horizontalLineScroll   forKey:CPScrollViewHLineScrollKey];
     [aCoder encodeFloat:_horizontalPageScroll   forKey:CPScrollViewHPageScrollKey];
-    
+
     [aCoder encodeBool:_hasVerticalScroller     forKey:CPScrollViewHasVScrollerKey];
     [aCoder encodeBool:_hasHorizontalScroller   forKey:CPScrollViewHasHScrollerKey];
     [aCoder encodeBool:_autohidesScrollers      forKey:CPScrollViewAutohidesScrollerKey];
diff --git a/AppKit/CPSearchField.j b/AppKit/CPSearchField.j
index 50528fb0d..f5fdea74a 100644
--- a/AppKit/CPSearchField.j
+++ b/AppKit/CPSearchField.j
@@ -581,7 +581,7 @@ var CPSearchFieldSearchImage = nil,
 
 - (void)_sendPartialString
 {
-    [[self target] performSelector:[self action] withObject:self];
+    [self _sendAction:self];
 }
 
 - (void)_searchFieldCancel:(id)sender
diff --git a/AppKit/CPSlider.j b/AppKit/CPSlider.j
index 76ea7db96..b448becd0 100644
--- a/AppKit/CPSlider.j
+++ b/AppKit/CPSlider.j
@@ -396,6 +396,18 @@ CPCircularSlider    = 1;
         _sendActionOn &= ~CPLeftMouseDraggedMask;
 }
 
+- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
+{
+    var count = objects.length,
+        value = [objects[0] valueForKeyPath:aKeyPath];
+
+    [self setObjectValue:value];
+
+    while (count-- > 1)
+        if (value !== ([objects[count] valueForKeyPath:aKeyPath]))
+            return [self setFloatValue:1.0];
+}
+
 @end
 
 var CPSliderMinValueKey             = "CPSliderMinValueKey",
diff --git a/AppKit/CPSplitView.j b/AppKit/CPSplitView.j
index d7c2bb72e..70f1a9282 100644
--- a/AppKit/CPSplitView.j
+++ b/AppKit/CPSplitView.j
@@ -217,10 +217,9 @@ var CPSplitViewHorizontalImage = nil,
         _DOMDividerElements[_drawingDivider].style.backgroundRepeat = "repeat";
 
         CPDOMDisplayServerAppendChild(_DOMElement, _DOMDividerElements[_drawingDivider]);
-
-        [self _setupDOMDivider];
     }
 
+    [self _setupDOMDivider];
     CPDOMDisplayServerSetStyleLeftTop(_DOMDividerElements[_drawingDivider], NULL, _CGRectGetMinX(aRect), _CGRectGetMinY(aRect));
     CPDOMDisplayServerSetStyleSize(_DOMDividerElements[_drawingDivider], _CGRectGetWidth(aRect), _CGRectGetHeight(aRect));
 #endif
diff --git a/AppKit/CPTableColumn.j b/AppKit/CPTableColumn.j
index 13a025811..b1720cf56 100644
--- a/AppKit/CPTableColumn.j
+++ b/AppKit/CPTableColumn.j
@@ -79,11 +79,16 @@ CPTableColumnUserResizingMask   = 1 << 1;
         [self setHeaderView:header];
         
         var textDataView = [CPTextField new];
-        [textDataView setLineBreakMode:CPLineBreakByTruncatingTail];
-        [textDataView setValue:[CPColor colorWithHexString:@"333333"] forThemeAttribute:@"text-color"];
-        [textDataView setValue:[CPColor whiteColor] forThemeAttribute:@"text-color" inState:CPThemeStateSelected];
-        [textDataView setValue:[CPFont boldSystemFontOfSize:12] forThemeAttribute:@"font" inState:CPThemeStateSelected];
+
+        [textDataView setValue:[CPColor colorWithRed:51.0 / 255.0 green:51.0 / 255.0 blue:51.0 / 255.0 alpha:1.0] 
+          forThemeAttribute:"text-color"];
+
+        [textDataView setValue:[CPColor whiteColor] forThemeAttribute:@"text-color" inState:CPThemeStateSelectedDataView];
+        [textDataView setLineBreakMode:CPLineBreakByTruncatingTail];  
+        [textDataView setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateSelectedDataView];
         [textDataView setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"];
+        [textDataView setValue:CGInsetMake(0.0, 0.0, 0.0, 5.0) forThemeAttribute:@"content-inset"];
+
         [self setDataView:textDataView];
     }
 
@@ -348,7 +353,14 @@ CPTableColumnUserResizingMask   = 1 << 1;
 
 - (void)setHidden:(BOOL)shouldBeHidden
 {
+    shouldBeHidden = !!shouldBeHidden
+    if (_isHidden === shouldBeHidden)
+        return;
+    
     _isHidden = shouldBeHidden;
+    
+    [[self headerView] setHidden:shouldBeHidden];
+    [[self tableView] _tableColumnVisibilityDidChange:self];
 }
 
 - (BOOL)isHidden
@@ -427,7 +439,10 @@ var CPTableColumnIdentifierKey   = @"CPTableColumnIdentifierKey",
     CPTableColumnWidthKey        = @"CPTableColumnWidthKey",
     CPTableColumnMinWidthKey     = @"CPTableColumnMinWidthKey",
     CPTableColumnMaxWidthKey     = @"CPTableColumnMaxWidthKey",
-    CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey";
+    CPTableColumnResizingMaskKey = @"CPTableColumnResizingMaskKey",
+    CPTableColumnIsHiddenkey     = @"CPTableColumnIsHiddenKey",
+    CPSortDescriptorPrototypeKey = @"CPSortDescriptorPrototypeKey";
+    CPTableColumnIsHiddenkey     = @"CPTableColumnIsHiddenKey";
 
 @implementation CPTableColumn (CPCoding)
 
@@ -449,6 +464,9 @@ var CPTableColumnIdentifierKey   = @"CPTableColumnIdentifierKey",
         [self setHeaderView:[aCoder decodeObjectForKey:CPTableColumnHeaderViewKey]];
 
         _resizingMask  = [aCoder decodeBoolForKey:CPTableColumnResizingMaskKey];
+        _isHidden = [aCoder decodeBoolForKey:CPTableColumnIsHiddenkey];
+        
+        _sortDescriptorPrototype = [aCoder decodeObjectForKey:CPSortDescriptorPrototypeKey];
     }
 
     return self;
@@ -466,6 +484,9 @@ var CPTableColumnIdentifierKey   = @"CPTableColumnIdentifierKey",
     [aCoder encodeObject:_dataView forKey:CPTableColumnDataViewKey];
 
     [aCoder encodeObject:_resizingMask forKey:CPTableColumnResizingMaskKey];
+    [aCoder encodeBool:_isHidden forKey:CPTableColumnIsHiddenkey];
+    
+    [aCoder encodeObject:_sortDescriptorPrototype forKey:CPSortDescriptorPrototypeKey];
 }
 
 @end
diff --git a/AppKit/CPTableHeaderView.j b/AppKit/CPTableHeaderView.j
index 8d8d3b9ec..76f71f426 100644
--- a/AppKit/CPTableHeaderView.j
+++ b/AppKit/CPTableHeaderView.j
@@ -26,7 +26,7 @@
  
 @implementation _CPTableColumnHeaderView : CPView
 {
-    _CPImageAndTextView _textField;
+    _CPImageAndTextView     _textField;
 }
 
 - (void)initWithFrame:(CGRect)frame
@@ -36,17 +36,19 @@
     {   
         [self _init];
     }
-    
+
     return self;
 }
 
 - (void)_init
 {
-    _textField = [[_CPImageAndTextView alloc] initWithFrame:CGRectMake(5, 1, CGRectGetWidth([self bounds]) - 10, CGRectGetHeight([self bounds]) - 1)];
+    _textField = [[_CPImageAndTextView alloc] initWithFrame:
+        CGRectMake(5.0, 0.0, CGRectGetWidth([self bounds]) - 10.0, CGRectGetHeight([self bounds]))];
+        
     [_textField setAutoresizingMask:CPViewWidthSizable|CPViewHeightSizable];
-    
+
     [_textField setLineBreakMode:CPLineBreakByTruncatingTail];
-    [_textField setTextColor: [CPColor colorWithHexString: @"333333"]];
+    [_textField setTextColor:[CPColor colorWithRed:51.0 / 255.0 green:51.0 / 255.0 blue:51.0 / 255.0 alpha:1.0]];
     [_textField setFont:[CPFont boldSystemFontOfSize:12.0]];
     [_textField setAlignment:CPLeftTextAlignment];
     [_textField setVerticalAlignment:CPCenterVerticalTextAlignment];
@@ -61,13 +63,13 @@
     var themeState = [self themeState];
 
     if(themeState & CPThemeStateSelected && themeState & CPThemeStateHighlighted)
-        [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview-highlighted-pressed.png", CGSizeMake(1.0, 22.0))]];
+        [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview-highlighted-pressed.png", CGSizeMake(1.0, 23.0))]];
     else if (themeState & CPThemeStateSelected)
-        [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview-highlighted.png", CGSizeMake(1.0, 22.0))]];
+        [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview-highlighted.png", CGSizeMake(1.0, 23.0))]];
     else if (themeState & CPThemeStateHighlighted)
-        [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview-pressed.png", CGSizeMake(1.0, 22.0))]];
+        [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview-pressed.png", CGSizeMake(1.0, 23.0))]];
     else 
-        [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview.png", CGSizeMake(1.0, 22.0))]];
+        [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview.png", CGSizeMake(1.0, 23.0))]];
 }
 
 - (void)setStringValue:(CPString)string
@@ -137,7 +139,7 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
 - (void)encodeWithCoder:(CPCoder)aCoder
 {
     [super encodeWithCoder:aCoder];
-    
+
     [aCoder encodeObject:[_textField text] forKey:_CPTableColumnHeaderViewStringValueKey];
     [aCoder encodeObject:[_textField image] forKey:_CPTableColumnHeaderViewImageKey];
     [aCoder encodeObject:[_textField font] forKey:_CPTableColumnHeaderViewFontKey];
@@ -147,27 +149,34 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
 
 @implementation CPTableHeaderView : CPView
 {
-    int _resizedColumn @accessors(readonly, property=resizedColumn);
-    int _draggedColumn @accessors(readonly, property=draggedColumn);
-    int _pressedColumn @accessors(readonly, property=pressedColumn);
-    
-    float _draggedDistance @accessors(readonly, property=draggedDistance);
-    float _lastLocation;
-    float _columnOldWidth;
-    
-    CPTableView _tableView @accessors(property=tableView);
+    CPPoint                 _mouseDownLocation;
+    CPPoint                 _previousTrackingLocation;
+    int                     _activeColumn;
+    int                     _pressedColumn;
+
+    BOOL                    _isResizing;
+    BOOL                    _isDragging;
+    BOOL                    _isTrackingColumn;
+
+    float                   _columnOldWidth;
+
+    CPTableView             _tableView @accessors(property=tableView);
 }
 
 - (void)_init
 {
-    _resizedColumn = -1;
-    _draggedColumn = -1;
+    _mouseDownLocation = CPPointMakeZero();
+    _previousTrackingLocation = CPPointMakeZero();
+    _activeColumn = -1;
     _pressedColumn = -1;
-    _draggedDistance = 0.0;
-    _lastLocation = nil;
-    _columnOldWidth = nil;
 
-    [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview.png", CGSizeMake(1.0, 22.0))]];
+    _isResizing = NO;
+    _isDragging = NO;
+    _isTrackingColumn = NO;
+
+    _columnOldWidth = 0.0;
+
+    [self setBackgroundColor:[CPColor colorWithPatternImage:CPAppKitImage("tableview-headerview.png", CGSizeMake(1.0, 23.0))]];
 }
 
 - (id)initWithFrame:(CGRect)aFrame
@@ -182,26 +191,18 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
 
 - (int)columnAtPoint:(CGPoint)aPoint
 {
-    return [_tableView columnAtPoint:CGPointMake(aPoint.x, 0)];
+    return [_tableView columnAtPoint:CGPointMake(aPoint.x, aPoint.y)];
 }
 
 - (CGRect)headerRectOfColumn:(int)aColumnIndex
 {
-    var tableColumns = [_tableView tableColumns];
+    var headerRect = [self bounds],
+        columnRect = [_tableView rectOfColumn:aColumnIndex];
 
-    if (aColumnIndex < 0 || aColumnIndex > [tableColumns count])
-        [CPException raise:"invalid" reason:"tried to get headerRectOfColumn: on invalid column"];
+    headerRect.origin.x = CPRectGetMinX(columnRect);
+    headerRect.size.width = CPRectGetWidth(columnRect);
 
-    // UPDATE COLUMN RANGES ?
-        
-    var tableRange = _tableView._tableColumnRanges[aColumnIndex],
-        bounds = [self bounds];
-
-    var rMinX = ROUND(tableRange.location);
-    bounds.origin.x = rMinX;
-    bounds.size.width = FLOOR(tableRange.length + tableRange.location - rMinX);
-    
-    return bounds;
+    return headerRect;
 }
 
 - (CGRect)_cursorRectForColumn:(int)column
@@ -224,116 +225,292 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
         var headerView = [_tableView._tableColumns[_pressedColumn] headerView];
         [headerView unsetThemeState:CPThemeStateHighlighted];
     }    
-    
+
     if (column != -1)
     {
         var headerView = [_tableView._tableColumns[column] headerView];
         [headerView setThemeState:CPThemeStateHighlighted];
     }
-    
+
     _pressedColumn = column;
 }
 
 - (void)mouseDown:(CPEvent)theEvent
 {
-    var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],
-        clickedColumn = [self columnAtPoint:mouseLocation];    
-    
-    // should we send column -1 ?
-    [_tableView _sendDelegateDidMouseDownInHeader:clickedColumn];
-    
-    var resizeLocation = CGPointMake(mouseLocation.x - 5, mouseLocation.y),
-        resizedColumn = [self columnAtPoint:resizeLocation];
-    
-    if (resizedColumn == -1)
-        return;
-
-    // 2 different tracking methods: one for resizing/stop-resizing, another one for selection/reordering
-    if ([_tableView allowsColumnResizing]
-        && CGRectContainsPoint([self _cursorRectForColumn:resizedColumn], mouseLocation))
-    {
-        _resizedColumn = resizedColumn;
-        [_tableView._tableColumns[_resizedColumn] setDisableResizingPosting:YES];
-        [_tableView setDisableAutomaticResizing:YES];
-        [self trackResizeWithEvent:theEvent];
-    }
-    else
-    {
-        [self _setPressedColumn:clickedColumn];
-        [self trackMouseWithEvent:theEvent];
-    }    
+    [self trackMouse:theEvent];
 }
 
-- (void)trackMouseWithEvent:(CPEvent)theEvent
+- (void)trackMouse:(CPEvent)theEvent
 {
-    var type = [theEvent type];
-    
-    if (type == CPLeftMouseUp)
-    {
-        var location = [self convertPoint:[theEvent locationInWindow] fromView:nil],
-            clickedColumn = [self columnAtPoint:location];
+    var type = [theEvent type],
+        currentLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil];
 
-        [self _setPressedColumn:-1];
-        
-        if (clickedColumn != -1)
-            [_tableView _didClickTableColumn:clickedColumn modifierFlags:[theEvent modifierFlags]];
-        
-        return;
-    }
+    // Take the right columns resize tracking area into account
+    currentLocation.x -= 5.0;
 
-    [CPApp setTarget:self selector:@selector(trackMouseWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask | CPLeftMouseDownMask untilDate:nil inMode:nil dequeue:YES];
-}
-
-- (void)trackResizeWithEvent:(CPEvent)anEvent
-{
-    var location = [self convertPoint:[anEvent locationInWindow] fromView:nil],
-        tableColumn = [[_tableView tableColumns] objectAtIndex:_resizedColumn],
-        type = [anEvent type];
-
-    if (_lastLocation == nil)
-        _lastLocation = location;
-
-    if (_columnOldWidth == nil)
-        _columnOldWidth = [tableColumn width];
+    var columnIndex = [self columnAtPoint:currentLocation],
+        shouldResize = [self shouldResizeTableColumn:columnIndex at:CPPointMake(currentLocation.x + 5.0, currentLocation.y)];
 
     if (type === CPLeftMouseUp)
-    {   
-        [self _updateResizeCursor:anEvent];
-                
-        [tableColumn _postDidResizeNotificationWithOldWidth:_columnOldWidth];
-        [tableColumn setDisableResizingPosting:NO];        
-        [_tableView setDisableAutomaticResizing:NO];
-
-        _resizedColumn = -1;
-        _lastLocation = nil;
-        _columnOldWidth = nil;
-
-        return;
-    }            
-    else if (type === CPLeftMouseDragged)
     {
-        var newWidth = [tableColumn width] + location.x - _lastLocation.x;
-        
-        if (newWidth < [tableColumn minWidth])
-            [[CPCursor resizeRightCursor] set];
-        else if (newWidth > [tableColumn maxWidth])
-            [[CPCursor resizeLeftCursor] set];
+        if (shouldResize)
+            [self stopResizingTableColumn:_activeColumn at:currentLocation];
+        else if ([self _shouldStopTrackingTableColumn:columnIndex at:currentLocation])
+        {
+            [_tableView _didClickTableColumn:columnIndex modifierFlags:[theEvent modifierFlags]];
+            [self stopTrackingTableColumn:columnIndex at:currentLocation];
+
+            _isTrackingColumn = NO;
+        }
+
+        [self _updateResizeCursor:[CPApp currentEvent]];
+
+        _activeColumn = CPNotFound;
+        return;
+    }
+
+    if (type === CPLeftMouseDown)
+    {
+        if (columnIndex === -1)
+            return;
+
+        _mouseDownLocation = currentLocation;
+        _activeColumn = columnIndex;
+
+        [_tableView _sendDelegateDidMouseDownInHeader:columnIndex];
+
+        if (shouldResize)
+            [self startResizingTableColumn:columnIndex at:currentLocation];
         else
         {
-            _tableView._lastColumnShouldSnap = NO;
-            [tableColumn setWidth:newWidth];
-            // FIXME: there has to be a better way to do this...
-            // We should refactor the auto resizing crap.
-            // We need to figure out the exact cocoa behavior here though. 
-            _lastLocation = location;
+            [self startTrackingTableColumn:columnIndex at:currentLocation];
+            _isTrackingColumn = YES;
+        }
+    }
+    else if (type === CPLeftMouseDragged)
+    {
+        if (shouldResize)
+            [self continueResizingTableColumn:_activeColumn at:currentLocation];
+        else
+        {
+            if (_activeColumn === columnIndex && CPRectContainsPoint([self headerRectOfColumn:columnIndex], currentLocation))
+            {
+                if (_isTrackingColumn && _pressedColumn !== -1)
+                {
+                    if (![self continueTrackingTableColumn:columnIndex at:currentLocation])
+                        return; // Stop tracking the column, because it's being dragged
+                } else
+                    [self startTrackingTableColumn:columnIndex at:currentLocation];
 
-            [[CPCursor resizeLeftRightCursor] set];
-            [self setNeedsLayout];
-            [self setNeedsDisplay:YES];
+            } else if (_isTrackingColumn && _pressedColumn !== -1)
+                [self stopTrackingTableColumn:_activeColumn at:currentLocation];
         }
     }
 
-    [CPApp setTarget:self selector:@selector(trackResizeWithEvent:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
+    _previousTrackingLocation = currentLocation;
+    [CPApp setTarget:self selector:@selector(trackMouse:) forNextEventMatchingMask:CPLeftMouseDraggedMask | CPLeftMouseUpMask untilDate:nil inMode:nil dequeue:YES];
+}
+
+- (void)startTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+{
+    [self _setPressedColumn:aColumnIndex];
+}
+
+- (BOOL)continueTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+{
+    if ([self _shouldDragTableColumn:aColumnIndex at:aPoint])
+    {
+        var columnRect = [self headerRectOfColumn:aColumnIndex],
+            offset = CPPointMakeZero(),
+            view = [_tableView _dragViewForColumn:aColumnIndex event:[CPApp currentEvent] offset:offset],
+            viewLocation = CPPointMakeZero();
+
+        viewLocation.x = ( CPRectGetMinX(columnRect) + offset.x ) + ( aPoint.x - _mouseDownLocation.x );
+        viewLocation.y = CPRectGetMinY(columnRect) + offset.y;
+
+        [self dragView:view at:viewLocation offset:CPSizeMakeZero() event:[CPApp currentEvent] 
+            pasteboard:[CPPasteboard pasteboardWithName:CPDragPboard] source:self slideBack:YES];
+
+        return NO;
+    }
+
+    return YES;
+}
+
+- (BOOL)_shouldStopTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+{
+    return _isTrackingColumn && _activeColumn === aColumnIndex && 
+        CPRectContainsPoint([self headerRectOfColumn:aColumnIndex], aPoint);
+}
+
+- (void)stopTrackingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+{
+    [self _setPressedColumn:CPNotFound];
+    [self _updateResizeCursor:[CPApp currentEvent]];
+}
+
+- (BOOL)_shouldDragTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+{
+    return [_tableView allowsColumnReordering] && ABS(aPoint.x - _mouseDownLocation.x) >= 10.0;
+}
+
+- (CPRect)_headerRectOfLastVisibleColumn
+{
+    var tableColumns = [_tableView tableColumns],
+        columnIndex = [tableColumns count];
+
+    while (columnIndex--)
+    {
+        var tableColumn = [tableColumns objectAtIndex:columnIndex];
+
+        if (![tableColumn isHidden])
+            return [self headerRectOfColumn:columnIndex];
+    }
+
+    return nil;
+}
+
+- (void)_constrainDragView:(CPView)theDragView at:(CPPoint)aPoint
+{
+    var tableColumns = [_tableView tableColumns],
+        lastColumnRect = [self _headerRectOfLastVisibleColumn];
+        activeColumnRect = [self headerRectOfColumn:_activeColumn];
+        dragWindow = [theDragView window],
+        frame = [dragWindow frame];
+
+    // Convert the frame origin from the global coordinate system to the windows' coordinate system
+    frame.origin = [[self window] convertGlobalToBase:frame.origin];
+    // the from the window to the view
+    frame.origin = [self convertPoint:frame.origin fromView:nil];
+
+    // This effectively clamps the value between the minimum and maximum
+    frame.origin.x = MAX(0.0, MIN(CGRectGetMinX(frame), CGRectGetMaxX(lastColumnRect) - CGRectGetWidth(activeColumnRect)));
+
+    // Make sure the column cannot move vertically
+    frame.origin.y = CPRectGetMinY(lastColumnRect);
+
+    // Convert the calculated origin back to the window coordinate system
+    frame.origin = [self convertPoint:frame.origin toView:nil];
+    // Then back to the global coordinate system
+    frame.origin = [[self window] convertBaseToGlobal:frame.origin];
+
+    [dragWindow setFrame:frame];
+}
+
+- (void)_moveColumn:(int)aFromIndex toColumn:(int)aToIndex
+{
+    [_tableView moveColumn:aFromIndex toColumn:aToIndex];
+    _activeColumn = aToIndex;
+    _pressedColumn = _activeColumn;
+
+    [_tableView _setDraggedColumn:_activeColumn];
+}
+
+- (void)draggedView:(CPView)aView beganAt:(CPPoint)aPoint
+{
+    _isDragging = YES;
+
+    [[[[_tableView tableColumns] objectAtIndex:_activeColumn] headerView] setHidden:YES];
+    [_tableView _setDraggedColumn:_activeColumn];
+
+    [self setNeedsDisplay:YES];
+}
+
+- (void)draggedView:(CPView)aView movedTo:(CPPoint)aPoint
+{
+    [self _constrainDragView:aView at:aPoint];
+
+    var dragWindow = [aView window],
+        dragWindowFrame = [dragWindow frame];
+
+    var hoverPoint = CGPointCreateCopy(aPoint);
+
+    if (aPoint.x < _previousTrackingLocation.x)
+        hoverPoint = CGPointMake(CGRectGetMinX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
+    else if (aPoint.x > _previousTrackingLocation.x)
+        hoverPoint = CGPointMake(CGRectGetMaxX(dragWindowFrame), CGRectGetMinY(dragWindowFrame));
+
+    // Convert the hover point from the global coordinate system to windows' coordinate system
+    hoverPoint = [[self window] convertGlobalToBase:hoverPoint];
+    // then to the view
+    hoverPoint = [self convertPoint:hoverPoint fromView:nil];
+
+    var hoveredColumn = [self columnAtPoint:hoverPoint];
+
+    if (hoveredColumn !== -1)
+    {
+        var columnRect = [self headerRectOfColumn:hoveredColumn],
+            columnCenterPoint = [self convertPoint:CGPointMake(CGRectGetMidX(columnRect), CGRectGetMidY(columnRect)) fromView:self];
+        if (hoveredColumn < _activeColumn && hoverPoint.x < columnCenterPoint.x)
+            [self _moveColumn:_activeColumn toColumn:hoveredColumn];
+        else if (hoveredColumn > _activeColumn && hoverPoint.x > columnCenterPoint.x)
+            [self _moveColumn:_activeColumn toColumn:hoveredColumn];
+    }
+
+    _previousTrackingLocation = aPoint;
+}
+
+- (void)draggedView:(CPImage)aView endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
+{
+    _isDragging = NO;
+    _isTrackingColumn = NO; // We need to do this explicitly because the mouse up section of trackMouse is never reached
+
+    [_tableView _setDraggedColumn:-1];
+    [[[[_tableView tableColumns] objectAtIndex:_activeColumn] headerView] setHidden:NO];
+    [self stopTrackingTableColumn:_activeColumn at:aLocation];
+
+    [self setNeedsDisplay:YES];
+}
+
+- (BOOL)shouldResizeTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+{
+    if (_isResizing)
+        return YES;
+
+    if (_isTrackingColumn)
+        return NO;
+
+    return [_tableView allowsColumnResizing] && CPRectContainsPoint([self _cursorRectForColumn:aColumnIndex], aPoint);
+}
+
+- (void)startResizingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+{
+    _isResizing = YES;
+
+    var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
+
+    [tableColumn setDisableResizingPosting:YES];
+    [_tableView setDisableAutomaticResizing:YES];
+}
+
+- (void)continueResizingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+{
+    var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex],
+        newWidth = [tableColumn width] + aPoint.x - _previousTrackingLocation.x;
+
+    if (newWidth < [tableColumn minWidth])
+        [[CPCursor resizeRightCursor] set];
+    else if (newWidth > [tableColumn maxWidth])
+        [[CPCursor resizeLeftCursor] set];
+    else
+    {
+        _tableView._lastColumnShouldSnap = NO;
+        [tableColumn setWidth:newWidth];
+
+        [[CPCursor resizeLeftRightCursor] set];
+        [self setNeedsLayout];
+        [self setNeedsDisplay:YES];
+    }
+}
+
+- (void)stopResizingTableColumn:(int)aColumnIndex at:(CPPoint)aPoint
+{
+    var tableColumn = [[_tableView tableColumns] objectAtIndex:aColumnIndex];
+    [tableColumn _postDidResizeNotificationWithOldWidth:_columnOldWidth];
+    [tableColumn setDisableResizingPosting:NO];
+    [_tableView setDisableAutomaticResizing:NO];
+
+    _isResizing = NO;
 }
 
 - (void)_updateResizeCursor:(CPEvent)theEvent
@@ -348,12 +525,12 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
     var mouseLocation = [self convertPoint:[theEvent locationInWindow] fromView:nil],    
         mouseOverLocation = CGPointMake(mouseLocation.x - 5, mouseLocation.y),
         overColumn = [self columnAtPoint:mouseOverLocation];
-    
+
     if (overColumn >= 0 && CGRectContainsPoint([self _cursorRectForColumn:overColumn], mouseLocation))
     {
         var tableColumn = [[_tableView tableColumns] objectAtIndex:overColumn],
             width = [tableColumn width];
-            
+
         if (width == [tableColumn minWidth])
             [[CPCursor resizeRightCursor] set];
         else if (width == [tableColumn maxWidth])
@@ -365,12 +542,6 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
         [[CPCursor arrowCursor] set];
 }
 
-- (void)viewDidMoveToWindow
-{
-    //if ([_tableView allowsColumnResizing])
-    //    [[self window] setAcceptsMouseMovedEvents:YES];
-}
-
 - (void)mouseEntered:(CPEvent)theEvent
 {   
     [self _updateResizeCursor:theEvent];
@@ -391,12 +562,12 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
 {
     var tableColumns = [_tableView tableColumns],
         count = [tableColumns count];    
-    
+
     for (var i = 0; i < count; i++) 
     {
         var column = [tableColumns objectAtIndex:i],
             headerView = [column headerView];
-        
+
         var frame = [self headerRectOfColumn:i];
         frame.size.height -= 0.5;
         if (i > 0) 
@@ -404,9 +575,9 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
             frame.origin.x += 0.5;
             frame.size.width -= 1;
         }
-        
+
         [headerView setFrame:frame];
-        
+
         if([headerView superview] != self)
             [self addSubview:headerView];
     }
@@ -433,43 +604,28 @@ var _CPTableColumnHeaderViewStringValueKey = @"_CPTableColumnHeaderViewStringVal
     var columnArrayIndex = 0,
         columnArrayCount = columnsArray.length,
         columnMaxX;
-        
+
     CGContextBeginPath(context);
     for(; columnArrayIndex < columnArrayCount; columnArrayIndex++)
     {
         // grab each column rect and add vertical lines
         var columnIndex = columnsArray[columnArrayIndex],
             columnToStroke = [self headerRectOfColumn:columnIndex];
-            
+
         columnMaxX = CGRectGetMaxX(columnToStroke);
-        
+
         CGContextMoveToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(CGRectGetMinY(columnToStroke)));
         CGContextAddLineToPoint(context, ROUND(columnMaxX) + 0.5, ROUND(CGRectGetMaxY(columnToStroke)));
     }
-    
+
     CGContextClosePath(context);
     CGContextStrokePath(context);
-        
-/*
-    var maxY = CGRectGetMaxY([self bounds]);
-    // draw normal gradient for remaining space
-    if (supportsCanvasGradient)
+
+    if (_isDragging)
     {
-        aRect.origin.x = columnMaxX - 0.5;
-        aRect.size.width -= columnMaxX;
-        CGContextBeginPath(context);
-        CGContextAddRect(context, CGRectMake(columnMaxX + 1, 0, CGRectGetMaxX([self bounds]) - columnMaxX, CGRectGetHeight([self bounds])));
-        CGContextClosePath(context);    
-        CGContextDrawLinearGradient(context, [_CPTableColumnHeaderView headerGradient], CGPointMake(0,0), CGPointMake(0, maxY - 1),0);
-    }   
-   
-    // Draw bottom line
-    CGContextBeginPath(context);    
-    CGContextMoveToPoint(context, 0, maxY - 0.5);
-    CGContextAddLineToPoint(context, CGRectGetMaxX([self bounds]), maxY - 0.5);
-    CGContextClosePath(context);
-    CGContextStrokePath(context);
-*/   
+        CGContextSetFillColor(context, [CPColor grayColor]);
+        CGContextFillRect(context, [self headerRectOfColumn:_activeColumn])
+    }
 }
 
 @end
@@ -495,5 +651,4 @@ var CPTableHeaderViewTableViewKey = @"CPTableHeaderViewTableViewKey";
     [aCoder encodeObject:_tableView forKey:CPTableHeaderViewTableViewKey];
 }
 
-@end
-
+@end
\ No newline at end of file
diff --git a/AppKit/CPTableView.j b/AppKit/CPTableView.j
index 4e4c536dd..88fe5d5cb 100644
--- a/AppKit/CPTableView.j
+++ b/AppKit/CPTableView.j
@@ -28,7 +28,6 @@
 @import "_CPCornerView.j"
 @import "CPScroller.j"
 
-
 CPTableViewColumnDidMoveNotification        = @"CPTableViewColumnDidMoveNotification";
 CPTableViewColumnDidResizeNotification      = @"CPTableViewColumnDidResizeNotification";
 CPTableViewSelectionDidChangeNotification   = @"CPTableViewSelectionDidChangeNotification";
@@ -75,6 +74,10 @@ CPTableViewDraggingDestinationFeedbackStyleSourceList = 1;
 CPTableViewDropOn = 0;
 CPTableViewDropAbove = 1;
 
+CPSourceListGradient = "CPSourceListGradient";
+CPSourceListTopLineColor = "CPSourceListTopLineColor";
+CPSourceListBottomLineColor = "CPSourceListBottomLineColor";
+
 // TODO: add docs
 
 CPTableViewSelectionHighlightStyleNone = -1;
@@ -86,7 +89,7 @@ CPTableViewSolidVerticalGridLineMask   = 1 << 0;
 CPTableViewSolidHorizontalGridLineMask = 1 << 1;
 
 CPTableViewNoColumnAutoresizing = 0;
-CPTableViewUniformColumnAutoresizingStyle = 1;
+CPTableViewUniformColumnAutoresizingStyle = 1; // FIX ME: This is FUBAR
 CPTableViewSequentialColumnAutoresizingStyle = 2;
 CPTableViewReverseSequentialColumnAutoresizingStyle = 3;
 CPTableViewLastColumnOnlyAutoresizingStyle = 4;
@@ -123,13 +126,13 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
 @end
 
-/*! 
+/*!
     @ingroup appkit
     @class CPTableView
 
-    CPTableView object displays record-oriented data in a table and 
+    CPTableView object displays record-oriented data in a table and
     allows the user to edit values and resize and rearrange columns.
-    A CPTableView requires you to set a dataSource which implements numberOfRowsInTableView: 
+    A CPTableView requires you to set a dataSource which implements numberOfRowsInTableView:
     and tableView:objectValueForTableColumn:row:
 */
 @implementation CPTableView : CPControl
@@ -159,7 +162,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     BOOL        _allowsColumnSelection;
     BOOL        _allowsMultipleSelection;
     BOOL        _allowsEmptySelection;
-    
+
     CPArray     _sortDescriptors;
     //Setting Display Attributes
     CGSize      _intercellSpacing;
@@ -170,6 +173,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
     unsigned    _selectionHighlightStyle;
     CPTableColumn _currentHighlightedTableColumn;
+    CPColor     _selectionHighlightColor;
     unsigned    _gridStyleMask;
     CPColor     _gridColor;
 
@@ -205,13 +209,16 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     CPDragOperation             _dragOperationDefaultMask;
     int                         _retargetedDropRow;
     CPDragOperation             _retargetedDropOperation;
-    
+
     BOOL        _disableAutomaticResizing @accessors(property=disableAutomaticResizing);
     BOOL        _lastColumnShouldSnap;
 
     CPGradient  _sourceListActiveGradient;
     CPColor     _sourceListActiveTopLineColor;
-    CPColor     _sourceListActiveBottomLineColor;    
+    CPColor     _sourceListActiveBottomLineColor;
+
+    int         _draggedColumnIndex;
+
 /*
     CPGradient  _sourceListInactiveGradient;
     CPColor     _sourceListInactiveTopLineColor;
@@ -232,26 +239,19 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         _allowsEmptySelection = YES;
         _allowsColumnSelection = NO;
         _disableAutomaticResizing = NO;
-        _tableViewFlags = 0;
 
         //Setting Display Attributes
         _selectionHighlightStyle = CPTableViewSelectionHighlightStyleRegular;
 
         [self setUsesAlternatingRowBackgroundColors:NO];
-        [self setAlternatingRowBackgroundColors:[[CPColor whiteColor], [CPColor colorWithHexString:@"f5f9fc"]]];
+        [self setAlternatingRowBackgroundColors:
+            [[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]]];
 
         _tableColumns = [];
         _tableColumnRanges = [];
         _dirtyTableColumnRangeIndex = CPNotFound;
         _numberOfHiddenColumns = 0;
 
-        _objectValues = { };
-        _dataViewsForTableColumns = { };
-        _dataViews=  [];
-        _numberOfRows = 0;
-        _exposedRows = [CPIndexSet indexSet];
-        _exposedColumns = [CPIndexSet indexSet];
-        _cachedDataViews = { };
         _intercellSpacing = _CGSizeMake(0.0, 0.0);
         _rowHeight = 23.0;
 
@@ -265,8 +265,6 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         _cornerView = nil; //[[_CPCornerView alloc] initWithFrame:CGRectMake(0, 0, [CPScroller scrollerWidth], CGRectGetHeight([_headerView frame]))];
 
         _lastSelectedRow = -1;
-        _selectedColumnIndexes = [CPIndexSet indexSet];
-        _selectedRowIndexes = [CPIndexSet indexSet];
         _currentHighlightedTableColumn = nil;
 
         _sortDescriptors = [CPArray array];
@@ -279,10 +277,6 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         _dragOperationDefaultMask = nil;
         _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleRegular;
 
-        _tableDrawView = [[_CPTableDrawView alloc] initWithTableView:self];
-        [_tableDrawView setBackgroundColor:[CPColor clearColor]];
-        [self addSubview:_tableDrawView];
-
         [self setBackgroundColor:[CPColor whiteColor]];
         [self _init];
     }
@@ -293,11 +287,47 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 // FIX ME: we have a lot of redundent init stuff in initWithFrame: and initWithCoder: we should move it all into here.
 - (void)_init
 {
+        _tableViewFlags = 0;
+
+        _selectedColumnIndexes = [CPIndexSet indexSet];
+        _selectedRowIndexes = [CPIndexSet indexSet];
+
         _dropOperationFeedbackView = [[_CPDropOperationDrawingView alloc] initWithFrame:_CGRectMakeZero()];
         [_dropOperationFeedbackView setTableView:self];
 
         _lastColumnShouldSnap = NO;
 
+        if (!_alternatingRowBackgroundColors)
+            _alternatingRowBackgroundColors = [[CPColor whiteColor], [CPColor colorWithHexString:@"e4e7ff"]];
+
+        _selectionHighlightColor = [CPColor colorWithHexString:@"5f83b9"];
+
+        _tableColumnRanges = [];
+        _dirtyTableColumnRangeIndex = 0;
+        _numberOfHiddenColumns = 0;
+
+        _objectValues = { };
+        _dataViewsForTableColumns = { };
+        _dataViews=  [];
+        _numberOfRows = 0;
+        _exposedRows = [CPIndexSet indexSet];
+        _exposedColumns = [CPIndexSet indexSet];
+        _cachedDataViews = { };
+
+        _tableDrawView = [[_CPTableDrawView alloc] initWithTableView:self];
+        [_tableDrawView setBackgroundColor:[CPColor clearColor]];
+        [self addSubview:_tableDrawView];
+
+        if (!_headerView)
+            _headerView = [[CPTableHeaderView alloc] initWithFrame:CGRectMake(0, 0, [self bounds].size.width, _rowHeight)];
+
+        [_headerView setTableView:self];
+
+        if (!_cornerView)
+            _cornerView = [[_CPCornerView alloc] initWithFrame:CGRectMake(0, 0, [CPScroller scrollerWidth], CGRectGetHeight([_headerView frame]))];
+
+        _draggedColumnIndex = -1;
+
         // Gradients for the source list
         _sourceListActiveGradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [89.0/255.0, 153.0/255.0, 209.0/255.0,1.0, 33.0/255.0, 94.0/255.0, 208.0/255.0,1.0], [0,1], 2);
         _sourceListActiveTopLineColor = [CPColor colorWithCalibratedRed:(61.0/255.0) green:(123.0/255.0) blue:(218.0/255.0) alpha:1.0];
@@ -378,7 +408,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
 /*!
     Reloads the data for all rows and columns.
-    
+
 */
 - (void)reloadData
 {
@@ -397,9 +427,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
 //Target-action Behavior
 /*!
-    Sets the message sent to the target when the user double-clicks an 
+    Sets the message sent to the target when the user double-clicks an
     uneditable cell or a column header to a given selector.
-    @param aSelector The message the receiver sends to its target when the user 
+    @param aSelector The message the receiver sends to its target when the user
     double-clicks an uneditable cell or a column header.
 */
 - (void)setDoubleAction:(SEL)anAction
@@ -535,7 +565,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
 /*!
     Sets the colors for the rows as they alternate. The number of colors can be arbitrary. By deafult these colors are white and light blue.
-    @param anArray an array of CPColors 
+    @param anArray an array of CPColors
 */
 
 - (void)setAlternatingRowBackgroundColors:(CPArray)alternatingRowBackgroundColors
@@ -563,15 +593,67 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     //early return for IE.
     if (aSelectionHighlightStyle == CPTableViewSelectionHighlightStyleSourceList && !CPFeatureIsCompatible(CPHTMLCanvasFeature))
         return;
-    
+
     _selectionHighlightStyle = aSelectionHighlightStyle;
-    
+    [self setNeedsDisplay:YES];
+
     if (aSelectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList)
         _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleSourceList;
     else
         _destinationDragStyle = CPTableViewDraggingDestinationFeedbackStyleRegular;
 }
 
+/*!
+    Sets the highlight color for a row or column selection
+    @param aColor a CPColor
+*/
+- (void)setSelectionHighlightColor:(CPColor)aColor
+{
+    if (aColor === _selectionHighlightColor)
+        return;
+
+    _selectionHighlightColor = aColor;
+    [self setNeedsDisplay:YES];
+}
+
+/*!
+    Returns the highlight color for a row or column selection.
+*/
+- (CPColor)selectionHighlightColor
+{
+    return _selectionHighlightColor;
+}
+
+/*!
+    Sets the highlight gradient for a row or column selection
+    This is specific to the
+    @param aDictionary a CPDictionary expects three keys to be set:
+        CPSourceListGradient which is a CGGradient
+        CPSourceListTopLineColor which is a CPColor
+        CPSourceListBottomLineColor which is a CPColor
+*/
+- (void)setSelectionGradientColors:(CPDictionary)aDictionary
+{
+    if ([aDictionary valueForKey:"CPSourceListGradient"] === _sourceListActiveGradient && [aDictionary valueForKey:"CPSourceListTopLineColor"] === _sourceListActiveTopLineColor && [aDictionary valueForKey:"CPSourceListBottomLineColor"] === _sourceListActiveBottomLineColor)
+        return;
+
+    _sourceListActiveGradient        = [aDictionary valueForKey:CPSourceListGradient];
+    _sourceListActiveTopLineColor    = [aDictionary valueForKey:CPSourceListTopLineColor]
+    _sourceListActiveBottomLineColor = [aDictionary valueForKey:CPSourceListBottomLineColor];
+    [self setNeedsDisplay:YES]
+}
+
+/*!
+    Returns a dictionary of containing the keys:
+    CPSourceListGradient
+    CPSourceListTopLineColor
+    CPSourceListBottomLineColor
+*/
+- (CPDictionary)selectionGradientColors
+{
+    return [CPDictionary dictionaryWithObjects:[_sourceListActiveGradient, _sourceListActiveTopLineColor, _sourceListActiveBottomLineColor] forKeys:[CPSourceListGradient, CPSourceListTopLineColor, CPSourceListBottomLineColor]];
+}
+
 /*!
     Sets the grid color in the non highlighted state.
     @param aColor a CPColor
@@ -632,7 +714,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
 /*!
     Removes a given column from the receiver.
-    @param aTableColumn The column to remove from the receiver. 
+    @param aTableColumn The column to remove from the receiver.
 */
 - (void)removeTableColumn:(CPTableColumn)aTableColumn
 {
@@ -660,6 +742,15 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     [self setNeedsLayout];
 }
 
+- (void)_setDraggedColumn:(int)aColumnIndex
+{
+    if (_draggedColumnIndex === aColumnIndex)
+        return;
+
+    _draggedColumnIndex = aColumnIndex;
+
+    [self reloadDataForRowIndexes:_exposedRows columnIndexes:[CPIndexSet indexSetWithIndex:aColumnIndex]];
+}
 
 /*!
     Moves the column and heading at a given index to a new given index.
@@ -679,15 +770,37 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     else
         _dirtyTableColumnRangeIndex = MIN(fromIndex, toIndex, _dirtyTableColumnRangeIndex);
 
-    if (toIndex > fromIndex)
-        --toIndex;
-
     var tableColumn = _tableColumns[fromIndex];
 
     [_tableColumns removeObjectAtIndex:fromIndex];
     [_tableColumns insertObject:tableColumn atIndex:toIndex];
 
-    [self setNeedsLayout];
+    [[self headerView] setNeedsLayout];
+    [[self headerView] setNeedsDisplay:YES];
+
+    var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])],
+        columnIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(fromIndex, toIndex)];
+
+    [self reloadDataForRowIndexes:rowIndexes columnIndexes:columnIndexes];
+}
+
+/*!
+    @ignore
+*/
+- (void)_tableColumnVisibilityDidChange:(CPTableColumn)aColumn
+{
+    var columnIndex = [[self tableColumns] indexOfObjectIdenticalTo:aColumn];
+
+    if (_dirtyTableColumnRangeIndex < 0)
+        _dirtyTableColumnRangeIndex = columnIndex;
+    else
+        _dirtyTableColumnRangeIndex = MIN(columnIndex, _dirtyTableColumnRangeIndex);
+
+    [[self headerView] setNeedsLayout];
+    [[self headerView] setNeedsDisplay:YES];
+
+    var rowIndexes = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(0, [self numberOfRows])];
+    [self reloadDataForRowIndexes:rowIndexes columnIndexes:[CPIndexSet indexSetWithIndex:columnIndex]];
 }
 
 - (CPArray)tableColumns
@@ -760,7 +873,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 */
 - (void)selectRowIndexes:(CPIndexSet)rows byExtendingSelection:(BOOL)shouldExtendSelection
 {
-    if ([rows isEqualToIndexSet:_selectedRowIndexes] || 
+    if ([rows isEqualToIndexSet:_selectedRowIndexes] ||
         (([rows firstIndex] != CPNotFound && [rows firstIndex] < 0) || [rows lastIndex] >= [self numberOfRows]))
         return;
 
@@ -805,22 +918,22 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
         var count = deselectRows.length;
         while (count--)
-        {
-            var rowIndex = deselectRows[count];
-            var view = dataViewsInTableColumn[rowIndex];
-            [view unsetThemeState:CPThemeStateSelected];
-        }
+            [self _performSelection:NO forRow:deselectRows[count] context:dataViewsInTableColumn];
 
         count = selectRows.length;
         while (count--)
-        {
-            var rowIndex = selectRows[count];
-            var view = dataViewsInTableColumn[rowIndex];
-            [view setThemeState:CPThemeStateSelected];
-        }
+            [self _performSelection:YES forRow:selectRows[count] context:dataViewsInTableColumn];
     }
 }
 
+- (void)_performSelection:(BOOL)select forRow:(CPInteger)rowIndex context:(id)context
+{
+    var view = context[rowIndex],
+        selector = select ? @"setThemeState:" : @"unsetThemeState:";
+
+    [view performSelector:CPSelectorFromString(selector) withObject:CPThemeStateSelectedDataView];
+}
+
 - (void)_updateHighlightWithOldColumns:(CPIndexSet)oldColumns newColumns:(CPIndexSet)newColumns
 {
     var firstExposedColumn = [_exposedColumns firstIndex],
@@ -835,7 +948,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     [deselectColumnIndexes getIndexes:deselectColumns maxCount:-1 inIndexRange:CPMakeRange(firstExposedColumn, exposedLength)];
     [selectColumnIndexes getIndexes:selectColumns maxCount:-1 inIndexRange:CPMakeRange(firstExposedColumn, exposedLength)];
     [_exposedRows getIndexes:selectRows maxCount:-1 inIndexRange:nil];
-    
+
     var rowsCount = selectRows.length,
         count = deselectColumns.length;
     while (count--)
@@ -843,14 +956,14 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         var columnIndex = deselectColumns[count],
             identifier = [_tableColumns[columnIndex] UID],
             dataViewsInTableColumn = _dataViewsForTableColumns[identifier];
-        
+
         for (var i = 0; i < rowsCount; i++)
         {
             var rowIndex = selectRows[i],
                 dataView = dataViewsInTableColumn[rowIndex];
-            [dataView unsetThemeState:CPThemeStateSelected];
+            [dataView unsetThemeState:CPThemeStateSelectedDataView];
         }
-        
+
         if (_headerView)
         {
             var headerView = [_tableColumns[columnIndex] headerView];
@@ -864,12 +977,12 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         var columnIndex = selectColumns[count],
             identifier = [_tableColumns[columnIndex] UID],
             dataViewsInTableColumn = _dataViewsForTableColumns[identifier];
-        
+
         for (var i = 0; i < rowsCount; i++)
         {
             var rowIndex = selectRows[i],
                 dataView = dataViewsInTableColumn[rowIndex];
-            [dataView setThemeState:CPThemeStateSelected];
+            [dataView setThemeState:CPThemeStateSelectedDataView];
         }
         if (_headerView)
         {
@@ -976,13 +1089,13 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     {
         var destination = [contentBindingInfo objectForKey:CPObservedObjectKey],
             keyPath = [contentBindingInfo objectForKey:CPObservedKeyPathKey];
-            
+
         return [[destination valueForKeyPath:keyPath] count];
     }
 
     else if (_dataSource)
         return [_dataSource numberOfRowsInTableView:self];
-    
+
     return 0;
 }
 
@@ -990,12 +1103,44 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 /*
     * - preparedCellAtColumn:row:
 */
+
 //Editing Cells
-/*
-    * - editColumn:row:withEvent:select:
-    * - editedColumn
-    * - editedRow
+
+/*!
+    Edits the indicated row.
 */
+- (void)editColumn:(CPInteger)columnIndex row:(CPInteger)rowIndex withEvent:(CPEvent)theEvent select:(BOOL)flag
+{
+    if (![self isRowSelected:rowIndex])
+        [[CPException exceptionWithName:@"Error" reason:@"Attempt to edit row="+rowIndex+" when not selected." userInfo:nil] raise];
+
+    // TODO Do something with flag.
+
+    _editingCellIndex = CGPointMake(columnIndex, rowIndex);
+    [self reloadDataForRowIndexes:[CPIndexSet indexSetWithIndex:rowIndex]
+        columnIndexes:[CPIndexSet indexSetWithIndex:columnIndex]];
+}
+
+/*!
+    Returns the column of the currently edited cell, or -1 if none.
+*/
+- (CPInteger)editedColumn
+{
+    if (!_editingCellIndex)
+        return -1;
+    return _editingCellIndex.x;
+}
+
+/*!
+    Returns the row of the currently edited cell, or -1 if none.
+*/
+- (CPInteger)editedRow
+{
+    if (!_editingCellIndex)
+        return -1;
+    return _editingCellIndex.x;
+}
+
 //Setting Auxiliary Views
 /*
     * - setHeaderView:
@@ -1057,6 +1202,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     if (_dirtyTableColumnRangeIndex < 0)
         return;
 
+    _numberOfHiddenColumns = 0;
+
     var index = _dirtyTableColumnRangeIndex,
         count = NUMBER_OF_COLUMNS(),
         x = index === 0 ? 0.0 : CPMaxRange(_tableColumnRanges[index - 1]);
@@ -1066,7 +1213,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         var tableColumn = _tableColumns[index];
 
         if ([tableColumn isHidden])
+        {
+            _numberOfHiddenColumns += 1;
             _tableColumnRanges[index] = CPMakeRange(x, 0.0);
+        }
 
         else
         {
@@ -1088,7 +1238,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 {
     aColumnIndex = +aColumnIndex;
 
-    if (aColumnIndex < 0 || aColumnIndex >= NUMBER_OF_COLUMNS())
+    var column = [[self tableColumns] objectAtIndex:aColumnIndex];
+
+    if ([column isHidden] || aColumnIndex < 0 || aColumnIndex >= NUMBER_OF_COLUMNS())
         return _CGRectMakeZero();
 
     UPDATE_COLUMN_RANGES_IF_NECESSARY();
@@ -1241,85 +1393,80 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     return _CGRectMake(tableColumnRange.location, _CGRectGetMinY(rectOfRow), tableColumnRange.length, _CGRectGetHeight(rectOfRow));
 }
 
-//FIX ME: We should refactor this!
 - (void)resizeWithOldSuperviewSize:(CGSize)aSize
 {
     [super resizeWithOldSuperviewSize:aSize];
-    
+
     if (_disableAutomaticResizing)
         return;
 
     var mask = _columnAutoResizingStyle;
 
     if(mask === CPTableViewUniformColumnAutoresizingStyle)
-    {
        [self _resizeAllColumnUniformlyWithOldSize:aSize];
-    }
-
-    if(mask === CPTableViewLastColumnOnlyAutoresizingStyle)
-    {
+    else if(mask === CPTableViewLastColumnOnlyAutoresizingStyle)
         [self sizeLastColumnToFit];
-    }
+    else if(mask === CPTableViewFirstColumnOnlyAutoresizingStyle)
+        [self _autoResizeFirstColumn];
+}
 
-    if(mask === CPTableViewFirstColumnOnlyAutoresizingStyle)
-    {
-        var superview = [self superview];
+- (void)_autoResizeFirstColumn
+{
+    var superview = [self superview];
 
-        if (!superview)
-            return;
+     if (!superview)
+         return;
 
-        var superviewSize = [superview bounds].size;
+     var superviewSize = [superview bounds].size;
 
-        UPDATE_COLUMN_RANGES_IF_NECESSARY();
+     UPDATE_COLUMN_RANGES_IF_NECESSARY();
 
-        var count = NUMBER_OF_COLUMNS();
+     var count = NUMBER_OF_COLUMNS(),
+         visColumns = [[CPArray alloc] init],
+         totalWidth = 0,
+         i = 0;
 
-        var visColumns = [[CPArray alloc] init];
-        var totalWidth = 0;
+     for(; i < count; i++)
+     {
+         if(![_tableColumns[i] isHidden])
+         {
+             [visColumns addObject:i];
+             totalWidth += [_tableColumns[i] width];
+         }
+     }
 
-        for(var i=0; i < count; i++)
-        {
-            if(![_tableColumns[i] isHidden])
-            {
-                [visColumns addObject:i];
-                totalWidth += [_tableColumns[i] width];
-            }
-        }
+     count = [visColumns count];
 
-        count = [visColumns count];
+     //if there are rows
+     if (count > 0)
+     {
+         var columnToResize = _tableColumns[visColumns[0]];
+         var newWidth = superviewSize.width - totalWidth;// - [columnToResize width];
+         newWidth += [columnToResize width];
+         newWidth = (newWidth < [columnToResize minWidth]) ? [columnToResize minWidth] : newWidth;
+         newWidth = (newWidth > [columnToResize maxWidth]) ? [columnToResize maxWidth] : newWidth;
 
-        //if there are rows
-        if (count > 0)
-        {
-            var columnToResize = _tableColumns[visColumns[0]];
-            var newWidth = superviewSize.width - totalWidth;// - [columnToResize width];
-            newWidth += [columnToResize width];
-            newWidth = (newWidth < [columnToResize minWidth]) ? [columnToResize minWidth] : newWidth;
-            newWidth = (newWidth > [columnToResize maxWidth]) ? [columnToResize maxWidth] : newWidth;
-
-            [columnToResize setWidth:FLOOR(newWidth)];
-        }
-
-        [self setNeedsLayout];
-    }
+         [columnToResize setWidth:FLOOR(newWidth)];
+     }
 
+     [self setNeedsLayout];
 }
 
 - (void)_resizeAllColumnUniformlyWithOldSize:(CGSize)oldSize
 {
         var superview = [self superview];
-        
+
         if (!superview)
             return;
 
         var superviewSize = [superview bounds].size;
-        
+
         if (_dirtyTableColumnRangeIndex !== CPNotFound) [self _recalculateTableColumnRanges];//UPDATE_COLUMN_RANGES_IF_NECESSARY();
 
         var count = _tableColumns.length,//NUMBER_OF_COLUMNS(),
             visColumns = [[CPArray alloc] init],
             buffer = 0.0;
-        
+
         // Fixme: cache resizable columns because they won't changes betwwen two calls to this method.
         for(var i=0; i < count; i++)
         {
@@ -1336,15 +1483,15 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         {
             var maxXofColumns = CGRectGetMaxX([self rectOfColumn:visColumns[count - 1]]);
 
-            // If the x value of the end of the last column is between the current bounds and the previous bounds we should snap. 
+            // If the x value of the end of the last column is between the current bounds and the previous bounds we should snap.
             if (!_lastColumnShouldSnap && (maxXofColumns >= superviewSize.width && maxXofColumns <= oldSize.width || maxXofColumns <= superviewSize.width && maxXofColumns >= oldSize.width))
             {
                 //set the snap mask
                 _lastColumnShouldSnap = YES;
                 //then we need to make sure everything is set correctly.
                 [self _resizeAllColumnUniformlyWithOldSize:CGSizeMake(maxXofColumns, 0)];
-            }   
-            
+            }
+
             if(!_lastColumnShouldSnap)
                 return;
 
@@ -1379,8 +1526,8 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
 /*!
     Sets the column autoresizing style of the receiver to a given style.
-    @param aStyle The column autoresizing style for the receiver. 
-    CPTableViewNoColumnAutoresizing, CPTableViewUniformColumnAutoresizingStyle, 
+    @param aStyle The column autoresizing style for the receiver.
+    CPTableViewNoColumnAutoresizing, CPTableViewUniformColumnAutoresizingStyle,
     CPTableViewLastColumnOnlyAutoresizingStyle, CPTableViewFirstColumnOnlyAutoresizingStyle
 */
 - (void)setColumnAutoresizingStyle:(unsigned)style
@@ -1435,6 +1582,17 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     _numberOfRows = nil;
     _numberOfRows = [self numberOfRows];
 
+    var oldNumberOfRows = _numberOfRows;
+
+    // remove row indexes from the selection if they no longer exist
+    var hangingSelections = oldNumberOfRows - _numberOfRows;
+
+    if (hangingSelections > 0)
+    {
+        [_selectedRowIndexes removeIndexesInRange:CPMakeRange(_numberOfRows, hangingSelections)];
+        [self _noteSelectionDidChange];
+    }
+
     [self tile];
 }
 
@@ -1653,16 +1811,17 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 - (void)_didClickTableColumn:(int)clickedColumn modifierFlags:(unsigned)modifierFlags
 {
     [self _sendDelegateDidClickColumn:clickedColumn];
-        
+
     if (_allowsColumnSelection)
     {
+        [self _noteSelectionIsChanging];
         if (modifierFlags & CPCommandKeyMask)
         {
             if ([self isColumnSelected:clickedColumn])
                 [self deselectColumn:clickedColumn];
             else if ([self allowsMultipleSelection] == YES)
                 [self selectColumnIndexes:[CPIndexSet indexSetWithIndex:clickedColumn] byExtendingSelection:YES];
-                
+
             return;
         }
         else if (modifierFlags & CPShiftKeyMask)
@@ -1670,60 +1829,60 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         // should be from clickedColumn to lastClickedColum with extending:(direction == previous selection)
             var startColumn = MIN(clickedColumn, [_selectedColumnIndexes lastIndex]),
                 endColumn = MAX(clickedColumn, [_selectedColumnIndexes firstIndex]);
-         
-            [self selectColumnIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(startColumn, endColumn - startColumn + 1)] 
+
+            [self selectColumnIndexes:[CPIndexSet indexSetWithIndexesInRange:CPMakeRange(startColumn, endColumn - startColumn + 1)]
                  byExtendingSelection:YES];
-            
+
             return;
         }
         else
             [self selectColumnIndexes:[CPIndexSet indexSetWithIndex:clickedColumn] byExtendingSelection:NO];
     }
-    
+
     [self _changeSortDescriptorsForClickOnColumn:clickedColumn];
 }
 
 // From GNUSTEP
 - (void)_changeSortDescriptorsForClickOnColumn:(int)column
 {
-    var tableColumn = [_tableColumns objectAtIndex:column],        
+    var tableColumn = [_tableColumns objectAtIndex:column],
         newMainSortDescriptor = [tableColumn sortDescriptorPrototype];
-        
+
     if (!newMainSortDescriptor)
        return;
-            
-    var oldMainSortDescriptor = nil,    
+
+    var oldMainSortDescriptor = nil,
         oldSortDescriptors = [self sortDescriptors],
         newSortDescriptors = [CPArray arrayWithArray:oldSortDescriptors],
-    
+
         e = [newSortDescriptors objectEnumerator],
         descriptor = nil,
         outdatedDescriptors = [CPArray array];
 
     if ([_sortDescriptors count] > 0)
         oldMainSortDescriptor = [[self sortDescriptors] objectAtIndex: 0];
-    
+
     // Remove every main descriptor equivalents (normally only one)
     while ((descriptor = [e nextObject]) != nil)
     {
         if ([[descriptor key] isEqual: [newMainSortDescriptor key]])
             [outdatedDescriptors addObject:descriptor];
     }
-    
+
     // Invert the sort direction when the same column header is clicked twice
     if ([[newMainSortDescriptor key] isEqual:[oldMainSortDescriptor key]])
         newMainSortDescriptor = [oldMainSortDescriptor reversedSortDescriptor];
-    
+
     [newSortDescriptors removeObjectsInArray:outdatedDescriptors];
     [newSortDescriptors insertObject:newMainSortDescriptor atIndex:0];
-        
+
     // Update indicator image & highlighted column before
-   	var image = [newMainSortDescriptor ascending] ? [CPTableView _defaultTableHeaderSortImage] : [CPTableView _defaultTableHeaderReverseSortImage];	
+   	var image = [newMainSortDescriptor ascending] ? [CPTableView _defaultTableHeaderSortImage] : [CPTableView _defaultTableHeaderReverseSortImage];
 
     [self setIndicatorImage:nil inTableColumn:_currentHighlightedTableColumn];
-	[self setIndicatorImage:image inTableColumn:tableColumn];	
+	[self setIndicatorImage:image inTableColumn:tableColumn];
 	[self setHighlightedTableColumn:tableColumn];
-	
+
     [self setSortDescriptors:newSortDescriptors];
 }
 
@@ -1751,19 +1910,19 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 }
 
 - (void)setHighlightedTableColumn:(CPTableColumn)aTableColumn
-{   
+{
     if (_currentHighlightedTableColumn == aTableColumn)
         return;
-    
+
     if (_headerView)
     {
         if (_currentHighlightedTableColumn != nil)
             [[_currentHighlightedTableColumn headerView] unsetThemeState:CPThemeStateSelected];
-   
+
         if (aTableColumn != nil)
             [[aTableColumn headerView] setThemeState:CPThemeStateSelected];
     }
-    
+
     _currentHighlightedTableColumn = aTableColumn;
 }
 
@@ -1782,7 +1941,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     return [[CPImage alloc] initWithContentsOfFile:@"Frameworks/AppKit/Resources/GenericFile.png" size:CGSizeMake(32,32)];
 }
 
-- (CPView)dragViewForRowsWithIndexes:(CPIndexSet)theDraggedRows tableColumns:(CPArray)theTableColumns event:(CPEvent)theDragEvent offset:(CPPoint)dragViewOffset
+- (CPView)dragViewForRowsWithIndexes:(CPIndexSet)theDraggedRows tableColumns:(CPArray)theTableColumns event:(CPEvent)theDragEvent offset:(CPPointPointer)dragViewOffset
 {
     var bounds = [self bounds],
         view = [[CPView alloc] initWithFrame:bounds];
@@ -1790,16 +1949,16 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     [view setAlphaValue:0.7];
 
     // We have to fetch all the data views for the selected rows and columns
-    // After that we can copy these add them to a transparent drag view and use that drag view 
+    // After that we can copy these add them to a transparent drag view and use that drag view
     // to make it appear we are dragging images of those rows (as you would do in regular Cocoa)
     var columnIndex = [theTableColumns count];
-    while (columnIndex--) 
+    while (columnIndex--)
     {
         var tableColumn = [theTableColumns objectAtIndex:columnIndex],
             row = [theDraggedRows firstIndex];
 
         while (row !== CPNotFound)
-        { 
+        {
             var dataView = [self _newDataViewForRow:row tableColumn:tableColumn];
 
             [dataView setFrame:[self frameOfDataViewAtColumn:columnIndex row:row]];
@@ -1818,6 +1977,55 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     return view;
 }
 
+/*!
+    @ignore
+    // Fetches all the data views (from the datasource) for the column and it's visible rows
+    // Copy the dataviews add them to a transparent drag view and use that drag view
+    // to make it appear we are dragging images of those rows (as you would do in regular Cocoa)
+*/
+- (CPView)_dragViewForColumn:(int)theColumnIndex event:(CPEvent)theDragEvent offset:(CPPointPointer)theDragViewOffset
+{
+    var dragView = [[CPView alloc] initWithFrame:CPRectMakeZero()];
+        tableColumn = [[self tableColumns] objectAtIndex:theColumnIndex],
+        bounds = CPRectMake(0.0, 0.0, [tableColumn width], CPRectGetHeight([self _exposedRect]) + 23.0),
+        columnRect = [self rectOfColumn:theColumnIndex],
+        headerView = [tableColumn headerView];
+
+    row = [_exposedRows firstIndex];
+    while (row !== CPNotFound)
+    {
+        var dataView = [self _newDataViewForRow:row tableColumn:tableColumn],
+            dataViewFrame = [self frameOfDataViewAtColumn:theColumnIndex row:row];
+
+        // Only one column is ever dragged so we just place the view at
+        dataViewFrame.origin.x = 0.0;
+
+        // Offset by table header height - scroll position
+        dataViewFrame.origin.y = ( CPRectGetMinY(dataViewFrame) - CPRectGetMinY([self _exposedRect]) ) + 23.0;
+        [dataView setFrame:dataViewFrame];
+
+        [dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]];
+        [dragView addSubview:dataView];
+
+        row = [_exposedRows indexGreaterThanIndex:row];
+    }
+
+    // Add the column header view
+    var headerFrame = [headerView frame];
+    headerFrame.origin = CPPointMakeZero();
+
+    columnHeaderView = [[_CPTableColumnHeaderView alloc] initWithFrame:headerFrame];
+    [columnHeaderView setStringValue:[headerView stringValue]];
+    [columnHeaderView setThemeState:[headerView themeState]];
+    [dragView addSubview:columnHeaderView];
+
+    [dragView setBackgroundColor:[CPColor whiteColor]];
+    [dragView setAlphaValue:0.7];
+    [dragView setFrame:bounds];
+
+    return dragView;
+}
+
 - (void)setDraggingSourceOperationMask:(CPDragOperation)mask forLocal:(BOOL)isLocal
 {
     //ignoral local for the time being since only one capp app can run at a time...
@@ -1834,12 +2042,12 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     if(row > [self numberOfRows] && operation === CPTableViewDropOn)
     {
         var numberOfRows = [self numberOfRows] + 1;
-        var reason = @"Attempt to set dropRow=" + row + 
+        var reason = @"Attempt to set dropRow=" + row +
                      " dropOperation=CPTableViewDropOn when [0 - " + numberOfRows + "] is valid range of rows."
 
         [[CPException exceptionWithName:@"Error" reason:reason userInfo:nil] raise];
     }
-        
+
 
     _retargetedDropRow = row;
     _retargetedDropOperation = operation;
@@ -2058,8 +2266,12 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     for (; columnIndex < columnsCount; ++columnIndex)
     {
         var column = columnArray[columnIndex],
-            tableColumn = _tableColumns[column],
-            tableColumnUID = [tableColumn UID];
+            tableColumn = _tableColumns[column];
+
+        if ([tableColumn isHidden] || columnIndex === _draggedColumnIndex)
+            continue;
+
+        var tableColumnUID = [tableColumn UID];
 
         if (!_dataViewsForTableColumns[tableColumnUID])
             _dataViewsForTableColumns[tableColumnUID] = [];
@@ -2078,15 +2290,15 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
             [dataView setFrame:[self frameOfDataViewAtColumn:column row:row]];
             [dataView setObjectValue:[self _objectValueForTableColumn:tableColumn row:row]];
 
-            //This gives the table column an opportunity to apply the bindings. 
+            //This gives the table column an opportunity to apply the bindings.
             //It will override the value set in the data source, if there is a data source.
             //It will do nothing if there is no value binding set.
             [tableColumn prepareDataView:dataView forRow:row];
-            
+
             if (isColumnSelected || [self isRowSelected:row])
-                [dataView setThemeState:CPThemeStateSelected];
+                [dataView setThemeState:CPThemeStateSelectedDataView];
             else
-                [dataView unsetThemeState:CPThemeStateSelected];
+                [dataView unsetThemeState:CPThemeStateSelectedDataView];
 
             if (_implementedDelegateMethods & CPTableViewDelegate_tableView_willDisplayView_forTableColumn_row_)
                 [_delegate tableView:self willDisplayView:dataView forTableColumn:tableColumn row:row];
@@ -2135,21 +2347,21 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         columnsCount = columnArray.length;
 
     for (; columnIndex < columnsCount; ++columnIndex)
-    {        
+    {
         var column = columnArray[columnIndex],
             tableColumn = _tableColumns[column],
             tableColumnUID = [tableColumn UID],
             dataViewsForTableColumn = _dataViewsForTableColumns[tableColumnUID],
             columnRange = _tableColumnRanges[column];
-            
+
         var rowIndex = 0,
-            rowsCount = rowArray.length;            
+            rowsCount = rowArray.length;
 
         for (; rowIndex < rowsCount; ++rowIndex)
         {
             var row = rowArray[rowIndex],
-                dataView = dataViewsForTableColumn[row];                
-            
+                dataView = dataViewsForTableColumn[row];
+
             [dataView setFrame:[self frameOfDataViewAtColumn:column row:row]];
         }
     }
@@ -2170,13 +2382,16 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         var dataView = [_delegate tableView:self dataViewForTableColumn:aTableColumn row:aRow];
         [aTableColumn setDataView:dataView];
     }
-        
-    
+
+
     return [aTableColumn _newDataViewForRow:aRow];
 }
 
 - (void)_enqueueReusableDataView:(CPView)aDataView
 {
+    if (!aDataView)
+        return;
+
     // FIXME: yuck!
     var identifier = aDataView.identifier;
 
@@ -2205,15 +2420,24 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 }
 
 - (void)_drawRect:(CGRect)aRect
-{   
+{
     // FIX ME: All three of these methods will likely need to be rewritten for 1.0
     // We've got grid drawing in highlightSelection and crap everywhere.
-    
+
     var exposedRect = [self _exposedRect];
 
     [self drawBackgroundInClipRect:exposedRect];
     [self drawGridInClipRect:exposedRect];
     [self highlightSelectionInClipRect:exposedRect];
+
+    if (_draggedColumnIndex === -1)
+        return;
+
+    var context = [[CPGraphicsContext currentContext] graphicsPort],
+        columnRect = [self rectOfColumn:_draggedColumnIndex];
+
+    CGContextSetFillColor(context, [CPColor grayColor]);
+    CGContextFillRect(context, columnRect);
 }
 
 - (void)drawBackgroundInClipRect:(CGRect)aRect
@@ -2221,7 +2445,6 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     if (!_usesAlternatingRowBackgroundColors)
         return;
 
-
     var rowColors = [self alternatingRowBackgroundColors],
         colorCount = [rowColors count];
 
@@ -2359,6 +2582,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
 - (void)highlightSelectionInClipRect:(CGRect)aRect
 {
+    if (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleNone)
+        return;
+
     var context = [[CPGraphicsContext currentContext] graphicsPort],
         indexes = [],
         rectSelector = @selector(rectOfRow:);
@@ -2387,34 +2613,33 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
     if (!count)
         return;
-  
-    var drawGradient = (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList && [_selectedRowIndexes count] >= 1);
-    
-    var deltaHeight = 0.5 * (_gridStyleMask & CPTableViewSolidHorizontalGridLineMask);
+
+    var drawGradient = (_selectionHighlightStyle === CPTableViewSelectionHighlightStyleSourceList && [_selectedRowIndexes count] >= 1),
+        deltaHeight = 0.5 * (_gridStyleMask & CPTableViewSolidHorizontalGridLineMask);
 
     CGContextBeginPath(context);
     while (count--)
     {
         var rowRect = CGRectIntersection(objj_msgSend(self, rectSelector, indexes[count]), aRect);
         CGContextAddRect(context, rowRect);
-        
+
         if (drawGradient)
         {
             var minX = _CGRectGetMinX(rowRect),
                 minY = _CGRectGetMinY(rowRect),
                 maxX = _CGRectGetMaxX(rowRect),
                 maxY = _CGRectGetMaxY(rowRect) - deltaHeight;
-            
+
             CGContextDrawLinearGradient(context, _sourceListActiveGradient, rowRect.origin, CGPointMake(minX, maxY), 0);
             CGContextClosePath(context);
-            
+
             CGContextBeginPath(context);
             CGContextMoveToPoint(context, minX, minY);
             CGContextAddLineToPoint(context, maxX, minY);
             CGContextClosePath(context);
             CGContextSetStrokeColor(context, _sourceListActiveTopLineColor);
             CGContextStrokePath(context);
-            
+
             CGContextBeginPath(context);
             CGContextMoveToPoint(context, minX, maxY);
             CGContextAddLineToPoint(context, maxX, maxY - 1);
@@ -2423,15 +2648,15 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
             CGContextStrokePath(context);
         }
     }
-        
+
     CGContextClosePath(context);
-    
+
     if (!drawGradient)
     {
-        [[CPColor selectionColor] setFill];
+        [_selectionHighlightColor setFill];
         CGContextFillPath(context);
     }
-    
+
     CGContextBeginPath(context);
     gridStyleMask = [self gridStyleMask];
     for(var i=0; i < count2; i++)
@@ -2450,19 +2675,18 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
             exposedRange = CPMakeRange(firstExposedColumn, [exposedColumns lastIndex] - firstExposedColumn + 1);
             [exposedColumns getIndexes:exposedColumnIndexes maxCount:-1 inIndexRange:exposedRange];
             var exposedColumnCount = [exposedColumnIndexes count];
-            
+
             for(var c = firstExposedColumn; c < exposedColumnCount; c++)
             {
-                //console.log(columnIndexes);
                 var colRect = [self rectOfColumn:exposedColumnIndexes[c]],
                     colX = CGRectGetMaxX(colRect) + 0.5;
-                //console.log(colX);
+
                 CGContextMoveToPoint(context, colX, minY);
                 CGContextAddLineToPoint(context, colX, maxY);
             }
 
         }
-        
+
         //if the row after the current row is not selected then there is no need to draw the bottom grid line white.
         if([indexes containsObject:indexes[i]+1])
         {
@@ -2556,9 +2780,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     else
         _selectionAnchorRow = row;
 
-        
-    //set ivars for startTrackingPoint and time...    
-    _startTrackingPoint = aPoint; 
+
+    //set ivars for startTrackingPoint and time...
+    _startTrackingPoint = aPoint;
     _startTrackingTimestamp = new Date();
 
     if (_implementedDataSourceMethods & CPTableViewDataSource_tableView_setObjectValue_forTableColumn_row_)
@@ -2580,7 +2804,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 {
     // Prevent CPControl from eating the mouse events when we are in a drag session
     if (![_draggedRowIndexes count])
+    {
+        [self autoscroll:anEvent];
         [super trackMouse:anEvent];
+    }
     else
         [CPApp sendEvent:anEvent];
 }
@@ -2592,11 +2819,11 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 {
     var row = [self rowAtPoint:aPoint];
 
-    // begin the drag is the datasource lets us, we've move at least +-3px vertical or horizontal, 
+    // begin the drag is the datasource lets us, we've move at least +-3px vertical or horizontal,
     // or we're dragging from selected rows and we haven't begun a drag session
     if(!_isSelectingSession && _implementedDataSourceMethods & CPTableViewDataSource_tableView_writeRowsWithIndexes_toPasteboard_)
     {
-        if (row >= 0 && (ABS(_startTrackingPoint.x - aPoint.x) > 3 || (_verticalMotionCanDrag && ABS(_startTrackingPoint.y - aPoint.y) > 3)) || 
+        if (row >= 0 && (ABS(_startTrackingPoint.x - aPoint.x) > 3 || (_verticalMotionCanDrag && ABS(_startTrackingPoint.y - aPoint.y) > 3)) ||
             ([_selectedRowIndexes containsIndex:row]))
         {
             if ([_selectedRowIndexes containsIndex:row])
@@ -2613,33 +2840,33 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
                 var currentEvent = [CPApp currentEvent],
                     offset = CPPointMakeZero(),
                     tableColumns = [_tableColumns objectsAtIndexes:_exposedColumns];
-        
+
                 // We deviate from the default Cocoa implementation here by asking for a view in stead of an image
                 // We support both, but the view prefered over the image because we can mimic the rows we are dragging
                 // by re-creating the data views for the dragged rows
-                var view = [self dragViewForRowsWithIndexes:_draggedRowIndexes 
-                                               tableColumns:tableColumns 
-                                                      event:currentEvent 
+                var view = [self dragViewForRowsWithIndexes:_draggedRowIndexes
+                                               tableColumns:tableColumns
+                                                      event:currentEvent
                                                      offset:offset];
-    
+
                 if (!view)
                 {
-                    var image = [self dragImageForRowsWithIndexes:_draggedRowIndexes 
-                                                     tableColumns:tableColumns 
-                                                            event:currentEvent 
+                    var image = [self dragImageForRowsWithIndexes:_draggedRowIndexes
+                                                     tableColumns:tableColumns
+                                                            event:currentEvent
                                                            offset:offset];
                     view = [[CPImageView alloc] initWithFrame:CPMakeRect(0, 0, [image size].width, [image size].height)];
                     [view setImage:image];
                 }
-    
+
                 var bounds = [view bounds];
                 var viewLocation = CPPointMake(aPoint.x - CGRectGetWidth(bounds)/2 + offset.x, aPoint.y - CGRectGetHeight(bounds)/2 + offset.y);
                 [self dragView:view at:viewLocation offset:CPPointMakeZero() event:[CPApp currentEvent] pasteboard:pboard source:self slideBack:YES];
                 _startTrackingPoint = nil;
-    
+
                 return NO;
             }
-            
+
             // The delegate disallowed the drag so clear the dragged row indexes
             _draggedRowIndexes = [CPIndexSet indexSet];
         }
@@ -2715,10 +2942,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
                         shouldEdit = [_delegate tableView:self shouldEditTableColumn:column row:rowIndex];
                     if (shouldEdit)
                     {
-                        _editingCellIndex = CGPointMake(columnIndex, rowIndex);
-                        [self reloadDataForRowIndexes:[CPIndexSet indexSetWithIndex:rowIndex]
-                            columnIndexes:[CPIndexSet indexSetWithIndex:columnIndex]];
-
+                        [self editColumn:columnIndex row:rowIndex withEvent:nil select:YES];
                         return;
                     }
                 }
@@ -2743,17 +2967,17 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
     if(_retargetedDropRow !== nil)
         row = _retargetedDropRow;
-    
-    var draggedTypes = [self registeredDraggedTypes], 
+
+    var draggedTypes = [self registeredDraggedTypes],
         count = [draggedTypes count],
         i = 0;
-        
-    for (; i < count; i++) 
-    { 
-        if ([[[sender draggingPasteboard] types] containsObject:[draggedTypes objectAtIndex: i]]) 
-            return [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation]; 
+
+    for (; i < count; i++)
+    {
+        if ([[[sender draggingPasteboard] types] containsObject:[draggedTypes objectAtIndex: i]])
+            return [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation];
     }
-    
+
     return CPDragOperationNone;
 }
 
@@ -2793,7 +3017,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 */
 - (CPTableViewDropOperation)_proposedDropOperationAtPoint:(CGPoint)theDragPoint
 {
-    if(_retargetedDropOperation !== nil) 
+    if(_retargetedDropOperation !== nil)
         return _retargetedDropOperation;
 
     var row = [self _proposedRowAtPoint:theDragPoint],
@@ -2803,13 +3027,13 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     // This probably doesn't work if the row height is smaller than or around 5.0
     if ([self intercellSpacing].height < 5.0)
 		rowRect = CPRectInset(rowRect, 0.0, 5.0 - [self intercellSpacing].height);
-    
+
 	// If the altered row rect contains the drag point we show the drop on
-	// We don't show the drop on indicator if we are dragging below the last row 
+	// We don't show the drop on indicator if we are dragging below the last row
 	// in that case we always want to show the drop above indicator
-    if (CGRectContainsPoint(rowRect, theDragPoint) && row < _numberOfRows) 
+    if (CGRectContainsPoint(rowRect, theDragPoint) && row < _numberOfRows)
         return CPTableViewDropOn;
-        
+
     return CPTableViewDropAbove;
 }
 
@@ -2821,18 +3045,18 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 	// We don't use rowAtPoint here because the drag indicator can appear below the last row
 	// and rowAtPoint doesn't return rows that are larger than numberOfRows
 	var row = FLOOR(dragPoint.y / ( _rowHeight + _intercellSpacing.height ));
-	
+
 	// Determine if the mouse is currently closer to this row or the row below it
 	var lowerRow = row + 1,
 		rect = [self rectOfRow:row],
 		lowerRect = [self rectOfRow:lowerRow];
-		
+
 	if (ABS(CPRectGetMinY(lowerRect) - dragPoint.y) < ABS(dragPoint.y - CPRectGetMinY(rect)))
 		row = lowerRow;
 
     if (row >= [self numberOfRows])
         row = [self numberOfRows];
-	
+
 	return row;
 }
 
@@ -2848,7 +3072,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 {
     if (theRowIndex >= [self numberOfRows])
         theRowIndex = [self numberOfRows] - 1;
-        
+
     return [self rectOfRow:theRowIndex];
 }
 
@@ -2869,38 +3093,31 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     var row = [self _proposedRowAtPoint:location],
         dragOperation = [self _validateDrop:sender proposedRow:row proposedDropOperation:dropOperation];
         exposedClipRect = [self exposedClipRect];
-	
+
     if(_retargetedDropRow !== nil)
         row = _retargetedDropRow;
 
-    
+
     if (dropOperation === CPTableViewDropOn && row >= [self numberOfRows])
         row = [self numberOfRows] - 1;
 
     var rect = CPRectMakeZero();
-    
+
     if (row === -1)
         rect = exposedClipRect;
-        
+
     else if (dropOperation === CPTableViewDropAbove)
         rect = [self _rectForDropHighlightViewBetweenUpperRow:row - 1 andLowerRow:row offset:location];
-        
-    else 
+
+    else
         rect = [self _rectForDropHighlightViewOnRow:row];
-    
+
     [_dropOperationFeedbackView setDropOperation:row !== -1 ? dropOperation : CPDragOperationNone];
     [_dropOperationFeedbackView setHidden:(dragOperation == CPDragOperationNone)];
     [_dropOperationFeedbackView setFrame:rect];
     [_dropOperationFeedbackView setCurrentRow:row];
     [self addSubview:_dropOperationFeedbackView];
-    
-    // FIXME : Maybe we should do this in a timer outside this method. 
-    // Problem: we don't know when the scroll ends or when the next -draggingUpdated is called. 
-    if (row > 0 && location.y - CGRectGetMinY(exposedClipRect) < _rowHeight)
-        [self scrollRowToVisible:row - 1];
-    else if (row < numberOfRows && CGRectGetMaxY(exposedClipRect) - location.y < _rowHeight)
-        [self scrollRowToVisible:row + 1];
-        
+
     return dragOperation;
 }
 
@@ -2953,7 +3170,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     we're using this because we drag views instead of images so we can get the rows themselves to actually drag
 */
 - (void)draggedView:(CPImage)aView endedAt:(CGPoint)aLocation operation:(CPDragOperation)anOperation
-{   
+{
     [self _draggingEnded];
     [self draggedImage:aView endedAt:aLocation operation:anOperation];
 }
@@ -2990,7 +3207,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     else if (_allowsMultipleSelection)
     {
         newSelection = [CPIndexSet indexSetWithIndexesInRange:CPMakeRange(MIN(aRow, _selectionAnchorRow), ABS(aRow - _selectionAnchorRow) + 1)];
-        shouldExtendSelection = [self mouseDownFlags] & CPShiftKeyMask && 
+        shouldExtendSelection = [self mouseDownFlags] & CPShiftKeyMask &&
                                 ((_lastSelectedRow == [_selectedRowIndexes lastIndex] && aRow > _lastSelectedRow) ||
                                 (_lastSelectedRow == [_selectedRowIndexes firstIndex] && aRow < _lastSelectedRow));
     }
@@ -3027,9 +3244,9 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
                 [newSelection removeIndex:index];
         }
     }
-    
+
     _lastSelectedRow = ([newSelection count] > 0) ? aRow : -1;
-    
+
     // if empty selection is not allowed and the new selection has nothing selected, abort
     if (!_allowsEmptySelection && [newSelection count] === 0)
         return;
@@ -3068,7 +3285,7 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
 
 - (void)keyDown:(CPEvent)anEvent
 {
-    [self interpretKeyEvents:[CPArray arrayWithObject:anEvent]];
+    [self interpretKeyEvents:[anEvent]];
 }
 
 - (void)moveDown:(id)sender
@@ -3128,10 +3345,10 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
     if([[self selectedRowIndexes] count] > 0)
     {
          var extend = NO;
-    
+
          if(([anEvent modifierFlags] & CPShiftKeyMask) && _allowsMultipleSelection)
            extend = YES;
-    
+
           var i = [[self selectedRowIndexes] firstIndex];
           if(i > 0)
               i--; //set index to the prev row before the first row selected
@@ -3143,24 +3360,24 @@ CPTableViewFirstColumnOnlyAutoresizingStyle = 5;
         if([self numberOfRows] > 0)
             var i = [self numberOfRows] - 1; //select the first row
      }
-    
-    
+
+
      if(_implementedDelegateMethods & CPTableViewDelegate_tableView_shouldSelectRow_)
      {
-    
+
           while((![_delegate tableView:self shouldSelectRow:i]) && i > 0)
           {
               //check to see if the row can be selected if it can't be then see if the prev row can be selected
               i--;
           }
-    
+
           //if the index still can be selected after the loop then just return
            if(![_delegate tableView:self shouldSelectRow:i])
                return;
      }
-    
+
      [self selectRowIndexes:[CPIndexSet indexSetWithIndex:i] byExtendingSelection:extend];
-    
+
      if(i >= 0)
         [self scrollRowToVisible:i];
 }
@@ -3225,53 +3442,32 @@ var CPTableViewDataSourceKey                = @"CPTableViewDataSourceKey",
         _allowsEmptySelection = [aCoder decodeBoolForKey:CPTableViewEmptySelectionKey];
         _allowsColumnSelection = [aCoder decodeBoolForKey:CPTableViewColumnSelectionKey];
 
-        _tableViewFlags = 0;
-
         //Setting Display Attributes
         _selectionHighlightStyle = CPTableViewSelectionHighlightStyleRegular;
 
-        _usesAlternatingRowBackgroundColors = [aCoder decodeBoolForKey:CPTableViewUsesAlternatingBackgroundKey];
-        [self setAlternatingRowBackgroundColors:[[CPColor whiteColor], [CPColor colorWithHexString:@"e4e7ff"]]];
-
-        _tableColumns = [aCoder decodeObjectForKey:CPTableViewTableColumnsKey];
+        _tableColumns = [aCoder decodeObjectForKey:CPTableViewTableColumnsKey] || [];
         [_tableColumns makeObjectsPerformSelector:@selector(setTableView:) withObject:self];
 
-        _tableColumnRanges = [];
-        _dirtyTableColumnRangeIndex = 0;
-        _numberOfHiddenColumns = 0;
-
-        _objectValues = { };
-        _dataViewsForTableColumns = { };
-        _dataViews=  [];
-        _numberOfRows = 0;
-        _exposedRows = [CPIndexSet indexSet];
-        _exposedColumns = [CPIndexSet indexSet];
-        _cachedDataViews = { };
-        _rowHeight = [aCoder decodeFloatForKey:CPTableViewRowHeightKey];
-        
-        if ([aCoder containsValueForKey:CPTableViewIntercellSpacingKey])
-            _intercellSpacing = [aCoder decodeSizeForKey:CPTableViewIntercellSpacingKey];
+        if ([aCoder containsValueForKey:CPTableViewRowHeightKey])
+            _rowHeight = [aCoder decodeFloatForKey:CPTableViewRowHeightKey];
         else
-            _intercellSpacing = _CGSizeMake(0.0, 0.0);
-        
+            _rowHeight = 23.0;
+
+        _intercellSpacing = [aCoder decodeSizeForKey:CPTableViewIntercellSpacingKey] || _CGSizeMake(0.0, 0.0);
+
         _gridColor = [aCoder decodeObjectForKey:CPTableViewGridColorKey] || [CPColor grayColor];
         _gridStyleMask = [aCoder decodeIntForKey:CPTableViewGridStyleMaskKey] || CPTableViewGridNone;
 
-        _alternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewAlternatingRowColorsKey];
         _usesAlternatingRowBackgroundColors = [aCoder decodeObjectForKey:CPTableViewUsesAlternatingBackgroundKey]
-        
+        _alternatingRowBackgroundColors =
+            [[CPColor whiteColor], [CPColor colorWithRed:245.0 / 255.0 green:249.0 / 255.0 blue:252.0 / 255.0 alpha:1.0]];
+
         _headerView = [aCoder decodeObjectForKey:CPTableViewHeaderViewKey];
         _cornerView = [aCoder decodeObjectForKey:CPTableViewCornerViewKey];
 
-        _selectedColumnIndexes = [CPIndexSet indexSet];
-        _selectedRowIndexes = [CPIndexSet indexSet];
-
         _dataSource = [aCoder decodeObjectForKey:CPTableViewDataSourceKey];
         _delegate = [aCoder decodeObjectForKey:CPTableViewDelegateKey];
 
-        _tableDrawView = [[_CPTableDrawView alloc] initWithTableView:self];
-        [_tableDrawView setBackgroundColor:[CPColor clearColor]];
-        [self addSubview:_tableDrawView];
         [self _init];
 
         [self viewWillMoveToSuperview:[self superview]];
@@ -3289,7 +3485,7 @@ var CPTableViewDataSourceKey                = @"CPTableViewDataSourceKey",
 
     [aCoder encodeFloat:_rowHeight forKey:CPTableViewRowHeightKey];
     [aCoder encodeSize:_intercellSpacing forKey:CPTableViewIntercellSpacingKey];
-    
+
     [aCoder encodeBool:_allowsMultipleSelection forKey:CPTableViewMultipleSelectionKey];
     [aCoder encodeBool:_allowsEmptySelection forKey:CPTableViewEmptySelectionKey];
     [aCoder encodeBool:_allowsColumnReordering forKey:CPTableViewColumnReorderingKey];
@@ -3297,10 +3493,10 @@ var CPTableViewDataSourceKey                = @"CPTableViewDataSourceKey",
     [aCoder encodeBool:_allowsColumnSelection forKey:CPTableViewColumnSelectionKey];
 
     [aCoder encodeObject:_tableColumns forKey:CPTableViewTableColumnsKey];
-    
+
     [aCoder encodeObject:_gridColor forKey:CPTableViewGridColorKey];
     [aCoder encodeInt:_gridStyleMask forKey:CPTableViewGridStyleMaskKey];
-    
+
     [aCoder encodeBool:_usesAlternatingRowBackgroundColors forKey:CPTableViewUsesAlternatingBackgroundKey];
     [aCoder encodeObject:_alternatingRowBackgroundColors forKey:CPTableViewAlternatingRowColorsKey]
 
@@ -3310,20 +3506,6 @@ var CPTableViewDataSourceKey                = @"CPTableViewDataSourceKey",
 
 @end
 
-@implementation CPColor (tableview)
-
-+ (CPColor)selectionColor
-{
-    return [CPColor colorWithHexString:@"5f83b9"];
-}
-
-+ (CPColor)selectionColorSourceView
-{
-    return [CPColor colorWithPatternImage:[[CPImage alloc] initByReferencingFile:@"Resources/tableviewselection.png" size:CGSizeMake(6,22)]];
-}
-
-@end
-
 @implementation CPIndexSet (tableview)
 
 - (void)removeMatches:otherSet
@@ -3352,23 +3534,24 @@ var CPTableViewDataSourceKey                = @"CPTableViewDataSourceKey",
     unsigned    dropOperation @accessors;
     CPTableView tableView @accessors;
     int         currentRow @accessors;
+    BOOL        isBlinking @accessors;
 }
 
 - (void)drawRect:(CGRect)aRect
 {
-    if(tableView._destinationDragStyle === CPTableViewDraggingDestinationFeedbackStyleNone)
+    if(tableView._destinationDragStyle === CPTableViewDraggingDestinationFeedbackStyleNone || isBlinking)
         return;
-    
+
     var context = [[CPGraphicsContext currentContext] graphicsPort];
 
     CGContextSetStrokeColor(context, [CPColor colorWithHexString:@"4886ca"]);
     CGContextSetLineWidth(context, 3);
-    
+
     if (currentRow === -1)
     {
         CGContextStrokeRect(context, [self bounds]);
     }
-    
+
     else if (dropOperation === CPTableViewDropOn)
     {
         //if row is selected don't fill and stroke white
@@ -3387,7 +3570,7 @@ var CPTableViewDataSourceKey                = @"CPTableViewDataSourceKey",
         }
         CGContextStrokeRoundedRectangleInRect(context, newRect, 8, YES, YES, YES, YES);
 
-    } 
+    }
     else if (dropOperation === CPTableViewDropAbove)
     {
         //reposition the view up a tad
@@ -3422,7 +3605,28 @@ var CPTableViewDataSourceKey                = @"CPTableViewDataSourceKey",
         CGContextStrokePath(context);
         //CGContextStrokeLineSegments(context, [aRect.origin.x + 8,  aRect.origin.y + 8, 300 , aRect.origin.y + 8]);
     }
-        
+}
 
+- (void)blink
+{
+    if (dropOperation !== CPTableViewDropOn)
+        return;
+
+    isBlinking = YES;
+
+    var showCallback = function() {
+        objj_msgSend(self, "setHidden:", NO)
+        isBlinking = NO;
+    }
+
+    var hideCallback = function() {
+        objj_msgSend(self, "setHidden:", YES)
+        isBlinking = YES;
+    }
+
+    objj_msgSend(self, "setHidden:", YES);
+    [CPTimer scheduledTimerWithTimeInterval:0.1 callback:showCallback repeats:NO];
+    [CPTimer scheduledTimerWithTimeInterval:0.19 callback:hideCallback repeats:NO];
+    [CPTimer scheduledTimerWithTimeInterval:0.27 callback:showCallback repeats:NO];
 }
 @end
diff --git a/AppKit/CPTextField.j b/AppKit/CPTextField.j
index 7459fa772..346301121 100644
--- a/AppKit/CPTextField.j
+++ b/AppKit/CPTextField.j
@@ -997,7 +997,7 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
     return bounds;
 }
 
-- (CGRect)bezelRectForBounds:(CFRect)bounds
+- (CGRect)bezelRectForBounds:(CGRect)bounds
 {
     var bezelInset = [self currentValueForThemeAttribute:@"bezel-inset"];
 
@@ -1087,6 +1087,22 @@ CPTextFieldStatePlaceholder = CPThemeState("placeholder");
     }
 }
 
+- (void)takeValueFromKeyPath:(CPString)aKeyPath ofObjects:(CPArray)objects
+{
+    var count = objects.length,
+        value = [objects[0] valueForKeyPath:aKeyPath];
+
+    [self setStringValue:value];
+    [self setPlaceholderString:@""];
+
+    while (count-- > 1)
+        if (value !== [objects[count] valueForKeyPath:aKeyPath])
+        {
+            [self setPlaceholderString:@"Multiple Values"];
+            [self setStringValue:@""];
+        }
+}
+
 @end
 
 var secureStringForString = function(aString)
diff --git a/AppKit/CPTheme.j b/AppKit/CPTheme.j
index 77bac886d..4b37e6d7a 100644
--- a/AppKit/CPTheme.j
+++ b/AppKit/CPTheme.j
@@ -242,6 +242,7 @@ CPThemeStateNormal          = CPThemeStates["normal"] = 0;
 CPThemeStateDisabled        = CPThemeState("disabled");
 CPThemeStateHighlighted     = CPThemeState("highlighted");
 CPThemeStateSelected        = CPThemeState("selected");
+CPThemeStateSelectedDataView = CPThemeState("selectedDataView"); 
 CPThemeStateBezeled         = CPThemeState("bezeled");
 CPThemeStateBordered        = CPThemeState("bordered");
 CPThemeStateEditable        = CPThemeState("editable");
diff --git a/AppKit/CPToolbar.j b/AppKit/CPToolbar.j
index f78f891b6..b45d70211 100644
--- a/AppKit/CPToolbar.j
+++ b/AppKit/CPToolbar.j
@@ -329,7 +329,11 @@ var CPToolbarConfigurationsByIdentifier = nil;
     return _itemsSortedByVisibilityPriority;
 }
 
-- (void)validateVisibleToolbarItems
+/*!
+    Validates the visible toolbar items by sending a validate message to
+    each visible toolbar item.
+*/
+- (void)validateVisibleItems
 {
     var toolbarItems = [self visibleItems],
         count = [toolbarItems count];
diff --git a/AppKit/CPToolbarItem.j b/AppKit/CPToolbarItem.j
index 57e33cdfc..e5148576e 100644
--- a/AppKit/CPToolbarItem.j
+++ b/AppKit/CPToolbarItem.j
@@ -449,6 +449,9 @@ CPToolbarItemVisibilityPriorityUser
 
 - (void)validate
 {
+    var action = [self action],
+        target = [self target];
+
     // View items do not do any target-action analysis.
     if (_view)
     {
@@ -458,13 +461,9 @@ CPToolbarItemVisibilityPriorityUser
         return;
     }
 
-    var action = [self action];
-
     if (!action)
         return [self setEnabled:NO];
 
-    var target = [self target];
-
     if (target && ![target respondsToSelector:action])
         return [self setEnabled:NO];
 
diff --git a/AppKit/CPView.j b/AppKit/CPView.j
index c3b2499b8..3085a06fe 100644
--- a/AppKit/CPView.j
+++ b/AppKit/CPView.j
@@ -94,7 +94,8 @@ var DOMElementPrototype         = nil,
     BackgroundTrivialColor              = 0,
     BackgroundVerticalThreePartImage    = 1,
     BackgroundHorizontalThreePartImage  = 2,
-    BackgroundNinePartImage             = 3;
+    BackgroundNinePartImage             = 3,
+    BackgroundTransparentColor          = 4;
 #endif
 
 var CPViewFlags                     = { },
@@ -139,6 +140,7 @@ var CPViewFlags                     = { },
     
     BOOL                _isHidden;
     BOOL                _hitTests;
+    BOOL                _clipsToBounds;
     
     BOOL                _postsFrameChangedNotifications;
     BOOL                _postsBoundsChangedNotifications;
@@ -277,6 +279,7 @@ var CPViewFlags                     = { },
 
         _autoresizingMask = CPViewNotSizable;
         _autoresizesSubviews = YES;
+        _clipsToBounds = YES;
     
         _opacity = 1.0;
         _isHidden = NO;
@@ -835,28 +838,33 @@ var CPViewFlags                     = { },
 
     if (_backgroundType !== BackgroundTrivialColor)
     {
-        var images = [[_backgroundColor patternImage] imageSlices];
-
-        if (_backgroundType === BackgroundVerticalThreePartImage)
+        if (_backgroundType === BackgroundTransparentColor)
         {
-            CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], size.width, size.height - _DOMImageSizes[0].height - _DOMImageSizes[2].height);
+            CPDOMDisplayServerSetStyleSize(_DOMImageParts[0], size.width, size.height);
         }
-
-        else if (_backgroundType === BackgroundHorizontalThreePartImage)
+        else
         {
-            CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], size.width - _DOMImageSizes[0].width - _DOMImageSizes[2].width, size.height);
-        }
+            var images = [[_backgroundColor patternImage] imageSlices];
 
-        else if (_backgroundType === BackgroundNinePartImage)
-        {
-            var width = size.width - _DOMImageSizes[0].width - _DOMImageSizes[2].width,
-                height = size.height - _DOMImageSizes[0].height - _DOMImageSizes[6].height;
+            if (_backgroundType === BackgroundVerticalThreePartImage)
+            {
+                CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], size.width, size.height - _DOMImageSizes[0].height - _DOMImageSizes[2].height);
+            }
+            else if (_backgroundType === BackgroundHorizontalThreePartImage)
+            {
+                CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], size.width - _DOMImageSizes[0].width - _DOMImageSizes[2].width, size.height);
+            }
+            else if (_backgroundType === BackgroundNinePartImage)
+            {
+                var width = size.width - _DOMImageSizes[0].width - _DOMImageSizes[2].width,
+                    height = size.height - _DOMImageSizes[0].height - _DOMImageSizes[6].height;
 
-            CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], width, _DOMImageSizes[0].height);
-            CPDOMDisplayServerSetStyleSize(_DOMImageParts[3], _DOMImageSizes[3].width, height);
-            CPDOMDisplayServerSetStyleSize(_DOMImageParts[4], width, height);
-            CPDOMDisplayServerSetStyleSize(_DOMImageParts[5], _DOMImageSizes[5].width, height);
-            CPDOMDisplayServerSetStyleSize(_DOMImageParts[7], width, _DOMImageSizes[7].height);
+                CPDOMDisplayServerSetStyleSize(_DOMImageParts[1], width, _DOMImageSizes[0].height);
+                CPDOMDisplayServerSetStyleSize(_DOMImageParts[3], _DOMImageSizes[3].width, height);
+                CPDOMDisplayServerSetStyleSize(_DOMImageParts[4], width, height);
+                CPDOMDisplayServerSetStyleSize(_DOMImageParts[5], _DOMImageSizes[5].width, height);
+                CPDOMDisplayServerSetStyleSize(_DOMImageParts[7], width, _DOMImageSizes[7].height);
+            }
         }
     }
 #endif
@@ -1188,6 +1196,23 @@ var CPViewFlags                     = { },
     return _isHidden;
 }
 
+- (void)setClipsToBounds:(BOOL)shouldClip
+{
+    if (_clipsToBounds === shouldClip)
+        return;
+
+    _clipsToBounds = shouldClip;
+
+#if PLATFORM(DOM)
+    _DOMElement.style.overflow = _clipsToBounds ? "hidden" :  "visible";
+#endif
+}
+
+- (BOOL)clipsToBounds
+{
+    return _clipsToBounds;
+}
+
 /*!
     Sets the opacity of the receiver. The value must be in the range of 0.0 to 1.0, where 0.0 is 
     completely transparent and 1.0 is completely opaque.
@@ -1361,7 +1386,7 @@ var CPViewFlags                     = { },
     }
     else
     {
-        _backgroundType = BackgroundTrivialColor;
+        _backgroundType = colorNeedsDOMElement ? BackgroundTransparentColor : BackgroundTrivialColor;
         amount = (colorNeedsDOMElement ? 1 : 0) - _DOMImageParts.length;
     }
 
@@ -1384,7 +1409,7 @@ var CPViewFlags                     = { },
             _DOMElement.removeChild(_DOMImageParts.pop());
     }
 
-    if (_backgroundType === BackgroundTrivialColor)
+    if (_backgroundType === BackgroundTrivialColor || _backgroundType === BackgroundTransparentColor)
     {
         var colorCSS = colorExists ? [_backgroundColor cssString] : "";
 
diff --git a/AppKit/CPViewAnimation.j b/AppKit/CPViewAnimation.j
index 692523584..b1bc4d414 100644
--- a/AppKit/CPViewAnimation.j
+++ b/AppKit/CPViewAnimation.j
@@ -1,5 +1,5 @@
 /*
- * CPFlashView.j
+ * CPViewAnimation.j
  * AppKit
  *
  * Created by Klaas Pieter Annema on September 3, 2009.
@@ -22,6 +22,8 @@
 
 @import 
 
+#include "CoreGraphics/CGGeometry.h"
+
 CPViewAnimationTargetKey = @"CPViewAnimationTarget";
 CPViewAnimationStartFrameKey = @"CPViewAnimationStartFrame";
 CPViewAnimationEndFrameKey = @"CPViewAnimationEndFrame";
@@ -69,7 +71,7 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut";
     [super startAnimation];
 }
 
-- (void)setCurrentProgress:(NSAnimationProgress)progress
+- (void)setCurrentProgress:(CPAnimationProgress)progress
 {
     [super setCurrentProgress:progress];
 
@@ -80,14 +82,14 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut";
             view = [self _targetView:dictionary]
             startFrame = [self _startFrame:dictionary]
             endFrame = [self _endFrame:dictionary]
-            differenceFrame = CPRectMakeZero();
+            differenceFrame = _CGRectMakeZero();
 
         differenceFrame.origin.x = endFrame.origin.x - startFrame.origin.x;
         differenceFrame.origin.y = endFrame.origin.y - startFrame.origin.y;
         differenceFrame.size.width = endFrame.size.width - startFrame.size.width;
         differenceFrame.size.height = endFrame.size.height - startFrame.size.height;
 
-        var intermediateFrame = CPRectMakeZero();
+        var intermediateFrame = _CGRectMakeZero();
         intermediateFrame.origin.x = startFrame.origin.x + differenceFrame.origin.x * progress;
         intermediateFrame.origin.y = startFrame.origin.y + differenceFrame.origin.y * progress;
         intermediateFrame.size.width = startFrame.size.width + differenceFrame.size.width * progress;
@@ -103,7 +105,7 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut";
             [view setAlphaValue:1.0 + ( 0.0 - 1.0 ) * progress];
 
         if (progress === 1.0)
-            [view setHidden:CPRectIsNull(endFrame) || [view alphaValue] === 0.0];
+            [view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
     }
 }
 
@@ -124,7 +126,7 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut";
         else if (effect === CPViewAnimationFadeOutEffect)
             [view setAlphaValue:0.0];
 
-        [view setHidden:CPRectIsNull(endFrame) || [view alphaValue] === 0.0];
+        [view setHidden:_CGRectIsNull(endFrame) || [view alphaValue] === 0.0];
     }
 
     [super stopAnimation];
@@ -139,7 +141,7 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut";
     return targetView;
 }
 
-- (CPRect)_startFrame:(CPDictionary)dictionary
+- (CGRect)_startFrame:(CPDictionary)dictionary
 {
     var startFrame = [dictionary valueForKey:CPViewAnimationStartFrameKey];
     if (!startFrame)
@@ -148,7 +150,7 @@ CPViewAnimationFadeOutEffect = @"CPViewAnimationFadeOut";
     return startFrame;
 }
 
-- (CPRect)_endFrame:(CPDictionary)dictionary
+- (CGRect)_endFrame:(CPDictionary)dictionary
 {
     var endFrame = [dictionary valueForKey:CPViewAnimationEndFrameKey];
     if (!endFrame)
diff --git a/AppKit/CPWebView.j b/AppKit/CPWebView.j
index 38ddfb5fa..bf36a7773 100644
--- a/AppKit/CPWebView.j
+++ b/AppKit/CPWebView.j
@@ -69,11 +69,13 @@ CPWebViewScrollNative                           = 2;
     
     CPString    _url;
     CPString    _html;
-    
+
     Function    _loadCallback;
-    
+
     int         _scrollMode;
     CGSize      _scrollSize;
+
+    int         _loadHTMLStringTimer;
 }
 
 - (id)initWithFrame:(CPRect)frameRect frameName:(CPString)frameName groupName:(CPString)groupName
@@ -110,6 +112,7 @@ CPWebViewScrollNative                           = 2;
     _iframe.style.width = "100%";
     _iframe.style.height = "100%";
     _iframe.style.borderWidth = "0px";
+    _iframe.frameBorder = "0";
     
     [self setDrawsBackground:YES];
     
@@ -171,7 +174,32 @@ CPWebViewScrollNative                           = 2;
     [self _resizeWebFrame];
 }
 
-- (BOOL)_resizeWebFrame
+- (void)_attachScrollEventIfNecessary
+{
+    if (_scrollMode !== CPWebViewScrollAppKit)
+        return;
+
+    var win = null;
+    try { win = [self DOMWindow]; } catch (e) {}
+
+    if (win && win.addEventListener)
+    {
+        var scrollEventHandler = function(anEvent)
+        {
+            var frameBounds = [self bounds],
+                frameCenter = CGPointMake(CGRectGetMidX(frameBounds), CGRectGetMidY(frameBounds)),
+                windowOrigin = [self convertPoint:frameCenter toView:nil],
+                globalOrigin = [[self window] convertBaseToBridge:windowOrigin];
+
+            anEvent._overrideLocation = globalOrigin;
+            [[[self window] platformWindow] scrollEvent:anEvent];
+        };
+
+        win.addEventListener("DOMMouseScroll", scrollEventHandler, false);
+    }
+}
+
+- (void)_resizeWebFrame
 {
     if (_scrollMode === CPWebViewScrollAppKit)
     {
@@ -181,13 +209,14 @@ CPWebViewScrollNative                           = 2;
         }
         else
         {
-            [_frameView setFrameSize:[_scrollView contentSize]];
+            var visibleRect = [_frameView visibleRect];
+            [_frameView setFrameSize:CGSizeMake(CGRectGetMaxX(visibleRect), CGRectGetMaxY(visibleRect))];
             
             // try to get the document size so we can correctly set the frame
             var win = null;
             try { win = [self DOMWindow]; } catch (e) {}
 
-            if (win && win.document)
+            if (win && win.document && win.document.body)
             {
                 var width = win.document.body.scrollWidth,
                     height = win.document.body.scrollHeight;
@@ -203,6 +232,8 @@ CPWebViewScrollNative                           = 2;
             
                 [_frameView setFrameSize:CGSizeMake(800, 1600)];
             }
+
+            [_frameView scrollRectToVisible:visibleRect];
         }
     }
 }
@@ -217,7 +248,10 @@ CPWebViewScrollNative                           = 2;
 
 - (void)_setScrollMode:(int)aScrollMode
 {
-    _scrollMode = aScrollMode;
+    if (CPBrowserIsEngine(CPInternetExplorerBrowserEngine))
+        _scrollMode = CPWebViewScrollNative;
+    else
+        _scrollMode = aScrollMode;
         
     _ignoreLoadStart = YES;
     _ignoreLoadEnd  = YES;
@@ -256,6 +290,8 @@ CPWebViewScrollNative                           = 2;
 
     [self _setScrollMode:CPWebViewScrollAppKit];
 
+    [_frameView setFrameSize:[_scrollView contentSize]];
+
     [self _startedLoading];
     
     _ignoreLoadStart = YES;
@@ -293,8 +329,15 @@ CPWebViewScrollNative                           = 2;
         // clear the iframe
         _iframe.src = "";
 
+        if (_loadHTMLStringTimer !== nil)
+        {
+            window.clearTimeout(_loadHTMLStringTimer);
+            _loadHTMLStringTimer = nil;
+        }
+
         // need to give the browser a chance to reset iframe, otherwise we'll be document.write()-ing the previous document 
-        window.setTimeout(function() {
+        _loadHTMLStringTimer = window.setTimeout(function()
+        {
             var win = [self DOMWindow];
             
             win.document.write(_html);
@@ -315,7 +358,8 @@ CPWebViewScrollNative                           = 2;
 - (void)_finishedLoading
 {
     [self _resizeWebFrame];
-    
+    [self _attachScrollEventIfNecessary];
+
     [[CPNotificationCenter defaultCenter] postNotificationName:CPWebViewProgressFinishedNotification object:self];
 
     if ([_frameLoadDelegate respondsToSelector:@selector(webView:didFinishLoadForFrame:)])
diff --git a/AppKit/CPWindow/CPWindow.j b/AppKit/CPWindow/CPWindow.j
index 7a916d9eb..f06953ab1 100644
--- a/AppKit/CPWindow/CPWindow.j
+++ b/AppKit/CPWindow/CPWindow.j
@@ -372,6 +372,15 @@ CPTexturedBackgroundWindowMask
             [self setPlatformWindow:[CPPlatformWindow primaryPlatformWindow]];
         else
         {
+            // give zero sized borderless bridge windows a default size if we're not in the browser so they show up in NativeHost.
+            if ((aStyleMask & CPBorderlessBridgeWindowMask) && aContentRect.size.width === 0 && aContentRect.size.height === 0)
+            {
+                var visibleFrame = [[[CPScreen alloc] init] visibleFrame];
+                _frame.size.height = MIN(768.0, visibleFrame.size.height);
+                _frame.size.width = MIN(1024.0, visibleFrame.size.width);
+                _frame.origin.x = (visibleFrame.size.width - _frame.size.width) / 2;
+                _frame.origin.y = (visibleFrame.size.height - _frame.size.height) / 2;
+            }
             [self setPlatformWindow:[[CPPlatformWindow alloc] initWithContentRect:_frame]];
             [self platformWindow]._only = self;
         }
@@ -883,6 +892,23 @@ CPTexturedBackgroundWindowMask
     return _contentView;
 }
 
+/*!
+    Applies an alpha value to the window.
+    @param aValue the alpha value to apply
+*/
+- (void)setAlphaValue:(float)aValue
+{
+    [_windowView setAlphaValue:aValue];
+}
+
+/*!
+    Returns the alpha value of the window.
+*/
+- (float)alphaValue
+{
+    return [_windowView alphaValue];
+}
+
 /*!
     Sets the window's background color.
     @param aColor the new color for the background
@@ -1907,7 +1933,8 @@ CPTexturedBackgroundWindowMask
             [keyWindow makeKeyWindow];
         else
         {
-            var menuWindow = [CPApp mainMenu]._menuWindow;
+            var mainMenu = [CPApp mainMenu],
+                menuWindow = mainMenu ? mainMenu._menuWindow : nil;
             for (var i = 0; i < windowCount; i++)
             {
                 var currentWindow = allWindows[i];
@@ -1935,7 +1962,8 @@ CPTexturedBackgroundWindowMask
             [mainWindow makeMainWindow];
         else
         {
-            var menuWindow = [CPApp mainMenu]._menuWindow;
+            var mainMenu = [CPApp mainMenu],
+                menuWindow = mainMenu ? mainMenu._menuWindow : nil;
             for (var i = 0; i < windowCount; i++)
             {
                 var currentWindow = allWindows[i];
diff --git a/AppKit/Cib/CPCibLoading.j b/AppKit/Cib/CPCibLoading.j
index aa7007556..22543eddc 100644
--- a/AppKit/Cib/CPCibLoading.j
+++ b/AppKit/Cib/CPCibLoading.j
@@ -37,12 +37,12 @@ var CPCibOwner = @"CPCibOwner";
 
 @implementation CPBundle (CPCibLoading)
 
-+ (void)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable
++ (CPCib)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable
 {
-    [[[CPCib alloc] initWithContentsOfURL:anAbsolutePath] instantiateCibWithExternalNameTable:aNameTable];
+    return [[[CPCib alloc] initWithContentsOfURL:anAbsolutePath] instantiateCibWithExternalNameTable:aNameTable];
 }
 
-+ (void)loadCibNamed:(CPString)aName owner:(id)anOwner
++ (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner
 {
     if (![aName hasSuffix:@".cib"])
         aName = [aName stringByAppendingString:@".cib"];
@@ -51,24 +51,24 @@ var CPCibOwner = @"CPCibOwner";
     var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
         path = [bundle pathForResource:aName];
 
-    [self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]];
+    return [self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner]];
 }
 
-- (void)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable
+- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable
 {
-    [[[CPCib alloc] initWithContentsOfURL:aFileName] instantiateCibWithExternalNameTable:aNameTable];
+    return [[[CPCib alloc] initWithContentsOfURL:aFileName] instantiateCibWithExternalNameTable:aNameTable];
 }
 
-+ (void)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable loadDelegate:aDelegate
++ (CPCib)loadCibFile:(CPString)anAbsolutePath externalNameTable:(CPDictionary)aNameTable loadDelegate:aDelegate
 {
-    [[CPCib alloc]
+    return ([[CPCib alloc]
     initWithContentsOfURL:anAbsolutePath
              loadDelegate:[[_CPCibLoadDelegate alloc]
                 initWithLoadDelegate:aDelegate
-                   externalNameTable:aNameTable]];
+                   externalNameTable:aNameTable]]);
 }
 
-+ (void)loadCibNamed:(CPString)aName owner:(id)anOwner loadDelegate:(id)aDelegate
++ (CPCib)loadCibNamed:(CPString)aName owner:(id)anOwner loadDelegate:(id)aDelegate
 {
     if (![aName hasSuffix:@".cib"])
         aName = [aName stringByAppendingString:@".cib"];
@@ -77,17 +77,17 @@ var CPCibOwner = @"CPCibOwner";
     var bundle = anOwner ? [CPBundle bundleForClass:[anOwner class]] : [CPBundle mainBundle],
         path = [bundle pathForResource:aName];
     
-    [self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner] loadDelegate:aDelegate];
+    return [self loadCibFile:path externalNameTable:[CPDictionary dictionaryWithObject:anOwner forKey:CPCibOwner] loadDelegate:aDelegate];
 }
 
-- (void)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable loadDelegate:(id)aDelegate
+- (CPCib)loadCibFile:(CPString)aFileName externalNameTable:(CPDictionary)aNameTable loadDelegate:(id)aDelegate
 {
-    [[CPCib alloc]
+    return ([[CPCib alloc]
         initWithCibNamed:aFileName
                   bundle:self
             loadDelegate:[[_CPCibLoadDelegate alloc]
                 initWithLoadDelegate:aDelegate
-                   externalNameTable:aNameTable]];
+                   externalNameTable:aNameTable]]);
 }
 
 @end
diff --git a/AppKit/Platform/CPPlatformWindow.j b/AppKit/Platform/CPPlatformWindow.j
index 4ebfa8f81..a0f46cc52 100644
--- a/AppKit/Platform/CPPlatformWindow.j
+++ b/AppKit/Platform/CPPlatformWindow.j
@@ -41,6 +41,7 @@ var PrimaryPlatformWindow   = NULL;
 
     DOMElement      _DOMBodyElement;
     DOMElement      _DOMFocusElement;
+    DOMElement      _DOMEventGuard;
 
     CPArray         _windowLevels;
     CPDictionary    _windowLayers;
@@ -73,6 +74,15 @@ var PrimaryPlatformWindow   = NULL;
     return [CPSet set];
 }
 
++ (BOOL)supportsMultipleInstances
+{
+#if PLATFORM(DOM)
+    return !CPBrowserIsEngine(CPInternetExplorerBrowserEngine);
+#else
+    return NO;
+#endif
+}
+
 + (CPPlatformWindow)primaryPlatformWindow
 {
     return PrimaryPlatformWindow;
diff --git a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j
index 30f560581..a0b9403bb 100644
--- a/AppKit/Platform/DOM/CPPlatformWindow+DOM.j
+++ b/AppKit/Platform/DOM/CPPlatformWindow+DOM.j
@@ -130,8 +130,6 @@ var CPDOMEventGetClickCount,
     StopDOMEventPropagation,
     StopContextMenuDOMEventPropagation;
 
-var _DOMEventGuard;
-
 //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.
@@ -280,7 +278,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
     _DOMBodyElement = theDocument.getElementById("cappuccino-body") || theDocument.body;
 
     // FIXME: Always do this?
-    if ([CPPlatform supportsDragAndDrop])
+    if (supportsNativeDragAndDrop)
         _DOMBodyElement.style["-khtml-user-select"] = "none";
 
     _DOMBodyElement.webkitTouchCallout = "none";
@@ -469,7 +467,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
     [PlatformWindows addObject:self];
 
     // FIXME: cpSetFrame?
-    _DOMWindow.document.write("");
+    _DOMWindow.document.write("");
     _DOMWindow.document.close();
 
     if (![CPPlatform isBrowser])
@@ -516,6 +514,11 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
         DOMDragElement.style.left = -_CGRectGetWidth(draggedWindowFrame) + "px";
         DOMDragElement.style.top = -_CGRectGetHeight(draggedWindowFrame) + "px";
 
+        var parentNode = DOMDragElement.parentNode;
+
+        if (parentNode)
+            parentNode.removeChild(DOMDragElement);
+
         _DOMBodyElement.appendChild(DOMDragElement);
 
         var draggingOffset = [dragServer draggingOffset];
@@ -848,12 +851,13 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
     if(!aDOMEvent)
         aDOMEvent = window.event;
 
+    var location = nil;
     if (CPFeatureIsCompatible(CPJavaScriptMouseWheelValues_8_15))
     {
-        var x = 0.0,
-            y = 0.0,
+        var x = aDOMEvent._offsetX || 0.0,
+            y = aDOMEvent._offsetY || 0.0,
             element = aDOMEvent.target;
-        
+
         while (element.nodeType !== 1)
             element = element.parentNode;
 
@@ -867,11 +871,13 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
             } while (element = element.offsetParent);
         }
     
-        var location = _CGPointMake((x + ((aDOMEvent.clientX - 8) / 15)), (y + ((aDOMEvent.clientY - 8) / 15)));
+        location = _CGPointMake((x + ((aDOMEvent.clientX - 8) / 15)), (y + ((aDOMEvent.clientY - 8) / 15)));
     }
+    else if (aDOMEvent._overrideLocation)
+        location = aDOMEvent._overrideLocation;
     else
-        var location = _CGPointMake(aDOMEvent.clientX, aDOMEvent.clientY);
-        
+        location = _CGPointMake(aDOMEvent.clientX, aDOMEvent.clientY);
+
     var deltaX = 0.0,
         deltaY = 0.0,
         windowNumber = 0,
@@ -880,7 +886,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
                         (aDOMEvent.ctrlKey ? CPControlKeyMask : 0) | 
                         (aDOMEvent.altKey ? CPAlternateKeyMask : 0) | 
                         (aDOMEvent.metaKey ? CPCommandKeyMask : 0);
-          
+
     StopDOMEventPropagation = YES;
 
     var theWindow = [self hitTest:location];
@@ -1189,6 +1195,7 @@ var supportsNativeDragAndDrop = [CPPlatform supportsDragAndDrop];
 
         [_windowLevels insertObject:aLevel atIndex:insertionIndex];
         layer._DOMElement.style.zIndex = aLevel;
+
         _DOMBodyElement.appendChild(layer._DOMElement);
     }
     
diff --git a/AppKit/Resources/action_button.png b/AppKit/Resources/action_button.png
new file mode 100644
index 000000000..2af0dfc48
Binary files /dev/null and b/AppKit/Resources/action_button.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-center.png b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-center.png
index 6b3e348c1..cc9401c73 100644
Binary files a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-center.png and b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-disabled-center.png b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-disabled-center.png
new file mode 100644
index 000000000..dd4ce2152
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-disabled-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-disabled-left.png b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-disabled-left.png
new file mode 100644
index 000000000..a615b9382
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-disabled-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-disabled-right.png b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-disabled-right.png
new file mode 100644
index 000000000..6f14f35d8
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-disabled-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-center.png b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-center.png
index 0f35e8c4b..be25a32af 100644
Binary files a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-center.png and b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-left.png b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-left.png
index fe9f27411..a4b766ac2 100644
Binary files a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-left.png and b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-right.png b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-right.png
index 809339ae4..cd709f319 100644
Binary files a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-right.png and b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-highlighted-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-left.png b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-left.png
index 6c10bf51e..674aacd37 100644
Binary files a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-left.png and b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-right.png b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-right.png
index 91439ae90..88a8540cc 100644
Binary files a/AppKit/Themes/Aristo/Resources/HUD/button-bezel-right.png and b/AppKit/Themes/Aristo/Resources/HUD/button-bezel-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-center.png b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-center.png
new file mode 100644
index 000000000..bc5b3a93b
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-disabled-center.png b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-disabled-center.png
new file mode 100644
index 000000000..8289aed50
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-disabled-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-disabled-left.png b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-disabled-left.png
new file mode 100644
index 000000000..b0e2c0ac5
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-disabled-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-disabled-right.png b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-disabled-right.png
new file mode 100644
index 000000000..25dac55c3
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-disabled-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-highlighted-center.png b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-highlighted-center.png
new file mode 100644
index 000000000..79e37a1a4
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-highlighted-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-highlighted-left.png b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-highlighted-left.png
new file mode 100644
index 000000000..19b884419
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-highlighted-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-highlighted-right.png b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-highlighted-right.png
new file mode 100644
index 000000000..da0cb9a8a
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-highlighted-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-left.png b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-left.png
new file mode 100644
index 000000000..dd02f1725
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-right.png b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-right.png
new file mode 100644
index 000000000..e32647f97
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/default-button-bezel-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-center.png b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-center.png
new file mode 100644
index 000000000..b565d8e6a
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-disabled-center.png b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-disabled-center.png
new file mode 100644
index 000000000..31da63ab7
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-disabled-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-disabled-left.png b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-disabled-left.png
new file mode 100644
index 000000000..e395935df
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-disabled-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-disabled-right.png b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-disabled-right.png
new file mode 100644
index 000000000..d752a54ec
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-disabled-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-left.png b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-left.png
new file mode 100644
index 000000000..05ff0804e
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-right.png b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-right.png
new file mode 100644
index 000000000..db4488023
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/horizontal-track-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/knob-highlighted.png b/AppKit/Themes/Aristo/Resources/HUD/knob-highlighted.png
new file mode 100644
index 000000000..5c4387a55
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/knob-highlighted.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/knob.png b/AppKit/Themes/Aristo/Resources/HUD/knob.png
new file mode 100755
index 000000000..bad25cf31
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/knob.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-center.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-center.png
new file mode 100644
index 000000000..455b25dec
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-center.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-center.png
new file mode 100644
index 000000000..a32359e5c
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-divider.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-divider.png
new file mode 100644
index 000000000..a07b15d5c
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-divider.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-left.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-left.png
new file mode 100644
index 000000000..cca119c66
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-right.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-right.png
new file mode 100644
index 000000000..9f3a03b7d
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-disabled-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-divider.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-divider.png
new file mode 100644
index 000000000..6811fc89a
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-divider.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-center.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-center.png
new file mode 100644
index 000000000..ac04df7bb
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-center.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-center.png
new file mode 100644
index 000000000..06105ca17
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-divider.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-divider.png
new file mode 100644
index 000000000..513282884
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-divider.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-left.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-left.png
new file mode 100644
index 000000000..e8bf1b6b0
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-right copy.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-right copy.png
new file mode 100644
index 000000000..23587f095
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-right copy.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-right.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-right.png
new file mode 100644
index 000000000..73880f0de
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-disabled-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-divider.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-divider.png
new file mode 100644
index 000000000..6811fc89a
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-divider.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-left.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-left.png
new file mode 100644
index 000000000..56ac58d65
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-right.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-right.png
new file mode 100644
index 000000000..2941208e8
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-highlighted-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-left.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-left.png
new file mode 100644
index 000000000..61b659212
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-center.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-center.png
new file mode 100644
index 000000000..aa78a13bc
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-highlighted-center.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-highlighted-center.png
new file mode 100644
index 000000000..dc64067fe
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-highlighted-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-highlighted-left.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-highlighted-left.png
new file mode 100644
index 000000000..c81b5aba0
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-highlighted-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-highlighted-right.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-highlighted-right.png
new file mode 100644
index 000000000..6eb94808d
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-highlighted-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-left.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-left.png
new file mode 100644
index 000000000..a1c5e0323
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-left.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-right.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-right.png
new file mode 100644
index 000000000..949864422
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-pushed-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-right.png b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-right.png
new file mode 100644
index 000000000..12e90829c
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/segmented-control-bezel-right.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/slider-circular-bezel.png b/AppKit/Themes/Aristo/Resources/HUD/slider-circular-bezel.png
new file mode 100644
index 000000000..69d5c29a9
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/slider-circular-bezel.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/slider-circular-disabled-bezel.png b/AppKit/Themes/Aristo/Resources/HUD/slider-circular-disabled-bezel.png
new file mode 100644
index 000000000..9c54fa23e
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/slider-circular-disabled-bezel.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/slider-circular-disabled-knob.png b/AppKit/Themes/Aristo/Resources/HUD/slider-circular-disabled-knob.png
new file mode 100644
index 000000000..5c0d8bd11
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/slider-circular-disabled-knob.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/slider-circular-knob.png b/AppKit/Themes/Aristo/Resources/HUD/slider-circular-knob.png
new file mode 100644
index 000000000..050ffefe6
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/slider-circular-knob.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/vertical-track-bottom.png b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-bottom.png
new file mode 100644
index 000000000..bdec63744
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-bottom.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/vertical-track-center.png b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-center.png
new file mode 100644
index 000000000..2fbcce41e
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/vertical-track-disabled-bottom.png b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-disabled-bottom.png
new file mode 100644
index 000000000..efc622a82
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-disabled-bottom.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/vertical-track-disabled-center.png b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-disabled-center.png
new file mode 100644
index 000000000..25675211c
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-disabled-center.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/vertical-track-disabled-top.png b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-disabled-top.png
new file mode 100644
index 000000000..12abe0927
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-disabled-top.png differ
diff --git a/AppKit/Themes/Aristo/Resources/HUD/vertical-track-top.png b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-top.png
new file mode 100644
index 000000000..a27c09e56
Binary files /dev/null and b/AppKit/Themes/Aristo/Resources/HUD/vertical-track-top.png differ
diff --git a/AppKit/Themes/Aristo/ThemeDescriptors.j b/AppKit/Themes/Aristo/ThemeDescriptors.j
old mode 100644
new mode 100755
index eb7e8f50e..d1d77ee49
--- a/AppKit/Themes/Aristo/ThemeDescriptors.j
+++ b/AppKit/Themes/Aristo/ThemeDescriptors.j
@@ -439,7 +439,7 @@
         isVertical:NO]);
 
     [button setValue:CPLeftTextAlignment forThemeAttribute:@"alignment" inState:CPThemeStateBordered];
-    [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
+    [button setValue:[CPFont systemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
     [button setValue:CGInsetMake(0.0, 0.0, 0.0, 20.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
     [button setValue:bezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];    
     [button setValue:bezelColorSelected forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateSelected];
@@ -493,7 +493,7 @@
         isVertical:NO]);
     
     [button setValue:CPLeftTextAlignment forThemeAttribute:@"alignment" inState:CPThemeStateBordered];
-    [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
+    [button setValue:[CPFont systemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
     [button setValue:CGInsetMake(0.0, 0.0, 0.0, 20.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
     [button setValue:bezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];    
     [button setValue:bezelColorSelected forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered | CPThemeStateSelected];
@@ -721,7 +721,7 @@
 
 + (CPButtonBar)themedButtonBar
 {
-    var buttonBar = [[CPButtonBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 140.0, 26.0)],
+    var buttonBar = [[CPButtonBar alloc] initWithFrame:CGRectMake(0.0, 0.0, 147.0, 26.0)],
         color = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"buttonbar-bezel.png" size:CGSizeMake(1.0, 26.0)]];
 
     [buttonBar setHasResizeControl:YES];
@@ -761,8 +761,13 @@
     [buttonBar setValue:buttonBezelColor forThemeAttribute:@"button-bezel-color"];
     [buttonBar setValue:buttonBezelHighlightedColor forThemeAttribute:@"button-bezel-color" inState:CPThemeStateHighlighted];
     [buttonBar setValue:buttonBezelDisabledColor forThemeAttribute:@"button-bezel-color" inState:CPThemeStateDisabled];
+    [buttonBar setValue:[CPColor blackColor] forThemeAttribute:@"button-text-color"];
 
-    [buttonBar setButtons:[[CPButtonBar plusButton], [CPButtonBar minusButton], [self themedPullDownMenu]]];
+    var popup = [CPButtonBar actionPopupButton];
+    [popup addItemWithTitle:"Item 1"];
+    [popup addItemWithTitle:"Item 2"];
+
+    [buttonBar setButtons:[[CPButtonBar plusButton], [CPButtonBar minusButton], popup]];
 
     return buttonBar;
 }
@@ -783,38 +788,186 @@
     return [CPColor blackColor];
 }
 
-+ (CPButton)themedButton
++ (CPPopUpButton)themedSegmentedControl
 {
-    var button = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 20.0)],
+    var segmentedControl = [[CPSegmentedControl alloc] initWithFrame:CGRectMake(0.0, 0.0, 0.0, 24.0)];
+    
+    [segmentedControl setTrackingMode:CPSegmentSwitchTrackingSelectAny];
+    [segmentedControl setSegmentCount:3];
+    
+    [segmentedControl setWidth:40.0 forSegment:0];
+    [segmentedControl setLabel:@"foo" forSegment:0];
+    [segmentedControl setTag:1 forSegment:0];
+
+    [segmentedControl setWidth:60.0 forSegment:1];
+    [segmentedControl setLabel:@"bar" forSegment:1];
+    [segmentedControl setTag:2 forSegment:1];
+
+    [segmentedControl setWidth:35.0 forSegment:2];
+    [segmentedControl setLabel:@"1" forSegment:2];
+    [segmentedControl setTag:3 forSegment:2];
+    
+    //various colors
+    var centerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-center.png" size:CGSizeMake(1.0, 24.0)]),
+        dividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-divider.png" size:CGSizeMake(1.0, 24.0)]),
+        centerHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-center.png" size:CGSizeMake(1.0, 24.0)]),
+        dividerHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-divider.png" size:CGSizeMake(1.0, 24.0)]),
+        leftHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)]),
+        rightHighlightedBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]),
+        inactiveCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)]),
+        inactiveDividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-disabled-divider.png" size:CGSizeMake(1.0, 24.0)]),
+        inactiveLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)]),
+        inactiveRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]),
+        inactiveHighlightedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-disabled-center.png" size:CGSizeMake(1.0, 24.0)]),
+        inactiveHighlightedDividerBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-disabled-divider.png" size:CGSizeMake(1.0, 24.0)]),
+        inactiveHighlightedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-disabled-left.png" size:CGSizeMake(4.0, 24.0)]),
+        inactiveHighlightedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-highlighted-disabled-right.png" size:CGSizeMake(4.0, 24.0)]),
+        leftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-left.png" size:CGSizeMake(4.0, 24.0)]),
+        rightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-right.png" size:CGSizeMake(4.0, 24.0)]),
+        pushedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-center.png" size:CGSizeMake(1.0, 24.0)]),
+        pushedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-left.png" size:CGSizeMake(4.0, 24.0)]),
+        pushedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-right.png" size:CGSizeMake(4.0, 24.0)]);
+        pushedHighlightedCenterBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-highlighted-center.png" size:CGSizeMake(1.0, 24.0)]),
+        pushedHighlightedLeftBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-highlighted-left.png" size:CGSizeMake(4.0, 24.0)]),
+        pushedHighlightedRightBezelColor = PatternColor([_CPCibCustomResource imageResourceWithName:@"HUD/segmented-control-bezel-pushed-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]);
+
+    [segmentedControl setValue:centerBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateNormal];
+    [segmentedControl setValue:inactiveCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateDisabled];
+    [segmentedControl setValue:inactiveHighlightedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
+    [segmentedControl setValue:centerHighlightedBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateSelected];
+    [segmentedControl setValue:pushedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateHighlighted];
+    [segmentedControl setValue:pushedHighlightedCenterBezelColor forThemeAttribute:@"center-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected];
+
+    [segmentedControl setValue:dividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateNormal];
+    [segmentedControl setValue:inactiveDividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateDisabled];
+    [segmentedControl setValue:inactiveHighlightedDividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
+    [segmentedControl setValue:dividerHighlightedBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateSelected];
+    [segmentedControl setValue:dividerBezelColor forThemeAttribute:@"divider-bezel-color" inState:CPThemeStateHighlighted];
+
+    [segmentedControl setValue:rightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateNormal];
+    [segmentedControl setValue:inactiveRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateDisabled];
+    [segmentedControl setValue:inactiveHighlightedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
+    [segmentedControl setValue:rightHighlightedBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateSelected];
+    [segmentedControl setValue:pushedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateHighlighted];
+    [segmentedControl setValue:pushedHighlightedRightBezelColor forThemeAttribute:@"right-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected];
+
+    [segmentedControl setValue:leftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateNormal];
+    [segmentedControl setValue:inactiveLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateDisabled];
+    [segmentedControl setValue:inactiveHighlightedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateSelected|CPThemeStateDisabled];
+    [segmentedControl setValue:leftHighlightedBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateSelected];
+    [segmentedControl setValue:pushedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateHighlighted];
+    [segmentedControl setValue:pushedHighlightedLeftBezelColor forThemeAttribute:@"left-segment-bezel-color" inState:CPThemeStateHighlighted|CPThemeStateSelected];
+
+    [segmentedControl setValue:CGInsetMake(0.0, 4.0, 0.0, 4.0) forThemeAttribute:@"content-inset" inState:CPThemeStateNormal];
+
+    [segmentedControl setValue:CGInsetMake(0.0, 0.0, 0.0, 0.0) forThemeAttribute:@"bezel-inset" inState:CPThemeStateNormal];
+
+    [segmentedControl setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font"];
+    [segmentedControl setValue:[CPColor colorWithCalibratedWhite:255.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"];
+    [segmentedControl setValue:[CPColor colorWithCalibratedWhite:0.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-shadow-color"];
+    [segmentedControl setValue:CGSizeMake(-1.0, -1.0) forThemeAttribute:@"text-shadow-offset"];
+    [segmentedControl setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
+
+    [segmentedControl setValue:1.0 forThemeAttribute:@"divider-thickness"];
+    [segmentedControl setValue:24.0 forThemeAttribute:@"default-height"];
+
+    return segmentedControl;
+}
+
++ (CPButton)button
+{
+    var button = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 60.0, 24.0)],
 
         bezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
             [
-                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-left.png" size:CGSizeMake(13.0, 20.0)],
-                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-center.png" size:CGSizeMake(1.0, 20.0)],
-                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-right.png" size:CGSizeMake(13.0, 20.0)]
+                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-left.png" size:CGSizeMake(4.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-center.png" size:CGSizeMake(1.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-right.png" size:CGSizeMake(4.0, 24.0)]
             ]
         isVertical:NO]],
 
         highlightedBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
             [
-                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-highlighted-left.png" size:CGSizeMake(13.0, 20.0)],
-                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-highlighted-center.png" size:CGSizeMake(1.0, 20.0)],
-                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-highlighted-right.png" size:CGSizeMake(13.0, 20.0)]
+                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-highlighted-center.png" size:CGSizeMake(1.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]
+            ]
+        isVertical:NO]],
+
+        defaultBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+            [
+                [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-left.png" size:CGSizeMake(4.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-center.png" size:CGSizeMake(1.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-right.png" size:CGSizeMake(4.0, 24.0)]
+            ]
+        isVertical:NO]],
+
+        defaultHighlightedBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+            [
+                [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-highlighted-left.png" size:CGSizeMake(4.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-highlighted-center.png" size:CGSizeMake(1.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-highlighted-right.png" size:CGSizeMake(4.0, 24.0)]
+            ]
+        isVertical:NO]],
+
+        defaultDisabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+            [
+                [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/default-button-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]
+            ]
+        isVertical:NO]],
+        
+        disabledBezelColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+            [
+                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-disabled-left.png" size:CGSizeMake(4.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-disabled-center.png" size:CGSizeMake(1.0, 24.0)],
+                [_CPCibCustomResource imageResourceWithName:"HUD/button-bezel-disabled-right.png" size:CGSizeMake(4.0, 24.0)]
             ]
         isVertical:NO]];
 
-    [button setTitle:@"Cancel"];
+    [button setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
 
-    [button setValue:[CPFont systemFontOfSize:11.0] forThemeAttribute:@"font" inState:CPThemeStateBordered];
-    [button setValue:[CPColor whiteColor] forThemeAttribute:@"text-color"];
+    [button setValue:[CPColor colorWithCalibratedWhite:255.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-color"];
+    [button setValue:[CPColor colorWithCalibratedWhite:0.0 / 255.0 alpha:1.0] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered];
+    [button setValue:[CPColor colorWithCalibratedWhite:0.0 / 255.0 alpha:0.6] forThemeAttribute:@"text-shadow-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
+    [button setValue:CGSizeMake(-1.0,-1.0) forThemeAttribute:@"text-shadow-offset" inState:CPThemeStateBordered];
     [button setValue:CPLineBreakByTruncatingTail forThemeAttribute:@"line-break-mode"];
-
+    
     [button setValue:bezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered];
     [button setValue:highlightedBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateHighlighted];
-    [button setValue:CGInsetMake(2.0, 5.0, 4.0, 5.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
+    [button setValue:CGInsetMake(0.0, 5.0, 0.0, 5.0) forThemeAttribute:@"content-inset" inState:CPThemeStateBordered];
 
-    [button setValue:CGSizeMake(0.0, 20.0) forThemeAttribute:@"min-size"];
-    [button setValue:CGSizeMake(-1.0, 20.0) forThemeAttribute:@"max-size"];
+    [button setValue:[CPColor colorWithCalibratedWhite:0.97 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled];
+    [button setValue:disabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDisabled];
+    [button setValue:defaultDisabledBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDefault|CPThemeStateDisabled];
+    
+    [button setValue:defaultBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateDefault];
+    [button setValue:defaultHighlightedBezelColor forThemeAttribute:@"bezel-color" inState:CPThemeStateBordered|CPThemeStateHighlighted|CPThemeStateDefault];
+    [button setValue:[CPColor colorWithCalibratedRed:250.0/255.0 green:250.0/255.0 blue:250.0/255.0 alpha:1.0] forThemeAttribute:@"text-color" inState:CPThemeStateDefault];
+    [button setValue:[CPColor colorWithCalibratedRed:250.0/255.0 green:250.0/255.0 blue:250.0/255.0 alpha:0.6] forThemeAttribute:@"text-color" inState:CPThemeStateDisabled|CPThemeStateDefault];
+
+    [button setValue:CGSizeMake(0.0, 24.0) forThemeAttribute:@"min-size"];
+    [button setValue:CGSizeMake(-1.0, 24.0) forThemeAttribute:@"max-size"];
+
+    return button;
+}
+
++ (CPButton)themedStandardButton
+{
+    var button = [self button];
+
+    [button setTitle:@"Cancel"];
+
+    return button;
+}
+
++ (CPButton)themedDefaultButton
+{
+    var button = [self button];
+
+    [button setTitle:@"OK"];
+    [button setDefaultButton:YES];
 
     return button;
 }
@@ -931,6 +1084,96 @@
     return scroller;
 }
 
++ (CPSlider)themedHorizontalSlider
+{
+    var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 50.0, 24.0)],
+        trackColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+        [
+            [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-left.png" size:CGSizeMake(4.0, 5.0)],
+            [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-center.png" size:CGSizeMake(1.0, 5.0)],
+            [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-right.png" size:CGSizeMake(4.0, 5.0)]
+        ]
+        isVertical:NO]],
+        
+        trackDisabledColor = [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+        [
+            [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-disabled-left.png" size:CGSizeMake(4.0, 5.0)],
+            [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-disabled-center.png" size:CGSizeMake(1.0, 5.0)],
+            [_CPCibCustomResource imageResourceWithName:"HUD/horizontal-track-disabled-right.png" size:CGSizeMake(4.0, 5.0)]
+        ]
+        isVertical:NO]];
+
+    [slider setValue:5.0 forThemeAttribute:@"track-width"];
+    [slider setValue:trackColor forThemeAttribute:@"track-color"];
+    [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled];
+    
+        var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob.png" size:CGSizeMake(23.0, 24.0)]],
+            knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]],
+            knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob-disabled.png" size:CGSizeMake(23.0, 24.0)]];
+
+    [slider setValue:CGSizeMake(23.0, 24.0) forThemeAttribute:@"knob-size"];
+    [slider setValue:knobColor forThemeAttribute:@"knob-color"];
+    [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateHighlighted];
+    [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled];
+    
+    return slider;
+}
+
++ (CPSlider)themedVerticalSlider
+{
+    var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 24.0, 50.0)],
+        trackColor =  [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+        [
+            [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-top.png" size:CGSizeMake(5.0, 6.0)],
+            [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-center.png" size:CGSizeMake(5.0, 1.0)],
+            [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-bottom.png" size:CGSizeMake(5.0, 4.0)]
+        ]
+        isVertical:YES]],
+        trackDisabledColor =  [CPColor colorWithPatternImage:[[CPThreePartImage alloc] initWithImageSlices:
+        [
+            [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-disabled-top.png" size:CGSizeMake(5.0, 6.0)],
+            [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-disabled-center.png" size:CGSizeMake(5.0, 1.0)],
+            [_CPCibCustomResource imageResourceWithName:"HUD/vertical-track-disabled-bottom.png" size:CGSizeMake(5.0, 4.0)]
+        ]
+        isVertical:YES]];
+        
+    [slider setValue:5.0 forThemeAttribute:@"track-width"];
+    [slider setValue:trackColor forThemeAttribute:@"track-color" inState:CPThemeStateVertical];
+    [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled];
+    
+        var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob.png" size:CGSizeMake(23.0, 24.0)]],
+            knobHighlightedColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob-highlighted.png" size:CGSizeMake(23.0, 24.0)]],
+            knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/knob-disabled.png" size:CGSizeMake(23.0, 24.0)]];
+
+    [slider setValue:CGSizeMake(23.0, 24.0) forThemeAttribute:@"knob-size"];
+    [slider setValue:knobColor forThemeAttribute:@"knob-color"];
+    [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateHighlighted];
+    [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled];
+    
+    return slider;
+}
++ (CPSlider)themedCircularSlider
+{
+    var slider = [[CPSlider alloc] initWithFrame:CGRectMake(0.0, 0.0, 34.0, 34.0)],
+        trackColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/slider-circular-bezel.png" size:CGSizeMake(34.0, 34.0)]),
+        trackDisabledColor = PatternColor([_CPCibCustomResource imageResourceWithName:"HUD/slider-circular-disabled-bezel.png" size:CGSizeMake(34.0, 34.0)]);
+
+    [slider setSliderType:CPCircularSlider];
+    [slider setValue:trackColor forThemeAttribute:@"track-color" inState:CPThemeStateCircular];
+    [slider setValue:trackDisabledColor forThemeAttribute:@"track-color" inState:CPThemeStateDisabled|CPThemeStateCircular];
+
+    var knobColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/slider-circular-knob.png" size:CGSizeMake(5.0, 5.0)]],
+        knobDisabledColor = [CPColor colorWithPatternImage:[_CPCibCustomResource imageResourceWithName:"HUD/slider-circular-disabled-knob.png" size:CGSizeMake(5.0, 5.0)]],
+        knobHighlightedColor = knobColor;
+
+    [slider setValue:CGSizeMake(5.0, 5.0) forThemeAttribute:@"knob-size" inState:CPThemeStateCircular];
+    [slider setValue:knobColor forThemeAttribute:@"knob-color" inState:CPThemeStateCircular];
+    [slider setValue:knobDisabledColor forThemeAttribute:@"knob-color" inState:CPThemeStateDisabled|CPThemeStateCircular];
+    [slider setValue:knobHighlightedColor forThemeAttribute:@"knob-color" inState:CPThemeStateCircular|CPThemeStateHighlighted];
+
+    return slider;
+}
+
 @end
 
 function PatternColor(anImage)
diff --git a/AppKit/_CPCornerView.j b/AppKit/_CPCornerView.j
index 81c4a86e5..a5e4f9a65 100644
--- a/AppKit/_CPCornerView.j
+++ b/AppKit/_CPCornerView.j
@@ -20,20 +20,6 @@
     return self;
 }
 
-- (void)drawRect:(CGRect)aRect
-{
-    var context = [[CPGraphicsContext currentContext] graphicsPort];
-
-    CGContextSetStrokeColor(context, [CPColor colorWithHexString:@"dce0e2"]);
-
-    var points = [
-                    CGPointMake(aRect.origin.x, aRect.origin.y + 0.5), 
-                    CGPointMake(aRect.origin.x, aRect.origin.y + aRect.size.height)
-                 ];
-
-    CGContextStrokeLineSegments(context, points, 2);
-}
-
 - (id)initWithCoder:(CPCoder)aCoder
 {
     self = [super initWithCoder:aCoder];
diff --git a/AppKit/_CPImageAndTextView.j b/AppKit/_CPImageAndTextView.j
index 695598a4d..cee931360 100644
--- a/AppKit/_CPImageAndTextView.j
+++ b/AppKit/_CPImageAndTextView.j
@@ -63,6 +63,7 @@ var HORIZONTAL_MARGIN   = 3.0,
     
     CPCellImagePosition     _imagePosition;
     CPImageScaling          _imageScaling;
+    BOOL                    _shouldDimImage;
     
     CPImage                 _image;
     CPString                _text;
@@ -211,6 +212,17 @@ var HORIZONTAL_MARGIN   = 3.0,
     return _imageScaling;
 }
 
+- (void)setDimsImage:(BOOL)shouldDim
+{
+    var shouldDimImage = !!shouldDimImage;
+
+    if (_shouldDimImage !== shouldDimImage)
+    {
+        _shouldDimImage = shouldDim;
+        [self setNeedsLayout];
+    }
+}
+
 - (void)setTextColor:(CPColor)aTextColor
 {
     if (_textColor === aTextColor)
@@ -572,6 +584,11 @@ var HORIZONTAL_MARGIN   = 3.0,
             imageHeight *= scale;
         }
 
+        if (CPFeatureIsCompatible(CPOpacityRequiresFilterFeature))
+            imageStyle.filter = @"alpha(opacity=" + _shouldDimImage ? 35 : 100 + ")";
+        else
+            imageStyle.opacity = _shouldDimImage ? 0.35 : 1.0;
+
         _DOMImageElement.width = imageWidth;
         _DOMImageElement.height = imageHeight;        
         imageStyle.width = MAX(imageWidth, 0) + "px";
diff --git a/CommonJS/lib/cappuccino/nativehost.js b/CommonJS/lib/cappuccino/nativehost.js
new file mode 100644
index 000000000..8def87642
--- /dev/null
+++ b/CommonJS/lib/cappuccino/nativehost.js
@@ -0,0 +1,70 @@
+var FILE = require("file");
+var OS = require("os");
+
+var NATIVEHOST_SOURCE = FILE.path(module.path).dirname().dirname().dirname().join("support", "NativeHost.app");
+
+exports.buildNativeHost = function(rootPath, buildNative, options) {
+    options = options || {};
+    options.index = options.index || "index.html";
+
+    rootPath = FILE.path(rootPath);
+    buildNative = FILE.path(buildNative);
+
+    if (buildNative.exists())
+        buildNative.rmtree();
+
+    buildNative.dirname().mkdirs();
+    // FIXME: Narwhal doesn't preserve permissions
+    // FILE.copyTree(NATIVEHOST_SOURCE, buildNative);
+    OS.system(["cp", "-r", NATIVEHOST_SOURCE, buildNative]);
+    FILE.chmod(buildNative.join("Contents", "MacOS", "NativeHost"), 0755);
+
+    var rootBaseName = rootPath.basename();
+    var buildClientDirectory = buildNative.join("Contents", "Resources", rootBaseName);
+
+    FILE.mkdirs(FILE.dirname(buildClientDirectory));
+    // FILE.copyTree(rootPath, buildClientDirectory);
+    OS.system(["cp", "-r", rootPath, buildClientDirectory]);
+
+    var defaultBundleName = buildNative.basename().match(/^(.*)(\.app)?$/)[1];
+
+    function mergePlist(plist, path) {
+        var otherPlist = CFPropertyList.readPropertyListFromFile(String(path));
+
+        otherPlist.keys().forEach(function(key) {
+            var value = otherPlist.valueForKey(key);
+            plist.setValueForKey(key, value);
+
+            if (key === "CPBundleName")
+                plist.setValueForKey("CFBundleName", value);
+
+            if (key === "CFBundleIconFile") {
+                var iconPath = rootPath.join("Resources", value);
+                if (iconPath.isFile())
+                    iconPath.copy(buildNative.join("Contents", "Resources", value));
+                else
+                    print("Warning: CFBundleIconFile references " + value + " but does not exist in the resources directory.");
+            }
+
+            if (key === "CFBundleExecutable") {
+                buildNative.join("Contents", "MacOS", "NativeHost").rename(value);
+                // FIXME:
+                FILE.chmod(buildNative.join("Contents", "MacOS", value), 0755);
+            }
+        });
+    }
+
+    CFPropertyList.modifyPlist(buildNative.join("Contents", "Info.plist"), function(plist) {
+
+        plist.setValueForKey("CFBundleName", defaultBundleName);
+        plist.setValueForKey("NHInitialResource", FILE.join(rootBaseName, options.index));
+
+        // merge Cappuccino plist
+        var cappPlistPath = rootPath.join("Info.plist");
+        if (cappPlistPath.isFile())
+            mergePlist(plist, cappPlistPath);
+
+        if (options.extraPlistPath)
+            mergePlist(plist, options.extraPlistPath);
+    });
+}
diff --git a/CommonJS/lib/cappuccino/objj-flatten-additions.js b/CommonJS/lib/cappuccino/objj-flatten-additions.js
index 2b4781e62..4c69f133e 100644
--- a/CommonJS/lib/cappuccino/objj-flatten-additions.js
+++ b/CommonJS/lib/cappuccino/objj-flatten-additions.js
@@ -1,6 +1,7 @@
 
 var URLCache = { };
 
+var CFHTTPRequest_open = CFHTTPRequest.prototype.open;
 CFHTTPRequest.prototype.open = function(/*String*/ method, /*String*/ url, /*Boolean*/ async, /*String*/ user, /*String*/ password)
 {
     var cachedRequest = CFHTTPRequest._lookupCachedRequest(url);
@@ -13,7 +14,7 @@ CFHTTPRequest.prototype.open = function(/*String*/ method, /*String*/ url, /*Boo
             ObjectiveJ.determineAndDispatchHTTPRequestEvents(self);
         };
     }
-    return this._nativeRequest.open(method, url, async, user, password);
+    return CFHTTPRequest_open.apply(this, arguments);
 }
 
 CFHTTPRequest._cacheRequest = function(/*CFURL|String*/ aURL, /*Number*/ status, /*Object*/ headers, /*String*/ body)
diff --git a/Foundation/CPArray+KVO.j b/Foundation/CPArray+KVO.j
index f0269b968..03274b748 100644
--- a/Foundation/CPArray+KVO.j
+++ b/Foundation/CPArray+KVO.j
@@ -23,6 +23,7 @@
 @import "CPArray.j"
 @import "CPNull.j"
 
+
 @implementation CPObject (CPArrayKVO)
 
 - (id)mutableArrayValueForKey:(id)aKey
@@ -33,12 +34,12 @@
 - (id)mutableArrayValueForKeyPath:(id)aKeyPath
 {
     var dotIndex = aKeyPath.indexOf(".");
-    
+
     if (dotIndex < 0)
         return [self mutableArrayValueForKey:aKeyPath];
 
     var firstPart = aKeyPath.substring(0, dotIndex),
-        lastPart = aKeyPath.substring(dotIndex+1);
+        lastPart = aKeyPath.substring(dotIndex + 1);
 
     return [[self valueForKeyPath:firstPart] valueForKeyPath:lastPart];
 }
@@ -49,34 +50,34 @@
 {
     id _proxyObject;
     id _key;
-    
+
     SEL         _insertSEL;
     Function    _insert;
-    
+
     SEL         _removeSEL;
     Function    _remove;
-    
+
     SEL         _replaceSEL;
     Function    _replace;
- 
+
     SEL         _insertManySEL;
     Function    _insertMany;
-    
+
     SEL         _removeManySEL;
     Function    _removeMany;
-    
+
     SEL         _replaceManySEL;
     Function    _replaceMany;
-   
+
     SEL         _objectAtIndexSEL;
     Function    _objectAtIndex;
-    
+
     SEL         _countSEL;
     Function    _count;
-    
+
     SEL         _accessSEL;
     Function    _access;
-    
+
     SEL         _setSEL;
     Function    _set;
 }
@@ -96,15 +97,15 @@
     return array;
 }
 
--(id)initWithKey:(id)aKey forProxyObject:(id)anObject
+- (id)initWithKey:(id)aKey forProxyObject:(id)anObject
 {
     self = [super init];
 
     _key = aKey;
     _proxyObject = anObject;
-    
+
     var capitalizedKey = _key.charAt(0).toUpperCase() + _key.substring(1);
-    
+
     _insertSEL = sel_getName(@"insertObject:in"+capitalizedKey+"AtIndex:");
     if ([_proxyObject respondsToSelector:_insertSEL])
         _insert = [_proxyObject methodForSelector:_insertSEL];
@@ -112,7 +113,7 @@
     _removeSEL = sel_getName(@"removeObjectFrom"+capitalizedKey+"AtIndex:");
     if ([_proxyObject respondsToSelector:_removeSEL])
         _remove = [_proxyObject methodForSelector:_removeSEL];
-        
+
     _replaceSEL = sel_getName(@"replaceObjectFrom"+capitalizedKey+"AtIndex:withObject:");
     if ([_proxyObject respondsToSelector:_replaceSEL])
         _replace = [_proxyObject methodForSelector:_replaceSEL];
@@ -124,11 +125,11 @@
     _removeManySEL = sel_getName(@"removeObjectsFrom"+capitalizedKey+"AtIndexes:");
     if ([_proxyObject respondsToSelector:_removeManySEL])
         _remove = [_proxyObject methodForSelector:_removeManySEL];
-        
+
     _replaceManySEL = sel_getName(@"replaceObjectsFrom"+capitalizedKey+"AtIndexes:withObjects:");
     if ([_proxyObject respondsToSelector:_replaceManySEL])
         _replace = [_proxyObject methodForSelector:_replaceManySEL];
-        
+
     _objectAtIndexSEL = sel_getName(@"objectIn"+capitalizedKey+"AtIndex:");
     if ([_proxyObject respondsToSelector:_objectAtIndexSEL])
         _objectAtIndex = [_proxyObject methodForSelector:_objectAtIndexSEL];
@@ -150,16 +151,17 @@
 
 - (id)copy
 {
-    var theCopy = [],
+    var i = 0,
+        theCopy = [],
         count = [self count];
 
-    for (var i=0; i= 0)
     {
         [self[index] addObserver:anObserver forKeyPath:aKeyPath options:options context:context];
@@ -468,7 +483,7 @@ kvoOperators["sum"] = function sumOperator(self, _cmd, param)
 - (void)removeObserver:(id)anObserver fromObjectsAtIndexes:(CPIndexSet)indexes forKeyPath:(CPString)aKeyPath
 {
     var index = [indexes firstIndex];
-    
+
     while (index >= 0)
     {
         [self[index] removeObserver:anObserver forKeyPath:aKeyPath];
@@ -477,7 +492,7 @@ kvoOperators["sum"] = function sumOperator(self, _cmd, param)
     }
 }
 
--(void)addObserver:(id)observer forKeyPath:(CPString)aKeyPath options:(unsigned)options context:(id)context
+- (void)addObserver:(id)observer forKeyPath:(CPString)aKeyPath options:(unsigned)options context:(id)context
 {
     if ([isa instanceMethodForSelector:_cmd] === [CPArray instanceMethodForSelector:_cmd])
         [CPException raise:CPInvalidArgumentException reason:"Unsupported method on CPArray"];
diff --git a/Foundation/CPArray.j b/Foundation/CPArray.j
index 81711e4d2..e10f8e8b0 100755
--- a/Foundation/CPArray.j
+++ b/Foundation/CPArray.j
@@ -20,11 +20,11 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+@import "CPEnumerator.j"
+@import "CPException.j"
 @import "CPObject.j"
 @import "CPRange.j"
-@import "CPEnumerator.j"
 @import "CPSortDescriptor.j"
-@import "CPException.j"
 
 
 /* @ignore */
@@ -37,7 +37,7 @@
 - (id)initWithArray:(CPArray)anArray
 {
     self = [super init];
-    
+
     if (self)
     {
         _array = anArray;
@@ -67,13 +67,13 @@
 - (id)initWithArray:(CPArray)anArray
 {
     self = [super init];
-    
+
     if (self)
     {
         _array = anArray;
         _index = [_array count];
     }
-    
+
     return self;
 }
 
@@ -87,7 +87,7 @@
 
 @end
 
-/*! 
+/*!
     @class CPArray
     @brief A mutable array backed by a JavaScript Array.
     @ingroup foundation
@@ -145,10 +145,10 @@
 {
     var i = 2,
         array = [[self alloc] init],
-        argument;
+        count = arguments.length;
 
-    for(; i < arguments.length && (argument = arguments[i]) != nil; ++i)
-        array.push(argument);
+    for (; i < count; ++i)
+        array.push(arguments[i]);
 
     return array;
 }
@@ -182,10 +182,10 @@
 - (id)initWithArray:(CPArray)anArray
 {
     self = [super init];
-    
+
     if (self)
         [self setArray:anArray];
-    
+
     return self;
 }
 
@@ -203,22 +203,22 @@
         return [self initWithArray:anArray];
 
     self = [super init];
-    
+
     if (self)
     {
         var index = 0,
             count = [anArray count];
-            
-        for(; index < count; ++index)
+
+        for (; index < count; ++index)
         {
             if (anArray[index].isa)
                 self[index] = [anArray[index] copy];
             // Do a deep/shallow copy?
             else
-                self[index] = anArray;
+                self[index] = anArray[index];
         }
     }
-    
+
     return self;
 }
 
@@ -229,12 +229,12 @@
 {
     // The arguments array contains self and _cmd, so the first object is at position 2.
     var i = 2,
-        argument;
-    
-    for(; i < arguments.length && (argument = arguments[i]) != nil; ++i)
-        push(argument);
+        count = arguments.length;
 
-    return self; 
+    for (; i < count; ++i)
+        push(arguments[i]);
+
+    return self;
 }
 
 /*!
@@ -246,12 +246,12 @@
 - (id)initWithObjects:(id)objects count:(unsigned)aCount
 {
     self = [super init];
-    
+
     if (self)
     {
         var index = 0;
-    
-        for(; index < aCount; ++index)
+
+        for (; index < aCount; ++index)
             push(objects[index]);
     }
 
@@ -278,97 +278,88 @@
 
 /*!
     Returns the index of \c anObject in this array.
-    If the object is \c nil or not in the array,
+    If the object is not in the array,
     returns \c CPNotFound. It first attempts to find
-    a match using \c -isEqual:, then \c ==.
+    a match using \c -isEqual:, then \c ===.
     @param anObject the object to search for
 */
 - (int)indexOfObject:(id)anObject
 {
-    if (anObject === nil)
-        return CPNotFound;
-    
-    var i = 0, 
+    var i = 0,
         count = length;
 
     // Only use -isEqual: if our object is a CPObject.
-    if (anObject.isa)
+    if (anObject && anObject.isa)
     {
-        for(; i < count; ++i)
-            if([self[i] isEqual:anObject])
+        for (; i < count; ++i)
+            if ([self[i] isEqual:anObject])
                 return i;
     }
-    // If indexOf exists, use it since it's probably 
+    // If indexOf exists, use it since it's probably
     // faster than anything we can implement.
     else if (self.indexOf)
         return indexOf(anObject);
     // Last resort, do a straight forward linear O(N) search.
     else
-        for(; i < count; ++i)
-            if(self[i] == anObject)
+        for (; i < count; ++i)
+            if (self[i] === anObject)
                 return i;
-    
+
     return CPNotFound;
 }
 
 /*!
     Returns the index of \c anObject in the array
     within \c aRange. It first attempts to find
-    a match using \c -isEqual:, then \c ==.
+    a match using \c -isEqual:, then \c ===.
     @param anObject the object to search for
     @param aRange the range to search within
     @return the index of the object, or \c CPNotFound if it was not found.
 */
 - (int)indexOfObject:(id)anObject inRange:(CPRange)aRange
 {
-    if (anObject === nil || anObject === undefined)
-        return CPNotFound;
-    
-    var i = aRange.location, 
+    var i = aRange.location,
         count = MIN(CPMaxRange(aRange), length);
-    
+
     // Only use isEqual: if our object is a CPObject.
-    if (anObject.isa)
+    if (anObject && anObject.isa)
     {
-        for(; i < count; ++i)
-            if([self[i] isEqual:anObject])
+        for (; i < count; ++i)
+            if ([self[i] isEqual:anObject])
                 return i;
     }
     // Last resort, do a straight forward linear O(N) search.
     else
-        for(; i < count; ++i)
-            if(self[i] == anObject)
+        for (; i < count; ++i)
+            if (self[i] === anObject)
                 return i;
-    
+
     return CPNotFound;
 }
 
 /*!
-    Returns the index of \c anObject in the array. The test for equality is done using only \c ==.
+    Returns the index of \c anObject in the array. The test for equality is done using only \c ===.
     @param anObject the object to search for
     @return the index of the object in the array. \c CPNotFound if the object is not in the array.
 */
 - (int)indexOfObjectIdenticalTo:(id)anObject
 {
-    if (anObject === nil)
-        return CPNotFound;
-    
-    // If indexOf exists, use it since it's probably 
+    // If indexOf exists, use it since it's probably
     // faster than anything we can implement.
     if (self.indexOf)
         return indexOf(anObject);
-    
+
     // Last resort, do a straight forward linear O(N) search.
     else
     {
-        var index = 0, 
+        var index = 0,
             count = length;
-        
-        for(; index < count; ++index)
-            if(self[index] === anObject)
+
+        for (; index < count; ++index)
+            if (self[index] === anObject)
                 return index;
     }
-    
+
     return CPNotFound;
 }
 
@@ -382,36 +373,33 @@
 */
 - (int)indexOfObjectIdenticalTo:(id)anObject inRange:(CPRange)aRange
 {
-    if (anObject === nil)
-        return CPNotFound;
-    
-    // If indexOf exists, use it since it's probably 
+    // If indexOf exists, use it since it's probably
     // faster than anything we can implement.
     if (self.indexOf)
     {
         var index = indexOf(anObject, aRange.location);
-        
+
         if (CPLocationInRange(index, aRange))
             return index;
     }
-    
+
     // Last resort, do a straight forward linear O(N) search.
     else
     {
-        var index = aRange.location, 
+        var index = aRange.location,
             count = MIN(CPMaxRange(aRange), length);
-        
-        for(; index < count; ++index)
-            if(self[index] == anObject)
+
+        for (; index < count; ++index)
+            if (self[index] == anObject)
                 return index;
     }
-    
+
     return CPNotFound;
 }
 
 /*!
     Returns the index of \c anObject in the array, which must be sorted in the same order as
-    calling sortUsingSelector: with the selector passed to this method would result in. 
+    calling sortUsingSelector: with the selector passed to this method would result in.
     @param anObject the object to search for
     @param aSelector the comparison selector to call on each item in the list, the same
     selector should have been used to sort the array (or to maintain its sorted order).
@@ -419,12 +407,12 @@
 */
 - (unsigned)indexOfObject:(id)anObject sortedBySelector:(SEL)aSelector
 {
-    return [self indexOfObject:anObject sortedByFunction: function(lhs, rhs) { objj_msgSend(lhs, aSelector, rhs); }];
+    return [self indexOfObject:anObject sortedByFunction:function(lhs, rhs) { objj_msgSend(lhs, aSelector, rhs); }];
 }
 
 /*!
     Returns the index of \c anObject in the array, which must be sorted in the same order as
-    calling sortUsingFunction: with the selector passed to this method would result in. 
+    calling sortUsingFunction: with the selector passed to this method would result in.
     The function will be called like so:
     
     aFunction(anObject, currentObjectInArrayForComparison)
@@ -441,7 +429,7 @@
 
 /*!
     Returns the index of \c anObject in the array, which must be sorted in the same order as
-    calling sortUsingFunction: with the selector passed to this method would result in. 
+    calling sortUsingFunction: with the selector passed to this method would result in.
     The function will be called like so:
     
     aFunction(anObject, currentObjectInArrayForComparison, context)
@@ -460,13 +448,17 @@
 
 - (unsigned)_indexOfObject:(id)anObject sortedByFunction:(Function)aFunction context:(id)aContext
 {
-    if (!aFunction || anObject === undefined)
+    if (!aFunction)
         return CPNotFound;
 
     if (length === 0)
         return -1;
 
-    var mid, c, first = 0, last = length - 1;
+    var mid,
+        c,
+        first = 0,
+        last = length - 1;
+
     while (first <= last)
     {
         mid = FLOOR((first + last) / 2);
@@ -478,19 +470,19 @@
             last = mid - 1;
         else
         {
-            while (mid < length - 1 && aFunction(anObject, self[mid+1], aContext) == CPOrderedSame)
+            while (mid < length - 1 && aFunction(anObject, self[mid + 1], aContext) == CPOrderedSame)
                 mid++;
 
             return mid;
         }
     }
 
-    return -first-1;
+    return -first - 1;
 }
 
 /*!
     Returns the index of \c anObject in the array, which must be sorted in the same order as
-    calling sortUsingDescriptors: with the descriptors passed to this method would result in. 
+    calling sortUsingDescriptors: with the descriptors passed to this method would result in.
     @param anObject the object to search for
     @param descriptors the array of descriptors to use to compare each item in the array that we search. the same
     descriptors should have been used to sort the array (or to maintain its sorted order).
@@ -498,14 +490,15 @@
 */
 - (unsigned)indexOfObject:(id)anObject sortedByDescriptors:(CPArray)descriptors
 {
+    var count = [descriptors count];
+
     return [self indexOfObject:anObject sortedByFunction:function(lhs, rhs)
     {
         var i = 0,
-            count = [descriptors count],
             result = CPOrderedSame;
 
         while (i < count)
-            if((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame)
+            if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame)
                 return result;
 
         return result;
@@ -539,9 +532,10 @@
 - (id)lastObject
 {
     var count = [self count];
-    
-    if (!count) return nil;
-    
+
+    if (!count)
+        return nil;
+
     return self[count - 1];
 }
 
@@ -567,7 +561,7 @@
     var index = CPNotFound,
         objects = [];
 
-    while((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound)
+    while ((index = [indexes indexGreaterThanIndex:index]) !== CPNotFound)
         [objects addObject:[self objectAtIndex:index]];
 
     return objects;
@@ -603,11 +597,11 @@
 {
     if (!aSelector)
         [CPException raise:CPInvalidArgumentException reason:"makeObjectsPerformSelector: 'aSelector' can't be nil"];
-    
-    var index = 0, 
+
+    var index = 0,
         count = length;
-        
-    for(; index < count; ++index)
+
+    for (; index < count; ++index)
         objj_msgSend(self[index], aSelector);
 }
 
@@ -622,10 +616,10 @@
     if (!aSelector)
         [CPException raise:CPInvalidArgumentException reason:"makeObjectsPerformSelector:withObject 'aSelector' can't be nil"];
 
-    var index = 0, 
+    var index = 0,
         count = length;
-        
-    for(; index < count; ++index) 
+
+    for (; index < count; ++index)
         objj_msgSend(self[index], aSelector, anObject);
 }
 
@@ -638,7 +632,7 @@
         count = length,
         argumentsArray = [nil, aSelector].concat(objects || []);
 
-    for(; index < count; ++index)
+    for (; index < count; ++index)
     {
         argumentsArray[0] = self[index];
         objj_msgSend.apply(this, argumentsArray);
@@ -656,12 +650,12 @@
 {
     if (![anArray count] || ![self count])
         return nil;
-    
+
     var i = 0,
         count = [self count];
 
-    for(; i < count; ++i)
-        if([anArray containsObject:self[i]])
+    for (; i < count; ++i)
+        if ([anArray containsObject:self[i]])
             return self[i];
 
     return nil;
@@ -677,20 +671,20 @@
 
     if (anArray === nil || length !== anArray.length)
         return NO;
-    
+
     var index = 0,
         count = [self count];
-    
-    for(; index < count; ++index)
+
+    for (; index < count; ++index)
     {
         var lhs = self[index],
             rhs = anArray[index];
-        
+
         // If they're not equal, and either doesn't have an isa, or they're !isEqual (not isEqual)
         if (lhs !== rhs && (lhs && !lhs.isa || rhs && !rhs.isa || ![lhs isEqual:rhs]))
             return NO;
     }
-        
+
     return YES;
 }
 
@@ -698,8 +692,8 @@
 {
     if (self === anObject)
         return YES;
-    
-    if(![anObject isKindOfClass:[CPArray class]])
+
+    if (![anObject isKindOfClass:[CPArray class]])
         return NO;
 
     return [self isEqualToArray:anObject];
@@ -714,14 +708,9 @@
 */
 - (CPArray)arrayByAddingObject:(id)anObject
 {
-    if (anObject === nil || anObject === undefined)
-        [CPException raise:CPInvalidArgumentException
-                    reason:"arrayByAddingObject: object can't be nil"];
-
     var array = [self copy];
-    
     array.push(anObject);
-    
+
     return array;
 }
 
@@ -734,17 +723,17 @@
     return slice(0).concat(anArray);
 }
 
-/*  
+/*
 - (CPArray)filteredArrayUsingPredicate:(CPPredicate)aPredicate
 {
-    var i= 0, 
+    var i= 0,
         count = [self count],
         array = [CPArray array];
-    
-    for(; i= [self count] + indexesCount)
-        [CPException raise:CPRangeException reason:"the last index (" + lastIndex + ") must be less than the sum of the original count (" + [self count] + ") and the insertion count (" + indexesCount + ")."];    
-    
+
+    if (lastIndex >= [self count] + indexesCount)
+        [CPException raise:CPRangeException reason:"the last index (" + lastIndex + ") must be less than the sum of the original count (" + [self count] + ") and the insertion count (" + indexesCount + ")."];
+
     var index = 0,
         currentIndex = [indexes firstIndex];
- 
+
     for (; index < objectsCount; ++index, currentIndex = [indexes indexGreaterThanIndex:currentIndex])
         [self insertObject:objects[index] atIndex:currentIndex];
 }
 
 - (unsigned)insertObject:(id)anObject inArraySortedByDescriptors:(CPArray)descriptors
 {
+    var count = [descriptors count];
+
     var index = [self _indexOfObject:anObject sortedByFunction:function(lhs, rhs)
     {
         var i = 0,
-            count = [descriptors count],
             result = CPOrderedSame;
 
         while (i < count)
@@ -1018,7 +1008,7 @@
     } context:nil];
 
     if (index < 0)
-        index = -index-1;
+        index = -index - 1;
 
     [self insertObject:anObject atIndex:index];
     return index;
@@ -1042,10 +1032,10 @@
 */
 - (void)replaceObjectsAtIndexes:(CPIndexSet)anIndexSet withObjects:(CPArray)objects
 {
-    var i = 0, 
+    var i = 0,
         index = [anIndexSet firstIndex];
-   
-    while(index != CPNotFound)
+
+    while (index != CPNotFound)
     {
         [self replaceObjectAtIndex:index withObject:objects[i++]];
         index = [anIndexSet indexGreaterThanIndex:index];
@@ -1086,8 +1076,9 @@
 */
 - (void)setArray:(CPArray)anArray
 {
-    if(self == anArray) return;
-    
+    if (self == anArray)
+        return;
+
     splice.apply(self, [0, length].concat(anArray));
 }
 
@@ -1125,7 +1116,7 @@
 - (void)removeObject:(id)anObject inRange:(CPRange)aRange
 {
     var index;
-    
+
     while ((index = [self indexOfObject:anObject inRange:aRange]) != CPNotFound)
     {
         [self removeObjectAtIndex:index];
@@ -1149,7 +1140,7 @@
 - (void)removeObjectsAtIndexes:(CPIndexSet)anIndexSet
 {
     var index = [anIndexSet lastIndex];
-   
+
     while (index != CPNotFound)
     {
         [self removeObjectAtIndex:index];
@@ -1178,7 +1169,7 @@
 {
     var index,
         count = [self count];
-    
+
     while ((index = [self indexOfObjectIdenticalTo:anObject inRange:aRange]) !== CPNotFound)
     {
         [self removeObjectAtIndex:index];
@@ -1194,7 +1185,7 @@
 {
     var index = 0,
         count = [anArray count];
-        
+
     for (; index < count; ++index)
         [self removeObject:anArray[index]];
 }
@@ -1223,16 +1214,17 @@
 
 - (CPArray)sortUsingDescriptors:(CPArray)descriptors
 {
+    var count = [descriptors count];
+
     sort(function(lhs, rhs)
     {
         var i = 0,
-            count = [descriptors count],
             result = CPOrderedSame;
-        
-        while(i < count)
-            if((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame)
+
+        while (i < count)
+            if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) != CPOrderedSame)
                 return result;
-        
+
         return result;
     });
 }
diff --git a/Foundation/CPAttributedString.j b/Foundation/CPAttributedString.j
index 82e7f93a7..f61b5c0e1 100644
--- a/Foundation/CPAttributedString.j
+++ b/Foundation/CPAttributedString.j
@@ -20,12 +20,13 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-@import "CPObject.j"
-@import "CPString.j"
 @import "CPDictionary.j"
+@import "CPObject.j"
 @import "CPRange.j"
+@import "CPString.j"
 
-/*! 
+
+/*!
     @class CPAttributedString
     @ingroup foundation
     @brief A mutable character string with attributes.
@@ -62,13 +63,13 @@
     Creates a new attributed string from an existing attributed string.
     @param aString is the attributed string to initialise from.
     @return a new CPAttributedString containing the string \c aString.
-*/ 
+*/
 - (id)initWithAttributedString:(CPAttributedString)aString
 {
     var string = [self initWithString:"" attributes:nil];
-    
+
     [string setAttributedString:aString];
-    
+
     return string;
 }
 
@@ -79,16 +80,19 @@
     @param attributes is a dictionary of string attributes.
     @return a new CPAttributedString containing the string \c aString
     with associated attributes, \c attributes.
-*/ 
+*/
 - (id)initWithString:(CPString)aString attributes:(CPDictionary)attributes
 {
     self = [super init];
-    
-    if (!attributes)
-        attributes = [CPDictionary dictionary];
 
-    _string = ""+aString;
-    _rangeEntries = [makeRangeEntry(CPMakeRange(0, _string.length), attributes)];
+    if (self)
+    {
+        if (!attributes)
+            attributes = [CPDictionary dictionary];
+
+        _string = ""+aString;
+        _rangeEntries = [makeRangeEntry(CPMakeRange(0, _string.length), attributes)];
+    }
 
     return self;
 }
@@ -98,7 +102,7 @@
     Returns a string containing the receiver's character data without
     attributes.
     @return a string of type CPString.
-*/ 
+*/
 - (CPString)string
 {
     return _string;
@@ -108,17 +112,17 @@
     Returns a string containing the receiver's character data without
     attributes.
     @return a string of type CPString.
-*/ 
+*/
 - (CPString)mutableString
 {
     return [self string];
-}   
+}
 
 /*!
     Get the length of the string.
     @return an unsigned integer equivalent to the number of characters in the
     string.
-*/ 
+*/
 - (unsigned)length
 {
     return _string.length;
@@ -149,7 +153,7 @@
 /*!
     Returns a dictionary of attributes for the character at a given index. The
     range in which this character resides in which the attributes are the
-    same, can be returned if desired. 
+    same, can be returned if desired.
     @note there is no guarantee that the range returned is in fact the complete
     range of the particular attributes. To ensure this use
     \c attributesAtIndex:longestEffectiveRange:inRange: instead. Note
@@ -160,10 +164,10 @@
     that is set (upon return) to the range over which the attributes are the
     same as those at index, \c anIndex. If not required pass
     \c nil.
-    @return a CPDictionary containing the attributes associated with the 
+    @return a CPDictionary containing the attributes associated with the
     character at index \c anIndex. Returns \c nil if index
     is out of bounds.
-*/ 
+*/
 - (CPDictionary)attributesAtIndex:(unsigned)anIndex effectiveRange:(CPRangePointer)aRange
 {
     //find the range entry that contains anIndex.
@@ -178,7 +182,7 @@
         aRange.location = matchingRange.range.location;
         aRange.length = matchingRange.range.length;
     }
-    
+
     return matchingRange.attributes;
 }
 
@@ -199,17 +203,17 @@
     to the range over which the attributes apply.
     @param rangeLimit a range limiting the search for the attributes' applicable
     range.
-    @return a CPDictionary containing the attributes associated with the 
+    @return a CPDictionary containing the attributes associated with the
     character at index \c anIndex. Returns \c nil if index
     is out of bounds.
-*/ 
+*/
 - (CPDictionary)attributesAtIndex:(unsigned)anIndex longestEffectiveRange:(CPRangePointer)aRange inRange:(CPRange)rangeLimit
-{    
+{
     var startingEntryIndex = [self _indexOfEntryWithIndex:anIndex];
 
     if (startingEntryIndex == CPNotFound)
         return nil;
-    
+
     if (!aRange)
         return _rangeEntries[startingEntryIndex].attributes;
 
@@ -217,7 +221,7 @@
     {
         aRange.location = rangeLimit.location;
         aRange.length = rangeLimit.length;
-        
+
         return _rangeEntries[startingEntryIndex].attributes;
     }
 
@@ -257,7 +261,7 @@
         else
             break;
     }
-        
+
     aRange.length = MIN(CPMaxRange(currentEntry.range), CPMaxRange(rangeLimit)) - aRange.location;
 
     return comparisonDict;
@@ -265,7 +269,7 @@
 
 /*!
     Returns the specified named attribute for the given character index and, if
-    required, the range over which the attribute applies. 
+    required, the range over which the attribute applies.
     @note there is no guarantee that the range returned is in fact the complete
     range of a particular attribute. To ensure this use
     \c -attribute:atIndex:longestEffectiveRange:inRange: instead but
@@ -278,7 +282,7 @@
     \c nil.
     @return the named attribute or \c nil is the attribute does not
     exist.
-*/ 
+*/
 - (id)attribute:(CPString)attribute atIndex:(unsigned)index effectiveRange:(CPRangePointer)aRange
 {
     if (!attribute)
@@ -320,7 +324,7 @@
 {
     //find the range entry that contains anIndex.
     var startingEntryIndex = [self _indexOfEntryWithIndex:anIndex];
-    
+
     if (startingEntryIndex == CPNotFound || !attribute)
         return nil;
 
@@ -331,7 +335,7 @@
     {
         aRange.location = rangeLimit.location;
         aRange.length = rangeLimit.length;
-        
+
         return [_rangeEntries[startingEntryIndex].attributes objectForKey:attribute];
     }
 
@@ -371,7 +375,7 @@
         else
             break;
     }
-        
+
     aRange.length = MIN(CPMaxRange(currentEntry.range), CPMaxRange(rangeLimit)) - aRange.location;
 
     return comparisonAttribute;
@@ -386,10 +390,10 @@
 */
 - (BOOL)isEqualToAttributedString:(CPAttributedString)aString
 {
-	if(!aString)
+	if (!aString)
 		return NO;
 
-	if(_string != [aString string])
+	if (_string != [aString string])
 		return NO;
 
     var myRange = CPMakeRange(),
@@ -407,7 +411,7 @@
         else
             comparisonAttributes = [aString attributesAtIndex:CPMaxRange(comparisonRange) effectiveRange:comparisonRange];
     }
-    
+
     return YES;
 }
 
@@ -422,10 +426,10 @@
 {
 	if (anObject == self)
 		return YES;
-		
+
 	if ([anObject isKindOfClass:[self class]])
 		return [self isEqualToAttributedString:anObject];
-		
+
 	return NO;
 }
 
@@ -440,7 +444,7 @@
 - (CPAttributedString)attributedSubstringFromRange:(CPRange)aRange
 {
     if (!aRange || CPMaxRange(aRange) > _string.length || aRange.location < 0)
-        [CPException raise:CPRangeException 
+        [CPException raise:CPRangeException
                     reason:"tried to get attributedSubstring for an invalid range: "+(aRange?CPStringFromRange(aRange):"nil")];
 
     var newString = [[CPAttributedString alloc] initWithString:_string.substring(aRange.location, CPMaxRange(aRange))],
@@ -449,7 +453,7 @@
         lastIndex = CPMaxRange(aRange);
 
     newString._rangeEntries = [];
-    
+
     while (currentRangeEntry && CPMaxRange(currentRangeEntry.range) < lastIndex)
     {
         var newEntry = copyRangeEntry(currentRangeEntry);
@@ -468,10 +472,10 @@
     if (currentRangeEntry)
     {
         var newRangeEntry = copyRangeEntry(currentRangeEntry);
-    
+
         newRangeEntry.range.length = CPMaxRange(aRange) - newRangeEntry.range.location;
         newRangeEntry.range.location -= aRange.location;
-        
+
         if (newRangeEntry.range.location < 0)
         {
             newRangeEntry.range.length += newRangeEntry.range.location;
@@ -480,12 +484,12 @@
 
         newString._rangeEntries.push(newRangeEntry);
     }
-    
+
     return newString;
 }
 
 //Changing Characters
-/*! 
+/*!
     Replaces the characters in the receiver with those of the specified string
     over the range, \c aRange. If the range has a length of 0 then
     the specified string is inserted at the range location. The new characters
@@ -505,10 +509,10 @@
 
     if (!aString)
         aString = "";
-        
+
     var startingIndex = [self _indexOfEntryWithIndex:aRange.location],
         startingRangeEntry = _rangeEntries[startingIndex],
-        endingIndex = [self _indexOfEntryWithIndex:MAX(CPMaxRange(aRange)-1, 0)],
+        endingIndex = [self _indexOfEntryWithIndex:MAX(CPMaxRange(aRange) - 1, 0)],
         endingRangeEntry = _rangeEntries[endingIndex],
         additionalLength = aString.length - aRange.length;
 
@@ -527,10 +531,10 @@
     }
 
     endingIndex = startingIndex + 1;
-    
+
     while(endingIndex < _rangeEntries.length)
         _rangeEntries[endingIndex++].range.location+=additionalLength;
-    
+
     [self endEditing];
 }
 
@@ -545,9 +549,9 @@
 
 //Changing Attributes
 /*!
-    Sets the attributes of the specified character range. 
+    Sets the attributes of the specified character range.
 
-    @note This process removes the attributes already associated with the 
+    @note This process removes the attributes already associated with the
     character range. If you wish to retain the current attributes use
     \c -addAttributes:range:.
     @param aDictionary a CPDictionary of attributes (names and values) to set
@@ -576,11 +580,11 @@
 }
 
 /*!
-    Add a collection of attributes to the specified character range. 
+    Add a collection of attributes to the specified character range.
 
     @note Attributes currently associated with the characters in the range are
     untouched. To remove all previous attributes when adding use
-    \c -setAttributes:range:.  
+    \c -setAttributes:range:.
     @param aDictionary a CPDictionary of attributes (names and values) to add.
     @param aRange a CPRange indicating the range of characters to add the
     attributes to.
@@ -600,7 +604,7 @@
     {
         var keys = [aDictionary allKeys],
             count = [keys count];
-            
+
         while (count--)
             [_rangeEntries[current].attributes setObject:[aDictionary objectForKey:keys[count]] forKey:keys[count]];
 
@@ -703,8 +707,8 @@
     {
         var entryCopy = copyRangeEntry(otherRangeEntries[index++]);
         entryCopy.range.location += anIndex;
-        
-        _rangeEntries.splice(entryIndexOfNextEntry-1+index, 0, entryCopy);
+
+        _rangeEntries.splice(entryIndexOfNextEntry - 1 + index, 0, entryCopy);
     }
 
     //necessary?
@@ -724,10 +728,10 @@
 - (void)replaceCharactersInRange:(CPRange)aRange withAttributedString:(CPAttributedString)aString
 {
     [self beginEditing];
-        
+
     [self deleteCharactersInRange:aRange];
     [self insertAttributedString:aString atIndex:aRange.location];
-    
+
     [self endEditing];
 }
 
@@ -739,11 +743,14 @@
 - (void)setAttributedString:(CPAttributedString)aString
 {
     [self beginEditing];
-    
+
     _string = aString._string;
     _rangeEntries = [];
-    
-    for (var i=0, count = aString._rangeEntries.length; i= _rangeEntries.length)
-        end = _rangeEntries.length -1;
+        end = _rangeEntries.length - 1;
 
     while (current < end)
     {
         var a = _rangeEntries[current],
-            b = _rangeEntries[current+1];
+            b = _rangeEntries[current + 1];
 
         if ([a.attributes isEqualToDictionary:b.attributes])
         {
             a.range.length = CPMaxRange(b.range) - a.range.location;
-            _rangeEntries.splice(current+1, 1);
+            _rangeEntries.splice(current + 1, 1);
             end--;
         }
         else
@@ -821,7 +828,8 @@
     CPAttributedString already implements mutable methods and
     this class only exists for source compatability.
 */
-@implementation CPMutableAttributedString : CPAttributedString {}
+@implementation CPMutableAttributedString : CPAttributedString
+
 @end
 
 var isEqual = function isEqual(a, b)
@@ -849,11 +857,11 @@ var splitRangeEntry = function splitRangeEntryAtIndex(/*RangeEntry*/aRangeEntry,
 {
     var newRangeEntry = copyRangeEntry(aRangeEntry),
         cachedIndex = CPMaxRange(aRangeEntry.range);
-    
+
     aRangeEntry.range.length = anIndex - aRangeEntry.range.location;
     newRangeEntry.range.location = anIndex;
     newRangeEntry.range.length = cachedIndex - anIndex;
     newRangeEntry.attributes = [newRangeEntry.attributes copy];
-    
+
     return [aRangeEntry, newRangeEntry];
 }
diff --git a/Foundation/CPBundle.j b/Foundation/CPBundle.j
index bef8fdb69..39aae0b29 100644
--- a/Foundation/CPBundle.j
+++ b/Foundation/CPBundle.j
@@ -20,8 +20,9 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-@import "CPObject.j"
 @import "CPDictionary.j"
+@import "CPObject.j"
+
 
 /*!
     @class CPBundle
diff --git a/Foundation/CPCoder.j b/Foundation/CPCoder.j
index a9cd1bf97..0d8bc04da 100644
--- a/Foundation/CPCoder.j
+++ b/Foundation/CPCoder.j
@@ -20,28 +20,27 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-@import "CPObject.j"
 @import "CPException.j"
+@import "CPObject.j"
 
 
-/*! 
+/*!
     @class CPCoder
     @ingroup foundation
     @brief Defines methods for use when archiving & restoring (enc/decoding).
 
-    Top-level class defining methods for use when archiving (encoding) objects to a byte array 
-    or file, and when restoring (decoding) objects. 
+    Top-level class defining methods for use when archiving (encoding) objects to a byte array
+    or file, and when restoring (decoding) objects.
 */
 @implementation CPCoder : CPObject
 {
-
 }
 
 /*!
     Returns a flag indicating whether the receiver supports keyed coding. The default implementation returns
     \c NO. Subclasses supporting keyed coding must override this to return \c YES.
 */
--(BOOL)allowsKeyedCoding
+- (BOOL)allowsKeyedCoding
 {
    return NO;
 }
@@ -53,7 +52,7 @@
     @param aType the structure or object type
     @param anObject the object to be encoded
 */
--(void)encodeValueOfObjCType:(CPString)aType at:(id)anObject
+- (void)encodeValueOfObjCType:(CPString)aType at:(id)anObject
 {
    CPInvalidAbstractInvocation();
 }
@@ -62,7 +61,7 @@
     Encodes a data object. Subclasses must override this method.
     @param aData the object to be encoded.
 */
--(void)encodeDataObject:(CPData)aData
+- (void)encodeDataObject:(CPData)aData
 {
    CPInvalidAbstractInvocation();
 }
@@ -71,7 +70,7 @@
     Encodes an object. Subclasses must override this method.
     @param anObject the object to be encoded
 */
--(void)encodeObject:(id)anObject
+- (void)encodeObject:(id)anObject
 {
 //   [self encodeValueOfObjCType:@encode(id) at:object];
 }
@@ -110,7 +109,7 @@
     Encodes a property list. Not yet implemented.
     @param aPropertyList the property list to be encoded
 */
--(void)encodePropertyList:(id)aPropertyList
+- (void)encodePropertyList:(id)aPropertyList
 {
 //   [self encodeValueOfObjCType:@encode(id) at:&propertyList];
 }
@@ -119,7 +118,7 @@
     Encodes the root object of a group of Obj-J objects.
     @param rootObject the root object to be encoded.
 */
--(void)encodeRootObject:(id)anObject
+- (void)encodeRootObject:(id)anObject
 {
    [self encodeObject:anObject];
 }
@@ -128,18 +127,18 @@
     Encodes an object.
     @param anObject the object to be encoded.
 */
--(void)encodeBycopyObject:(id)anObject
+- (void)encodeBycopyObject:(id)anObject
 {
-   [self encodeObject:object];
+   [self encodeObject:anObject];
 }
 
 /*!
     Encodes an object.
     @param anObject the object to be encoded.
 */
--(void)encodeConditionalObject:(id)anObject
+- (void)encodeConditionalObject:(id)anObject
 {
-   [self encodeObject:object];
+   [self encodeObject:anObject];
 }
 
 @end
diff --git a/Foundation/CPCountedSet.j b/Foundation/CPCountedSet.j
index a99b94a98..86b4753af 100644
--- a/Foundation/CPCountedSet.j
+++ b/Foundation/CPCountedSet.j
@@ -22,7 +22,8 @@
 
 @import "CPSet.j"
 
-/*! 
+
+/*!
     @class CPCountedSet
     @ingroup foundation
     @brief An mutable collection which may contain a specific object
@@ -37,11 +38,11 @@
 {
     if (!_counts)
         _counts = {};
-    
+
     [super addObject:anObject];
-    
+
     var UID = [anObject UID];
-    
+
     if (_counts[UID] === undefined)
         _counts[UID] = 1;
     else
@@ -52,16 +53,16 @@
 {
     if (!_counts)
         return;
-        
+
     var UID = [anObject UID];
-    
+
     if (_counts[UID] === undefined)
         return;
-    
+
     else
     {
         --_counts[UID];
-        
+
         if (_counts[UID] === 0)
         {
             delete _counts[UID];
@@ -84,17 +85,17 @@
 {
     if (!_counts)
         _counts = {};
-    
+
     var UID = [anObject UID];
-    
+
     if (_counts[UID] === undefined)
         return 0;
-    
+
     return _counts[UID];
 }
 
 
-/* 
+/*
 
 Eventually we should see what these are supposed to do, and then do that.
 
diff --git a/Foundation/CPData.j b/Foundation/CPData.j
index 2d73b5a42..0a75abd36 100644
--- a/Foundation/CPData.j
+++ b/Foundation/CPData.j
@@ -23,6 +23,7 @@
 @import "CPObject.j"
 @import "CPString.j"
 
+
 /*!
     @class CPData
     @ingroup foundation
diff --git a/Foundation/CPDate.j b/Foundation/CPDate.j
index e4bc13273..6340ef48c 100644
--- a/Foundation/CPDate.j
+++ b/Foundation/CPDate.j
@@ -24,7 +24,7 @@
 @import "CPString.j"
 
 
-var CPDateReferenceDate = new Date(Date.UTC(2001,1,1,0,0,0,0));
+var CPDateReferenceDate = new Date(Date.UTC(2001, 1, 1, 0, 0, 0, 0));
 
 /*!
     @class CPDate
@@ -64,12 +64,12 @@ var CPDateReferenceDate = new Date(Date.UTC(2001,1,1,0,0,0,0));
 
 + (id)distantPast
 {
-    return new Date(-10000,1,1,0,0,0,0);
+    return new Date(-10000, 1, 1, 0, 0, 0, 0);
 }
 
 + (id)distantFuture
 {
-    return new Date(10000,1,1,0,0,0,0);
+    return new Date(10000, 1, 1, 0, 0, 0, 0);
 }
 
 - (id)initWithTimeIntervalSinceNow:(CPTimeInterval)seconds
@@ -110,7 +110,7 @@ var CPDateReferenceDate = new Date(Date.UTC(2001,1,1,0,0,0,0));
         [CPException raise:CPInvalidArgumentException
                     reason:"initWithString: the string must be of YYYY-MM-DD HH:MM:SS ±HHMM format"];
 
-    var date = new Date(d[1], d[2]-1, d[3]),
+    var date = new Date(d[1], d[2] - 1, d[3]),
         timeZoneOffset =  (Number(d[8]) * 60 + Number(d[9])) * (d[7] === '-' ? -1 : 1);
 
     date.setHours(d[4]);
@@ -180,7 +180,7 @@ var CPDateReferenceDate = new Date(Date.UTC(2001,1,1,0,0,0,0));
     var hours = Math.floor(self.getTimezoneOffset() / 60),
         minutes = self.getTimezoneOffset() - hours * 60;
 
-    return [CPString stringWithFormat:@"%04d-%02d-%02d %02d:%02d:%02d +%02d%02d", self.getFullYear(), self.getMonth()+1, self.getDate(), self.getHours(), self.getMinutes(), self.getSeconds(), hours, minutes];
+    return [CPString stringWithFormat:@"%04d-%02d-%02d %02d:%02d:%02d +%02d%02d", self.getFullYear(), self.getMonth() + 1, self.getDate(), self.getHours(), self.getMinutes(), self.getSeconds(), hours, minutes];
 }
 
 - (id)copy
diff --git a/Foundation/CPDictionary.j b/Foundation/CPDictionary.j
index f805282f2..4fd2d46d8 100755
--- a/Foundation/CPDictionary.j
+++ b/Foundation/CPDictionary.j
@@ -21,9 +21,9 @@
  */
 
 @import "CPArray.j"
-@import "CPObject.j"
 @import "CPEnumerator.j"
 @import "CPException.j"
+@import "CPObject.j"
 
 
 /* @ignore */
@@ -36,20 +36,20 @@
 - (id)initWithDictionary:(CPDictionary)aDictionary
 {
     self = [super init];
-    
+
     if (self)
     {
         _keyEnumerator = [aDictionary keyEnumerator];
         _dictionary = aDictionary;
     }
-    
+
     return self;
 }
 
 - (id)nextObject
 {
     var key = [_keyEnumerator nextObject];
-    
+
     if (!key)
         return nil;
 
@@ -58,7 +58,7 @@
 
 @end
 
-/*! 
+/*!
     @class CPDictionary
     @ingroup foundation
     @brief A mutable key-value pair collection.
@@ -145,29 +145,33 @@
 */
 + (id)dictionaryWithJSObject:(JSObject)object recursively:(BOOL)recursively
 {
-    var dictionary = [[self alloc] init];
-        
-    for (var key in object)
+    var key = "",
+        dictionary = [[self alloc] init];
+
+    for (key in object)
     {
         if (!object.hasOwnProperty(key))
             continue;
 
         var value = object[key];
-        
+
         if (value === null)
         {
             [dictionary setObject:[CPNull null] forKey:key];
             continue;
         }
-    
+
         if (recursively)
         {
             if (value.constructor === Object)
                 value = [CPDictionary dictionaryWithJSObject:value recursively:YES];
             else if ([value isKindOfClass:CPArray])
             {
-                var newValue = [];
-                for (var i = 0, count = value.length; i < count; i++)
+                var newValue = [],
+                    i = 0,
+                    count = value.length;
+
+                for (; i < count; i++)
                 {
                     var thisValue = value[i];
 
@@ -183,7 +187,7 @@
 
         [dictionary setObject:value forKey:key];
     }
-    
+
     return dictionary;
 }
 
@@ -193,22 +197,22 @@
     @param ... key for the first object and ongoing value-key pairs for more objects.
     @throws CPInvalidArgumentException if the number of objects and keys is different
     @return the new CPDictionary
-    
+
     Assuming that there's no object retaining in Cappuccino, you can create
     dictionaries same way as with alloc and initWithObjectsAndKeys:
     var dict = [CPDictionary dictionaryWithObjectsAndKeys:
     @"value1", @"key1",
     @"value2", @"key2"];
-    
+
     Note, that there's no final nil like in Objective-C/Cocoa.
-    
+
     @see [CPDictionary initWithObjectsAndKeys:]
 */
 + (id)dictionaryWithObjectsAndKeys:(id)firstObject, ...
 {
     arguments[0] = [self alloc];
     arguments[1] = @selector(initWithObjectsAndKeys:);
-    
+
     return objj_msgSend.apply(this, arguments);
 }
 
@@ -221,10 +225,10 @@
 {
     var key = "",
         dictionary = [[CPDictionary alloc] init];
-    
+
     for (key in aDictionary._buckets)
         [dictionary setObject:[aDictionary objectForKey:key] forKey:key];
-        
+
     return dictionary;
 }
 
@@ -240,16 +244,16 @@
     self = [super init];
 
     if ([objects count] != [keyArray count])
-        [CPException raise:CPInvalidArgumentException reason:"Counts are different.("+[objects count]+"!="+[keyArray count]+")"];
+        [CPException raise:CPInvalidArgumentException reason:"Counts are different.(" + [objects count] + "!=" + [keyArray count] + ")"];
 
     if (self)
     {
         var i = [keyArray count];
-        
+
         while (i--)
             [self setObject:objects[i] forKey:keyArray[i]];
     }
-    
+
     return self;
 }
 
@@ -259,29 +263,29 @@
     @param ... key for the first object and ongoing value-key pairs for more objects.
     @throws CPInvalidArgumentException if the number of objects and keys is different
     @return the new CPDictionary
-    
+
     You can create dictionaries this way:
     var dict = [[CPDictionary alloc] initWithObjectsAndKeys:
     @"value1", @"key1",
     @"value2", @"key2"];
-    
+
     Note, that there's no final nil like in Objective-C/Cocoa.
 */
 - (id)initWithObjectsAndKeys:(id)firstObject, ...
 {
     var argCount = arguments.length;
-    
+
     if (argCount % 2 !== 0)
         [CPException raise:CPInvalidArgumentException reason:"Key-value count is mismatched. (" + argCount + " arguments passed)"];
 
     self = [super init];
-    
+
     if (self)
     {
         // The arguments array contains self and _cmd, so the first object is at position 2.
         var index = 2;
-        
-        for(; index < argCount; index += 2)
+
+        for (; index < argCount; index += 2)
         {
             var value = arguments[index];
 
@@ -316,7 +320,7 @@
 */
 - (CPArray)allKeys
 {
-    return _keys;
+    return [_keys copy];
 }
 
 /*!
@@ -326,7 +330,7 @@
 {
     var index = _keys.length,
         values = [];
-        
+
     while (index--)
         values.push(self.valueForKey(_keys[index]));
 
@@ -350,7 +354,7 @@
 }
 
 /*!
-    Compare the receiver to this dictionary, and return whether or not they are equal. 
+    Compare the receiver to this dictionary, and return whether or not they are equal.
 */
 - (BOOL)isEqualToDictionary:(CPDictionary)aDictionary
 {
@@ -372,10 +376,10 @@
 
         if (lhsObject === rhsObject)
             continue;
-            
+
         if (lhsObject && lhsObject.isa && rhsObject && rhsObject.isa && [lhsObject respondsToSelector:@selector(isEqual:)] && [lhsObject isEqual:rhsObject])
             continue;
-        
+
         return NO;
     }
 
@@ -399,9 +403,9 @@
         var i= 0,
             keys= CPArray.array(),
             count= this.count();
-        
-        while((i= this._objects.indexOfObjectInRage(0, count-i))!=CPNotFound) keys.addObject(this._keys[i]);
-        
+
+        while ((i= this._objects.indexOfObjectInRage(0, count-i))!=CPNotFound) keys.addObject(this._keys[i]);
+
         return keys;
     }
 
@@ -412,7 +416,7 @@
             {
                 return aSelector.apply(dictionary.objectForKey(this), [dictionary.objectForKey(rhs)]);
             };
-        
+
         return this._keys.sortedArrayUsingSelector(objectSelector);
     }
 */
@@ -424,7 +428,7 @@
 - (id)objectForKey:(CPString)aKey
 {
     var object = _buckets[aKey];
-    
+
     return (object === undefined) ? nil : object;
 }
 /*
@@ -432,20 +436,20 @@
     {
         var i= keys.length,
             objects= CPArray.array();
-        
-        while(i--)
+
+        while (i--)
         {
             var object= this.objectForKey(keys[i]);
             objects.addObject(object==nil?aNotFoundMarker:object);
         }
-        
+
         return objects;
     }
-    
+
     Instance.valueForKey(aKey)
     {
-        if(aKey.length && aKey[0]=="@") return this.objectForKey(aKey.substr(1));
-        
+        if (aKey.length && aKey[0]=="@") return this.objectForKey(aKey.substr(1));
+
         return base.valueForKey(aKey);
     }
 */
@@ -475,7 +479,7 @@
     var index = keysForRemoval.length;
 
     while (index--)
-        self.removeValueForKey(keysForRemoval[index]);
+        [self removeObjectForKey:keysForRemoval[index]];
 }
 
 /*
@@ -483,11 +487,11 @@
     {
         this._keys= CPArray.arrayWithArray(aDictionary.allKeys());
         this._objects= CPArray.arrayWithArray(aDictionary.allValues());
-        
+
         this._dictionary= { };
-        
+
         var i= this._keys.count();
-        while(i--) this._dictionary[this._keys[i]]= { object: this._objects[i], index: i };
+        while (i--) this._dictionary[this._keys[i]]= { object: this._objects[i], index: i };
     }
 */
 /*!
@@ -507,10 +511,10 @@
 {
     if (!aDictionary)
         return;
-        
+
     var keys = [aDictionary allKeys],
         index = [keys count];
-    
+
     while (index--)
     {
         var key = keys[index];
diff --git a/Foundation/CPEnumerator.j b/Foundation/CPEnumerator.j
index 51d25b92a..50efcb571 100755
--- a/Foundation/CPEnumerator.j
+++ b/Foundation/CPEnumerator.j
@@ -22,15 +22,12 @@
 
 @import "CPObject.j"
 
-/*! 
-    
-*/
 
-/*! 
+/*!
     @class CPEnumerator
     @ingroup foundation
     @brief Defines an interface for enumerators.
-    
+
     CPEnumerator is a superclass (with useless method bodies)
     that defines an interface for subclasses to follow. The purpose of an
     enumerator is to be a convenient system for traversing over the elements
diff --git a/Foundation/CPException.j b/Foundation/CPException.j
index 28abb60ec..770c6ad63 100755
--- a/Foundation/CPException.j
+++ b/Foundation/CPException.j
@@ -30,7 +30,7 @@ CPUnsupportedMethodException        = "CPUnsupportedMethodException";
 CPRangeException                    = "CPRangeException";
 CPInternalInconsistencyException    = "CPInternalInconsistencyException";
 
-/*! 
+/*!
     @class CPException
     @ingroup foundation
     @brief Used to implement exception handling (creating & raising).
@@ -40,7 +40,7 @@ CPInternalInconsistencyException    = "CPInternalInconsistencyException";
 // some code here...
 if (input == nil)
     [CPException raise:"MyException" reason:"You didn't do something right."];
-    
+
 // code that gets executed if no exception was raised
 
*/ @@ -96,7 +96,7 @@ if (input == nil) message = aReason; _userInfo = aUserInfo; } - + return self; } @@ -165,14 +165,14 @@ var CPExceptionNameKey = "CPExceptionNameKey", - (id)initWithCoder:(CPCoder)aCoder { self = [super init]; - + if (self) { name = [aCoder decodeObjectForKey:CPExceptionNameKey]; message = [aCoder decodeObjectForKey:CPExceptionReasonKey]; _userInfo = [aCoder decodeObjectForKey:CPExceptionUserInfoKey]; } - + return self; } diff --git a/Foundation/CPFunctionOperation.j b/Foundation/CPFunctionOperation.j index 795058737..f9f98c911 100644 --- a/Foundation/CPFunctionOperation.j +++ b/Foundation/CPFunctionOperation.j @@ -23,22 +23,24 @@ @import "CPOperation.j" -/*! +/*! @class CPFunctionOperation @brief Represents an operation using a JavaScript function that can be run in an CPOperationQueue */ -@implementation CPFunctionOperation : CPOperation +@implementation CPFunctionOperation : CPOperation { CPArray _functions; } -- (void)main +- (void)main { - if (_functions && [_functions count] > 0) + if (_functions && [_functions count] > 0) { - var i = 0; - for (i = 0; i < [_functions count]; i++) + var i = 0, + count = [_functions count]; + + for (; i < count; i++) { var func = [_functions objectAtIndex:i]; func(); @@ -46,9 +48,9 @@ } } -- (id)init +- (id)init { - if (self = [super init]) + if (self = [super init]) { _functions = []; } @@ -58,7 +60,7 @@ /*! Adds the specified JS function to the receiver’s list of functions to perform. */ -- (void)addExecutionFunction:(JSObject)jsFunction +- (void)addExecutionFunction:(JSObject)jsFunction { [_functions addObject:jsFunction]; } @@ -66,7 +68,7 @@ /*! Returns an array containing the functions associated with the receiver. */ -- (CPArray)executionFunctions +- (CPArray)executionFunctions { return _functions; } @@ -74,12 +76,12 @@ /*! Creates and returns an NSFunctionOperation object and adds the specified function to it. */ -+ (id)functionOperationWithFunction:(JSObject)jsFunction ++ (id)functionOperationWithFunction:(JSObject)jsFunction { functionOp = [[CPFunctionOperation alloc] init]; [functionOp addExecutionFunction:jsFunction]; - + return functionOp; } -@end \ No newline at end of file +@end diff --git a/Foundation/CPIndexSet.j b/Foundation/CPIndexSet.j index 60e6736e9..266c7273e 100644 --- a/Foundation/CPIndexSet.j +++ b/Foundation/CPIndexSet.j @@ -20,14 +20,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import "CPRange.j" @import "CPObject.j" +@import "CPRange.j" + #define _CPMaxRange(aRange) ((aRange).location + (aRange).length) #define _CPMakeRange(aLocation, aLength) { location:(aLocation), length:aLength } #define _CPMakeRangeCopy(aRange) { location:(aRange).location, length:(aRange).length } -/*! +/*! @class CPIndexSet @ingroup foundation @brief A collection of unique integers. @@ -179,7 +180,7 @@ return NO; // If we have less total indexes than aRange, we can't possibly contain aRange. - if(_count < aRange.length) + if (_count < aRange.length) return NO; // Search for first location @@ -203,7 +204,7 @@ { var otherCount = anIndexSet._count; - if(otherCount <= 0) + if (otherCount <= 0) return YES; // If we have less total indexes than anIndexSet, we can't possibly contain aRange. @@ -692,7 +693,7 @@ var i = _ranges.length - 1, shifted = CPMakeRange(CPNotFound, 0); - for(; i >= 0; --i) + for (; i >= 0; --i) { var range = _ranges[i], maximum = CPMaxRange(range); @@ -700,7 +701,7 @@ if (anIndex > maximum) break; - // If our index is within our range, but not the first index, + // If our index is within our range, but not the first index, // then this range will be split. if (anIndex > range.location && anIndex < maximum) { diff --git a/Foundation/CPInvocation.j b/Foundation/CPInvocation.j index 19e8c2cb1..da66a2a68 100644 --- a/Foundation/CPInvocation.j +++ b/Foundation/CPInvocation.j @@ -20,11 +20,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import "CPObject.j" @import "CPException.j" +@import "CPObject.j" -/*! +/*! @class CPInvocation @ingroup foundation @brief An object representation of a message. @@ -57,13 +57,13 @@ - (id)initWithMethodSignature:(CPMethodSignature)aMethodSignature { self = [super init]; - + if (self) { _arguments = []; _methodSignature = aMethodSignature; } - + return self; } @@ -174,13 +174,13 @@ var CPInvocationArguments = @"CPInvocationArguments", - (id)initWithCoder:(CPCoder)aCoder { self = [super init]; - + if (self) { _returnValue = [aCoder decodeObjectForKey:CPInvocationReturnValue]; _arguments = [aCoder decodeObjectForKey:CPInvocationArguments]; } - + return self; } diff --git a/Foundation/CPInvocationOperation.j b/Foundation/CPInvocationOperation.j index e98fc6c49..0c1bdfda6 100644 --- a/Foundation/CPInvocationOperation.j +++ b/Foundation/CPInvocationOperation.j @@ -19,32 +19,32 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import -@import +@import "CPInvocation.j" +@import "CPObject.j" @import "CPOperation.j" -/*! +/*! @class CPInvocationOperation @brief Represents an operation using an invocation that can be run in an CPOperationQueue */ -@implementation CPInvocationOperation : CPOperation +@implementation CPInvocationOperation : CPOperation { CPInvocation _invocation; } -- (void)main +- (void)main { - if (_invocation) + if (_invocation) { [_invocation invoke]; } } -- (id)init +- (id)init { - if (self = [super init]) + if (self = [super init]) { _invocation = nil; } @@ -55,13 +55,13 @@ Returns a CPInvocationOperation object initialized with the specified invocation object. @param inv the invocation */ -- (id)initWithInvocation:(CPInvocation)inv +- (id)initWithInvocation:(CPInvocation)inv { - if (self = [self init]) + if (self = [self init]) { _invocation = inv; } - + return self; } @@ -71,20 +71,20 @@ @param sel the selector that should be called on the target @param arg the arguments */ -- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg +- (id)initWithTarget:(id)target selector:(SEL)sel object:(id)arg { var inv = [[CPInvocation alloc] initWithMethodSignature:nil]; [inv setTarget:target]; [inv setSelector:sel]; [inv setArgument:arg atIndex:2]; - + return [self initWithInvocation:inv]; } /*! Returns the receiver’s invocation object. */ -- (CPInvocation)invocation +- (CPInvocation)invocation { return _invocation; } @@ -92,16 +92,14 @@ /*! Returns the result of the invocation or method. */ -- (id)result +- (id)result { - if ([self isFinished] && _invocation) + if ([self isFinished] && _invocation) { return [_invocation returnValue]; } - + return nil; } - - -@end \ No newline at end of file +@end diff --git a/Foundation/CPJSONPConnection.j b/Foundation/CPJSONPConnection.j index 8ec76e0e7..c95a362c5 100644 --- a/Foundation/CPJSONPConnection.j +++ b/Foundation/CPJSONPConnection.j @@ -19,15 +19,15 @@ * 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 "CPObject.j" CPJSONPConnectionCallbacks = {}; CPJSONPCallbackReplacementString = @"${JSONP_CALLBACK}"; -/*! +/*! @ingroup foundation @brief Allows cross domain connections using JSONP protocol. @@ -43,23 +43,23 @@ CPJSONPCallbackReplacementString = @"${JSONP_CALLBACK}"; { CPURLRequest _request; id _delegate; - + CPString _callbackParameter; DOMElement _scriptTag; } /*! @deprecated */ -+ (CPJSONPConnection)sendRequest:(CPURLRequest)aRequest callback:(CPString)callbackParameter delegate:(id)aDelegate ++ (CPJSONPConnection)sendRequest:(CPURLRequest)aRequest callback:(CPString)callbackParameter delegate:(id)aDelegate { return [self connectionWithRequest:aRequest callback:callbackParameter delegate:aDelegate]; } + (CPJSONPConnection)connectionWithRequest:(CPURLRequest)aRequest callback:(CPString)callbackParameter delegate:(id)aDelegate { - return [[[self class] alloc] initWithRequest:aRequest callback:callbackParameter delegate:aDelegate startImmediately:YES];; + return [[[self class] alloc] initWithRequest:aRequest callback:callbackParameter delegate:aDelegate startImmediately:YES]; } -- (id)initWithRequest:(CPURLRequest)aRequest callback:(CPString)aString delegate:(id)aDelegate +- (id)initWithRequest:(CPURLRequest)aRequest callback:(CPString)aString delegate:(id)aDelegate { return [self initWithRequest:aRequest callback:aString delegate:aDelegate startImmediately: NO]; } @@ -67,18 +67,18 @@ CPJSONPCallbackReplacementString = @"${JSONP_CALLBACK}"; - (id)initWithRequest:(CPURLRequest)aRequest callback:(CPString)aString delegate:(id)aDelegate startImmediately:(BOOL)shouldStartImmediately { self = [super init]; - + _request = aRequest; _delegate = aDelegate; - + _callbackParameter = aString; - + if (!_callbackParameter && [[_request URL] absoluteString].indexOf(CPJSONPCallbackReplacementString) < 0) [CPException raise:CPInvalidArgumentException reason:@"JSONP source specified without callback parameter or CPJSONPCallbackReplacementString in URL."]; - if(shouldStartImmediately) + if (shouldStartImmediately) [self start]; - + return self; } @@ -88,9 +88,14 @@ CPJSONPCallbackReplacementString = @"${JSONP_CALLBACK}"; { CPJSONPConnectionCallbacks["callback"+[self UID]] = function(data) { - [_delegate connection:self didReceiveData:data]; + if ([_delegate respondsToSelector:@selector(connection:didReceiveData:)]) + [_delegate connection:self didReceiveData:data]; + + if ([_delegate respondsToSelector:@selector(connectionDidFinishLoading:)]) + [_delegate connectionDidFinishLoading:self]; + [self removeScriptTag]; - + [[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; }; @@ -108,17 +113,19 @@ CPJSONPCallbackReplacementString = @"${JSONP_CALLBACK}"; } else return; - + _scriptTag = document.createElement("script"); _scriptTag.setAttribute("type", "text/javascript"); _scriptTag.setAttribute("charset", "utf-8"); _scriptTag.setAttribute("src", source); - + head.appendChild(_scriptTag); } catch (exception) { - [_delegate connection: self didFailWithError: exception]; + if ([_delegate respondsToSelector:@selector(connection:didFailWithError:)]) + [_delegate connection: self didFailWithError: exception]; + [self removeScriptTag]; } } @@ -126,12 +133,12 @@ CPJSONPCallbackReplacementString = @"${JSONP_CALLBACK}"; - (void)removeScriptTag { var head = document.getElementsByTagName("head").item(0); - + if(_scriptTag && _scriptTag.parentNode == head) head.removeChild(_scriptTag); - CPJSONPConnectionCallbacks["callback"+[self UID]] = nil; - delete CPJSONPConnectionCallbacks["callback"+[self UID]]; + CPJSONPConnectionCallbacks["callback" + [self UID]] = nil; + delete CPJSONPConnectionCallbacks["callback" + [self UID]]; } - (void)cancel diff --git a/Foundation/CPKeyValueCoding.j b/Foundation/CPKeyValueCoding.j index 36ef5b48c..104a6931f 100644 --- a/Foundation/CPKeyValueCoding.j +++ b/Foundation/CPKeyValueCoding.j @@ -25,6 +25,7 @@ @import "CPNull.j" @import "CPObject.j" + var CPObjectAccessorsForClass = nil, CPObjectModifiersForClass = nil; @@ -44,41 +45,41 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; { if (!CPObjectAccessorsForClass) CPObjectAccessorsForClass = [CPDictionary dictionary]; - + var UID = [isa UID], selector = nil, accessors = [CPObjectAccessorsForClass objectForKey:UID]; - + if (accessors) { selector = [accessors objectForKey:aKey]; - + if (selector) return selector === [CPNull null] ? nil : selector; } else { accessors = [CPDictionary dictionary]; - + [CPObjectAccessorsForClass setObject:accessors forKey:UID]; } var capitalizedKey = aKey.charAt(0).toUpperCase() + aKey.substr(1); - - if ([self instancesRespondToSelector:selector = CPSelectorFromString("get" + capitalizedKey)] || - [self instancesRespondToSelector:selector = CPSelectorFromString(aKey)] || - [self instancesRespondToSelector:selector = CPSelectorFromString("is" + capitalizedKey)] || - [self instancesRespondToSelector:selector = CPSelectorFromString("_get" + capitalizedKey)] || - [self instancesRespondToSelector:selector = CPSelectorFromString("_" + aKey)] || + + if ([self instancesRespondToSelector:selector = CPSelectorFromString("get" + capitalizedKey)] || + [self instancesRespondToSelector:selector = CPSelectorFromString(aKey)] || + [self instancesRespondToSelector:selector = CPSelectorFromString("is" + capitalizedKey)] || + [self instancesRespondToSelector:selector = CPSelectorFromString("_get" + capitalizedKey)] || + [self instancesRespondToSelector:selector = CPSelectorFromString("_" + aKey)] || [self instancesRespondToSelector:selector = CPSelectorFromString("_is" + capitalizedKey)]) { [accessors setObject:selector forKey:aKey]; - + return selector; } - + [accessors setObject:[CPNull null] forKey:aKey]; - + return nil; } @@ -87,25 +88,25 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; { if (!CPObjectModifiersForClass) CPObjectModifiersForClass = [CPDictionary dictionary]; - + var UID = [isa UID], selector = nil, modifiers = [CPObjectModifiersForClass objectForKey:UID]; - + if (modifiers) { selector = [modifiers objectForKey:aKey]; - + if (selector) return selector === [CPNull null] ? nil : selector; } else { modifiers = [CPDictionary dictionary]; - + [CPObjectModifiersForClass setObject:modifiers forKey:UID]; } - + if (selector) return selector === [CPNull null] ? nil : selector; @@ -115,12 +116,12 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; [self instancesRespondToSelector:selector = CPSelectorFromString("_set" + capitalizedKey)]) { [modifiers setObject:selector forKey:aKey]; - + return selector; } - + [modifiers setObject:[CPNull null] forKey:aKey]; - + return nil; } @@ -128,30 +129,30 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; - (CPString)_ivarForKey:(CPString)aKey { var ivar = '_' + aKey; - + if (typeof self[ivar] != "undefined") return ivar; var isKey = "is" + aKey.charAt(0).toUpperCase() + aKey.substr(1); - ivar = '_' + isKey; - + ivar = '_' + isKey; + if (typeof self[ivar] != "undefined") return ivar; - + ivar = aKey; - + if (typeof self[ivar] != "undefined") return ivar; - + ivar = isKey; - + if (typeof self[ivar] != "undefined") return ivar; return nil; } - + - (id)valueForKey:(CPString)aKey { var theClass = [self class], @@ -159,18 +160,18 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; if (selector) return objj_msgSend(self, selector); - - if([theClass accessInstanceVariablesDirectly]) + + if ([theClass accessInstanceVariablesDirectly]) { var ivar = [self _ivarForKey:aKey]; - + if (ivar) return self[ivar]; } - + return [self valueForUndefinedKey:aKey]; } - + - (id)valueForKeyPath:(CPString)aKeyPath { var firstDotIndex = aKeyPath.indexOf("."); @@ -179,7 +180,7 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; return [self valueForKey:aKeyPath]; var firstKeyComponent = aKeyPath.substring(0, firstDotIndex), - remainingKeyPath = aKeyPath.substring(firstDotIndex+1), + remainingKeyPath = aKeyPath.substring(firstDotIndex + 1), value = [self valueForKey:firstKeyComponent]; return [value valueForKeyPath:remainingKeyPath]; @@ -190,7 +191,7 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; var index = 0, count = keys.length, dictionary = [CPDictionary dictionary]; - + for (; index < count; ++index) { var key = keys[index], @@ -201,7 +202,7 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; else [dictionary setObject:value forKey:key]; } - + return dictionary; } @@ -211,7 +212,7 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; reason:[self description] + " is not key value coding-compliant for the key " + aKey userInfo:[CPDictionary dictionaryWithObjects:[self, aKey] forKeys:[CPTargetObjectUserInfoKey, CPUnknownUserInfoKey]]] raise]; } - + - (void)setValue:(id)aValue forKeyPath:(CPString)aKeyPath { if (!aKeyPath) aKeyPath = "self"; @@ -221,12 +222,12 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; count = keys.length - 1, owner = self; - for(; i < count; ++i) + for (; i < count; ++i) owner = [owner valueForKey:keys[i]]; - + [owner setValue:aValue forKey:keys[i]]; } - + - (void)setValue:(id)aValue forKey:(CPString)aKey { var theClass = [self class], @@ -234,26 +235,26 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; if (selector) return objj_msgSend(self, selector, aValue); - - if([theClass accessInstanceVariablesDirectly]) + + if ([theClass accessInstanceVariablesDirectly]) { var ivar = [self _ivarForKey:aKey]; - + if (ivar) { [self willChangeValueForKey:aKey]; - + self[ivar] = aValue; - + [self didChangeValueForKey:aKey]; return; } } - + [self setValue:aValue forUndefinedKey:aKey]; } - + - (void)setValue:(id)aValue forUndefinedKey:(CPString)aKey { [[CPException exceptionWithName:CPUndefinedKeyException @@ -267,7 +268,10 @@ CPUnknownUserInfoKey = @"CPUnknownUserInfoKey"; - (id)valueForKey:(CPString)aKey { - return [self objectForKey:aKey]; + if ([aKey hasPrefix:@"@"]) + return [super valueForKey:aKey.substr(1)]; + + return [self objectForKey:aKey]; } - (void)setValue:(id)aValue forKey:(CPString)aKey diff --git a/Foundation/CPKeyValueObserving.j b/Foundation/CPKeyValueObserving.j index 3867c9f13..bee35ec8f 100644 --- a/Foundation/CPKeyValueObserving.j +++ b/Foundation/CPKeyValueObserving.j @@ -224,52 +224,69 @@ var kvoNewAndOld = CPKeyValueObservingOptionNew|CPKeyValueObservingOptionOld, - (void)_replaceClass { var currentClass = _nativeClass, - kvoClassName = "$KVO_"+class_getName(_nativeClass), + kvoClassName = "$KVO_" + class_getName(_nativeClass), existingKVOClass = objj_lookUpClass(kvoClassName); - + if (existingKVOClass) { _targetObject.isa = existingKVOClass; return; } - + var kvoClass = objj_allocateClassPair(currentClass, kvoClassName); - + objj_registerClassPair(kvoClass); - + //copy in the methods from our model subclass var methodList = _CPKVOModelSubclass.method_list, - count = methodList.length; + count = methodList.length, + i = 0; - for (var i=0; i O(N^2)), so to avoid it, - // we keep track of whether observers are added or removed, and only do our + // we keep track of whether observers are added or removed, and only do our // rigorous testing in those cases. var observerRemovalCount = _observerRemovalCount, object = [aNotification object], @@ -286,18 +286,18 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ { var currentObservers = observers, count = observers.length; - + while (count--) { var observer = observers[count]; - - // if there wasn't removal of an observer during this posting, or there - // was but we are still in the observer list... + + // if there wasn't removal of an observer during this posting, or there + // was but we are still in the observer list... if ((observerRemovalCount === _observerRemovalCount) || [currentObservers indexOfObjectIdenticalTo:observer] !== CPNotFound) [observer postNotification:aNotification]; } } - + // Now do the same for the nil object observers... observers = [[_objectObservers objectForKey:[[CPNull null] UID]] copy]; @@ -312,8 +312,8 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ { var observer = observers[count]; - // if there wasn't removal of an observer during this posting, or there - // was but we are still in the observer list... + // if there wasn't removal of an observer during this posting, or there + // was but we are still in the observer list... if ((observerRemovalCount === _observerRemovalCount) || [currentObservers indexOfObjectIdenticalTo:observer] !== CPNotFound) [observer postNotification:aNotification]; } @@ -349,7 +349,7 @@ var _CPNotificationCenterPostNotification = function(/* CPNotificationCenter */ return _observer; } --(void)postNotification:(CPNotification)aNotification +- (void)postNotification:(CPNotification)aNotification { [_observer performSelector:_selector withObject:aNotification]; } diff --git a/Foundation/CPNull.j b/Foundation/CPNull.j index 43ba1cbc9..0eaca5773 100644 --- a/Foundation/CPNull.j +++ b/Foundation/CPNull.j @@ -25,7 +25,7 @@ var CPNullSharedNull = nil; -/*! +/*! @class CPNull @ingroup foundation @brief An object representation of \c nil. @@ -41,7 +41,7 @@ var CPNullSharedNull = nil; { if (CPNullSharedNull) return CPNullSharedNull; - + return [super alloc]; }*/ /*! @@ -53,8 +53,27 @@ var CPNullSharedNull = nil; { if (!CPNullSharedNull) CPNullSharedNull = [[CPNull alloc] init]; - + return CPNullSharedNull; } +/*! + Returns CPNull null. + @param aCoder the coder from which to do nothing + @return [CPNull null] +*/ +- (id)initWithCoder:(CPCoder)aCoder +{ + return [CPNull null]; +} + +/*! + Writes out nothing to the specified coder. + @param aCoder the coder to which nothing will + be written +*/ +- (void)encodeWithCoder:(CPCoder)aCoder +{ +} + @end diff --git a/Foundation/CPNumber.j b/Foundation/CPNumber.j index 3b95e242d..a0bbef39e 100644 --- a/Foundation/CPNumber.j +++ b/Foundation/CPNumber.j @@ -27,15 +27,15 @@ var __placeholder = new Number(), CPNumberUIDs = new CFMutableDictionary(); -/*! +/*! @class CPNumber @ingroup foundation @brief A bridged object to native Javascript numbers. This class primarily exists for source compatability. The JavaScript \c Number type can be changed on the fly based on context, - so there is no need to call any of these methods. - + so there is no need to call any of these methods. + In other words, native JavaScript numbers are bridged to CPNumber, so you can use them interchangeably (including operators and methods). */ @@ -93,7 +93,7 @@ var __placeholder = new Number(), { if (aChar.charCodeAt) return aChar.charCodeAt(0); - + return aChar; } @@ -126,7 +126,7 @@ var __placeholder = new Number(), { if (aChar.charCodeAt) return aChar.charCodeAt(0); - + return aChar; } @@ -164,7 +164,7 @@ var __placeholder = new Number(), { if (aChar.charCodeAt) return aChar.charCodeAt(0); - + return aChar; } @@ -217,14 +217,14 @@ FIXME: Do we need this? */ - (CPDecimal)decimalValue { - objj_throw_exception("decimalValue: NOT YET IMPLEMENTED"); + throw new Error("decimalValue: NOT YET IMPLEMENTED"); } - (CPString)descriptionWithLocale:(CPDictionary)aDictionary { if (!aDictionary) return toString(); - - objj_throw_exception("descriptionWithLocale: NOT YET IMPLEMENTED"); + + throw new Error("descriptionWithLocale: NOT YET IMPLEMENTED"); } - (CPString)description @@ -273,7 +273,7 @@ FIXME: Do we need this? return toString(); } -- (unsigned char)unsignedCharValue +- (unsigned char)unsignedCharValue { return String.fromCharCode(self); } @@ -306,7 +306,7 @@ FIXME: Do we need this? { if (self > aNumber) return CPOrderedDescending; else if (self < aNumber) return CPOrderedAscending; - + return CPOrderedSame; } diff --git a/Foundation/CPObjJRuntime.j b/Foundation/CPObjJRuntime.j index 76cfb8f76..019888d19 100644 --- a/Foundation/CPObjJRuntime.j +++ b/Foundation/CPObjJRuntime.j @@ -20,8 +20,6 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import "CPLog.j" - function CPStringFromSelector(aSelector) { @@ -44,7 +42,7 @@ function CPStringFromClass(aClass) } CPOrderedAscending = -1; -CPOrderedSame = 0; +CPOrderedSame = 0; CPOrderedDescending = 1; CPNotFound = -1; diff --git a/Foundation/CPObject.j b/Foundation/CPObject.j index b6c276ee6..c518f4f70 100644 --- a/Foundation/CPObject.j +++ b/Foundation/CPObject.j @@ -24,27 +24,27 @@ @class CPObject @ingroup foundation @brief The root class from which most classes are subclassed. - + CPObject is the root class for most Cappuccino classes. Like in Objective-C, you have to declare parent class explicitly in Objective-J, so your custom classes should almost always subclass CPObject or one of its children. - + CPObject provides facilities for class allocation and initialization, querying runtime about parent classes and available selectors, using KVC (key-value coding). - + When you subclass CPObject, most of the time you override one selector - init. It is called for default initialization of custom object. You must call parent class init in your overriden code:
- (id)init
 {
     self = [super init];
-    if(self) {
+    if (self) {
         ... provide default initialization code for your object ...
     }
     return self;
 }
- + One more useful thing to override is description(). This selector is used to provide developer-readable information about object. description selector is often used with CPLog debugging: @@ -58,7 +58,7 @@ CPLog(@"Got some class: %@", inst); would output: \c Got \c some \c class: \c - + @todo document KVC usage. */ @implementation CPObject @@ -164,11 +164,11 @@ CPLog(@"Got some class: %@", inst); + (BOOL)isSubclassOfClass:(Class)aClass { var theClass = self; - - for(; theClass; theClass = theClass.super_class) - if(theClass === aClass) + + for (; theClass; theClass = theClass.super_class) + if (theClass === aClass) return YES; - + return NO; } @@ -231,6 +231,23 @@ CPLog(@"Got some class: %@", inst); return !!class_getInstanceMethod(isa, aSelector); } +/*! + Tests whether the receiver implements to the provided selector regardless of inheritance. + @param aSelector the selector for which to test the receiver + @return \c YES if the receiver implements the selector +*/ +- (BOOL)implementsSelector:(SEL)aSelector +{ + var methods = class_copyMethodList(isa), + count = methods.length; + + while (count--) + if (method_getName(methods[count]) === aSelector) + return YES; + + return NO; +} + // Obtaining method information /*! @@ -332,25 +349,25 @@ CPLog(@"Got some class: %@", inst); - (void)forward:(SEL)aSelector :(marg_list)args { var signature = [self methodSignatureForSelector:aSelector]; - + if (signature) { invocation = [CPInvocation invocationWithMethodSignature:signature]; - + [invocation setTarget:self]; [invocation setSelector:aSelector]; - + var index = 2, count = args.length; - + for (; index < count; ++index) [invocation setArgument:args[index] atIndex:index]; - + [self forwardInvocation:invocation]; - + return [invocation returnValue]; } - + [self doesNotRecognizeSelector:aSelector]; } @@ -436,7 +453,7 @@ CPLog(@"Got some class: %@", inst); + (id)setVersion:(int)aVersion { version = aVersion; - + return self; } diff --git a/Foundation/CPOperation.j b/Foundation/CPOperation.j index e24617ae7..710dc5961 100644 --- a/Foundation/CPOperation.j +++ b/Foundation/CPOperation.j @@ -19,7 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import +@import "CPObject.j" /*! Operations receive very low priority for execution. @@ -57,13 +57,13 @@ CPOperationQueuePriorityHigh = 4; CPOperationQueuePriorityVeryHigh = 8; -/*! +/*! @class CPOperation @brief Represents an operation that can be run in an CPOperationQueue It should be subclassed an the subclass should implement its own main method to do the actual work. */ -@implementation CPOperation : CPObject +@implementation CPOperation : CPObject { CPArray operations; BOOL _cancelled; @@ -75,15 +75,14 @@ CPOperationQueuePriorityVeryHigh = 8; CPArray _dependencies; } - -- (void)main +- (void)main { // should be overridden in child class } -- (id)init +- (id)init { - if (self = [super init]) + if (self = [super init]) { _cancelled = NO; _executing = NO; @@ -98,15 +97,15 @@ CPOperationQueuePriorityVeryHigh = 8; /*! Starts the operation (runs the main method), sets all the status flags and runs the completion function if it's set */ -- (void)start +- (void)start { - if (!_cancelled) + if (!_cancelled) { [self willChangeValueForKey:@"isExecuting"]; _executing = YES; [self didChangeValueForKey:@"isExecuting"]; [self main]; - if (_completionFunction) + if (_completionFunction) { _completionFunction(); } @@ -123,7 +122,7 @@ CPOperationQueuePriorityVeryHigh = 8; Indicates if this operation has been cancelled @return if this operation has been cancelled */ -- (BOOL)isCancelled +- (BOOL)isCancelled { return _cancelled; } @@ -132,7 +131,7 @@ CPOperationQueuePriorityVeryHigh = 8; Indicates if this operation is currently executing @return if this operation is currently executing */ -- (BOOL)isExecuting +- (BOOL)isExecuting { return _executing; } @@ -141,7 +140,7 @@ CPOperationQueuePriorityVeryHigh = 8; Indicates if this operation has finished running @return if this operation has finished running */ -- (BOOL)isFinished +- (BOOL)isFinished { return _finished; } @@ -150,7 +149,7 @@ CPOperationQueuePriorityVeryHigh = 8; Just added for Cocoa compatibility @return always false */ -- (BOOL)isConcurrent +- (BOOL)isConcurrent { return NO; } @@ -159,8 +158,8 @@ CPOperationQueuePriorityVeryHigh = 8; Indicates if this operation is ready to be executed. Takes the "isFinished" state of dependent operations into account @return if this operation is ready to run */ -- (BOOL)isReady -{ +- (BOOL)isReady +{ return _ready; } @@ -168,7 +167,7 @@ CPOperationQueuePriorityVeryHigh = 8; The JS function that should be run after the main method @return JS function */ -- (JSObject)completionFunction +- (JSObject)completionFunction { return _completionFunction; } @@ -176,7 +175,7 @@ CPOperationQueuePriorityVeryHigh = 8; /*! Sets the JS function that should be run after the main method */ -- (void)setCompletionFunction:(JSObject)aJavaScriptFunction +- (void)setCompletionFunction:(JSObject)aJavaScriptFunction { _completionFunction = aJavaScriptFunction; } @@ -185,7 +184,7 @@ CPOperationQueuePriorityVeryHigh = 8; Makes the receiver dependent on the completion of the specified operation. @param anOperation the operation that the receiver should depend on */ -- (void)addDependency:(CPOperation)anOperation +- (void)addDependency:(CPOperation)anOperation { [self willChangeValueForKey:@"dependencies"]; [anOperation addObserver:self @@ -201,12 +200,12 @@ CPOperationQueuePriorityVeryHigh = 8; Removes the receiver’s dependence on the specified operation. @param anOperation the operation that the receiver should no longer depend on */ -- (void)removeDependency:(CPOperation)anOperation +- (void)removeDependency:(CPOperation)anOperation { [self willChangeValueForKey:@"dependencies"]; [_dependencies removeObject:anOperation]; [anOperation removeObserver:self - forKeyPath:@"isFinished"]; + forKeyPath:@"isFinished"]; [self didChangeValueForKey:@"dependencies"]; [self _updateIsReadyState]; } @@ -215,7 +214,7 @@ CPOperationQueuePriorityVeryHigh = 8; The operations that the receiver depends on @return array of operations */ -- (CPArray)dependencies +- (CPArray)dependencies { return _dependencies; } @@ -223,14 +222,14 @@ CPOperationQueuePriorityVeryHigh = 8; /*! Just added for Cocoa compatibility, doesn't do anything */ -- (void)waitUntilFinished +- (void)waitUntilFinished { } /*! Advises the operation object that it should stop executing its task. */ -- (void)cancel +- (void)cancel { [self willChangeValueForKey:@"isCancelled"]; _cancelled = YES; @@ -241,7 +240,7 @@ CPOperationQueuePriorityVeryHigh = 8; Sets the priority of the operation when used in an operation queue. @param priority the priority */ -- (void)setQueuePriority:(int)priority +- (void)setQueuePriority:(int)priority { _queuePriority = priority; } @@ -250,7 +249,7 @@ CPOperationQueuePriorityVeryHigh = 8; The priority of the operation when used in an operation queue. @return the priority */ -- (int)queuePriority +- (int)queuePriority { return _queuePriority; } @@ -261,28 +260,28 @@ CPOperationQueuePriorityVeryHigh = 8; change:(CPDictionary)change context:(void)context { - if (keyPath == @"isFinished") + if (keyPath == @"isFinished") { [self _updateIsReadyState]; } } -- (void)_updateIsReadyState +- (void)_updateIsReadyState { var newReady = YES; - if (_dependencies && [_dependencies count] > 0) + if (_dependencies && [_dependencies count] > 0) { var i = 0; - for (i = 0; i < [_dependencies count]; i++) + for (i = 0; i < [_dependencies count]; i++) { - if (![[_dependencies objectAtIndex:i] isFinished]) + if (![[_dependencies objectAtIndex:i] isFinished]) { newReady = NO; } } } - - if (newReady != _ready) + + if (newReady != _ready) { [self willChangeValueForKey:@"isReady"]; _ready = newReady; diff --git a/Foundation/CPOperationQueue.j b/Foundation/CPOperationQueue.j index ac4e4842b..b849cdd43 100644 --- a/Foundation/CPOperationQueue.j +++ b/Foundation/CPOperationQueue.j @@ -19,29 +19,29 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import -@import "CPOperation.j" -@import "CPInvocationOperation.j" @import "CPFunctionOperation.j" +@import "CPInvocationOperation.j" +@import "CPObject.j" +@import "CPOperation.j" // the global queue (mainQueue) var cpOperationMainQueue = nil; -/*! +/*! @class CPOperationQueue @brief Represents an operation queue that can run CPOperations */ -@implementation CPOperationQueue : CPObject +@implementation CPOperationQueue : CPObject { CPArray _operations; BOOL _suspended; CPString _name @accessors(property=name); - CPTimer _timer; + CPTimer _timer; } -- (id)init +- (id)init { - if (self = [super init]) + if (self = [super init]) { _operations = [[CPArray alloc] init]; _suspended = NO; @@ -55,35 +55,37 @@ var cpOperationMainQueue = nil; return self; } -- (void)_runNextOpsInQueue +- (void)_runNextOpsInQueue { - if (!_suspended && [self operationCount] > 0) + if (!_suspended && [self operationCount] > 0) { - var i = 0; - for (i = 0; i < [_operations count]; i++) + var i = 0, + count = [_operations count]; + + for (; i < count; i++) { var op = [_operations objectAtIndex:i]; - if ([op isReady] && ![op isCancelled] && ![op isFinished] && ![op isExecuting]) + if ([op isReady] && ![op isCancelled] && ![op isFinished] && ![op isExecuting]) { [op start]; - } + } } } } -- (void)_enableTimer:(BOOL)enable +- (void)_enableTimer:(BOOL)enable { - if (!enable) + if (!enable) { - if (_timer) + if (_timer) { [_timer invalidate]; _timer = nil; } - } - else + } + else { - if (!_timer) + if (!_timer) { _timer = [CPTimer scheduledTimerWithTimeInterval:0.01 target:self @@ -98,7 +100,7 @@ var cpOperationMainQueue = nil; Adds the specified operation object to the receiver. @param anOperation the operation that should be scheduled for execution */ -- (void)addOperation:(CPOperation)anOperation +- (void)addOperation:(CPOperation)anOperation { [self willChangeValueForKey:@"operations"]; [self willChangeValueForKey:@"operationCount"]; @@ -112,70 +114,72 @@ var cpOperationMainQueue = nil; Adds the specified array of operations to the queue. @param ops The array of CPOperation objects that you want to add to the receiver. @param wait If YES, the method only returns once all of the specified operations finish executing. If NO, the operations are added to the queue and control returns immediately to the caller. -*/ -- (void)addOperations:(CPArray)ops waitUntilFinished:(BOOL)wait +*/ +- (void)addOperations:(CPArray)ops waitUntilFinished:(BOOL)wait { - if (ops) + if (ops) { - if (wait) + if (wait) { [self _sortOpsByPriority:ops]; [self _runOpsSynchronously:ops]; - } - + } + [_operations addObjectsFromArray:ops]; [self _sortOpsByPriority:_operations]; - } + } } /*! Wraps the given js function in a CPOperation and adds it to the queue @param aFunction the JS function to add */ -- (void)addOperationWithFunction:(JSObject)aFunction +- (void)addOperationWithFunction:(JSObject)aFunction { [self addOperation:[CPFunctionOperation functionOperationWithFunction:aFunction]]; } -- (CPArray)operations +- (CPArray)operations { return _operations; } -- (int)operationCount +- (int)operationCount { - if (_operations) + if (_operations) { return [_operations count]; } - + return 0; } /*! Cancels all queued and executing operations. */ -- (void)cancelAllOperations +- (void)cancelAllOperations { - if (_operations) + if (_operations) { - var i = 0; - for (i = 0; i < [_operations count]; i++) - { - [[_operations objectAtIndex:i] cancel]; - } + var i = 0, + count = [_operations count]; + + for (; i < count; i++) + { + [[_operations objectAtIndex:i] cancel]; + } } } /*! Blocks until all of the receiver’s queued and executing operations finish executing. */ -- (void)waitUntilAllOperationsAreFinished +- (void)waitUntilAllOperationsAreFinished { // lets first stop the timer so it won't interfere [self _enableTimer:NO]; [self _runOpsSynchronously:_operations]; - if (!_suspended) + if (!_suspended) { [self _enableTimer:YES]; } @@ -186,7 +190,7 @@ var cpOperationMainQueue = nil; Returns the maximum number of concurrent operations that the receiver can execute. Always returns 1 because JS doesn't have threads */ -- (int)maxConcurrentOperationCount +- (int)maxConcurrentOperationCount { return 1; } @@ -195,7 +199,7 @@ var cpOperationMainQueue = nil; Modifies the execution of pending operations @param suspend if YES, queue execution is suspended. If NO, it is resumed */ -- (void)setSuspended:(BOOL)suspend +- (void)setSuspended:(BOOL)suspend { _suspended = suspend; [self _enableTimer:!suspend]; @@ -204,62 +208,64 @@ var cpOperationMainQueue = nil; /*! Returns a Boolean value indicating whether the receiver is scheduling queued operations for execution. */ -- (BOOL)isSuspended +- (BOOL)isSuspended { return _suspended; } -- (void)_sortOpsByPriority:(CPArray)someOps +- (void)_sortOpsByPriority:(CPArray)someOps { - if (someOps) + if (someOps) { - [someOps sortUsingFunction:function(lhs, rhs) + [someOps sortUsingFunction:function(lhs, rhs) { - if ([lhs queuePriority] < [rhs queuePriority]) + if ([lhs queuePriority] < [rhs queuePriority]) { return 1; - } - else + } + else { - if ([lhs queuePriority] > [rhs queuePriority]) + if ([lhs queuePriority] > [rhs queuePriority]) { return -1; - } - else + } + else { return 0; } } - } + } context:nil]; } } -- (void)_runOpsSynchronously:(CPArray)ops +- (void)_runOpsSynchronously:(CPArray)ops { - if (ops) + if (ops) { var keepGoing = YES; - while (keepGoing) + while (keepGoing) { - var i = 0; + var i = 0, + count = [ops count]; + keepGoing = NO; - + // start the ones that are ready - for (i = 0; i < [ops count]; i++) + for (; i < count; i++) { var op = [ops objectAtIndex:i]; - if ([op isReady] && ![op isCancelled] && ![op isFinished] && ![op isExecuting]) + if ([op isReady] && ![op isCancelled] && ![op isFinished] && ![op isExecuting]) { [op start]; - } + } } - + // make sure they are all done - for (i = 0; i < [ops count]; i++) + for (i = 0; i < count; i++) { var op = [ops objectAtIndex:i]; - if (![op isFinished] && ![op isCancelled]) + if (![op isFinished] && ![op isCancelled]) { keepGoing = YES; } @@ -271,23 +277,23 @@ var cpOperationMainQueue = nil; /*! Convenience method for one system wide singelton queue. Returns the same queue as currentQueue. */ -+ (CPOperationQueue)mainQueue ++ (CPOperationQueue)mainQueue { - if (!cpOperationMainQueue) + if (!cpOperationMainQueue) { cpOperationMainQueue = [[CPOperationQueue alloc] init]; [cpOperationMainQueue setName:@"main"]; } - + return cpOperationMainQueue; } /*! Convenience method for one system wide singelton queue. Returns the same queue as mainQueue. */ -+ (CPOperationQueue)currentQueue ++ (CPOperationQueue)currentQueue { return [CPOperationQueue mainQueue]; } -@end \ No newline at end of file +@end diff --git a/Foundation/CPProxy.j b/Foundation/CPProxy.j index 2d5fba736..2b4054c83 100644 --- a/Foundation/CPProxy.j +++ b/Foundation/CPProxy.j @@ -133,7 +133,7 @@ return [invocation returnValue]; } --(BOOL)isMemberOfClass:(Class)aClass +- (BOOL)isMemberOfClass:(Class)aClass { var signature = [self methodSignatureForSelector:_cmd], invocation = [CPInvocation invocationWithMethodSignature:signature]; diff --git a/Foundation/CPRange.j b/Foundation/CPRange.j index 6a0b1b5ce..b91a878d3 100755 --- a/Foundation/CPRange.j +++ b/Foundation/CPRange.j @@ -105,7 +105,7 @@ function CPLocationInRange(aLocation, aRange) } /*! - Creates a new range with the minimum \c location and a \c length + Creates a new range with the minimum \c location and a \c length that extends to the maximum \c length. @param lhsRange the first CPRange @param rhsRange the second CPRange @@ -129,7 +129,7 @@ function CPIntersectionRange(lhsRange, rhsRange) { if(CPMaxRange(lhsRange) < rhsRange.location || CPMaxRange(rhsRange) < lhsRange.location) return CPMakeRange(0, 0); - + var location = MAX(lhsRange.location, rhsRange.location); return CPMakeRange(location, MIN(CPMaxRange(lhsRange), CPMaxRange(rhsRange)) - location); } @@ -166,10 +166,11 @@ function CPStringFromRange(aRange) function CPRangeFromString(aString) { var comma = aString.indexOf(','); - + return { location:parseInt(aString.substr(1, comma - 1)), length:parseInt(aString.substring(comma + 1, aString.length)) }; } /*! @} */ + diff --git a/Foundation/CPRunLoop.j b/Foundation/CPRunLoop.j index 0884e4915..89ad9337a 100644 --- a/Foundation/CPRunLoop.j +++ b/Foundation/CPRunLoop.j @@ -20,8 +20,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import "CPObject.j" @import "CPArray.j" +@import "CPObject.j" @import "CPString.j" /*! @@ -53,7 +53,7 @@ var _CPRunLoopPerformPool = [], { if (!aPerform || _CPRunLoopPerformPool.length >= _CPRunLoopPerformPoolCapacity) return; - + _CPRunLoopPerformPool.push(aPerform); } @@ -62,24 +62,24 @@ var _CPRunLoopPerformPool = [], if (_CPRunLoopPerformPool.length) { var perform = _CPRunLoopPerformPool.pop(); - + perform._target = aTarget; perform._selector = aSelector; perform._argument = anArgument; perform._order = anOrder; perform._runLoopModes = modes; perform._isValid = YES; - + return perform; } - + return [[self alloc] initWithSelector:aSelector target:aTarget argument:anArgument order:anOrder modes:modes]; } - (id)initWithSelector:(SEL)aSelector target:(SEL)aTarget argument:(id)anArgument order:(unsigned)anOrder modes:(CPArray)modes { self = [super init]; - + if (self) { _selector = aSelector; @@ -89,7 +89,7 @@ var _CPRunLoopPerformPool = [], _runLoopModes = modes; _isValid = YES; } - + return self; } @@ -117,14 +117,14 @@ var _CPRunLoopPerformPool = [], { if (!_isValid) return YES; - + if ([_runLoopModes containsObject:aRunLoopMode]) { [_target performSelector:_selector withObject:_argument]; - + return YES; } - + return NO; } @@ -157,6 +157,7 @@ var CPRunLoopLastNativeRunLoop = 0; CPDate _effectiveDate; CPArray _orderedPerforms; + int _runLoopInsuranceTimer; } /* @@ -173,16 +174,16 @@ var CPRunLoopLastNativeRunLoop = 0; - (id)init { self = [super init]; - + if (self) { _orderedPerforms = []; - + _timersForModes = {}; _nativeTimersForModes = {}; _nextTimerFireDatesForModes = {}; } - + return self; } @@ -219,7 +220,7 @@ var CPRunLoopLastNativeRunLoop = 0; while (count--) if (anOrder < [_orderedPerforms[count] order]) break; - + _orderedPerforms.splice(count + 1, 0, perform); } @@ -232,11 +233,11 @@ var CPRunLoopLastNativeRunLoop = 0; - (void)cancelPerformSelector:(SEL)aSelector target:(id)aTarget argument:(id)anArgument { var count = _orderedPerforms.length; - + while (count--) { var perform = _orderedPerforms[count]; - + if ([perform selector] === aSelector && [perform target] == aTarget && [perform argument] == anArgument) [_orderedPerforms[count] invalidate]; } @@ -260,13 +261,24 @@ var CPRunLoopLastNativeRunLoop = 0; _timersForModes[aMode].push(aTimer); else _timersForModes[aMode] = [aTimer]; - + _didAddTimer = YES; - + if (!aTimer._lastNativeRunLoopsForModes) aTimer._lastNativeRunLoopsForModes = {}; - + aTimer._lastNativeRunLoopsForModes[aMode] = CPRunLoopLastNativeRunLoop; + + + // FIXME: Hack for not doing this in CommonJS + if ([CFBundle.environments() indexOfObject:("Browser")] !== CPNotFound) + { + if (!_runLoopInsuranceTimer) + _runLoopInsuranceTimer = window.setNativeTimeout(function() + { + [self limitDateForMode:CPDefaultRunLoopMode]; + }, 0); + } } /*! @@ -277,102 +289,112 @@ var CPRunLoopLastNativeRunLoop = 0; //simple locking to try to prevent concurrent iterating over timers if (_runLoopLock) return; - + _runLoopLock = YES; - + + // FIXME: Hack for not doing this in CommonJS + if ([CFBundle.environments() indexOfObject:("Browser")] !== CPNotFound) + { + if (_runLoopInsuranceTimer) + { + window.clearNativeTimeout(_runLoopInsuranceTimer); + _runLoopInsuranceTimer = nil; + } + } + var now = _effectiveDate ? [_effectiveDate laterDate:[CPDate date]] : [CPDate date], nextFireDate = nil, nextTimerFireDate = _nextTimerFireDatesForModes[aMode]; - + // Perform Timers if necessary if (_didAddTimer || nextTimerFireDate && nextTimerFireDate <= now) { _didAddTimer = NO; - + // Cancel existing window.setTimeout if (_nativeTimersForModes[aMode] !== nil) { window.clearNativeTimeout(_nativeTimersForModes[aMode]); - + _nativeTimersForModes[aMode] = nil; } - + // Empty timers to avoid catastrophe if a timer is added during a timer fire. var timers = _timersForModes[aMode], index = timers.length; - + _timersForModes[aMode] = nil; - - // Loop through timers looking for ones that had fired + + // Loop through timers looking for ones that had fired while (index--) { var timer = timers[index]; - + if (timer._lastNativeRunLoopsForModes[aMode] < CPRunLoopLastNativeRunLoop && timer._isValid && timer._fireDate <= now) [timer fire]; - + // Timer may or may not still be valid if (timer._isValid) nextFireDate = (nextFireDate === nil) ? timer._fireDate : [nextFireDate earlierDate:timer._fireDate]; - + else { // FIXME: Is there an issue with reseting the fire date in -fire? or adding it back to the run loop?... timer._lastNativeRunLoopsForModes[aMode] = 0; - + timers.splice(index, 1); } } - + // Timers may have been added during the firing of timers - // They do NOT get a shot at firing, because they certainly + // They do NOT get a shot at firing, because they certainly // haven't gone through one native timer. var newTimers = _timersForModes[aMode]; - + if (newTimers && newTimers.length) { index = newTimers.length; - + while (index--) { var timer = newTimers[index]; - + if ([timer isValid]) nextFireDate = (nextFireDate === nil) ? timer._fireDate : [nextFireDate earlierDate:timer._fireDate]; else newTimers.splice(index, 1); } - + _timersForModes[aMode] = newTimers.concat(timers); } else _timersForModes[aMode] = timers; - + _nextTimerFireDatesForModes[aMode] = nextFireDate; - + //initiate a new window.setTimeout if there are any timers if (_nextTimerFireDatesForModes[aMode] !== nil) _nativeTimersForModes[aMode] = window.setNativeTimeout(function() { _effectiveDate = nextFireDate; _nativeTimersForModes[aMode] = nil; ++CPRunLoopLastNativeRunLoop; [self limitDateForMode:aMode]; _effectiveDate = nil; }, MAX(0, [nextFireDate timeIntervalSinceNow] * 1000)); } - + // Run loop performers var performs = _orderedPerforms, index = performs.length; - + _orderedPerforms = []; - + while (index--) { var perform = performs[index]; - + if ([perform fireInMode:CPDefaultRunLoopMode]) { [_CPRunLoopPerform _poolPerform:perform]; - + performs.splice(index, 1); } } - + if (_orderedPerforms.length) { _orderedPerforms = _orderedPerforms.concat(performs); @@ -380,9 +402,9 @@ var CPRunLoopLastNativeRunLoop = 0; } else _orderedPerforms = performs; - + _runLoopLock = NO; - + return nextFireDate; } diff --git a/Foundation/CPSet.j b/Foundation/CPSet.j index 7026a376e..0a1e5f821 100644 --- a/Foundation/CPSet.j +++ b/Foundation/CPSet.j @@ -23,11 +23,11 @@ * * TODO: Needs to implement CPCoding, CPCopying. */ - -@import "CPObject.j" + @import "CPArray.j" -@import "CPNumber.j" @import "CPEnumerator.j" +@import "CPNumber.j" +@import "CPObject.j" /*! @class CPSet @@ -79,7 +79,7 @@ /* Creates and returns a set containing the objects in a given argument list. @param anObject The first object to add to the new set. - @param ... A comma-separated list of objects, ending with nil, to add to the new set. If the same object appears more than once objects, it is added only once to the returned set. + @param ... A comma-separated list of objects, ending with nil, to add to the new set. If the same object appears more than once objects, it is added only once to the returned set. */ + (id)setWithObjects:(id)anObject, ... { @@ -87,9 +87,9 @@ argLength = arguments.length, i = 2; - for(; i < argLength && ((argument = arguments[i]) !== nil); ++i) + for (; i < argLength && ((argument = arguments[i]) !== nil); ++i) [set addObject:argument]; - + return set; } @@ -112,7 +112,7 @@ _count = 0; _contents = {}; } - + return self; } @@ -121,21 +121,21 @@ @param array An array of objects to add to the new set. If the same object appears more than once in array, it is represented only once in the returned set. */ - (id)initWithArray:(CPArray)anArray -{ +{ if (self = [self init]) { var count = anArray.length; - + while (count--) [self addObject:anArray[count]]; } - + return self; } /* Initializes a newly allocated set with members taken from the specified list of objects. - @param objects A array of objects to add to the new set. If the same object appears more than once objects, it is added only once to the returned set. + @param objects A array of objects to add to the new set. If the same object appears more than once objects, it is added only once to the returned set. @param count The number of objects from objects to add to the new set. */ - (id)initWithObjects:(id)objects count:(unsigned)count @@ -155,10 +155,10 @@ var argLength = arguments.length, i = 2; - for(; i < argLength && (argument = arguments[i]) != nil; ++i) + for (; i < argLength && (argument = arguments[i]) != nil; ++i) [self addObject:argument]; } - + return self; } @@ -179,10 +179,11 @@ if (!aSet) return self; - - var contents = aSet._contents; - - for (var property in contents) + + var contents = aSet._contents, + property; + + for (property in contents) { if (contents.hasOwnProperty(property)) { @@ -192,7 +193,7 @@ [self addObject:contents[property]]; } } - + return self; } @@ -201,14 +202,15 @@ */ - (CPArray)allObjects { - var array = []; - - for (var property in _contents) + var array = [], + property; + + for (property in _contents) { if (_contents.hasOwnProperty(property)) array.push(_contents[property]); } - + return array; } @@ -217,12 +219,14 @@ */ - (id)anyObject { - for (var property in _contents) + var property; + + for (property in _contents) { if (_contents.hasOwnProperty(property)) return _contents[property]; } - + return nil; } @@ -236,7 +240,7 @@ if (obj !== undefined && [obj isEqual:anObject]) return YES; - + return NO; } @@ -277,7 +281,7 @@ @param set The set with which to compare the receiver. */ - (BOOL)isEqualToSet:(CPSet)set -{ +{ // If both are subsets of each other, they are equal return self === set || ([self count] === [set count] && [set isSubsetOfSet:self]); } @@ -288,14 +292,17 @@ */ - (BOOL)isSubsetOfSet:(CPSet)set { - var items = [self allObjects]; - for (var i = 0; i < items.length; i++) + var items = [self allObjects], + i = 0, + count = items.length; + + for (; i < count; i++) { // If at least one item is not in both sets, self isn't a subset if (![set containsObject:items[i]]) return NO; } - + return YES; } @@ -315,8 +322,11 @@ */ - (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)argument { - var items = [self allObjects]; - for (var i = 0; i < items.length; i++) + var items = [self allObjects], + i = 0, + count = items.length; + + for (; i < count; i++) { [items[i] performSelector:aSelector withObject:argument]; } @@ -330,7 +340,7 @@ { if ([self containsObject:object]) return object; - + return nil; } @@ -432,8 +442,11 @@ */ - (void)intersectSet:(CPSet)set { - var items = [self allObjects]; - for (var i = 0, count = items.length; i < count; i++) + var items = [self allObjects], + i = 0, + count = items.length; + + for (; i < count; i++) { if (![set containsObject:items[i]]) [self removeObject:items[i]]; @@ -446,8 +459,11 @@ */ - (void)minusSet:(CPSet)set { - var items = [set allObjects]; - for (var i = 0; i < items.length; i++) + var items = [set allObjects], + i = 0, + count = items.length; + + for (; i < count; i++) { if ([self containsObject:items[i]]) [self removeObject:items[i]]; @@ -460,13 +476,21 @@ */ - (void)unionSet:(CPSet)set { - var items = [set allObjects]; - for (var i = 0, count = items.length; i < count; i++) + var items = [set allObjects], + i = 0, + count = items.length; + + for (; i < count; i++) { [self addObject:items[i]]; } } +- (CPString)description +{ + return @"{(" + [self allObjects].join(", ") + ")}"; +} + @end @implementation CPSet (CPCopying) @@ -509,5 +533,5 @@ var CPSetObjectsKey = @"CPSetObjectsKey"; */ @implementation CPMutableSet : CPSet -@end +@end diff --git a/Foundation/CPSortDescriptor.j b/Foundation/CPSortDescriptor.j index c7b78c840..8c246bd4c 100755 --- a/Foundation/CPSortDescriptor.j +++ b/Foundation/CPSortDescriptor.j @@ -43,7 +43,7 @@ CPOrderedSame = 0; */ CPOrderedDescending = 1; -/*! +/*! @class CPSortDescriptor @ingroup foundation @brief Holds attributes necessary to describe how to sort a set of objects. @@ -85,20 +85,20 @@ CPOrderedDescending = 1; Initializes the sort descriptor @param aKey the property key path to sort @param isAscending the sort order - @param aSelector this method gets called to compare objects. The method will take one argument + @param aSelector this method gets called to compare objects. The method will take one argument (the object to compare against itself, and must return a CPComparisonResult. */ - (id)initWithKey:(CPString)aKey ascending:(BOOL)isAscending selector:(SEL)aSelector { self = [super init]; - + if (self) { _key = aKey; _ascending = isAscending; _selector = aSelector; } - + return self; } @@ -149,3 +149,30 @@ CPOrderedDescending = 1; } @end + +var CPSortDescriptorKeyKey = @"CPSortDescriptorKeyKey", // Don't you just love naming schemes ;) + CPSortDescriptorAscendingKey = @"CPSortDescriptorAscendingKey", + CPSortDescriptorSelectorKey = @"CPSortDescriptorSelectorKey"; + +@implementation CPSortDescriptor (CPCoding) + +- (id)initWithCoder:(CPCoder)aCoder +{ + if (self = [super init]) + { + _key = [aCoder decodeObjectForKey:CPSortDescriptorKeyKey]; + _ascending = [aCoder decodeBoolForKey:CPSortDescriptorAscendingKey]; + _selector = CPSelectorFromString([aCoder decodeObjectForKey:CPSortDescriptorSelectorKey]); + } + + return self; +} + +- (void)encodeWithCoder:(CPCoder)aCoder +{ + [aCoder encodeObject:_key forKey:CPSortDescriptorKeyKey]; + [aCoder encodeBool:_ascending forKey:CPSortDescriptorAscendingKey]; + [aCoder encodeObject:CPStringFromSelector(_selector) forKey:CPSortDescriptorSelectorKey]; +} + +@end diff --git a/Foundation/CPString.j b/Foundation/CPString.j index 51154a43c..abbe70c2d 100644 --- a/Foundation/CPString.j +++ b/Foundation/CPString.j @@ -20,8 +20,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import "CPObject.j" @import "CPException.j" +@import "CPObject.j" @import "CPSortDescriptor.j" @import "CPValue.j" @@ -65,7 +65,7 @@ var CPStringRegexSpecialCharacters = [ CPStringRegexEscapeExpression = new RegExp("(\\" + CPStringRegexSpecialCharacters.join("|\\") + ")", 'g'), CPStringRegexTrimWhitespace = new RegExp("(^\\s+|\\s+$)", 'g'); -/*! +/*! @class CPString @ingroup foundation @brief An immutable string (collection of characters). @@ -103,7 +103,7 @@ var CPStringRegexSpecialCharacters = [ + (id)stringWithHash:(unsigned)aHash { var hashString = parseInt(aHash, 10).toString(16); - return "000000".substring(0, MAX(6-hashString.length, 0)) + hashString; + return "000000".substring(0, MAX(6 - hashString.length, 0)) + hashString; } /*! @@ -147,7 +147,7 @@ var CPStringRegexSpecialCharacters = [ } /*! - Creates a new string using C printf-style formatting. First argument should be a constant format string, + Creates a new string using C printf-style formatting. First argument should be a constant format string, like ' "float val = %f" ', remaining arguments should be the variables to print the values of, comma-separated. @param format the format to be used, printf-style @return the initialized CPString @@ -238,8 +238,8 @@ var CPStringRegexSpecialCharacters = [ while ((difference -= substring.length) >= 0) string += substring; - - if (-difference < substring.length) + + if (-difference < substring.length) string += substring.substring(0, -difference); return string; @@ -329,7 +329,7 @@ var CPStringRegexSpecialCharacters = [ /*! Finds the range of characters in the receiver - where the specified string exists in the given range + where the specified string exists in the given range of the receiver.The search is subject to the options specified in the specified mask which can be a combination of:
@@ -374,7 +374,7 @@ var CPStringRegexSpecialCharacters = [
 }
 
 /*!
-    Returns a new string in which all occurrences of a target string in the reciever are replaced by 
+    Returns a new string in which all occurrences of a target string in the reciever are replaced by
     another given string.
     @param target The string to replace.
     @param replacement the string with which to replace the \c target
@@ -403,7 +403,7 @@ var CPStringRegexSpecialCharacters = [
         regExp;
 
     if (options & CPCaseInsensitiveSearch)
-        regExp = new RegExp(target, "gi"); 
+        regExp = new RegExp(target, "gi");
     else
         regExp = new RegExp(target, "g");
 
@@ -411,7 +411,7 @@ var CPStringRegexSpecialCharacters = [
 }
 
 /*
-   Returns a new string in which the characters in a specified range of the receiver 
+   Returns a new string in which the characters in a specified range of the receiver
    are replaced by a given string.
    @param range A range of characters in the receiver.
    @param replacement The string with which to replace the characters in \c range.
@@ -442,7 +442,6 @@ var CPStringRegexSpecialCharacters = [
     return [self compare:aString options:nil];
 }
 
-
 /*
     Compares the receiver to the specified string.
     @param aString the string with which to compare
@@ -463,18 +462,18 @@ var CPStringRegexSpecialCharacters = [
 {
     var lhs = self,
         rhs = aString;
-    
+
     if (aMask & CPCaseInsensitiveSearch)
     {
         lhs = lhs.toLowerCase();
         rhs = rhs.toLowerCase();
     }
-    
+
     if (lhs < rhs)
         return CPOrderedAscending;
     else if (lhs > rhs)
         return CPOrderedDescending;
-    
+
     return CPOrderedSame;
 }
 
@@ -559,7 +558,7 @@ var CPStringRegexSpecialCharacters = [
         lhs = self,
         rhs = aString,
         min = MIN([lhs length], [rhs length]);
-    
+
     if (aMask & CPCaseInsensitiveSearch)
     {
         lhs = [lhs lowercaseString];
@@ -580,10 +579,13 @@ var CPStringRegexSpecialCharacters = [
 */
 - (CPString)capitalizedString
 {
-    var parts = self.split(/\b/g); // split on word boundaries
-    for (var i = 0; i < parts.length; i++)
+    var parts = self.split(/\b/g), // split on word boundaries
+        i = 0,
+        count = parts.length;
+
+    for (; i < count; i++)
     {
-        if (i == 0 || (/\s$/).test(parts[i-1])) // only capitalize if previous token was whitespace
+        if (i == 0 || (/\s$/).test(parts[i - 1])) // only capitalize if previous token was whitespace
             parts[i] = parts[i].substring(0, 1).toUpperCase() + parts[i].substring(1).toLowerCase();
         else
             parts[i] = parts[i].toLowerCase();
@@ -615,8 +617,8 @@ var CPStringRegexSpecialCharacters = [
     return parseFloat(self, 10);
 }
 /*!
-    Returns \c YES on encountering one of "Y", "y", "T", "t", or 
-    a digit 1-9. Returns \c NO otherwise. This method skips the initial 
+    Returns \c YES on encountering one of "Y", "y", "T", "t", or
+    a digit 1-9. Returns \c NO otherwise. This method skips the initial
     whitespace characters, +,- followed by Zeroes.
 */
 
@@ -678,7 +680,7 @@ var CPStringRegexSpecialCharacters = [
 - (CPString)lastPathComponent
 {
     var components = [self pathComponents];
-    return components[components.length -1];
+    return components[components.length - 1];
 }
 
 /*!
@@ -690,12 +692,12 @@ var CPStringRegexSpecialCharacters = [
 {
     var path = self,
         start = length - 1;
-    
+
     while (path.charAt(start) === '/')
         start--;
-    
+
     path = path.substr(0, path.lastIndexOf('/', start));
-    
+
     if (path === "" && charAt(0) === '/')
         return '/';
 
@@ -758,11 +760,12 @@ var CPStringRegexSpecialCharacters = [
 */
 + (CPString)UUID
 {
-    var g = @"";
-    
-    for(var i = 0; i < 32; i++)
+    var g = @"",
+        i = 0;
+
+    for (; i < 32; i++)
         g += FLOOR(RAND() * 0xF).toString(0xF);
-    
+
     return g;
 }
 
diff --git a/Foundation/CPTimer.j b/Foundation/CPTimer.j
index e1ec83ce5..6e14ba46a 100644
--- a/Foundation/CPTimer.j
+++ b/Foundation/CPTimer.j
@@ -20,13 +20,14 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
-@import "CPObject.j"
-@import "CPInvocation.j"
 @import "CPDate.j"
+@import "CPInvocation.j"
+@import "CPObject.j"
 @import "CPRunLoop.j"
 
+
 /*!
-    @class CPTimer 
+    @class CPTimer
     @ingroup foundation
 
     @brief A timer object that can send a message after the given time interval.
@@ -36,7 +37,7 @@
     CPTimeInterval      _timeInterval;
     CPInvocation        _invocation;
     Function            _callback;
-    
+
     BOOL                _repeats;
     BOOL                _isValid;
     CPDate              _fireDate;
@@ -46,13 +47,13 @@
 /*!
     Returns a new NSTimer object and adds it to the current NSRunLoop object in the default mode.
 */
-+ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat 
++ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
 {
     var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds invocation:anInvocation repeats:shouldRepeat];
 
     //add to the runloop
     [[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
-    
+
     return timer;
 }
 
@@ -65,27 +66,27 @@
 
     //add to the runloop
     [[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
-        
+
     return timer;
 }
 
 /*!
     Returns a new NSTimer object and adds it to the current NSRunLoop object in the default mode.
 */
-+ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat 
++ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
 {
     var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds callback:aFunction repeats:shouldRepeat];
 
     //add to the runloop
     [[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
-    
+
     return timer;
 }
 
 /*!
     Returns a new NSTimer that, when added to a run loop, will fire after seconds.
 */
-+ (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat 
++ (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
 {
     return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds invocation:anInvocation repeats:shouldRepeat];
 }
@@ -101,7 +102,7 @@
 /*!
     Returns a new NSTimer that, when added to a run loop, will fire after seconds.
 */
-+ (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat 
++ (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
 {
     return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds callback:aFunction repeats:shouldRepeat];
 }
@@ -112,8 +113,8 @@
 - (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
 {
     self = [super init];
-    
-    if (self) 
+
+    if (self)
     {
         _timeInterval = seconds;
         _invocation = anInvocation;
@@ -121,7 +122,7 @@
         _isValid = YES;
         _fireDate = aDate;
     }
-    
+
     return self;
 }
 
@@ -131,16 +132,16 @@
 - (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
 {
     var invocation = [CPInvocation invocationWithMethodSignature:1];
-    
+
     [invocation setTarget:aTarget];
     [invocation setSelector:aSelector];
     [invocation setArgument:self atIndex:2];
-    
+
     self = [self initWithFireDate:aDate interval:seconds invocation:invocation repeats:shouldRepeat];
 
     if (self)
         _userInfo = userInfo;
-        
+
     return self;
 }
 
@@ -150,8 +151,8 @@
 - (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
 {
     self = [super init];
-    
-    if (self) 
+
+    if (self)
     {
         _timeInterval = seconds;
         _callback = aFunction;
@@ -159,14 +160,14 @@
         _isValid = YES;
         _fireDate = aDate;
     }
-    
+
     return self;
 }
 
 /*!
     Returns the receiver’s time interval.
 */
-- (CPTimeInterval)timeInterval 
+- (CPTimeInterval)timeInterval
 {
    return _timeInterval;
 }
@@ -174,15 +175,15 @@
 /*!
     Returns the date at which the receiver will fire.
 */
-- (CPDate)fireDate 
+- (CPDate)fireDate
 {
-   return _fireDate;  
+   return _fireDate;
 }
 
 /*!
     Resets the receiver to fire next at a given date.
 */
-- (void)setFireDate:(CPDate)aDate 
+- (void)setFireDate:(CPDate)aDate
 {
     _fireDate = aDate;
 }
@@ -205,7 +206,7 @@
 
     if (_repeats)
         _fireDate = [CPDate dateWithTimeIntervalSinceNow:_timeInterval];
-    
+
     else
         [self invalidate];
 }
@@ -213,7 +214,7 @@
 /*!
     Returns a Boolean value that indicates whether the receiver is currently valid.
 */
-- (BOOL)isValid 
+- (BOOL)isValid
 {
    return _isValid;
 }
@@ -221,7 +222,7 @@
 /*!
     Stops the receiver from ever firing again and requests its removal from its NSRunLoop object.
 */
-- (void)invalidate 
+- (void)invalidate
 {
    _isValid = NO;
    _userInfo = nil;
@@ -246,14 +247,14 @@ var _CPTimerBridgeTimer = function(codeOrFunction, aDelay, shouldRepeat, functio
 {
     var timeoutID = CPTimersTimeoutID++,
         theFunction = nil;
-        
+
     if (typeof codeOrFunction === "string")
         theFunction = function() { new Function(codeOrFunction)(); if (!shouldRepeat) CPTimersForTimeoutIDs[timeoutID] = nil; }
     else
     {
         if (!functionArgs)
             functionArgs = [];
-        
+
         theFunction = function() { codeOrFunction.apply(window, functionArgs); if (!shouldRepeat) CPTimersForTimeoutIDs[timeoutID] = nil; }
     }
 
@@ -270,10 +271,10 @@ window.setTimeout = function(codeOrFunction, aDelay)
 window.clearTimeout = function(aTimeoutID)
 {
     var timer = CPTimersForTimeoutIDs[aTimeoutID];
-    
+
     if (timer)
         [timer invalidate];
-        
+
     CPTimersForTimeoutIDs[aTimeoutID] = nil;
 }
 
diff --git a/Foundation/CPURL.j b/Foundation/CPURL.j
index 25dbb7d5a..1486baad3 100644
--- a/Foundation/CPURL.j
+++ b/Foundation/CPURL.j
@@ -1,5 +1,26 @@
+/*
+ * CPURL.j
+ * Foundation
+ *
+ * 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 "CPObject.j"
 
 CPURLNameKey                        = @"CPURLNameKey";
 CPURLLocalizedNameKey               = @"CPURLLocalizedNameKey";
@@ -44,7 +65,7 @@ CPURLCustomIconKey                  = @"CPURLCustomIconKey";
 - (id)initWithScheme:(CPString)aScheme host:(CPString)aHost path:(CPString)aPath
 {
     var URLString = (aScheme ? aScheme + ":" : "") + (aHost ? aHost + "//" : "") + (aPath || "");
-    
+
     return [self initWithString:URLString];
 }
 
@@ -94,6 +115,12 @@ CPURLCustomIconKey                  = @"CPURLCustomIconKey";
     return [self absoluteURL].path();
 }
 
+- (CPArray)pathComponents
+{
+    var path = self.pathComponents();
+    return [path copy];
+}
+
 // if absolute, returns the same as path
 - (CPString)relativePath
 {
diff --git a/Foundation/CPURLConnection.j b/Foundation/CPURLConnection.j
index 0d8c7b2a7..d6f260226 100644
--- a/Foundation/CPURLConnection.j
+++ b/Foundation/CPURLConnection.j
@@ -31,45 +31,45 @@ var CPURLConnectionDelegate = nil;
 /*!
     @class CPURLConnection
     @ingroup foundation
-    @brief Provides loading of a URL request. 
+    @brief Provides loading of a URL request.
 
     An interface to downloading content at a specified URL. Using one of the
     class methods, you can obtain the data.
-    
+
     @delegate -(void)connection:(CPURLConnection)connection didFailWithError:(id)error;
     Called when the connection encounters an error.
     @param connection the connection that had an error
     @param error the error, which is either a javascript DOMException or an http
     status code (javascript number/CPNumber)
-    
+
     @delegate -(void)connection:(CPURLConnection)connection didReceiveResponse:(CPHTTPURLResponse)response;
     Called when the connection receives a response.
     @param connection the connection that received a response
     @param response the received response
-    
+
     @delegate -(void)connection:(CPURLConnection)connection didReceiveData:(CPString)data;
     Called when the connection has received data.
     @param connection the connection that received data
     @param data the received data
-    
+
     @delegate -(void)connectionDidFinishLoading:(CPURLConnection)connection;
     Called when the URL has finished loading.
     @param connection the connection that finished loading
-    
+
     Class Delegate Method:
-    
+
     @delegate -(void)connectionDidReceiveAuthenticationChallenge:(id)connection
     The class delegate allows you to set global behavior for when authentication challenges (401 status codes) are returned.
-    
+
     The recommended way to handle this method is to store a reference to the connection, and then use whatever
-    method you have to authenticate yourself.  Once you've authenticated yourself, you should cancel 
+    method you have to authenticate yourself.  Once you've authenticated yourself, you should cancel
     and then start the connection:
-    
+
 
 [connection cancel];
 [connection start];
 
- + @param connection the connection that received the authentication challenge. */ @implementation CPURLConnection : CPObject @@ -116,7 +116,7 @@ var CPURLConnectionDelegate = nil; catch (anException) { } - + return nil; } @@ -141,13 +141,13 @@ var CPURLConnectionDelegate = nil; - (id)initWithRequest:(CPURLRequest)aRequest delegate:(id)aDelegate startImmediately:(BOOL)shouldStartImmediately { self = [super init]; - + if (self) { _request = aRequest; _delegate = aDelegate; _isCanceled = NO; - + var URL = [_request URL], scheme = [URL scheme]; @@ -187,7 +187,7 @@ var CPURLConnectionDelegate = nil; _isCanceled = NO; try - { + { _HTTPRequest.open([_request HTTPMethod], [[_request URL] absoluteString], YES); _HTTPRequest.onreadystatechange = function() { [self _readyStateDidChange]; } @@ -214,7 +214,7 @@ var CPURLConnectionDelegate = nil; - (void)cancel { _isCanceled = YES; - + try { _HTTPRequest.abort(); @@ -238,24 +238,22 @@ var CPURLConnectionDelegate = nil; var statusCode = _HTTPRequest.status(), URL = [_request URL]; - if ([_delegate respondsToSelector:@selector(connection:didReceiveResponse:)]) + if (statusCode === 401 && [CPURLConnectionDelegate respondsToSelector:@selector(connectionDidReceiveAuthenticationChallenge:)]) + [CPURLConnectionDelegate connectionDidReceiveAuthenticationChallenge:self]; + else { - if (_isLocalFileConnection) - [_delegate connection:self didReceiveResponse:[[CPURLResponse alloc] initWithURL:URL]]; - else + if ([_delegate respondsToSelector:@selector(connection:didReceiveResponse:)]) { - var response = [[CPHTTPURLResponse alloc] initWithURL:URL]; - - [response _setStatusCode:statusCode]; - - [_delegate connection:self didReceiveResponse:response]; + if (_isLocalFileConnection) + [_delegate connection:self didReceiveResponse:[[CPURLResponse alloc] initWithURL:URL]]; + else + { + var response = [[CPHTTPURLResponse alloc] initWithURL:URL]; + [response _setStatusCode:statusCode]; + [_delegate connection:self didReceiveResponse:response]; + } } - } - if (!_isCanceled) - { - if (statusCode === 401 && [CPURLConnectionDelegate respondsToSelector:@selector(connectionDidReceiveAuthenticationChallenge:)]) - [CPURLConnectionDelegate connectionDidReceiveAuthenticationChallenge:self]; - else + if (!_isCanceled) { if ([_delegate respondsToSelector:@selector(connection:didReceiveData:)]) [_delegate connection:self didReceiveData:_HTTPRequest.responseText()]; @@ -292,4 +290,4 @@ var CPURLConnectionDelegate = nil; return [self _HTTPRequest]; } -@end \ No newline at end of file +@end diff --git a/Foundation/CPURLRequest.j b/Foundation/CPURLRequest.j index 5d6b7fc9b..28b4e4dc4 100644 --- a/Foundation/CPURLRequest.j +++ b/Foundation/CPURLRequest.j @@ -23,7 +23,7 @@ @import "CPObject.j" -/*! +/*! @class CPURLRequest @ingroup foundation @brief Contains data obtained during a request made with CPURLConnection. @@ -34,7 +34,7 @@ @implementation CPURLRequest : CPObject { CPURL _URL; - + // FIXME: this should be CPData CPString _HTTPBody; CPString _HTTPMethod; @@ -59,7 +59,7 @@ - (id)initWithURL:(CPURL)aURL { self = [super init]; - + if (self) { [self setURL:aURL]; @@ -67,12 +67,12 @@ _HTTPBody = @""; _HTTPMethod = @"GET"; _HTTPHeaderFields = [CPDictionary dictionary]; - - [self setValue:"Thu, 1 Jan 1970 00:00:00 GMT" forHTTPHeaderField:"If-Modified-Since"]; + + [self setValue:"Thu, 01 Jan 1970 00:00:00 GMT" forHTTPHeaderField:"If-Modified-Since"]; [self setValue:"no-cache" forHTTPHeaderField:"Cache-Control"]; [self setValue:"XMLHttpRequest" forHTTPHeaderField:"X-Requested-With"]; } - + return self; } diff --git a/Foundation/CPURLResponse.j b/Foundation/CPURLResponse.j index ed524d940..6d0c0bf5e 100644 --- a/Foundation/CPURLResponse.j +++ b/Foundation/CPURLResponse.j @@ -44,10 +44,10 @@ - (id)initWithURL:(CPURL)aURL { self = [super init]; - + if (self) _URL = aURL; - + return self; } @@ -57,13 +57,13 @@ } /* Creating a Response -initWithURL:MIMEType:expectedContentLength:textEncodingName: +initWithURL:MIMEType:expectedContentLength:textEncodingName: Getting the Response Properties -expectedContentLength -suggestedFilename -MIMEType -textEncodingName -URL +expectedContentLength +suggestedFilename +MIMEType +textEncodingName +URL */ @end diff --git a/Foundation/CPUndoManager.j b/Foundation/CPUndoManager.j index 7d9c8d8f0..f07247589 100644 --- a/Foundation/CPUndoManager.j +++ b/Foundation/CPUndoManager.j @@ -20,15 +20,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import "CPObject.j" @import "CPInvocation.j" +@import "CPObject.j" @import "CPProxy.j" var CPUndoManagerNormal = 0, CPUndoManagerUndoing = 1, CPUndoManagerRedoing = 2; - + CPUndoManagerCheckpointNotification = @"CPUndoManagerCheckpointNotification"; CPUndoManagerDidOpenUndoGroupNotification = @"CPUndoManagerDidOpenUndoGroupNotification"; CPUndoManagerDidRedoChangeNotification = @"CPUndoManagerDidRedoChangeNotification"; @@ -53,7 +53,7 @@ var _CPUndoGroupingPool = [], { if (!anUndoGrouping || _CPUndoGroupingPool.length >= _CPUndoGroupingPoolCapacity) return; - + _CPUndoGroupingPool.push(anUndoGrouping); } @@ -62,28 +62,28 @@ var _CPUndoGroupingPool = [], if (_CPUndoGroupingPool.length) { var grouping = _CPUndoGroupingPool.pop(); - + grouping._parent = anUndoGrouping; - + if (grouping._invocations.length) grouping._invocations = []; - + return grouping; } - + return [[self alloc] initWithParent:anUndoGrouping]; } - (id)initWithParent:(_CPUndoGrouping)anUndoGrouping { self = [super init]; - + if (self) { _parent = anUndoGrouping; _invocations = []; } - + return self; } @@ -134,13 +134,13 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey", - (id)initWithCoder:(CPCoder)aCoder { self = [super init]; - + if (self) { _parent = [aCoder decodeObjectForKey:_CPUndoGroupingParentKey]; _invocations = [aCoder decodeObjectForKey:_CPUndoGroupingInvocationsKey]; } - + return self; } @@ -152,7 +152,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey", @end -/*! +/*! @class CPUndoManager @ingroup foundation @brief A general mechanism for user action "undo". @@ -193,21 +193,21 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey", - (id)init { self = [super init]; - + if (self) { _redoStack = []; _undoStack = []; - + _state = CPUndoManagerNormal; - + [self setRunLoopModes:[CPDefaultRunLoopMode]]; [self setGroupsByEvent:YES]; _undoManagerProxy = [_CPUndoManagerProxy alloc]; _undoManagerProxy._undoManager = self; } - + return self; } @@ -258,7 +258,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey", - (id)prepareWithInvocationTarget:(id)aTarget { _preparedTarget = aTarget; - + return _undoManagerProxy; } @@ -270,7 +270,7 @@ var _CPUndoGroupingParentKey = @"_CPUndoGroupingParentKey", { if ([_preparedTarget respondsToSelector:aSelector]) return 1; - + return nil;//[_preparedTarget methodSignatureForSelector:selector]; } @@ -317,8 +317,8 @@ if (_currentGroup == nil) { if (_undoStack.length > 0) return YES; - - return [_currentGrouping actions].length > 0; + + return [[_currentGrouping invocations] count] > 0; } // Preform Undo and Redo @@ -337,7 +337,7 @@ if (_currentGroup == nil) Performs an undo on the last undo group. */ - (void)undoNestedGroup -{ +{ if ([_undoStack count] <= 0) return; @@ -373,7 +373,7 @@ if (_currentGroup == nil) // Don't do anything if we have no redos. if ([_redoStack count] <= 0) return; - + /* if (_state == NSUndoManagerUndoing) [NSException raise:NSInternalInconsistencyException format:@"redo called while undoing"]; @@ -386,19 +386,19 @@ if (_currentGroup == nil) [defaultCenter postNotificationName:CPUndoManagerWillRedoChangeNotification object:self]; - + var oldUndoGrouping = _currentGrouping, undoGrouping = _redoStack.pop(); - + _currentGrouping = nil; _state = CPUndoManagerRedoing; [self _beginUndoGrouping]; [undoGrouping invoke]; [self endUndoGrouping]; - + [_CPUndoGrouping _poolUndoGrouping:undoGrouping]; - + _currentGrouping = oldUndoGrouping; _state = CPUndoManagerNormal; @@ -452,31 +452,31 @@ if (_currentGroup == nil) object:self]; var parent = [_currentGrouping parent]; - + if (!parent && [_currentGrouping invocations].length > 0) { [defaultCenter postNotificationName:CPUndoManagerWillCloseUndoGroupNotification object:self]; - + // Put this group on the redo stack if we are currently undoing, otherwise // put it on the undo stack. That way, "undos" become "redos". var stack = _state === CPUndoManagerUndoing ? _redoStack : _undoStack; - + stack.push(_currentGrouping); - + if (_levelsOfUndo > 0 && stack.length > _levelsOfUndo) - stack.splice(0, 1); + stack.splice(0, 1); } - + // Nested Undo Grouping else { [parent addInvocationsFromArray:[_currentGrouping invocations]]; - + [_CPUndoGrouping _poolUndoGrouping:_currentGrouping]; } - + _currentGrouping = parent; } @@ -491,7 +491,7 @@ if (_currentGroup == nil) if (_disableCount <= 0) [CPException raise:CPInternalInconsistencyException reason:"enableUndoRegistration. There are no disable messages in effect right now."]; - + _disableCount--; } @@ -527,10 +527,10 @@ if (_currentGroup == nil) { var grouping = _currentGrouping, level = _currentGrouping != nil; - + while (grouping = [grouping parent]) ++level; - + return level; } @@ -586,27 +586,27 @@ if (_currentGroup == nil) - (void)removeAllActionsWithTarget:(id)aTarget { [_currentGrouping removeInvocationsWithTarget:aTarget]; - + var index = _redoStack.length; - + while (index--) { var grouping = _redoStack[index]; - + [grouping removeInvocationsWithTarget:aTarget]; - + if (![grouping invocations].length) _redoStack.splice(index, 1); } index = _undoStack.length; - + while (index--) { var grouping = _undoStack[index]; - + [grouping removeInvocationsWithTarget:aTarget]; - + if (![grouping invocations].length) _undoStack.splice(index, 1); } @@ -739,7 +739,7 @@ var CPUndoManagerRedoStackKey = @"CPUndoManagerRedoStackKey", CPUndoManagerLevelsOfUndoKey = @"CPUndoManagerLevelsOfUndoKey"; CPUndoManagerActionNameKey = @"CPUndoManagerActionNameKey"; CPUndoManagerCurrentGroupingKey = @"CPUndoManagerCurrentGroupingKey"; - + CPUndoManagerRunLoopModesKey = @"CPUndoManagerRunLoopModesKey"; CPUndoManagerGroupsByEventKey = @"CPUndoManagerGroupsByEventKey"; @@ -748,22 +748,22 @@ var CPUndoManagerRedoStackKey = @"CPUndoManagerRedoStackKey", - (id)initWithCoder:(CPCoder)aCoder { self = [super init]; - + if (self) { _redoStack = [aCoder decodeObjectForKey:CPUndoManagerRedoStackKey]; _undoStack = [aCoder decodeObjectForKey:CPUndoManagerUndoStackKey]; - + _levelsOfUndo = [aCoder decodeObjectForKey:CPUndoManagerLevelsOfUndoKey]; _actionName = [aCoder decodeObjectForKey:CPUndoManagerActionNameKey]; _currentGrouping = [aCoder decodeObjectForKey:CPUndoManagerCurrentGroupingKey]; - + _state = CPUndoManagerNormal; - + [self setRunLoopModes:[aCoder decodeObjectForKey:CPUndoManagerRunLoopModesKey]]; [self setGroupsByEvent:[aCoder decodeBoolForKey:CPUndoManagerGroupsByEventKey]]; } - + return self; } @@ -771,10 +771,10 @@ var CPUndoManagerRedoStackKey = @"CPUndoManagerRedoStackKey", { [aCoder encodeObject:_redoStack forKey:CPUndoManagerRedoStackKey]; [aCoder encodeObject:_undoStack forKey:CPUndoManagerUndoStackKey]; - + [aCoder encodeInt:_levelsOfUndo forKey:CPUndoManagerLevelsOfUndoKey]; [aCoder encodeObject:_actionName forKey:CPUndoManagerActionNameKey]; - + [aCoder encodeObject:_currentGrouping forKey:CPUndoManagerCurrentGroupingKey]; [aCoder encodeObject:_runLoopModes forKey:CPUndoManagerRunLoopModesKey]; diff --git a/Foundation/CPUserSessionManager.j b/Foundation/CPUserSessionManager.j index b15d1e916..5feca31ff 100644 --- a/Foundation/CPUserSessionManager.j +++ b/Foundation/CPUserSessionManager.j @@ -20,8 +20,8 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import -@import +@import "CPObject.j" +@import "CPString.j" CPUserSessionUndeterminedStatus = 0; @@ -36,7 +36,7 @@ var CPDefaultUserSessionManager = nil; @implementation CPUserSessionManager : CPObject { CPUserSessionStatus _status; - + CPString _userIdentifier; } @@ -51,10 +51,10 @@ var CPDefaultUserSessionManager = nil; - (id)init { self = [super init]; - + if (self) _status = CPUserSessionUndeterminedStatus; - + return self; } @@ -67,9 +67,9 @@ var CPDefaultUserSessionManager = nil; { if (_status == aStatus) return; - + _status = aStatus; - + [[CPNotificationCenter defaultCenter] postNotificationName:CPUserSessionManagerStatusDidChangeNotification object:self]; @@ -87,9 +87,9 @@ var CPDefaultUserSessionManager = nil; { if (_userIdentifier == anIdentifier) return; - + _userIdentifier = anIdentifier; - + [[CPNotificationCenter defaultCenter] postNotificationName:CPUserSessionManagerUserIdentifierDidChangeNotification object:self]; diff --git a/Foundation/CPValue.j b/Foundation/CPValue.j index 1c6de7a92..70ea751ad 100644 --- a/Foundation/CPValue.j +++ b/Foundation/CPValue.j @@ -20,11 +20,11 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -@import "CPObject.j" @import "CPCoder.j" +@import "CPObject.j" -/*! +/*! @class CPValue @ingroup foundation @brief A generic "value". Can be subclassed to hold specific data types. @@ -54,10 +54,10 @@ - (id)initWithJSObject:(JSObject)aJSObject { self = [super init]; - + if (self) _JSObject = aJSObject; - + return self; } @@ -83,7 +83,7 @@ var CPValueValueKey = @"CPValueValueKey"; - (id)initWithCoder:(CPCoder)aCoder { self = [super init]; - + if (self) _JSObject = JSON.parse([aCoder decodeObjectForKey:CPValueValueKey]); diff --git a/Foundation/CPWebDAVManager.j b/Foundation/CPWebDAVManager.j index fb17b72a2..6b821fb08 100644 --- a/Foundation/CPWebDAVManager.j +++ b/Foundation/CPWebDAVManager.j @@ -1,3 +1,24 @@ +/* + * CPWebDAVManager.j + * Foundation + * + * 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 + */ var setURLResourceValuesForKeysFromProperties = function(aURL, keys, properties) { diff --git a/Foundation/Foundation.j b/Foundation/Foundation.j index 2d4fb3ebb..03709afa5 100755 --- a/Foundation/Foundation.j +++ b/Foundation/Foundation.j @@ -35,7 +35,6 @@ @import "CPKeyedUnarchiver.j" @import "CPKeyValueCoding.j" @import "CPKeyValueObserving.j" -@import "CPLog.j" @import "CPNotification.j" @import "CPNotificationCenter.j" @import "CPNull.j" diff --git a/Objective-J/CFBundle.js b/Objective-J/CFBundle.js index 7f75d3d1d..8c7044e73 100644 --- a/Objective-J/CFBundle.js +++ b/Objective-J/CFBundle.js @@ -635,9 +635,9 @@ function decompileStaticFile(/*Bundle*/ aBundle, /*String*/ aString, /*String*/ if (CFBundleSupportedSpriteType === CFBundleMHTMLUncachedSpriteType) { - var exclamationIndex = URLString.indexOf("!"), - firstPart = URLString.substring(0, exclamationIndex), - lastPart = URLString.substring(exclamationIndex); + var exclamationIndex = mappedURLString.indexOf("!"), + firstPart = mappedURLString.substring(0, exclamationIndex), + lastPart = mappedURLString.substring(exclamationIndex); mappedURLString = firstPart + "?" + CFCacheBuster + lastPart; } diff --git a/Objective-J/CFDictionary.js b/Objective-J/CFDictionary.js index a9c0ccb74..8c10b7fdf 100644 --- a/Objective-J/CFDictionary.js +++ b/Objective-J/CFDictionary.js @@ -75,7 +75,7 @@ CFDictionary.prototype.containsValue = function(/*id*/ anObject) count = keys.length; for (; index < count; ++index) - if (buckets[keys] === anObject) + if (buckets[keys[index]] === anObject) return YES; return NO; @@ -106,8 +106,8 @@ CFDictionary.prototype.countOfValue = function(/*id*/ anObject) countOfValue = 0; for (; index < count; ++index) - if (buckets[keys] === anObject) - return ++countOfValue; + if (buckets[keys[index]] === anObject) + ++countOfValue; return countOfValue; } diff --git a/Objective-J/CFHTTPRequest.js b/Objective-J/CFHTTPRequest.js index d4f9cba76..d4007f585 100644 --- a/Objective-J/CFHTTPRequest.js +++ b/Objective-J/CFHTTPRequest.js @@ -92,15 +92,23 @@ if (!NativeRequest) GLOBAL(CFHTTPRequest) = function() { + this._isOpen = false; + this._requestHeaders = {}; + this._mimeType = null; + this._eventDispatcher = new EventDispatcher(this); this._nativeRequest = new NativeRequest(); var self = this; - - this._nativeRequest.onreadystatechange = function() + this._stateChangeHandler = function() { determineAndDispatchHTTPRequestEvents(self); } + + this._nativeRequest.onreadystatechange = this._stateChangeHandler; + + if (CFHTTPRequest.AuthenticationDelegate !== nil) + this._eventDispatcher.addEventListener("HTTP403", function(){CFHTTPRequest.AuthenticationDelegate(self)}); } CFHTTPRequest.UninitializedState = 0; @@ -109,6 +117,9 @@ CFHTTPRequest.LoadedState = 2; CFHTTPRequest.InteractiveState = 3; CFHTTPRequest.CompleteState = 4; +//override to forward all CFHTTPRequest authorization failures to a single function +CFHTTPRequest.AuthenticationDelegate = nil; + CFHTTPRequest.prototype.status = function() { try @@ -177,7 +188,7 @@ CFHTTPRequest.prototype.responseText = function() CFHTTPRequest.prototype.setRequestHeader = function(/*String*/ aHeader, /*Object*/ aValue) { - return this._nativeRequest.setRequestHeader(aHeader, aValue); + this._requestHeaders[aHeader] = aValue; } CFHTTPRequest.prototype.getResponseHeader = function(/*String*/ aHeader) @@ -192,17 +203,40 @@ CFHTTPRequest.prototype.getAllResponseHeaders = function() CFHTTPRequest.prototype.overrideMimeType = function(/*String*/ aMimeType) { - if ("overrideMimeType" in this._nativeRequest) - return this._nativeRequest.overrideMimeType(aMimeType); + this._mimeType = aMimeType; } CFHTTPRequest.prototype.open = function(/*String*/ aMethod, /*String*/ aURL, /*Boolean*/ isAsynchronous, /*String*/ aUser, /*String*/ aPassword) { + this._isOpen = true; + this._URL = aURL; + this._async = isAsynchronous; + this._method = aMethod; + this._user = aUser; + this._password = aPassword; return this._nativeRequest.open(aMethod, aURL, isAsynchronous, aUser, aPassword); } CFHTTPRequest.prototype.send = function(/*Object*/ aBody) { + for (var i in this._requestHeaders) + { + if (this._requestHeaders.hasOwnProperty(i)) + this._nativeRequest.setRequestHeader(i, this._requestHeaders[i]); + } + + if (!this._isOpen) + { + delete this._nativeRequest.onreadystatechange; + this._nativeRequest.open(this._method, this._URL, this._async, this._user, this._password); + this._nativeRequest.onreadystatechange = this._stateChangeHandler; + } + + if (this._mimeType && "overrideMimeType" in this._nativeRequest) + this._nativeRequest.overrideMimeType(this._mimeType); + + this._isOpen = false; + try { return this._nativeRequest.send(aBody); @@ -216,6 +250,7 @@ CFHTTPRequest.prototype.send = function(/*Object*/ aBody) CFHTTPRequest.prototype.abort = function() { + this._isOpen = false; return this._nativeRequest.abort(); } @@ -236,20 +271,20 @@ function determineAndDispatchHTTPRequestEvents(/*CFHTTPRequest*/ aRequest) eventDispatcher.dispatchEvent({ type:"readystatechange", request:aRequest}); var nativeRequest = aRequest._nativeRequest, - readyState = ["uninitialized", "loading", "loaded", "interactive", "complete"][aRequest.readyState()]; + readyStates = ["uninitialized", "loading", "loaded", "interactive", "complete"]; - eventDispatcher.dispatchEvent({ type:readyState, request:aRequest}); - - if (readyState === "complete") + if (readyStates[aRequest.readyState()] === "complete") { var status = "HTTP" + aRequest.status(); - eventDispatcher.dispatchEvent({ type:status, request:aRequest }); var result = aRequest.success() ? "success" : "failure"; - eventDispatcher.dispatchEvent({ type:result, request:aRequest }); + + eventDispatcher.dispatchEvent({ type:readyStates[aRequest.readyState()], request:aRequest}); } + else + eventDispatcher.dispatchEvent({ type:readyStates[aRequest.readyState()], request:aRequest}); } function FileRequest(/*CFURL*/ aURL, onsuccess, onfailure) diff --git a/Objective-J/CFPropertyList.js b/Objective-J/CFPropertyList.js index 1533f4863..39ba97e13 100644 --- a/Objective-J/CFPropertyList.js +++ b/Objective-J/CFPropertyList.js @@ -91,6 +91,16 @@ CFPropertyList.writePropertyListToFile = function(/*CFPropertyList*/ aPropertyLi { return FILE.write(aFilePath, CFPropertyList.stringFromPropertyList(aPropertyList, aFormat), { charset:"UTF-8" }); } +CFPropertyList.modifyPlist = function(/*String*/ aFilePath, /*Function*/ aCallback, /*String*/ aFormat) +{ + var string = FILE.read(aFilePath, { charset:"UTF-8" }); + var format = CFPropertyList.sniffedFormatOfString(string); + var plist = CFPropertyList.propertyListFromString(string, format); + + aCallback(plist); + + CFPropertyList.writePropertyListToFile(plist, aFilePath, aFormat || format); +} #endif function serializePropertyList(/*CFPropertyList*/ aPropertyList, /*Object*/ serializers) diff --git a/Objective-J/CommonJS/lib/objective-j.js b/Objective-J/CommonJS/lib/objective-j.js index 1134dc857..444c47806 100644 --- a/Objective-J/CommonJS/lib/objective-j.js +++ b/Objective-J/CommonJS/lib/objective-j.js @@ -182,5 +182,11 @@ exports.fullVersionString = function() { ); } +global.ObjectiveJ = {}; + +for (key in exports) + if (Object.prototype.hasOwnProperty.call(exports, key)) + global.ObjectiveJ[key] = exports[key]; + if (require.main == module.id) exports.run(system.args); diff --git a/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js b/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js index 9cf34552b..13e3d7a8b 100644 --- a/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js +++ b/Objective-J/CommonJS/lib/objective-j/jake/bundletask.js @@ -453,7 +453,9 @@ BundleTask.prototype.resourcesPath = function() // Don't sprite images larger than 32KB, IE 8 doesn't like it. BundleTask.isSpritable = function(aResourcePath) { - return isImage(aResourcePath) && FILE.size(aResourcePath) < 32768; + return isImage(aResourcePath) && FILE.size(aResourcePath) < 32768 && + ("data:" + mimeType(aResourcePath) + ";base64," + + base64.encode(FILE.read(aResourcePath, "b"))).length < 32768; } BundleTask.prototype.defineResourceTask = function(aResourcePath, aDestinationPath) diff --git a/Objective-J/Preprocessor.js b/Objective-J/Preprocessor.js index b95b4e61b..d66d47144 100644 --- a/Objective-J/Preprocessor.js +++ b/Objective-J/Preprocessor.js @@ -244,19 +244,19 @@ Preprocessor.prototype.accessors = function(tokens) value = true; if (!IS_WORD(name)) - throw new SyntaxError(this.error_message("*** @property attribute name not valid.")); + throw new SyntaxError(this.error_message("*** @accessors attribute name not valid.")); if ((token = tokens.skip_whitespace()) == TOKEN_EQUAL) { value = tokens.skip_whitespace(); if (!IS_WORD(value)) - throw new SyntaxError(this.error_message("*** @property attribute value not valid.")); + throw new SyntaxError(this.error_message("*** @accessors attribute value not valid.")); if (name == "setter") { if ((token = tokens.next()) != TOKEN_COLON) - throw new SyntaxError(this.error_message("*** @property setter attribute requires argument with \":\" at end of selector name.")); + throw new SyntaxError(this.error_message("*** @accessors setter attribute requires argument with \":\" at end of selector name.")); value += ":"; } @@ -270,7 +270,7 @@ Preprocessor.prototype.accessors = function(tokens) break; if (token != TOKEN_COMMA) - throw new SyntaxError(this.error_message("*** Expected ',' or ')' in @property attribute list.")); + throw new SyntaxError(this.error_message("*** Expected ',' or ')' in @accessors attribute list.")); } return attributes; diff --git a/Objective-J/Runtime.js b/Objective-J/Runtime.js index fda9ad563..1df7ab26f 100644 --- a/Objective-J/Runtime.js +++ b/Objective-J/Runtime.js @@ -386,7 +386,7 @@ DISPLAY_NAME(objj_registerClassPair); GLOBAL(class_createInstance) = function(/*Class*/ aClass) { if (!aClass) - objj_exception_throw(new objj_exception(OBJJNilClassException, "*** Attempting to create object with Nil class.")); + throw new Error("*** Attempting to create object with Nil class."); var object = new aClass.allocator(); diff --git a/Tests/AppKit/CPPopUpButtonTest.j b/Tests/AppKit/CPPopUpButtonTest.j new file mode 100644 index 000000000..c45085633 --- /dev/null +++ b/Tests/AppKit/CPPopUpButtonTest.j @@ -0,0 +1,23 @@ + +@import +@import + +@implementation CPPopUpButtonTest : OJTestCase +{ + CPPopUpButton button; +} + +- (void)setUp +{ + button = [CPPopUpButton new]; +} + +- (void)testItemTitles +{ + [self assert:[] equals:[button itemTitles]]; + [button addItem:[[CPMenuItem alloc] initWithTitle:"Option A" action:nil keyEquivalent:nil]]; + [button addItem:[[CPMenuItem alloc] initWithTitle:"Option B" action:nil keyEquivalent:nil]]; + [self assert:["Option A", "Option B"] equals:[button itemTitles]]; +} + +@end diff --git a/Tests/Foundation/CPArrayPerformance.j b/Tests/Foundation/CPArrayPerformance.j new file mode 100644 index 000000000..a41c16dce --- /dev/null +++ b/Tests/Foundation/CPArrayPerformance.j @@ -0,0 +1,56 @@ +@import +@import +@import +@import + +@implementation CPArrayPerformance : OJTestCase + +- (void)testSortUsingDescriptorsSpeed +{ + + var ELEMENTS = 1000, + REPEATS = 10, + array = []; + for (var i=0; i +@import +@import + +@implementation CPNullTest : OJTestCase + +- (void)testArchiving +{ + [self assert:[CPKeyedUnarchiver unarchiveObjectWithData:[CPKeyedArchiver archivedDataWithRootObject:[CPNull null]]] equals:[CPNull null]]; +} + +@end \ No newline at end of file diff --git a/Tests/Foundation/CPObjectTest.j b/Tests/Foundation/CPObjectTest.j new file mode 100644 index 000000000..729e01c9e --- /dev/null +++ b/Tests/Foundation/CPObjectTest.j @@ -0,0 +1,45 @@ +@import + +@implementation CPObjectTest : OJTestCase +{ +} + +- (void)testImplementsSelector +{ + var receiver = [[Receiver alloc] init]; + + [self assertTrue:[receiver implementsSelector:@selector(implementedInReceiverAndSuper)]]; + [self assertTrue:[receiver implementsSelector:@selector(implementedInReceiverOnly)]]; + + [self assertFalse:[receiver implementsSelector:@selector(implementedInSuperOnly)]]; + [self assertFalse:[receiver implementsSelector:@selector(notImplementedInSuperNorReceiver)]]; + +} + +@end + +@implementation SuperReceiver : CPObject +{ +} + +- (void)implementedInReceiverAndSuper +{ +} + +- (void)implementedInSuperOnly +{ +} +@end + +@implementation Receiver : SuperReceiver +{ +} + +- (void)implementedInReceiverAndSuper +{ +} + +- (void)implementedInReceiverOnly +{ +} +@end diff --git a/Tests/Foundation/CPSetTest.j b/Tests/Foundation/CPSetTest.j index 905d8fdb8..e8f4fd162 100644 --- a/Tests/Foundation/CPSetTest.j +++ b/Tests/Foundation/CPSetTest.j @@ -56,4 +56,17 @@ [self assertFalse:[set containsObject:nil]]; } +- (void)testDescription +{ + [self assert:@"{()}" equals:[set description]]; + [set addObject:"horizon"]; + [set addObject:"surfer"]; + [set addObject:"7"]; + var desc = [set description]; + // Ordering may not be guaranteed. + [self assertTrue:desc.match(new RegExp("{\(.*horizon.*\)}"))]; + [self assertTrue:desc.match(new RegExp("{\(.*surfer.*\)}"))]; + [self assertTrue:desc.match(new RegExp("{\(.*7.*\)}"))]; +} + @end diff --git a/Tests/Manual/CPBrowserTest/AppController.j b/Tests/Manual/CPBrowserTest/AppController.j index d70984352..9a002c9b2 100644 --- a/Tests/Manual/CPBrowserTest/AppController.j +++ b/Tests/Manual/CPBrowserTest/AppController.j @@ -29,6 +29,16 @@ - (CPArray)children { return [[Node withValue:1], + [Node withValue:2], + [Node withValue:3], + [Node withValue:2], + [Node withValue:3], + [Node withValue:2], + [Node withValue:3], + [Node withValue:2], + [Node withValue:3], + [Node withValue:2], + [Node withValue:3], [Node withValue:2], [Node withValue:3], [Node withValue:4]]; diff --git a/Tests/Manual/CPBrowserTest/CPBrowser.j b/Tests/Manual/CPBrowserTest/CPBrowser.j deleted file mode 120000 index 00538b439..000000000 --- a/Tests/Manual/CPBrowserTest/CPBrowser.j +++ /dev/null @@ -1 +0,0 @@ -../../../AppKit/CPBrowser.j \ No newline at end of file diff --git a/Tests/Manual/LoadTimeTest/AppController.j b/Tests/Manual/LoadTimeTest/AppController.j index bd1f94618..bdb9a6c30 100644 --- a/Tests/Manual/LoadTimeTest/AppController.j +++ b/Tests/Manual/LoadTimeTest/AppController.j @@ -28,7 +28,7 @@ if (data.length < 10) { // reload with data in the query string - window.location.href = [window.location.protocol, "//", window.location.host, window.location.port, window.location.pathname, "?", encodeURIComponent(JSON.stringify(data))].join(""); + window.location.href = [window.location.protocol, "//", window.location.host, window.location.pathname, "?", encodeURIComponent(JSON.stringify(data))].join(""); } else { @@ -48,7 +48,16 @@ loadStdev = Math.sqrt(loadStdev / data.length); launchStdev = Math.sqrt(launchStdev / data.length); - + + if (window.parent.BCOMM) { + window.parent.BCOMM.finishTest({ + loadAvg : loadAvg, + launchAvg : launchAvg, + loadStdev : loadStdev, + launchStdev : launchStdev + }); + } + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMakeZero() styleMask:CPBorderlessBridgeWindowMask], contentView = [theWindow contentView]; diff --git a/Tests/Manual/TableCibTest/Resources/MainMenu.cib b/Tests/Manual/TableCibTest/Resources/MainMenu.cib index 94613c585..b13ff6731 100644 --- a/Tests/Manual/TableCibTest/Resources/MainMenu.cib +++ b/Tests/Manual/TableCibTest/Resources/MainMenu.cib @@ -1 +1 @@ -280NPLIST;1.0;D;K;4;$topD;K;18;CPCibObjectDataKeyD;K;6;CP$UIDd;1;2E;E;K;8;$objectsA;S;5;$nullD;K;10;$classnameS;16;_CPCibObjectDataK;8;$classesA;S;16;_CPCibObjectDataS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;1E;K;28;_CPCibObjectDataNamesKeysKeyD;K;6;CP$UIDd;1;4E;K;30;_CPCibObjectDataNamesValuesKeyD;K;6;CP$UIDd;1;5E;K;30;_CPCibObjectDataClassesKeysKeyD;K;6;CP$UIDd;1;6E;K;32;_CPCibObjectDataClassesValuesKeyD;K;6;CP$UIDd;1;7E;K;30;_CPCibObjectDataConnectionsKeyD;K;6;CP$UIDd;1;8E;K;28;_CPCibObjectDataFrameworkKeyD;K;6;CP$UIDd;1;9E;K;26;_CPCibObjectDataNextOidKeyD;K;6;CP$UIDd;2;10E;K;30;_CPCibObjectDataObjectsKeysKeyD;K;6;CP$UIDd;2;11E;K;32;_CPCibObjectDataObjectsValuesKeyD;K;6;CP$UIDd;2;12E;K;26;_CPCibObjectDataOidKeysKeyD;K;6;CP$UIDd;2;13E;K;28;_CPCibObjectDataOidValuesKeyD;K;6;CP$UIDd;2;14E;K;28;_CPCibObjectDataFileOwnerKeyD;K;6;CP$UIDd;2;16E;K;33;_CPCibObjectDataVisibleWindowsKeyD;K;6;CP$UIDd;2;18E;E;D;K;10;$classnameS;7;CPArrayK;8;$classesA;S;7;CPArrayS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;20E;D;K;6;CP$UIDd;2;16E;D;K;6;CP$UIDd;2;22E;D;K;6;CP$UIDd;2;24E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;27E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;33E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;37E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;38E;D;K;6;CP$UIDd;2;39E;D;K;6;CP$UIDd;2;40E;D;K;6;CP$UIDd;2;41E;D;K;6;CP$UIDd;2;42E;D;K;6;CP$UIDd;2;43E;D;K;6;CP$UIDd;2;44E;D;K;6;CP$UIDd;2;45E;D;K;6;CP$UIDd;2;46E;D;K;6;CP$UIDd;2;47E;D;K;6;CP$UIDd;2;48E;D;K;6;CP$UIDd;2;49E;D;K;6;CP$UIDd;2;50E;D;K;6;CP$UIDd;2;51E;D;K;6;CP$UIDd;2;52E;D;K;6;CP$UIDd;2;53E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;55E;D;K;6;CP$UIDd;2;56E;D;K;6;CP$UIDd;2;57E;E;E;S;16;IBCocoaFrameworkD;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;20E;D;K;6;CP$UIDd;2;22E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;24E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;2;27E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;33E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;36E;D;K;6;CP$UIDd;2;37E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;24E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;2;33E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;16E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;27E;D;K;6;CP$UIDd;2;20E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;26E;D;K;6;CP$UIDd;2;16E;D;K;6;CP$UIDd;2;16E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;10;$classnameS;18;_CPCibCustomObjectK;8;$classesA;S;18;_CPCibCustomObjectS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;15E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;58E;E;D;K;10;$classnameS;5;CPSetK;8;$classesA;S;5;CPSetS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;17E;K;15;CPSetObjectsKeyD;K;6;CP$UIDd;2;59E;E;D;K;10;$classnameS;13;CPTableColumnK;8;$classesA;S;13;CPTableColumnS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;19E;K;26;CPTableColumnIdentifierKeyD;K;6;CP$UIDd;2;60E;K;21;CPTableColumnWidthKeyD;K;6;CP$UIDd;2;61E;K;24;CPTableColumnMinWidthKeyD;K;6;CP$UIDd;2;62E;K;24;CPTableColumnMaxWidthKeyD;K;6;CP$UIDd;2;63E;K;26;CPTableColumnHeaderViewKeyD;K;6;CP$UIDd;2;65E;K;24;CPTableColumnDataViewKeyD;K;6;CP$UIDd;2;67E;K;28;CPTableColumnResizingMaskKeyD;K;6;CP$UIDd;1;0E;E;D;K;10;$classnameS;17;CPTableHeaderViewK;8;$classesA;S;17;CPTableHeaderViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;21E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;68E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;69E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;69E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;68E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;K;29;CPTableHeaderViewTableViewKeyD;K;6;CP$UIDd;2;35E;E;D;K;10;$classnameS;6;CPViewK;8;$classesA;S;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;23E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;71E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;71E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;2;72E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;E;D;K;10;$classnameS;12;CPScrollViewK;8;$classesA;S;12;CPScrollViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;25E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;24E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;73E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;74E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;2;75E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;24E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;76E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;K;23;CPScrollViewContentViewD;K;6;CP$UIDd;2;78E;K;29;CPScrollViewHeaderClipViewKeyD;K;6;CP$UIDd;2;68E;K;21;CPScrollViewVScrollerD;K;6;CP$UIDd;2;32E;K;21;CPScrollViewHScrollerD;K;6;CP$UIDd;2;29E;K;23;CPScrollViewVLineScrollD;K;6;CP$UIDd;2;79E;K;23;CPScrollViewVPageScrollD;K;6;CP$UIDd;2;79E;K;23;CPScrollViewHLineScrollD;K;6;CP$UIDd;2;79E;K;23;CPScrollViewHPageScrollD;K;6;CP$UIDd;2;79E;K;24;CPScrollViewHasVScrollerD;K;6;CP$UIDd;2;80E;K;24;CPScrollViewHasHScrollerD;K;6;CP$UIDd;2;80E;K;29;CPScrollViewAutohidesScrollerD;K;6;CP$UIDd;2;80E;K;25;CPScrollViewCornerViewKeyD;K;6;CP$UIDd;2;82E;E;D;K;6;$classD;K;6;CP$UIDd;2;19E;K;26;CPTableColumnIdentifierKeyD;K;6;CP$UIDd;2;83E;K;21;CPTableColumnWidthKeyD;K;6;CP$UIDd;2;84E;K;24;CPTableColumnMinWidthKeyD;K;6;CP$UIDd;2;79E;K;24;CPTableColumnMaxWidthKeyD;K;6;CP$UIDd;2;85E;K;26;CPTableColumnHeaderViewKeyD;K;6;CP$UIDd;2;86E;K;24;CPTableColumnDataViewKeyD;K;6;CP$UIDd;2;87E;K;28;CPTableColumnResizingMaskKeyD;K;6;CP$UIDd;1;0E;E;D;K;10;$classnameS;10;CPScrollerK;8;$classesA;S;10;CPScrollerS;9;CPControlS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;28E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;26E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;88E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;89E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;26E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;90E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;91E;K;17;CPControlValueKeyD;K;6;CP$UIDd;2;92E;K;18;CPControlTargetKeyD;K;6;CP$UIDd;2;26E;K;18;CPControlActionKeyD;K;6;CP$UIDd;2;93E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;2;94E;K;21;CPScrollerControlSizeD;K;6;CP$UIDd;2;95E;K;24;CPScrollerKnobProportionD;K;6;CP$UIDd;2;96E;E;D;K;10;$classnameS;20;_CPCibWindowTemplateK;8;$classesA;S;20;_CPCibWindowTemplateS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;30E;K;30;_CPCibWindowTemplateMaxSizeKeyD;K;6;CP$UIDd;2;97E;K;32;_CPCibWindowTemplateViewClassKeyD;K;6;CP$UIDd;1;0E;K;34;_CPCibWindowTemplateWindowClassKeyD;K;6;CP$UIDd;2;98E;K;33;_CPCibWindowTemplateWindowRectKeyD;K;6;CP$UIDd;2;99E;K;30;_CPCibWindowTempatStyleMaskKeyD;K;6;CP$UIDd;3;100E;K;34;_CPCibWindowTemplateWindowTitleKeyD;K;6;CP$UIDd;3;101E;K;33;_CPCibWindowTemplateWindowViewKeyD;K;6;CP$UIDd;2;24E;E;D;K;6;$classD;K;6;CP$UIDd;2;28E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;26E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;102E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;103E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;26E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;90E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;104E;K;17;CPControlValueKeyD;K;6;CP$UIDd;2;95E;K;18;CPControlTargetKeyD;K;6;CP$UIDd;2;26E;K;18;CPControlActionKeyD;K;6;CP$UIDd;3;105E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;2;94E;K;21;CPScrollerControlSizeD;K;6;CP$UIDd;2;95E;K;24;CPScrollerKnobProportionD;K;6;CP$UIDd;3;106E;E;D;K;6;$classD;K;6;CP$UIDd;2;19E;K;26;CPTableColumnIdentifierKeyD;K;6;CP$UIDd;3;107E;K;21;CPTableColumnWidthKeyD;K;6;CP$UIDd;3;108E;K;24;CPTableColumnMinWidthKeyD;K;6;CP$UIDd;2;62E;K;24;CPTableColumnMaxWidthKeyD;K;6;CP$UIDd;2;63E;K;26;CPTableColumnHeaderViewKeyD;K;6;CP$UIDd;3;109E;K;24;CPTableColumnDataViewKeyD;K;6;CP$UIDd;3;110E;K;28;CPTableColumnResizingMaskKeyD;K;6;CP$UIDd;1;0E;E;D;K;10;$classnameS;11;CPTableViewK;8;$classesA;S;11;CPTableViewS;9;CPControlS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;34E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;78E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;111E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;111E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;78E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;2;94E;K;24;CPTableViewDataSourceKeyD;K;6;CP$UIDd;1;0E;K;22;CPTableViewDelegateKeyD;K;6;CP$UIDd;1;0E;K;23;CPTableViewRowHeightKeyD;K;6;CP$UIDd;3;112E;K;30;CPTableViewIntercellSpacingKeyD;K;6;CP$UIDd;3;113E;K;31;CPTableViewMultipleSelectionKeyD;K;6;CP$UIDd;3;114E;K;28;CPTableViewEmptySelectionKeyD;K;6;CP$UIDd;2;80E;K;30;CPTableViewColumnReorderingKeyD;K;6;CP$UIDd;2;80E;K;28;CPTableViewColumnResizingKeyD;K;6;CP$UIDd;2;80E;K;29;CPTableViewColumnSelectionKeyD;K;6;CP$UIDd;2;80E;K;26;CPTableViewTableColumnsKeyD;K;6;CP$UIDd;3;115E;K;23;CPTableViewGridColorKeyD;K;6;CP$UIDd;3;117E;K;27;CPTableViewGridStyleMaskKeyD;K;6;CP$UIDd;1;0E;K;39;CPTableViewUsesAlternatingBackgroundKeyD;K;6;CP$UIDd;2;80E;K;34;CPTableViewAlternatingRowColorsKeyD;K;6;CP$UIDd;3;118E;K;24;CPTableViewCornerViewKeyD;K;6;CP$UIDd;2;82E;K;24;CPTableViewHeaderViewKeyD;K;6;CP$UIDd;2;22E;E;D;K;6;$classD;K;6;CP$UIDd;2;15E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;58E;E;D;K;6;$classD;K;6;CP$UIDd;2;15E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;3;119E;E;S;16;Table Column (2)S;12;File's OwnerS;17;Table Header ViewS;12;Content ViewS;33;Bordered Scroll View (Table View)S;16;Table Column (3)S;19;Horizontal ScrollerS;15;Window (Window)S;29;Text Field Cell (Text Cell)-1S;29;Text Field Cell (Text Cell)-2S;27;Text Field Cell (Text Cell)S;17;Vertical ScrollerS;16;Table Column (1)S;29;Table View (Column 1, Two, 3)S;11;ApplicationS;14;App ControllerD;K;10;$classnameS;20;CPCibOutletConnectorK;8;$classesA;S;20;CPCibOutletConnectorS;14;CPCibConnectorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;54E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;16E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;37E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;3;120E;E;D;K;6;$classD;K;6;CP$UIDd;2;54E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;37E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;31E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;3;121E;E;D;K;6;$classD;K;6;CP$UIDd;2;54E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;35E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;37E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;3;122E;E;S;13;CPApplicationD;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;31E;E;E;S;1;2d;3;154d;2;40d;4;1000D;K;10;$classnameS;24;_CPTableColumnHeaderViewK;8;$classesA;S;24;_CPTableColumnHeaderViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;64E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;123E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;123E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;124E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;K;38;_CPTableColumnHeaderViewStringValueKeyD;K;6;CP$UIDd;3;125E;K;32;_CPTableColumnHeaderViewImageKeyD;K;6;CP$UIDd;1;0E;K;31;_CPTableColumnHeaderViewFontKeyD;K;6;CP$UIDd;3;127E;E;D;K;10;$classnameS;11;CPTextFieldK;8;$classesA;S;11;CPTextFieldS;9;CPControlS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;66E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;123E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;123E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;128E;K;12;$atext-colorD;K;6;CP$UIDd;3;130E;K;11;$aalignmentD;K;6;CP$UIDd;2;95E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;131E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;132E;K;24;CPTextFieldIsEditableKeyD;K;6;CP$UIDd;1;0E;K;26;CPTextFieldIsSelectableKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldDrawsBackgroundKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldBackgroundColorKeyD;K;6;CP$UIDd;1;0E;K;31;CPTextFieldPlaceholderStringKeyD;K;6;CP$UIDd;3;131E;E;D;K;6;$classD;K;6;CP$UIDd;2;77E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;26E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;136E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;137E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;138E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;26E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;90E;K;21;CPViewBackgroundColorD;K;6;CP$UIDd;3;135E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;K;24;CPScrollViewDocumentViewD;K;6;CP$UIDd;2;22E;E;S;19;{{0, 0}, {516, 23}}S;6;normalS;20;{{0, 0}, {558, 348}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;26E;E;E;S;22;{{20, 20}, {518, 308}}S;20;{{0, 0}, {518, 308}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;78E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;68E;D;K;6;CP$UIDd;2;82E;E;E;d;2;36D;K;10;$classnameS;10;CPClipViewK;8;$classesA;S;10;CPClipViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;77E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;26E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;133E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;111E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;134E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;26E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;90E;K;21;CPViewBackgroundColorD;K;6;CP$UIDd;3;135E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;K;24;CPScrollViewDocumentViewD;K;6;CP$UIDd;2;35E;E;d;2;10T;D;K;10;$classnameS;13;_CPCornerViewK;8;$classesA;S;13;_CPCornerViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;81E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;26E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;139E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;140E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;26E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;90E;K;21;CPViewBackgroundColorD;K;6;CP$UIDd;3;141E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;E;S;1;3d;3;188d;22;3.4028234663852886e+38D;K;6;$classD;K;6;CP$UIDd;2;64E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;123E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;123E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;142E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;K;38;_CPTableColumnHeaderViewStringValueKeyD;K;6;CP$UIDd;2;83E;K;32;_CPTableColumnHeaderViewImageKeyD;K;6;CP$UIDd;1;0E;K;31;_CPTableColumnHeaderViewFontKeyD;K;6;CP$UIDd;3;127E;E;D;K;6;$classD;K;6;CP$UIDd;2;66E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;123E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;123E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;128E;K;12;$atext-colorD;K;6;CP$UIDd;3;143E;K;11;$aalignmentD;K;6;CP$UIDd;2;95E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;131E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;132E;K;24;CPTextFieldIsEditableKeyD;K;6;CP$UIDd;1;0E;K;26;CPTextFieldIsSelectableKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldDrawsBackgroundKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldBackgroundColorKeyD;K;6;CP$UIDd;1;0E;K;31;CPTextFieldPlaceholderStringKeyD;K;6;CP$UIDd;3;131E;E;S;19;{{1, 1}, {516, 15}}S;19;{{0, 0}, {516, 15}}d;1;8S;8;disabledd;1;1S;29;_horizontalScrollerDidScroll:d;1;4d;1;0f;18;0.9866156787762906S;28;{1.79769e+308, 1.79769e+308}S;8;CPWindowS;24;{{335, 128}, {558, 348}}d;1;7S;6;WindowS;22;{{502, 16}, {15, 275}}S;19;{{0, 0}, {15, 275}}S;17;disabled+verticalS;27;_verticalScrollerDidScroll:f;18;0.9482758620689655S;1;1d;3;101D;K;6;$classD;K;6;CP$UIDd;2;64E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;123E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;123E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;144E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;K;38;_CPTableColumnHeaderViewStringValueKeyD;K;6;CP$UIDd;3;145E;K;32;_CPTableColumnHeaderViewImageKeyD;K;6;CP$UIDd;1;0E;K;31;_CPTableColumnHeaderViewFontKeyD;K;6;CP$UIDd;3;127E;E;D;K;6;$classD;K;6;CP$UIDd;2;66E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;123E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;123E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;128E;K;12;$atext-colorD;K;6;CP$UIDd;3;146E;K;11;$aalignmentD;K;6;CP$UIDd;2;95E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;131E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;132E;K;24;CPTextFieldIsEditableKeyD;K;6;CP$UIDd;1;0E;K;26;CPTextFieldIsSelectableKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldDrawsBackgroundKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldBackgroundColorKeyD;K;6;CP$UIDd;1;0E;K;31;CPTextFieldPlaceholderStringKeyD;K;6;CP$UIDd;3;131E;E;S;20;{{0, 0}, {516, 290}}d;2;23S;6;{0, 0}F;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;33E;D;K;6;CP$UIDd;2;20E;D;K;6;CP$UIDd;2;27E;E;E;D;K;10;$classnameS;7;CPColorK;8;$classesA;S;7;CPColorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;116E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;147E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;135E;D;K;6;CP$UIDd;3;148E;E;E;S;13;AppControllerS;8;delegateS;9;theWindowS;10;dataSourceS;16;{{0, 0}, {0, 0}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;150E;E;E;S;3;TwoD;K;10;$classnameS;6;CPFontK;8;$classesA;S;6;CPFontS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;126E;K;13;CPFontNameKeyD;K;6;CP$UIDd;3;151E;K;13;CPFontSizeKeyD;K;6;CP$UIDd;3;152E;K;15;CPFontIsBoldKeyD;K;6;CP$UIDd;3;114E;E;S;11;placeholderD;K;10;$classnameS;17;_CPThemeAttributeK;8;$classesA;S;17;_CPThemeAttributeS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;129E;K;4;nameD;K;6;CP$UIDd;3;153E;K;5;stateD;K;6;CP$UIDd;3;154E;K;5;valueD;K;6;CP$UIDd;3;135E;E;S;0;d;4;3072S;20;{{1, 1}, {516, 290}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;35E;E;E;D;K;6;$classD;K;6;CP$UIDd;3;116E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;155E;E;S;21;{{1, 291}, {516, 17}}S;19;{{0, 0}, {516, 17}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;22E;E;E;S;22;{{502, 285}, {16, 23}}S;18;{{0, 0}, {16, 23}}D;K;6;$classD;K;6;CP$UIDd;3;116E;K;22;CPColorPatternImageKeyD;K;6;CP$UIDd;3;157E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;158E;E;E;D;K;6;$classD;K;6;CP$UIDd;3;129E;K;4;nameD;K;6;CP$UIDd;3;153E;K;5;stateD;K;6;CP$UIDd;3;154E;K;5;valueD;K;6;CP$UIDd;3;135E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;159E;E;E;S;8;Column 1D;K;6;$classD;K;6;CP$UIDd;3;129E;K;4;nameD;K;6;CP$UIDd;3;153E;K;5;stateD;K;6;CP$UIDd;3;154E;K;5;valueD;K;6;CP$UIDd;3;135E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;160E;D;K;6;CP$UIDd;3;160E;D;K;6;CP$UIDd;3;160E;D;K;6;CP$UIDd;2;92E;E;E;D;K;6;$classD;K;6;CP$UIDd;3;116E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;161E;E;D;K;10;$classnameS;19;_CPImageAndTextViewK;8;$classesA;S;19;_CPImageAndTextViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;149E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;65E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;162E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;163E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;65E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;164E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;E;S;5;Ariald;2;11S;10;text-colorS;8;selectedD;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;92E;D;K;6;CP$UIDd;2;92E;D;K;6;CP$UIDd;2;92E;D;K;6;CP$UIDd;2;92E;E;E;D;K;10;$classnameS;7;CPImageK;8;$classesA;S;7;CPImageS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;156E;K;10;CPFilenameD;K;6;CP$UIDd;3;165E;K;6;CPSizeD;K;6;CP$UIDd;3;166E;E;D;K;6;$classD;K;6;CP$UIDd;3;149E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;86E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;162E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;163E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;86E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;164E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;E;D;K;6;$classD;K;6;CP$UIDd;3;149E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;3;109E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;162E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;163E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;3;109E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;164E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;70E;E;f;3;0.5D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;167E;D;K;6;CP$UIDd;3;168E;D;K;6;CP$UIDd;2;92E;D;K;6;CP$UIDd;2;92E;E;E;S;19;{{5, 1}, {-10, -1}}S;19;{{0, 0}, {-10, -1}}d;2;18S;102;file:/usr/local/narwhal/packages/cappuccino/Frameworks/Debug/AppKit/Resources/tableview-headerview.pngS;7;{1, 23}f;18;0.8941176470588236f;18;0.9058823529411765E;K;9;$archiverS;15;CPKeyedArchiverK;8;$versionS;6;100000E; \ No newline at end of file +280NPLIST;1.0;D;K;4;$topD;K;18;CPCibObjectDataKeyD;K;6;CP$UIDd;1;2E;E;K;8;$objectsA;S;5;$nullD;K;10;$classnameS;16;_CPCibObjectDataK;8;$classesA;S;16;_CPCibObjectDataS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;1E;K;28;_CPCibObjectDataNamesKeysKeyD;K;6;CP$UIDd;1;4E;K;30;_CPCibObjectDataNamesValuesKeyD;K;6;CP$UIDd;1;5E;K;30;_CPCibObjectDataClassesKeysKeyD;K;6;CP$UIDd;1;6E;K;32;_CPCibObjectDataClassesValuesKeyD;K;6;CP$UIDd;1;7E;K;30;_CPCibObjectDataConnectionsKeyD;K;6;CP$UIDd;1;8E;K;28;_CPCibObjectDataFrameworkKeyD;K;6;CP$UIDd;1;9E;K;26;_CPCibObjectDataNextOidKeyD;K;6;CP$UIDd;2;10E;K;30;_CPCibObjectDataObjectsKeysKeyD;K;6;CP$UIDd;2;11E;K;32;_CPCibObjectDataObjectsValuesKeyD;K;6;CP$UIDd;2;12E;K;26;_CPCibObjectDataOidKeysKeyD;K;6;CP$UIDd;2;13E;K;28;_CPCibObjectDataOidValuesKeyD;K;6;CP$UIDd;2;14E;K;28;_CPCibObjectDataFileOwnerKeyD;K;6;CP$UIDd;2;16E;K;33;_CPCibObjectDataVisibleWindowsKeyD;K;6;CP$UIDd;2;18E;E;D;K;10;$classnameS;7;CPArrayK;8;$classesA;S;7;CPArrayS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;19E;D;K;6;CP$UIDd;2;16E;D;K;6;CP$UIDd;2;21E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;2;25E;D;K;6;CP$UIDd;2;27E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;33E;D;K;6;CP$UIDd;2;34E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;37E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;38E;D;K;6;CP$UIDd;2;39E;D;K;6;CP$UIDd;2;40E;D;K;6;CP$UIDd;2;41E;D;K;6;CP$UIDd;2;42E;D;K;6;CP$UIDd;2;43E;D;K;6;CP$UIDd;2;44E;D;K;6;CP$UIDd;2;45E;D;K;6;CP$UIDd;2;46E;D;K;6;CP$UIDd;2;47E;D;K;6;CP$UIDd;2;48E;D;K;6;CP$UIDd;2;49E;D;K;6;CP$UIDd;2;50E;D;K;6;CP$UIDd;2;51E;D;K;6;CP$UIDd;2;52E;D;K;6;CP$UIDd;2;53E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;55E;D;K;6;CP$UIDd;2;56E;D;K;6;CP$UIDd;2;57E;E;E;S;16;IBCocoaFrameworkD;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;19E;D;K;6;CP$UIDd;2;21E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;25E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;27E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;1;0E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;33E;D;K;6;CP$UIDd;2;34E;D;K;6;CP$UIDd;2;37E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;16E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;27E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;16E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;2;25E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;21E;D;K;6;CP$UIDd;2;29E;D;K;6;CP$UIDd;2;16E;D;K;6;CP$UIDd;2;23E;D;K;6;CP$UIDd;2;23E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;E;E;D;K;10;$classnameS;18;_CPCibCustomObjectK;8;$classesA;S;18;_CPCibCustomObjectS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;15E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;58E;E;D;K;10;$classnameS;5;CPSetK;8;$classesA;S;5;CPSetS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;17E;K;15;CPSetObjectsKeyD;K;6;CP$UIDd;2;59E;E;D;K;6;$classD;K;6;CP$UIDd;2;15E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;60E;E;D;K;10;$classnameS;13;CPTableColumnK;8;$classesA;S;13;CPTableColumnS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;20E;K;26;CPTableColumnIdentifierKeyD;K;6;CP$UIDd;2;61E;K;21;CPTableColumnWidthKeyD;K;6;CP$UIDd;2;62E;K;24;CPTableColumnMinWidthKeyD;K;6;CP$UIDd;2;63E;K;24;CPTableColumnMaxWidthKeyD;K;6;CP$UIDd;2;64E;K;26;CPTableColumnHeaderViewKeyD;K;6;CP$UIDd;2;66E;K;24;CPTableColumnDataViewKeyD;K;6;CP$UIDd;2;68E;K;28;CPTableColumnResizingMaskKeyD;K;6;CP$UIDd;2;69E;K;24;CPTableColumnIsHiddenKeyD;K;6;CP$UIDd;1;0E;K;28;CPSortDescriptorPrototypeKeyD;K;6;CP$UIDd;1;0E;E;D;K;10;$classnameS;12;CPScrollViewK;8;$classesA;S;12;CPScrollViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;22E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;27E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;70E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;71E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;2;72E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;27E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;2;73E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;K;23;CPScrollViewContentViewD;K;6;CP$UIDd;2;76E;K;29;CPScrollViewHeaderClipViewKeyD;K;6;CP$UIDd;2;77E;K;21;CPScrollViewVScrollerD;K;6;CP$UIDd;2;34E;K;21;CPScrollViewHScrollerD;K;6;CP$UIDd;2;31E;K;23;CPScrollViewVLineScrollD;K;6;CP$UIDd;2;63E;K;23;CPScrollViewVPageScrollD;K;6;CP$UIDd;2;63E;K;23;CPScrollViewHLineScrollD;K;6;CP$UIDd;2;63E;K;23;CPScrollViewHPageScrollD;K;6;CP$UIDd;2;63E;K;24;CPScrollViewHasVScrollerD;K;6;CP$UIDd;2;78E;K;24;CPScrollViewHasHScrollerD;K;6;CP$UIDd;2;78E;K;29;CPScrollViewAutohidesScrollerD;K;6;CP$UIDd;2;78E;K;25;CPScrollViewCornerViewKeyD;K;6;CP$UIDd;2;80E;E;D;K;10;$classnameS;20;_CPCibWindowTemplateK;8;$classesA;S;20;_CPCibWindowTemplateS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;24E;K;30;_CPCibWindowTemplateMaxSizeKeyD;K;6;CP$UIDd;2;81E;K;32;_CPCibWindowTemplateViewClassKeyD;K;6;CP$UIDd;1;0E;K;34;_CPCibWindowTemplateWindowClassKeyD;K;6;CP$UIDd;2;82E;K;33;_CPCibWindowTemplateWindowRectKeyD;K;6;CP$UIDd;2;83E;K;30;_CPCibWindowTempatStyleMaskKeyD;K;6;CP$UIDd;2;84E;K;34;_CPCibWindowTemplateWindowTitleKeyD;K;6;CP$UIDd;2;85E;K;33;_CPCibWindowTemplateWindowViewKeyD;K;6;CP$UIDd;2;27E;E;D;K;10;$classnameS;6;CPViewK;8;$classesA;S;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;26E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;86E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;86E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;2;87E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;E;D;K;10;$classnameS;11;CPTableViewK;8;$classesA;S;11;CPTableViewS;9;CPControlS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;28E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;76E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;88E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;88E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;76E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;2;89E;K;24;CPTableViewDataSourceKeyD;K;6;CP$UIDd;1;0E;K;22;CPTableViewDelegateKeyD;K;6;CP$UIDd;1;0E;K;23;CPTableViewRowHeightKeyD;K;6;CP$UIDd;2;90E;K;30;CPTableViewIntercellSpacingKeyD;K;6;CP$UIDd;2;91E;K;31;CPTableViewMultipleSelectionKeyD;K;6;CP$UIDd;2;92E;K;28;CPTableViewEmptySelectionKeyD;K;6;CP$UIDd;2;78E;K;30;CPTableViewColumnReorderingKeyD;K;6;CP$UIDd;2;78E;K;28;CPTableViewColumnResizingKeyD;K;6;CP$UIDd;2;78E;K;29;CPTableViewColumnSelectionKeyD;K;6;CP$UIDd;2;78E;K;26;CPTableViewTableColumnsKeyD;K;6;CP$UIDd;2;93E;K;23;CPTableViewGridColorKeyD;K;6;CP$UIDd;2;95E;K;27;CPTableViewGridStyleMaskKeyD;K;6;CP$UIDd;2;96E;K;39;CPTableViewUsesAlternatingBackgroundKeyD;K;6;CP$UIDd;2;78E;K;34;CPTableViewAlternatingRowColorsKeyD;K;6;CP$UIDd;2;97E;K;24;CPTableViewCornerViewKeyD;K;6;CP$UIDd;2;80E;K;24;CPTableViewHeaderViewKeyD;K;6;CP$UIDd;2;37E;E;D;K;10;$classnameS;10;CPScrollerK;8;$classesA;S;10;CPScrollerS;9;CPControlS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;30E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;23E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;2;98E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;99E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;23E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;100E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;101E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;102E;K;18;CPControlTargetKeyD;K;6;CP$UIDd;2;23E;K;18;CPControlActionKeyD;K;6;CP$UIDd;3;103E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;2;89E;K;21;CPScrollerControlSizeD;K;6;CP$UIDd;3;104E;K;24;CPScrollerKnobProportionD;K;6;CP$UIDd;3;105E;E;D;K;6;$classD;K;6;CP$UIDd;2;20E;K;26;CPTableColumnIdentifierKeyD;K;6;CP$UIDd;3;106E;K;21;CPTableColumnWidthKeyD;K;6;CP$UIDd;3;107E;K;24;CPTableColumnMinWidthKeyD;K;6;CP$UIDd;3;108E;K;24;CPTableColumnMaxWidthKeyD;K;6;CP$UIDd;3;109E;K;26;CPTableColumnHeaderViewKeyD;K;6;CP$UIDd;3;110E;K;24;CPTableColumnDataViewKeyD;K;6;CP$UIDd;3;111E;K;28;CPTableColumnResizingMaskKeyD;K;6;CP$UIDd;2;69E;K;24;CPTableColumnIsHiddenKeyD;K;6;CP$UIDd;1;0E;K;28;CPSortDescriptorPrototypeKeyD;K;6;CP$UIDd;1;0E;E;D;K;6;$classD;K;6;CP$UIDd;2;15E;K;27;_CPCibCustomObjectClassNameD;K;6;CP$UIDd;2;58E;E;D;K;6;$classD;K;6;CP$UIDd;2;30E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;23E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;112E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;113E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;23E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;100E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;114E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;104E;K;18;CPControlTargetKeyD;K;6;CP$UIDd;2;23E;K;18;CPControlActionKeyD;K;6;CP$UIDd;3;115E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;2;89E;K;21;CPScrollerControlSizeD;K;6;CP$UIDd;3;104E;K;24;CPScrollerKnobProportionD;K;6;CP$UIDd;3;116E;E;D;K;6;$classD;K;6;CP$UIDd;2;20E;K;26;CPTableColumnIdentifierKeyD;K;6;CP$UIDd;3;117E;K;21;CPTableColumnWidthKeyD;K;6;CP$UIDd;3;118E;K;24;CPTableColumnMinWidthKeyD;K;6;CP$UIDd;3;108E;K;24;CPTableColumnMaxWidthKeyD;K;6;CP$UIDd;3;109E;K;26;CPTableColumnHeaderViewKeyD;K;6;CP$UIDd;3;119E;K;24;CPTableColumnDataViewKeyD;K;6;CP$UIDd;3;120E;K;28;CPTableColumnResizingMaskKeyD;K;6;CP$UIDd;2;69E;K;24;CPTableColumnIsHiddenKeyD;K;6;CP$UIDd;1;0E;K;28;CPSortDescriptorPrototypeKeyD;K;6;CP$UIDd;1;0E;E;D;K;10;$classnameS;17;CPTableHeaderViewK;8;$classesA;S;17;CPTableHeaderViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;36E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;77E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;121E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;121E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;77E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;K;29;CPTableHeaderViewTableViewKeyD;K;6;CP$UIDd;2;29E;E;S;14;App ControllerS;12;File's OwnerS;16;Table Column (3)S;33;Bordered Scroll View (Table View)S;15;Window (Window)S;12;Content ViewS;29;Table View (Column 1, Two, 3)S;19;Horizontal ScrollerS;29;Text Field Cell (Text Cell)-2S;29;Text Field Cell (Text Cell)-1S;16;Table Column (1)S;27;Text Field Cell (Text Cell)S;11;ApplicationS;17;Vertical ScrollerS;16;Table Column (2)S;17;Table Header ViewD;K;10;$classnameS;20;CPCibOutletConnectorK;8;$classesA;S;20;CPCibOutletConnectorS;14;CPCibConnectorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;54E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;16E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;19E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;3;122E;E;D;K;6;$classD;K;6;CP$UIDd;2;54E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;19E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;25E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;3;123E;E;D;K;6;$classD;K;6;CP$UIDd;2;54E;K;24;_CPCibConnectorSourceKeyD;K;6;CP$UIDd;2;29E;K;29;_CPCibConnectorDestinationKeyD;K;6;CP$UIDd;2;19E;K;23;_CPCibConnectorLabelKeyD;K;6;CP$UIDd;3;124E;E;S;13;CPApplicationD;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;25E;E;E;S;13;AppControllerS;1;3d;3;188d;2;10d;22;3.4028234663852886e+38D;K;10;$classnameS;24;_CPTableColumnHeaderViewK;8;$classesA;S;24;_CPTableColumnHeaderViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;65E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;125E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;125E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;126E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;K;38;_CPTableColumnHeaderViewStringValueKeyD;K;6;CP$UIDd;2;61E;K;32;_CPTableColumnHeaderViewImageKeyD;K;6;CP$UIDd;1;0E;K;31;_CPTableColumnHeaderViewFontKeyD;K;6;CP$UIDd;3;128E;E;D;K;10;$classnameS;11;CPTextFieldK;8;$classesA;S;11;CPTextFieldS;9;CPControlS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;67E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;125E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;125E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;129E;K;6;$afontD;K;6;CP$UIDd;3;131E;K;12;$atext-colorD;K;6;CP$UIDd;3;132E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;89E;K;20;$avertical-alignmentD;K;6;CP$UIDd;2;69E;K;11;$aalignmentD;K;6;CP$UIDd;3;104E;K;15;$acontent-insetD;K;6;CP$UIDd;3;134E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;135E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;136E;K;24;CPTextFieldIsEditableKeyD;K;6;CP$UIDd;1;0E;K;26;CPTextFieldIsSelectableKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldDrawsBackgroundKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldBackgroundColorKeyD;K;6;CP$UIDd;1;0E;K;31;CPTextFieldPlaceholderStringKeyD;K;6;CP$UIDd;3;135E;E;d;1;2S;22;{{20, 20}, {518, 308}}S;20;{{0, 0}, {518, 308}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;76E;D;K;6;CP$UIDd;2;34E;D;K;6;CP$UIDd;2;31E;D;K;6;CP$UIDd;2;77E;D;K;6;CP$UIDd;2;80E;E;E;d;2;36S;6;normalD;K;10;$classnameS;10;CPClipViewK;8;$classesA;S;10;CPClipViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;75E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;23E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;137E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;2;88E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;138E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;23E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;100E;K;21;CPViewBackgroundColorD;K;6;CP$UIDd;3;139E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;K;24;CPScrollViewDocumentViewD;K;6;CP$UIDd;2;29E;E;D;K;6;$classD;K;6;CP$UIDd;2;75E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;23E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;140E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;141E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;142E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;23E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;100E;K;21;CPViewBackgroundColorD;K;6;CP$UIDd;3;139E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;K;24;CPScrollViewDocumentViewD;K;6;CP$UIDd;2;37E;E;T;D;K;10;$classnameS;13;_CPCornerViewK;8;$classesA;S;13;_CPCornerViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;79E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;23E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;143E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;144E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;23E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;100E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;E;S;28;{1.79769e+308, 1.79769e+308}S;8;CPWindowS;24;{{335, 128}, {558, 348}}d;1;7S;6;WindowS;20;{{0, 0}, {558, 348}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;23E;E;E;S;20;{{0, 0}, {516, 290}}d;1;4d;2;23S;6;{0, 0}F;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;32E;D;K;6;CP$UIDd;2;35E;D;K;6;CP$UIDd;2;21E;E;E;D;K;10;$classnameS;7;CPColorK;8;$classesA;S;7;CPColorS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;2;94E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;145E;E;d;1;3D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;139E;D;K;6;CP$UIDd;3;146E;E;E;S;19;{{1, 1}, {516, 15}}S;19;{{0, 0}, {516, 15}}d;1;8S;8;disabledd;1;1S;29;_horizontalScrollerDidScroll:d;1;0f;18;0.9866156787762906S;1;1d;3;101d;2;40d;4;1000D;K;6;$classD;K;6;CP$UIDd;2;65E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;125E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;125E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;147E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;K;38;_CPTableColumnHeaderViewStringValueKeyD;K;6;CP$UIDd;3;148E;K;32;_CPTableColumnHeaderViewImageKeyD;K;6;CP$UIDd;1;0E;K;31;_CPTableColumnHeaderViewFontKeyD;K;6;CP$UIDd;3;128E;E;D;K;6;$classD;K;6;CP$UIDd;2;67E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;125E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;125E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;129E;K;6;$afontD;K;6;CP$UIDd;3;149E;K;12;$atext-colorD;K;6;CP$UIDd;3;150E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;89E;K;20;$avertical-alignmentD;K;6;CP$UIDd;2;69E;K;11;$aalignmentD;K;6;CP$UIDd;3;104E;K;15;$acontent-insetD;K;6;CP$UIDd;3;151E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;135E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;136E;K;24;CPTextFieldIsEditableKeyD;K;6;CP$UIDd;1;0E;K;26;CPTextFieldIsSelectableKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldDrawsBackgroundKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldBackgroundColorKeyD;K;6;CP$UIDd;1;0E;K;31;CPTextFieldPlaceholderStringKeyD;K;6;CP$UIDd;3;135E;E;S;22;{{502, 16}, {15, 275}}S;19;{{0, 0}, {15, 275}}S;17;disabled+verticalS;27;_verticalScrollerDidScroll:f;18;0.9482758620689655S;1;2d;3;154D;K;6;$classD;K;6;CP$UIDd;2;65E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;125E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;125E;K;17;CPViewSubviewsKeyD;K;6;CP$UIDd;3;152E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;K;38;_CPTableColumnHeaderViewStringValueKeyD;K;6;CP$UIDd;3;153E;K;32;_CPTableColumnHeaderViewImageKeyD;K;6;CP$UIDd;1;0E;K;31;_CPTableColumnHeaderViewFontKeyD;K;6;CP$UIDd;3;128E;E;D;K;6;$classD;K;6;CP$UIDd;2;67E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;125E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;125E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;3;129E;K;6;$afontD;K;6;CP$UIDd;3;154E;K;12;$atext-colorD;K;6;CP$UIDd;3;155E;K;17;$aline-break-modeD;K;6;CP$UIDd;2;89E;K;20;$avertical-alignmentD;K;6;CP$UIDd;2;69E;K;11;$aalignmentD;K;6;CP$UIDd;3;104E;K;15;$acontent-insetD;K;6;CP$UIDd;3;156E;K;17;CPControlValueKeyD;K;6;CP$UIDd;3;135E;K;24;CPControlSendActionOnKeyD;K;6;CP$UIDd;3;136E;K;24;CPTextFieldIsEditableKeyD;K;6;CP$UIDd;1;0E;K;26;CPTextFieldIsSelectableKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldDrawsBackgroundKeyD;K;6;CP$UIDd;1;0E;K;29;CPTextFieldBackgroundColorKeyD;K;6;CP$UIDd;1;0E;K;31;CPTextFieldPlaceholderStringKeyD;K;6;CP$UIDd;3;135E;E;S;19;{{0, 0}, {516, 23}}S;8;delegateS;9;theWindowS;10;dataSourceS;16;{{0, 0}, {0, 0}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;158E;E;E;D;K;10;$classnameS;6;CPFontK;8;$classesA;S;6;CPFontS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;127E;K;13;CPFontNameKeyD;K;6;CP$UIDd;3;159E;K;13;CPFontSizeKeyD;K;6;CP$UIDd;3;160E;K;15;CPFontIsBoldKeyD;K;6;CP$UIDd;2;78E;E;S;11;placeholderD;K;10;$classnameS;17;_CPThemeAttributeK;8;$classesA;S;17;_CPThemeAttributeS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;130E;K;4;nameD;K;6;CP$UIDd;3;161E;K;5;stateD;K;6;CP$UIDd;3;162E;K;5;valueD;K;6;CP$UIDd;3;128E;E;D;K;6;$classD;K;6;CP$UIDd;3;130E;K;4;nameD;K;6;CP$UIDd;3;163E;K;6;valuesD;K;6;CP$UIDd;3;165E;E;D;K;10;$classnameS;21;_CPKeyedArchiverValueK;8;$classesA;S;21;_CPKeyedArchiverValueS;7;CPValueS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;133E;K;15;CPValueValueKeyD;K;6;CP$UIDd;3;166E;E;S;0;d;4;3072S;20;{{1, 1}, {516, 290}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;29E;E;E;D;K;6;$classD;K;6;CP$UIDd;2;94E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;167E;E;S;21;{{1, 291}, {516, 17}}S;19;{{0, 0}, {516, 17}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;2;37E;E;E;S;22;{{502, 285}, {16, 23}}S;18;{{0, 0}, {16, 23}}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;168E;D;K;6;CP$UIDd;3;168E;D;K;6;CP$UIDd;3;168E;D;K;6;CP$UIDd;3;102E;E;E;D;K;6;$classD;K;6;CP$UIDd;2;94E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;169E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;170E;E;E;S;8;Column 1D;K;6;$classD;K;6;CP$UIDd;3;130E;K;4;nameD;K;6;CP$UIDd;3;161E;K;5;stateD;K;6;CP$UIDd;3;162E;K;5;valueD;K;6;CP$UIDd;3;128E;E;D;K;6;$classD;K;6;CP$UIDd;3;130E;K;4;nameD;K;6;CP$UIDd;3;163E;K;6;valuesD;K;6;CP$UIDd;3;171E;E;D;K;6;$classD;K;6;CP$UIDd;3;133E;K;15;CPValueValueKeyD;K;6;CP$UIDd;3;166E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;172E;E;E;S;3;TwoD;K;6;$classD;K;6;CP$UIDd;3;130E;K;4;nameD;K;6;CP$UIDd;3;161E;K;5;stateD;K;6;CP$UIDd;3;162E;K;5;valueD;K;6;CP$UIDd;3;128E;E;D;K;6;$classD;K;6;CP$UIDd;3;130E;K;4;nameD;K;6;CP$UIDd;3;163E;K;6;valuesD;K;6;CP$UIDd;3;173E;E;D;K;6;$classD;K;6;CP$UIDd;3;133E;K;15;CPValueValueKeyD;K;6;CP$UIDd;3;166E;E;D;K;10;$classnameS;19;_CPImageAndTextViewK;8;$classesA;S;19;_CPImageAndTextViewS;6;CPViewS;11;CPResponderS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;157E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;2;66E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;174E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;175E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;2;66E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;176E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;E;S;17;Arial, sans-serifd;2;12S;4;fontS;16;selectedDataViewS;10;text-colorD;K;10;$classnameS;19;CPMutableDictionaryK;8;$classesA;S;19;CPMutableDictionaryS;12;CPDictionaryS;8;CPObjectE;E;D;K;6;$classD;K;6;CP$UIDd;3;164E;K;10;CP.objectsD;K;16;selectedDataViewD;K;6;CP$UIDd;3;139E;K;6;normalD;K;6;CP$UIDd;3;177E;E;E;S;39;{"top":0,"right":0,"bottom":0,"left":5}D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;102E;D;K;6;CP$UIDd;3;102E;D;K;6;CP$UIDd;3;102E;D;K;6;CP$UIDd;3;102E;E;E;f;18;0.8980392156862745D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;178E;D;K;6;CP$UIDd;3;179E;D;K;6;CP$UIDd;3;180E;D;K;6;CP$UIDd;3;102E;E;E;D;K;6;$classD;K;6;CP$UIDd;3;157E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;3;110E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;174E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;175E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;3;110E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;176E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;E;D;K;6;$classD;K;6;CP$UIDd;3;164E;K;10;CP.objectsD;K;16;selectedDataViewD;K;6;CP$UIDd;3;139E;K;6;normalD;K;6;CP$UIDd;3;181E;E;E;D;K;6;$classD;K;6;CP$UIDd;3;157E;K;27;CPResponderNextResponderKeyD;K;6;CP$UIDd;3;119E;K;14;CPViewFrameKeyD;K;6;CP$UIDd;3;174E;K;15;CPViewBoundsKeyD;K;6;CP$UIDd;3;175E;K;18;CPViewSuperviewKeyD;K;6;CP$UIDd;3;119E;K;22;CPViewAutoresizingMaskD;K;6;CP$UIDd;3;176E;K;19;CPViewThemeStateKeyD;K;6;CP$UIDd;2;74E;E;D;K;6;$classD;K;6;CP$UIDd;3;164E;K;10;CP.objectsD;K;16;selectedDataViewD;K;6;CP$UIDd;3;139E;K;6;normalD;K;6;CP$UIDd;3;182E;E;E;S;18;{{5, 0}, {-10, 0}}S;18;{{0, 0}, {-10, 0}}d;2;18D;K;6;$classD;K;6;CP$UIDd;2;94E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;183E;E;f;18;0.9607843137254902f;18;0.9764705882352941f;18;0.9882352941176471D;K;6;$classD;K;6;CP$UIDd;2;94E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;184E;E;D;K;6;$classD;K;6;CP$UIDd;2;94E;K;20;CPColorComponentsKeyD;K;6;CP$UIDd;3;185E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;186E;D;K;6;CP$UIDd;3;186E;D;K;6;CP$UIDd;3;186E;D;K;6;CP$UIDd;3;102E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;186E;D;K;6;CP$UIDd;3;186E;D;K;6;CP$UIDd;3;186E;D;K;6;CP$UIDd;3;102E;E;E;D;K;6;$classD;K;6;CP$UIDd;1;3E;K;10;CP.objectsA;D;K;6;CP$UIDd;3;186E;D;K;6;CP$UIDd;3;186E;D;K;6;CP$UIDd;3;186E;D;K;6;CP$UIDd;3;102E;E;E;f;3;0.2E;K;9;$archiverS;15;CPKeyedArchiverK;8;$versionS;6;100000E; \ No newline at end of file diff --git a/Tests/Manual/TableCibTest/Resources/MainMenu.xib b/Tests/Manual/TableCibTest/Resources/MainMenu.xib index 01d7aae19..42934a3ea 100644 --- a/Tests/Manual/TableCibTest/Resources/MainMenu.xib +++ b/Tests/Manual/TableCibTest/Resources/MainMenu.xib @@ -2,17 +2,17 @@ 1050 - 10C540 - 740 - 1038.25 - 458.00 + 10D573 + 759 + 1038.29 + 460.00 com.apple.InterfaceBuilder.CocoaPlugin - 740 + 759 YES - + YES @@ -218,9 +218,10 @@ 23 - -694157312 + -157286400 + 3 4 15 0 @@ -531,6 +532,7 @@ 0 + IBCocoaFramework com.apple.InterfaceBuilder.CocoaPlugin.macosx diff --git a/Tests/Manual/TableTest/AppController.j b/Tests/Manual/TableTest/AppController.j index ff2889de2..f383bcc8a 100644 --- a/Tests/Manual/TableTest/AppController.j +++ b/Tests/Manual/TableTest/AppController.j @@ -19,17 +19,17 @@ tableTestDragType = @"CPTableViewTestDragType"; dataSet2 = [], dataSet3 = []; - for(var i = 1; i < 100; i++) + for(var i = 1; i < 10; i++) { dataSet1[i - 1] = [CPNumber numberWithInt:i]; dataSet2[i - 1] = [CPNumber numberWithInt:i+10]; dataSet3[i - 1] = [CPNumber numberWithInt:i+20]; } - var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(50,50,400,400) styleMask:CPClosableWindowMask], + var theWindow = [[CPWindow alloc] initWithContentRect:CGRectMake(50,50,700,500) styleMask:CPClosableWindowMask], contentView = [theWindow contentView]; - tableView = [[CPTableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 500.0, 500.0)]; + tableView = [[CPTableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 400.0, 400.0)]; [tableView setAllowsMultipleSelection:YES]; [tableView setAllowsColumnSelection:YES]; @@ -41,6 +41,7 @@ tableTestDragType = @"CPTableViewTestDragType"; [tableView registerForDraggedTypes:[CPArray arrayWithObject:tableTestDragType]]; [tableView setColumnAutoresizingStyle:CPTableViewLastColumnOnlyAutoresizingStyle]; [tableView setDelegate:self]; + [tableView setSelectionHighlightColor:[CPColor redColor]]; [tableView setDataSource:self]; @@ -55,7 +56,7 @@ tableTestDragType = @"CPTableViewTestDragType"; var desc = [CPSortDescriptor sortDescriptorWithKey:@"self" ascending:YES]; - for (var i = 1; i <= 2; i++) + for (var i = 1; i <= 5; i++) { var column = [[CPTableColumn alloc] initWithIdentifier:String(i)]; [column setSortDescriptorPrototype:desc]; @@ -63,13 +64,15 @@ tableTestDragType = @"CPTableViewTestDragType"; [column setMinWidth:50.0]; [column setMaxWidth:500.0]; - [column setWidth:200.0]; + [column setWidth:75.0]; [column setEditable:YES]; [tableView addTableColumn:column]; } - var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(0, 0, CGRectGetWidth([contentView bounds]), CGRectGetHeight([contentView bounds]))]; + // we offset this scrollview to make sure all the coordinates are calculated correctly + // bad things can happen when the tableview doesn't sit at (0,0) + var scrollView = [[CPScrollView alloc] initWithFrame:CGRectMake(200, 50, CGRectGetWidth([contentView bounds]) - 200, CGRectGetHeight([contentView bounds]) -200)]; [scrollView setDocumentView:tableView]; [scrollView setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; @@ -79,6 +82,46 @@ tableTestDragType = @"CPTableViewTestDragType"; [theWindow orderFront:self]; [self newWindow]; [self sourceList]; + + var button = [[CPButton alloc] initWithFrame:CGRectMake(10,10,100, 24)]; + [button setTitle:@"Remove Row"]; + [button setTarget:self]; + [button setAction:@selector(removeRow:)]; + [contentView addSubview:button]; + + var button = [[CPButton alloc] initWithFrame:CGRectMake(10,40,100, 24)]; + [button setTitle:@"Add Row"]; + [button setTarget:self]; + [button setAction:@selector(addRow:)]; + [contentView addSubview:button]; + + var sourceListActiveGradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [255.0/255.0, 153.0/255.0, 209.0/255.0,1.0, 33.0/255.0, 94.0/255.0, 208.0/255.0,1.0], [0,1], 2), + sourceListActiveTopLineColor = [CPColor colorWithCalibratedRed:(255.0/255.0) green:(123.0/255.0) blue:(218.0/255.0) alpha:1.0], + sourceListActiveBottomLineColor = [CPColor colorWithCalibratedRed:(255.0/255.0) green:(92.0/255.0) blue:(207.0/255.0) alpha:1.0]; + [tableView setSelectionGradientColors:[CPDictionary dictionaryWithObjects:[sourceListActiveGradient, sourceListActiveTopLineColor, sourceListActiveBottomLineColor] forKeys:[CPSourceListGradient, CPSourceListTopLineColor, CPSourceListBottomLineColor]]]; + + var button = [[CPButton alloc] initWithFrame:CGRectMake(10,70,100, 24)]; + [button setTitle:@"Switch Highlight"]; + [button setTarget:self]; + [button setAction:@selector(switchSelectionHighlightType:)]; + [contentView addSubview:button]; +} + +- (void)switchSelectionHighlightType:(id)sender +{ + [tableView setSelectionHighlightStyle: ABS([tableView selectionHighlightStyle] - 1)]; +} + +- (void)removeRow:(id)sender +{ + [dataSet1 removeObjectAtIndex:0]; + [tableView reloadData]; +} + +- (void)addRow:(id)sender +{ + [dataSet1 addObject:[dataSet1 count] || 0]; + [tableView reloadData]; } - (void)newWindow @@ -97,6 +140,13 @@ tableTestDragType = @"CPTableViewTestDragType"; [tableView2 setDelegate:self]; [tableView2 setDataSource:self]; + + var checkBox = [[CPCheckBox alloc] initWithFrame:CGRectMake(5,3,24,24)], + checkBoxColumn = [[CPTableColumn alloc] initWithIdentifier:@"checkBox"]; + [checkBoxColumn setDataView:checkBox]; + + [tableView2 addTableColumn:checkBoxColumn]; + var desc = [CPSortDescriptor sortDescriptorWithKey:@"self" ascending:YES]; for (var i = 1; i <= 3; i++) { @@ -129,8 +179,7 @@ tableTestDragType = @"CPTableViewTestDragType"; - (void)sourceList { - - var window3 = [[CPWindow alloc] initWithContentRect:CGRectMake(450, 250, 200, 400) styleMask:CPTitledWindowMask | CPResizableWindowMask]; + var window3 = [[CPWindow alloc] initWithContentRect:CGRectMake(450, 250, 500, 400) styleMask:CPTitledWindowMask | CPResizableWindowMask]; tableView3 = [[CPTableView alloc] initWithFrame:CGRectMake(0.0, 0.0, 200.0, 500.0)]; @@ -150,11 +199,20 @@ tableTestDragType = @"CPTableViewTestDragType"; [column setMinWidth:50.0]; [column setEditable:YES]; [tableView3 addTableColumn:column]; + + var column = [[CPTableColumn alloc] initWithIdentifier:"sourcelist2"]; + [[column headerView] setStringValue:"Source List 2"]; + [tableView3 addTableColumn:column]; var scrollView3 = [[CPScrollView alloc] initWithFrame:[[window3 contentView] bounds]]; [tableView3 setRowHeight:32.0]; [scrollView3 setDocumentView:tableView3]; [scrollView3 setAutoresizingMask:CPViewWidthSizable | CPViewHeightSizable]; + + var sourceListActiveGradient = CGGradientCreateWithColorComponents(CGColorSpaceCreateDeviceRGB(), [255.0/255.0, 153.0/255.0, 209.0/255.0,1.0, 33.0/255.0, 94.0/255.0, 208.0/255.0,1.0], [0,1], 2), + sourceListActiveTopLineColor = [CPColor colorWithCalibratedRed:(255.0/255.0) green:(123.0/255.0) blue:(218.0/255.0) alpha:1.0], + sourceListActiveBottomLineColor = [CPColor colorWithCalibratedRed:(255.0/255.0) green:(92.0/255.0) blue:(207.0/255.0) alpha:1.0]; + [tableView3 setSelectionGradientColors:[CPDictionary dictionaryWithObjects:[sourceListActiveGradient, sourceListActiveTopLineColor, sourceListActiveBottomLineColor] forKeys:[CPSourceListGradient, CPSourceListTopLineColor, CPSourceListBottomLineColor]]]; [[window3 contentView] addSubview:scrollView3]; @@ -183,7 +241,7 @@ tableTestDragType = @"CPTableViewTestDragType"; else if (aTableView === tableView2) return String(dataSet2[aRow]); else if(aTableView === tableView3) - return String(dataSet3[row]); + return String(dataSet3[aRow]); } - (void)tableView:(CPTableView)aTableView sortDescriptorsDidChange:(CPArray)oldDescriptors @@ -381,4 +439,4 @@ tableTestDragType = @"CPTableViewTestDragType"; } } -@end \ No newline at end of file +@end diff --git a/Tests/Objective-J/CFDictionaryTest.j b/Tests/Objective-J/CFDictionaryTest.j new file mode 100644 index 000000000..924d209fe --- /dev/null +++ b/Tests/Objective-J/CFDictionaryTest.j @@ -0,0 +1,31 @@ + +@implementation CFDictionaryTest : OJTestCase + +- (void)testContainsValue +{ + var dict = new CFMutableDictionary(); + [self assert:NO equals:dict.containsValue(@"123")]; + [self assert:NO equals:dict.containsValue(@"abc")]; + + dict.setValueForKey(@"abc", @"123"); + [self assert:YES equals:dict.containsValue(@"123")]; + [self assert:NO equals:dict.containsValue(@"abc")]; + + dict.setValueForKey(@"def", @"123"); + [self assert:YES equals:dict.containsValue(@"123")]; + [self assert:NO equals:dict.containsValue(@"abc")]; +} + +- (void)testCountOfValue +{ + var dict = new CFMutableDictionary(); + [self assert:0 equals:dict.countOfValue(@"123")]; + + dict.setValueForKey(@"abc", @"123"); + [self assert:1 equals:dict.countOfValue(@"123")]; + + dict.setValueForKey(@"def", @"123"); + [self assert:2 equals:dict.countOfValue(@"123")]; +} + +@end diff --git a/Tests/Tools/ToolsTest.j b/Tests/Tools/ToolsTest.j new file mode 100644 index 000000000..b44cd234b --- /dev/null +++ b/Tests/Tools/ToolsTest.j @@ -0,0 +1,39 @@ +var OS = require("os"); +var FILE = require("file"); + +function cleanup() { + ["ToolsTestApp", "PressTestApp", "FlattenTestApp"].forEach(function(dir) { + if (FILE.isDirectory(dir)) + FILE.rmtree(dir); + }); +} + +@implementation ToolsTest : OJTestCase +{ +} + +- (void)setUp +{ + cleanup(); +} + +- (void)testTools +{ + var status; + + status = OS.system(["capp", "gen", "ToolsTestApp"].map(OS.enquote).join(" ") + " > /dev/null"); + [self assert:status equals:0 message:"capp gen failed"]; + + status = OS.system(["press", "-f", "ToolsTestApp", "PressTestApp"].map(OS.enquote).join(" ") + " > /dev/null"); + [self assert:status equals:0 message:"press failed"]; + + status = OS.system(["flatten", "-f", "ToolsTestApp", "FlattenTestApp"].map(OS.enquote).join(" ") + " > /dev/null"); + [self assert:status equals:0 message:"flatten failed"]; +} + +- (void)tearDown +{ + cleanup(); +} + +@end diff --git a/Tools/Jakefile b/Tools/Jakefile index fe675b736..e9e0f5fc9 100644 --- a/Tools/Jakefile +++ b/Tools/Jakefile @@ -2,4 +2,4 @@ require("../common.jake"); // nib2cib has to come before capp since capp uses nib2cib -subtasks(["nib2cib", "capp"], ["build"/*, "clean", "clobber"*/]); +subtasks(["nib2cib", "capp", "NativeHost"], ["build"/*, "clean", "clobber"*/]); diff --git a/Tools/NativeHost/.gitignore b/Tools/NativeHost/.gitignore new file mode 100644 index 000000000..f921bd442 --- /dev/null +++ b/Tools/NativeHost/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +*.mode1v3 +*.pbxuser +*.perspectivev3 +build diff --git a/Tools/NativeHost/AppController+MainMenu.m b/Tools/NativeHost/AppController+MainMenu.m new file mode 100644 index 000000000..dbc98e63e --- /dev/null +++ b/Tools/NativeHost/AppController+MainMenu.m @@ -0,0 +1,33 @@ +// +// AppController+MainMenu.m +// NativeHost +// +// Created by Francisco Tolmasky on 9/2/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import + +#import "MenuBridge.h" +#import "AppController.h" + + +@interface NSApplication (MenuMethods) + +- (void)setAppleMenu:(NSMenu *)aMenu; + +@end + +@implementation AppController (MainMenu) + +- (void)setMainMenuObject:(WebScriptObject *)aMenuObject +{ + NSMenu * mainMenu = [[MenuBridge alloc] initWithMenuObject:aMenuObject]; + + [NSApp setAppleMenu:[[mainMenu itemArray] objectAtIndex:0]]; + [NSApp setMainMenu:mainMenu]; + + [mainMenu release]; +} + +@end diff --git a/Tools/NativeHost/AppController+WebFrameLoadDelegate.m b/Tools/NativeHost/AppController+WebFrameLoadDelegate.m new file mode 100644 index 000000000..fdce4d601 --- /dev/null +++ b/Tools/NativeHost/AppController+WebFrameLoadDelegate.m @@ -0,0 +1,26 @@ + + +// +// AppController+WebFrameLoadDelegate.m +// NativeHost +// +// Created by Francisco Tolmasky on 7/28/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "AppController.h" + +#import "BridgedMethods.h" + + +@implementation AppController (WebFrameLoadDelegate) + +- (void)webView:(WebView *)aWebView didClearWindowObject:(WebScriptObject *)aWindowObject forFrame:(WebFrame *)aFrame +{ + if (aWebView == webView) + [GlobalMethods enhanceWindowObject:aWindowObject ofWebView:aWebView]; + else + [WindowMethods enhanceWindowObject:aWindowObject ofWebView:aWebView]; +} + +@end diff --git a/Tools/NativeHost/AppController+WebPolicyDelegate.m b/Tools/NativeHost/AppController+WebPolicyDelegate.m new file mode 100644 index 000000000..209870a55 --- /dev/null +++ b/Tools/NativeHost/AppController+WebPolicyDelegate.m @@ -0,0 +1,35 @@ +// +// AppController+WebPolicyDelegate.m +// NativeHost +// +// Created by Francisco Tolmasky on 5/11/10. +// Copyright 2010 280 North, Inc. All rights reserved. +// + +#import "NSURL+Additions.h" +#import "AppController.h" + + +@implementation AppController (WebPolicyDelegate) + +- (void)webView:(WebView *)aWebView decidePolicyForNewWindowAction:(NSDictionary *)actionInformation request:(NSURLRequest *)aRequest newFrameName:(NSString *)aFrameName decisionListener:(id < WebPolicyDecisionListener >)aListener +{ + [[NSWorkspace sharedWorkspace] openURL:[actionInformation objectForKey:WebActionOriginalURLKey]]; + [aListener ignore]; +} + +- (void)webView:(WebView *)aWebView decidePolicyForNavigationAction:(NSDictionary *)aDictionary request:(NSURLRequest *)aRequest frame:(WebFrame *)aWebFrame decisionListener:(id )aDecisionListener +{ + NSURL * requestURL = [aRequest URL]; + + if([aWebView mainFrame] != aWebFrame || + [[baseURL scheme] isEqualTo:[requestURL scheme]] && + ![baseURL host] || [[baseURL host] isEqualTo:[requestURL host]] && + ![baseURL port] || [[baseURL port] isEqualTo:[requestURL port]]) + return [aDecisionListener use]; + + [[NSWorkspace sharedWorkspace] openURL:[aDictionary objectForKey:WebActionOriginalURLKey]]; + [aDecisionListener ignore]; +} + +@end diff --git a/Tools/NativeHost/AppController+WebResourceLoadDelegate.m b/Tools/NativeHost/AppController+WebResourceLoadDelegate.m new file mode 100644 index 000000000..a60540b75 --- /dev/null +++ b/Tools/NativeHost/AppController+WebResourceLoadDelegate.m @@ -0,0 +1,32 @@ +// +// AppController+WebResourceLoadDelegate.m +// NativeHost +// +// Created by Francisco Tolmasky on 7/28/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "AppController.h" +#import "SSCrypto.h" + +NSString *authHeader = nil; + +@implementation AppController (WebResourceLoadDelegate) + +- (NSURLRequest *)webView:(WebView *)aSender resource:(id)anIdentifier willSendRequest:(NSURLRequest *)aRequest redirectResponse:(NSURLResponse *)aRedirectResponse fromDataSource:(WebDataSource *)aDataSource +{ + aRequest = [[aRequest mutableCopy] autorelease]; + + if (!authHeader) + { + NSString *plaintext = [NSString stringWithFormat:@"%@:%@", SERVER_USER, SERVER_PASSWORD]; + NSData *data = [plaintext dataUsingEncoding:NSASCIIStringEncoding]; + authHeader = [[NSString alloc] initWithFormat:@"Basic %@", [data encodeBase64WithNewlines:NO]]; + } + + [(NSMutableURLRequest *)aRequest setValue:authHeader forHTTPHeaderField:@"Authorization"]; + + return aRequest; +} + +@end diff --git a/Tools/NativeHost/AppController+WebUIDelegate.m b/Tools/NativeHost/AppController+WebUIDelegate.m new file mode 100644 index 000000000..a75f9d6a9 --- /dev/null +++ b/Tools/NativeHost/AppController+WebUIDelegate.m @@ -0,0 +1,71 @@ +// +// AppController+WebUIDelegate.m +// NativeHost +// +// Created by Francisco Tolmasky on 7/28/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "AppController.h" +#import "WebWindow.h" + + +@implementation AppController (WebUIDelegate) + +- (void)webView:(WebView *)aWebView setFrame:(NSRect)aFrame +{ + [[aWebView window] setFrame:aFrame display:YES]; +} + +- (NSRect)webViewFrame:(WebView *)aWebView +{ + return [[aWebView window] frame]; +} + +- (WebView *)webView:(WebView *)aWebView createWebViewWithRequest:(NSURLRequest *)aRequest +{ + return [[WebWindow webWindow] webView]; +} + +- (void)webViewClose:(WebView *)aWebView +{ + // Important to call close and not: + // -performClose:, which beeps without the presence of a close button. + // -orderOut:, which doesn't release the window. + [[aWebView window] close]; + //[[aWebView window] orderOut:self]; +} + +- (void)webViewShow:(WebView *)aWebView +{ + [[aWebView window] makeKeyAndOrderFront:self]; +} + +- (NSAttributedString *)loggedOutput; +{ + return loggedOutput; +} + +- (void)webView:(WebView *)aWebView addMessageToConsole:(NSDictionary *)aDictionary +{ + NSString * message = [aDictionary objectForKey:@"message"]; + NSAttributedString * attributedString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@\n", message, nil]]; + + [loggedOutput appendAttributedString:attributedString]; + + [attributedString release]; + NHLog(@"CONSOLE", message); +} + +- (void)webView:(WebView *)aSender runJavaScriptAlertPanelWithMessage:(NSString *)aMessage initiatedByFrame:(WebFrame *)aFrame +{ + WebDataSource * dataSource = [aFrame dataSource]; + NSString * title = [dataSource pageTitle]; + + if (![title length]) + title = [[[dataSource request] URL] absoluteString]; + + [[NSAlert alertWithMessageText:title defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:@"%@", aMessage, nil] runModal]; +} + +@end diff --git a/Tools/NativeHost/AppController.h b/Tools/NativeHost/AppController.h new file mode 100644 index 000000000..0e245965b --- /dev/null +++ b/Tools/NativeHost/AppController.h @@ -0,0 +1,53 @@ +// +// AppController.h +// NativeHost +// +// Created by Francisco Tolmasky on 6/4/09. +// Copyright 2009 280 North, Inc. All rights reserved. +// + +#import + +extern int SERVER_PORT; +extern NSString *SERVER_PASSWORD; +extern NSString *SERVER_USER; + +@class Server; +@class WebScriptObject; + +@interface AppController : NSObject +{ + NSWindow * webViewWindow; + WebView * webView; + + NSMutableArray * openingFilenames; + NSMutableArray * openingURLStrings; + + Server * server; + NSFileHandle * stdinFileHandle; + + NSMutableAttributedString * loggedOutput; + + NSURL * baseURL; +} + +- (NSURL *)baseURL; + +- (void)startServer; +- (void)startCappuccinoApplication; + +- (NSArray *)openingURLStrings; + +- (NSView *)keyView; +- (WebView *)webView; +- (WebScriptObject *)windowScriptObject; + +@end + +@interface AppController (MainMenu) + +- (void)setMainMenuObject:(WebScriptObject *)aMenuObject; + +@end + +void NHLog(NSString *type, NSString *message); \ No newline at end of file diff --git a/Tools/NativeHost/AppController.m b/Tools/NativeHost/AppController.m new file mode 100644 index 000000000..da1204bbd --- /dev/null +++ b/Tools/NativeHost/AppController.m @@ -0,0 +1,203 @@ +// +// AppController.m +// NativeHost +// +// Created by Francisco Tolmasky on 6/4/09. +// Copyright 2009 280 North, Inc. All rights reserved. +// + +#import "Console.h" +#import "Server.h" +#import "AppController.h" +#import "NSURL+Additions.h" +#import "BridgedMethods.h" +#import "WebWindow.h" +#import "WebScripObject+Objective-J.h" + +extern int interactive; + +int SERVER_PORT = 9191; +NSString *SERVER_PASSWORD = nil; +NSString *SERVER_USER = nil; + + +@implementation AppController + +- (id)init +{ + self = [super init]; + + if (self) + openingURLStrings = [NSMutableArray new]; + + return self; +} + +- (void)dealloc +{ + [webView release]; + [webViewWindow release]; + + [openingURLStrings release]; + + [super dealloc]; +} + +- (BOOL)application:(NSApplication *)anApplication openFile:(NSString *)aFilename +{ + id canOpenFilesImmediately = [[webView windowScriptObject] evaluateWebScript:@"CPApp && CPApp._finishedLaunching"]; + + NSString *path = [[[NSURL fileURLWithPath:aFilename] HTTPFileSystemURL] absoluteString]; + if ([canOpenFilesImmediately isKindOfClass:[NSNumber class]] && [canOpenFilesImmediately boolValue]) + [[webView windowScriptObject] evaluateObjectiveJ:[NSString stringWithFormat:@"[CPApp _openURL:[CPURL URLWithString:\"%@\"]]", path]]; + else + [openingURLStrings addObject:path]; + + return NO; +} + +- (void)applicationWillFinishLaunching:(NSNotification *)aNotification +{ + NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults]; + + [defaults registerDefaults:[NSDictionary dictionaryWithObject:[NSNumber numberWithBool:YES] forKey:@"WebKitDeveloperExtras"]]; + + [self startServer]; +} + +- (void)applicationDidFinishLaunching:(NSNotification *)notification +{ + [self startCappuccinoApplication]; +} + +- (void)applicationDidResignActive:(NSNotification *)aNotification +{ + // FIXME: handle this better. + [[self windowScriptObject] evaluateObjectiveJ:@"[CPApp _willResignActive];"]; + [[self windowScriptObject] evaluateObjectiveJ:@"CPApp._isActive = NO;"]; + [[self windowScriptObject] evaluateObjectiveJ:@"[CPApp _didResignActive];"]; +} + +- (void)applicationDidBecomeActive:(NSNotification *)aNotification +{ + // FIXME: handle this better. + [[self windowScriptObject] evaluateObjectiveJ:@"[CPApp _willBecomeActive];"]; + [[self windowScriptObject] evaluateObjectiveJ:@"CPApp._isActive = YES;"]; + [[self windowScriptObject] evaluateObjectiveJ:@"[CPApp _didBecomeActive];"]; +} + +- (NSArray *)openingURLStrings +{ + return openingURLStrings; +} + +- (NSView *)keyView +{ + return [[[webView mainFrame] frameView] documentView]; +} + +- (WebView *)webView +{ + return webView; +} + +- (WebScriptObject *)windowScriptObject +{ + return [webView windowScriptObject]; +} + +- (void)startServer +{ + server = [[Server alloc] init]; + + if (![server start]) + exit(0); + + if (interactive) + { + stdinFileHandle = [[NSFileHandle fileHandleWithStandardInput] retain]; + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(didReadStdin:) + name:NSFileHandleReadCompletionNotification + object:stdinFileHandle]; + + fprintf(stdout, "objj> "); + fflush(stdout); + [stdinFileHandle readInBackgroundAndNotify]; + } +} + +- (void)didReadStdin:(NSNotification *)aNotification +{ + NSString *string = [[NSString alloc] initWithData:[[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem] encoding:NSASCIIStringEncoding]; + NSString *result = [[self windowScriptObject] evaluateObjectiveJReturningString:string]; + [string release]; + if ([result respondsToSelector:@selector(UTF8String)]) + fprintf(stdout, "%s\n", [result UTF8String]); + fprintf(stdout, "objj> "); + fflush(stdout); + [stdinFileHandle readInBackgroundAndNotify]; +} + +- (NSURL *)baseURL +{ + return baseURL; +} + +- (void)startCappuccinoApplication +{ + NSString *initialURL = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NHInitialURL"]; + BOOL prependServer = [[[NSBundle mainBundle] objectForInfoDictionaryKey:@"NHInitialURLPrependServer"] boolValue]; + + if (!initialURL) + { + NSString *initialResource = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"NHInitialResource"]; + + if (!initialResource) + initialResource = @"Application/index.html"; + + initialURL = [[NSString stringWithFormat:@"file://%@/%@", [[NSBundle mainBundle] resourcePath], initialResource] stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]; + baseURL = [[NSURL alloc] initWithString:@"file:///"]; + } + else if (prependServer) + { + initialURL = [NSString stringWithFormat:@"http://127.0.0.1:%d/%@", SERVER_PORT, initialURL]; + baseURL = [[NSURL alloc] initWithString:[NSString stringWithFormat:@"http://127.0.0.1:%d/", SERVER_PORT]]; + } + + NHLog(@"STARTUP", [initialURL description]); + + [[webView mainFrame] loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:initialURL]]]; +} + +- (void)applicationWillTerminate:(NSNotification *)aNotification +{ + [server stop]; +} + +- (void)awakeFromNib +{ + webView = [[WebView alloc] init]; + + [webView setUIDelegate:self]; + [webView setFrameLoadDelegate:self]; + [webView setResourceLoadDelegate:self]; + [webView setPolicyDelegate:self]; + + webViewWindow = [[NSWindow alloc] init]; + + [webViewWindow setContentView:webView]; +} + +@end + +void NHLog(NSString *type, NSString *message) +{ + NSArray *lines = [message componentsSeparatedByString:@"\n"]; + for (NSString *line in lines) + { + if ([line length] > 0) + NSLog(@"%10@: %@", type, line); + } +} diff --git a/Tools/NativeHost/Application.h b/Tools/NativeHost/Application.h new file mode 100644 index 000000000..d09086a9f --- /dev/null +++ b/Tools/NativeHost/Application.h @@ -0,0 +1,18 @@ +// +// Application.h +// NativeHost +// +// Created by Francisco Tolmasky on 10/8/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import + + +@interface Application : NSApplication +{ +} + +- (void)_reallyTerminate:(id)sender; + +@end diff --git a/Tools/NativeHost/Application.m b/Tools/NativeHost/Application.m new file mode 100644 index 000000000..4dbb8a397 --- /dev/null +++ b/Tools/NativeHost/Application.m @@ -0,0 +1,47 @@ +// +// Application.m +// NativeHost +// +// Created by Francisco Tolmasky on 10/8/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "Application.h" +#import "AppController.h" +#import "WebScripObject+Objective-J.h" +#import "WebWindow.h" + +@implementation Application + +- (void)sendEvent:(NSEvent *)anEvent +{ + NSWindow * window = [anEvent window]; + + if (!window || [window isKindOfClass:[WebWindow class]]) + { + NSResponder * firstResponder = window ? [window firstResponder] : [(AppController *)[self delegate] keyView]; + + switch ([anEvent type]) + { + case NSKeyDown: return [firstResponder keyDown:anEvent]; + case NSKeyUp: return [firstResponder keyUp:anEvent]; + } + } + + [super sendEvent:anEvent]; +} + +- (void)terminate:(id)sender +{ + if ([[[self delegate] windowScriptObject] evaluateObjectiveJ:@"(CPApp)"] == [WebUndefined undefined]) + [self _reallyTerminate:sender]; + else + [[(AppController *)[self delegate] windowScriptObject] evaluateObjectiveJ:@"[CPApp terminate:nil]"]; +} + +- (void)_reallyTerminate:(id)sender +{ + [super terminate:sender]; +} + +@end diff --git a/Tools/NativeHost/BridgedMethods.h b/Tools/NativeHost/BridgedMethods.h new file mode 100644 index 000000000..a9af18f1f --- /dev/null +++ b/Tools/NativeHost/BridgedMethods.h @@ -0,0 +1,65 @@ +// +// BridgedMethods.h +// NativeHost +// +// Created by Francisco Tolmasky on 10/16/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import + + +@interface BridgedMethods : NSObject +{ + WebView * webView; +} ++ (NSMutableArray *)arrayFromWebScriptObject:(WebScriptObject *)incomingObject; ++ (void)enhanceWindowObject:(WebScriptObject *)aWindowObject ofWebView:(WebView *)aWebView; +@end + +@interface GlobalMethods : BridgedMethods +{ +} +- (id)isDesktop; + +- (id)openingURLStrings; + +- (id)setMainMenu:(id)arguments; + +- (id)terminate; +- (id)activateIgnoringOtherApps:(NSArray *)arguments; +- (id)deactivate; +- (id)hide; +- (id)hideOtherApplications; + +- (id)savePanel:(NSArray *)arguments; // FIXME: This should be implemented in Cappuccino. +- (id)openPanel; // FIXME: This should be implemented in Cappuccino. + +- (id)clearRecentDocuments; +- (id)noteNewRecentDocumentPath:(NSArray *)aURL; +- (id)recentDocumentURLs; + +- (id)pasteboardWithName:(NSArray *)arguments; + +@end + +@interface WindowMethods : BridgedMethods +{ +} +- (id)miniaturize; +- (id)deminiaturize; + +- (id)level; +- (id)setLevel:(NSArray *)arguments; + +- (id)hasShadow; +- (id)setHasShadow:(NSArray *)arguments; + +- (id)shadowStyle; +- (id)setShadowStyle:(NSArray *)arguments; + +- (id)frame; +- (id)setFrame:(NSArray *)arguments; + +@end + diff --git a/Tools/NativeHost/BridgedMethods.m b/Tools/NativeHost/BridgedMethods.m new file mode 100644 index 000000000..7e637692a --- /dev/null +++ b/Tools/NativeHost/BridgedMethods.m @@ -0,0 +1,345 @@ +// +// BridgedMethods.m +// NativeHost +// +// Created by Francisco Tolmasky on 10/16/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + + +#import +#import "Application.h" +#import "AppController.h" +#import "BridgedMethods.h" +#import "NSObject+WebAdditions.h" +#import "NSURL+Additions.h" +#import "WebWindow.h" +#import "WebPasteboardProxy.h" + +static NSMutableDictionary * MethodClasses; + +@implementation GlobalMethods + +- (id)isDesktop +{ + return [NSNumber numberWithBool:YES]; +} + +- (id)openingURLStrings +{ + return [(AppController *)[NSApp delegate] openingURLStrings]; +} + +- (id)setMainMenu:(id)arguments +{ + [[NSApp delegate] setMainMenuObject:[arguments objectAtIndex:0]]; + + return [WebUndefined undefined]; +} + +- (id)terminate +{ + [(Application *)NSApp _reallyTerminate:self]; + + return [WebUndefined undefined]; +} + +- (id)activateIgnoringOtherApps:(NSArray *)arguments +{ + id argument = [arguments objectAtIndex:0]; + + [(NSApplication *)NSApp activateIgnoringOtherApps:[argument isKindOfClass:[NSNumber class]] && [(NSNumber *)argument boolValue]]; + + return [WebUndefined undefined]; +} + +- (id)deactivate +{ + [(NSApplication *)NSApp deactivate]; + return [WebUndefined undefined]; +} + +- (id)hide +{ + [NSApp hide:self]; + return [WebUndefined undefined]; +} + +- (id)hideOtherApplications +{ + [NSApp hideOtherApplications:self]; + return [WebUndefined undefined]; +} + +- (id)openPanel +{ + NSOpenPanel * openPanel = [NSOpenPanel openPanel]; + + NSInteger result = [openPanel runModal]; + + NSMutableString * resultObject = [NSMutableString stringWithFormat:@"({ button:%d, URLs:", result]; + + NSMutableArray * fileSystemURLs = [NSMutableArray array]; + + for (NSURL * URL in [openPanel URLs]) + [fileSystemURLs addObject:[URL HTTPFileSystemURL]]; + + if (result == NSFileHandlingPanelOKButton) + [resultObject appendString:[NSString stringWithFormat:@"[\"%@\"] })", [fileSystemURLs componentsJoinedByString:@"\", \""]]]; + + else + [resultObject appendString:@"[] })"]; + + return [[webView windowScriptObject] evaluateWebScript:resultObject]; +} + +- (id)savePanel:(NSArray *)arguments +{ + NSSavePanel * savePanel = [NSSavePanel savePanel]; + WebScriptObject *args = [arguments objectAtIndex:0]; + + [savePanel setAllowedFileTypes:[BridgedMethods arrayFromWebScriptObject:[args valueForKey:@"allowedFileTypes"]]]; + + id allowsOtherFileTypes = [args valueForKey:@"allowsOtherFileTypes"]; + + [savePanel setAllowsOtherFileTypes:allowsOtherFileTypes && [allowsOtherFileTypes webBoolValue]]; + + id isExtensionHidden = [args valueForKey:@"isExtensionHidden"]; + + [savePanel setExtensionHidden:isExtensionHidden && [isExtensionHidden webBoolValue]]; + + id canSelectHiddenExtension = [args valueForKey:@"canSelectHiddenExtension"]; + + [savePanel setCanSelectHiddenExtension:canSelectHiddenExtension && canSelectHiddenExtension]; + + id canCreateDirectories = [args valueForKey:@"canCreateDirectories"]; + + [savePanel setCanCreateDirectories:canCreateDirectories && [canCreateDirectories webBoolValue]]; + + NSInteger result = [savePanel runModal]; + + return [[webView windowScriptObject] evaluateWebScript:[NSString stringWithFormat:@"({ button:%d, URL:\"%@\" })", result, [[[savePanel URL] HTTPFileSystemURL] absoluteString]]]; +} + +- (id)clearRecentDocuments +{ + [[NSDocumentController sharedDocumentController] clearRecentDocuments:self]; + return [WebUndefined undefined]; +} + +- (id)noteNewRecentDocumentPath:(NSArray *)arguments +{ + if (![arguments count]) + return [WebUndefined undefined]; + + NSString *aURL = [arguments objectAtIndex:0]; + NSURL *fileURL = [NSURL fileSystemURLFromHTTPFileSystemString:aURL]; + + [[NSDocumentController sharedDocumentController] noteNewRecentDocumentURL:fileURL]; + + return [WebUndefined undefined]; +} + +- (id)recentDocumentURLs +{ + NSArray *existingURLs = [[NSDocumentController sharedDocumentController] recentDocumentURLs]; + NSMutableArray *urlsAsStrings = [NSMutableArray array]; + + for (int i=0, count = [existingURLs count]; i < count; i++) + [urlsAsStrings addObject:[[existingURLs objectAtIndex:i] absoluteString]]; + + return urlsAsStrings; +} + +- (id)pasteboardWithName:(NSArray *)arguments +{ + if (![arguments count]) + return [WebUndefined undefined]; + + return [WebPasteboardProxy pasteboardWithName:[arguments objectAtIndex:0]]; +} + +@end + +@implementation WindowMethods + +- (id)miniaturize +{ + [[webView window] miniaturize:nil]; + return [WebUndefined undefined]; +} + +- (id)deminiaturize +{ + [[webView window] deminiaturize:nil]; + return [WebUndefined undefined]; +} + +- (id)level +{ + return [NSNumber numberWithInt:[[webView window] level]]; +} + +- (id)setLevel:(NSArray *)arguments +{ + [[webView window] setLevel:[[arguments objectAtIndex:0] intValue]]; + + return [WebUndefined undefined]; +} + +- (id)hasShadow +{ + return [NSNumber numberWithBool:[[webView window] hasShadow]]; +} + +- (id)setHasShadow:(NSArray *)arguments +{ + [[webView window] setHasShadow:[[arguments objectAtIndex:0] boolValue]]; + + return [WebUndefined undefined]; +} + +- (id)frame +{ + NSRect frame = [[webView window] frame]; + + return [[webView windowScriptObject] evaluateWebScript:[NSString stringWithFormat:@"({ origin:{%f, %f}, size:{%f, %f}})", NSMinX(frame), NSMinY(frame), NSWidth(frame), NSHeight(frame)]]; +} + +- (id)setFrame:(NSArray *)arguments +{ + id origin = [[arguments objectAtIndex:0] valueForKey:@"origin"], + size = [[arguments objectAtIndex:0] valueForKey:@"size"]; + + NSWindow * window = [webView window]; + NSScreen * screen = [window screen]; + + if (!screen) + screen = [NSScreen mainScreen]; // Is this correct? + + float height = [[size valueForKey:@"height"] floatValue]; + + [[webView window] setFrame:NSMakeRect([[origin valueForKey:@"x"] floatValue], NSMaxY([screen frame]) - [[origin valueForKey:@"y"] floatValue] - height, [[size valueForKey:@"width"] floatValue], height) display:YES]; + /* + NSLog([webView description]); + NSLog(@"%@ %@", [window screen], NSStringFromRect([window frame])); + NSLog(@"%f - %f - %f = %f", NSMaxY([[window screen] frame]), [[origin valueForKey:@"y"] floatValue], height, NSMaxY([[window screen] frame]) - [[origin valueForKey:@"y"] floatValue] - height); + NSLog(@"FROM: %@", NSStringFromRect(NSMakeRect([[origin valueForKey:@"x"] floatValue], [[origin valueForKey:@"y"] floatValue], [[size valueForKey:@"width"] floatValue], height))); + NSLog(@" TO: %@", NSStringFromRect([[webView window] frame])); + */ + + return [WebUndefined undefined]; +} + +- (id)shadowStyle +{ + WebWindow * window = (WebWindow *)[webView window]; + + return [NSNumber numberWithInt:[window shadowStyle]]; +} + +- (id)setShadowStyle:(id)arguments +{ + int type = [[arguments objectAtIndex:0] intValue]; + + WebWindow * window = (WebWindow *)[webView window]; + + [window setShadowStyle:type]; + + return [WebUndefined undefined]; +} + +@end + + +@interface WindowScriptObjectMethod : NSObject +{ + WebView * webView; +} + +- (id)initWithWebView:(WebView *)aWebView; + +@end + +@implementation WindowScriptObjectMethod + +- (id)initWithWebView:(WebView *)aWebView +{ + self = [super init]; + + if (self) + webView = aWebView; + + return self; +} + +@end + +@implementation BridgedMethods + ++ (NSMutableArray *)arrayFromWebScriptObject:(WebScriptObject *)incomingObject +{ + if (!incomingObject) + return nil; + + NSMutableArray * newItems = [[NSMutableArray alloc] init]; + + for (int i=0; [incomingObject webScriptValueAtIndex:i]!=[WebUndefined undefined]; i++) + { + id oneItem =[incomingObject webScriptValueAtIndex:i]; + [newItems addObject:oneItem]; + } + + return [newItems autorelease]; +} + ++ (void)enhanceWindowObject:(WebScriptObject *)aWindowObject ofWebView:(WebView *)aWebView +{ + NSDictionary * classMethods = [MethodClasses objectForKey:[self className]]; + NSString * key = nil; + NSEnumerator * keyEnumerator = [classMethods keyEnumerator]; + + while (key = [keyEnumerator nextObject]) + [aWindowObject setValue:[(WindowScriptObjectMethod *)[[[classMethods objectForKey:key] alloc] initWithWebView:aWebView] autorelease] forKey:key]; +} + ++ (void)bridgeMethod:(Method)aMethod dictionary:(NSMutableDictionary *)aDictionary +{ + NSString * methodName = NSStringFromSelector(method_getName(aMethod)), + * publicMethodName = [NSString stringWithFormat:@"cp%@", [[NSString stringWithFormat:@"%@%@", [[methodName substringToIndex:1] uppercaseString], [methodName substringFromIndex:1]] stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:@":"]]]; + + Class methodClass = objc_allocateClassPair([WindowScriptObjectMethod class], [[NSString stringWithFormat:@"WebScriptObjectMethod_%@", publicMethodName] cString], 0); + + class_addMethod(methodClass, @selector(invokeDefaultMethodWithArguments:), method_getImplementation(aMethod), "@@:@"); + + objc_registerClassPair(methodClass); + + [aDictionary setObject:methodClass forKey:publicMethodName]; +} + ++ (void)initialize +{ + if (!MethodClasses) + MethodClasses = [[NSMutableDictionary alloc] init]; + + NSMutableDictionary * dictionary = [[NSMutableDictionary alloc] init]; + + [MethodClasses setObject:dictionary forKey:[self className]]; + [dictionary release]; + + unsigned int methodCount = 0; + Method * methods = class_copyMethodList(self, &methodCount); + + if (!methods) + return; + + int index = 0; + + for (; index < methodCount; ++index) + [self bridgeMethod:methods[index] dictionary:dictionary]; + + free(methods); + methods = NULL; +} + +@end diff --git a/Tools/NativeHost/Console.h b/Tools/NativeHost/Console.h new file mode 100644 index 000000000..a4d9b6c42 --- /dev/null +++ b/Tools/NativeHost/Console.h @@ -0,0 +1,21 @@ +// +// Console.h +// NativeHost +// +// Created by Francisco Tolmasky on 6/16/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import + + +@interface Console : NSObject +{ + NSMutableAttributedString * contents; +} + ++ (id)sharedConsole; + +- (NSAttributedString *)contents; + +@end diff --git a/Tools/NativeHost/Console.m b/Tools/NativeHost/Console.m new file mode 100644 index 000000000..2118f999b --- /dev/null +++ b/Tools/NativeHost/Console.m @@ -0,0 +1,69 @@ +// +// Console.m +// NativeHost +// +// Created by Francisco Tolmasky on 6/16/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "Console.h" + +#import "AppController.h" + + +@implementation Console + ++ (id)sharedConsole +{ + static Console * console = nil; + + if (!console) + console = [[Console alloc] init]; + + return console; +} + +- (id)init +{ + self = [super init]; + + if (self) + contents = [[NSMutableAttributedString alloc] init]; + + return self; +} + +- (void)dealloc +{ + [contents release]; + [super dealloc]; +} + +- (NSAttributedString *)contents +{ + return contents; +} + +- (void)webView:(WebView *)aWebView addMessageToConsole:(NSDictionary *)aDictionary +{ + NSString * message = [aDictionary objectForKey:@"message"]; + + NSAttributedString *attrString = [[NSAttributedString alloc] initWithString:[NSString stringWithFormat:@"%@\n", message, nil]]; + [contents appendAttributedString:attrString]; + [attrString release]; + + NHLog(@"CONSOLE", message); +} + +- (void)webView:(WebView *)aSender runJavaScriptAlertPanelWithMessage:(NSString *)aMessage initiatedByFrame:(WebFrame *)aFrame +{ + WebDataSource * dataSource = [aFrame dataSource]; + NSString * title = [dataSource pageTitle]; + + if (![title length]) + title = [[[dataSource request] URL] absoluteString]; + + [[NSAlert alertWithMessageText:title defaultButton:@"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:aMessage, nil] runModal]; +} + +@end diff --git a/Tools/NativeHost/English.lproj/InfoPlist.strings b/Tools/NativeHost/English.lproj/InfoPlist.strings new file mode 100644 index 000000000..5e45963c3 Binary files /dev/null and b/Tools/NativeHost/English.lproj/InfoPlist.strings differ diff --git a/Tools/NativeHost/English.lproj/MainMenu.xib b/Tools/NativeHost/English.lproj/MainMenu.xib new file mode 100644 index 000000000..2bce8a810 --- /dev/null +++ b/Tools/NativeHost/English.lproj/MainMenu.xib @@ -0,0 +1,548 @@ + + + + 1050 + 10C540 + 732 + 1038.25 + 458.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 732 + + + YES + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + YES + + + YES + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + NSFontManager + + + AppController + + + + + YES + + + delegate + + + + 519 + + + + + YES + + 0 + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 420 + + + + + 487 + + + + + + + -3.IBPluginDependency + com.apple.InterfaceBuilder.CocoaPlugin + + + YES + + + YES + + + + + YES + + + YES + + + + 677 + + + + YES + + AppController + NSObject + + IBProjectSource + AppController.h + + + + NSObject + + IBProjectSource + NSObject+WebAdditions.h + + + + + YES + + NSApplication + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSApplication.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSApplicationScripting.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSColorPanel.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSHelpManager.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSPageLayout.h + + + + NSApplication + + IBFrameworkSource + AppKit.framework/Headers/NSUserInterfaceItemSearching.h + + + + NSFontManager + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontManager.h + + + + NSMenu + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSMenu.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSAccessibility.h + + + + NSObject + + + + NSObject + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSControl.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDictionaryController.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSDragging.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSFontPanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSKeyValueBinding.h + + + + NSObject + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSNibLoading.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSOutlineView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSPasteboard.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSSavePanel.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSTableView.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSToolbarItem.h + + + + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSView.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSError.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSFileManager.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyValueObserving.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSKeyedArchiver.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObject.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSObjectScripting.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSPortCoder.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSRunLoop.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptClassDescription.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptKeyValueCoding.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptObjectSpecifiers.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSScriptWhoseTests.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSThread.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURL.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLConnection.h + + + + NSObject + + IBFrameworkSource + Foundation.framework/Headers/NSURLDownload.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebDownload.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebEditingDelegate.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebFrameLoadDelegate.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebJavaPlugIn.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebPlugin.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebPluginContainer.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebPolicyDelegate.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebResourceLoadDelegate.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebScriptObject.h + + + + NSObject + + IBFrameworkSource + WebKit.framework/Headers/WebUIDelegate.h + + + + NSResponder + + IBFrameworkSource + AppKit.framework/Headers/NSInterfaceStyle.h + + + + NSResponder + NSObject + + IBFrameworkSource + AppKit.framework/Headers/NSResponder.h + + + + + 0 + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.macosx + + + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + ../NativeHost.xcodeproj + 3 + + diff --git a/Tools/NativeHost/Info.plist b/Tools/NativeHost/Info.plist new file mode 100644 index 000000000..2f8dbca99 --- /dev/null +++ b/Tools/NativeHost/Info.plist @@ -0,0 +1,30 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIconFile + icon + CFBundleShortVersionString + 0.9 + CFBundleIdentifier + com.280north.${PRODUCT_NAME:identifier} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + NativeHost + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleVersion + 1.0 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + diff --git a/Tools/NativeHost/Jakefile b/Tools/NativeHost/Jakefile new file mode 100644 index 000000000..f6151df83 --- /dev/null +++ b/Tools/NativeHost/Jakefile @@ -0,0 +1,36 @@ + +require("../../common.jake"); + +var OS = require("os"); +var task = require("jake").task + +task ("build", function() +{ + if (executableExists("xcodebuild")) + { + if (OS.system("xcodebuild -alltargets -configuration Release")) + OS.exit(1); + + rm_rf(FILE.join($BUILD_CJS_CAPPUCCINO, "support", "NativeHost.app")); + FILE.mkdirs(FILE.join($BUILD_CJS_CAPPUCCINO, "support")) + cp_r(FILE.join("build", "Release", "NativeHost.app"), FILE.join($BUILD_CJS_CAPPUCCINO, "support", "NativeHost.app")); + } + else + { + print("Building NativeHost requires Xcode."); + } +}); + +task ("clean", function() +{ + if (OS.system("xcodebuild clean")) + OS.exit(1); +}); + +task ("clobber", function() +{ + if (OS.system("xcodebuild clean")) + OS.exit(1); +}); + +task ("default", ["build"]); diff --git a/Tools/NativeHost/MenuBridge.h b/Tools/NativeHost/MenuBridge.h new file mode 100644 index 000000000..847237409 --- /dev/null +++ b/Tools/NativeHost/MenuBridge.h @@ -0,0 +1,22 @@ +// +// MenuBridge.h +// NativeHost +// +// Created by Francisco Tolmasky on 9/2/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import + + +@class WebScriptObject; + +@interface MenuBridge : NSMenu +{ + WebScriptObject * menuObject; +} + ++ (id)menuBridgeWithMenuObject:(WebScriptObject *)aMenuObject; +- (id)initWithMenuObject:(WebScriptObject *)aMenuObject; + +@end diff --git a/Tools/NativeHost/MenuBridge.m b/Tools/NativeHost/MenuBridge.m new file mode 100644 index 000000000..ad14a7191 --- /dev/null +++ b/Tools/NativeHost/MenuBridge.m @@ -0,0 +1,63 @@ +// +// MenuBridge.m +// NativeHost +// +// Created by Francisco Tolmasky on 9/2/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "WebScripObject+Objective-J.h" +#import "MenuItemBridge.h" +#import "MenuBridge.h" + + +@implementation MenuBridge + ++ (id)menuBridgeWithMenuObject:(WebScriptObject *)aMenuObject +{ + if (!aMenuObject) + return nil; + + static NSMapTable * cache = nil; + + if (!cache) + cache = [[NSMapTable mapTableWithStrongToStrongObjects] retain]; + + MenuBridge * menuBridge = [cache objectForKey:aMenuObject]; + + if (!menuBridge) + { + menuBridge = [MenuBridge alloc]; + + [cache setObject:menuBridge forKey:aMenuObject]; + + [[menuBridge initWithMenuObject:aMenuObject] autorelease]; + } + + return menuBridge; +} + +- (id)initWithMenuObject:(WebScriptObject *)aMenuObject +{ + self = [super initWithTitle:[aMenuObject bridgeSelector:@selector(title)]]; + + if (self) + { + menuObject = aMenuObject; + + NSInteger index = 0, + count = [[aMenuObject bridgeSelector:@selector(numberOfItems)] intValue]; + + for (; index < count; ++index) + [self addItem:[[[MenuItemBridge alloc] initWithMenuItemObject:[aMenuObject bridgeSelector:@selector(itemAtIndex:) withObject:[NSNumber numberWithInt:index]]] autorelease]]; + } + + return self; +} + +- (void)performActionForItemAtIndex:(NSInteger)anIndex +{ + [menuObject bridgeSelector:@selector(performActionForItemAtIndex:) withObject:[NSNumber numberWithInt:anIndex]]; +} + +@end diff --git a/Tools/NativeHost/MenuItemBridge.h b/Tools/NativeHost/MenuItemBridge.h new file mode 100644 index 000000000..86e70d6f2 --- /dev/null +++ b/Tools/NativeHost/MenuItemBridge.h @@ -0,0 +1,24 @@ +// +// ReflectMenuItem.h +// NativeHost +// +// Created by Francisco Tolmasky on 9/2/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import + + +@class WebScriptObject; + +@interface MenuItemBridge : NSMenuItem +{ + WebScriptObject * menuItemObject; +} + ++ (id)menuItemBridgeWithMenuItemObject:(WebScriptObject *)aMenuItemObject; +- (id)initWithMenuItemObject:(WebScriptObject *)aMenuItemObject; + +- (void)updateFromMenuItemObject; + +@end diff --git a/Tools/NativeHost/MenuItemBridge.m b/Tools/NativeHost/MenuItemBridge.m new file mode 100644 index 000000000..b661f35e8 --- /dev/null +++ b/Tools/NativeHost/MenuItemBridge.m @@ -0,0 +1,77 @@ +// +// ReflectMenuItem.m +// NativeHost +// +// Created by Francisco Tolmasky on 9/2/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "WebScripObject+Objective-J.h" +#import "MenuBridge.h" +#import "MenuItemBridge.h" + + +@implementation MenuItemBridge + ++ (id)menuItemBridgeWithMenuItemObject:(WebScriptObject *)aMenuItemObject +{ + if (!aMenuItemObject) + return nil; + + static NSMapTable * cache = nil; + + if (!cache) + cache = [[NSMapTable mapTableWithStrongToStrongObjects] retain]; + + MenuItemBridge * menuItemBridge = [cache objectForKey:aMenuItemObject]; + + if (!menuItemBridge) + { + menuItemBridge = [MenuBridge alloc]; + + [cache setObject:menuItemBridge forKey:aMenuItemObject]; + + [[menuItemBridge initWithMenuItemObject:aMenuItemObject] autorelease]; + } + + return menuItemBridge; +} + +- (id)initWithMenuItemObject:(WebScriptObject *)aMenuItemObject +{ + if ([[aMenuItemObject bridgeSelector:@selector(isSeparatorItem)] boolValue]) + return [[NSMenuItem separatorItem] retain]; + + self = [super init]; + + if (self) + { + menuItemObject = aMenuItemObject; + + [self updateFromMenuItemObject]; + } + + return self; +} +/* +- (BOOL)isSeparatorItem +{ + return [[menuItemObject bridgeSelector:@selector(isSeparatorItem)] boolValue]; +} +*/ +- (void)updateFromMenuItemObject +{ + [self setTitle:[menuItemObject bridgeSelector:@selector(title)]]; + [self setSubmenu:[MenuBridge menuBridgeWithMenuObject:[menuItemObject bridgeSelector:@selector(submenu)]]]; + [self setKeyEquivalent:[menuItemObject bridgeSelector:@selector(keyEquivalent)]]; + [self setKeyEquivalentModifierMask:[[menuItemObject evaluateObjectiveJ:[NSString stringWithFormat:@"(function() { var mask = %u; return (((mask & CPAlphaShiftKeyMask) ? %u : 0) | ((mask & CPShiftKeyMask) ? %u : 0) | ((mask & CPControlKeyMask) ? %u : 0) | ((mask & CPAlternateKeyMask) ? %u : 0) | ((mask & CPCommandKeyMask) ? %u : 0) | ((mask & CPNumericPadKeyMask) ? %u : 0) | ((mask & CPHelpKeyMask) ? %u : 0) | ((mask & CPFunctionKeyMask) ? %u : 0)) }())", [[menuItemObject bridgeSelector:@selector(keyEquivalentModifierMask)] unsignedIntegerValue], NSAlphaShiftKeyMask, NSShiftKeyMask, NSControlKeyMask, NSAlternateKeyMask, NSCommandKeyMask, NSNumericPadKeyMask, NSHelpKeyMask, NSFunctionKeyMask, NSDeviceIndependentModifierFlagsMask]] unsignedIntegerValue]];[self setAction:@selector(bridgeAction:)]; + [self setTarget:self]; + [self setAction:@selector(bridgeAction:)]; +} + +// This exists so that the menu items are enabled. +- (void)bridgeAction:(id)aSender +{ +} + +@end diff --git a/Tools/NativeHost/NSObject+WebAdditions.h b/Tools/NativeHost/NSObject+WebAdditions.h new file mode 100644 index 000000000..b4ab41035 --- /dev/null +++ b/Tools/NativeHost/NSObject+WebAdditions.h @@ -0,0 +1,16 @@ +// +// NSObject+WebAdditions.h +// NativeHost +// +// Created by Francisco Tolmasky on 11/16/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import + + +@interface NSObject (WebAdditions) + +- (BOOL)webBoolValue; + +@end diff --git a/Tools/NativeHost/NSObject+WebAdditions.m b/Tools/NativeHost/NSObject+WebAdditions.m new file mode 100644 index 000000000..1ffd942a1 --- /dev/null +++ b/Tools/NativeHost/NSObject+WebAdditions.m @@ -0,0 +1,19 @@ +// +// NSObject+WebAdditions.m +// NativeHost +// +// Created by Francisco Tolmasky on 11/16/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "NSObject+WebAdditions.h" + + +@implementation NSObject (WebAdditions) + +- (BOOL)webBoolValue +{ + return [self isKindOfClass:[NSNumber class]] && [(NSNumber *)self boolValue]; +} + +@end diff --git a/Tools/NativeHost/NSURL+Additions.h b/Tools/NativeHost/NSURL+Additions.h new file mode 100644 index 000000000..32cee28f6 --- /dev/null +++ b/Tools/NativeHost/NSURL+Additions.h @@ -0,0 +1,17 @@ +// +// NSURL+Additions.h +// NativeHost +// +// Created by Francisco Tolmasky on 6/3/09. +// Copyright 2009 280 North, Inc. All rights reserved. +// + +#import + + +@interface NSURL (Additions) + +- (NSURL *)HTTPFileSystemURL; ++ (NSURL *)fileSystemURLFromHTTPFileSystemString:(NSString *)aString; + +@end diff --git a/Tools/NativeHost/NSURL+Additions.m b/Tools/NativeHost/NSURL+Additions.m new file mode 100644 index 000000000..39cabb1a3 --- /dev/null +++ b/Tools/NativeHost/NSURL+Additions.m @@ -0,0 +1,36 @@ +// +// NSURL+Additions.m +// NativeHost +// +// Created by Francisco Tolmasky on 6/3/09. +// Copyright 2009 280 North, Inc. All rights reserved. +// + +#import "NSURL+Additions.h" +#import "AppController.h" + +@implementation NSURL (Additions) + +- (NSURL *)HTTPFileSystemURL +{ + if ([[self scheme] caseInsensitiveCompare:@"file"] == NSOrderedSame) + return [[[NSURL alloc] initWithScheme:@"http" + host:[NSString stringWithFormat:@"127.0.0.1:%d", SERVER_PORT] + path:[@"/filesystem/" stringByAppendingString:[self path]]] autorelease]; + + return [[self copy] autorelease]; +} + ++ (NSURL *)fileSystemURLFromHTTPFileSystemString:(NSString *)aString +{ + NSURL *httpURL = [NSURL URLWithString:aString]; + NSString *path = [httpURL path]; + NSRange range = [path rangeOfString:@"/filesystem"]; + + if (range.location == 0 && NSMaxRange(range) < [path length]) + path = [path substringFromIndex:[@"/filesystem" length]]; + + return [[[NSURL alloc] initFileURLWithPath:path] autorelease]; +} + +@end diff --git a/Tools/NativeHost/NativeHost.xcodeproj/TemplateIcon.icns b/Tools/NativeHost/NativeHost.xcodeproj/TemplateIcon.icns new file mode 100644 index 000000000..62cb7015e Binary files /dev/null and b/Tools/NativeHost/NativeHost.xcodeproj/TemplateIcon.icns differ diff --git a/Tools/NativeHost/NativeHost.xcodeproj/project.pbxproj b/Tools/NativeHost/NativeHost.xcodeproj/project.pbxproj new file mode 100644 index 000000000..f7d2bbc37 --- /dev/null +++ b/Tools/NativeHost/NativeHost.xcodeproj/project.pbxproj @@ -0,0 +1,416 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 45; + objects = { + +/* Begin PBXBuildFile section */ + 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; }; + 4D55A7941180F1E000B5187D /* icon.icns in Resources */ = {isa = PBXBuildFile; fileRef = 4D55A7931180F1BE00B5187D /* icon.icns */; }; + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; }; + 8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; }; + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; }; + 9E2E533A103B33CB00DF2D13 /* Server.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E2E5339103B33CB00DF2D13 /* Server.m */; }; + 9E43AC84104DB51400263989 /* WebScripObject+Objective-J.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E43AC83104DB51400263989 /* WebScripObject+Objective-J.m */; }; + 9E44CA9D0FD798FE00860A54 /* NSURL+Additions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E44CA9C0FD798FE00860A54 /* NSURL+Additions.m */; }; + 9E4EBB4210892BDE0091C8E9 /* BridgedMethods.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E4EBB4110892BDE0091C8E9 /* BridgedMethods.m */; }; + 9E52D2F9119B05EF00B831FC /* AppController+WebPolicyDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E52D2F8119B05EF00B831FC /* AppController+WebPolicyDelegate.m */; }; + 9E535D05104F6F4900C119FA /* AppController+MainMenu.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E535D04104F6F4900C119FA /* AppController+MainMenu.m */; }; + 9E535D32104F73FB00C119FA /* MenuBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E535D31104F73FB00C119FA /* MenuBridge.m */; }; + 9E535D37104F761F00C119FA /* MenuItemBridge.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E535D36104F761F00C119FA /* MenuItemBridge.m */; }; + 9E55CFE50FE8750C007D1D64 /* Console.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E55CFE40FE8750C007D1D64 /* Console.m */; }; + 9E5D4F0D1090FB6E00E76B38 /* WebWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = 9E5D4F0C1090FB6E00E76B38 /* WebWindow.m */; }; + 9E9DD6C00FC93C7A0001EF2E /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9E9DD6BF0FC93C7A0001EF2E /* WebKit.framework */; }; + 9EA1BEF7107E7EE500CDE684 /* Application.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EA1BEF6107E7EE500CDE684 /* Application.m */; }; + 9EB99C960FD8968100AA94A2 /* AppController.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EB99C950FD8968100AA94A2 /* AppController.m */; }; + 9EBC421E10B1D07100C2AE33 /* NSObject+WebAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 9EBC421D10B1D07100C2AE33 /* NSObject+WebAdditions.m */; }; + 9ED96C8A1020072300D24B9C /* AppController+WebUIDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9ED96C871020072300D24B9C /* AppController+WebUIDelegate.m */; }; + 9ED96C8B1020072300D24B9C /* AppController+WebFrameLoadDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9ED96C881020072300D24B9C /* AppController+WebFrameLoadDelegate.m */; }; + 9ED96C8C1020072300D24B9C /* AppController+WebResourceLoadDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 9ED96C891020072300D24B9C /* AppController+WebResourceLoadDelegate.m */; }; + AD8D31C6108F98B00008F7C4 /* SSCrypto.m in Sources */ = {isa = PBXBuildFile; fileRef = AD8D31C5108F98B00008F7C4 /* SSCrypto.m */; }; + AD8D31C9108F98E40008F7C4 /* libssl.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AD8D31C8108F98E40008F7C4 /* libssl.dylib */; }; + AD8D31CB108F98EF0008F7C4 /* libcrypto.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = AD8D31CA108F98EF0008F7C4 /* libcrypto.dylib */; }; + AD8D350F109040300008F7C4 /* IOKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD8D350E109040300008F7C4 /* IOKit.framework */; }; + ADCCEF6810D43C970055BDFD /* WebPasteboardProxy.m in Sources */ = {isa = PBXBuildFile; fileRef = ADCCEF3310D4370F0055BDFD /* WebPasteboardProxy.m */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 10; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; }; + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; }; + 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; + 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; }; + 29B97316FDCFA39411CA2CEA /* main.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = ""; }; + 29B97324FDCFA39411CA2CEA /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 29B97325FDCFA39411CA2CEA /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 32CA4F630368D1EE00C91783 /* NativeHost_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NativeHost_Prefix.pch; sourceTree = ""; }; + 4D55A7931180F1BE00B5187D /* icon.icns */ = {isa = PBXFileReference; lastKnownFileType = image.icns; path = icon.icns; sourceTree = ""; }; + 8D1107310486CEB800E47090 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 8D1107320486CEB800E47090 /* NativeHost.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = NativeHost.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 9E2E5338103B33CB00DF2D13 /* Server.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Server.h; sourceTree = ""; }; + 9E2E5339103B33CB00DF2D13 /* Server.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Server.m; sourceTree = ""; }; + 9E43AC82104DB51400263989 /* WebScripObject+Objective-J.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "WebScripObject+Objective-J.h"; sourceTree = ""; }; + 9E43AC83104DB51400263989 /* WebScripObject+Objective-J.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "WebScripObject+Objective-J.m"; sourceTree = ""; }; + 9E44CA9B0FD798FE00860A54 /* NSURL+Additions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSURL+Additions.h"; sourceTree = ""; }; + 9E44CA9C0FD798FE00860A54 /* NSURL+Additions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSURL+Additions.m"; sourceTree = ""; }; + 9E4EBB4010892BDE0091C8E9 /* BridgedMethods.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BridgedMethods.h; sourceTree = ""; }; + 9E4EBB4110892BDE0091C8E9 /* BridgedMethods.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BridgedMethods.m; sourceTree = ""; }; + 9E52D2F8119B05EF00B831FC /* AppController+WebPolicyDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AppController+WebPolicyDelegate.m"; sourceTree = ""; }; + 9E535D04104F6F4900C119FA /* AppController+MainMenu.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AppController+MainMenu.m"; sourceTree = ""; }; + 9E535D30104F73FB00C119FA /* MenuBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuBridge.h; sourceTree = ""; }; + 9E535D31104F73FB00C119FA /* MenuBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuBridge.m; sourceTree = ""; }; + 9E535D35104F761F00C119FA /* MenuItemBridge.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MenuItemBridge.h; sourceTree = ""; }; + 9E535D36104F761F00C119FA /* MenuItemBridge.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MenuItemBridge.m; sourceTree = ""; }; + 9E55CFE30FE8750C007D1D64 /* Console.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Console.h; sourceTree = ""; }; + 9E55CFE40FE8750C007D1D64 /* Console.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Console.m; sourceTree = ""; }; + 9E5D4F0B1090FB6E00E76B38 /* WebWindow.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebWindow.h; sourceTree = ""; }; + 9E5D4F0C1090FB6E00E76B38 /* WebWindow.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebWindow.m; sourceTree = ""; }; + 9E9DD6BF0FC93C7A0001EF2E /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; }; + 9EA1BEF5107E7EE500CDE684 /* Application.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Application.h; sourceTree = ""; }; + 9EA1BEF6107E7EE500CDE684 /* Application.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = Application.m; sourceTree = ""; }; + 9EB99C940FD8968100AA94A2 /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = ""; }; + 9EB99C950FD8968100AA94A2 /* AppController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppController.m; sourceTree = ""; }; + 9EBC421C10B1D07100C2AE33 /* NSObject+WebAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "NSObject+WebAdditions.h"; sourceTree = ""; }; + 9EBC421D10B1D07100C2AE33 /* NSObject+WebAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "NSObject+WebAdditions.m"; sourceTree = ""; }; + 9ED96C871020072300D24B9C /* AppController+WebUIDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AppController+WebUIDelegate.m"; sourceTree = ""; }; + 9ED96C881020072300D24B9C /* AppController+WebFrameLoadDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AppController+WebFrameLoadDelegate.m"; sourceTree = ""; }; + 9ED96C891020072300D24B9C /* AppController+WebResourceLoadDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "AppController+WebResourceLoadDelegate.m"; sourceTree = ""; }; + AD8D31C4108F98B00008F7C4 /* SSCrypto.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SSCrypto.h; sourceTree = ""; }; + AD8D31C5108F98B00008F7C4 /* SSCrypto.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = SSCrypto.m; sourceTree = ""; }; + AD8D31C8108F98E40008F7C4 /* libssl.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libssl.dylib; path = /usr/lib/libssl.dylib; sourceTree = ""; }; + AD8D31CA108F98EF0008F7C4 /* libcrypto.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libcrypto.dylib; path = /usr/lib/libcrypto.dylib; sourceTree = ""; }; + AD8D350E109040300008F7C4 /* IOKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = IOKit.framework; path = /System/Library/Frameworks/IOKit.framework; sourceTree = ""; }; + ADCCEF3210D4370F0055BDFD /* WebPasteboardProxy.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WebPasteboardProxy.h; sourceTree = ""; }; + ADCCEF3310D4370F0055BDFD /* WebPasteboardProxy.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = WebPasteboardProxy.m; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 8D11072E0486CEB800E47090 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */, + 9E9DD6C00FC93C7A0001EF2E /* WebKit.framework in Frameworks */, + AD8D31C9108F98E40008F7C4 /* libssl.dylib in Frameworks */, + AD8D31CB108F98EF0008F7C4 /* libcrypto.dylib in Frameworks */, + AD8D350F109040300008F7C4 /* IOKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 080E96DDFE201D6D7F000001 /* Classes */ = { + isa = PBXGroup; + children = ( + 9E535D04104F6F4900C119FA /* AppController+MainMenu.m */, + 9ED96C881020072300D24B9C /* AppController+WebFrameLoadDelegate.m */, + 9ED96C891020072300D24B9C /* AppController+WebResourceLoadDelegate.m */, + 9ED96C871020072300D24B9C /* AppController+WebUIDelegate.m */, + 9E52D2F8119B05EF00B831FC /* AppController+WebPolicyDelegate.m */, + 9EB99C940FD8968100AA94A2 /* AppController.h */, + 9EB99C950FD8968100AA94A2 /* AppController.m */, + 9EA1BEF5107E7EE500CDE684 /* Application.h */, + 9EA1BEF6107E7EE500CDE684 /* Application.m */, + 9E55CFE30FE8750C007D1D64 /* Console.h */, + 9E55CFE40FE8750C007D1D64 /* Console.m */, + 9E2E5338103B33CB00DF2D13 /* Server.h */, + 9E2E5339103B33CB00DF2D13 /* Server.m */, + AD8D31C4108F98B00008F7C4 /* SSCrypto.h */, + AD8D31C5108F98B00008F7C4 /* SSCrypto.m */, + 9E5D4F0B1090FB6E00E76B38 /* WebWindow.h */, + 9E5D4F0C1090FB6E00E76B38 /* WebWindow.m */, + 9E1E34190FCC82A5007F8CA0 /* Bridge */, + ); + name = Classes; + sourceTree = ""; + }; + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */ = { + isa = PBXGroup; + children = ( + AD8D31CA108F98EF0008F7C4 /* libcrypto.dylib */, + AD8D31C8108F98E40008F7C4 /* libssl.dylib */, + 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */, + AD8D350E109040300008F7C4 /* IOKit.framework */, + ); + name = "Linked Frameworks"; + sourceTree = ""; + }; + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */ = { + isa = PBXGroup; + children = ( + 29B97324FDCFA39411CA2CEA /* AppKit.framework */, + 13E42FB307B3F0F600E4EEF1 /* CoreData.framework */, + 29B97325FDCFA39411CA2CEA /* Foundation.framework */, + 9E9DD6BF0FC93C7A0001EF2E /* WebKit.framework */, + ); + name = "Other Frameworks"; + sourceTree = ""; + }; + 19C28FACFE9D520D11CA2CBB /* Products */ = { + isa = PBXGroup; + children = ( + 8D1107320486CEB800E47090 /* NativeHost.app */, + ); + name = Products; + sourceTree = ""; + }; + 29B97314FDCFA39411CA2CEA /* NativeHost */ = { + isa = PBXGroup; + children = ( + 080E96DDFE201D6D7F000001 /* Classes */, + 29B97315FDCFA39411CA2CEA /* Other Sources */, + 29B97317FDCFA39411CA2CEA /* Resources */, + 29B97323FDCFA39411CA2CEA /* Frameworks */, + 19C28FACFE9D520D11CA2CBB /* Products */, + ); + name = NativeHost; + sourceTree = ""; + }; + 29B97315FDCFA39411CA2CEA /* Other Sources */ = { + isa = PBXGroup; + children = ( + 29B97316FDCFA39411CA2CEA /* main.m */, + 32CA4F630368D1EE00C91783 /* NativeHost_Prefix.pch */, + ); + name = "Other Sources"; + sourceTree = ""; + }; + 29B97317FDCFA39411CA2CEA /* Resources */ = { + isa = PBXGroup; + children = ( + 4D55A7931180F1BE00B5187D /* icon.icns */, + 8D1107310486CEB800E47090 /* Info.plist */, + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */, + 1DDD58140DA1D0A300B32029 /* MainMenu.xib */, + ); + name = Resources; + sourceTree = ""; + }; + 29B97323FDCFA39411CA2CEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1058C7A0FEA54F0111CA2CBB /* Linked Frameworks */, + 1058C7A2FEA54F0111CA2CBB /* Other Frameworks */, + ); + name = Frameworks; + sourceTree = ""; + }; + 9E1E34190FCC82A5007F8CA0 /* Bridge */ = { + isa = PBXGroup; + children = ( + 9E535DB7104F923E00C119FA /* Menus */, + 9E4EBB4010892BDE0091C8E9 /* BridgedMethods.h */, + 9E4EBB4110892BDE0091C8E9 /* BridgedMethods.m */, + 9EBC421C10B1D07100C2AE33 /* NSObject+WebAdditions.h */, + 9EBC421D10B1D07100C2AE33 /* NSObject+WebAdditions.m */, + 9E44CA9B0FD798FE00860A54 /* NSURL+Additions.h */, + 9E44CA9C0FD798FE00860A54 /* NSURL+Additions.m */, + ADCCEF3210D4370F0055BDFD /* WebPasteboardProxy.h */, + ADCCEF3310D4370F0055BDFD /* WebPasteboardProxy.m */, + 9E43AC82104DB51400263989 /* WebScripObject+Objective-J.h */, + 9E43AC83104DB51400263989 /* WebScripObject+Objective-J.m */, + ); + name = Bridge; + sourceTree = ""; + }; + 9E535DB7104F923E00C119FA /* Menus */ = { + isa = PBXGroup; + children = ( + 9E535D30104F73FB00C119FA /* MenuBridge.h */, + 9E535D31104F73FB00C119FA /* MenuBridge.m */, + 9E535D35104F761F00C119FA /* MenuItemBridge.h */, + 9E535D36104F761F00C119FA /* MenuItemBridge.m */, + ); + name = Menus; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 8D1107260486CEB800E47090 /* NativeHost */ = { + isa = PBXNativeTarget; + buildConfigurationList = C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "NativeHost" */; + buildPhases = ( + 8D1107290486CEB800E47090 /* Resources */, + 8D11072C0486CEB800E47090 /* Sources */, + 8D11072E0486CEB800E47090 /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = NativeHost; + productInstallPath = "$(HOME)/Applications"; + productName = NativeHost; + productReference = 8D1107320486CEB800E47090 /* NativeHost.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 29B97313FDCFA39411CA2CEA /* Project object */ = { + isa = PBXProject; + buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "NativeHost" */; + compatibilityVersion = "Xcode 3.1"; + hasScannedForEncodings = 1; + mainGroup = 29B97314FDCFA39411CA2CEA /* NativeHost */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 8D1107260486CEB800E47090 /* NativeHost */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 8D1107290486CEB800E47090 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 8D11072B0486CEB800E47090 /* InfoPlist.strings in Resources */, + 1DDD58160DA1D0A300B32029 /* MainMenu.xib in Resources */, + 4D55A7941180F1E000B5187D /* icon.icns in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 8D11072C0486CEB800E47090 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 9E535D05104F6F4900C119FA /* AppController+MainMenu.m in Sources */, + 9ED96C8B1020072300D24B9C /* AppController+WebFrameLoadDelegate.m in Sources */, + 9ED96C8C1020072300D24B9C /* AppController+WebResourceLoadDelegate.m in Sources */, + 9ED96C8A1020072300D24B9C /* AppController+WebUIDelegate.m in Sources */, + 9EB99C960FD8968100AA94A2 /* AppController.m in Sources */, + 9EA1BEF7107E7EE500CDE684 /* Application.m in Sources */, + 9E4EBB4210892BDE0091C8E9 /* BridgedMethods.m in Sources */, + 9E55CFE50FE8750C007D1D64 /* Console.m in Sources */, + 8D11072D0486CEB800E47090 /* main.m in Sources */, + 9E535D32104F73FB00C119FA /* MenuBridge.m in Sources */, + 9E535D37104F761F00C119FA /* MenuItemBridge.m in Sources */, + 9EBC421E10B1D07100C2AE33 /* NSObject+WebAdditions.m in Sources */, + 9E44CA9D0FD798FE00860A54 /* NSURL+Additions.m in Sources */, + 9E2E533A103B33CB00DF2D13 /* Server.m in Sources */, + AD8D31C6108F98B00008F7C4 /* SSCrypto.m in Sources */, + ADCCEF6810D43C970055BDFD /* WebPasteboardProxy.m in Sources */, + 9E43AC84104DB51400263989 /* WebScripObject+Objective-J.m in Sources */, + 9E5D4F0D1090FB6E00E76B38 /* WebWindow.m in Sources */, + 9E52D2F9119B05EF00B831FC /* AppController+WebPolicyDelegate.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXVariantGroup section */ + 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = { + isa = PBXVariantGroup; + children = ( + 089C165DFE840E0CC02AAC07 /* English */, + ); + name = InfoPlist.strings; + sourceTree = ""; + }; + 1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = { + isa = PBXVariantGroup; + children = ( + 1DDD58150DA1D0A300B32029 /* English */, + ); + name = MainMenu.xib; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + C01FCF4B08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + COPY_PHASE_STRIP = NO; + GCC_DYNAMIC_NO_PIC = NO; + GCC_ENABLE_FIX_AND_CONTINUE = YES; + GCC_MODEL_TUNING = G5; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = NativeHost_Prefix.pch; + GCC_PREPROCESSOR_DEFINITIONS = "ACTIVATION=0"; + INFOPLIST_FILE = Info.plist; + INFOPLIST_PREPROCESS = YES; + INFOPLIST_PREPROCESSOR_DEFINITIONS = "ACTIVATION=0"; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = NativeHost; + }; + name = Debug; + }; + C01FCF4C08A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + GCC_MODEL_TUNING = G5; + GCC_PRECOMPILE_PREFIX_HEADER = YES; + GCC_PREFIX_HEADER = NativeHost_Prefix.pch; + INFOPLIST_FILE = Info.plist; + INFOPLIST_PREPROCESS = YES; + INFOPLIST_PREPROCESSOR_DEFINITIONS = "ACTIVATION=0"; + INSTALL_PATH = "$(HOME)/Applications"; + PRODUCT_NAME = NativeHost; + }; + name = Release; + }; + C01FCF4F08A954540054247B /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_VERSION = 4.2; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + ONLY_ACTIVE_ARCH = YES; + PREBINDING = NO; + SDKROOT = macosx10.5; + }; + name = Debug; + }; + C01FCF5008A954540054247B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ARCHS = "$(ARCHS_STANDARD_32_BIT)"; + GCC_C_LANGUAGE_STANDARD = c99; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + PREBINDING = NO; + SDKROOT = macosx10.5; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C01FCF4A08A954540054247B /* Build configuration list for PBXNativeTarget "NativeHost" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4B08A954540054247B /* Debug */, + C01FCF4C08A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + C01FCF4E08A954540054247B /* Build configuration list for PBXProject "NativeHost" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C01FCF4F08A954540054247B /* Debug */, + C01FCF5008A954540054247B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 29B97313FDCFA39411CA2CEA /* Project object */; +} diff --git a/Tools/NativeHost/NativeHost_Prefix.pch b/Tools/NativeHost/NativeHost_Prefix.pch new file mode 100644 index 000000000..17e44cd86 --- /dev/null +++ b/Tools/NativeHost/NativeHost_Prefix.pch @@ -0,0 +1,8 @@ +// +// Prefix header for all source files of the 'NativeHost' target in the 'NativeHost' project +// + +#ifdef __OBJC__ + #import + #import +#endif diff --git a/Tools/NativeHost/SSCrypto.h b/Tools/NativeHost/SSCrypto.h new file mode 100644 index 000000000..89ff70a9e --- /dev/null +++ b/Tools/NativeHost/SSCrypto.h @@ -0,0 +1,116 @@ +/* + Copyright (c) 2003-2006, Septicus Software All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Septicus Software nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +// +// SSCrypto.h +// +// Created by Ed Silva on Sat May 31 2003. +// Copyright (c) 2003-2006 Septicus Software. All rights reserved. +// + + +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import +#import + +@interface NSData (HexDump) +- (NSString *)encodeBase64; +- (NSString *)encodeBase64WithNewlines:(BOOL)encodeWithNewlines; +- (NSData *)decodeBase64; +- (NSData *)decodeBase64WithNewLines:(BOOL)decodeWithNewLines; +- (NSString *)hexval; +- (NSString *)hexdump; +@end + +@interface SSCrypto : NSObject +{ + NSData *symmetricKey; + NSData *cipherText; + NSData *clearText; + NSData *publicKey; + NSData *privateKey; + + BOOL isSymmetric; +} + +- (id)init; +- (id)initWithSymmetricKey:(NSData *)k; +- (id)initWithPublicKey:(NSData *)pub; +- (id)initWithPrivateKey:(NSData *)priv; +- (id)initWithPublicKey:(NSData *)pub privateKey:(NSData *)priv; + +- (BOOL)isSymmetric; +- (void)setIsSymmetric:(BOOL)flag; + +- (NSData *)symmetricKey; +- (void)setSymmetricKey:(NSData *)k; + +- (NSData *)publicKey; +- (void)setPublicKey:(NSData *)k; + +- (NSData *)privateKey; +- (void)setPrivateKey:(NSData *)k; + +- (NSData *)clearTextAsData; +- (NSString *)clearTextAsString; +- (void)setClearTextWithData:(NSData *)c; +- (void)setClearTextWithString:(NSString *)c; + +- (NSData *)cipherTextAsData; +- (NSString *)cipherTextAsString; +- (void)setCipherText:(NSData *)c; + +- (NSData *)decrypt; +- (NSData *)decrypt:(NSString *)cipherName; + +- (NSData *)verify; + +- (NSData *)encrypt; +- (NSData *)encrypt:(NSString *)cipherName; + +- (NSData *)sign; + +- (NSData *)digest:(NSString *)digestName; + ++ (NSData *)generateRSAPrivateKeyWithLength:(int)length; ++ (NSData *)generateRSAPublicKeyFromPrivateKey:(NSData *)privateKey; ++ (NSData *)getKeyDataWithLength:(int)length; ++ (NSData *)getKeyDataWithLength:(int)length fromPassword:(NSString *)pass withSalt:(NSString *)salt; ++ (NSData *)getKeyDataWithLength:(int)length fromPassword:(NSString *)pass withSalt:(NSString *)salt withIterations:(int)count; ++ (NSData *)getSHA1ForData:(NSData *)d; ++ (NSData *)getMD5ForData:(NSData *)d; + +@end diff --git a/Tools/NativeHost/SSCrypto.m b/Tools/NativeHost/SSCrypto.m new file mode 100644 index 000000000..09c557531 --- /dev/null +++ b/Tools/NativeHost/SSCrypto.m @@ -0,0 +1,1214 @@ +/* + Copyright (c) 2003-2006, Septicus Software All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + * Neither the name of Septicus Software nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS + IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A + PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER + OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +// +// SSCrypto.m +// SimpleWebCam +// +// Created by Ed Silva on Sat May 31 2003. +// Copyright (c) 2003-2006 Septicus Software. All rights reserved. +// + +#import "SSCrypto.h" + +@implementation NSData (HexDump) + +/** + * Encodes the current data in base64, and creates and returns an NSString from the result. + * This is the same as piping data through "... | openssl enc -base64" on the command line. + * + * Code courtesy of DaveDribin (http://www.dribin.org/dave/) + * Taken from http://www.cocoadev.com/index.pl?BaseSixtyFour +**/ +- (NSString *)encodeBase64 +{ + return [self encodeBase64WithNewlines: YES]; +} + +/** + * Encodes the current data in base64, and creates and returns an NSString from the result. + * This is the same as piping data through "... | openssl enc -base64" on the command line. + * + * Code courtesy of DaveDribin (http://www.dribin.org/dave/) + * Taken from http://www.cocoadev.com/index.pl?BaseSixtyFour +**/ +- (NSString *)encodeBase64WithNewlines:(BOOL)encodeWithNewlines +{ + // Create a memory buffer which will contain the Base64 encoded string + BIO * mem = BIO_new(BIO_s_mem()); + + // Push on a Base64 filter so that writing to the buffer encodes the data + BIO * b64 = BIO_new(BIO_f_base64()); + if (!encodeWithNewlines) + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + mem = BIO_push(b64, mem); + + // Encode all the data + BIO_write(mem, [self bytes], [self length]); + BIO_flush(mem); + + // Create a new string from the data in the memory buffer + char * base64Pointer; + long base64Length = BIO_get_mem_data(mem, &base64Pointer); + // we use an NSData here since the base64pointer is not null terminated + // the byte array the NSData return should be + // so making an NSString from that will be okay + NSData * base64data = [NSData dataWithBytes:base64Pointer length:base64Length]; + NSString * base64String = [NSString stringWithUTF8String:[base64data bytes]]; + + // Clean up and go home + BIO_free_all(mem); + + if (!encodeWithNewlines) + base64String = [[base64String componentsSeparatedByString:@"\n"] componentsJoinedByString:@""]; + + return base64String; +} + +- (NSData *)decodeBase64 +{ + return [self decodeBase64WithNewLines:YES]; +} + +- (NSData *)decodeBase64WithNewLines:(BOOL)encodedWithNewlines +{ + // Create a memory buffer containing Base64 encoded string data + BIO * mem = BIO_new_mem_buf((void *) [self bytes], [self length]); + + // Push a Base64 filter so that reading from the buffer decodes it + BIO * b64 = BIO_new(BIO_f_base64()); + if (!encodedWithNewlines) + BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); + mem = BIO_push(b64, mem); + + // Decode into an NSMutableData + NSMutableData * data = [NSMutableData data]; + char inbuf[512]; + int inlen; + while ((inlen = BIO_read(mem, inbuf, sizeof(inbuf))) > 0) + [data appendBytes: inbuf length: inlen]; + + // Clean up and go home + BIO_free_all(mem); + return data; +} + +- (NSString *)hexval +{ + NSMutableString *hex = [NSMutableString string]; + unsigned char *bytes = (unsigned char *)[self bytes]; + char temp[3]; + int i = 0; + + for (i = 0; i < [self length]; i++) { + temp[0] = temp[1] = temp[2] = 0; + (void)sprintf(temp, "%02x", bytes[i]); + [hex appendString:[NSString stringWithUTF8String:temp]]; + } + + return hex; +} + +- (NSString *)hexdump +{ + NSMutableString *ret=[NSMutableString stringWithCapacity:[self length]*2]; + /* dumps size bytes of *data to string. Looks like: + * [0000] 75 6E 6B 6E 6F 77 6E 20 + * 30 FF 00 00 00 00 39 00 unknown 0.....9. + * (in a single line of course) + */ + unsigned int size= [self length]; + const unsigned char *p = [self bytes]; + unsigned char c; + int n; + char bytestr[4] = {0}; + char addrstr[10] = {0}; + char hexstr[ 16*3 + 5] = {0}; + char charstr[16*1 + 5] = {0}; + for(n=1;n<=size;n++) { + if (n%16 == 1) { + /* store address for this line */ + snprintf(addrstr, sizeof(addrstr), "%.4x", + (unsigned int)((long)p-(long)self) ); + } + + c = *p; + if (isalnum(c) == 0) { + c = '.'; + } + + /* store hex str (for left side) */ + snprintf(bytestr, sizeof(bytestr), "%02X ", *p); + strncat(hexstr, bytestr, sizeof(hexstr)-strlen(hexstr)-1); + + /* store char str (for right side) */ + snprintf(bytestr, sizeof(bytestr), "%c", c); + strncat(charstr, bytestr, sizeof(charstr)-strlen(charstr)-1); + + if(n%16 == 0) { + /* line completed */ + //printf("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr); + [ret appendString:[NSString stringWithFormat:@"[%4.4s] %-50.50s %s\n", + addrstr, hexstr, charstr]]; + hexstr[0] = 0; + charstr[0] = 0; + } else if(n%8 == 0) { + /* half line: add whitespaces */ + strncat(hexstr, " ", sizeof(hexstr)-strlen(hexstr)-1); + strncat(charstr, " ", sizeof(charstr)-strlen(charstr)-1); + } + p++; /* next byte */ + } + + if (strlen(hexstr) > 0) { + /* print rest of buffer if not empty */ + //printf("[%4.4s] %-50.50s %s\n", addrstr, hexstr, charstr); + [ret appendString:[NSString stringWithFormat:@"[%4.4s] %-50.50s %s\n", + addrstr, hexstr, charstr]]; + } + return ret; +} + +@end + +@interface SSCrypto (PrivateAPI) +- (void)setupOpenSSL; +- (void)cleanupOpenSSL; +@end + +// SSCrypto object +@implementation SSCrypto + +/** + * Generic constructor. + * Simply configures internal OpenSSL setup. +**/ +- (id)init +{ + if(self = [super init]) + { + // Call private method to handle the setup for internal OpenSSL stuff + [self setupOpenSSL]; + } + return self; +} + +/** + * Symmetric key constructor. + * Configures the instance to use symmetric encryption/decryption using the given symmetric key. +**/ +- (id)initWithSymmetricKey:(NSData *)k +{ + if(self = [super init]) + { + // Call private method to handle the setup for internal OpenSSL stuff + [self setupOpenSSL]; + + [self setSymmetricKey:k]; + [self setIsSymmetric:YES]; + } + return self; +} + +/** + * Public key only constructor. + * Configures the instance to use non-symmetric encryption/decryption, and to use the given public key. +**/ +- (id)initWithPublicKey:(NSData *)pub +{ + return [self initWithPublicKey:pub privateKey:nil]; +} + +/** + * Private key only constructor. + * Configures the instance to use non-symmetric encryption/decryption, and to use the given private key. +**/ +- (id)initWithPrivateKey:(NSData *)priv +{ + return [self initWithPublicKey:nil privateKey:priv]; +} + +/** + * Public and Private key constructor. + * Configures the instance to use non-symmetric encryption/decryption, and to use the given public and private keys. +**/ +- (id)initWithPublicKey:(NSData *)pub privateKey:(NSData *)priv; +{ + if(self = [super init]) + { + // Call private method to handle the setup for internal OpenSSL stuff + [self setupOpenSSL]; + + // Store the publicKey variable (if not nil) + if(pub != nil) + [self setPublicKey:pub]; + + // Store the privateKey variable (if not nil) + if(priv != nil) + [self setPrivateKey:priv]; + + // Since we're using public and private keys, we can assume we're not using symmetric encryption + [self setIsSymmetric:NO]; + } + return self; +} + +/** + * This method sets up everything needed to use the OpenSSL libraries later within the code. + * This method should be called by every constructor. +**/ +- (void)setupOpenSSL +{ + // OpenSSL keeps an internal table of digest algorithms and ciphers. + // It uses this table to lookup ciphers via functions such as EVP_get_cipher_byname(). + // OpenSSL_add_all_digests() adds all digest algorithms to the table. + // OpenSSL_add_all_ciphers() adds all cipher algorithms to the table. + // OpenSSL_add_all_algorithms() adds all algorithms to the table (digests and ciphers). + // EVP_cleanup() removes all ciphers and digests from the table. + // + // A typical application will call OpenSSL_add_all_algorithms() initially and EVP_cleanup() before exiting. + + OpenSSL_add_all_algorithms(); + + // ERR_load_crypto_strings() registers the error strings for all libcrypto functions. + // SSL_load_error_strings() does the same, but also registers the libssl error strings. + // ERR_free_strings() frees all previously loaded error strings. + // + // One of these functions should be called before generating textual error messages. + // However, this is not required when memory usage is an issue. + + ERR_load_crypto_strings(); +} + +/** + * Standard deallocation method. +**/ +- (void)dealloc +{ + // Cleanup all OpenSSL stuff + [self cleanupOpenSSL]; + + // Release all instance variables + [symmetricKey release]; + [cipherText release]; + [clearText release]; + [publicKey release]; + [privateKey release]; + + // Move up the inheritance chain + [super dealloc]; +} + +- (void)cleanupOpenSSL +{ + // EVP_cleanup() removes all ciphers and digests from the table. + EVP_cleanup(); + + // ERR_free_strings() frees all previously loaded error strings. + ERR_free_strings(); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Getter, Setter Methods: +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Returns whether or not symmetric encryption is to be used. + * If symmetric encryption is in use, then calls to encrypt or decrypt operate using symmetric encryption/decryption. + * Otherwise, it is assumed that asymmetric encryption/decryption is to be used. +**/ +- (BOOL)isSymmetric +{ + return isSymmetric; +} + +- (void)setIsSymmetric:(BOOL)flag +{ + isSymmetric = flag; +} + +- (NSData *)symmetricKey +{ + return symmetricKey; +} + +- (void)setSymmetricKey:(NSData *)k +{ + [k retain]; + [symmetricKey release]; + symmetricKey = k; +} + +/** + * Returns the public key currently in use. +**/ +- (NSData *)publicKey +{ + return publicKey; +} + +/** + * Sets the public key to use. + * Public keys are used to verify signed data, or to encrypt data. (ToDo) +**/ +- (void)setPublicKey:(NSData *)k +{ + [k retain]; + [publicKey release]; + publicKey = k; +} + +/** + * Returns the private key currently in use. +**/ +- (NSData *)privateKey +{ + return privateKey; +} + +/** + * Sets the private key to use. + * Private keys are used to sign data, or to decrypt data. (ToDo) + * + * The data that is provided should from a file (such as private.pem) that was generated by openssl. +**/ +- (void)setPrivateKey:(NSData *)k +{ + [k retain]; + [privateKey release]; + privateKey = k; +} + +/** + * Returns the clear text as plain NSData. + * The plain text contains the text that was previously set. + * It's the known text, which is to be encrypted, decrypted, etc. +**/ +- (NSData *)clearTextAsData +{ + return clearText; +} + +/** + * Returns the clear text formatted as an NSString. + * The plain text contains the text that was previously set. + * It's the known text, which is to be encrypted, decrypted, etc. +**/ +- (NSString *)clearTextAsString +{ + return [[[NSString alloc] initWithData:[self clearTextAsData] encoding:NSUTF8StringEncoding] autorelease]; +} + +/** + * Sets the clear text using the given data. + * The clear text will be used for encryption, decryption, etc. + * + * Note that the given data reference is retained and later used, so it shouldn't be externally modified. +**/ +- (void)setClearTextWithData:(NSData *)c +{ + [c retain]; + [clearText release]; + clearText = c; +} + +/** + * Sets the clear text using the given string. + * The clear text will be used for encryption, signing, and digests. + **/ +- (void)setClearTextWithString:(NSString *)c +{ + [clearText release]; + + // BUG FIX : PLL (2009/02/21) + // + // [c length] : Returns the number of Unicode characters in the receiver. + // For example "éàç test" in UTF8 is 11 bytes (c3 a9 c3 a0 c3 a7 20 74 65 73 74) + // but only 8 Unicode characters. + // So this will truncate the text and result in one error. + // + // clearText = [[NSData alloc] initWithBytes:[c UTF8String] length:[c length]]; + + // The number of bytes required to store the receiver in the encoding enc in a non-external representation. The length does not include space for a terminating NULL character. + unsigned int length = [c lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + clearText = [[NSData alloc] initWithBytes:[c UTF8String] length:length]; +} + +/** + * Returns the cipher text as plain NSData. + * The cipher text contains the most recent encrypted data. (Result of call to encrypt) +**/ +- (NSData *)cipherTextAsData +{ + return cipherText; +} + +/** + * Returns the ciper text formatted as an NSString. + * The ciper text contains the most recent encrypted data. (Result of call to encrypt) +**/ +- (NSString *)cipherTextAsString +{ + return [[[NSString alloc] initWithData:[self cipherTextAsData] encoding:NSUTF8StringEncoding] autorelease]; +} + +/** + * Sets the cipher text using the given data. + * The cipher text will be used for decryption and verifying. +**/ +- (void)setCipherText:(NSData *)c +{ + [c retain]; + [cipherText release]; + cipherText = c; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Decryption methods: +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Peforms decryption, and returns resulting clear text data. + * If symmetric decryption is being used, performs symmetric decryption using aes128. + * Otherwise, performs decryption using the private key. +**/ +- (NSData *)decrypt +{ + if([self isSymmetric] && [self symmetricKey]) + { + return [self decrypt:@"aes128"]; + } + else if([self privateKey]) + { + return [self decrypt:nil]; + } + else + { + NSLog(@"No symmetric key or private key is set!"); + return nil; + } +} + +/** + * Decrypts the cipher text data. + * If symmetric decryption is being used, then the decryption is done using the given cipher. + * Otherwise, asymmetric decryptions is used, and the data is encrypted using the private key. + * + * Returns the clear text data that is the result of the decryption. + * The resulting clear text data may also be later retrieved with the clearTextAsData or clearTextAsString methods. +**/ +- (NSData *)decrypt:(NSString *)cipherName +{ + // If there is no cipher text set, or the cipher text is an empty string (zero length data) + // then there is nothing to decrypt, and we may as well return nil + if(cipherText == nil || [cipherText length] == 0) + { + return nil; + } + + unsigned char *outbuf, iv[EVP_MAX_IV_LENGTH]; + int outlen, templen, inlen; + inlen = [cipherText length]; + unsigned char *input = (unsigned char *)[cipherText bytes]; + + if([self isSymmetric]) + { + // Use symmetric decryption... + + unsigned char evp_key[EVP_MAX_KEY_LENGTH] = {"\0"}; + EVP_CIPHER_CTX cCtx; + const EVP_CIPHER *cipher; + + if(cipherName) + { + cipher = EVP_get_cipherbyname((const char *)[cipherName UTF8String]); + if(!cipher) + { + NSLog(@"cannot get cipher with name %@", cipherName); + return nil; + } + } + else + { + cipher = EVP_bf_cbc(); + if(!cipher) + { + NSLog(@"cannot get cipher with name %@", @"EVP_bf_cbc"); + return nil; + } + } + + // Sometimes OpenSSL encrypted data contains an 8 byte salt. + // This is indicated by the "Salted__" prefix in the encrypted data. + + NSData *salt = nil; + + if([cipherText length] > 8+8) + { + if(strncmp((const char *)[cipherText bytes], "Salted__", 8) == 0) + { + salt = [cipherText subdataWithRange:NSMakeRange(8, 8)]; + + input += 16; + inlen -= 16; + } + } + + EVP_BytesToKey(cipher, EVP_md5(), [salt bytes], + [symmetricKey bytes], [symmetricKey length], 1, evp_key, iv); + + EVP_CIPHER_CTX_init(&cCtx); + + if (!EVP_DecryptInit(&cCtx, cipher, evp_key, iv)) + { + NSLog(@"EVP_DecryptInit() failed!"); + EVP_CIPHER_CTX_cleanup(&cCtx); + return nil; + } + EVP_CIPHER_CTX_set_key_length(&cCtx, EVP_MAX_KEY_LENGTH); + + // The data buffer passed to EVP_DecryptUpdate() should have sufficient room for + // (input_length + cipher_block_size) bytes unless the cipher block size is 1 in which + // case input_length bytes is sufficient. + + if(EVP_CIPHER_CTX_block_size(&cCtx) > 1) + outbuf = (unsigned char *)calloc(inlen + EVP_CIPHER_CTX_block_size(&cCtx), sizeof(unsigned char)); + else + outbuf = (unsigned char *)calloc(inlen, sizeof(unsigned char)); + + NSAssert(outbuf, @"Cannot allocate memory for buffer!"); + + if (!EVP_DecryptUpdate(&cCtx, outbuf, &outlen, input, inlen)) + { + NSLog(@"EVP_DecryptUpdate() failed!"); + EVP_CIPHER_CTX_cleanup(&cCtx); + return nil; + } + + if (!EVP_DecryptFinal(&cCtx, outbuf + outlen, &templen)) + { + NSLog(@"EVP_DecryptFinal() failed!"); + EVP_CIPHER_CTX_cleanup(&cCtx); + return nil; + } + + outlen += templen; + EVP_CIPHER_CTX_cleanup(&cCtx); + } + else + { + // Use asymmetric decryption... + + if([self privateKey] == nil) + { + NSLog(@"Cannot decrypt without the private key, which is currently nil"); + return nil; + } + + BIO *privateBIO = NULL; + RSA *privateRSA = NULL; + + if(!(privateBIO = BIO_new_mem_buf((unsigned char*)[[self privateKey] bytes], [[self privateKey] length]))) + { + NSLog(@"BIO_new_mem_buf() failed!"); + return nil; + } + + if(!PEM_read_bio_RSAPrivateKey(privateBIO, &privateRSA, NULL, NULL)) + { + NSLog(@"PEM_read_bio_RSAPrivateKey() failed!"); + return nil; + } + + // RSA_check_key() returns 1 if rsa is a valid RSA key, and 0 otherwise. + + unsigned long check = RSA_check_key(privateRSA); + if(check != 1) + { + NSLog(@"RSA_check_key() failed with result %d!", check); + return nil; + } + + // RSA_size() returns the RSA modulus size in bytes. + // It can be used to determine how much memory must be allocated for an RSA encrypted value. + + outbuf = (unsigned char *)malloc(RSA_size(privateRSA)); + + if(!(outlen = RSA_private_decrypt(inlen, input, outbuf, privateRSA, RSA_PKCS1_PADDING))) + { + NSLog(@"RSA_private_decrypt() failed!"); + return nil; + } + + if(outlen == -1) + { + NSLog(@"Decrypt error: %s (%s)", + ERR_error_string(ERR_get_error(), NULL), + ERR_reason_error_string(ERR_get_error())); + return nil; + } + + if (privateBIO) BIO_free(privateBIO); + if (privateRSA) RSA_free(privateRSA); + } + + // Store the decrypted data as the clear text + [self setClearTextWithData:[NSData dataWithBytes:outbuf length:outlen]]; + + // Release the outbuf, since it was malloc'd + if (outbuf) { + free(outbuf); + } + + return [self clearTextAsData]; +} + +/** + * Verifies (decrypts) the cipher text data using the public key. + * The resulting clear text data is returned. + * + * The resulting clear text data may also be later retrieved with the clearTextAsData or clearTextAsString methods. + **/ +- (NSData *)verify +{ + // If there is no cipher text set, or the cipher text is an empty string (zero length data) + // then there is nothing to decrypt, and we may as well return nil + if(cipherText == nil || [cipherText length] == 0) + { + return nil; + } + + unsigned char *outbuf; + int outlen, inlen; + inlen = [cipherText length]; + unsigned char *input = (unsigned char *)[cipherText bytes]; + + if([self publicKey] == nil) + { + NSLog(@"Cannot verify (decrypt) without the public key, which is currently nil"); + return nil; + } + + BIO *publicBIO = NULL; + RSA *publicRSA = NULL; + + if(!(publicBIO = BIO_new_mem_buf((unsigned char *)[[self publicKey] bytes], [[self publicKey] length]))) + { + NSLog(@"BIO_new_mem_buf() failed!"); + return nil; + } + + if(!PEM_read_bio_RSA_PUBKEY(publicBIO, &publicRSA, NULL, NULL)) + { + NSLog(@"PEM_read_bio_RSA_PUBKEY() failed!"); + return nil; + } + + // RSA_size() returns the RSA modulus size in bytes. + // It can be used to determine how much memory must be allocated for an RSA encrypted value. + + outbuf = (unsigned char *)malloc(RSA_size(publicRSA)); + + if(!(outlen = RSA_public_decrypt(inlen, input, outbuf, publicRSA, RSA_PKCS1_PADDING))) + { + NSLog(@"RSA_public_decrypt() failed!"); + return nil; + } + + if(outlen == -1) + { + NSLog(@"Decrypt error: %s (%s)", + ERR_error_string(ERR_get_error(), NULL), + ERR_reason_error_string(ERR_get_error())); + return nil; + } + + if (publicBIO) BIO_free(publicBIO); + if (publicRSA) RSA_free(publicRSA); + + // Store the decrypted data as the clear text + [self setClearTextWithData:[NSData dataWithBytes:outbuf length:outlen]]; + + // Release the outbuf, since it was malloc'd + if (outbuf) { + free(outbuf); + } + + return [self clearTextAsData]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Encryption methods: +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Peforms encryption, and returns resulting ciper text data. + * If symmetric encryption is being used, performs symmetric encryption using aes128. + * Otherwise, performs encryption using the public key. +**/ +- (NSData *)encrypt +{ + if([self isSymmetric] && [self symmetricKey]) + { + return [self encrypt:@"aes128"]; + } + else if([self publicKey]) + { + return [self encrypt:nil]; + } + else + { + NSLog(@"No symmetric key or public key is set!"); + return nil; + } +} + +/** + * Encrypts the clear text data. + * If symmetric encryption is being used, then the encryption is done using the given cipher. + * Otherwise, asymmetric encryption is used, and the data is encrypted using the public key. + * + * Returns the cipher text data that is the result of the encryption. + * The resulting cipher text data may also be later retrieved with the cipherTextAsData or cipherTextAsString methods. +**/ +- (NSData *)encrypt:(NSString *)cipherName +{ + // If there is no clear text set, or the clear text is an empty string (zero length data) + // then there is nothing to encrypt, and we may as well return nil + if(clearText == nil || [clearText length] == 0) + { + return nil; + } + + unsigned char *input = (unsigned char *)[clearText bytes]; + unsigned char *outbuf, iv[EVP_MAX_IV_LENGTH]; + int outlen, templen, inlen; + inlen = [clearText length]; + + if([self isSymmetric]) + { + // Perform symmetric encryption... + + unsigned char evp_key[EVP_MAX_KEY_LENGTH] = {"\0"}; + EVP_CIPHER_CTX cCtx; + const EVP_CIPHER *cipher; + + if (cipherName){ + cipher = EVP_get_cipherbyname((const char *)[cipherName UTF8String]); + if (!cipher){ + NSLog(@"cannot get cipher with name %@", cipherName); + return nil; + } + } else { + cipher = EVP_bf_cbc(); + if (!cipher){ + NSLog(@"cannot get cipher with name %@", @"EVP_bf_cbc"); + return nil; + } + } + + EVP_BytesToKey(cipher, EVP_md5(), NULL, + [[self symmetricKey] bytes], [[self symmetricKey] length], 1, evp_key, iv); + EVP_CIPHER_CTX_init(&cCtx); + + if (!EVP_EncryptInit(&cCtx, cipher, evp_key, iv)) { + NSLog(@"EVP_EncryptInit() failed!"); + EVP_CIPHER_CTX_cleanup(&cCtx); + return nil; + } + EVP_CIPHER_CTX_set_key_length(&cCtx, EVP_MAX_KEY_LENGTH); + + // The data buffer passed to EVP_EncryptUpdate() should have sufficient room for + // (input_length + cipher_block_size - 1) + + outbuf = (unsigned char *)calloc(inlen + EVP_CIPHER_CTX_block_size(&cCtx) - 1, sizeof(unsigned char)); + NSAssert(outbuf, @"Cannot allocate memory for buffer!"); + + if (!EVP_EncryptUpdate(&cCtx, outbuf, &outlen, input, inlen)) + { + NSLog(@"EVP_EncryptUpdate() failed!"); + EVP_CIPHER_CTX_cleanup(&cCtx); + return nil; + } + if (!EVP_EncryptFinal(&cCtx, outbuf + outlen, &templen)) + { + NSLog(@"EVP_EncryptFinal() failed!"); + EVP_CIPHER_CTX_cleanup(&cCtx); + return nil; + } + outlen += templen; + EVP_CIPHER_CTX_cleanup(&cCtx); + + } + else + { + // Perform asymmetric encryption... + + if([self publicKey] == nil) + { + NSLog(@"Cannot encrypt without the public key, which is currently nil"); + return nil; + } + + BIO *publicBIO = NULL; + RSA *publicRSA = NULL; + + if(!(publicBIO = BIO_new_mem_buf((unsigned char*)[[self publicKey] bytes], [[self publicKey] length]))) + { + NSLog(@"BIO_new_mem_buf() failed!"); + return nil; + } + + if(!PEM_read_bio_RSA_PUBKEY(publicBIO, &publicRSA, NULL, NULL)) + { + NSLog(@"PEM_read_bio_RSA_PUBKEY() failed!"); + return nil; + } + + // RSA_size() returns the RSA modulus size in bytes. + // It can be used to determine how much memory must be allocated for an RSA encrypted value. + + outbuf = (unsigned char *)malloc(RSA_size(publicRSA)); + + if(!(outlen = RSA_public_encrypt(inlen, input, (unsigned char*)outbuf, publicRSA, RSA_PKCS1_PADDING))) + { + NSLog(@"RSA_public_encrypt failed!"); + return nil; + } + + if(outlen == -1) + { + NSLog(@"Encrypt error: %s (%s)", + ERR_error_string(ERR_get_error(), NULL), + ERR_reason_error_string(ERR_get_error())); + return nil; + } + + if (publicBIO) BIO_free(publicBIO); + if (publicRSA) RSA_free(publicRSA); + } + + // Store the encrypted data as the cipher text + [self setCipherText:[NSData dataWithBytes:outbuf length:outlen]]; + + // Release the outbuf, since it was malloc'd + if(outbuf) { + free(outbuf); + } + + return [self cipherTextAsData]; +} + +/** + * Signs (encrypts) the clear text data using the private key. + * The resulting cipher text data is returned, and may later be verified (decrypted) using the public key. + * + * The resulting cipher text data may also be later retrieved with the cipherTextAsData or cipherTextAsString methods. +**/ +- (NSData *)sign +{ + // If there is no clear text set, or the clear text is an empty string (zero length data) + // then there is nothing to encrypt, and we may as well return nil + if(clearText == nil || [clearText length] == 0) + { + return nil; + } + + unsigned char *input = (unsigned char *)[clearText bytes]; + unsigned char *outbuf; + int outlen, inlen; + inlen = [clearText length]; + + if([self privateKey] == nil) + { + NSLog(@"Cannot sign (encrypt) without the private key, which is currently nil"); + return nil; + } + + BIO *privateBIO = NULL; + RSA *privateRSA = NULL; + + if(!(privateBIO = BIO_new_mem_buf((unsigned char*)[[self privateKey] bytes], [[self privateKey] length]))) + { + NSLog(@"BIO_new_mem_buf() failed!"); + return nil; + } + + if(!PEM_read_bio_RSAPrivateKey(privateBIO, &privateRSA, NULL, NULL)) + { + NSLog(@"PEM_read_bio_RSAPrivateKey() failed!"); + return nil; + } + + // RSA_check_key() returns 1 if rsa is a valid RSA key, and 0 otherwise. + + unsigned long check = RSA_check_key(privateRSA); + if(check != 1) + { + NSLog(@"RSA_check_key() failed with result %d!", check); + return nil; + } + + // RSA_size() returns the RSA modulus size in bytes. + // It can be used to determine how much memory must be allocated for an RSA encrypted value. + + outbuf = (unsigned char *)malloc(RSA_size(privateRSA)); + + if(!(outlen = RSA_private_encrypt(inlen, input, (unsigned char*)outbuf, privateRSA, RSA_PKCS1_PADDING))) + { + NSLog(@"RSA_private_encrypt failed!"); + return nil; + } + + if(outlen == -1) + { + NSLog(@"Encrypt error: %s (%s)", + ERR_error_string(ERR_get_error(), NULL), + ERR_reason_error_string(ERR_get_error())); + return nil; + } + + if (privateBIO) BIO_free(privateBIO); + if (privateRSA) RSA_free(privateRSA); + + // Store the encrypted data as the cipher text + [self setCipherText:[NSData dataWithBytes:outbuf length:outlen]]; + + // Release the outbuf, since it was malloc'd + if(outbuf) { + free(outbuf); + } + + return [self cipherTextAsData]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Other methods: +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Description forthcoming... +**/ +- (NSData *)digest:(NSString *)digestName +{ + if(clearText == nil) { + return nil; + } + + unsigned char outbuf[EVP_MAX_MD_SIZE]; + unsigned int templen, inlen; + unsigned char *input=(unsigned char*)[clearText bytes]; + EVP_MD_CTX ctx; + const EVP_MD *digest = NULL; + + inlen = [clearText length]; + + if(inlen==0) + return nil; + if(digestName) { + digest = EVP_get_digestbyname((const char*)[digestName UTF8String]); + if (!digest) { + NSLog(@"cannot get digest with name %@",digestName); + return nil; + } + } else { + digest=EVP_md5(); + if(!digest) { + NSLog(@"cannot get digest with name %@",@"MD5"); + return nil; + } + } + + EVP_MD_CTX_init(&ctx); + EVP_DigestInit(&ctx,digest); + if(!EVP_DigestUpdate(&ctx,input,inlen)) { + NSLog(@"EVP_DigestUpdate() failed!"); + EVP_MD_CTX_cleanup(&ctx); + return nil; + } + if (!EVP_DigestFinal(&ctx, outbuf, &templen)) { + NSLog(@"EVP_DigesttFinal() failed!"); + EVP_MD_CTX_cleanup(&ctx); + return nil; + } + EVP_MD_CTX_cleanup(&ctx); + + return [NSData dataWithBytes:outbuf length:templen]; +} + +- (NSString *)description +{ + NSString *format = @"clearText: %@, cipherText: %@, symmetricKey: %@, publicKey: %@, privateKey: %@"; + NSString *desc = [NSString stringWithFormat:format, [clearText hexdump], [cipherText hexdump], + [symmetricKey hexdump], [publicKey hexdump], [privateKey hexdump]]; + return desc; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Class methods: +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ++ (NSData *)generateRSAPrivateKeyWithLength:(int)length +{ + RSA *key = NULL; + do { + key = RSA_generate_key(length, RSA_F4, NULL, NULL); + } while (1 != RSA_check_key(key)); + + BIO *bio = BIO_new(BIO_s_mem()); + + if (!PEM_write_bio_RSAPrivateKey(bio, key, NULL, NULL, 0, NULL, NULL)) + { + NSLog(@"cannot write private key to memory"); + return nil; + } + if (key) RSA_free(key); + + char *pbio_data = NULL; + int data_len = BIO_get_mem_data(bio, &pbio_data); + NSData *result = [NSData dataWithBytes:pbio_data length:data_len]; + + if (bio) + BIO_free(bio); + + return result; +} + ++ (NSData *)generateRSAPublicKeyFromPrivateKey:(NSData *)privateKey +{ + BIO *privateBIO = NULL; + RSA *privateRSA = NULL; + + if (!(privateBIO = BIO_new_mem_buf((unsigned char*)[privateKey bytes], [privateKey length]))) + { + NSLog(@"BIO_new_mem_buf() failed!"); + return nil; + } + + if (!PEM_read_bio_RSAPrivateKey(privateBIO, &privateRSA, NULL, NULL)) + { + NSLog(@"PEM_read_bio_RSAPrivateKey() failed!"); + return nil; + } + + // RSA_check_key() returns 1 if rsa is a valid RSA key, and 0 otherwise. + + unsigned long check = RSA_check_key(privateRSA); + if (check != 1) + { + NSLog(@"RSA_check_key() failed with result %d!", check); + return nil; + } + + BIO *bio = BIO_new(BIO_s_mem()); + + if (!PEM_write_bio_RSA_PUBKEY(bio, privateRSA)) + { + NSLog(@"cannot write public key to memory"); + return nil; + } + if (privateRSA) RSA_free(privateRSA); + + char *pbio_data = NULL; + int data_len = BIO_get_mem_data(bio, &pbio_data); + NSData *result = [NSData dataWithBytes:pbio_data length:data_len]; + + if (bio) + BIO_free(bio); + + return result; +} + ++ (NSData *)getKeyDataWithLength:(int)length +{ + NSData *randData = nil; + unsigned char *buffer; + + buffer = (unsigned char *)calloc(length*4, sizeof(unsigned char)); + NSAssert((buffer != NULL), @"Cannot calloc memory for buffer."); + + if (!RAND_bytes(buffer, length)){ + free(buffer); + } + + randData = [NSData dataWithBytes:buffer length:length]; + free(buffer); + + return randData; +} + +// PBKDF2 support functions +// Thanks to Chris Benedict (chrisbdaemon@gmail.com) for the code ++ (NSData *)getKeyDataWithLength:(int)length fromPassword:(NSString *)pass withSalt:(NSString *)salt +{ + return [SSCrypto getKeyDataWithLength:length fromPassword:pass withSalt:salt withIterations:1000]; +} + ++ (NSData *)getKeyDataWithLength:(int)length fromPassword:(NSString *)pass withSalt:(NSString *)salt withIterations:(int)count +{ + NSData *key = nil; + unsigned char *buffer = (unsigned char *)calloc(length, sizeof(unsigned char)); + NSAssert((buffer != NULL), @"Cannot calloc memory for buffer."); + const char *password = [pass UTF8String]; + const char *saltVal = [salt UTF8String]; + + if(!PKCS5_PBKDF2_HMAC_SHA1(password, [pass length], (unsigned char *)saltVal, [salt length], count, length, buffer)) { + return nil; + } + + key = [NSData dataWithBytes:buffer length:length]; + free(buffer); + + return key; +} + ++ (NSData *)getSHA1ForData:(NSData *)d +{ + unsigned length = [d length]; + const void *buffer = [d bytes]; + unsigned char *md = (unsigned char *)calloc(SHA_DIGEST_LENGTH, sizeof(unsigned char)); + NSAssert((md != NULL), @"Cannot calloc memory for buffer."); + + (void)SHA1(buffer, length, md); + + return [NSData dataWithBytesNoCopy:md length:SHA_DIGEST_LENGTH freeWhenDone:YES]; +} + ++ (NSData *)getMD5ForData:(NSData *)d +{ + unsigned length = [d length]; + const void *buffer = [d bytes]; + unsigned char *md = (unsigned char *)calloc(MD5_DIGEST_LENGTH, sizeof(unsigned char)); + NSAssert((md != NULL), @"Cannot calloc memory for buffer."); + + (void)MD5(buffer, length, md); + + return [NSData dataWithBytesNoCopy:md length:MD5_DIGEST_LENGTH freeWhenDone:YES]; +} + +@end diff --git a/Tools/NativeHost/Server.h b/Tools/NativeHost/Server.h new file mode 100644 index 000000000..a11e9d701 --- /dev/null +++ b/Tools/NativeHost/Server.h @@ -0,0 +1,21 @@ +// +// Server.h +// NativeHost +// +// Created by Francisco Tolmasky on 8/18/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import + +@interface Server : NSObject +{ + NSFileHandle * outputFile; + NSFileHandle * errorFile; + NSTask * process; +} + +- (BOOL)start; +- (void)stop; + +@end diff --git a/Tools/NativeHost/Server.m b/Tools/NativeHost/Server.m new file mode 100644 index 000000000..5439bc967 --- /dev/null +++ b/Tools/NativeHost/Server.m @@ -0,0 +1,193 @@ +// +// Server.m +// NativeHost +// +// Created by Francisco Tolmasky on 8/18/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "Server.h" +#import "AppController.h" + +#import +#import + +@implementation Server + +- (BOOL)start +{ + NSBundle *mainBundle = [NSBundle mainBundle]; + NSString *serverPath = [mainBundle pathForResource:@"NativeHostServer" ofType:@"" inDirectory:@"Server"]; + + if (!serverPath) + { + return YES; + } + + // find an available port + struct sockaddr_in addr; + int sockfd; + int attempts = 0; + + while (SERVER_PORT == 9191 && attempts++ < 5) + { + // Create a socket + sockfd = socket( AF_INET, SOCK_STREAM, 0 ); + + // Setup its address structure + bzero( &addr, sizeof(struct sockaddr_in)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = htonl( INADDR_ANY ); + addr.sin_port = htons( 0 ); + + // Bind it to an address and port + bind( sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr)); + + // Set it listening for connections + listen( sockfd, 5 ); + + // Find out what port the socket was bound to + socklen_t namelen = sizeof(struct sockaddr_in); + getsockname( sockfd, (struct sockaddr *)&addr, &namelen ); + + if (addr.sin_port <= 1024) + addr.sin_port = 9191; + else + SERVER_PORT = addr.sin_port; + + shutdown(sockfd, 2); + } + + if (SERVER_PASSWORD == nil) + { + CFUUIDRef uuidObj = CFUUIDCreate(nil); + SERVER_PASSWORD = (NSString*)CFUUIDCreateString(nil, uuidObj); + CFRelease(uuidObj); + } + + if (SERVER_USER == nil) + { + CFUUIDRef uuidObj = CFUUIDCreate(nil); + SERVER_USER = (NSString*)CFUUIDCreateString(nil, uuidObj); + CFRelease(uuidObj); + // usernames can't have "-" in them + SERVER_USER = [[SERVER_USER autorelease] stringByReplacingOccurrencesOfString:@"-" withString:@""]; + } + + // setup environment variables + NSDictionary *env = [[[NSProcessInfo processInfo] environment] mutableCopy]; + [env setValue:[mainBundle resourcePath] forKey:@"NATIVEHOST_RESOURCES"]; + [env setValue:[[NSNumber numberWithInt:SERVER_PORT] stringValue] forKey:@"NATIVEHOST_SERVER_PORT"]; + [env setValue:SERVER_USER forKey:@"NATIVEHOST_SERVER_USER"]; + [env setValue:SERVER_PASSWORD forKey:@"NATIVEHOST_SERVER_PASSWORD"]; + + // create pipes for stdout and stderr + NSPipe * outputPipe = [NSPipe pipe]; + outputFile = [outputPipe fileHandleForReading]; + NSPipe * errorPipe = [NSPipe pipe]; + errorFile = [errorPipe fileHandleForReading]; + + // setup the process + process = [[NSTask alloc] init]; + [process setLaunchPath:serverPath]; + [process setStandardOutput:outputPipe]; + [process setStandardError:errorPipe]; + [process setEnvironment:env]; + [env release]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(serverDidTerminate:) + name:NSTaskDidTerminateNotification + object:process]; + + [process launch]; + + BOOL didStartup = NO; + NSData * data = nil; + + while (!didStartup && (data = [outputFile availableData]) && [data length]) + { + NSString * string = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; + if (string) + { + NHLog(@"SERVER-OUT", string); + + didStartup = [string rangeOfString:@"Jack is starting up"].location != NSNotFound; + + [string release]; + } + } + NSLog(@"Server has started."); + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(didReadStdOut:) + name:NSFileHandleReadCompletionNotification + object:outputFile]; + + [[NSNotificationCenter defaultCenter] + addObserver:self + selector:@selector(didReadStdErr:) + name:NSFileHandleReadCompletionNotification + object:errorFile]; + + [outputFile readInBackgroundAndNotify]; + [errorFile readInBackgroundAndNotify]; + + return didStartup; +} + +- (void)stop +{ + [process terminate]; +} + +- (void)didReadStdOut:(NSNotification *)aNotification +{ + NSString * string = [[NSString alloc] initWithData:[[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem] + encoding:NSASCIIStringEncoding]; + NHLog(@"SERVER-OUT", string); + [string release]; + [outputFile readInBackgroundAndNotify]; +} + +- (void)didReadStdErr:(NSNotification *)aNotification +{ + NSString * string = [[NSString alloc] initWithData:[[aNotification userInfo] objectForKey:NSFileHandleNotificationDataItem] + encoding:NSASCIIStringEncoding]; + NHLog(@"SERVER-ERR", string); + [string release]; + [errorFile readInBackgroundAndNotify]; +} + +- (void)serverDidTerminate:(NSNotification *)note +{ + if ([process terminationStatus] != 0) + { + NSString *appName = [[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:@"CFBundleDisplayName"]; + if (appName == nil) + appName = [[NSProcessInfo processInfo] processName]; + + if (SERVER_PORT != 9191 && [self start]) + { + NSRunAlertPanel(@"Unexpected Error", + [NSString stringWithFormat:@"%@ was not able to complete the last action you performed. Try again, and report the problem if it continues to occur.", appName], + @"OK", nil, nil); + } + else + { + NSRunAlertPanel(@"Unexpected Error", + [NSString stringWithFormat:@"A fatal error occurred. %@ could not recover and must terminate.", appName], + @"Terminate", nil, nil); + exit(1); + } + } +} + +- (void)dealloc +{ + [process release]; + [super dealloc]; +} + +@end diff --git a/Tools/NativeHost/WebPasteboardProxy.h b/Tools/NativeHost/WebPasteboardProxy.h new file mode 100644 index 000000000..887b95f4c --- /dev/null +++ b/Tools/NativeHost/WebPasteboardProxy.h @@ -0,0 +1,19 @@ +// +// WebPasteboardProxy.h +// NativeHost +// +// Created by Ross Boucher on 12/12/09. +// Copyright 2009 280 North. All rights reserved. +// + +#import + + +@interface WebPasteboardProxy : NSObject +{ + NSPasteboard *pasteboard; +} + ++ (id)pasteboardWithName:(NSString *)aName; + +@end diff --git a/Tools/NativeHost/WebPasteboardProxy.m b/Tools/NativeHost/WebPasteboardProxy.m new file mode 100644 index 000000000..6eba5c038 --- /dev/null +++ b/Tools/NativeHost/WebPasteboardProxy.m @@ -0,0 +1,95 @@ +// +// WebPasteboardProxy.m +// NativeHost +// +// Created by Ross Boucher on 12/12/09. +// Copyright 2009 280 North. All rights reserved. +// + +#import "WebPasteboardProxy.h" +#import "WebScripObject+Objective-J.h" +#import "BridgedMethods.h" + +NSMutableDictionary *WebPasteboardDictionary = nil; + +@implementation WebPasteboardProxy + ++ (void)initialize +{ + WebPasteboardDictionary = [[NSMutableDictionary dictionary] retain]; +} + ++ (BOOL)isSelectorExcludedFromWebScript:(SEL)aSelector +{ + return NO; +} + ++ (id)pasteboardWithName:(NSString *)aName +{ + if ([aName isEqual:@"CPGeneralPboard"]) + return [self pasteboardWithName:NSGeneralPboard]; + + id proxy = [WebPasteboardDictionary objectForKey:aName]; + if (!proxy) + { + proxy = [[self alloc] initWithPasteboard:[NSPasteboard pasteboardWithName:aName]]; + [WebPasteboardDictionary setObject:proxy forKey:aName]; + [proxy release]; + } + + return proxy; +} + +- (id)initWithPasteboard:(NSPasteboard *)aPasteboard +{ + if (self = [super init]) + { + pasteboard = [aPasteboard retain]; + } + + return self; +} + +- (void)dealloc +{ + [pasteboard release]; + [super dealloc]; +} + +- (int)changeCount +{ + return [pasteboard changeCount]; +} + +- (void)declareTypes:(WebScriptObject *)args +{ + [pasteboard declareTypes:[BridgedMethods arrayFromWebScriptObject:args] owner:self]; +} + +- (void)addTypes:(WebScriptObject *)args +{ + [pasteboard addTypes:[BridgedMethods arrayFromWebScriptObject:args ] owner:self]; +} + +- (NSArray *)types +{ + return [pasteboard types]; +} + +- (NSString *)stringForType:(NSString *)aType +{ + return [pasteboard stringForType:aType]; +} + +- (void)pasteboard:(NSPasteboard *)aPasteboard provideDataForType:(NSString *)aType +{ + NSString *name = aPasteboard == [NSPasteboard generalPasteboard] ? @"CPGeneralPboard" : [aPasteboard name]; + + NSString *data = [[[NSApp delegate] windowScriptObject] evaluateObjectiveJReturningString: + [NSString stringWithFormat:@"[[CPPasteboard pasteboardWithName:'%@'] stringForType:'%@']", name, aType] + ]; + + [aPasteboard setString:data forType:aType]; +} + +@end diff --git a/Tools/NativeHost/WebScripObject+Objective-J.h b/Tools/NativeHost/WebScripObject+Objective-J.h new file mode 100644 index 000000000..4b448574d --- /dev/null +++ b/Tools/NativeHost/WebScripObject+Objective-J.h @@ -0,0 +1,22 @@ +// +// WebScripObject+Objective-J.h +// NativeHost +// +// Created by Francisco Tolmasky on 9/1/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import +#import + + +@interface WebScriptObject (ObjectiveJ) + +- (id)evaluateObjectiveJ:(NSString *)aString; +- (NSString *)evaluateObjectiveJReturningString:(NSString *)aString; + +- (id)bridgeSelector:(SEL)aSelector; +- (id)bridgeSelector:(SEL)aSelector withObject:(id)anObject; +- (id)bridgeSelector:(SEL)aSelector withObjects:(NSArray *)array; + +@end diff --git a/Tools/NativeHost/WebScripObject+Objective-J.m b/Tools/NativeHost/WebScripObject+Objective-J.m new file mode 100644 index 000000000..d1be2df47 --- /dev/null +++ b/Tools/NativeHost/WebScripObject+Objective-J.m @@ -0,0 +1,46 @@ +// +// WebScripObject+Objective-J.m +// NativeHost +// +// Created by Francisco Tolmasky on 9/1/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "WebScripObject+Objective-J.h" + + +@implementation WebScriptObject (ObjectiveJ) + +- (id)evaluateObjectiveJ:(NSString *)aString +{ + return [self evaluateWebScript:[NSString stringWithFormat:@"(ObjectiveJ.eval(decodeURIComponent(\"%@\")))", [aString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], nil]]; +} + +- (NSString *)evaluateObjectiveJReturningString:(NSString *)aString +{ + return [self evaluateWebScript:[NSString stringWithFormat:@"String(ObjectiveJ.eval(decodeURIComponent(\"%@\")))", [aString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], nil]]; +} + +- (id)bridgeSelector:(SEL)aSelector +{ + return [self bridgeSelector:aSelector withObjects:nil]; +} + +- (id)bridgeSelector:(SEL)aSelector withObject:(id)anObject +{ + return [self bridgeSelector:aSelector withObjects:[NSArray arrayWithObject:anObject]]; +} + +- (id)bridgeSelector:(SEL)aSelector withObjects:(NSArray *)objects +{ + [self evaluateWebScript:@"objj_object.prototype.__objj_msgSend = function() { return objj_msgSend.apply(null, arguments); }"]; + + NSArray * arguments = [NSArray arrayWithObjects:self, NSStringFromSelector(aSelector), nil]; + + if (objects) + arguments = [arguments arrayByAddingObjectsFromArray:objects]; + + return [self callWebScriptMethod:@"__objj_msgSend" withArguments:arguments]; +} + +@end diff --git a/Tools/NativeHost/WebWindow.h b/Tools/NativeHost/WebWindow.h new file mode 100644 index 000000000..a7feb9fac --- /dev/null +++ b/Tools/NativeHost/WebWindow.h @@ -0,0 +1,44 @@ +// +// WebWindow.h +// NativeHost +// +// Created by Francisco Tolmasky on 10/18/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import + + +typedef enum _CPWindowShadowStyle +{ + CPStandardWindowShadowStyle = 0, + CPMenuWindowShadowStyle = 1, + CPPanelWindowShadowStyle = 2, + CPCustomWindowShadowStyle = 3 +} CPWindowShadowStyle; + +@interface WebWindow : NSWindow +{ + NSView * leftMouseDownView; + NSView * rightMouseDownView; + + WebView * webView; + NSView * shadowView; + + BOOL hasShadow; + CPWindowShadowStyle shadowStyle; +} + ++ (WebWindow *)webWindow; + +- (WebView *)webView; + +- (BOOL)hitTest:(NSPoint)aPoint; + +- (BOOL)hasShadow; +- (void)setHasShadow:(BOOL)shouldHaveShadow; + +- (void)setShadowStyle:(CPWindowShadowStyle)aStyle; +- (CPWindowShadowStyle)shadowStyle; + +@end diff --git a/Tools/NativeHost/WebWindow.m b/Tools/NativeHost/WebWindow.m new file mode 100644 index 000000000..631e73a72 --- /dev/null +++ b/Tools/NativeHost/WebWindow.m @@ -0,0 +1,510 @@ +// +// WebWindow.m +// NativeHost +// +// Created by Francisco Tolmasky on 10/18/09. +// Copyright 2009 280 North, Inc.. All rights reserved. +// + +#import "WebWindow.h" + + +static NSMutableArray * DisabledWindows; + +CFMachPortRef tap_port; + +CGEventRef headTapCallback(CGEventTapProxy proxy, CGEventType type, CGEventRef event, void *refcon) +{ + // It's dangerous to fail in this code: could disable mousedown system-wide. So just try catch it all. + @try + { + [DisabledWindows makeObjectsPerformSelector:@selector(stopIgnoringMouseEvents)]; + [DisabledWindows removeAllObjects]; + + if (type == kCGEventLeftMouseDown) + { + CGPoint location = CGEventGetLocation(event); + NSPoint NSLocation = NSMakePoint(location.x, location.y); + + for (NSWindow * window in [NSApp windows]) + { + if ([window isKindOfClass:[WebWindow class]] && ![(WebWindow *)window hitTest:NSLocation]) + { + [window setIgnoresMouseEvents:YES]; + + [DisabledWindows addObject:window]; + } + } + } + else if (type == kCGEventTapDisabledByTimeout) + CGEventTapEnable(tap_port, YES); + + } + @catch (NSException * anException) + { + } + + return event; +} + + +@interface WebWindowContentView : NSView +{ +} +@end + +@implementation WebWindow + ++ (void)initialize +{ + if (self != [WebWindow class]) + return; + + DisabledWindows = [[NSMutableArray alloc] init]; + + //| CGEventMaskBit(kCGEventTapDisabledByTimeout) + tap_port = CGEventTapCreate(kCGSessionEventTap, kCGHeadInsertEventTap, + kCGEventTapOptionDefault, + CGEventMaskBit(kCGEventLeftMouseDown), + headTapCallback, NULL); + + if (NULL == tap_port) + { + fprintf(stderr, "Error creating event tap\n"); + return; + } + + /* Create a run loop source from the tap port, add it to my run loop + * and start executing the loop + */ + CFRunLoopSourceRef tap_source = CFMachPortCreateRunLoopSource(kCFAllocatorDefault, tap_port, 0); + + if (NULL == tap_source) + { + fprintf(stderr, "Error converting port to run loop source\n"); + + if (tap_port != NULL) + CFRelease(tap_port); + + return; + } + + CFRunLoopAddSource(CFRunLoopGetCurrent(), tap_source, kCFRunLoopCommonModes); +} + +- (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)windowStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)shouldDeferCreation +{ + self = [super initWithContentRect:aContentRect styleMask:NSBorderlessWindowMask backing:aBufferingType defer:shouldDeferCreation]; + + if (self) + { + id delegate = [NSApp delegate]; + + shadowView = [[WebWindowContentView alloc] init]; + + [self setContentView:shadowView]; + + webView = [[WebView alloc] initWithFrame:NSZeroRect]; + + [webView setDrawsBackground:NO]; + [webView setUIDelegate:delegate]; + [webView setFrameLoadDelegate:delegate]; + [webView setResourceLoadDelegate:delegate]; + [webView setPolicyDelegate:delegate]; + [webView setShouldCloseWithWindow:YES]; + + [shadowView addSubview:webView]; + + [self setBackgroundColor:[NSColor clearColor]]; + [self setOpaque:NO]; + [self setReleasedWhenClosed:YES]; + [super setHasShadow:NO]; + } + + return self; +} + +- (void)setFrame:(NSRect)aFrame display:(BOOL)shouldDisplay +{ + [webView setFrame:NSMakeRect(33.0, 33.0, NSWidth(aFrame), NSHeight(aFrame))]; + [super setFrame:NSInsetRect(aFrame, -33.0, -33.0) display:YES]; +} + +- (void)dealloc +{ + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResizeNotification object:self]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidBecomeKeyNotification object:self]; + [[NSNotificationCenter defaultCenter] removeObserver:self name:NSWindowDidResignKeyNotification object:self]; + + [shadowView release]; + [webView release]; + [super dealloc]; +} + +- (BOOL)canBecomeKeyWindow +{ + // Necessary since this apparently returns NO for borderless windows. + return YES; +} + +- (NSView *)webHitTest:(NSPoint)aPoint +{ + NSPoint locationInWebView = [[self contentView] convertPoint:aPoint fromView:nil]; + + return [(NSView *)[self contentView] hitTest:locationInWebView]; +} + +- (void)sendEvent:(NSEvent *)anEvent +{ + NSInteger type = [anEvent type]; + + if (type == NSLeftMouseDown) + { + NSView * view = [self webHitTest:[anEvent locationInWindow]]; + + leftMouseDownView = view; + + [view mouseDown:anEvent]; + } + + else if (type == NSLeftMouseDragged) + [leftMouseDownView mouseDragged:anEvent]; + + else if (type == NSLeftMouseUp) + [leftMouseDownView mouseUp:anEvent]; + + else if (type == NSRightMouseDown) + { + NSView * view = [self webHitTest:[anEvent locationInWindow]]; + + rightMouseDownView = view; + + [view rightMouseDown:anEvent]; + } + else if (type == NSRightMouseDragged) + [rightMouseDownView rightMouseDragged:anEvent]; + + else if (type == NSRightMouseUp) + [rightMouseDownView rightMouseUp:anEvent]; + + else + [super sendEvent:anEvent]; + + // FIXME: other +} + +// Override this to avoid the beep from unhandled events. +- (void)keyDown:(NSEvent *)anEvent +{ +} + ++ (WebWindow *)webWindow +{ + return [[WebWindow alloc] initWithContentRect:NSZeroRect styleMask:NSBorderlessWindowMask backing:NSBackingStoreBuffered defer:YES]; +} + +- (WebView *)webView +{ + return webView; +} + +- (void)updateShadow +{ + [super setHasShadow:hasShadow && shadowStyle == CPCustomWindowShadowStyle]; + [shadowView setNeedsDisplay:YES]; +} + +- (BOOL)hasShadow +{ + return hasShadow; +} + +- (void)setHasShadow:(BOOL)shouldHaveShadow +{ + if (hasShadow == shouldHaveShadow) + return; + + hasShadow = shouldHaveShadow; + + [self updateShadow]; +} + +- (CPWindowShadowStyle)shadowStyle +{ + return shadowStyle; +} + +- (void)setShadowStyle:(CPWindowShadowStyle)aStyle +{ + if (shadowStyle == aStyle) + return; + + shadowStyle = aStyle; + + [self updateShadow]; +} + +- (BOOL)hitTest:(NSPoint)aPoint +{ + NSInteger windowNumber = [[[webView windowScriptObject] valueForKey:@"cpWindowNumber"] intValue]; + +#if LOGGING + WebScriptObject * frame = [[[NSApp delegate] windowScriptObject] evaluateWebScript:[NSString stringWithFormat:@"(objj_msgSend(objj_msgSend(CPApp, \"windowWithWindowNumber:\", %d), \"frame\"))", windowNumber]], + * origin = [frame valueForKey:@"origin"], + * size = [frame valueForKey:@"size"]; + + NSLog(@"(%f, %f) in { %f, %f, %f, %f }", aPoint.x, aPoint.y, [[origin valueForKey:@"x"] floatValue], [[origin valueForKey:@"y"] floatValue], [[size valueForKey:@"width"] floatValue], [[size valueForKey:@"height"] floatValue]); +#endif + + return [[[[NSApp delegate] windowScriptObject] evaluateWebScript:[NSString stringWithFormat:@"(CGRectContainsPoint(objj_msgSend(objj_msgSend(CPApp, \"windowWithWindowNumber:\", %d), \"frame\"), CGPointMake(%f, %f)))", windowNumber, aPoint.x, aPoint.y]] boolValue]; +} + +- (void)stopIgnoringMouseEvents +{ + [self setIgnoresMouseEvents:NO]; +} + +- (void)becomeKeyWindow +{ + [super becomeKeyWindow]; + + [self updateShadow]; +} + +- (void)resignKeyWindow +{ + [super resignKeyWindow]; + + [self updateShadow]; +} + +@end + +@interface NSImage (Additions) + +- (void)drawInRect:(NSRect)aRect slices:(CGFloat [])slices; + +@end + +@implementation NSImage (Additions) + +- (void)drawInRect:(NSRect)aRect slices:(CGFloat [])slices +{ + NSRect imageBounds = NSMakeRect(0.0, 0.0, [self size].width, [self size].height); + + [self drawInRect:NSMakeRect(0.0, 0.0, slices[0], slices[1]) + fromRect:NSMakeRect(0.0, 0.0, slices[0], slices[1]) + operation:NSCompositeSourceOver + fraction:1.0]; + + [self drawInRect:NSMakeRect(NSMaxX(aRect) - slices[2], 0.0, slices[2], slices[1]) + fromRect:NSMakeRect(NSMaxX(imageBounds) - slices[2], 0.0, slices[2], slices[1]) + operation:NSCompositeSourceOver + fraction:1.0]; + + [self drawInRect:NSMakeRect(0.0, NSMaxY(aRect) - slices[3], slices[0], slices[3]) + fromRect:NSMakeRect(0.0, NSMaxY(imageBounds) - slices[3], slices[0], slices[3]) + operation:NSCompositeSourceOver + fraction:1.0]; + + [self drawInRect:NSMakeRect(NSMaxX(aRect) - slices[2], NSMaxY(aRect) - slices[3], slices[2], slices[3]) + fromRect:NSMakeRect(NSMaxX(imageBounds) - slices[2], NSMaxY(imageBounds) - slices[3], slices[2], slices[3]) + operation:NSCompositeSourceOver + fraction:1.0]; + + [self drawInRect:NSMakeRect(slices[0], 0.0, NSWidth(aRect) - slices[0] - slices[2], slices[1]) + fromRect:NSMakeRect(slices[0], 0.0, 1.0, slices[1]) + operation:NSCompositeSourceOver + fraction:1.0]; + + [self drawInRect:NSMakeRect(0.0, slices[1], slices[0], NSHeight(aRect) - slices[1] - slices[3]) + fromRect:NSMakeRect(0.0, slices[1], slices[0], 1.0) + operation:NSCompositeSourceOver + fraction:1.0]; + + [self drawInRect:NSMakeRect(NSMaxX(aRect) - slices[2], slices[1], slices[2], NSHeight(aRect) - slices[1] - slices[3]) + fromRect:NSMakeRect(NSMaxX(imageBounds) - slices[2], slices[1], slices[2], 1.0) + operation:NSCompositeSourceOver + fraction:1.0]; + + [self drawInRect:NSMakeRect(slices[0], NSMaxY(aRect) - slices[3], NSWidth(aRect) - slices[0] - slices[2], slices[3]) + fromRect:NSMakeRect(slices[0], NSMaxY(imageBounds) - slices[3], 1.0, slices[3]) + operation:NSCompositeSourceOver + fraction:1.0]; +} + +@end + +@implementation WebWindowContentView : NSView +{ +} + +static float OUTlINE_HORIZONTAL_INSET = 33.0; +static float OUTlINE_VERTICAL_INSET = 33.0; + +- (void)drawStandardActiveShadowInRect:(NSRect)aRect +{ + CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; + + CGRect bounds = CGRectInset(*(CGRect *)&aRect, OUTlINE_HORIZONTAL_INSET - 0.5, OUTlINE_VERTICAL_INSET - 0.5); + + CGColorRef shadowColor = CGColorCreateGenericGray(0.0, 0.8); + CGContextSetShadowWithColor (context, CGSizeMake(0.0, -10.0), 100.0, shadowColor); + CGColorRelease(shadowColor); + + CGColorRef fillColor = CGColorCreateGenericGray(1.0, 1.0); + CGColorRef strokeColor = CGColorCreateGenericGray(0.0, 0.2); + + CGContextSetFillColorWithColor(context, fillColor); + CGContextSetStrokeColorWithColor(context, strokeColor); + + CGColorRelease(fillColor); + CGColorRelease(strokeColor); + + CGContextBeginPath(context); + + CGContextAddArc(context, CGRectGetMinX(bounds) + 5.0, CGRectGetMaxY(bounds) - 5.0, 5.0, M_PI, M_PI_2, YES); + CGContextAddArc(context, CGRectGetMaxX(bounds) - 5.0, CGRectGetMaxY(bounds) - 5.0, 5.0, M_PI_2, 0, YES); + CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMinY(bounds)); + CGContextAddLineToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds)); + + CGContextClosePath(context); + + CGContextDrawPath(context, kCGPathFillStroke); +} + +- (void)drawStandardInactiveShadowInRect:(NSRect)aRect +{ + CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; + + CGRect bounds = CGRectInset(*(CGRect *)&aRect, OUTlINE_HORIZONTAL_INSET - 0.5, OUTlINE_VERTICAL_INSET - 0.5); + + CGColorRef shadowColor = CGColorCreateGenericGray(0.0, 0.5); + CGContextSetShadowWithColor (context, CGSizeMake(0.0, -10.0), 40.0, shadowColor); + CGColorRelease(shadowColor); + + CGColorRef fillColor = CGColorCreateGenericGray(1.0, 1.0); + CGColorRef strokeColor = CGColorCreateGenericGray(0.0, 0.2); + + CGContextSetFillColorWithColor(context, fillColor); + CGContextSetStrokeColorWithColor(context, strokeColor); + + CGColorRelease(fillColor); + CGColorRelease(strokeColor); + + CGContextBeginPath(context); + + CGContextAddArc(context, CGRectGetMinX(bounds) + 5.0, CGRectGetMaxY(bounds) - 5.0, 5.0, M_PI, M_PI_2, YES); + CGContextAddArc(context, CGRectGetMaxX(bounds) - 5.0, CGRectGetMaxY(bounds) - 5.0, 5.0, M_PI_2, 0, YES); + CGContextAddLineToPoint(context, CGRectGetMaxX(bounds), CGRectGetMinY(bounds)); + CGContextAddLineToPoint(context, CGRectGetMinX(bounds), CGRectGetMinY(bounds)); + + CGContextClosePath(context); + + CGContextDrawPath(context, kCGPathFillStroke); +} + +- (void)drawMenuShadowInRect:(NSRect)aRect +{ + CGContextRef context = [[NSGraphicsContext currentContext] graphicsPort]; + + CGRect bounds = CGRectInset(*(CGRect *)&aRect, OUTlINE_HORIZONTAL_INSET - 0.5, OUTlINE_VERTICAL_INSET - 0.5); + + CGColorRef shadowColor = CGColorCreateGenericGray(0.0, 0.5); + CGContextSetShadowWithColor (context, CGSizeMake(0.0, -10.0), 30.0, shadowColor); + CGColorRelease(shadowColor); + + CGColorRef fillColor = CGColorCreateGenericGray(1.0, 1.0); + CGColorRef strokeColor = CGColorCreateGenericGray(0.0, 0.2); + + CGContextSetFillColorWithColor(context, fillColor); + CGContextSetStrokeColorWithColor(context, strokeColor); + + CGColorRelease(fillColor); + CGColorRelease(strokeColor); + + CGContextBeginPath(context); + + CGContextAddArc(context, CGRectGetMinX(bounds) + 3.0, CGRectGetMaxY(bounds) - 3.0, 2.0, M_PI, M_PI_2, YES); + CGContextAddArc(context, CGRectGetMaxX(bounds) - 3.0, CGRectGetMaxY(bounds) - 3.0, 2.0, M_PI_2, 0, YES); + CGContextAddArc(context, CGRectGetMaxX(bounds) - 3.0, CGRectGetMinY(bounds) + 3.0, 2.0, 0, 3 * M_PI_2, YES); + CGContextAddArc(context, CGRectGetMinX(bounds) + 3.0, CGRectGetMinY(bounds) + 3.0, 2.0, 3 * M_PI_2, M_PI, YES); + + CGContextClosePath(context); + + CGContextDrawPath(context, kCGPathFill);//Stroke); + +/* + CGContextBeginPath(context); + + CGContextAddArc(context, CGRectGetMinX(bounds) + 3.0, CGRectGetMaxY(bounds) - 3.0, 2.0, M_PI, M_PI_2, YES); + CGContextAddArc(context, CGRectGetMaxX(bounds) - 3.0, CGRectGetMaxY(bounds) - 3.0, 2.0, M_PI_2, 0, YES); + CGContextAddArc(context, CGRectGetMaxX(bounds) - 3.0, CGRectGetMinY(bounds) + 3.0, 2.0, 0, 3 * M_PI_2, YES); + CGContextAddArc(context, CGRectGetMinX(bounds) + 3.0, CGRectGetMinY(bounds) + 3.0, 2.0, 3 * M_PI_2, M_PI, YES); + + CGContextClosePath(context); + + CGContextSetFillColorWithColor(context, CGColorGetConstantColor(kCGColorClear)); + + CGContextSetBlendMode(context, kCGBlendModeCopy); + CGContextDrawPath(context, kCGPathFill); +*/ +} + +- (NSImage *)shadowImageWithSelector:(SEL)aSelector +{ + static NSMutableDictionary * images = nil; + + if (!images) + images = [[NSMutableDictionary alloc] init]; + + NSString * name = NSStringFromSelector(aSelector); + NSImage * image = [images objectForKey:name]; + + if (!image) + { + image = [[NSImage alloc] initWithSize:NSMakeSize(100.0, 100.0)]; + + [image lockFocus]; + objc_msgSend(self, aSelector, NSMakeRect(0.0, 0.0, 100.0, 100.0)); + [image unlockFocus]; + + [images setObject:image forKey:name]; + + [image release]; + } + + return image; +} + +- (void)drawShadowWithSelector:(SEL)aSelector inRect:(NSRect)aRect +{ + CGFloat slices[] = { 40.0, 49.0, 40.0, 49.0 }; + + if (NSWidth(aRect) > 100.0 || NSHeight(aRect) > 100.0) + [[self shadowImageWithSelector:aSelector] drawInRect:aRect slices:slices]; + else + objc_msgSend(self, aSelector, aRect); +} + +- (void)drawRect:(NSRect)aRect +{ + WebWindow * window = (WebWindow *)[self window]; + NSRect bounds = [self bounds]; + + if (![window hasShadow]) + return; + + if ([window shadowStyle] == CPStandardWindowShadowStyle) + if ([window isKeyWindow]) + return [self drawShadowWithSelector:@selector(drawStandardActiveShadowInRect:) inRect:bounds]; + else + return [self drawShadowWithSelector:@selector(drawStandardInactiveShadowInRect:) inRect:bounds]; + + + if ([window shadowStyle] == CPMenuWindowShadowStyle) + return [self drawShadowWithSelector:@selector(drawMenuShadowInRect:) inRect:bounds]; +} + +@end + diff --git a/Tools/NativeHost/icon.icns b/Tools/NativeHost/icon.icns new file mode 100644 index 000000000..cd9ecb6bb Binary files /dev/null and b/Tools/NativeHost/icon.icns differ diff --git a/Tools/NativeHost/main.m b/Tools/NativeHost/main.m new file mode 100644 index 000000000..2a040439e --- /dev/null +++ b/Tools/NativeHost/main.m @@ -0,0 +1,23 @@ +// +// main.m +// NativeHost +// +// Created by Francisco Tolmasky on 5/24/09. +// Copyright 280 North, Inc. 2009. All rights reserved. +// + +#import +#import "Application.h" + +int interactive = 0; + +int main(int argc, char *argv[]) +{ + if (argc > 1 && strcmp(argv[1], "-i") == 0) { + interactive = 1; + } + + [Application sharedApplication]; + + return NSApplicationMain(argc, (const char **) argv); +} diff --git a/Tools/capp/Resources/Templates/Application/Jakefile b/Tools/capp/Resources/Templates/Application/Jakefile index d27bba79f..81068711d 100644 --- a/Tools/capp/Resources/Templates/Application/Jakefile +++ b/Tools/capp/Resources/Templates/Application/Jakefile @@ -37,13 +37,6 @@ app ("__project.nameasidentifier__", function(task) task.setCompilerFlags("-O"); }); -function printResults(configuration) -{ - print("----------------------------"); - print(configuration+" app built at path: "+FILE.join("Build", configuration, "__project.nameasidentifier__")); - print("----------------------------"); -} - task ("default", ["__project.nameasidentifier__"], function() { printResults(configuration); @@ -79,3 +72,22 @@ task ("deploy", ["release"], function() OS.system(["press", "-f", FILE.join("Build", "Release", "__project.nameasidentifier__"), FILE.join("Build", "Deployment", "__project.nameasidentifier__")]); printResults("Deployment") }); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "__project.nameasidentifier__")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "__project.nameasidentifier__"), FILE.join("Build", "Desktop", "__project.nameasidentifier__", "__project.nameasidentifier__.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "__project.nameasidentifier__", "__project.nameasidentifier__.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "__project.nameasidentifier__")); + print("----------------------------"); +} diff --git a/Tools/capp/Resources/Templates/NibApplication/Jakefile b/Tools/capp/Resources/Templates/NibApplication/Jakefile index 5b44b5db3..9e688f19f 100644 --- a/Tools/capp/Resources/Templates/NibApplication/Jakefile +++ b/Tools/capp/Resources/Templates/NibApplication/Jakefile @@ -38,13 +38,6 @@ app ("__project.nameasidentifier__", function(task) task.setCompilerFlags("-O"); }); -function printResults(configuration) -{ - print("----------------------------"); - print(configuration+" app built at path: "+FILE.join("Build", configuration, "__project.nameasidentifier__")); - print("----------------------------"); -} - task ("default", ["__project.nameasidentifier__"], function() { printResults(configuration); @@ -80,3 +73,22 @@ task ("deploy", ["release"], function() OS.system(["press", "-f", FILE.join("Build", "Release", "__project.nameasidentifier__"), FILE.join("Build", "Deployment", "__project.nameasidentifier__")]); printResults("Deployment") }); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "__project.nameasidentifier__")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "__project.nameasidentifier__"), FILE.join("Build", "Desktop", "__project.nameasidentifier__", "__project.nameasidentifier__.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "__project.nameasidentifier__", "__project.nameasidentifier__.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "__project.nameasidentifier__")); + print("----------------------------"); +} diff --git a/Tools/capp/Resources/Templates/ThemeDescriptor/Jakefile b/Tools/capp/Resources/Templates/ThemeDescriptor/Jakefile index 7a0a7a965..b458f36ea 100644 --- a/Tools/capp/Resources/Templates/ThemeDescriptor/Jakefile +++ b/Tools/capp/Resources/Templates/ThemeDescriptor/Jakefile @@ -42,3 +42,22 @@ task ("release", function() ENV["CONFIGURATION"] = "Release"; JAKE.subjake(["."], "build", ENV); }); + +task ("desktop", ["release"], function() +{ + FILE.mkdirs(FILE.join("Build", "Desktop", "__project.nameasidentifier__")); + require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "__project.nameasidentifier__"), FILE.join("Build", "Desktop", "__project.nameasidentifier__", "__project.nameasidentifier__.app")); + printResults("Desktop") +}); + +task ("run-desktop", ["desktop"], function() +{ + OS.system([FILE.join("Build", "Desktop", "__project.nameasidentifier__", "__project.nameasidentifier__.app", "Contents", "MacOS", "NativeHost"), "-i"]); +}); + +function printResults(configuration) +{ + print("----------------------------"); + print(configuration+" app built at path: "+FILE.join("Build", configuration, "__project.nameasidentifier__")); + print("----------------------------"); +} diff --git a/Tools/nib2cib/NSAppKit.j b/Tools/nib2cib/NSAppKit.j index 8a54fb2d1..96a3a52b8 100644 --- a/Tools/nib2cib/NSAppKit.j +++ b/Tools/nib2cib/NSAppKit.j @@ -67,6 +67,7 @@ @import "NSViewController.j" @import "NSWindowTemplate.j" @import "WebView.j" +@import "NSSortDescriptor.j" function CP_NSMapClassName(aClassName) diff --git a/Tools/nib2cib/NSFont.j b/Tools/nib2cib/NSFont.j index 35508f4e9..41643eb22 100644 --- a/Tools/nib2cib/NSFont.j +++ b/Tools/nib2cib/NSFont.j @@ -33,8 +33,8 @@ if ((fontName === "LucidaGrande" || fontName === "LucidaGrande-Bold") && size === 13) { - CPLog.debug("Converting default IB font: <"+fontName+", "+size+"> to default Aristo font: "); - return [CPFont boldSystemFontOfSize:12.0]; + CPLog.debug("Removing default IB font: <"+fontName+", "+size+"> for theme default font."); + return nil; } // FIXME: Is this alwasy true? diff --git a/Tools/nib2cib/NSSearchField.j b/Tools/nib2cib/NSSearchField.j index 5e9173712..a68d5ede3 100644 --- a/Tools/nib2cib/NSSearchField.j +++ b/Tools/nib2cib/NSSearchField.j @@ -32,6 +32,12 @@ if (self) { + var cell = [aCoder decodeObjectForKey:@"NSCell"]; + + [self setRecentsAutosaveName:[cell recentsAutosaveName]]; + [self setMaximumRecents:[cell maximumRecents]]; + [self setSendsWholeSearchString:[cell sendsWholeSearchString]]; + [self setSendsSearchStringImmediately:[cell sendsSearchStringImmediately]]; } return self; @@ -59,5 +65,25 @@ @implementation NSSearchFieldCell : NSTextFieldCell { + CPString _recentsAutosaveName @accessors(property=recentsAutosaveName); + int _maximumRecents @accessors(property=maximumRecents); + BOOL _sendsWholeSearchString @accessors(property=sendsWholeSearchString); + BOOL _sendsSearchStringImmediately @accessors(property=sendsSearchStringImmediately); } + +- (id)initWithCoder:(CPCoder)aCoder +{ + if (self = [super initWithCoder:aCoder]) + { + _recentsAutosaveName = [aCoder decodeObjectForKey:@"NSRecentsAutosaveName"]; + _maximumRecents = [aCoder decodeIntForKey:@"NSMaximumRecents"]; + _sendsWholeSearchString = [aCoder decodeBoolForKey:@"NSSendsWholeSearchString"] ? YES : NO; + + // These bytes don't seem to be used for anything else but the send immediatly flag + _sendsSearchStringImmediately = [aCoder decodeBytesForKey:@"NSSearchFieldFlags"] ? YES: NO; + } + + return self; +} + @end diff --git a/Tools/nib2cib/NSSortDescriptor.j b/Tools/nib2cib/NSSortDescriptor.j new file mode 100644 index 000000000..c4f0beb87 --- /dev/null +++ b/Tools/nib2cib/NSSortDescriptor.j @@ -0,0 +1,35 @@ +@import + +@implementation CPSortDescriptor (NSCoding) +{ +} + +- (id)NS_initWithCoder:(CPCoder)aCoder +{ + if (self = [super init]) + { + _key = [aCoder decodeObjectForKey:@"NSKey"]; + _selector = CPSelectorFromString([aCoder decodeObjectForKey:@"NSSelector"]); + _ascending = [aCoder decodeBoolForKey:@"NSAscending"]; + } + + return self; +} + +@end + +@implementation NSSortDescriptor : CPSortDescriptor +{ +} + +- (id)initWithCoder:(CPCoder)aCoder +{ + return [self NS_initWithCoder:aCoder]; +} + +- (Class)classForKeyedArchiver +{ + return [CPSortDescriptor class]; +} + +@end \ No newline at end of file diff --git a/Tools/nib2cib/NSTableColumn.j b/Tools/nib2cib/NSTableColumn.j index 735ed5fc5..8af344031 100644 --- a/Tools/nib2cib/NSTableColumn.j +++ b/Tools/nib2cib/NSTableColumn.j @@ -37,23 +37,32 @@ //var dataViewCell = [aCoder decodeObjectForKey:@"NSDataCell"]; _dataView = [[CPTextField alloc] initWithFrame:CPRectMakeZero()]; - [_dataView setValue:[CPColor whiteColor] forThemeAttribute:@"text-color" inState:CPThemeStateSelected]; + + [_dataView setValue:[CPColor colorWithRed:51.0 / 255.0 green:51.0 / 255.0 blue:51.0 / 255.0 alpha:1.0] + forThemeAttribute:"text-color"]; + + [_dataView setValue:[CPColor whiteColor] forThemeAttribute:@"text-color" inState:CPThemeStateSelectedDataView]; + [_dataView setLineBreakMode:CPLineBreakByTruncatingTail]; + [_dataView setValue:[CPFont boldSystemFontOfSize:12.0] forThemeAttribute:@"font" inState:CPThemeStateSelectedDataView]; + [_dataView setValue:CPCenterVerticalTextAlignment forThemeAttribute:@"vertical-alignment"]; + [_dataView setValue:CGInsetMake(0.0, 0.0, 0.0, 5.0) forThemeAttribute:@"content-inset"]; var headerCell = [aCoder decodeObjectForKey:@"NSHeaderCell"], headerView = [[_CPTableColumnHeaderView alloc] initWithFrame:CPRectMakeZero()]; - - [_headerView setStringValue:[headerCell objectValue]]; - [_headerView setFont:[headerCell font]]; - - [self setHeaderView:_headerView]; + + [headerView setStringValue:[headerCell objectValue]]; + [self setHeaderView:headerView]; _width = [aCoder decodeFloatForKey:@"NSWidth"]; _minWidth = [aCoder decodeFloatForKey:@"NSMinWidth"]; _maxWidth = [aCoder decodeFloatForKey:@"NSMaxWidth"]; - _resizingMask = [aCoder decodeBoolForKey:@"NSIsResizable"]; + _resizingMask = [aCoder decodeBoolForKey:@"NSIsResizeable"] ? CPTableColumnUserResizingMask : CPTableColumnAutoresizingMask; + _isHidden = [aCoder decodeBoolForKey:@"NSHidden"]; + + _sortDescriptorPrototype = [aCoder decodeObjectForKey:@"NSSortDescriptorPrototype"]; } - + return self; } diff --git a/Tools/nib2cib/NSTableView.j b/Tools/nib2cib/NSTableView.j index 955ba7f14..f185ce817 100644 --- a/Tools/nib2cib/NSTableView.j +++ b/Tools/nib2cib/NSTableView.j @@ -54,6 +54,11 @@ _intercellSpacing = CGSizeMake(0.0, 0.0);//CGSizeMake([aCoder decodeFloatForKey:"NSIntercellSpacingWidth"], [aCoder decodeFloatForKey:"NSIntercellSpacingHeight"]); _gridColor = [aCoder decodeObjectForKey:@"NSGridColor"]; + + // Convert xib default to cib default + if ([_gridColor isEqual:[CPColor colorWithRed:127.0 / 255.0 green:127.0 / 255.0 blue:127.0 / 255.0 alpha:1.0]]) + _gridColor = [CPColor colorWithRed:229.0 / 255.0 green:229.0 / 255.0 blue:229.0 / 255.0 alpha:1.0]; + _gridStyleMask = [aCoder decodeIntForKey:@"NSGridStyleMask"]; _usesAlternatingRowBackgroundColors = (flags & 0x00800000) ? YES : NO;