Merge pull request #2278 from cacaodev/CPAnimationContext

Animations: CPAnimationContext & animator
This commit is contained in:
Antoine Mercadal
2016-04-04 09:57:55 -07:00
22 changed files with 5676 additions and 133 deletions
+2
View File
@@ -27,6 +27,7 @@
@import "CPAccordionView.j"
@import "CPAlert.j"
@import "CPAnimation.j"
@import "CPAnimationContext.j"
@import "CPAppearance.j"
@import "CPApplication.j"
@import "CPArrayController.j"
@@ -107,6 +108,7 @@
@import "CPTreeNode.j"
@import "CPUserDefaultsController.j"
@import "CPView.j"
@import "CPViewAnimator.j"
@import "CPViewAnimation.j"
@import "CPViewController.j"
@import "CPVisualEffectView.j"
+12
View File
@@ -369,6 +369,18 @@ function CPBrowserStyleProperty(aProperty)
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['transform']] || nil;
break;
case 'animationend':
var candidates = {
'WebkitAnimation' : 'webkitAnimationEnd',
'MozAnimation' : 'animationend',
'OAnimation' : 'oAnimationEnd',
'msAnimation' : 'MSAnimationEnd',
'animation' : 'animationend'
};
r = candidates[PLATFORM_STYLE_JS_PROPERTIES['animation']] || nil;
break;
default:
var prefixes = ["Webkit", "Moz", "O", "ms"],
strippedProperty = aProperty.split('-').join(' '),
+6
View File
@@ -243,6 +243,9 @@ var CPViewHighDPIDrawingEnabled = YES;
CPMutableArray _trackingAreas @accessors(getter=trackingAreas, copy);
BOOL _inhibitUpdateTrackingAreas;
id _animator;
CPDictionary _animationsDictionary;
}
/*
@@ -389,6 +392,9 @@ var CPViewHighDPIDrawingEnabled = YES;
_DOMImageSizes = [];
#endif
_animator = nil;
_animationsDictionary = @{};
[self _setupViewFlags];
[self _loadThemeAttributes];
}
+10 -133
View File
@@ -30,8 +30,10 @@
*/
@implementation CAAnimation : CPObject
{
BOOL _isRemovedOnCompletion;
id _delegate;
BOOL _isRemovedOnCompletion;
id _delegate;
CAMediaTimingFunction _timingFunction @accessors(property=timingFunction);
double _duration @accessors(property=duration);
}
/*!
@@ -47,8 +49,10 @@
{
self = [super init];
if (self)
_isRemovedOnCompletion = YES;
_isRemovedOnCompletion = YES;
_timingFunction = nil;
_duration = 0.0;
_delegate = nil;
return self;
}
@@ -102,7 +106,7 @@
- (CAMediaTimingFunction)timingFunction
{
// Linear Pacing
return nil;
return _timingFunction;
}
/*!
@@ -127,131 +131,4 @@
[anObject addAnimation:self forKey:aKey];
}
@end
/*
*/
@implementation CAPropertyAnimation : CAAnimation
{
CPString _keyPath;
BOOL _isCumulative;
BOOL _isAdditive;
}
+ (id)animationWithKeyPath:(CPString)aKeyPath
{
var animation = [self animation];
[animation setKeyPath:aKeyPath];
return animation;
}
- (void)setKeyPath:(CPString)aKeyPath
{
_keyPath = aKeyPath;
}
- (CPString)keyPath
{
return _keyPath;
}
- (void)setCumulative:(BOOL)isCumulative
{
_isCumulative = isCumulative;
}
- (BOOL)cumulative
{
return _isCumulative;
}
- (BOOL)isCumulative
{
return _isCumulative;
}
- (void)setAdditive:(BOOL)isAdditive
{
_isAdditive = isAdditive;
}
- (BOOL)additive
{
return _isAdditive;
}
- (BOOL)isAdditive
{
return _isAdditive;
}
@end
/*!
A CABasicAnimation is a simple animation that moves a
CALayer from one point to another over a specified
period of time.
*/
@implementation CABasicAnimation : CAPropertyAnimation
{
id _fromValue;
id _toValue;
id _byValue;
}
/*!
Sets the starting position for the animation.
@param aValue the animation starting position
*/
- (void)setFromValue:(id)aValue
{
_fromValue = aValue;
}
/*!
Returns the animation's starting position.
*/
- (id)fromValue
{
return _fromValue;
}
/*!
Sets the ending position for the animation.
@param aValue the animation ending position
*/
- (void)setToValue:(id)aValue
{
_toValue = aValue;
}
/*!
Returns the animation's ending position.
*/
- (id)toValue
{
return _toValue;
}
/*!
Sets the optional byValue for animation interpolation.
@param aValue the byValue
*/
- (void)setByValue:(id)aValue
{
_byValue = aValue;
}
/*!
Returns the animation's byValue.
*/
- (id)byValue
{
return _byValue;
}
@end
@end
+84
View File
@@ -0,0 +1,84 @@
@import <Foundation/CPObject.j>
@import "CAPropertyAnimation.j"
/*!
A CABasicAnimation is a simple animation that moves a
CALayer from one point to another over a specified
period of time.
*/
/*!
A CABasicAnimation is a simple animation that moves a
CALayer from one point to another over a specified
period of time.
*/
@implementation CABasicAnimation : CAPropertyAnimation
{
id _fromValue;
id _toValue;
id _byValue;
}
- (id)init
{
self = [super init];
_fromValue = nil;
_toValue = nil;
_byValue = nil;
return self;
}
/*!
Sets the starting position for the animation.
@param aValue the animation starting position
*/
- (void)setFromValue:(id)aValue
{
_fromValue = aValue;
}
/*!
Returns the animation's starting position.
*/
- (id)fromValue
{
return _fromValue;
}
/*!
Sets the ending position for the animation.
@param aValue the animation ending position
*/
- (void)setToValue:(id)aValue
{
_toValue = aValue;
}
/*!
Returns the animation's ending position.
*/
- (id)toValue
{
return _toValue;
}
/*!
Sets the optional byValue for animation interpolation.
@param aValue the byValue
*/
- (void)setByValue:(id)aValue
{
_byValue = aValue;
}
/*!
Returns the animation's byValue.
*/
- (id)byValue
{
return _byValue;
}
@end
@@ -0,0 +1,23 @@
@import <Foundation/CPObject.j>
@import "CAPropertyAnimation.j"
@implementation CAKeyframeAnimation : CAPropertyAnimation
{
CPArray _values @accessors(property=values);
CPArray _keyTimes @accessors(property=keyTimes);
CPArray _timingFunctions @accessors(property=timingFunctions);
}
- (id)init
{
self = [super init];
_values = [CPArray array];
_keyTimes = [CPArray array];
_timingFunctions = [CPArray array];
return self;
}
@end
@@ -0,0 +1,74 @@
@import <Foundation/CPObject.j>
@import "CAAnimation.j"
@implementation CAPropertyAnimation : CAAnimation
{
CPString _keyPath;
BOOL _isCumulative;
BOOL _isAdditive;
}
- (id)init
{
self = [super init];
_keyPath = nil;
_isCumulative = NO;
_isAdditive = NO;
return self;
}
+ (id)animationWithKeyPath:(CPString)aKeyPath
{
var animation = [self animation];
[animation setKeyPath:aKeyPath];
return animation;
}
- (void)setKeyPath:(CPString)aKeyPath
{
_keyPath = aKeyPath;
}
- (CPString)keyPath
{
return _keyPath;
}
- (void)setCumulative:(BOOL)isCumulative
{
_isCumulative = isCumulative;
}
- (BOOL)cumulative
{
return _isCumulative;
}
- (BOOL)isCumulative
{
return _isCumulative;
}
- (void)setAdditive:(BOOL)isAdditive
{
_isAdditive = isAdditive;
}
- (BOOL)additive
{
return _isAdditive;
}
- (BOOL)isAdditive
{
return _isAdditive;
}
@end
+707
View File
@@ -0,0 +1,707 @@
@import "CABasicAnimation.j"
@import "CAKeyframeAnimation.j"
@import "CPView.j"
@import <Foundation/CPTimer.j>
@import "jshashtable.j"
@import "CSSAnimation.j"
@typedef HashTable;
var _CPAnimationContextStack = nil,
_animationFlushingObserver = nil,
_animationFrameUpdaters = {};
@implementation CPAnimationContext : CPObject
{
double _duration @accessors(property=duration);
CAMediaTimingFunction _timingFunction @accessors(property=timingFunction);
Function _completionHandlerAgent;
HashTable _animationsByObject;
}
+ (id)currentContext
{
var contextStack = [self contextStack],
context = [contextStack lastObject];
if (!context)
{
context = [[CPAnimationContext alloc] init];
[contextStack addObject:context];
[self _scheduleAnimationContextStackFlush];
}
return context;
}
+ (CPArray)contextStack
{
if (!_CPAnimationContextStack)
_CPAnimationContextStack = [CPArray array];
return _CPAnimationContextStack;
}
+ (void)runAnimationGroup:(Function/*(CPAnimationContext context)*/)animationsBlock completionHandler:(Function)aCompletionHandler
{
[CPAnimationContext beginGrouping];
var context = [CPAnimationContext currentContext];
[context setCompletionHandler:aCompletionHandler];
animationsBlock(context);
[CPAnimationContext endGrouping];
}
- (id)init
{
self = [super init];
_duration = 0.0;
_timingFunction = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear];
_completionHandlerAgent = nil;
_animationsByObject = new Hashtable();
return self;
}
- (id)copy
{
var context = [[CPAnimationContext alloc] init];
[context setDuration:[self duration]];
[context setTimingFunction:[self timingFunction]];
[context setCompletionHandler:[self completionHandler]];
return context;
}
+ (void)_scheduleAnimationContextStackFlush
{
if (!_animationFlushingObserver)
{
CPLog.debug("create new observer");
_animationFlushingObserver = CFRunLoopObserverCreate(2, true, 0, _animationFlushingObserverCallback,0);
CFRunLoopAddObserver([CPRunLoop mainRunLoop], _animationFlushingObserver);
}
}
+ (void)beginGrouping
{
var newContext;
if ([_CPAnimationContextStack count])
{
var currentContext = [_CPAnimationContextStack lastObject];
newContext = [currentContext copy];
}
else
{
newContext = [[CPAnimationContext alloc] init];
}
[_CPAnimationContextStack addObject:newContext];
}
+ (BOOL)endGrouping
{
if (![_CPAnimationContextStack count])
return NO;
var context = [_CPAnimationContextStack lastObject];
[context _flushAnimations];
[_CPAnimationContextStack removeLastObject];
CPLog.debug(_cmd + "context stack =" + _CPAnimationContextStack);
return YES;
}
- (void)_enqueueActionForObject:(id)anObject keyPath:(id)aKeyPath targetValue:(id)aTargetValue animationCompletion:(id)animationCompletion
{
var resolvedAction = [self _actionForObject:anObject keyPath:aKeyPath targetValue:aTargetValue animationCompletion:animationCompletion];
if (!resolvedAction)
return;
var animByKeyPath = _animationsByObject.get(anObject);
if (!animByKeyPath)
{
var newAnimByKeyPath = @{aKeyPath:resolvedAction};
_animationsByObject.put(anObject, newAnimByKeyPath);
}
else
[animByKeyPath setObject:resolvedAction forKey:aKeyPath];
}
- (Object)_actionForObject:(id)anObject keyPath:(CPString)aKeyPath targetValue:(id)aTargetValue animationCompletion:(Function)animationCompletion
{
var animation,
duration,
animatedKeyPath,
values,
keyTimes,
timingFunctions,
objectId;
if (!aKeyPath || !anObject || !(animation = [anObject animationForKey:aKeyPath]) || ![animation isKindOfClass:[CAAnimation class]])
return nil;
duration = [animation duration] || [self duration];
var needsFrameTimer = (aKeyPath == @"frame" || aKeyPath == @"frameSize") &&
([anObject hasCustomLayoutSubviews] || [anObject hasCustomDrawRect]) &&
(objectId = [anObject UID]);
if (_completionHandlerAgent)
_completionHandlerAgent.increment();
var completionFunction = function()
{
if (needsFrameTimer)
[self stopFrameUpdaterWithIdentifier:objectId];
else if (animationCompletion)
animationCompletion();
if (_completionHandlerAgent)
_completionHandlerAgent.decrement();
};
if (![animation isKindOfClass:[CAPropertyAnimation class]] || !(animatedKeyPath = [animation keyPath]))
animatedKeyPath = aKeyPath;
if ([animation isKindOfClass:[CAKeyframeAnimation class]])
{
values = [animation values];
keyTimes = [animation keyTimes];
timingFunctions = [animation timingFunctionsControlPoints];
}
else
{
var isBasicAnimation = [animation isKindOfClass:[CABasicAnimation class]],
fromValue,
toValue;
if (!isBasicAnimation || (fromValue = [animation fromValue]) == nil)
fromValue = [anObject valueForKey:animatedKeyPath];
if (!isBasicAnimation || (toValue = [animation toValue]) == nil)
toValue = aTargetValue;
values = [fromValue, toValue];
keyTimes = [0, 1];
timingFunctions = isBasicAnimation ? [animation timingFunctionControlPoints] : [_timingFunction controlPoints];
}
return {
object:anObject,
keypath:animatedKeyPath,
values:values,
keytimes:keyTimes,
duration:duration,
timingfunctions:timingFunctions,
completion:completionFunction
};
}
- (void)_flushAnimations
{
if (![_CPAnimationContextStack count])
return;
if (_animationsByObject.size() == 0)
{
if (_completionHandlerAgent)
_completionHandlerAgent.fire();
}
else
[self _startAnimations];
}
- (void)_startAnimations
{
var targetViews = _animationsByObject.keys(),
cssAnimations = [],
timers = [];
[targetViews enumerateObjectsUsingBlock:function(targetView, idx, stop)
{
var animByKeyPath = _animationsByObject.get(targetView);
[animByKeyPath enumerateKeysAndObjectsUsingBlock:function(aKey, anAction, stop)
{
[self getAnimations:cssAnimations getTimers:timers forView:targetView usingAction:anAction rootView:targetView cssAnimate:YES];
}];
_animationsByObject.remove(targetView);
}];
// start timers
var k = timers.length;
while(k--)
{
CPLog.debug("START TIMER " + timers[k].identifier());
timers[k].start();
}
// start css animations
var n = cssAnimations.length;
while(n--)
{
CPLog.debug("START ANIMATION " + cssAnimations[n].animationsnames);
cssAnimations[n].start();
}
}
- (void)getAnimations:(CPArray)cssAnimations getTimers:(CPArray)timers forView:(CPView)aTargetView usingAction:(Object)anAction rootView:(CPView)rootView cssAnimate:(BOOL)needsCSSAnimation
{
var keyPath = anAction.keypath,
isFrameKeyPath = (keyPath == @"frame" || keyPath == @"frameSize"),
customLayout = [aTargetView hasCustomLayoutSubviews],
customDrawing = [aTargetView hasCustomDrawRect],
needsFrameTimer = isFrameKeyPath && (customLayout || customDrawing);
if (needsCSSAnimation)
{
var identifier = [aTargetView UID],
duration = anAction.duration,
timingFunctions = anAction.timingfunctions,
properties = [],
valueFunctions = [],
cssAnimation = nil;
[cssAnimations enumerateObjectsUsingBlock:function(anim, idx, stop)
{
if (anim.identifier == identifier)
{
cssAnimation = anim;
stop(YES);
}
}];
if (cssAnimation == nil)
{
var domElement = [aTargetView DOMElementForKeyPath:keyPath];
cssAnimation = new CSSAnimation(domElement, identifier);
cssAnimations.push(cssAnimation);
}
var css_mapping = [[aTargetView class] cssPropertiesForKeyPath:keyPath];
[css_mapping enumerateObjectsUsingBlock:function(aDict, anIndex, stop)
{
var completionFunction = (anIndex == 0) ? anAction.completion : null;
var property = [aDict objectForKey:@"property"],
getter = [aDict objectForKey:@"value"];
cssAnimation.addPropertyAnimation(property, getter, duration, anAction.keytimes, anAction.values, timingFunctions, completionFunction);
}];
if (needsFrameTimer)
cssAnimation.setRemoveAnimationPropertyOnCompletion(false);
}
if (needsFrameTimer)
{
var timer = [self addFrameUpdaterWithIdentifier:[rootView UID] forView:aTargetView keyPath:keyPath duration:anAction.duration];
if (timer)
timers.push(timer);
}
var subviews = [aTargetView subviews],
count = [subviews count];
if (count && isFrameKeyPath)
{
var frameTimerId = [rootView UID],
lastIndex = count - 1;
[subviews enumerateObjectsUsingBlock:function(aSubview, idx, stop)
{
var action = [self actionFromAction:anAction forAnimatedSubview:aSubview],
targetFrame = [action.values lastObject];
if (CGRectEqualToRect([aSubview frame], targetFrame))
return;
if ([aSubview hasCustomDrawRect])
{
action.completion = function()
{
[aSubview setFrame:targetFrame];
CPLog.debug(aSubview + " setFrame: ");
if (idx == lastIndex)
[self stopFrameUpdaterWithIdentifier:frameTimerId];
};
}
[self getAnimations:cssAnimations getTimers:timers forView:aSubview usingAction:action rootView:rootView cssAnimate:!customLayout];
}];
}
}
- (Object)actionFromAction:(Object)anAction forAnimatedSubview:(CPView)aView
{
var targetValue = [anAction.values lastObject],
endFrame,
values;
if (anAction.keypath == @"frame")
targetValue = targetValue.size;
endFrame = [aView frameWithNewSuperviewSize:targetValue];
values = [[aView frame], endFrame];
return {
object:aView,
keypath:"frame",
values:values,
keytimes:[0, 1],
duration:anAction.duration,
timingfunctions:anAction.timingFunctions
};
}
- (Function)addFrameUpdaterWithIdentifier:(CPString)anIdentifier forView:(CPView)aView keyPath:(CPString)aKeyPath duration:(float)aDuration
{
var frameUpdater = _animationFrameUpdaters[anIdentifier],
result = nil;
if (frameUpdater == null)
{
frameUpdater = new FrameUpdater(anIdentifier);
_animationFrameUpdaters[anIdentifier] = frameUpdater;
result = frameUpdater;
}
frameUpdater.addTarget(aView, aKeyPath, aDuration);
return result;
}
- (void)stopFrameUpdaterWithIdentifier:(CPString)anIdentifier
{
var frameUpdater = _animationFrameUpdaters[anIdentifier];
if (frameUpdater)
{
frameUpdater.stop();
delete _animationFrameUpdaters[anIdentifier];
}
else
CPLog.warn("Could not find FrameUpdater with identifier " + anIdentifier);
}
- (void)setCompletionHandler:(Function)aCompletionHandler
{
if (_completionHandlerAgent)
_completionHandlerAgent.invalidate();
_completionHandlerAgent = aCompletionHandler ? (new CompletionHandlerAgent(aCompletionHandler)) : nil;
}
- (void)completionHandler
{
if (!_completionHandlerAgent)
return nil;
return _completionHandlerAgent.completionHandler();
}
@end
@implementation CPView (CPAnimationContext)
- (CGRect)frameWithNewSuperviewSize:(CGSize)newSize
{
var mask = [self autoresizingMask];
if (mask == CPViewNotSizable)
return _frame;
var oldSize = _superview._frame.size,
newFrame = CGRectMakeCopy(_frame),
dX = newSize.width - oldSize.width,
dY = newSize.height - oldSize.height,
evenFractionX = 1.0 / ((mask & CPViewMinXMargin ? 1 : 0) + (mask & CPViewWidthSizable ? 1 : 0) + (mask & CPViewMaxXMargin ? 1 : 0)),
evenFractionY = 1.0 / ((mask & CPViewMinYMargin ? 1 : 0) + (mask & CPViewHeightSizable ? 1 : 0) + (mask & CPViewMaxYMargin ? 1 : 0)),
baseX = (mask & CPViewMinXMargin ? _frame.origin.x : 0) +
(mask & CPViewWidthSizable ? _frame.size.width : 0) +
(mask & CPViewMaxXMargin ? oldSize.width - _frame.size.width - _frame.origin.x : 0),
baseY = (mask & CPViewMinYMargin ? _frame.origin.y : 0) +
(mask & CPViewHeightSizable ? _frame.size.height : 0) +
(mask & CPViewMaxYMargin ? oldSize.height - _frame.size.height - _frame.origin.y : 0);
if (mask & CPViewMinXMargin)
newFrame.origin.x += dX * (baseX > 0 ? _frame.origin.x / baseX : evenFractionX);
if (mask & CPViewWidthSizable)
newFrame.size.width += dX * (baseX > 0 ? _frame.size.width / baseX : evenFractionX);
if (mask & CPViewMinYMargin)
newFrame.origin.y += dY * (baseY > 0 ? _frame.origin.y / baseY : evenFractionY);
if (mask & CPViewHeightSizable)
newFrame.size.height += dY * (baseY > 0 ? _frame.size.height / baseY : evenFractionY);
return newFrame;
}
- (BOOL)hasCustomDrawRect
{
return self._viewClassFlags & 1;
}
- (BOOL)hasCustomLayoutSubviews
{
return self._viewClassFlags & 2;
}
@end
@implementation CAMediaTimingFunction (Additions)
- (CPArray)controlPoints
{
return [_c1x, _c1y, _c2x, _c2y];
}
@end
@implementation CAAnimation (Additions)
- (CPArray)timingFunctionControlPoints
{
if (_timingFunction)
return [_timingFunction controlPoints];
return [0, 0, 1, 1];
}
@end
@implementation CAKeyframeAnimation (Additions)
- (CPArray)timingFunctionsControlPoints
{
var result = [CPArray array];
[_timingFunctions enumerateObjectsUsingBlock:function(timingFunction, idx)
{
[result addObject:[timingFunction controlPoints]];
}];
return result;
}
@end
var CompletionHandlerAgent = function(aCompletionHandler)
{
this._completionHandler = aCompletionHandler;
this.total = 0;
this.valid = true;
};
CompletionHandlerAgent.prototype.completionHandler = function()
{
return this._completionHandler;
};
CompletionHandlerAgent.prototype.fire = function()
{
this._completionHandler();
};
CompletionHandlerAgent.prototype.increment = function()
{
this.total++;
};
CompletionHandlerAgent.prototype.decrement = function()
{
if (this.total <= 0)
return;
this.total--;
if (this.valid && this.total == 0)
{
this.fire();
}
};
CompletionHandlerAgent.prototype.invalidate = function()
{
this.valid = false;
};
var _animationFlushingObserverCallback = function()
{
CPLog.debug("_animationFlushingObserverCallback");
if ([_CPAnimationContextStack count] == 1)
{
var context = [_CPAnimationContextStack lastObject];
[context _flushAnimations];
[_CPAnimationContextStack removeLastObject];
}
CPLog.debug("_animationFlushingObserver "+_animationFlushingObserver+" stack:" + [_CPAnimationContextStack count]);
if (_animationFlushingObserver && ![_CPAnimationContextStack count])
{
CPLog.debug("removeObserver");
CFRunLoopObserverInvalidate([CPRunLoop mainRunLoop], _animationFlushingObserver);
_animationFlushingObserver = nil;
}
};
CFRunLoopObserver = function(activities, repeats, order, callout, context)
{
this.activities = activities;
this.repeats = repeats;
this.order = order;
this.callout = callout;
this.context = context;
this.isvalid = true;
};
CFRunLoopObserverCreate = function(activities, repeats, order, callout, context)
{
return new CFRunLoopObserver(activities, repeats, order, callout, context);
};
CFRunLoopAddObserver = function(runloop, observer, mode)
{
var observers = runloop._observers;
if (!observers)
observers = (runloop._observers = []);
if (observers.indexOf(observer) == -1)
observers.push(observer);
};
CFRunLoopObserverInvalidate = function(runloop, observer, mode)
{
CFRunLoopRemoveObserver(runloop, observer, mode);
};
CFRunLoopRemoveObserver = function(runloop, observer, mode)
{
var observers = runloop._observers;
if (observers)
{
var idx = observers.indexOf(observer);
if (idx !== -1)
{
observers.splice(idx, 1);
if (observers.length == 0)
runloop._observers = nil;
}
}
};
var FrameUpdater = function(anIdentifier)
{
this._identifier = anIdentifier;
this._duration = 0;
this._stop = false;
this._targets = [];
this._callbacks = [];
var frameUpdater = this;
this._updateFunction = function(timestamp)
{
if (frameUpdater._stop)
return;
if (this._startDate == null)
this._startDate = timestamp;
for (var i = 0; i < frameUpdater._callbacks.length; i++)
frameUpdater._callbacks[i]();
if (timestamp - this._startDate < frameUpdater._duration * 1000)
window.requestAnimationFrame(frameUpdater._updateFunction);
};
};
FrameUpdater.prototype.start = function()
{
window.requestAnimationFrame(this._updateFunction);
};
FrameUpdater.prototype.stop = function()
{
CPLog.debug("stop FrameUpdater with id " + this.identifier());
this._stop = true;
var targets = this._targets;
for (var i = 0; i < targets.length; i++)
{
CPLog.debug(targets[i] + " Remove animation-name property");
targets[i]._DOMElement.style.removeProperty(CPBrowserCSSProperty("animation-name"));
}
};
FrameUpdater.prototype.updateFunction = function()
{
return this._updateFunction;
};
FrameUpdater.prototype.identifier = function()
{
return this._identifier;
};
FrameUpdater.prototype.addTarget = function(target, keyPath, duration)
{
var callback = createUpdateFrame(target, keyPath);
if (callback)
{
this._duration = MAX(this._duration, duration);
this._targets.push(target);
this._callbacks.push(callback);
}
};
var createUpdateFrame = function(aView, aKeyPath)
{
if (aKeyPath !== "frame" && aKeyPath !== "frameSize")
return nil;
var style = getComputedStyle(aView._DOMElement);
var updateFrame = function(timestamp)
{
var width = ROUND(style.getPropertyCSSValue('width').getFloatValue(CSSPrimitiveValue.CSS_PX)),
height = ROUND(style.getPropertyCSSValue('height').getFloatValue(CSSPrimitiveValue.CSS_PX));
if (aKeyPath == "frame")
{
var left = ROUND(style.getPropertyCSSValue('left').getFloatValue(CSSPrimitiveValue.CSS_PX)),
top = ROUND(style.getPropertyCSSValue('top').getFloatValue(CSSPrimitiveValue.CSS_PX)),
frame = CGRectMake(left, top, width, height);
[aView setFrame:frame];
}
else if (aKeyPath == "frameSize")
{
[aView setFrameSize:CGSizeMake(width, height)];
}
[[CPRunLoop currentRunLoop] performSelectors];
};
return updateFrame;
};
+222
View File
@@ -0,0 +1,222 @@
@import "_CPObjectAnimator.j"
@import "CPView.j"
@implementation CPViewAnimator : _CPObjectAnimator
{
}
- (void)viewWillMoveToSuperview:(CPView)aSuperview
{
var orderInAnim = [self animationForKey:@"CPAnimationTriggerOrderIn"];
if (orderInAnim && [orderInAnim isKindOfClass:[CAPropertyAnimation class]])
{
[_target setValue:[orderInAnim fromValue] forKeyPath:[orderInAnim keyPath]];
}
[_target viewWillMoveToSuperview:aSuperview];
}
- (void)viewDidMoveToSuperview
{
var orderInAnim = [self animationForKey:@"CPAnimationTriggerOrderIn"];
if (orderInAnim && [orderInAnim isKindOfClass:[CAPropertyAnimation class]])
{
[self _setTargetValue:YES withKeyPath:@"CPAnimationTriggerOrderIn" fallback:nil completion:function()
{
[_target setValue:[orderInAnim toValue] forKeyPath:[orderInAnim keyPath]];
}];
}
else
{
[_target viewDidMoveToSuperview];
}
}
- (void)removeFromSuperview
{
[self _setTargetValue:nil withKeyPath:@"CPAnimationTriggerOrderOut" setter:_cmd];
}
- (void)setHidden:(BOOL)shouldHide
{
if ([_target isHidden] == shouldHide)
return;
if (shouldHide == NO)
return [_target setHidden:NO];
[self _setTargetValue:YES withKeyPath:@"CPAnimationTriggerOrderOut" setter:_cmd];
}
- (void)setAlphaValue:(CGPoint)alphaValue
{
[self _setTargetValue:alphaValue withKeyPath:@"alphaValue" setter:_cmd];
}
- (void)setBackgroundColor:(CPColor)aColor
{
[self _setTargetValue:aColor withKeyPath:@"backgroundColor" setter:_cmd];
}
- (void)setFrameOrigin:(CGPoint)aFrameOrigin
{
[self _setTargetValue:aFrameOrigin withKeyPath:@"frameOrigin" setter:_cmd];
}
- (void)setFrame:(CGRect)aFrame
{
[self _setTargetValue:aFrame withKeyPath:@"frame" setter:_cmd];
}
- (void)setFrameSize:(CGSize)aFrameSize
{
[self _setTargetValue:aFrameSize withKeyPath:@"frameSize" setter:_cmd];
}
// Convenience method for the common case where the setter has zero or one argument
- (void)_setTargetValue:(id)aTargetValue withKeyPath:(CPString)aKeyPath setter:(SEL)aSelector
{
var handler = function()
{
[_target performSelector:aSelector withObject:aTargetValue];
};
[self _setTargetValue:aTargetValue withKeyPath:aKeyPath fallback:handler completion:handler];
}
- (void)_setTargetValue:(id)aTargetValue withKeyPath:(CPString)aKeyPath fallback:(Function)fallback completion:(Function)completion
{
var animation = [_target animationForKey:aKeyPath],
context = [CPAnimationContext currentContext];
if (!animation || ![animation isKindOfClass:[CAAnimation class]] || (![context duration] && ![animation duration]) || ![_CPObjectAnimator supportsCSSAnimations])
{
if (fallback)
fallback();
}
else
{
[context _enqueueActionForObject:_target keyPath:aKeyPath targetValue:aTargetValue animationCompletion:completion];
}
}
@end
var transformOrigin = function(start, current)
{
return "translate(" + (current.x - start.x) + "px," + (current.y - start.y) + "px)";
};
var transformFrameToTranslate = function(start, current)
{
return transformOrigin(start.origin, current.origin);
};
var transformFrameToWidth = function(start, current)
{
return current.size.width + "px";
};
var transformFrameToHeight = function(start, current)
{
return current.size.height + "px";
};
var transformSizeToWidth = function(start, current)
{
return current.width + "px";
};
var transformSizeToHeight = function(start, current)
{
return current.height + "px";
};
var DEFAULT_CSS_PROPERTIES = nil;
@implementation CPView (CPAnimatablePropertyContainer)
+ (CPDictionary)defaultCSSProperties
{
if (DEFAULT_CSS_PROPERTIES == nil)
{
var transformProperty = CPBrowserCSSProperty("transform");
DEFAULT_CSS_PROPERTIES = @{
"backgroundColor" : [@{"property":"background", "value":function(sv, val){return [val cssString];}}],
"alphaValue" : [@{"property":"opacity"}],
"frame" : [@{"property":transformProperty, "value":transformFrameToTranslate},
@{"property":"width", "value":transformFrameToWidth},
@{"property":"height", "value":transformFrameToHeight}],
"frameOrigin" : [@{"property":transformProperty, "value":transformOrigin}],
"frameSize" : [@{"property":"width", "value":transformSizeToWidth},
@{"property":"height", "value":transformSizeToHeight}]
};
}
return DEFAULT_CSS_PROPERTIES;
}
+ (CPArray)cssPropertiesForKeyPath:(CPString)aKeyPath
{
return [[self defaultCSSProperties] objectForKey:aKeyPath];
}
+ (Class)animatorClass
{
var anim_class = CPClassFromString(CPStringFromClass(self) + "Animator");
if (anim_class)
return anim_class;
return [[self superclass] animatorClass];
}
- (id)animator
{
if (!_animator)
_animator = [[[[self class] animatorClass] alloc] initWithTarget:self];
return _animator;
}
- (id)DOMElementForKeyPath:(CPString)aKeyPath
{
return _DOMElement;
}
+ (CAAnimation)defaultAnimationForKey:(CPString)aKey
{
if ([self cssPropertiesForKeyPath:aKey] !== nil)
return [CAAnimation animation];
return nil;
}
- (CAAnimation)animationForKey:(CPString)aKey
{
var animations = [self animations],
animation = nil;
if (!animations || !(animation = [animations objectForKey:aKey]))
{
animation = [[self class] defaultAnimationForKey:aKey];
}
return animation;
}
- (CPDictionary)animations
{
return _animationsDictionary;
}
- (void)setAnimations:(CPDictionary)animationsDict
{
_animationsDictionary = [animationsDict copy];
}
@end
+288
View File
@@ -0,0 +1,288 @@
var ANIMATIONS_GLOBAL_ID = 0,
CURRENT_ANIMATIONS = {},
ANIMATION_END_EVENT_NAME,
ANIMATION__PROPERTY,
ANIMATION_NAME_PROPERTY,
ANIMATION_DURATION_PROPERTY,
ANIMATION_TIMING_FUNCTION_PROPERTY,
ANIMATION_FILL_MODE_PROPERTY,
ANIMATION_KEYFRAMES_RULE;
var defineCSSProperties = function()
{
if (this.done)
return;
ANIMATION_END_EVENT_NAME = CPBrowserStyleProperty("animationend"),
ANIMATION_PROPERTY = CPBrowserCSSProperty("animation"),
ANIMATION_NAME_PROPERTY = CPBrowserCSSProperty("animation-name"),
ANIMATION_DURATION_PROPERTY = CPBrowserCSSProperty("animation-duration"),
ANIMATION_TIMING_FUNCTION_PROPERTY = CPBrowserCSSProperty("animation-timing-function"),
ANIMATION_FILL_MODE_PROPERTY = CPBrowserCSSProperty("animation-fill-mode"),
ANIMATION_KEYFRAMES_RULE = "@" + ANIMATION_PROPERTY.substring(0, ANIMATION_PROPERTY.indexOf("animation")) + "keyframes";
this.done = true;
}
CSSAnimation = function(aTarget/*DOM Element*/, anIdentifier)
{
defineCSSProperties();
if (!anIdentifier)
anIdentifier = ANIMATIONS_GLOBAL_ID++;
var animationName = "anim_" + anIdentifier,
animation = CURRENT_ANIMATIONS[anIdentifier];
if (animation)
console.warn("Animation "+ anIdentifier + " is already in use. Ignoring.");
else
{
this.target = aTarget;
this.identifier = anIdentifier;
this.animationName = animationName;
this.listener = null;
this.styleElement = null;
this.propertyanimations = [];
this.animationsnames = [];
this.animationstimingfunctions = [];
this.animationsdurations = [];
this.islive = false;
this.didBuildDOMElements = false;
this.removeAnimationPropertyOnCompletion = true;
animation = this;
CURRENT_ANIMATIONS[anIdentifier] = animation;
}
return animation;
}
CSSAnimation.prototype.addPropertyAnimation = function(propertyName/*String*/, valueFunction/*Function*/, aDuration/*float*/, aKeyTimes/*d, [d]*/, aValues/*Array*/, aTimingFunctions/*[d,d,d,d],[[d,d,d,d]]*/, aCompletionfunction/*Function*/)
{
if (this.islive)
return false;
// TODO: If a property already exist, replace its values & valueFunctions.
var name = this.animationName + "_" + propertyName;
var animation = {name:name,
property:propertyName,
valuefunction:valueFunction,
keytimes:aKeyTimes,
values:aValues,
duration:aDuration,
completionfunction:aCompletionfunction};
var animationTimingFunction;
if (aTimingFunctions && (aTimingFunctions[0] instanceof Array))
{
animation.keyframestimingFunctions = aTimingFunctions;
// dummy timing function overriden by keyframes timings functions
animationTimingFunction = "linear";
}
else
animationTimingFunction = "cubic-bezier(" + aTimingFunctions + ")";
this.animationstimingfunctions.push(animationTimingFunction);
this.propertyanimations.push(animation);
this.animationsnames.push(name);
this.animationsdurations.push(aDuration + "s");
return true;
}
CSSAnimation.prototype.keyFrames = function()
{
var keyframesRules = [];
var count = this.propertyanimations.length;
for (var i = 0; i < count; i++)
{
var animation = this.propertyanimations[i],
property = animation.property,
valuefunction = animation.valuefunction,
keytimes = animation.keytimes,
values = animation.values,
timingFunctions = animation.keyframestimingFunctions;
var keyframes = [],
keytimescount = keytimes.length,
start_value = values[0];
for (var j = 0; j < keytimescount; j++)
{
var keytime = keytimes[j],
value = values[j],
timingFunction;
if (valuefunction !== nil)
value = valuefunction(start_value, value);
var keyframeContent = property + ": " + value + ";";
if (timingFunctions && timingFunctions.length && (timingFunction = timingFunctions[j]))
{
keyframeContent += ANIMATION_TIMING_FUNCTION_PROPERTY + ":cubic-bezier(" + timingFunction + ");";
}
var keyframe = "\t" + Math.round(keytime * 100) + "% {\n\t\t" + keyframeContent + "\n\t}\n";
keyframes.push(keyframe);
}
// TODO ! Add keyframe rule to CPCompatibility
var rule = ANIMATION_KEYFRAMES_RULE + " " + animation.name + " {\n" + keyframes.join(" ") + "}\n";
keyframesRules.push(rule);
}
return keyframesRules.join("\n");
}
CSSAnimation.prototype.appendKeyFramesRule = function()
{
var styleElement = this.createKeyFramesStyleElement(),
keyframesText = this.keyFrames(),
nodeText = document.createTextNode(keyframesText);
styleElement.appendChild(nodeText);
document.head.appendChild(styleElement);
}
CSSAnimation.prototype.createKeyFramesStyleElement = function()
{
if (!this.styleElement)
{
var styleElement = document.createElement("style");
styleElement.setAttribute("type", "text/css");
this.styleElement = styleElement;
}
return this.styleElement;
}
CSSAnimation.prototype.endEventListener = function()
{
var animation = this,
animationsNames = this.animationsnames,
inFlightAnimationsNames = animationsNames.slice();
if (!animation.listener)
{
var AnimationEndListener = function(event)
{
var idx = inFlightAnimationsNames.indexOf(event.animationName);
if (idx !== -1)
inFlightAnimationsNames.splice(idx, 1);
if (inFlightAnimationsNames.length == 0)
{
for (var i = 0; i < animationsNames.length; i++)
{
var completion = animation.completionFunctionForAnimationName(animationsNames[i]);
if (completion)
completion();
}
var eventTarget = event.target,
style = eventTarget.style;
if (animation.removeAnimationPropertyOnCompletion)
style.removeProperty(ANIMATION_NAME_PROPERTY);
style.removeProperty(ANIMATION_DURATION_PROPERTY);
style.removeProperty(ANIMATION_FILL_MODE_PROPERTY);
style.removeProperty("-webkit-backface-visibility");
if (animation.animationstimingfunctions.length)
style.removeProperty(ANIMATION_TIMING_FUNCTION_PROPERTY);
removeFromParent(animation.styleElement);
eventTarget.removeEventListener(ANIMATION_END_EVENT_NAME, AnimationEndListener);
animation.listener = null;
delete (CURRENT_ANIMATIONS[animation.identifier]);
}
};
this.listener = AnimationEndListener;
}
return this.listener;
}
CSSAnimation.prototype.completionFunctionForAnimationName = function(aName)
{
var propanims = this.propertyanimations,
count = propanims.length;
while (count--)
{
var anim = propanims[count];
if (anim.name == aName)
return anim.completionfunction;
}
return null;
}
CSSAnimation.prototype.addAnimationEndEventListener = function()
{
var listener = this.endEventListener();
this.target.addEventListener(ANIMATION_END_EVENT_NAME, listener, false);
}
CSSAnimation.prototype.setTargetStyleProperties = function()
{
var style = this.target.style;
if (this.animationstimingfunctions.length)
style.setProperty(ANIMATION_TIMING_FUNCTION_PROPERTY, this.animationstimingfunctions.join(","));
style.setProperty(ANIMATION_DURATION_PROPERTY, this.animationsdurations.join(","));
style.setProperty(ANIMATION_FILL_MODE_PROPERTY, "forwards");
// http://webdesign.tutsplus.com/tutorials/htmlcss-tutorials/css3-animations-the-hiccups-and-bugs-youll-want-to-avoid/
style.setProperty("-webkit-backface-visibility", "hidden");
}
CSSAnimation.prototype.buildDOMElements = function()
{
this.appendKeyFramesRule();
this.addAnimationEndEventListener();
this.setTargetStyleProperties();
this.didBuildDOMElements = true;
}
CSSAnimation.prototype.setRemoveAnimationPropertyOnCompletion = function(flag)
{
this.removeAnimationPropertyOnCompletion = flag;
}
CSSAnimation.prototype.start = function()
{
if (this.propertyanimations.length == 0 || this.islive)
return false;
if (!this.didBuildDOMElements)
this.buildDOMElements();
this.target.style.setProperty(ANIMATION_NAME_PROPERTY, this.animationsnames.join(","));
this.islive = true;
return true;
}
var removeFromParent= function(aNode)
{
var parentNode = aNode.parentNode;
if (parentNode)
parentNode.removeChild(aNode);
}
+91
View File
@@ -0,0 +1,91 @@
@import <Foundation/CPProxy.j>
@import "CPAnimationContext.j"
var _supportsCSSAnimations = null;
@protocol CPAnimatablePropertyContainer <CPObject>
+ (id)defaultAnimationForKey:(CPString)key;
- (id)animationForKey:(CPString)key;
- (id)animator;
- (CPDictionary)animations;
- (void)setAnimations:(CPDictionary)animations;
@end
@implementation _CPObjectAnimator : CPProxy
{
id <CPAnimatablePropertyContainer> _target;
}
+ (BOOL)supportsCSSAnimations
{
if (_supportsCSSAnimations === null)
_supportsCSSAnimations = CPBrowserCSSProperty("animation");
return _supportsCSSAnimations;
}
- (id)initWithTarget:(id)aTarget
{
_target = aTarget;
return self;
}
- (id)animator
{
return self;
}
- (BOOL)isEqual:(id)anObject
{
return [_target isEqual:anObject];
}
- (id)forwardingTargetForSelector:(SEL)aSelector
{
return _target;
}
- (CPMethodSignature)methodSignatureForSelector:(SEL)aSelector
{
return [_target methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(CPInvocation)anInvocation
{
var target = [self forwardingTargetForSelector:[anInvocation selector]];
[anInvocation invokeWithTarget:target];
return;
}
- (void)doesNotRecognizeSelector:(SEL)aSelector
{
[CPException raise:CPInvalidArgumentException reason:@"Animator does not recognize selector " + CPStringFromSelector(aSelector)];
}
- (CPString)description
{
return [CPString stringWithFormat:@"%@ Animator Proxy for %@", [self class], _target];
}
- (void)setValue:(id)aTargetValue forKey:(id)aKeyPath
{
var animation = [_target animationForKey:aKeyPath],
context = [CPAnimationContext currentContext];
if (!animation || ![animation isKindOfClass:[CAAnimation class]] || (![context duration] && ![animation duration]) || ![_CPObjectAnimator supportsCSSAnimations])
[_target setValue:aTargetValue forKey:aKeyPath];
else
{
[context _enqueueActionForObject:_target keyPath:aKeyPath targetValue:aTargetValue completionHandler:function()
{
[_target setValue:aTargetValue forKey:aKeyPath];
}];
}
}
@end
+370
View File
@@ -0,0 +1,370 @@
/**
* Copyright 2010 Tim Down.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* jshashtable
*
* jshashtable is a JavaScript implementation of a hash table. It creates a single constructor function called Hashtable
* in the global scope.
*
* Author: Tim Down <tim@timdown.co.uk>
* Version: 2.1
* Build date: 21 March 2010
* Website: http://www.timdown.co.uk/jshashtable
*/
Hashtable = (function() {
var FUNCTION = "function";
var arrayRemoveAt = (typeof Array.prototype.splice == FUNCTION) ?
function(arr, idx) {
arr.splice(idx, 1);
} :
function(arr, idx) {
var itemsAfterDeleted, i, len;
if (idx === arr.length - 1) {
arr.length = idx;
} else {
itemsAfterDeleted = arr.slice(idx + 1);
arr.length = idx;
for (i = 0, len = itemsAfterDeleted.length; i < len; ++i) {
arr[idx + i] = itemsAfterDeleted[i];
}
}
};
function hashObject(obj) {
var hashCode;
if (typeof obj == "string") {
return obj;
} else if (typeof obj.hashCode == FUNCTION) {
// Check the hashCode method really has returned a string
hashCode = obj.hashCode();
return (typeof hashCode == "string") ? hashCode : hashObject(hashCode);
} else if (typeof obj.toString == FUNCTION) {
return obj.toString();
} else {
try {
return String(obj);
} catch (ex) {
// For host objects (such as ActiveObjects in IE) that have no toString() method and throw an error when
// passed to String()
return Object.prototype.toString.call(obj);
}
}
}
function equals_fixedValueHasEquals(fixedValue, variableValue) {
return fixedValue.equals(variableValue);
}
function equals_fixedValueNoEquals(fixedValue, variableValue) {
return (typeof variableValue.equals == FUNCTION) ?
variableValue.equals(fixedValue) : (fixedValue === variableValue);
}
function createKeyValCheck(kvStr) {
return function(kv) {
if (kv === null) {
throw new Error("null is not a valid " + kvStr);
} else if (typeof kv == "undefined") {
throw new Error(kvStr + " must not be undefined");
}
};
}
var checkKey = createKeyValCheck("key"), checkValue = createKeyValCheck("value");
/*----------------------------------------------------------------------------------------------------------------*/
function Bucket(hash, firstKey, firstValue, equalityFunction) {
this[0] = hash;
this.entries = [];
this.addEntry(firstKey, firstValue);
if (equalityFunction !== null) {
this.getEqualityFunction = function() {
return equalityFunction;
};
}
}
var EXISTENCE = 0, ENTRY = 1, ENTRY_INDEX_AND_VALUE = 2;
function createBucketSearcher(mode) {
return function(key) {
var i = this.entries.length, entry, equals = this.getEqualityFunction(key);
while (i--) {
entry = this.entries[i];
if ( equals(key, entry[0]) ) {
switch (mode) {
case EXISTENCE:
return true;
case ENTRY:
return entry;
case ENTRY_INDEX_AND_VALUE:
return [ i, entry[1] ];
}
}
}
return false;
};
}
function createBucketLister(entryProperty) {
return function(aggregatedArr) {
var startIndex = aggregatedArr.length;
for (var i = 0, len = this.entries.length; i < len; ++i) {
aggregatedArr[startIndex + i] = this.entries[i][entryProperty];
}
};
}
Bucket.prototype = {
getEqualityFunction: function(searchValue) {
return (typeof searchValue.equals == FUNCTION) ? equals_fixedValueHasEquals : equals_fixedValueNoEquals;
},
getEntryForKey: createBucketSearcher(ENTRY),
getEntryAndIndexForKey: createBucketSearcher(ENTRY_INDEX_AND_VALUE),
removeEntryForKey: function(key) {
var result = this.getEntryAndIndexForKey(key);
if (result) {
arrayRemoveAt(this.entries, result[0]);
return result[1];
}
return null;
},
addEntry: function(key, value) {
this.entries[this.entries.length] = [key, value];
},
keys: createBucketLister(0),
values: createBucketLister(1),
getEntries: function(entries) {
var startIndex = entries.length;
for (var i = 0, len = this.entries.length; i < len; ++i) {
// Clone the entry stored in the bucket before adding to array
entries[startIndex + i] = this.entries[i].slice(0);
}
},
containsKey: createBucketSearcher(EXISTENCE),
containsValue: function(value) {
var i = this.entries.length;
while (i--) {
if ( value === this.entries[i][1] ) {
return true;
}
}
return false;
}
};
/*----------------------------------------------------------------------------------------------------------------*/
// Supporting functions for searching hashtable buckets
function searchBuckets(buckets, hash) {
var i = buckets.length, bucket;
while (i--) {
bucket = buckets[i];
if (hash === bucket[0]) {
return i;
}
}
return null;
}
function getBucketForHash(bucketsByHash, hash) {
var bucket = bucketsByHash[hash];
// Check that this is a genuine bucket and not something inherited from the bucketsByHash's prototype
return ( bucket && (bucket instanceof Bucket) ) ? bucket : null;
}
/*----------------------------------------------------------------------------------------------------------------*/
function Hashtable(hashingFunctionParam, equalityFunctionParam) {
var that = this;
var buckets = [];
var bucketsByHash = {};
var hashingFunction = (typeof hashingFunctionParam == FUNCTION) ? hashingFunctionParam : hashObject;
var equalityFunction = (typeof equalityFunctionParam == FUNCTION) ? equalityFunctionParam : null;
this.put = function(key, value) {
checkKey(key);
checkValue(value);
var hash = hashingFunction(key), bucket, bucketEntry, oldValue = null;
// Check if a bucket exists for the bucket key
bucket = getBucketForHash(bucketsByHash, hash);
if (bucket) {
// Check this bucket to see if it already contains this key
bucketEntry = bucket.getEntryForKey(key);
if (bucketEntry) {
// This bucket entry is the current mapping of key to value, so replace old value and we're done.
oldValue = bucketEntry[1];
bucketEntry[1] = value;
} else {
// The bucket does not contain an entry for this key, so add one
bucket.addEntry(key, value);
}
} else {
// No bucket exists for the key, so create one and put our key/value mapping in
bucket = new Bucket(hash, key, value, equalityFunction);
buckets[buckets.length] = bucket;
bucketsByHash[hash] = bucket;
}
return oldValue;
};
this.get = function(key) {
checkKey(key);
var hash = hashingFunction(key);
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, hash);
if (bucket) {
// Check this bucket to see if it contains this key
var bucketEntry = bucket.getEntryForKey(key);
if (bucketEntry) {
// This bucket entry is the current mapping of key to value, so return the value.
return bucketEntry[1];
}
}
return null;
};
this.containsKey = function(key) {
checkKey(key);
var bucketKey = hashingFunction(key);
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, bucketKey);
return bucket ? bucket.containsKey(key) : false;
};
this.containsValue = function(value) {
checkValue(value);
var i = buckets.length;
while (i--) {
if (buckets[i].containsValue(value)) {
return true;
}
}
return false;
};
this.clear = function() {
buckets.length = 0;
bucketsByHash = {};
};
this.isEmpty = function() {
return !buckets.length;
};
var createBucketAggregator = function(bucketFuncName) {
return function() {
var aggregated = [], i = buckets.length;
while (i--) {
buckets[i][bucketFuncName](aggregated);
}
return aggregated;
};
};
this.keys = createBucketAggregator("keys");
this.values = createBucketAggregator("values");
this.entries = createBucketAggregator("getEntries");
this.remove = function(key) {
checkKey(key);
var hash = hashingFunction(key), bucketIndex, oldValue = null;
// Check if a bucket exists for the bucket key
var bucket = getBucketForHash(bucketsByHash, hash);
if (bucket) {
// Remove entry from this bucket for this key
oldValue = bucket.removeEntryForKey(key);
if (oldValue !== null) {
// Entry was removed, so check if bucket is empty
if (!bucket.entries.length) {
// Bucket is empty, so remove it from the bucket collections
bucketIndex = searchBuckets(buckets, hash);
arrayRemoveAt(buckets, bucketIndex);
delete bucketsByHash[hash];
}
}
}
return oldValue;
};
this.size = function() {
var total = 0, i = buckets.length;
while (i--) {
total += buckets[i].entries.length;
}
return total;
};
this.each = function(callback) {
var entries = that.entries(), i = entries.length, entry;
while (i--) {
entry = entries[i];
callback(entry[0], entry[1]);
}
};
this.putAll = function(hashtable, conflictCallback) {
var entries = hashtable.entries();
var entry, key, value, thisValue, i = entries.length;
var hasConflictCallback = (typeof conflictCallback == FUNCTION);
while (i--) {
entry = entries[i];
key = entry[0];
value = entry[1];
// Check for a conflict. The default behaviour is to overwrite the value for an existing key
if ( hasConflictCallback && (thisValue = that.get(key)) ) {
value = conflictCallback(key, thisValue, value);
}
that.put(key, value);
}
};
this.clone = function() {
var clone = new Hashtable(hashingFunctionParam, equalityFunctionParam);
clone.putAll(that);
return clone;
};
}
return Hashtable;
})();
+15
View File
@@ -200,6 +200,7 @@ var CPRunLoopLastNativeRunLoop = 0;
CPArray _orderedPerforms;
int _runLoopInsuranceTimer;
CPArray _observers;
}
/*
@@ -224,6 +225,7 @@ var CPRunLoopLastNativeRunLoop = 0;
_timersForModes = {};
_nativeTimersForModes = {};
_nextTimerFireDatesForModes = {};
_observers = nil;
}
return self;
@@ -473,6 +475,19 @@ var CPRunLoopLastNativeRunLoop = 0;
else
_orderedPerforms = performs;
if (_observers)
{
var count = _observers.length;
while(count--)
{
var obs = _observers[count];
obs.callout();
if (!obs.repeats)
_observers.splice(count, 1);
}
}
_runLoopLock = NO;
return nextFireDate;
@@ -0,0 +1,533 @@
/*
* AppController.j
* CPAnimatablePropertyContainerTest
*
* Created by You on December 3, 2012.
* Copyright 2012, Your Company All rights reserved.
*/
@import <Foundation/CPObject.j>
CPLogRegister(CPLogConsole);
var ANIMATIONS_NAMES = ["Fade In", "Fade Out", "Background Color", "Frame Origin", "Bounce", "Frame Size", "Frame"];
@implementation AppController : CPObject
{
@outlet CPWindow theWindow; //this "outlet" is connected automatically by the Cib
@outlet CPBox group1Box;
@outlet CPBox group2Box;
@outlet CPView animationSandbox;
CPView leftView;
CPView rightView;
CPArray animations;
float duration1 @accessors;
float duration2 @accessors;
CPString message1 @accessors;
CPString message2 @accessors;
CPInteger selectedTimingFunction1 @accessors;
CPInteger selectedTimingFunction2 @accessors;
CPString timingFunction1 @accessors;
CPString timingFunction2 @accessors;
}
- (id)init
{
self = [super init];
animations = [CPArray array];
[ANIMATIONS_NAMES enumerateObjectsUsingBlock:function(anim, idx)
{
var dict = [CPDictionary dictionaryWithObjectsAndKeys:anim, @"name", NO, @"enabled1", NO, @"enabled2"];
[animations addObject:dict];
}];
duration1 = 1.0;
duration2 = 1.0;
message1 = @"Done for View 1";
message2 = @"Done for View 2";
timingFunction1 = @"0,1,1,0";
timingFunction2 = @"0,1,1,0";
selectedTimingFunction1 = 1;
selectedTimingFunction2 = 1;
return self;
}
- (void)applicationDidFinishLaunching:(CPNotification)aNotification
{
// This is called when the application is done loading.
}
- (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.
[[theWindow contentView] setBackgroundColor:[CPColor colorWithRed:1 green:238/255 blue:185/255 alpha:1]];
[animationSandbox setBackgroundColor:[CPColor whiteColor]];
[self revert:nil];
// In this case, we want the window from Cib to become our full browser window
[theWindow setFullPlatformWindow:YES];
}
- (IBAction)runAnimationsGroup1:(id)sender
{
[[CPAnimationContext currentContext] setDuration:duration1];
[[CPAnimationContext currentContext] setTimingFunction:[self timingFunctionForGroup:1 fromPopUp:selectedTimingFunction1]];
[[CPAnimationContext currentContext] setCompletionHandler:[self completionHandlerFromString:message1]];
[self runAnimationsForGroup:1];
}
- (IBAction)runAnimationsGroup2:(id)sender
{
[[CPAnimationContext currentContext] setDuration:duration2];
[[CPAnimationContext currentContext] setTimingFunction:[self timingFunctionForGroup:2 fromPopUp:selectedTimingFunction2]];
[[CPAnimationContext currentContext] setCompletionHandler:[self completionHandlerFromString:message2]];
[self runAnimationsForGroup:2];
}
- (IBAction)animate:(id)sender
{
[[CPAnimationContext currentContext] setDuration:duration1];
[[CPAnimationContext currentContext] setTimingFunction:[self timingFunctionForGroup:1 fromPopUp:selectedTimingFunction2]];
[[CPAnimationContext currentContext] setCompletionHandler:[self completionHandlerFromString:message1]];
var tag = [sender tag];
var width = tag ? 100 : 200;
[[sender animator] setFrameSize:CGSizeMake(width, CGRectGetHeight([sender frame]))];
[sender setTag:(1 - tag)];
}
- (IBAction)runBothGroups:(id)sender
{
[CPAnimationContext runAnimationGroup:function(context)
{
[context setDuration:duration1];
[context setTimingFunction:[self timingFunctionForGroup:1 fromPopUp:selectedTimingFunction1]];
[self runAnimationsForGroup:1];
} completionHandler:function()
{
CPLogConsole(message1);
}];
[CPAnimationContext runAnimationGroup:function(context)
{
[context setDuration:duration2];
[context setTimingFunction:[self timingFunctionForGroup:2 fromPopUp:selectedTimingFunction2]];
[self runAnimationsForGroup:2];
} completionHandler:function()
{
CPLogConsole(message2);
}];
}
/*
- (IBAction)runInGroups:(id)sender
{
[CPAnimationContext beginGrouping];
[[CPAnimationContext currentContext] setDuration:duration1];
[[CPAnimationContext currentContext] setTimingFunction:[self timingFunctionFromString:timingFunction1]];
[[CPAnimationContext currentContext] setCompletionHandler:[self completionHandlerFromString:message1]];
[self runAnimationsForGroup:1];
[CPAnimationContext endGrouping];
[CPAnimationContext beginGrouping];
[[CPAnimationContext currentContext] setDuration:duration2];
[[CPAnimationContext currentContext] setCompletionHandler:[self completionHandlerFromString:message2]];
[self runAnimationsForGroup:2];
[CPAnimationContext endGrouping];
}
- (IBAction)emptyGroupTest:(id)sender
{
[CPAnimationContext beginGrouping];
[[CPAnimationContext currentContext] setDuration:10];
[[CPAnimationContext currentContext] setCompletionHandler:function()
{
CPLogConsole("Context duration is 10s but no animations were set. We run the completionHandler (this message) immediately and return");
}];
[CPAnimationContext endGrouping];
}
*/
- (IBAction)removeFromSuperview:(id)sender
{
[[leftView animator] removeFromSuperview];
[[rightView animator] removeFromSuperview];
}
- (IBAction)revert:(id)sender
{
[self setupGroup1:nil];
[self setupGroup2:nil];
}
/*
- (IBAction)addSubview:(id)sender
{
[[theWindow contentView] addSubview:];
}
*/
- (void)runAnimationsForGroup:(CPInteger)group
{
var aView = group == 1 ? leftView : rightView,
enabledKey = @"enabled" + group;
var enabledIndexes = [animations indexesOfObjectsPassingTest:function(obj, idx)
{
return [obj objectForKey:enabledKey];
}];
if ([enabledIndexes count] == 0)
return;
if ([enabledIndexes containsIndex:0])
{
[aView setAlphaValue:0];
[[aView animator] setAlphaValue:1];
}
else if ([enabledIndexes containsIndex:1])
{
[[aView animator] setAlphaValue:0];
}
if ([enabledIndexes containsIndex:2])
{
[[aView animator] setBackgroundColor:[CPColor magentaColor]];
}
if ([enabledIndexes containsIndex:6])
{
var frame = CGRectMakeCopy([aView frame]),
origin = frame.origin,
size = frame.size;
origin.x += 450;
origin.y += 200;
size.width += 100;
size.height += 100;
[[aView animator] setFrame:frame];
return;
}
if ([enabledIndexes containsIndex:4])
{
[[aView animations] setObject:[self bounceAnimation:aView] forKey:@"frameOrigin"];
var origin = CGPointMakeCopy([aView frameOrigin]);
[[aView animator] setFrameOrigin:origin];
}
else if ([enabledIndexes containsIndex:3])
{
[[aView animations] removeObjectForKey:@"frameOrigin"];
var origin = CGPointMakeCopy([aView frameOrigin]);
origin.x +=550;
origin.y +=300;
[[aView animator] setFrameOrigin:origin];
}
if ([enabledIndexes containsIndex:5])
{
var size = CGSizeMakeCopy([aView frameSize]);
size.width += 300;
size.height += 200;
[[aView animator] setFrameSize:size];
}
}
- (CAMediaTimingFunction)controlsPointsForGroup:(CPInteger)aGroup
{
var aString = (aGroup == 1) ? timingFunction1 : timingFunction2;
if (!aString || ![aString length])
return nil;
var controlsPoints = [aString componentsSeparatedByString:@","];
return [[controlsPoints[0] floatValue], [controlsPoints[1] floatValue], [controlsPoints[2] floatValue], [controlsPoints[3] floatValue]];
}
- (CAMediaTimingFunction)timingFunctionForGroup:(CPInteger)aGroup fromPopUp:(CPInteger)selectedTag
{
var controlsPoints;
switch (selectedTag)
{
case 1: controlsPoints = [0, 0, 1, 1];
break;
case 2: controlsPoints = [0.42, 0, 1, 1];
break;
case 3: controlsPoints = [0, 0, 0.58, 1];
break;
case 4: controlsPoints = [0.42, 0, 0.58, 1];
break;
case 0: controlsPoints = [self controlsPointsForGroup:aGroup];
break;
}
return [CAMediaTimingFunction functionWithControlPoints:controlsPoints[0] :controlsPoints[1] :controlsPoints[2] :controlsPoints[3]];
}
- (Function)completionHandlerFromString:(CPString)aMessage
{
if (!aMessage || ![aMessage length])
return nil;
var s = new Date();
return function()
{
var e = new Date() - s;
CPLogConsole(aMessage + " in " + e + " ms");
};
}
- (CAKeyframeAnimation)bounceAnimation:(CPView)aView
{
var anim = [CAKeyframeAnimation animation],
easein = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseIn],
easeout = [CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionEaseOut];
[anim setKeyTimes:[0, 0.25, 0.5, 0.75, 1]];
var origin = CGPointMakeCopy([aView frameOrigin]);
[anim setValues:[origin, CGPointMake(origin.x, origin.y + 50), origin, CGPointMake(origin.x, origin.y + 25), origin]];
[anim setTimingFunctions:[easeout, easein, easeout, easein]];
return anim;
}
- (CABasicAnimation)fadeOutAnimation
{
var animation = [CABasicAnimation animationWithKeyPath:@"alphaValue"];
[animation setDuration:0.2];
[animation setFromValue:1];
[animation setToValue:0];
[animation setTimingFunction:[CAMediaTimingFunction functionWithName:kCAMediaTimingFunctionLinear]];
return animation;
}
- (IBAction)setupGroup1:(id)sender
{
var contentView = [group1Box contentView];
var hasSubviews = [[contentView viewWithTag:1000] state] * 2,
customLayout = [[contentView viewWithTag:1001] state] * 4,
customDraw = [[contentView viewWithTag:1002] state] * 8,
customDrawSubviews = [[contentView viewWithTag:1003] state],
autoLayout = [[contentView viewWithTag:1004] state],
options = hasSubviews | customLayout | customDraw;
[leftView removeFromSuperview];
leftView = [self viewWithOptions:options];
if (leftView)
{
[leftView setFrame:CGRectMake(0, 0, 200, 200)];
[animationSandbox addSubview:leftView];
}
if (hasSubviews)
[self addSubviewsToView:leftView autoLayout:(autoLayout && !customLayout) customDrawSubviews:customDrawSubviews];
}
- (IBAction)setupGroup2:(id)sender
{
var contentView = [group2Box contentView];
var hasSubviews = [[contentView viewWithTag:1000] state] * 2,
customLayout = [[contentView viewWithTag:1001] state] * 4,
customDraw = [[contentView viewWithTag:1002] state] * 8,
options = hasSubviews | customLayout | customDraw;
[rightView removeFromSuperview];
rightView = [self viewWithOptions:options];
if (rightView)
{
[rightView setFrame:CGRectMake(250, 0, 200, 200)];
[animationSandbox addSubview:rightView];
}
if (leftView)
[animationSandbox addSubview:leftView];
if (hasSubviews)
[self addSubviewsToView:rightView autoLayout:!customLayout customDrawSubviews:NO];
}
- (void)viewWithOptions:(CPInteger)options
{
var view = nil,
hasSubviews = NO;
switch (options)
{
case 0: view = [[ColorView alloc] initWithFrame:CGRectMakeZero()];
break;
case 2: view = [[ColorView alloc] initWithFrame:CGRectMakeZero()];
hasSubviews = YES;
break;
case 4:
case 6: view = [[CustomLayoutView alloc] initWithFrame:CGRectMakeZero()];
[view setBackgroundColor:[CPColor randomColor]];
hasSubviews = YES;
break;
case 12:
case 14: view = [[CustomLayoutDrawView alloc] initWithFrame:CGRectMakeZero()];
hasSubviews = YES;
break;
case 8: view = [[DrawView alloc] initWithFrame:CGRectMakeZero()];
break;
case 10: view = [[DrawView alloc] initWithFrame:CGRectMakeZero()];
hasSubviews = YES;
break;
}
var anims = [CPDictionary dictionaryWithObject:[self fadeOutAnimation] forKey:@"CPAnimationTriggerOrderOut"];
[view setAnimations:anims];
return view;
}
- (void)addSubviewsToView:(CPView)aView autoLayout:(BOOL)autoLayout customDrawSubviews:(BOOL)customDrawSubviews
{
var subViewClass = customDrawSubviews ? [DrawView class] : [ColorView class];
for (var i = 0; i < 5; i++)
{
var view = [[subViewClass alloc] initWithFrame:CGRectMake(50, i * 40, 80, 26)];
if (autoLayout)
[view setAutoresizingMask:CPViewWidthSizable|CPViewMinYMargin];
[aView addSubview:view];
}
}
@end
@implementation ColorView : CPView
{
}
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
[self setBackgroundColor:[CPColor randomColor]];
return self;
}
@end
@implementation DrawView : CPView
{
CPColor color @accessors;
}
- (id)initWithFrame:(CGRect)aFrame
{
self = [super initWithFrame:aFrame];
color = [CPColor randomColor];
return self;
}
- (void)drawRect:(CGRect)aRect
{
var context = [[CPGraphicsContext currentContext] graphicsPort],
bounds = [self bounds];
CGContextSetLineWidth(context, 2);
CGContextSetStrokeColor(context, [CPColor blackColor]);
CGContextSetFillColor(context, color);
CGContextBeginPath(context);
CGContextFillRect(context, bounds);
var height = CGRectGetHeight(bounds),
width = CGRectGetWidth(bounds);
CGContextStrokeLineSegments(context, [CGPointMake(10,0), CGPointMake(10, height),
CGPointMake(0, 10), CGPointMake(CGRectGetWidth(aRect), 10),
CGPointMake(width - 10, 0), CGPointMake(width - 10, height),
CGPointMake(0, height - 10), CGPointMake(width, height - 10)], 8);
}
@end
@implementation CustomLayoutView : CPView
{
}
- (void)layoutSubviews
{
var subviews = [self subviews],
count = [subviews count] - 1;
var dx = (CGRectGetWidth([self frame]) - 100) / count,
dy = (CGRectGetHeight([self frame]) - 26) / count
[subviews enumerateObjectsUsingBlock:function(view, idx, stop)
{
[view setFrameOrigin:CGPointMake(dx * idx, dy * idx)];
}];
}
@end
@implementation CustomLayoutDrawView : DrawView
{
}
- (void)layoutSubviews
{
var subviews = [self subviews],
count = [subviews count] - 1;
var dx = (CGRectGetWidth([self frame]) - 100) / count,
dy = (CGRectGetHeight([self frame]) - 26) / count
[subviews enumerateObjectsUsingBlock:function(view, idx, stop)
{
[view setFrameOrigin:CGPointMake(dx * idx, dy * idx)];
}];
}
@end
@@ -0,0 +1,10 @@
<?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>CPAnimatablePropertyContainerTest</string>
</dict>
</plist>
@@ -0,0 +1,94 @@
/*
* Jakefile
* CPAnimatablePropertyContainerTest
*
* Created by You on December 3, 2012.
* Copyright 2012, 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");
app ("CPAnimatablePropertyContainerTest", function(task)
{
task.setBuildIntermediatesPath(FILE.join("Build", "CPAnimatablePropertyContainerTest.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setProductName("CPAnimatablePropertyContainerTest");
task.setIdentifier("com.yourcompany.CPAnimatablePropertyContainerTest");
task.setVersion("1.0");
task.setAuthor("Your Company");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("CPAnimatablePropertyContainerTest");
task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**")));
task.setResources(new FileList("Resources/**"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
task.setNib2CibFlags("-R Resources/");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
else
task.setCompilerFlags("-O");
});
task ("default", ["CPAnimatablePropertyContainerTest"], function()
{
printResults(configuration);
});
task ("build", ["default"]);
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", "CPAnimatablePropertyContainerTest", "index.html")]);
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", "CPAnimatablePropertyContainerTest", "index.html")]);
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", "CPAnimatablePropertyContainerTest"));
OS.system(["press", "-f", FILE.join("Build", "Release", "CPAnimatablePropertyContainerTest"), FILE.join("Build", "Deployment", "CPAnimatablePropertyContainerTest")]);
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Desktop", "CPAnimatablePropertyContainerTest"));
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "CPAnimatablePropertyContainerTest"), FILE.join("Build", "Desktop", "CPAnimatablePropertyContainerTest", "CPAnimatablePropertyContainerTest.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
OS.system([FILE.join("Build", "Desktop", "CPAnimatablePropertyContainerTest", "CPAnimatablePropertyContainerTest.app", "Contents", "MacOS", "NativeHost"), "-i"]);
});
function printResults(configuration)
{
print("----------------------------");
print(configuration+" app built at path: "+FILE.join("Build", configuration, "CPAnimatablePropertyContainerTest"));
print("----------------------------");
}
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -0,0 +1,107 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!--
index-debug.html
CPAnimatablePropertyContainerTest
Created by You on December 3, 2012.
Copyright 2012, Your Company All rights reserved.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1" />
<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>CPAnimatablePropertyContainerTest</title>
<script type="text/javascript">
OBJJ_MAIN_FILE = "main.j";
OBJJ_INCLUDE_PATHS = ["Frameworks/Debug", "Frameworks", "SomethingElse"];
</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">
body{margin:0; padding:0;}
#container {position: absolute; top:50%; left:50%;}
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
</style>
<!--[if lt IE 7]>
<STYLE type="text/css">
#container { position: relative; top: 50%; }
#content { position: relative;}
</STYLE>
<![endif]-->
</head>
<body style="">
<div id="cappuccino-body">
<div id="loadingcontainer" style="background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
<script type="text/javascript">
document.write("<div id='container'><p id='content'>" +
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
"Loading CPAnimatablePropertyContainerTest...</p></div>");
</script>
<noscript>
<div id="container">
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
</ul>
</div>
</div>
</noscript>
</div>
</div>
</body>
</html>
@@ -0,0 +1,77 @@
<!DOCTYPE html
PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!--
index.html
CPAnimatablePropertyContainerTest
Created by You on December 3, 2012.
Copyright 2012, Your Company All rights reserved.
-->
<head>
<meta http-equiv="X-UA-Compatible" content="IE=EmulateIE7, chrome=1" />
<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>CPAnimatablePropertyContainerTest</title>
<script type="text/javascript">
OBJJ_MAIN_FILE = "main.j";
</script>
<script type="text/javascript" src="Frameworks/Objective-J/Objective-J.js" charset="UTF-8"></script>
<style type="text/css">
body{margin:0; padding:0;}
#container {position: absolute; top:50%; left:50%;}
#content {width:800px; text-align:center; margin-left: -400px; height:50px; margin-top:-25px; line-height: 50px;}
#content {font-family: "Helvetica", "Arial", sans-serif; font-size: 18px; color: black; text-shadow: 0px 1px 0px white; }
#loadgraphic {margin-right: 0.2em; margin-bottom:-2px;}
</style>
<!--[if lt IE 7]>
<STYLE type="text/css">
#container { position: relative; top: 50%; }
#content { position: relative;}
</STYLE>
<![endif]-->
</head>
<body style="">
<div id="cappuccino-body">
<div id="loadingcontainer" style="background-color: #eeeeee; overflow:hidden; width:100%; height:100%; position: absolute; top: 0; left: 0;">
<script type="text/javascript">
document.write("<div id='container'><p id='content'>" +
"<img id='loadgraphic' width='16' height='16' src='Resources/spinner.gif' /> " +
"Loading CPAnimatablePropertyContainerTest...</p></div>");
</script>
<noscript>
<div id="container">
<div style="width: 440px; padding: 10px 25px 20px 25px; font-family: sans-serif; background-color: #ffffff; position: relative; left: -245px; top: -120px; text-align: center; -moz-border-radius: 20px; -webkit-border-radius: 20px; color: #555555">
<p style="line-height: 1.4em;">JavaScript is required for this site to work correctly but is either disabled or not supported by your browser.</p>
<p style="font-size:120%; padding:10px;"><a href="http://cappuccino.org/noscript">Show me how to enable JavaScript</a></p>
<p style="font-size:80%;">You may want to upgrade to a newer browser while you're at it:</p>
<ul style="margin:0;padding:0; text-align: center; font-size:80%;" >
<li style="display: inline;"><a href="http://www.apple.com/safari/download/">Safari</a></li>
<li style="display: inline;"><a href="http://www.mozilla.com/en-US/firefox/">Firefox</a></li>
<li style="display: inline;"><a href="http://www.google.com/chrome/">Chrome</a></li>
<li style="display: inline;"><a href="http://www.opera.com/download/">Opera</a></li>
<li style="display: inline;"><a href="http://www.microsoft.com/windows/downloads/ie/getitnow.mspx">Internet Explorer</a></li>
</ul>
</div>
</div>
</noscript>
</div>
</div>
</body>
</html>
@@ -0,0 +1,18 @@
/*
* AppController.j
* CPAnimatablePropertyContainerTest
*
* Created by You on December 3, 2012.
* Copyright 2012, Your Company All rights reserved.
*/
@import <Foundation/Foundation.j>
@import <AppKit/AppKit.j>
@import "AppController.j"
function main(args, namedArgs)
{
CPApplicationMain(args, namedArgs);
}