mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-08 03:37:14 +00:00
Merge remote-tracking branch 'upstream/master' into CPAnimationContext
Conflicts: AppKit/CPView.j
This commit is contained in:
@@ -71,7 +71,6 @@ var SharedColorPanel = nil,
|
||||
*/
|
||||
@implementation CPColorPanel : CPPanel
|
||||
{
|
||||
_CPColorPanelToolbar _toolbar;
|
||||
_CPColorPanelSwatches _swatchView;
|
||||
_CPColorPanelPreview _previewView;
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@
|
||||
|
||||
@implementation _CPMenuBarWindow : CPPanel
|
||||
{
|
||||
CPMenu _menu;
|
||||
CPView _highlightView;
|
||||
CPArray _menuItemViews;
|
||||
|
||||
|
||||
@@ -29,7 +29,6 @@
|
||||
CGRect _animationTargetRect @accessors(property=_animationTargetRect);
|
||||
BOOL _selected @accessors(getter=_isSelected, setter=_setSelected:);
|
||||
BOOL _lastSelected @accessors(getter=_isLastSelected, setter=_setLastSelected:);
|
||||
CPColor _backgroundColor @accessors(property=backgroundColor);
|
||||
BOOL _editable @accessors(getter=isEditable, setter=setEditable:);
|
||||
}
|
||||
|
||||
|
||||
+31
-22
@@ -33,6 +33,15 @@
|
||||
#define SHOULD_SHOW_CORNER_VIEW() (_scrollerStyle === CPScrollerStyleLegacy && _verticalScroller && ![_verticalScroller isHidden])
|
||||
|
||||
|
||||
@protocol CPScrollViewDelegate <CPObject>
|
||||
|
||||
@optional
|
||||
- (void)scrollViewWillScroll:(CPScrollView)aScrollView;
|
||||
- (void)scrollViewDidScroll:(CPScrollView)aScrollView;
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/*! @ignore */
|
||||
var _isBrowserUsingOverlayScrollers = function()
|
||||
{
|
||||
@@ -98,35 +107,35 @@ var CPScrollerStyleGlobal = CPScrollerStyleOverlay,
|
||||
|
||||
@implementation CPScrollView : CPView
|
||||
{
|
||||
CPClipView _contentView;
|
||||
CPClipView _headerClipView;
|
||||
CPView _cornerView;
|
||||
CPView _bottomCornerView;
|
||||
CPClipView _contentView;
|
||||
CPClipView _headerClipView;
|
||||
CPView _cornerView;
|
||||
CPView _bottomCornerView;
|
||||
|
||||
id _delegate;
|
||||
CPTimer _scrollTimer;
|
||||
id <CPScrollViewDelegate> _delegate;
|
||||
CPTimer _scrollTimer;
|
||||
|
||||
BOOL _hasVerticalScroller;
|
||||
BOOL _hasHorizontalScroller;
|
||||
BOOL _autohidesScrollers;
|
||||
BOOL _hasVerticalScroller;
|
||||
BOOL _hasHorizontalScroller;
|
||||
BOOL _autohidesScrollers;
|
||||
|
||||
CPScroller _verticalScroller;
|
||||
CPScroller _horizontalScroller;
|
||||
CPScroller _verticalScroller;
|
||||
CPScroller _horizontalScroller;
|
||||
|
||||
CPInteger _recursionCount;
|
||||
CPInteger _implementedDelegateMethods;
|
||||
CPInteger _recursionCount;
|
||||
CPInteger _implementedDelegateMethods;
|
||||
|
||||
float _verticalLineScroll;
|
||||
float _verticalPageScroll;
|
||||
float _horizontalLineScroll;
|
||||
float _horizontalPageScroll;
|
||||
float _verticalLineScroll;
|
||||
float _verticalPageScroll;
|
||||
float _horizontalLineScroll;
|
||||
float _horizontalPageScroll;
|
||||
|
||||
CPBorderType _borderType;
|
||||
CPBorderType _borderType;
|
||||
|
||||
CPTimer _timerScrollersHide;
|
||||
CPTimer _timerScrollersHide;
|
||||
|
||||
int _scrollerStyle;
|
||||
int _scrollerKnobStyle;
|
||||
int _scrollerStyle;
|
||||
int _scrollerKnobStyle;
|
||||
}
|
||||
|
||||
|
||||
@@ -296,7 +305,7 @@ Notifies the delegate when the scroll view has finished scrolling.
|
||||
@endcode
|
||||
|
||||
*/
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
- (void)setDelegate:(id <CPScrollViewDelegate>)aDelegate
|
||||
{
|
||||
if (aDelegate === _delegate)
|
||||
return;
|
||||
|
||||
@@ -601,7 +601,7 @@ CPTableColumnUserResizingMask = 1 << 1;
|
||||
}
|
||||
|
||||
/*!
|
||||
Binds the receiver to an object.
|
||||
Binds the receiver to an object. Note that unlike Cocoa, this works only *after* the receiver has been added to a \c CPTableView.
|
||||
|
||||
@param CPString aBinding - The binding you wish to make. Typically CPValueBinding.
|
||||
@param id anObject - The object to bind the receiver to.
|
||||
|
||||
+17
-6
@@ -4458,6 +4458,9 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
|
||||
var row = [self rowAtPoint:aPoint];
|
||||
|
||||
_clickedRow = row;
|
||||
_clickedColumn = [self columnAtPoint:aPoint];
|
||||
|
||||
// If the user clicks outside a row then deselect everything.
|
||||
if (row < 0 && _allowsEmptySelection)
|
||||
{
|
||||
@@ -4520,6 +4523,12 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
}
|
||||
else
|
||||
[CPApp sendEvent:anEvent];
|
||||
|
||||
if ([anEvent type] == CPLeftMouseUp)
|
||||
{
|
||||
_clickedRow = CPNotFound;
|
||||
_clickedColumn = CPNotFound;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -4529,6 +4538,9 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
{
|
||||
var row = [self rowAtPoint:aPoint];
|
||||
|
||||
_clickedRow = row;
|
||||
_clickedColumn = [self columnAtPoint:aPoint];
|
||||
|
||||
// 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 && [self _dataSourceRespondsToWriteRowsWithIndexesToPasteboard])
|
||||
@@ -4617,9 +4629,12 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
rowIndex,
|
||||
shouldEdit = YES;
|
||||
|
||||
_clickedRow = [self rowAtPoint:aPoint];
|
||||
_clickedColumn = [self columnAtPoint:aPoint];
|
||||
|
||||
if ([self _dataSourceRespondsToWriteRowsWithIndexesToPasteboard])
|
||||
{
|
||||
rowIndex = [self rowAtPoint:aPoint];
|
||||
rowIndex = _clickedRow;
|
||||
|
||||
if (rowIndex !== -1)
|
||||
{
|
||||
@@ -4641,7 +4656,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
&& ([self _dataSourceRespondsToSetObjectValueForTableColumnRow]
|
||||
|| [self infoForBinding:@"content"]))
|
||||
{
|
||||
columnIndex = [self columnAtPoint:lastPoint];
|
||||
columnIndex = _clickedColumn;
|
||||
|
||||
if (columnIndex !== -1)
|
||||
{
|
||||
@@ -4666,11 +4681,7 @@ Your delegate can implement this method to avoid subclassing the tableview to ad
|
||||
|
||||
//double click actions
|
||||
if ([[CPApp currentEvent] clickCount] === 2 && _doubleAction)
|
||||
{
|
||||
_clickedRow = [self rowAtPoint:aPoint];
|
||||
_clickedColumn = [self columnAtPoint:lastPoint];
|
||||
[self sendAction:_doubleAction to:_target];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+19
-12
@@ -315,13 +315,6 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
_viewClassFlags = CPViewFlags[classUID];
|
||||
}
|
||||
|
||||
- (void)_setupToolTipHandlers
|
||||
{
|
||||
_toolTipInstalled = NO;
|
||||
_toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; }
|
||||
_toolTipFunctionOut = function(e) { [_CPToolTip invalidateCurrentToolTipIfNeeded]; };
|
||||
}
|
||||
|
||||
+ (CPSet)keyPathsForValuesAffectingFrame
|
||||
{
|
||||
return [CPSet setWithObjects:@"frameOrigin", @"frameSize"];
|
||||
@@ -391,9 +384,8 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
#endif
|
||||
|
||||
_animator = nil;
|
||||
_animationsDictionary = [CPDictionary dictionary];
|
||||
_animationsDictionary = @{};
|
||||
|
||||
[self _setupToolTipHandlers];
|
||||
[self _setupViewFlags];
|
||||
|
||||
[self _loadThemeAttributes];
|
||||
@@ -418,12 +410,16 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
_toolTip = aToolTip;
|
||||
|
||||
if (_toolTip)
|
||||
[self _manageToolTipInstallation];
|
||||
}
|
||||
|
||||
- (void)_manageToolTipInstallation
|
||||
{
|
||||
if ([self window] && _toolTip)
|
||||
[self _installToolTipEventHandlers];
|
||||
else
|
||||
[self _uninstallToolTipEventHandlers];
|
||||
}
|
||||
|
||||
/*! @ignore
|
||||
|
||||
Install the handlers for the tooltip
|
||||
@@ -433,6 +429,12 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
if (_toolTipInstalled)
|
||||
return;
|
||||
|
||||
if (!_toolTipFunctionIn)
|
||||
_toolTipFunctionIn = function(e) { [_CPToolTip scheduleToolTipForView:self]; }
|
||||
|
||||
if (!_toolTipFunctionOut)
|
||||
_toolTipFunctionOut = function(e) { [_CPToolTip invalidateCurrentToolTipIfNeeded]; };
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
if (_DOMElement.addEventListener)
|
||||
{
|
||||
@@ -475,6 +477,9 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
}
|
||||
#endif
|
||||
|
||||
_toolTipFunctionIn = nil;
|
||||
_toolTipFunctionOut = nil;
|
||||
|
||||
_toolTipInstalled = NO;
|
||||
}
|
||||
|
||||
@@ -813,6 +818,8 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
|
||||
[self viewDidMoveToWindow];
|
||||
|
||||
[self _manageToolTipInstallation];
|
||||
|
||||
[[self window] _dirtyKeyViewLoop];
|
||||
}
|
||||
|
||||
@@ -840,6 +847,7 @@ var CPViewHighDPIDrawingEnabled = YES;
|
||||
{
|
||||
// if (_graphicsContext)
|
||||
[self setNeedsDisplay:YES];
|
||||
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -3561,7 +3569,6 @@ var CPViewAutoresizingMaskKey = @"CPViewAutoresizingMask",
|
||||
|
||||
_hitTests = ![aCoder containsValueForKey:CPViewHitTestsKey] || [aCoder decodeBoolForKey:CPViewHitTestsKey];
|
||||
|
||||
[self _setupToolTipHandlers];
|
||||
_toolTip = [aCoder decodeObjectForKey:CPViewToolTipKey];
|
||||
|
||||
if (_toolTip)
|
||||
|
||||
@@ -575,6 +575,9 @@ CPTexturedBackgroundWindowMask
|
||||
var fullPlatformWindowViewClass = [[self class] _windowViewClassForFullPlatformWindowStyleMask:_styleMask],
|
||||
windowView = [[fullPlatformWindowViewClass alloc] initWithFrame:CGRectMakeZero() styleMask:_styleMask];
|
||||
|
||||
if (_platformWindow != [CPPlatformWindow primaryPlatformWindow] && [_platformWindow _hasInitializeInstanceWithWindow])
|
||||
[_platformWindow setContentRect:[self frame]];
|
||||
|
||||
[self _setWindowView:windowView];
|
||||
|
||||
[self setLevel:CPBackgroundWindowLevel];
|
||||
@@ -759,6 +762,9 @@ CPTexturedBackgroundWindowMask
|
||||
if (originMoved)
|
||||
[self _moveChildWindows:delta];
|
||||
}
|
||||
|
||||
if ([_platformWindow _canUpdateContentRect] && _isFullPlatformWindow && _platformWindow != [CPPlatformWindow primaryPlatformWindow])
|
||||
[_platformWindow setContentRect:aFrame];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -909,6 +915,10 @@ CPTexturedBackgroundWindowMask
|
||||
{
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
|
||||
if (!_isVisible)
|
||||
[_platformWindow _setShouldUpdateContentRect:NO];
|
||||
|
||||
// -dw- if a sheet is clicked, the parent window should come up too
|
||||
if (_isSheet)
|
||||
[_parentView orderFront:self];
|
||||
@@ -932,6 +942,8 @@ CPTexturedBackgroundWindowMask
|
||||
|
||||
if (!CPApp._mainWindow)
|
||||
[self makeMainWindow];
|
||||
|
||||
[_platformWindow _setShouldUpdateContentRect:YES];
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -947,6 +959,11 @@ CPTexturedBackgroundWindowMask
|
||||
*/
|
||||
- (void)_windowWillBeAddedToTheDOM
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(_didReceivePlatformWindowWillCloseNotification:)
|
||||
name:_CPPlatformWindowWillCloseNotification
|
||||
object:_platformWindow];
|
||||
|
||||
[[self contentView] _addObservers];
|
||||
}
|
||||
|
||||
@@ -955,6 +972,8 @@ CPTexturedBackgroundWindowMask
|
||||
*/
|
||||
- (void)_windowWillBeRemovedFromTheDOM
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] removeObserver:self name:_CPPlatformWindowWillCloseNotification object:nil];
|
||||
|
||||
[[self contentView] _removeObservers];
|
||||
}
|
||||
|
||||
@@ -1002,6 +1021,9 @@ CPTexturedBackgroundWindowMask
|
||||
if ([self _sharesChromeWithPlatformWindow])
|
||||
[_platformWindow orderOut:self];
|
||||
|
||||
if (_isFullPlatformWindow && _platformWindow != [CPPlatformWindow primaryPlatformWindow])
|
||||
[_platformWindow orderOut:self];
|
||||
|
||||
[_platformWindow order:CPWindowOut window:self relativeTo:nil];
|
||||
#endif
|
||||
|
||||
@@ -3353,6 +3375,14 @@ CPTexturedBackgroundWindowMask
|
||||
[super setValue:aValue forKey:aKey];
|
||||
}
|
||||
|
||||
- (void)_didReceivePlatformWindowWillCloseNotification:(CPNotification)aNotification
|
||||
{
|
||||
if ([aNotification object] != _platformWindow)
|
||||
return;
|
||||
|
||||
[self close];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var keyViewComparator = function(lhs, rhs, context)
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
|
||||
@implementation _CPHUDWindowView : _CPTitleableWindowView
|
||||
{
|
||||
CPView _toolbarView;
|
||||
CPButton _closeButton;
|
||||
}
|
||||
|
||||
|
||||
@@ -94,7 +94,6 @@ var _CPStandardWindowViewDividerViewHeight = 1.0;
|
||||
_CPTexturedWindowHeadView _headView;
|
||||
CPView _dividerView;
|
||||
CPView _bodyView;
|
||||
CPView _toolbarView;
|
||||
|
||||
CPButton _closeButton;
|
||||
CPButton _minimizeButton;
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
|
||||
@implementation CPCibHelpConnector : CPCibConnector
|
||||
{
|
||||
id _destination;
|
||||
id _file;
|
||||
id _marker;
|
||||
}
|
||||
|
||||
@@ -43,6 +43,8 @@ var PrimaryPlatformWindow = NULL;
|
||||
BOOL _hasShadow;
|
||||
unsigned _shadowStyle;
|
||||
CPString _title;
|
||||
BOOL _shouldUpdateContentRect;
|
||||
BOOL _hasInitializeInstanceWithWindow;
|
||||
|
||||
#if PLATFORM(DOM)
|
||||
DOMWindow _DOMWindow;
|
||||
@@ -119,6 +121,17 @@ var PrimaryPlatformWindow = NULL;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)initWithWindow:(CPWindow)aWindow
|
||||
{
|
||||
self = [self initWithContentRect:CGRectMakeCopy([aWindow frame])];
|
||||
|
||||
_hasInitializeInstanceWithWindow = YES;
|
||||
[aWindow setPlatformWindow:self];
|
||||
[aWindow setFullPlatformWindow:YES];
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
- (id)init
|
||||
{
|
||||
return [self initWithContentRect:CGRectMake(0.0, 0.0, 400.0, 500.0)];
|
||||
@@ -284,6 +297,22 @@ var PrimaryPlatformWindow = NULL;
|
||||
return _title;
|
||||
}
|
||||
|
||||
- (BOOL)_canUpdateContentRect
|
||||
{
|
||||
// We onyl update the contentRect with the frame of the bridgeless window if we have initialized the platform with the method initWithWindow:
|
||||
return _shouldUpdateContentRect && _hasInitializeInstanceWithWindow;
|
||||
}
|
||||
|
||||
- (BOOL)_hasInitializeInstanceWithWindow
|
||||
{
|
||||
return _hasInitializeInstanceWithWindow;
|
||||
}
|
||||
|
||||
- (void)_setShouldUpdateContentRect:(BOOL)aBoolean
|
||||
{
|
||||
_shouldUpdateContentRect = aBoolean;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
#if PLATFORM(BROWSER)
|
||||
|
||||
@@ -203,6 +203,9 @@ var ModifierKeyCodes = [
|
||||
var resizeTimer = nil;
|
||||
var PreventScroll = true;
|
||||
|
||||
_CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotification";
|
||||
|
||||
|
||||
// When scrolling with an old-style scroll wheel with discete steps ('clicks'), the scroll amount can indicate how many "lines" to
|
||||
// scroll.
|
||||
#define SCROLLWHEEL_LINE_PIXELS 6.0
|
||||
@@ -429,6 +432,9 @@ var PreventScroll = true;
|
||||
|
||||
_DOMWindow.addEventListener("unload", function()
|
||||
{
|
||||
_DOMWindow.removeEventListener("unload", arguments.callee, NO);
|
||||
|
||||
[self _notifyPlatformWindowWillClose];
|
||||
[self updateFromNativeContentRect];
|
||||
[self _removeLayers];
|
||||
|
||||
@@ -452,8 +458,6 @@ var PreventScroll = true;
|
||||
_DOMWindow.removeEventListener("wheel", scrollEventCallback, NO);
|
||||
_DOMWindow.removeEventListener("mousewheel", scrollEventCallback, NO);
|
||||
|
||||
//_DOMWindow.removeEventListener("beforeunload", this, NO);
|
||||
|
||||
[PlatformWindows removeObject:self];
|
||||
|
||||
[_platformPasteboard setDOMWindow:nil];
|
||||
@@ -483,6 +487,9 @@ var PreventScroll = true;
|
||||
|
||||
_DOMWindow.attachEvent("onunload", function()
|
||||
{
|
||||
_DOMWindow.detachEvent("unload", arguments.callee);
|
||||
|
||||
[self _notifyPlatformWindowWillClose];
|
||||
[self updateFromNativeContentRect];
|
||||
[self _removeLayers];
|
||||
|
||||
@@ -504,8 +511,6 @@ var PreventScroll = true;
|
||||
_DOMBodyElement.ondrag = NULL;
|
||||
_DOMBodyElement.onselectstart = NULL;
|
||||
|
||||
//_DOMWindow.removeEvent("beforeunload", this);
|
||||
|
||||
[PlatformWindows removeObject:self];
|
||||
|
||||
[_platformPasteboard setDOMWindow:nil];
|
||||
@@ -988,6 +993,7 @@ var PreventScroll = true;
|
||||
|
||||
- (void)_actualResizeEvent
|
||||
{
|
||||
_shouldUpdateContentRect = NO;
|
||||
resizeTimer = nil;
|
||||
|
||||
// FIXME: This is not the right way to do this.
|
||||
@@ -1021,6 +1027,8 @@ var PreventScroll = true;
|
||||
//window.liveResize = NO;
|
||||
|
||||
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode];
|
||||
|
||||
_shouldUpdateContentRect = YES;
|
||||
}
|
||||
|
||||
- (void)touchEvent:(DOMEvent)aDOMEvent
|
||||
@@ -1629,6 +1637,13 @@ var PreventScroll = true;
|
||||
KeyCodesToPrevent = {};
|
||||
}
|
||||
|
||||
- (void)_notifyPlatformWindowWillClose
|
||||
{
|
||||
[[CPNotificationCenter defaultCenter] postNotificationName:_CPPlatformWindowWillCloseNotification
|
||||
object:self
|
||||
userInfo:nil];
|
||||
}
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
||||
@@ -63,7 +63,6 @@ var _CPPopoverWindow_shouldClose_ = 1 << 4,
|
||||
BOOL _browserAnimates;
|
||||
BOOL _isObservingFrame;
|
||||
BOOL _shouldPerformAnimation;
|
||||
CPInteger _implementedDelegateMethods;
|
||||
CGRect _targetRect;
|
||||
CPWindow _targetWindow;
|
||||
JSObject _orderOutTransitionFunction;
|
||||
|
||||
@@ -0,0 +1,371 @@
|
||||
/*
|
||||
* CPCache.j
|
||||
* Foundation
|
||||
*
|
||||
* Created by William Mura.
|
||||
* Copyright 2015, William Mura.
|
||||
*
|
||||
* 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 "CPObject.j"
|
||||
@import "CPDictionary.j"
|
||||
@import "CPString.j"
|
||||
|
||||
@class _CPCacheItem;
|
||||
|
||||
|
||||
/*
|
||||
* Delegate CPCacheDelegate
|
||||
*
|
||||
* - cache:willEvictObject: is called when a object is going to be removed
|
||||
* When the total cost or the count exceeds the total cost limit or the count limit
|
||||
* And also when removeObjectForKey or removeAllObjects are called
|
||||
*/
|
||||
@protocol CPCacheDelegate <CPObject>
|
||||
|
||||
@optional
|
||||
- (void)cache:(CPCache)cache willEvictObject:(id)obj;
|
||||
|
||||
@end
|
||||
|
||||
var CPCacheDelegate_cache_willEvictObject_ = 1 << 1;
|
||||
|
||||
|
||||
/*!
|
||||
@class CPCache
|
||||
@ingroup foundation
|
||||
@brief A collection-like container with discardable objects
|
||||
|
||||
https://developer.apple.com/library/mac/documentation/Cocoa/Reference/NSCache_Class/index.html#//apple_ref/occ/instp/NSCache/delegate
|
||||
|
||||
A CPCache object is a collection-like container, or cache, that stores key-value pairs,
|
||||
similar to the CPDictionary class. Developers often incorporate caches to temporarily
|
||||
store objects with transient data that are expensive to create.
|
||||
|
||||
Reusing these objects can provide performance benefits, because their values do not have to be recalculated.
|
||||
However, the objects are not critical to the application and can be discarded if memory is tight.
|
||||
If discarded, their values will have to be recomputed again when needed.
|
||||
*/
|
||||
@implementation CPCache : CPObject
|
||||
{
|
||||
CPDictionary _items;
|
||||
int _currentPosition;
|
||||
BOOL _totalCostCache;
|
||||
unsigned _implementedDelegateMethods;
|
||||
|
||||
CPString _name @accessors(property=name);
|
||||
int _countLimit @accessors(property=countLimit);
|
||||
int _totalCostLimit @accessors(property=totalCostLimit);
|
||||
id <CPCacheDelegate> _delegate @accessors(property=delegate);
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Initialization
|
||||
|
||||
/*!
|
||||
Initializes the cache with default values
|
||||
@return the initialized cache
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
if (self = [super init])
|
||||
{
|
||||
_items = [[CPDictionary alloc] init];
|
||||
_currentPosition = 0;
|
||||
_totalCostCache = -1;
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
_name = @"";
|
||||
_countLimit = 0;
|
||||
_totalCostLimit = 0;
|
||||
_delegate = nil;
|
||||
}
|
||||
|
||||
return self;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Managing cache
|
||||
|
||||
/*!
|
||||
Returns the object which correspond to the given key
|
||||
@param aKey the key for the object's entry
|
||||
@return the object for the entry
|
||||
*/
|
||||
- (id)objectForKey:(id)aKey
|
||||
{
|
||||
return [[_items objectForKey:aKey] object];
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds an object with default cost into the cache.
|
||||
@param anObject the object to add in the cache
|
||||
@param aKey the object's key
|
||||
*/
|
||||
- (void)setObject:(id)anObject forKey:(id)aKey
|
||||
{
|
||||
[self setObject:anObject forKey:aKey cost:0];
|
||||
}
|
||||
|
||||
/*!
|
||||
Adds an object with a cost into the cache.
|
||||
@param anObject the object to add in the cache
|
||||
@param aKey the object's key
|
||||
@param aCost the object's cost
|
||||
*/
|
||||
- (void)setObject:(id)anObject forKey:(id)aKey cost:(int)aCost
|
||||
{
|
||||
// Check if the key already exist
|
||||
if ([_items objectForKey:aKey])
|
||||
[self removeObjectForKey:aKey];
|
||||
|
||||
// Add object
|
||||
[_items setObject:[_CPCacheItem cacheItemWithObject:anObject cost:aCost position:++_currentPosition] forKey:aKey];
|
||||
|
||||
// Invalid cost cache
|
||||
_totalCostCache = -1;
|
||||
|
||||
// Clean cache to satisfy condition (< totalCostLimit & < countLimit) if necessary
|
||||
[self _cleanCache];
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes the object from the cache for the given key.
|
||||
@param aKey the key of the object to be removed
|
||||
*/
|
||||
- (void)removeObjectForKey:(id)aKey
|
||||
{
|
||||
// Call delegate method to warn that the object is going to be removed
|
||||
[self _sendDelegateWillEvictObjectForKey:aKey];
|
||||
|
||||
// Remove object
|
||||
[_items removeObjectForKey:aKey];
|
||||
|
||||
// Invalid cost cache
|
||||
_totalCostCache = -1;
|
||||
}
|
||||
|
||||
/*!
|
||||
Removes all the objects from the cache.
|
||||
*/
|
||||
- (void)removeAllObjects
|
||||
{
|
||||
// Call delegate method to warn that the objects are going to be removed
|
||||
var enumerator = [_items keyEnumerator],
|
||||
key;
|
||||
|
||||
while (key = [enumerator nextObject])
|
||||
[self _sendDelegateWillEvictObjectForKey:key]
|
||||
|
||||
// Remove all objects
|
||||
[_items removeAllObjects];
|
||||
|
||||
// Invalid cost cache and reset position counter
|
||||
_totalCostCache = -1;
|
||||
_currentPosition = 0;
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Setters
|
||||
|
||||
/*!
|
||||
Sets the count limit of the cache.
|
||||
Remove objects if not enough place to keep all of them
|
||||
@param aCountLimit the new count limit
|
||||
*/
|
||||
- (void)setCountLimit:(int)aCountLimit
|
||||
{
|
||||
_countLimit = aCountLimit;
|
||||
[self _cleanCache];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the total cost limit of the cache.
|
||||
Remove objects if not enough place to keep all of them
|
||||
@param aTotalCostLimit the new total cost limit
|
||||
*/
|
||||
- (void)setTotalCostLimit:(int)aTotalCostLimit
|
||||
{
|
||||
_totalCostLimit = aTotalCostLimit;
|
||||
[self _cleanCache];
|
||||
}
|
||||
|
||||
/*!
|
||||
Sets the cache's delegate.
|
||||
@param aDelegate the new delegate
|
||||
*/
|
||||
- (void)setDelegate:(id)aDelegate
|
||||
{
|
||||
if (_delegate === aDelegate)
|
||||
return;
|
||||
|
||||
_delegate = aDelegate;
|
||||
_implementedDelegateMethods = 0;
|
||||
|
||||
if ([_delegate respondsToSelector:@selector(cache:willEvictObject:)])
|
||||
_implementedDelegateMethods |= CPCacheDelegate_cache_willEvictObject_
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Privates
|
||||
|
||||
/*
|
||||
* This method return the number of objects in the cache
|
||||
*/
|
||||
- (int)_count
|
||||
{
|
||||
return [_items count];
|
||||
}
|
||||
|
||||
/*
|
||||
* This method return the total cost (addition of all object's cost in the cache)
|
||||
*/
|
||||
- (int)_totalCost
|
||||
{
|
||||
if (_totalCostCache >= 0)
|
||||
return _totalCostCache;
|
||||
|
||||
var enumerator = [_items objectEnumerator],
|
||||
value;
|
||||
|
||||
_totalCostCache = 0;
|
||||
|
||||
while (value = [enumerator nextObject])
|
||||
_totalCostCache += [value cost];
|
||||
|
||||
return _totalCostCache;
|
||||
}
|
||||
|
||||
/*
|
||||
* This method resequence the position of objects
|
||||
* Otherwise the position value could rise until to cause problem
|
||||
*/
|
||||
- (void)_resequencePosition
|
||||
{
|
||||
_currentPosition = 1;
|
||||
|
||||
// Sort keys by position
|
||||
var sortedKeys = [[_items allKeys] sortedArrayUsingFunction:
|
||||
function(k1, k2) {
|
||||
return [[[_items objectForKey:k1] position] compare:[[_items objectForKey:k2] position]]; }];
|
||||
|
||||
// Affect new positions
|
||||
for (var i = 0; i < sortedKeys.length; ++i)
|
||||
[[_items objectForKey:sortedKeys[i]] setPosition:_currentPosition++];
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the totalCostLimit is exceeded
|
||||
*/
|
||||
- (BOOL)_isTotalCostLimitExceeded
|
||||
{
|
||||
return ([self _totalCost] > _totalCostLimit && _totalCostLimit > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the countLimit is exceeded
|
||||
*/
|
||||
- (BOOL)_isCountLimitExceeded
|
||||
{
|
||||
return ([self _count] > _countLimit && _countLimit > 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* This method clean the cache if the totalCost or the count exceeds the totalCostLimit or the countLimit
|
||||
* until to satisfy condition (totalCost < totalCostLimit and count < countLimit)
|
||||
*/
|
||||
- (void)_cleanCache
|
||||
{
|
||||
// Check if the condition is satisfied
|
||||
if (![self _isTotalCostLimitExceeded] && ![self _isCountLimitExceeded])
|
||||
return;
|
||||
|
||||
// Sort keys by position
|
||||
var sortedKeys = [[_items allKeys] sortedArrayUsingFunction:
|
||||
function(k1, k2) {
|
||||
return [[[_items objectForKey:k1] position] compare:[[_items objectForKey:k2] position]]; }];
|
||||
|
||||
// Remove oldest objects until to satisfy the break condition
|
||||
for (var i = 0; i < sortedKeys.length; ++i)
|
||||
{
|
||||
if (![self _isTotalCostLimitExceeded] && ![self _isCountLimitExceeded])
|
||||
break;
|
||||
|
||||
// Call delegate method to warn that the object is going to be removed
|
||||
[self _sendDelegateWillEvictObjectForKey:sortedKeys[i]];
|
||||
|
||||
// Remove object
|
||||
[_items removeObjectForKey: sortedKeys[i]];
|
||||
|
||||
// Invalid cost cache
|
||||
_totalCostCache = -1;
|
||||
}
|
||||
|
||||
// Resequence position of all objects
|
||||
[self _resequencePosition];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
@implementation CPCache (CPCacheDelegate)
|
||||
|
||||
- (void)_sendDelegateWillEvictObjectForKey:(id)aKey
|
||||
{
|
||||
if (_implementedDelegateMethods & CPCacheDelegate_cache_willEvictObject_)
|
||||
[_delegate cache:self willEvictObject:[[_items objectForKey:aKey] object]];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
/*
|
||||
* Class _CPCacheItem
|
||||
* Represent an item of CPCache
|
||||
* This class allow to associate a cost and a position to an object
|
||||
*
|
||||
* Attributes:
|
||||
* - object: the stored object
|
||||
* - cost: represent the cost (of memory) of the object
|
||||
* - position: represent the insertion order to determine the oldest object
|
||||
*/
|
||||
@implementation _CPCacheItem : CPObject
|
||||
{
|
||||
CPObject _object @accessors(property=object);
|
||||
int _cost @accessors(property=cost);
|
||||
int _position @accessors(property=position);
|
||||
}
|
||||
|
||||
+ (id)cacheItemWithObject:(CPObject)anObject cost:(int)aCost position:(int)aPosition
|
||||
{
|
||||
var cacheItem = [[self alloc] init];
|
||||
|
||||
if (cacheItem)
|
||||
{
|
||||
[cacheItem setObject:anObject];
|
||||
[cacheItem setCost:aCost];
|
||||
[cacheItem setPosition:aPosition];
|
||||
}
|
||||
|
||||
return cacheItem;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -24,6 +24,7 @@
|
||||
@import "CPArray.j"
|
||||
@import "CPBundle.j"
|
||||
@import "CPByteCountFormatter.j"
|
||||
@import "CPCache.j"
|
||||
@import "CPCharacterSet.j"
|
||||
@import "CPCoder.j"
|
||||
@import "CPComparisonPredicate.j"
|
||||
|
||||
@@ -1754,8 +1754,15 @@ ClassDeclarationStatement: function(node, st, c) {
|
||||
ivar = {"type": ivarType, "name": ivarName},
|
||||
accessors = ivarDecl.accessors;
|
||||
|
||||
if (ivars[ivarName])
|
||||
throw compiler.error_message("Instance variable '" + ivarName + "' is already declared for class " + className, ivarDecl.id);
|
||||
var checkIfIvarIsAlreadyDeclaredAndInSuperClass = function(aClassDef, recursiveFunction) {
|
||||
if (aClassDef.ivars[ivarName])
|
||||
throw compiler.error_message("Instance variable '" + ivarName + "' is already declared for class " + className + (aClassDef.name !== className ? " in superclass " + aClassDef.name : ""), ivarDecl.id);
|
||||
if (aClassDef.superClass)
|
||||
recursiveFunction(aClassDef.superClass, recursiveFunction);
|
||||
}
|
||||
|
||||
// Check if ivar is already declared in this class or its super classes.
|
||||
checkIfIvarIsAlreadyDeclaredAndInSuperClass(classDef, checkIfIvarIsAlreadyDeclaredAndInSuperClass);
|
||||
|
||||
var isTypeDefined = !ivarTypeIsClass || typeof global[ivarType] !== "undefined" || typeof window[ivarType] !== "undefined"
|
||||
|| compiler.getClassDef(ivarType) || compiler.getTypeDef(ivarType) || ivarType == classDef.name;
|
||||
|
||||
@@ -699,6 +699,78 @@ var methodCalled;
|
||||
[self assert:expectedRestult equals:methodCalled];
|
||||
}
|
||||
|
||||
- (void)testToolTipInitialEmpty
|
||||
{
|
||||
[self assert:nil equals:view._toolTip];
|
||||
[self assert:nil equals:view._toolTipInstalled];
|
||||
[self assert:nil equals:view._toolTipFunctionIn];
|
||||
[self assert:nil equals:view._toolTipFunctionOut];
|
||||
}
|
||||
|
||||
- (void)testToolTipWithToolTipAndNoWindow
|
||||
{
|
||||
[view setToolTip:@"tooltip"];
|
||||
|
||||
[self assert:@"tooltip" equals:view._toolTip];
|
||||
[self assert:nil equals:view._toolTipInstalled];
|
||||
[self assert:nil equals:view._toolTipFunctionIn];
|
||||
[self assert:nil equals:view._toolTipFunctionOut];
|
||||
}
|
||||
|
||||
- (void)testToolTipWithToolTipAndWindow
|
||||
{
|
||||
[view setToolTip:@"tooltip"];
|
||||
|
||||
[[window contentView] addSubview:view]
|
||||
|
||||
[self assert:@"tooltip" equals:view._toolTip];
|
||||
[self assertTrue:view._toolTipInstalled];
|
||||
[self assertTrue:!!view._toolTipFunctionIn];
|
||||
[self assertTrue:!!view._toolTipFunctionOut];
|
||||
}
|
||||
|
||||
- (void)testToolTipWithToolTipAndWindowThenNoWindow
|
||||
{
|
||||
[view setToolTip:@"tooltip"];
|
||||
|
||||
[[window contentView] addSubview:view]
|
||||
[view removeFromSuperview];
|
||||
|
||||
[self assert:@"tooltip" equals:view._toolTip];
|
||||
[self assert:NO equals:view._toolTipInstalled];
|
||||
[self assert:nil equals:view._toolTipFunctionIn];
|
||||
[self assert:nil equals:view._toolTipFunctionOut];
|
||||
}
|
||||
|
||||
- (void)testToolTipWithNoToolTipAndWindowThenNoWindowThenToolTip
|
||||
{
|
||||
[self assert:nil equals:view._toolTip];
|
||||
[self assert:nil equals:view._toolTipInstalled];
|
||||
[self assert:nil equals:view._toolTipFunctionIn];
|
||||
[self assert:nil equals:view._toolTipFunctionOut];
|
||||
|
||||
[[window contentView] addSubview:view];
|
||||
|
||||
[self assert:nil equals:view._toolTip];
|
||||
[self assert:nil equals:view._toolTipInstalled];
|
||||
[self assert:nil equals:view._toolTipFunctionIn];
|
||||
[self assert:nil equals:view._toolTipFunctionOut];
|
||||
|
||||
[view setToolTip:@"tooltip"];
|
||||
|
||||
[self assert:@"tooltip" equals:view._toolTip];
|
||||
[self assertTrue:view._toolTipInstalled];
|
||||
[self assertTrue:!!view._toolTipFunctionIn];
|
||||
[self assertTrue:!!view._toolTipFunctionOut];
|
||||
|
||||
[view removeFromSuperview];
|
||||
|
||||
[self assert:@"tooltip" equals:view._toolTip];
|
||||
[self assert:NO equals:view._toolTipInstalled];
|
||||
[self assert:nil equals:view._toolTipFunctionIn];
|
||||
[self assert:nil equals:view._toolTipFunctionOut];
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation CPLayoutView : CPView
|
||||
@@ -747,4 +819,4 @@ var methodCalled;
|
||||
return YES;
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
@import <Foundation/CPCache.j>
|
||||
|
||||
|
||||
@implementation CPCacheItemTest : OJTestCase
|
||||
|
||||
- (void)testCacheItemWithObjectCostPosition
|
||||
{
|
||||
var cacheItem = [_CPCacheItem cacheItemWithObject:"Hello Cappuccino!" cost:50 position:1];
|
||||
[self assert:[cacheItem object] equals:"Hello Cappuccino!"];
|
||||
[self assert:[cacheItem cost] equals:50];
|
||||
[self assert:[cacheItem position] equals:1];
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,411 @@
|
||||
@import <Foundation/CPCache.j>
|
||||
|
||||
|
||||
/*
|
||||
* CPCacheTest tests all methods from CPCache
|
||||
*/
|
||||
@implementation CPCacheTest : OJTestCase<CPCacheDelegate>
|
||||
{
|
||||
// Parameters to check delegate
|
||||
int _countDelegateExecuted;
|
||||
CPArray _caches;
|
||||
CPArray _objects;
|
||||
}
|
||||
|
||||
- (void)setUp
|
||||
{
|
||||
// Initialize data to check delegate
|
||||
_countDelegateExecuted = 0;
|
||||
_caches = [[CPArray alloc] init];
|
||||
_objects = [[CPArray alloc] init];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Delegate method, called when an object is evicted
|
||||
*/
|
||||
- (void)cache:(CPCache)cache willEvictObject:(id)obj
|
||||
{
|
||||
_countDelegateExecuted++;
|
||||
[_caches addObject:cache];
|
||||
[_objects addObject:obj];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Tests methods of CPCache
|
||||
*/
|
||||
- (void)testInit
|
||||
{
|
||||
var cache = [[CPCache alloc] init];
|
||||
|
||||
// Private attributes
|
||||
[self assert:CPCache equals:[cache class]];
|
||||
[self assert:CPDictionary equals:[cache._items class]];
|
||||
[self assert:0 equals:cache._currentPosition];
|
||||
[self assert:-1 equals:cache._totalCostCache];
|
||||
[self assert:0 equals:cache._implementedDelegateMethods];
|
||||
|
||||
// Public attributes
|
||||
[self assert:@"" equals:[cache name]];
|
||||
[self assert:0 equals:[cache countLimit]];
|
||||
[self assert:0 equals:[cache totalCostLimit]];
|
||||
[self assert:nil equals:[cache delegate]];
|
||||
}
|
||||
|
||||
- (void)testObjectForKey
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setObject:@"Object1" forKey:@"key1"];
|
||||
|
||||
// With valid key
|
||||
[self assert:@"Object1" equals:[cache objectForKey:@"key1"]];
|
||||
|
||||
// With invalid key
|
||||
[self assert:nil equals:[cache objectForKey:@"key666"]];
|
||||
|
||||
// With nil key
|
||||
[self assert:nil equals:[cache objectForKey:nil]];
|
||||
}
|
||||
|
||||
- (void)testSetObjectForKeyDefault
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setDelegate:self];
|
||||
|
||||
// Check result when add key in empty cache
|
||||
[cache setObject:@"Object1" forKey:@"key1"]
|
||||
[self assert:@"Object1" equals:[cache objectForKey:@"key1"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:0 equals:_countDelegateExecuted];
|
||||
[self assert:0 equals:[_caches count]];
|
||||
[self assert:0 equals:[_objects count]];
|
||||
}
|
||||
|
||||
- (void)testSetObjectForKeyWithExistingKey
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setDelegate:self];
|
||||
[cache setObject:@"Object1" forKey:@"key1"];
|
||||
[cache setObject:@"Object2" forKey:@"key2"];
|
||||
|
||||
// Check result when add object with existing key in cache
|
||||
[cache setObject:@"Object3" forKey:@"key1"];
|
||||
[self assert:@"Object3" equals:[cache objectForKey:@"key1"]];
|
||||
[self assert:@"Object2" equals:[cache objectForKey:@"key2"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:1 equals:_countDelegateExecuted];
|
||||
[self assert:cache equals:[_caches objectAtIndex:0]];
|
||||
[self assert:@"Object1" equals:[_objects objectAtIndex:0]];
|
||||
}
|
||||
|
||||
- (void)testSetObjectForKeyDiscardByCost
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setDelegate:self];
|
||||
[cache setTotalCostLimit:100];
|
||||
[cache setObject:@"Object1" forKey:@"key1" cost:50];
|
||||
[cache setObject:@"Object2" forKey:@"key2" cost:50];
|
||||
|
||||
// Check result when add key in cache with total cost limit
|
||||
[cache setObject:@"Object3" forKey:@"key3" cost:1];
|
||||
[self assert:nil equals:[cache objectForKey:@"key1"]];
|
||||
[self assert:@"Object2" equals:[cache objectForKey:@"key2"]];
|
||||
[self assert:@"Object3" equals:[cache objectForKey:@"key3"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:1 equals:_countDelegateExecuted];
|
||||
[self assert:cache equals:[_caches objectAtIndex:0]];
|
||||
[self assert:@"Object1" equals:[_objects objectAtIndex:0]];
|
||||
}
|
||||
|
||||
- (void)testSetObjectForKeyDiscardByCount
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setDelegate:self];
|
||||
[cache setCountLimit:2];
|
||||
[cache setObject:@"Object1" forKey:@"key1"];
|
||||
[cache setObject:@"Object2" forKey:@"key2"];
|
||||
|
||||
// Check result when add key in cache with count limit
|
||||
[cache setObject:@"Object3" forKey:@"key3"];
|
||||
[self assert:nil equals:[cache objectForKey:@"key1"]];
|
||||
[self assert:@"Object2" equals:[cache objectForKey:@"key2"]];
|
||||
[self assert:@"Object3" equals:[cache objectForKey:@"key3"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:1 equals:_countDelegateExecuted];
|
||||
[self assert:cache equals:[_caches objectAtIndex:0]];
|
||||
[self assert:@"Object1" equals:[_objects objectAtIndex:0]];
|
||||
}
|
||||
|
||||
- (void)testRemoveObjectForKey
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setDelegate:self];
|
||||
[cache setObject:@"Object1" forKey:@"key1"];
|
||||
|
||||
// Remove object
|
||||
[cache removeObjectForKey:@"key1"];
|
||||
[self assert:nil equals:[cache objectForKey:@"key1"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:1 equals:_countDelegateExecuted];
|
||||
[self assert:cache equals:[_caches objectAtIndex:0]];
|
||||
[self assert:@"Object1" equals:[_objects objectAtIndex:0]];
|
||||
}
|
||||
|
||||
- (void)testRemoveAllObjects
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setDelegate:self];
|
||||
[cache setObject:@"Object1" forKey:@"key1"];
|
||||
[cache setObject:@"Object2" forKey:@"key2"];
|
||||
|
||||
// Remove all objects
|
||||
[cache removeAllObjects];
|
||||
[self assert:nil equals:[cache objectForKey:@"key1"]];
|
||||
[self assert:nil equals:[cache objectForKey:@"key2"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:2 equals:_countDelegateExecuted];
|
||||
[self assert:cache equals:[_caches objectAtIndex:0]];
|
||||
[self assert:@"Object1" equals:[_objects objectAtIndex:0]];
|
||||
[self assert:cache equals:[_caches objectAtIndex:1]];
|
||||
[self assert:@"Object2" equals:[_objects objectAtIndex:1]];
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Accessors and mutators
|
||||
*/
|
||||
|
||||
- (void)testSetCountLimit
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setDelegate:self];
|
||||
[cache setObject:@"Object1" forKey:@"key1"];
|
||||
[cache setObject:@"Object2" forKey:@"key2"];
|
||||
[cache setObject:@"Object3" forKey:@"key3"];
|
||||
|
||||
// Set count limit
|
||||
[cache setCountLimit:2];
|
||||
[self assert:nil equals:[cache objectForKey:@"key1"]];
|
||||
[self assert:@"Object2" equals:[cache objectForKey:@"key2"]];
|
||||
[self assert:@"Object3" equals:[cache objectForKey:@"key3"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:1 equals:_countDelegateExecuted];
|
||||
[self assert:cache equals:[_caches objectAtIndex:0]];
|
||||
[self assert:@"Object1" equals:[_objects objectAtIndex:0]];
|
||||
|
||||
// Check new count limit
|
||||
[self assert:2 equals:[cache countLimit]];
|
||||
}
|
||||
|
||||
- (void)testSetTotalCostLimit
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setDelegate:self];
|
||||
[cache setObject:@"Object1" forKey:@"key1" cost:50];
|
||||
[cache setObject:@"Object2" forKey:@"key2" cost:10];
|
||||
[cache setObject:@"Object3" forKey:@"key3" cost:70];
|
||||
|
||||
// Set total cost limit
|
||||
[cache setTotalCostLimit:100];
|
||||
[self assert:nil equals:[cache objectForKey:@"key1"]];
|
||||
[self assert:@"Object2" equals:[cache objectForKey:@"key2"]];
|
||||
[self assert:@"Object3" equals:[cache objectForKey:@"key3"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:1 equals:_countDelegateExecuted];
|
||||
[self assert:cache equals:[_caches objectAtIndex:0]];
|
||||
[self assert:@"Object1" equals:[_objects objectAtIndex:0]];
|
||||
|
||||
// Check total cost limit
|
||||
[self assert:100 equals:[cache totalCostLimit]];
|
||||
}
|
||||
|
||||
- (void)testSetDelegate
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setDelegate:self];
|
||||
|
||||
[self assert:self equals:[cache delegate]];
|
||||
[self assert:(1 << 1) equals:cache._implementedDelegateMethods]
|
||||
}
|
||||
|
||||
/*
|
||||
* Test private methods
|
||||
*/
|
||||
|
||||
- (void)testCount
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setObject:@"Object1" forKey:@"key1" cost:50];
|
||||
[cache setObject:@"Object2" forKey:@"key2" cost:10];
|
||||
[cache setObject:@"Object3" forKey:@"key3" cost:70];
|
||||
|
||||
[self assert:3 equals:[cache _count]];
|
||||
}
|
||||
|
||||
- (void)testTotalCost
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setObject:@"Object1" forKey:@"key1" cost:50];
|
||||
[cache setObject:@"Object2" forKey:@"key2" cost:10];
|
||||
[cache setObject:@"Object3" forKey:@"key3" cost:70];
|
||||
|
||||
[self assert:130 equals:[cache _totalCost]];
|
||||
|
||||
// Hack totalCost then invalidate it to be sure that it is recomputed
|
||||
cache._totalCostCache = 650;
|
||||
[self assert:650 equals:[cache _totalCost]];
|
||||
|
||||
cache._totalCostCache = -1;
|
||||
[self assert:130 equals:[cache _totalCost]];
|
||||
}
|
||||
|
||||
- (void)testResequencePosition
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setObject:@"Object1" forKey:@"key1" cost:50]; // 1 - 1
|
||||
[cache setObject:@"Object2" forKey:@"key2" cost:10];
|
||||
[cache setObject:@"Object3" forKey:@"key3" cost:70]; // 3 - 2
|
||||
[cache removeObjectForKey:@"key2"]
|
||||
[cache setObject:@"Object4" forKey:@"key4" cost:70]; // 4 - 3
|
||||
|
||||
// Before resequence
|
||||
[self assert:1 equals:[[cache._items objectForKey:@"key1"] position]];
|
||||
[self assert:3 equals:[[cache._items objectForKey:@"key3"] position]];
|
||||
[self assert:4 equals:[[cache._items objectForKey:@"key4"] position]];
|
||||
|
||||
[cache _resequencePosition];
|
||||
|
||||
// After resequence
|
||||
[self assert:1 equals:[[cache._items objectForKey:@"key1"] position]];
|
||||
[self assert:2 equals:[[cache._items objectForKey:@"key3"] position]];
|
||||
[self assert:3 equals:[[cache._items objectForKey:@"key4"] position]];
|
||||
}
|
||||
|
||||
- (void)testIsTotalCostLimitExceeded
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setObject:@"Object1" forKey:@"key1" cost:50];
|
||||
|
||||
// Not exceed (because of not limit)
|
||||
[self assert:NO equals:[cache _isTotalCostLimitExceeded]];
|
||||
|
||||
// Not exceed (with limit)
|
||||
[cache setTotalCostLimit:100];
|
||||
[self assert:NO equals:[cache _isTotalCostLimitExceeded]];
|
||||
|
||||
// Exceed (by hacking the cost of object)
|
||||
[[cache._items objectForKey:@"key1"] setCost: 150];
|
||||
cache._totalCostCache = -1;
|
||||
[self assert:YES equals:[cache _isTotalCostLimitExceeded]];
|
||||
}
|
||||
|
||||
- (void)testIsCountLimitExceeded
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setObject:@"Object1" forKey:@"key1"];
|
||||
|
||||
// Not exceed (because of not limit)
|
||||
[self assert:NO equals:[cache _isCountLimitExceeded]];
|
||||
|
||||
// Not exceed (with limit)
|
||||
[cache setCountLimit:1];
|
||||
[self assert:NO equals:[cache _isCountLimitExceeded]];
|
||||
|
||||
// Exceed (by hacking the items)
|
||||
[cache._items setObject:[_CPCacheItem cacheItemWithObject:@"Object2" cost:0 position:3] forKey:@"key2"];
|
||||
[self assert:YES equals:[cache _isCountLimitExceeded]];
|
||||
}
|
||||
|
||||
- (void)testCleanCacheWithoutDiscarding
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setTotalCostLimit: 100];
|
||||
[cache setCountLimit: 2];
|
||||
[cache setDelegate:self];
|
||||
[cache setObject:@"Object1" forKey:@"key1" cost:50];
|
||||
[cache setObject:@"Object2" forKey:@"key2" cost:40];
|
||||
|
||||
// Set total cost limit
|
||||
[cache _cleanCache];
|
||||
[self assert:@"Object1" equals:[cache objectForKey:@"key1"]];
|
||||
[self assert:@"Object2" equals:[cache objectForKey:@"key2"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:0 equals:_countDelegateExecuted];
|
||||
}
|
||||
|
||||
- (void)testCleanCacheWithCostDiscarding
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setTotalCostLimit: 100];
|
||||
[cache setDelegate:self];
|
||||
[cache setObject:@"Object1" forKey:@"key1" cost:50];
|
||||
[cache setObject:@"Object2" forKey:@"key2" cost:40];
|
||||
|
||||
// Hack cost of object
|
||||
[[cache._items objectForKey:@"key2"] setCost:80];
|
||||
cache._totalCostCache = -1;
|
||||
|
||||
// Set total cost limit
|
||||
[cache _cleanCache];
|
||||
[self assert:nil equals:[cache objectForKey:@"key1"]];
|
||||
[self assert:@"Object2" equals:[cache objectForKey:@"key2"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:1 equals:_countDelegateExecuted];
|
||||
[self assert:cache equals:[_caches objectAtIndex:0]];
|
||||
[self assert:@"Object1" equals:[_objects objectAtIndex:0]];
|
||||
}
|
||||
|
||||
- (void)testCleanCacheWithCountDiscarding
|
||||
{
|
||||
// Setup cache
|
||||
var cache = [[CPCache alloc] init];
|
||||
[cache setCountLimit:2];
|
||||
[cache setDelegate:self];
|
||||
[cache setObject:@"Object1" forKey:@"key1"];
|
||||
[cache setObject:@"Object2" forKey:@"key2"];
|
||||
|
||||
// Hack items
|
||||
[cache._items setObject:[_CPCacheItem cacheItemWithObject:@"Object3" cost:0 position:3] forKey:@"key3"];
|
||||
|
||||
// Set total cost limit
|
||||
[cache _cleanCache];
|
||||
[self assert:nil equals:[cache objectForKey:@"key1"]];
|
||||
[self assert:@"Object2" equals:[cache objectForKey:@"key2"]];
|
||||
[self assert:@"Object3" equals:[cache objectForKey:@"key3"]];
|
||||
|
||||
// Check delegate
|
||||
[self assert:1 equals:_countDelegateExecuted];
|
||||
[self assert:cache equals:[_caches objectAtIndex:0]];
|
||||
[self assert:@"Object1" equals:[_objects objectAtIndex:0]];
|
||||
}
|
||||
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* AppController.j
|
||||
* CPPlatformWindow
|
||||
*
|
||||
* Created by You on October 30, 2014.
|
||||
* Copyright 2014, Your Company All rights reserved.
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
|
||||
@implementation AppController : CPObject
|
||||
{
|
||||
@outlet CPWindow theWindow;
|
||||
@outlet CPWindow externalWindow;
|
||||
|
||||
CPPlatformWindow platformWindow;
|
||||
|
||||
}
|
||||
|
||||
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
|
||||
{
|
||||
// This is called when the application is done loading.
|
||||
[[CPPlatformWindow alloc] initWithWindow:externalWindow];
|
||||
[externalWindow setDelegate:self];
|
||||
}
|
||||
|
||||
- (void)awakeFromCib
|
||||
{
|
||||
// This is called when the cib is done loading.
|
||||
// You can implement this method on any object instantiated from a Cib.
|
||||
// It's a useful hook for setting up current UI values, and other things.
|
||||
|
||||
// In this case, we want the window from Cib to become our full browser window
|
||||
[theWindow setFullPlatformWindow:YES];
|
||||
}
|
||||
|
||||
- (IBAction)showWindow:(id)sender
|
||||
{
|
||||
[externalWindow orderFront:sender];
|
||||
}
|
||||
|
||||
- (IBAction)closeWindow:(id)sender
|
||||
{
|
||||
[externalWindow orderOut:sender];
|
||||
}
|
||||
|
||||
- (IBAction)changeFrame:(id)sender
|
||||
{
|
||||
[externalWindow setFrame:CGRectMake(250, 250, 200, 200)];
|
||||
}
|
||||
|
||||
- (IBAction)showAlert:(id)sender
|
||||
{
|
||||
var alert = [CPAlert alertWithMessageText:@"rer" defaultButton:"OK" alternateButton:nil otherButton:nil informativeTextWithFormat:"Multiple login failures may result in blocked access to the system."];
|
||||
[alert beginSheetModalForWindow:externalWindow];
|
||||
}
|
||||
|
||||
|
||||
#pragma mark -
|
||||
#pragma mark Delegate
|
||||
|
||||
- (void)windowDidBecomeKey:(CPNotification)aNotification
|
||||
{
|
||||
CPLog.debug("windowDidBecomeKey:");
|
||||
}
|
||||
|
||||
- (void)windowDidBecomeMain:(CPNotification)aNotification
|
||||
{
|
||||
CPLog.debug("windowDidBecomeMain:");
|
||||
}
|
||||
|
||||
- (void)windowDidEndSheet:(CPNotification)aNotification
|
||||
{
|
||||
CPLog.debug("windowDidEndSheet:");
|
||||
}
|
||||
|
||||
- (void)windowDidMove:(CPNotification)aNotification
|
||||
{
|
||||
CPLog.debug("windowDidMove:");
|
||||
}
|
||||
|
||||
- (void)windowDidResignKey:(CPNotification)aNotification
|
||||
{
|
||||
CPLog.debug("windowDidResignKey:");
|
||||
}
|
||||
|
||||
- (void)windowDidResignMain:(CPNotification)aNotification
|
||||
{
|
||||
CPLog.debug("windowDidResignMain:");
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(CPNotification)aNotification
|
||||
{
|
||||
CPLog.debug("windowDidResize:");
|
||||
}
|
||||
|
||||
- (void)windowWillClose:(CPWindow)aWindow
|
||||
{
|
||||
CPLog.debug("windowWillClose:");
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>Main cib file base name</key>
|
||||
<string>MainMenu.cib</string>
|
||||
<key>CPBundleName</key>
|
||||
<string>CPPlatformWindow</string>
|
||||
<key>CPBundleVersion</key>
|
||||
<string>1.0</string>
|
||||
<key>CPHumanReadableCopyright</key>
|
||||
<string>Copyright © 2014, Your Company All rights reserved.</string>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,184 @@
|
||||
/*
|
||||
* Jakefile
|
||||
* CPPlatformWindow
|
||||
*
|
||||
* Created by You on October 30, 2014.
|
||||
* Copyright 2014, Your Company All rights reserved.
|
||||
*/
|
||||
|
||||
var ENV = require("system").env,
|
||||
FILE = require("file"),
|
||||
JAKE = require("jake"),
|
||||
task = JAKE.task,
|
||||
FileList = JAKE.FileList,
|
||||
app = require("cappuccino/jake").app,
|
||||
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
|
||||
OS = require("os"),
|
||||
projectName = "CPPlatformWindow";
|
||||
|
||||
app (projectName, function(task)
|
||||
{
|
||||
ENV["OBJJ_INCLUDE_PATHS"] = "Frameworks";
|
||||
|
||||
if (configuration === "Debug")
|
||||
ENV["OBJJ_INCLUDE_PATHS"] = FILE.join(ENV["OBJJ_INCLUDE_PATHS"], configuration);
|
||||
|
||||
task.setBuildIntermediatesPath(FILE.join("Build", "CPPlatformWindow.build", configuration));
|
||||
task.setBuildPath(FILE.join("Build", configuration));
|
||||
|
||||
task.setProductName("CPPlatformWindow");
|
||||
task.setIdentifier("com.yourcompany.CPPlatformWindow");
|
||||
task.setVersion("1.0");
|
||||
task.setAuthor("Your Company");
|
||||
task.setEmail("feedback @nospam@ yourcompany.com");
|
||||
task.setSummary("CPPlatformWindow");
|
||||
task.setSources(new FileList("**/*.j").exclude(FILE.join("Build", "**")).exclude(FILE.join("Frameworks", "Source", "**")));
|
||||
task.setResources(new FileList("Resources/**"));
|
||||
task.setIndexFilePath("index.html");
|
||||
task.setInfoPlistPath("Info.plist");
|
||||
|
||||
if (configuration === "Debug")
|
||||
task.setCompilerFlags("-DDEBUG -g");
|
||||
else
|
||||
task.setCompilerFlags("-O");
|
||||
});
|
||||
|
||||
task ("default", [projectName], function()
|
||||
{
|
||||
printResults(configuration);
|
||||
});
|
||||
|
||||
task ("build", ["default"], function()
|
||||
{
|
||||
updateApplicationSize();
|
||||
});
|
||||
|
||||
task ("debug", function()
|
||||
{
|
||||
ENV["CONFIGURATION"] = "Debug";
|
||||
JAKE.subjake(["."], "build", ENV);
|
||||
});
|
||||
|
||||
task ("release", function()
|
||||
{
|
||||
ENV["CONFIGURATION"] = "Release";
|
||||
JAKE.subjake(["."], "build", ENV);
|
||||
});
|
||||
|
||||
task ("run", ["debug"], function()
|
||||
{
|
||||
OS.system(["open", FILE.join("Build", "Debug", projectName, "index.html")]);
|
||||
});
|
||||
|
||||
task ("run-release", ["release"], function()
|
||||
{
|
||||
OS.system(["open", FILE.join("Build", "Release", projectName, "index.html")]);
|
||||
});
|
||||
|
||||
task ("deploy", ["release"], function()
|
||||
{
|
||||
FILE.mkdirs(FILE.join("Build", "Deployment", projectName));
|
||||
OS.system(["press", "-f", FILE.join("Build", "Release", projectName), FILE.join("Build", "Deployment", projectName)]);
|
||||
printResults("Deployment")
|
||||
});
|
||||
|
||||
task ("desktop", ["release"], function()
|
||||
{
|
||||
FILE.mkdirs(FILE.join("Build", "Desktop", projectName));
|
||||
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", projectName), FILE.join("Build", "Desktop", projectName, "CPPlatformWindow.app"));
|
||||
printResults("Desktop")
|
||||
});
|
||||
|
||||
task ("run-desktop", ["desktop"], function()
|
||||
{
|
||||
OS.system([FILE.join("Build", "Desktop", projectName, "CPPlatformWindow.app", "Contents", "MacOS", "NativeHost"), "-i"]);
|
||||
});
|
||||
|
||||
function printResults(configuration)
|
||||
{
|
||||
print("----------------------------");
|
||||
print(configuration+" app built at path: "+FILE.join("Build", configuration, projectName));
|
||||
print("----------------------------");
|
||||
}
|
||||
|
||||
function updateApplicationSize()
|
||||
{
|
||||
print("Calculating application file sizes...");
|
||||
|
||||
var contents = FILE.read(FILE.join("Build", ENV["CONFIGURATION"], projectName, "Info.plist"), { charset:"UTF-8" }),
|
||||
format = CFPropertyList.sniffedFormatOfString(contents),
|
||||
plist = CFPropertyList.propertyListFromString(contents),
|
||||
totalBytes = {executable:0, data:0, mhtml:0};
|
||||
|
||||
// Get the size of all framework executables and sprite data
|
||||
var frameworksDir = "Frameworks";
|
||||
|
||||
if (ENV["CONFIGURATION"] === "Debug")
|
||||
frameworksDir = FILE.join(frameworksDir, "Debug");
|
||||
|
||||
var frameworks = FILE.list(frameworksDir);
|
||||
|
||||
frameworks.forEach(function(framework)
|
||||
{
|
||||
if (framework !== "Source")
|
||||
addBundleFileSizes(FILE.join(frameworksDir, framework), totalBytes);
|
||||
});
|
||||
|
||||
// Read in the default theme name, and attempt to get its size
|
||||
var themeName = plist.valueForKey("CPDefaultTheme") || "Aristo2",
|
||||
themePath = nil;
|
||||
|
||||
if (themeName === "Aristo" || themeName === "Aristo2")
|
||||
themePath = FILE.join(frameworksDir, "AppKit", "Resources", themeName + ".blend");
|
||||
else
|
||||
themePath = FILE.join("Frameworks", "Resources", themeName + ".blend");
|
||||
|
||||
if (FILE.isDirectory(themePath))
|
||||
addBundleFileSizes(themePath, totalBytes);
|
||||
|
||||
// Add sizes for the app
|
||||
addBundleFileSizes(FILE.join("Build", ENV["CONFIGURATION"], projectName), totalBytes);
|
||||
|
||||
print("Executables: " + totalBytes.executable + ", sprite data: " + totalBytes.data + ", total: " + (totalBytes.executable + totalBytes.data));
|
||||
|
||||
var dict = new CFMutableDictionary();
|
||||
|
||||
dict.setValueForKey("executable", totalBytes.executable);
|
||||
dict.setValueForKey("data", totalBytes.data);
|
||||
dict.setValueForKey("mhtml", totalBytes.mhtml);
|
||||
|
||||
plist.setValueForKey("CPApplicationSize", dict);
|
||||
|
||||
FILE.write(FILE.join("Build", ENV["CONFIGURATION"], projectName, "Info.plist"), CFPropertyList.stringFromPropertyList(plist, format), { charset:"UTF-8" });
|
||||
}
|
||||
|
||||
function addBundleFileSizes(bundlePath, totalBytes)
|
||||
{
|
||||
var bundleName = FILE.basename(bundlePath),
|
||||
environment = bundleName === "Foundation" ? "Objj" : "Browser",
|
||||
bundlePath = FILE.join(bundlePath, environment + ".environment");
|
||||
|
||||
if (FILE.isDirectory(bundlePath))
|
||||
{
|
||||
var filename = bundleName + ".sj",
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, filename));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.executable += filePath.size();
|
||||
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, "dataURLs.txt"));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.data += filePath.size();
|
||||
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLData.txt"));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.mhtml += filePath.size();
|
||||
|
||||
filePath = new FILE.Path(FILE.join(bundlePath, "MHTMLPaths.txt"));
|
||||
|
||||
if (filePath.exists())
|
||||
totalBytes.mhtml += filePath.size();
|
||||
}
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,191 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--
|
||||
index-debug.html
|
||||
CPPlatformWindow
|
||||
|
||||
Created by You on October 30, 2014.
|
||||
Copyright 2014, Your Company All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!--[if lte IE 8]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1">
|
||||
<![endif]-->
|
||||
<!--[if gte IE 9]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
|
||||
<![endif]-->
|
||||
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
<link rel="apple-touch-icon" href="Resources/icon.png">
|
||||
<link rel="apple-touch-startup-image" href="Resources/default.png">
|
||||
|
||||
<title>CPPlatformWindow</title>
|
||||
|
||||
<!-- Custom javascript goes here -->
|
||||
<!-- End custom javascript -->
|
||||
|
||||
<script type="text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks"];
|
||||
|
||||
var progressBar = null;
|
||||
|
||||
OBJJ_PROGRESS_CALLBACK = function(percent, appSize, path)
|
||||
{
|
||||
percent = percent * 100;
|
||||
|
||||
if (!progressBar)
|
||||
progressBar = document.getElementById("progress-bar");
|
||||
|
||||
if (progressBar)
|
||||
progressBar.style.width = Math.min(percent, 100) + "%";
|
||||
}
|
||||
|
||||
var loadingHTML =
|
||||
'<div id="loading">' +
|
||||
' <div id="loading-text">Loading...</div>' +
|
||||
' <div id="progress-indicator">' +
|
||||
' <span id="progress-bar" style="width:0%"></span>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="Frameworks/Debug/Objective-J/Objective-J.js" charset="UTF-8"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
objj_msgSend_reset();
|
||||
|
||||
// DEBUG OPTIONS:
|
||||
|
||||
// Uncomment to enable printing of backtraces on exceptions:
|
||||
//objj_msgSend_decorate(objj_backtrace_decorator);
|
||||
|
||||
// Uncomment to supress exceptions that take place inside a message
|
||||
//objj_msgSend_decorate(objj_supress_exceptions_decorator)
|
||||
|
||||
// Uncomment to enable runtime type checking:
|
||||
//objj_msgSend_decorate(objj_typecheck_decorator);
|
||||
|
||||
// Uncomment (along with both above) to print backtraces on type check errors:
|
||||
//objj_typecheck_prints_backtrace = true;
|
||||
|
||||
// Uncomment to disable the default logger (CPLogConsole if window.console exists, CPLogPopup otherwise):
|
||||
//CPLogUnregister(CPLogDefault);
|
||||
|
||||
// Uncomment to enable a specific logger:
|
||||
//CPLogRegister(CPLogConsole);
|
||||
//CPLogRegister(CPLogPopup);
|
||||
|
||||
// Tag view DOM elements with a "data-cappuccino-view" attribute that contains
|
||||
// the class name of the view that created them. Comment this or set to false to disable.
|
||||
appkit_tag_dom_elements = true;
|
||||
</script>
|
||||
|
||||
<style type="text/css">
|
||||
html, body, h1, p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* We need a body wrapper because Cappuccino is unhappy if we change the body element */
|
||||
#cappuccino-body {
|
||||
/* Position it absolutely so it will fill the height without content */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
/* Put it at the bottom of the stack so it doesn't interfere with UI */
|
||||
z-index: -1000;
|
||||
}
|
||||
|
||||
#cappuccino-body .container {
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#cappuccino-body .content {
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#loading {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
}
|
||||
|
||||
#loading-text {
|
||||
height: 1.5em;
|
||||
color: #555;
|
||||
font: normal bold 36px/36px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#progress-indicator {
|
||||
padding: 0px;
|
||||
height: 16px;
|
||||
border: 5px solid #555;
|
||||
border-radius: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#progress-bar {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
display: block;
|
||||
height: 18px;
|
||||
|
||||
/* Compensate for moving the bar left 1px to overlap the indicator border */
|
||||
border-right: 1px solid #555;
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
#noscript {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
padding: 1em 1.5em;
|
||||
border: 5px solid #555;
|
||||
border-radius: 16px;
|
||||
background-color: white;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
font: bold 24px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#noscript a {
|
||||
color: #98c0ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="cappuccino-body">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<script type="text/javascript">
|
||||
document.write(loadingHTML);
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<noscript style="position:absolute; top:0; left:0; width:100%; height:100%">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div id="noscript">
|
||||
<p style="font-size:120%; margin-bottom:.75em">JavaScript is required for this site.</p>
|
||||
<p><a href="http://www.enable-javascript.com" target="_blank">Enable JavaScript</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,161 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<!--
|
||||
index.html
|
||||
CPPlatformWindow
|
||||
|
||||
Created by You on October 30, 2014.
|
||||
Copyright 2014, Your Company All rights reserved.
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
|
||||
<!--[if lte IE 8]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1">
|
||||
<![endif]-->
|
||||
<!--[if gte IE 9]>
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge, chrome=1">
|
||||
<![endif]-->
|
||||
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
|
||||
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||
|
||||
<link rel="apple-touch-icon" href="Resources/icon.png">
|
||||
<link rel="apple-touch-startup-image" href="Resources/default.png">
|
||||
|
||||
<title>CPPlatformWindow</title>
|
||||
|
||||
<!-- Custom javascript goes here -->
|
||||
<!-- End custom javascript -->
|
||||
|
||||
<script type="text/javascript">
|
||||
OBJJ_MAIN_FILE = "main.j";
|
||||
|
||||
var progressBar = null;
|
||||
|
||||
OBJJ_PROGRESS_CALLBACK = function(percent, appSize, path)
|
||||
{
|
||||
percent = percent * 100;
|
||||
|
||||
if (!progressBar)
|
||||
progressBar = document.getElementById("progress-bar");
|
||||
|
||||
if (progressBar)
|
||||
progressBar.style.width = Math.min(percent, 100) + "%";
|
||||
}
|
||||
|
||||
var loadingHTML =
|
||||
'<div id="loading">' +
|
||||
' <div id="loading-text">Loading...</div>' +
|
||||
' <div id="progress-indicator">' +
|
||||
' <span id="progress-bar" style="width:0%"></span>' +
|
||||
' </div>' +
|
||||
'</div>';
|
||||
</script>
|
||||
|
||||
<script type="text/javascript" src="Frameworks/Objective-J/Objective-J.js" charset="UTF-8"></script>
|
||||
|
||||
<style type="text/css">
|
||||
html, body, h1, p {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
/* We need a body wrapper because Cappuccino is unhappy if we change the body element */
|
||||
#cappuccino-body {
|
||||
/* Position it absolutely so it will fill the height without content */
|
||||
position: absolute;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
|
||||
/* Put it at the bottom of the stack so it doesn't interfere with UI */
|
||||
z-index: -1000;
|
||||
}
|
||||
|
||||
#cappuccino-body .container {
|
||||
display: table;
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
#cappuccino-body .content {
|
||||
display: table-cell;
|
||||
height: 100%;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
#loading {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
}
|
||||
|
||||
#loading-text {
|
||||
height: 1.5em;
|
||||
color: #555;
|
||||
font: normal bold 36px/36px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#progress-indicator {
|
||||
padding: 0px;
|
||||
height: 16px;
|
||||
border: 5px solid #555;
|
||||
border-radius: 18px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
#progress-bar {
|
||||
position: relative;
|
||||
top: -1px;
|
||||
left: -1px;
|
||||
display: block;
|
||||
height: 18px;
|
||||
|
||||
/* Compensate for moving the bar left 1px to overlap the indicator border */
|
||||
border-right: 1px solid #555;
|
||||
background-color: #555;
|
||||
}
|
||||
|
||||
#noscript {
|
||||
position: relative;
|
||||
top: 35%;
|
||||
padding: 1em 1.5em;
|
||||
border: 5px solid #555;
|
||||
border-radius: 16px;
|
||||
background-color: white;
|
||||
color: #555;
|
||||
text-align: center;
|
||||
font: bold 24px Arial, sans-serif;
|
||||
}
|
||||
|
||||
#noscript a {
|
||||
color: #98c0ff;
|
||||
text-decoration: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="cappuccino-body">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<script type="text/javascript">
|
||||
document.write(loadingHTML);
|
||||
</script>
|
||||
</div>
|
||||
</div>
|
||||
<noscript style="position:absolute; top:0; left:0; width:100%; height:100%">
|
||||
<div class="container">
|
||||
<div class="content">
|
||||
<div id="noscript">
|
||||
<p style="font-size:120%; margin-bottom:.75em">JavaScript is required for this site.</p>
|
||||
<p><a href="http://www.enable-javascript.com" target="_blank">Enable JavaScript</a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* AppController.j
|
||||
* CPPlatformWindow
|
||||
*
|
||||
* Created by You on October 30, 2014.
|
||||
* Copyright 2014, Your Company All rights reserved.
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
@import <AppKit/AppKit.j>
|
||||
|
||||
@import "AppController.j"
|
||||
|
||||
|
||||
function main(args, namedArgs)
|
||||
{
|
||||
CPApplicationMain(args, namedArgs);
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
// Expected output: 256
|
||||
|
||||
/*
|
||||
|
||||
START_EXPECTED
|
||||
Error on line 10778 of file [unknown]
|
||||
SyntaxError:
|
||||
int myIvar;
|
||||
^
|
||||
ERROR line 12 in file:[__PATH__]: Instance variable 'myIvar' is already declared for class MySubclass in superclass MySuperclass
|
||||
END_EXPECTED
|
||||
*/
|
||||
|
||||
@import <Foundation/Foundation.j>
|
||||
|
||||
@implementation MySuperclass : CPObject
|
||||
{
|
||||
int myIvar;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
@implementation MySubclass : MySuperclass
|
||||
{
|
||||
int myIvar;
|
||||
}
|
||||
|
||||
@end
|
||||
@@ -7,7 +7,7 @@ Error on line 10001 of file [unknown]
|
||||
SyntaxError:
|
||||
@implementation MyType : CPObject
|
||||
^
|
||||
ERROR line 5 in file:[__PATH__]: MyType is already declared as type
|
||||
ERROR line 5 in file:[__PATH__]: MyType is already declared as a type
|
||||
END_EXPECTED
|
||||
|
||||
*/
|
||||
|
||||
@@ -55,6 +55,7 @@ def cleanup_output(output):
|
||||
output = output.replace(" ", "")
|
||||
output = output.replace("\n", "")
|
||||
output = output.replace("[0m", "")
|
||||
output = re.sub('\d*offile\[unknown\]', '', output)
|
||||
return re.sub('[^\s!-~]', '', output)
|
||||
|
||||
|
||||
|
||||
@@ -20,11 +20,16 @@
|
||||
{
|
||||
var button = [[CPButton alloc] initWithFrame:CGRectMake(0.0, 0.0, 100.0, 20.0)];
|
||||
|
||||
[button setValue:[CPColor blueColor] forThemeAttribute:@"bezel-color"];
|
||||
[button setValue:[CPColor greenColor] forThemeAttribute:@"bezel-color" inState:CPThemeStateHighlighted];
|
||||
var themedButtonValues =
|
||||
[
|
||||
[@"text-color", [CPColor redColor]],
|
||||
[@"text-color", [CPColor yellowColor], CPThemeStateHighlighted],
|
||||
|
||||
[button setValue:[CPColor redColor] forThemeAttribute:@"text-color"];
|
||||
[button setValue:[CPColor yellowColor] forThemeAttribute:@"text-color" inState:CPThemeStateHighlighted];
|
||||
[@"bezel-color", [CPColor blueColor]],
|
||||
[@"bezel-color", [CPColor greenColor], CPThemeStateHighlighted],
|
||||
];
|
||||
|
||||
[self registerThemeValues:themedButtonValues forView:button];
|
||||
|
||||
[button setTitle:@"Yikes!"];
|
||||
|
||||
|
||||
@@ -409,7 +409,6 @@ var NSButtonIsBorderedMask = 0x00800000,
|
||||
|
||||
@implementation NSButtonCell : NSActionCell
|
||||
{
|
||||
BOOL _isBordered @accessors(readonly, getter=isBordered);
|
||||
int _bezelStyle @accessors(readonly, getter=bezelStyle);
|
||||
|
||||
CPString _title @accessors(readonly, getter=title);
|
||||
|
||||
@@ -177,7 +177,6 @@ var NSDatePickerDefaultSize = 22,
|
||||
BOOL _drawsBackground @accessors(getter=drawsBackground);
|
||||
CPDate _minDate @accessors(getter=minDate);
|
||||
CPDate _maxDate @accessors(getter=maxDate);
|
||||
CPDateFormatter _formatter @accessors(getter=formatter);
|
||||
CPInteger _datePickerMode @accessors(getter=datePickerMode);
|
||||
CPInteger _datePickerElements @accessors(getter=datePickerElements);
|
||||
CPInteger _datePickerType @accessors(getter=datePickerType);
|
||||
|
||||
Reference in New Issue
Block a user