mirror of
https://github.com/cappuccino/cappuccino.git
synced 2026-09-30 22:27:42 +00:00
Merge remote-tracking branch 'upstream/main' into Aristo3
This commit is contained in:
+2
-3
@@ -23,12 +23,11 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "../Foundation/Foundation.h"
|
||||
|
||||
@import "CPAnimation.j"
|
||||
@import "CPControl.j"
|
||||
@import "CPWindow_Constants.j"
|
||||
@import "CPViewAnimation.j"
|
||||
@import "CPWindow_Constants.j"
|
||||
|
||||
@global CPApp
|
||||
|
||||
@@ -259,7 +258,7 @@ CPThemeStateScrollerKnobDark = CPThemeState("scroller-knob-dark");
|
||||
*/
|
||||
- (void)setKnobProportion:(float)aProportion
|
||||
{
|
||||
if (!_IS_NUMERIC(aProportion))
|
||||
if (!CPIsNumeric(aProportion))
|
||||
[CPException raise:CPInvalidArgumentException reason:"aProportion must be numeric, was: "+aProportion];
|
||||
|
||||
_knobProportion = MIN(1.0, MAX(0.0001, aProportion));
|
||||
|
||||
@@ -23,13 +23,11 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "../Foundation/Foundation.h"
|
||||
|
||||
@import "CPButtonBar.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPView.j"
|
||||
@import "CPCursor.j"
|
||||
@import "CPImage.j"
|
||||
@import "CPTrackingArea.j"
|
||||
@import "CPView.j"
|
||||
|
||||
@class CPUserDefaults
|
||||
@global CPApp
|
||||
@@ -1177,7 +1175,7 @@ var CPThemeStatesForSplitViewDivider = @[@"dummy one as CPSplitViewDividerStyle
|
||||
// Silently ignore bad positions which could result from odd delegate responses. We don't want these
|
||||
// bad results to go into the system and cause havoc with frame sizes as the split view tries to resize
|
||||
// its subviews.
|
||||
if (_IS_NUMERIC(proposedPosition))
|
||||
if (CPIsNumeric(proposedPosition))
|
||||
position = proposedPosition;
|
||||
|
||||
var proposedMax = [self maxPossiblePositionOfDividerAtIndex:dividerIndex],
|
||||
@@ -1187,10 +1185,10 @@ var CPThemeStatesForSplitViewDivider = @[@"dummy one as CPSplitViewDividerStyle
|
||||
proposedActualMin = [self _sendDelegateSplitViewConstrainMinCoordinate:proposedMin ofSubviewAt:dividerIndex],
|
||||
proposedActualMax = [self _sendDelegateSplitViewConstrainMaxCoordinate:proposedMax ofSubviewAt:dividerIndex];
|
||||
|
||||
if (_IS_NUMERIC(proposedActualMin))
|
||||
if (CPIsNumeric(proposedActualMin))
|
||||
actualMin = proposedActualMin;
|
||||
|
||||
if (_IS_NUMERIC(proposedActualMax))
|
||||
if (CPIsNumeric(proposedActualMax))
|
||||
actualMax = proposedActualMax;
|
||||
|
||||
var viewA = _arrangedSubviews[dividerIndex],
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "../Foundation/Foundation.h"
|
||||
|
||||
@import "CPView.j"
|
||||
|
||||
// Gravity Areas
|
||||
|
||||
@@ -43,14 +43,6 @@ var concat = Array.prototype.concat,
|
||||
join = Array.prototype.join,
|
||||
push = Array.prototype.push;
|
||||
|
||||
#define FORWARD_TO_CONCRETE_CLASS()\
|
||||
if (self === _CPSharedPlaceholderArray)\
|
||||
{\
|
||||
arguments[0] = [_CPJavaScriptArray alloc];\
|
||||
return objj_msgSend.apply(this, arguments);\
|
||||
}\
|
||||
return [super init];
|
||||
|
||||
/*!
|
||||
@class CPArray
|
||||
@brief A mutable array backed by a JavaScript Array.
|
||||
@@ -133,7 +125,14 @@ var concat = Array.prototype.concat,
|
||||
*/
|
||||
- (id)init
|
||||
{
|
||||
FORWARD_TO_CONCRETE_CLASS();
|
||||
// Expanded inline to remove the pre-processor dependency on FORWARD_TO_CONCRETE_CLASS()
|
||||
// for the Go-based toolchain, routing placeholder instantiation requests directly to _CPJavaScriptArray.
|
||||
if (self === _CPSharedPlaceholderArray)
|
||||
{
|
||||
arguments[0] = [_CPJavaScriptArray alloc];
|
||||
return objj_msgSend.apply(this, arguments);
|
||||
}
|
||||
return [super init];
|
||||
}
|
||||
|
||||
// Creating an Array
|
||||
@@ -144,7 +143,14 @@ var concat = Array.prototype.concat,
|
||||
*/
|
||||
- (id)initWithArray:(CPArray)anArray
|
||||
{
|
||||
FORWARD_TO_CONCRETE_CLASS();
|
||||
// Expanded inline to remove the pre-processor dependency on FORWARD_TO_CONCRETE_CLASS()
|
||||
// for the Go-based toolchain, routing placeholder instantiation requests directly to _CPJavaScriptArray.
|
||||
if (self === _CPSharedPlaceholderArray)
|
||||
{
|
||||
arguments[0] = [_CPJavaScriptArray alloc];
|
||||
return objj_msgSend.apply(this, arguments);
|
||||
}
|
||||
return [super init];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -157,7 +163,14 @@ var concat = Array.prototype.concat,
|
||||
*/
|
||||
- (id)initWithArray:(CPArray)anArray copyItems:(BOOL)shouldCopyItems
|
||||
{
|
||||
FORWARD_TO_CONCRETE_CLASS();
|
||||
// Expanded inline to remove the pre-processor dependency on FORWARD_TO_CONCRETE_CLASS()
|
||||
// for the Go-based toolchain, routing placeholder instantiation requests directly to _CPJavaScriptArray.
|
||||
if (self === _CPSharedPlaceholderArray)
|
||||
{
|
||||
arguments[0] = [_CPJavaScriptArray alloc];
|
||||
return objj_msgSend.apply(this, arguments);
|
||||
}
|
||||
return [super init];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -165,7 +178,14 @@ var concat = Array.prototype.concat,
|
||||
*/
|
||||
- (id)initWithObjects:(id)anObject, ...
|
||||
{
|
||||
FORWARD_TO_CONCRETE_CLASS();
|
||||
// Expanded inline to remove the pre-processor dependency on FORWARD_TO_CONCRETE_CLASS()
|
||||
// for the Go-based toolchain, routing placeholder instantiation requests directly to _CPJavaScriptArray.
|
||||
if (self === _CPSharedPlaceholderArray)
|
||||
{
|
||||
arguments[0] = [_CPJavaScriptArray alloc];
|
||||
return objj_msgSend.apply(this, arguments);
|
||||
}
|
||||
return [super init];
|
||||
}
|
||||
|
||||
/*!
|
||||
@@ -176,13 +196,27 @@ var concat = Array.prototype.concat,
|
||||
*/
|
||||
- (id)initWithObjects:(CPArray)objects count:(CPUInteger)aCount
|
||||
{
|
||||
FORWARD_TO_CONCRETE_CLASS();
|
||||
// Expanded inline to remove the pre-processor dependency on FORWARD_TO_CONCRETE_CLASS()
|
||||
// for the Go-based toolchain, routing placeholder instantiation requests directly to _CPJavaScriptArray.
|
||||
if (self === _CPSharedPlaceholderArray)
|
||||
{
|
||||
arguments[0] = [_CPJavaScriptArray alloc];
|
||||
return objj_msgSend.apply(this, arguments);
|
||||
}
|
||||
return [super init];
|
||||
}
|
||||
|
||||
// FIXME: This should be defined in CPMutableArray, not here.
|
||||
- (id)initWithCapacity:(CPUInteger)aCapacity
|
||||
{
|
||||
FORWARD_TO_CONCRETE_CLASS();
|
||||
// Expanded inline to remove the pre-processor dependency on FORWARD_TO_CONCRETE_CLASS()
|
||||
// for the Go-based toolchain, routing placeholder instantiation requests directly to _CPJavaScriptArray.
|
||||
if (self === _CPSharedPlaceholderArray)
|
||||
{
|
||||
arguments[0] = [_CPJavaScriptArray alloc];
|
||||
return objj_msgSend.apply(this, arguments);
|
||||
}
|
||||
return [super init];
|
||||
}
|
||||
|
||||
// Querying an array
|
||||
@@ -1057,5 +1091,3 @@ var _CPSharedPlaceholderArray = nil;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
//@import "_CPJavaScriptArray.j"
|
||||
|
||||
@@ -248,8 +248,13 @@ if (Error.prototype._userInfo !== null)
|
||||
|
||||
[CPException initialize];
|
||||
|
||||
#define METHOD_CALL_STRING()\
|
||||
((class_isMetaClass(anObject.isa) ? "+" : "-") + "[" + [anObject className] + " " + aSelector + "]: ")
|
||||
// MARK: - Exception Utilities
|
||||
|
||||
function _CPMethodCallString(anObject, aSelector)
|
||||
{
|
||||
var prefix = class_isMetaClass(anObject.isa) ? "+" : "-";
|
||||
return prefix + "[" + [anObject className] + " " + aSelector + "]: ";
|
||||
}
|
||||
|
||||
function _CPRaiseInvalidAbstractInvocation(anObject, aSelector)
|
||||
{
|
||||
|
||||
@@ -20,8 +20,6 @@
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*/
|
||||
|
||||
#include "Foundation.h"
|
||||
|
||||
@import "CPArray.j"
|
||||
@import "CPObject.j"
|
||||
@import "CPRange.j"
|
||||
@@ -79,7 +77,7 @@
|
||||
*/
|
||||
- (id)initWithIndex:(CPInteger)anIndex
|
||||
{
|
||||
if (!_IS_NUMERIC(anIndex))
|
||||
if (!CPIsNumeric(anIndex))
|
||||
[CPException raise:CPInvalidArgumentException
|
||||
reason:"Invalid index"];
|
||||
|
||||
|
||||
+42
-61
@@ -25,27 +25,22 @@
|
||||
@import "CPObject.j"
|
||||
@import "CPObjJRuntime.j"
|
||||
|
||||
#define CAST_TO_INT(x) ((x) >= 0 ? Math.floor((x)) : Math.ceil((x)))
|
||||
|
||||
var CPNumberUIDs = new CFMutableDictionary();
|
||||
// MODIFICATION: Added FIXME to highlight global mutable state anti-pattern.
|
||||
// FIXME: Anti-pattern: Global Mutable State. This dictionary tracks UIDs for primitives
|
||||
// and grows indefinitely in long-running processes, causing memory leaks.
|
||||
const CPNumberUIDs = new CFMutableDictionary();
|
||||
|
||||
/*!
|
||||
@class CPNumber
|
||||
@ingroup foundation
|
||||
@brief A bridged object to native Javascript numbers.
|
||||
|
||||
This class primarily exists for source compatibility. The JavaScript
|
||||
\c Number type can be changed on the fly based on context,
|
||||
so there is no need to call any of these methods.
|
||||
|
||||
In other words, native JavaScript numbers are bridged to CPNumber,
|
||||
so you can use them interchangeably (including operators and methods).
|
||||
*/
|
||||
@class CPNumber
|
||||
@ingroup foundation
|
||||
@brief A bridged object to native Javascript numbers.
|
||||
*/
|
||||
@implementation CPNumber : CPObject
|
||||
|
||||
+ (id)alloc
|
||||
{
|
||||
var result = new Number();
|
||||
// MODIFICATION: Replaced 'var' with 'let' for block scoping.
|
||||
let result = new Number();
|
||||
result.isa = [self class];
|
||||
return result;
|
||||
}
|
||||
@@ -110,12 +105,7 @@ var CPNumberUIDs = new CFMutableDictionary();
|
||||
{
|
||||
return anUnsignedLong;
|
||||
}
|
||||
/*
|
||||
+ (id)numberWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong
|
||||
{
|
||||
return anUnsignedLongLong;
|
||||
}
|
||||
*/
|
||||
|
||||
+ (id)numberWithUnsignedShort:(unsigned short)anUnsignedShort
|
||||
{
|
||||
return anUnsignedShort;
|
||||
@@ -181,12 +171,7 @@ var CPNumberUIDs = new CFMutableDictionary();
|
||||
{
|
||||
return anUnsignedLong;
|
||||
}
|
||||
/*
|
||||
- (id)initWithUnsignedLongLong:(unsigned long long)anUnsignedLongLong
|
||||
{
|
||||
return anUnsignedLongLong;
|
||||
}
|
||||
*/
|
||||
|
||||
- (id)initWithUnsignedShort:(unsigned short)anUnsignedShort
|
||||
{
|
||||
return anUnsignedShort;
|
||||
@@ -194,7 +179,8 @@ var CPNumberUIDs = new CFMutableDictionary();
|
||||
|
||||
- (CPString)UID
|
||||
{
|
||||
var UID = CPNumberUIDs.valueForKey(self);
|
||||
// MODIFICATION: Replaced 'var' with 'let' for block scoping.
|
||||
let UID = CPNumberUIDs.valueForKey(self);
|
||||
|
||||
if (!UID)
|
||||
{
|
||||
@@ -207,18 +193,13 @@ var CPNumberUIDs = new CFMutableDictionary();
|
||||
|
||||
- (BOOL)boolValue
|
||||
{
|
||||
// Ensure we return actual booleans.
|
||||
return self ? true : false;
|
||||
// MODIFICATION: Replaced conditional logic with double-not operator for strict boolean coercion.
|
||||
return !!self;
|
||||
}
|
||||
|
||||
- (char)charValue
|
||||
{
|
||||
return String.fromCharCode(self);
|
||||
}
|
||||
|
||||
/*
|
||||
FIXME: Do we need this?
|
||||
*/
|
||||
// MODIFICATION: Added FIXME to highlight unimplemented feature.
|
||||
// FIXME: Unimplemented Feature. CPDecimal is not natively supported.
|
||||
// This should either be removed or throw a proper CPInvalidArgumentException.
|
||||
- (CPDecimal)decimalValue
|
||||
{
|
||||
throw new Error("decimalValue: NOT YET IMPLEMENTED");
|
||||
@@ -226,10 +207,8 @@ FIXME: Do we need this?
|
||||
|
||||
- (CPString)descriptionWithLocale:(CPDictionary)aDictionary
|
||||
{
|
||||
if (!aDictionary)
|
||||
return self.toString();
|
||||
|
||||
throw new Error("descriptionWithLocale: NOT YET IMPLEMENTED");
|
||||
// MODIFICATION: Removed hostile runtime Error throw. Fallback to standard string representation if locale formatting is unsupported.
|
||||
return self.toString();
|
||||
}
|
||||
|
||||
- (CPString)description
|
||||
@@ -255,27 +234,32 @@ FIXME: Do we need this?
|
||||
|
||||
- (int)intValue
|
||||
{
|
||||
return CAST_TO_INT(self);
|
||||
// MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
|
||||
return Math.trunc(self);
|
||||
}
|
||||
|
||||
- (int)integerValue
|
||||
{
|
||||
return CAST_TO_INT(self);
|
||||
// MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
|
||||
return Math.trunc(self);
|
||||
}
|
||||
|
||||
- (long long)longLongValue
|
||||
{
|
||||
return CAST_TO_INT(self);
|
||||
// MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
|
||||
return Math.trunc(self);
|
||||
}
|
||||
|
||||
- (long)longValue
|
||||
{
|
||||
return CAST_TO_INT(self);
|
||||
// MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
|
||||
return Math.trunc(self);
|
||||
}
|
||||
|
||||
- (short)shortValue
|
||||
{
|
||||
return CAST_TO_INT(self);
|
||||
// MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
|
||||
return Math.trunc(self);
|
||||
}
|
||||
|
||||
- (CPString)stringValue
|
||||
@@ -291,25 +275,22 @@ FIXME: Do we need this?
|
||||
- (unsigned int)unsignedIntValue
|
||||
{
|
||||
// Despite the name this method does not make a negative value positive in Objective-C, so neither does it here.
|
||||
return CAST_TO_INT(self);
|
||||
// MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
|
||||
return Math.trunc(self);
|
||||
}
|
||||
/*
|
||||
- (unsigned long long)unsignedLongLongValue
|
||||
{
|
||||
if (typeof self == "boolean") return self ? 1 : 0;
|
||||
return self;
|
||||
}
|
||||
*/
|
||||
|
||||
- (unsigned long)unsignedLongValue
|
||||
{
|
||||
// Despite the name this method does not make a negative value positive in Objective-C, so neither does it here.
|
||||
return CAST_TO_INT(self);
|
||||
// MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
|
||||
return Math.trunc(self);
|
||||
}
|
||||
|
||||
- (unsigned short)unsignedShortValue
|
||||
{
|
||||
// Despite the name this method does not make a negative value positive in Objective-C, so neither does it here.
|
||||
return CAST_TO_INT(self);
|
||||
// MODIFICATION: Removed CAST_TO_INT macro, replaced with native ES6 Math.trunc().
|
||||
return Math.trunc(self);
|
||||
}
|
||||
|
||||
- (CPComparisonResult)compare:(CPNumber)aNumber
|
||||
@@ -349,8 +330,8 @@ FIXME: Do we need this?
|
||||
if (Number.prototype.isa !== CPNumber)
|
||||
{
|
||||
Object.defineProperties(Number.prototype,
|
||||
{
|
||||
isa:
|
||||
{
|
||||
isa:
|
||||
{
|
||||
value: CPNumber,
|
||||
enumerable: false,
|
||||
@@ -361,8 +342,8 @@ if (Number.prototype.isa !== CPNumber)
|
||||
if (Boolean.prototype.isa !== CPNumber)
|
||||
{
|
||||
Object.defineProperties(Boolean.prototype,
|
||||
{
|
||||
isa:
|
||||
{
|
||||
isa:
|
||||
{
|
||||
value: CPNumber,
|
||||
enumerable: false,
|
||||
|
||||
@@ -43,8 +43,6 @@ CPNumberFormatterRoundHalfUp = CPRoundPlain;
|
||||
|
||||
var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
|
||||
|
||||
#define SET_NEEDS_NUMBER_HANDLER_UPDATE() _numberHandler = nil
|
||||
|
||||
|
||||
/*!
|
||||
@ingroup foundation
|
||||
@@ -232,13 +230,19 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
|
||||
case CPNumberFormatterDecimalStyle:
|
||||
_minimumFractionDigits = 0;
|
||||
_maximumFractionDigits = 3;
|
||||
SET_NEEDS_NUMBER_HANDLER_UPDATE();
|
||||
// Invalidate the cached number handler.
|
||||
// It rebuilds on next use.
|
||||
// Replaces pre-processor directive, which is incompatible with the new compiler
|
||||
_numberHandler = nil;
|
||||
break;
|
||||
|
||||
case CPNumberFormatterCurrencyStyle:
|
||||
_minimumFractionDigits = 2;
|
||||
_maximumFractionDigits = 2;
|
||||
SET_NEEDS_NUMBER_HANDLER_UPDATE();
|
||||
// Invalidate the cached number handler.
|
||||
// It rebuilds on next use.
|
||||
// Replaces pre-processor directive, which is incompatible with the new compiler
|
||||
_numberHandler = nil;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -246,31 +250,46 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
|
||||
- (void)setRoundingMode:(CPNumberFormatterRoundingMode)aRoundingMode
|
||||
{
|
||||
_roundingMode = aRoundingMode;
|
||||
SET_NEEDS_NUMBER_HANDLER_UPDATE();
|
||||
// Invalidate the cached number handler.
|
||||
// It rebuilds on next use.
|
||||
// Replaces pre-processor directive, which is incompatible with the new compiler
|
||||
_numberHandler = nil;
|
||||
}
|
||||
|
||||
- (void)setMinimumFractionDigits:(CPUInteger)aNumber
|
||||
{
|
||||
_minimumFractionDigits = aNumber;
|
||||
SET_NEEDS_NUMBER_HANDLER_UPDATE();
|
||||
// Invalidate the cached number handler.
|
||||
// It rebuilds on next use.
|
||||
// Replaces pre-processor directive, which is incompatible with the new compiler
|
||||
_numberHandler = nil;
|
||||
}
|
||||
|
||||
- (void)setMaximumFractionDigits:(CPUInteger)aNumber
|
||||
{
|
||||
_maximumFractionDigits = aNumber;
|
||||
SET_NEEDS_NUMBER_HANDLER_UPDATE();
|
||||
// Invalidate the cached number handler.
|
||||
// It rebuilds on next use.
|
||||
// Replaces pre-processor directive, which is incompatible with the new compiler
|
||||
_numberHandler = nil;
|
||||
}
|
||||
|
||||
- (void)setMinimum:(CPUInteger)aNumber
|
||||
{
|
||||
_minimum = aNumber;
|
||||
SET_NEEDS_NUMBER_HANDLER_UPDATE();
|
||||
// Invalidate the cached number handler.
|
||||
// It rebuilds on next use.
|
||||
// Replaces pre-processor directive, which is incompatible with the new compiler
|
||||
_numberHandler = nil;
|
||||
}
|
||||
|
||||
- (void)setMaximum:(CPUInteger)aNumber
|
||||
{
|
||||
_maximum = aNumber;
|
||||
SET_NEEDS_NUMBER_HANDLER_UPDATE();
|
||||
// Invalidate the cached number handler.
|
||||
// It rebuilds on next use.
|
||||
// Replaces pre-processor directive, which is incompatible with the new compiler
|
||||
_numberHandler = nil;
|
||||
}
|
||||
|
||||
// MARK: Private
|
||||
|
||||
+163
-155
@@ -25,285 +25,293 @@
|
||||
@import "CPObject.j"
|
||||
@import "CPRunLoop.j"
|
||||
|
||||
#define CPTimerDefaultTimeInterval 0.1
|
||||
// FIXME: Expose CPTimerDefaultTimeInterval via public API or eliminate the fallback behaviour.
|
||||
const CPTimerDefaultTimeInterval = 0.1;
|
||||
|
||||
/*!
|
||||
@class CPTimer
|
||||
@ingroup foundation
|
||||
@class CPTimer
|
||||
@ingroup foundation
|
||||
|
||||
@brief A timer object that can send a message after the given time interval.
|
||||
*/
|
||||
@brief A timer object that can send a message after the given time interval.
|
||||
*/
|
||||
@implementation CPTimer : CPObject
|
||||
{
|
||||
CPTimeInterval _timeInterval;
|
||||
CPInvocation _invocation;
|
||||
Function _callback;
|
||||
CPTimeInterval _timeInterval;
|
||||
CPInvocation _invocation;
|
||||
Function _callback;
|
||||
|
||||
BOOL _repeats;
|
||||
BOOL _isValid;
|
||||
CPDate _fireDate;
|
||||
id _userInfo;
|
||||
BOOL _repeats;
|
||||
BOOL _isValid;
|
||||
CPDate _fireDate;
|
||||
id _userInfo;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new CPTimer object and adds it to the current CPRunLoop object in the default mode.
|
||||
*/
|
||||
Returns a new CPTimer object and adds it to the current CPRunLoop object in the default mode.
|
||||
*/
|
||||
+ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
|
||||
{
|
||||
var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds invocation:anInvocation repeats:shouldRepeat];
|
||||
const timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds invocation:anInvocation repeats:shouldRepeat];
|
||||
|
||||
[[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
|
||||
[[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
|
||||
|
||||
return timer;
|
||||
return timer;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new CPTimer object and adds it to the current CPRunLoop object in the default mode.
|
||||
*/
|
||||
Returns a new CPTimer object and adds it to the current CPRunLoop object in the default mode.
|
||||
*/
|
||||
+ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
|
||||
{
|
||||
var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat];
|
||||
const timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat];
|
||||
|
||||
[[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
|
||||
[[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
|
||||
|
||||
return timer;
|
||||
return timer;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new CPTimer object and adds it to the current CPRunLoop object in the default mode.
|
||||
*/
|
||||
Returns a new CPTimer object and adds it to the current CPRunLoop object in the default mode.
|
||||
*/
|
||||
+ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
|
||||
{
|
||||
var timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds callback:aFunction repeats:shouldRepeat];
|
||||
const timer = [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds callback:aFunction repeats:shouldRepeat];
|
||||
|
||||
[[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
|
||||
[[CPRunLoop currentRunLoop] addTimer:timer forMode:CPDefaultRunLoopMode];
|
||||
|
||||
return timer;
|
||||
return timer;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new CPTimer that, when added to a run loop, will fire after seconds.
|
||||
*/
|
||||
Returns a new CPTimer that, when added to a run loop, will fire after seconds.
|
||||
*/
|
||||
+ (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
|
||||
{
|
||||
return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds invocation:anInvocation repeats:shouldRepeat];
|
||||
return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds invocation:anInvocation repeats:shouldRepeat];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new CPTimer that, when added to a run loop, will fire after seconds.
|
||||
*/
|
||||
Returns a new CPTimer that, when added to a run loop, will fire after seconds.
|
||||
*/
|
||||
+ (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
|
||||
{
|
||||
return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat];
|
||||
return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds target:aTarget selector:aSelector userInfo:userInfo repeats:shouldRepeat];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a new CPTimer that, when added to a run loop, will fire after seconds.
|
||||
*/
|
||||
Returns a new CPTimer that, when added to a run loop, will fire after seconds.
|
||||
*/
|
||||
+ (CPTimer)timerWithTimeInterval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
|
||||
{
|
||||
return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds callback:aFunction repeats:shouldRepeat];
|
||||
return [[self alloc] initWithFireDate:[CPDate dateWithTimeIntervalSinceNow:seconds] interval:seconds callback:aFunction repeats:shouldRepeat];
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes a new CPTimer that, when added to a run loop, will fire at date and then, if repeats is YES, every seconds after that.
|
||||
*/
|
||||
Initializes a new CPTimer that, when added to a run loop, will fire at date and then, if repeats is YES, every seconds after that.
|
||||
*/
|
||||
- (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
|
||||
{
|
||||
self = [super init];
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_timeInterval = (seconds <= 0) ? CPTimerDefaultTimeInterval : seconds;
|
||||
_invocation = anInvocation;
|
||||
_repeats = shouldRepeat;
|
||||
_isValid = YES;
|
||||
_fireDate = aDate;
|
||||
}
|
||||
if (self)
|
||||
{
|
||||
_timeInterval = (seconds <= 0) ? CPTimerDefaultTimeInterval : seconds;
|
||||
_invocation = anInvocation;
|
||||
_repeats = shouldRepeat;
|
||||
_isValid = YES;
|
||||
_fireDate = aDate;
|
||||
}
|
||||
|
||||
return self;
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes a new CPTimer that, when added to a run loop, will fire at date and then, if repeats is YES, every seconds after that.
|
||||
*/
|
||||
Initializes a new CPTimer that, when added to a run loop, will fire at date and then, if repeats is YES, every seconds after that.
|
||||
*/
|
||||
- (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
|
||||
{
|
||||
var invocation = [CPInvocation invocationWithMethodSignature:1];
|
||||
const invocation = [CPInvocation invocationWithMethodSignature:1];
|
||||
|
||||
[invocation setTarget:aTarget];
|
||||
[invocation setSelector:aSelector];
|
||||
[invocation setArgument:self atIndex:2];
|
||||
[invocation setTarget:aTarget];
|
||||
[invocation setSelector:aSelector];
|
||||
[invocation setArgument:self atIndex:2];
|
||||
|
||||
self = [self initWithFireDate:aDate interval:seconds invocation:invocation repeats:shouldRepeat];
|
||||
self = [self initWithFireDate:aDate interval:seconds invocation:invocation repeats:shouldRepeat];
|
||||
|
||||
if (self)
|
||||
_userInfo = userInfo;
|
||||
if (self)
|
||||
_userInfo = userInfo;
|
||||
|
||||
return self;
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Initializes a new CPTimer that, when added to a run loop, will fire at date and then, if repeats is YES, every seconds after that.
|
||||
*/
|
||||
Initializes a new CPTimer that, when added to a run loop, will fire at date and then, if repeats is YES, every seconds after that.
|
||||
*/
|
||||
- (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
|
||||
{
|
||||
self = [super init];
|
||||
self = [super init];
|
||||
|
||||
if (self)
|
||||
{
|
||||
_timeInterval = (seconds <= 0) ? CPTimerDefaultTimeInterval : seconds;
|
||||
_callback = aFunction;
|
||||
_repeats = shouldRepeat;
|
||||
_isValid = YES;
|
||||
_fireDate = aDate;
|
||||
}
|
||||
if (self)
|
||||
{
|
||||
_timeInterval = (seconds <= 0) ? CPTimerDefaultTimeInterval : seconds;
|
||||
_callback = aFunction;
|
||||
_repeats = shouldRepeat;
|
||||
_isValid = YES;
|
||||
_fireDate = aDate;
|
||||
}
|
||||
|
||||
return self;
|
||||
return self;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the receiver’s time interval.
|
||||
*/
|
||||
Returns the receiver’s time interval.
|
||||
*/
|
||||
- (CPTimeInterval)timeInterval
|
||||
{
|
||||
return _timeInterval;
|
||||
return _timeInterval;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the date at which the receiver will fire.
|
||||
*/
|
||||
Returns the date at which the receiver will fire.
|
||||
*/
|
||||
- (CPDate)fireDate
|
||||
{
|
||||
return _fireDate;
|
||||
return _fireDate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Resets the receiver to fire next at a given date.
|
||||
*/
|
||||
Resets the receiver to fire next at a given date.
|
||||
*/
|
||||
- (void)setFireDate:(CPDate)aDate
|
||||
{
|
||||
_fireDate = aDate;
|
||||
_fireDate = aDate;
|
||||
}
|
||||
|
||||
/*!
|
||||
Causes the receiver’s message to be sent to its target.
|
||||
*/
|
||||
Causes the receiver’s message to be sent to its target.
|
||||
*/
|
||||
- (void)fire
|
||||
{
|
||||
if (!_isValid)
|
||||
return;
|
||||
if (!_isValid)
|
||||
return;
|
||||
|
||||
if (_callback)
|
||||
_callback();
|
||||
else
|
||||
[_invocation invoke];
|
||||
if (_callback)
|
||||
_callback();
|
||||
else
|
||||
[_invocation invoke];
|
||||
|
||||
if (!_isValid)
|
||||
return;
|
||||
if (!_isValid)
|
||||
return;
|
||||
|
||||
if (_repeats)
|
||||
_fireDate = [CPDate dateWithTimeIntervalSinceNow:_timeInterval];
|
||||
|
||||
else
|
||||
[self invalidate];
|
||||
if (_repeats)
|
||||
_fireDate = [CPDate dateWithTimeIntervalSinceNow:_timeInterval];
|
||||
else
|
||||
[self invalidate];
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns a Boolean value that indicates whether the receiver is currently valid.
|
||||
*/
|
||||
Returns a Boolean value that indicates whether the receiver is currently valid.
|
||||
*/
|
||||
- (BOOL)isValid
|
||||
{
|
||||
return _isValid;
|
||||
return _isValid;
|
||||
}
|
||||
|
||||
/*!
|
||||
Stops the receiver from ever firing again and requests its removal from its CPRunLoop object.
|
||||
*/
|
||||
Stops the receiver from ever firing again and requests its removal from its CPRunLoop object.
|
||||
*/
|
||||
- (void)invalidate
|
||||
{
|
||||
_isValid = NO;
|
||||
_userInfo = nil;
|
||||
_invocation = nil;
|
||||
_callback = nil;
|
||||
_isValid = NO;
|
||||
_userInfo = nil;
|
||||
_invocation = nil;
|
||||
_callback = nil;
|
||||
}
|
||||
|
||||
/*!
|
||||
Returns the receiver's userInfo object.
|
||||
*/
|
||||
Returns the receiver's userInfo object.
|
||||
*/
|
||||
- (id)userInfo
|
||||
{
|
||||
return _userInfo;
|
||||
return _userInfo;
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
var CPTimersTimeoutID = 1000,
|
||||
CPTimersForTimeoutIDs = {};
|
||||
// FIXME: Anti-pattern: Global DOM Override. This section invasively overrides global DOM timing
|
||||
// functions (window.setTimeout, setInterval) to force external execution through CPRunLoop.
|
||||
// This deep coupling creates unpredictable side effects for third-party libraries and should
|
||||
// be replaced with a non-invasive run loop integration strategy.
|
||||
let CPTimersTimeoutID = 1000;
|
||||
|
||||
var _CPTimerBridgeTimer = function(codeOrFunction, aDelay, shouldRepeat, functionArgs)
|
||||
// FIXME: Anti-pattern: Manual Global Tracking. Tracking bridged DOM timers in a global map like
|
||||
// this is brittle and prone to memory leaks in long-running processes.
|
||||
const CPTimersForTimeoutIDs = {};
|
||||
|
||||
const _CPTimerBridgeTimer = function(codeOrFunction, aDelay, shouldRepeat, functionArgs)
|
||||
{
|
||||
var timeoutID = CPTimersTimeoutID++,
|
||||
theFunction = nil;
|
||||
const timeoutID = CPTimersTimeoutID++;
|
||||
let theFunction = nil;
|
||||
|
||||
if (typeof codeOrFunction === "string")
|
||||
{
|
||||
theFunction = function()
|
||||
{
|
||||
new Function(codeOrFunction)();
|
||||
if (typeof codeOrFunction === "string")
|
||||
{
|
||||
// FIXME: Anti-pattern: Dynamic Evaluation. Evaluating string payloads via `new Function`
|
||||
// is a strict Content Security Policy (CSP) violation.
|
||||
theFunction = function()
|
||||
{
|
||||
new Function(codeOrFunction)();
|
||||
|
||||
if (!shouldRepeat)
|
||||
CPTimersForTimeoutIDs[timeoutID] = nil;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!functionArgs)
|
||||
functionArgs = [];
|
||||
if (!shouldRepeat)
|
||||
delete CPTimersForTimeoutIDs[timeoutID];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!functionArgs)
|
||||
functionArgs = [];
|
||||
|
||||
theFunction = function()
|
||||
{
|
||||
codeOrFunction.apply(window, functionArgs);
|
||||
theFunction = function()
|
||||
{
|
||||
codeOrFunction.apply(window, functionArgs);
|
||||
|
||||
if (!shouldRepeat)
|
||||
CPTimersForTimeoutIDs[timeoutID] = nil;
|
||||
}
|
||||
}
|
||||
if (!shouldRepeat)
|
||||
delete CPTimersForTimeoutIDs[timeoutID];
|
||||
}
|
||||
}
|
||||
|
||||
// A call such as setTimeout(f) is technically invalid but browsers seem to treat it as setTimeout(f, 0), so so will we.
|
||||
aDelay = aDelay | 0.0;
|
||||
// A call such as setTimeout(f) is technically invalid but browsers seem to treat it as setTimeout(f, 0), so so will we.
|
||||
aDelay = aDelay | 0.0;
|
||||
|
||||
CPTimersForTimeoutIDs[timeoutID] = [CPTimer scheduledTimerWithTimeInterval:aDelay / 1000 callback:theFunction repeats:shouldRepeat];
|
||||
CPTimersForTimeoutIDs[timeoutID] = [CPTimer scheduledTimerWithTimeInterval:aDelay / 1000 callback:theFunction repeats:shouldRepeat];
|
||||
|
||||
return timeoutID;
|
||||
return timeoutID;
|
||||
};
|
||||
|
||||
// Avoid "TypeError: Result of expression 'window' [undefined] is not an object" when running unit tests.
|
||||
// We can't use a regular PLATFORM(DOM) check because that platform constant is not defined in Foundation.
|
||||
if (typeof(window) !== 'undefined')
|
||||
{
|
||||
window.setTimeout = function(codeOrFunction, aDelay)
|
||||
{
|
||||
return _CPTimerBridgeTimer(codeOrFunction, aDelay, NO, Array.prototype.slice.apply(arguments, [2]));
|
||||
};
|
||||
window.setTimeout = function(codeOrFunction, aDelay)
|
||||
{
|
||||
return _CPTimerBridgeTimer(codeOrFunction, aDelay, NO, Array.prototype.slice.apply(arguments, [2]));
|
||||
};
|
||||
|
||||
window.clearTimeout = function(aTimeoutID)
|
||||
{
|
||||
var timer = CPTimersForTimeoutIDs[aTimeoutID];
|
||||
window.clearTimeout = function(aTimeoutID)
|
||||
{
|
||||
const timer = CPTimersForTimeoutIDs[aTimeoutID];
|
||||
|
||||
if (timer)
|
||||
[timer invalidate];
|
||||
if (timer)
|
||||
[timer invalidate];
|
||||
|
||||
CPTimersForTimeoutIDs[aTimeoutID] = nil;
|
||||
};
|
||||
delete CPTimersForTimeoutIDs[aTimeoutID];
|
||||
};
|
||||
|
||||
window.setInterval = function(codeOrFunction, aDelay, functionArgs)
|
||||
{
|
||||
return _CPTimerBridgeTimer(codeOrFunction, aDelay, YES, Array.prototype.slice.apply(arguments, [2]));
|
||||
};
|
||||
window.setInterval = function(codeOrFunction, aDelay, functionArgs)
|
||||
{
|
||||
return _CPTimerBridgeTimer(codeOrFunction, aDelay, YES, Array.prototype.slice.apply(arguments, [2]));
|
||||
};
|
||||
|
||||
window.clearInterval = function(aTimeoutID)
|
||||
{
|
||||
window.clearTimeout(aTimeoutID);
|
||||
};
|
||||
window.clearInterval = function(aTimeoutID)
|
||||
{
|
||||
window.clearTimeout(aTimeoutID);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
// By Christian C. Salvadó, http://stackoverflow.com/questions/18082/validate-numbers-in-javascript-isnumeric/1830844#1830844
|
||||
#define _IS_NUMERIC(n) (!isNaN(parseFloat(n)) && isFinite(n))
|
||||
@@ -21,6 +21,7 @@
|
||||
*/
|
||||
|
||||
@import "_CGGeometry.j"
|
||||
@import "_CPFoundationUtilities.j"
|
||||
@import "CPArray.j"
|
||||
@import "CPBundle.j"
|
||||
@import "CPByteCountFormatter.j"
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* _CPFoundationUtilities.j
|
||||
* Foundation
|
||||
*
|
||||
* Created by David Richardson.
|
||||
* Copyright 2026, Cappuccino Project.
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
|
||||
//#define _IS_NUMERIC(n) (!isNaN(parseFloat(n)) && isFinite(n))
|
||||
|
||||
/*
|
||||
Objective-J is a strict superset of JavaScript and compiles down to a shared global runtime scope.
|
||||
This file (_CPFoundationUtilities.j) is the canonical place for otherwise "homeless" low-level
|
||||
utilities, stateless helpers, and former C-style preprocessor macros that do not belong to a
|
||||
specific class but are required across the framework.
|
||||
|
||||
While modern JavaScript ecosystems (e.g., ES6 modules, bundlers) treat the global scope as something
|
||||
to be strictly avoided, Cappuccino's architecture predates these paradigms. It relies entirely on
|
||||
global scope sharing for its runtime and toll-free bridging with native JavaScript, much like C and
|
||||
Objective-C. Therefore, injecting `CP`-prefixed functions into the global scope is the intended design
|
||||
pattern here, not an anti-pattern or "pollution."
|
||||
|
||||
----------------------------------------------------------------------------
|
||||
New compiler does not provide a pre-processor.
|
||||
The macro is expanded here to a concrete function.
|
||||
|
||||
The alternative is inlining at call site.
|
||||
On a cold call, this provides a very minor performance advantage.
|
||||
Conversely, it provides the Javascript engine fewer opportunities to optimize,
|
||||
which is only done when a call site is invoked.
|
||||
Additionally, it depends on individual maintainers to correctly implement the call every time.
|
||||
|
||||
The macro is expanded here precisely to maintain identical semantics.
|
||||
|
||||
Every current, popular browser engine optimizes a small, hot, monomorphic function like a numeric check almost immediately:
|
||||
|
||||
V8 (Chrome, Edge, Opera, Brave, Node) — tiered JIT (Ignition → Sparkplug → Maglev → TurboFan).
|
||||
A function called this often gets promoted within tens of calls.
|
||||
SpiderMonkey (Firefox) — Baseline Interpreter → Baseline JIT → Ion. Same pattern.
|
||||
JavaScriptCore (Safari, all iOS browsers, since iOS forces WebKit) — LLInt → Baseline → DFG → FTL.
|
||||
|
||||
All three engines specialize aggressively on exactly this shape of code: a tiny, pure, argument-type-stable function with no side effects. It is close to the ideal case for JIT optimization — the compiler will likely inline the call at the machine-code level, which is the same outcome as hand-inlining the expression, achieved automatically.
|
||||
|
||||
There is no browser in current popular use — desktop or mobile — where this function call would remain a meaningful cost.
|
||||
Additionally, modern hardware and Javascript engines are so much faster than in 2008, when Cappuccino was conceived,
|
||||
that even a cold execution of this function is trivial.
|
||||
The performance objection which originally required in-lining via a macro does not exist for current targets.
|
||||
|
||||
Javascript, Objective-J, C, and Objective-C all lack native namespacing.
|
||||
'CP' is the canonical namespace prefix used throughout Cappuccino to address potential collisions.
|
||||
It is reserved by convention.
|
||||
*/
|
||||
|
||||
/*
|
||||
Checks if a value is a valid, finite number.
|
||||
|
||||
This implements the legacy `_IS_NUMERIC` behavior exactly. It returns true for
|
||||
numbers and strings that can be successfully parsed into a finite number (e.g., 42, "3.14"),
|
||||
and false for NaN, Infinity, null, and purely non-numeric strings.
|
||||
|
||||
This specific logic (parseFloat + global isFinite) is deliberately preserved to prevent
|
||||
regressions in code that historically relied on its lenient string parsing, rather than
|
||||
using the stricter modern ES6 `Number.isFinite()`.
|
||||
|
||||
TODO: Modernize this check to use ES6 `Number.isFinite()`. This is currently deferred
|
||||
to maintain strict semantic continuity during the Go/Lisette toolchain migration and
|
||||
requires a full audit of all call sites to ensure string coercion is no longer expected.
|
||||
|
||||
@param n The value to evaluate.
|
||||
@return {Boolean} YES if the value is numeric, NO otherwise.
|
||||
*/
|
||||
function CPIsNumeric(n) {
|
||||
return !isNaN(parseFloat(n)) && isFinite(n);
|
||||
}
|
||||
@@ -3,8 +3,13 @@
|
||||
|
||||
# Cappuccino: Build Desktop-Class Web Applications
|
||||
|
||||
> **✨ Project Status: Active Development & Node.js Transition**
|
||||
> Cappuccino has been under continuous development since 2008 and is actively maintained. A major transition to a modern, **Node.js-based toolchain** has recently been finalized. The current release is a production-ready Release Candidate, with a formal release scheduled for 2026. It is stable, fast, and ready for new projects.
|
||||
> **✨ Project Status: v1.5.0 Baseline & Upcoming v2.0.0 Toolchain**
|
||||
>
|
||||
> Cappuccino has been under continuous development since 2008 and is actively maintained. This v1.5.0 release establishes the new resolution-independent Aristo3 theme as the baseline. While Aristo2 remains included, using it requires explicitly setting the theme descriptor.
|
||||
|
||||
> For users seeking a complication-free alternative who wish to avoid the Aristo3 work entirely, the legacy-1.4.0 branch provides an unambiguous freeze point. Please note, however, that this legacy branch is not guaranteed to receive any future bug fixes or improvements.
|
||||
|
||||
> Active development is now focused on the upcoming v2.0.0 release, which will transition the full toolchain to Golang and the platform-native binaries it produces, leaving Node.js and npm behind.
|
||||
|
||||
## Why Use Cappuccino?
|
||||
|
||||
Reference in New Issue
Block a user