Compare commits

...
134 Commits
Author SHA1 Message Date
David RichardsonandGitHub f56855010f Merge pull request #3310 from enquora/manual-tests-audit
Manual tests audit
2026-09-08 19:28:28 -06:00
David Richardson b80713c1e5 Remove temporary test suite migration tooling
Deletes the Python migration script and shell restoration utilities used to transition the Tests/Manual directory to the @objj/jake toolchain.

The subsidiary Jakefile migration is complete.

The scripts are removed from the active tree to prevent repository clutter, while their implementation details and exact transformation logic remain permanently verifiable in the git history.
2026-09-08 19:25:21 -06:00
David Richardson 93a1eea08b Add missing copyright and license headers 2026-09-08 19:22:50 -06:00
David Richardson 7393607a52 Add batch compile tasks to Jakefile 2026-09-08 19:21:58 -06:00
David Richardson 53784f0e10 Migrate subsidiary test Jakefiles to @objj/jake toolchain
Applies the transformation script across all applications in the Tests/Manual directory to eliminate Narwhal-era dependencies and establish compatibility with the modern Node-based runner.

* Removes explicit `require("jake")` calls in favor of the injected JAKE global.
* Replaces legacy `cappuccino/jake` application instantiation with the `CAPPUCCINO.Jake.applicationtask.app` hook.
* Substitutes archaic `system`, `file`, and `os` API calls with native Node `child_process`, `fs`, and `path` modules.
* Corrects package paths for nativehost deployment targets.
2026-09-08 19:08:43 -06:00
David Richardson 900448528a Add temporary automation scripts for test suite migration
Commit the Python migration script and the shell restoration loop used to batch-process the transition of legacy test Jakefiles to the modern Node toolchain.

These scripts provide an auditable record of the automated transformations applied to the test suite and will be removed in a subsequent commit - once the migration is complete.
2026-09-08 19:05:20 -06:00
David Richardson e04e3d9ba5 Normallize naming of ScalingTest2
All test names must use Test suffix.
2026-09-08 18:48:55 -06:00
David Richardson 5eaede0622 Refactor subsidiary Jakefile migration script for @objj/jake compatibility
Updates the transformation script to target the modern Node-based Cappuccino toolchain, removing Narwhal-era APIs from test configurations.

* Replaces legacy `require("jake")` with the implicitly injected `JAKE` global.
* Routes application task instantiation through the modern `CAPPUCCINO.Jake.applicationtask.app` hook.
* Substitutes archaic `OS`, `FILE`, and `system` module calls with native Node `fs`, `path`, and `child_process` equivalents.
* Corrects package pathing for `nativehost` references.
2026-09-08 18:45:44 -06:00
David Richardson 2c06bf566f Refactor Tests/Manual orchestration
Removes recursive JAKE.subjake calls from the root Jakefile, eliminating silent error masking and path evaluation failures during test suite compilation. Replace subjake with POSIX find.

* Replaces Node-based directory traversal with POSIX find and -execdir to guarantee correct working directory context and propagate standard output.
* Restricts the `clean` task strictly to the removal of build artifacts.
* Introduces a `clobber` task to remove Frameworks symlinks alongside build artifacts, restoring the test directories to a pristine state.
2026-09-08 18:02:47 -06:00
David Richardson 4784a1a102 Normalize application naming in ./Tests/Manual
Ensure:
* CamelCase, ending in Test
* No hyphens, spaces, underscores or other non-alpha characters

Updated application directory names.
Update Jakefiles.
2026-09-08 17:47:30 -06:00
David RichardsonandGitHub e3cb2cd6db Merge pull request #3309 from cappuccino/manual-tests-xib-cleanup
Update test application menu titles in MainMenu.xib
2026-09-03 16:41:23 -06:00
David Richardson dfe14a0f97 Update test application menu titles in MainMenu.xib
* Replace generic 'NewApplication' placeholder strings with specific product names.

* Extract target names from Jakefile and assert parity with Info.plist CPBundleName.

* Improve visual grounding during manual test suite auditing.
2026-09-03 16:31:50 -06:00
David RichardsonandGitHub aa6fce3297 Merge pull request #3308 from enquora/manual-tests-upgrade-xibs-and-cibs
Upgrade legacy XIB documents and regenerate CIB test assets
2026-09-03 14:26:59 -06:00
David Richardson 06da3164fc Upgrade legacy XIB documents and regenerate CIB test assets
* Execute batch structural upgrade of all XIB files within ./Tests/Manual to modern XML DOM format using ibtool (Xcode 26.3) to resolve imminent Xcode 27 parsing failures.

* Add regenerate-test-cibs.py script to recursively compile the modernized XIB documents into CIB artifacts within Tests/Manual/ using the local nib2cib toolchain.

* Commit the updated CIB artifacts to maintain CI and test execution parity.

*Include batch execution bash and Python scripts with LGPL-2 licensing to serve as reference migration templates for the community.
2026-09-03 14:16:53 -06:00
David RichardsonandGitHub 9cacb134c7 Merge pull request #3307 from enquora/manual-tests-navigation-viewer
Manual tests navigation viewer
2026-09-03 12:36:11 -06:00
David Richardson 17e5d32621 rename Jakefile migration script
prefix with wip- to indicate it is not yet stable.
2026-09-03 12:19:20 -06:00
David Richardson f5b305317f Improve colour scheme
Match navigation bar/masthead to https://cappuccino.dev
Highlight table row on hover
Increase visual distinction of test buttons using colour
2026-09-03 11:26:10 -06:00
David Richardson 9677609340 Improve index.html presentation
Add logotype as self-contained data URI
Improve fonts for legibility
Improve table styling
2026-09-03 10:47:29 -06:00
David Richardson ab8e47fd01 Scripts to generate and serve index HTML file for all tests
Node.js, requires no additional interpreter or dependencies.
Generates a single index.html file at root which displays launch links for all tests.
2026-09-03 09:45:37 -06:00
David Richardson a2181c88d7 Improve migrate-jakefiles.py script
Move to ./Tests/Manual
Hard-code root path to ./
Exclude sibling Jakefile
2026-09-03 09:24:53 -06:00
David Richardson 1a9ff9ffb7 Provision framework dependencies for manual integration tests
Previously, the Tests/Manual integration suite lacked an automated mechanism to provision the required Frameworks directories, leaving test applications unexecutable or reliant on stale, manually copied dependencies.

This commit introduces a Jake task to automatically provision a shared, symlinked Frameworks cache for all manual test applications, ensuring dependencies remain synchronized with the current framework build. Additionally, it adds documentation detailing the architecture and execution of the manual test suite.
2026-09-02 18:32:11 -06:00
David Richardson cc4b0e2540 Jakefile Narwhal => Node migration script, initial effort 2026-09-01 20:03:32 -06:00
David RichardsonandGitHub 87e0a6fe34 Merge pull request #3304 from cappuccino/ToolsTest-disable
Disable ToolsTest unit
2026-08-31 10:13:32 -06:00
David Richardson 45d9f10a89 Disable ToolsTest unit
Unable to modify test so that it is successful both locally and on GitHub.
Tests Node hosted tooling which is deprecated and will not receive further support.

Expedient solution is to simply disable the test.
2026-08-31 10:11:39 -06:00
David RichardsonandGitHub 93937e8b1b Merge pull request #3303 from enquora/Address-failing-array-tests
test: disable unasserted performance benchmarks in CPArray suites
2026-08-31 09:52:15 -06:00
David Richardson bdc2b7cd67 test: disable unasserted performance benchmarks in CPArray suites
Prefix unasserted performance tests with 'disabled_' in CPArrayControllerTest
and CPArrayPerformanceTest to eliminate wall-clock timing noise from standard
CI runs.

Add a dummy assertion to CPArrayPerformanceTest to maintain runner
compatibility without executing obsolete micro-benchmarks.
2026-08-31 09:49:44 -06:00
David RichardsonandGitHub b6ba250f82 Merge pull request #3301 from enquora/_CPAutocompleteMenu-fix-cold-build-warning
Fix undeclared _clientView instance variable in CPRulerView
2026-08-28 20:58:43 -06:00
David Richardson 4a5a25bb65 Fix undeclared _clientView instance variable in CPRulerView
Declare _clientView within the CPRulerView instance variable block.

This resolves a cold build compilation failure where the Objective-J parser flags assignments to the undeclared variable as illegal global variable creations.
2026-08-28 20:57:03 -06:00
David RichardsonandGitHub bc1889c7d4 Merge pull request #3300 from enquora/Update-GitHub-Actions
ci: Restrict Node.js test matrix to version 24.x
2026-08-28 20:39:02 -06:00
David Richardson 5471c38905 ci: Restrict Node.js test matrix to version 24.x
Modify GitHub Actions workflows to execute strictly against Node.js 24.x.
* Reduced the execution matrix in BuildAndTest.yml to remove deprecated and redundant runtimes (versions 20 through 23).
* Explicitly pinned the setup-node step in main-branch-publish-manual-tests.yaml to version 24.x.

This eliminates unnecessary CI resource consumption. Node 24 is the active LTS, and exhaustive version testing is no longer warranted for the legacy infrastructure given the transition to the native Go toolchain.
2026-08-28 20:36:45 -06:00
David RichardsonandGitHub 9866dd49bb Merge pull request #3299 from enquora/CPTimeZoneTest-fix
Fixes: CPTimeZoneTest, CPTimeZone, and CPDate
2026-08-28 20:01:10 -06:00
David Richardson a6969bc756 Fix CPDate string initialization to respect explicit time zone offsets
The initWithString: method previously initialized the underlying JavaScript Date object using the local-time constructor (new Date(year, month, day)).
This caused the resulting absolute time to be corrupted by the host engine's local time zone configuration, effectively ignoring the explicit time zone offset provided in the initialization string.

The implementation was a fundamental violation of the canonical contract, as established by NSDate and to be followed by CPDate.

This commit refactors the parsing logic to use Date.UTC(), guaranteeing that the parsed components are evaluated independently of the host environment.

Additionally, it eliminates a redundant RegExp allocation during pattern matching and updates the API documentation to explicitly state that offset resolution is decoupled from both CPTimeZone and the host's local time rules.
2026-08-28 19:59:11 -06:00
David Richardson 23dff002a4 Fix CPTimeZone initialization and bridge prototype exceptions
The object returned by the host engine's Intl.supportedValuesOf("timeZone") lacks standard JavaScript Array prototype methods (e.g., slice, indexOf) when crossing the runtime bridge.

This previously caused exceptions in testKnownTimeZoneNamesUsesIntlWhenAvailable.

This commit resolves the exceptions by utilizing primitive JavaScript property access and zero-indexed iteration to construct a local array. This architecture safely isolates the class from bridge prototype limitations and CPArray dependencies.

Additionally, it explicitly appends the legacy aliases "GMT" and "UTC" during the manual array construction. Engines adhering to canonical IANA identifiers omit these aliases, which caused localTimeZone initialization to abort in UTC-bound CI environments.

Guaranteeing their presence resolves the null evaluation failure in testSecondsFromGMTForDate.
2026-08-28 19:46:46 -06:00
David Richardson 03b875697e Fix CPTimeZone initialization failure in UTC environments
Intl.supportedValuesOf("timeZone") returns canonical IANA identifiers, omitting legacy aliases such as "GMT" and "UTC". This causes localTimeZone to evaluate to nil when the host engine operates in a UTC time zone, as abbreviationDictionary statically maps these aliases.

We explicitly append "GMT" and "UTC" to the dynamic knownTimeZoneNames array. This ensures invariant preservation for static dictionary lookups, guarantees successful object initialization across all UTC-bound environments, and resolves the evaluation in testSecondsFromGMTForDate on CI runners.
2026-08-28 19:22:56 -06:00
David Richardson d717e07d5c Add offset-based fallback for local-zone abbreviation resolution
* Fix a GitHub CI failure on a UTC-configured runner. Name-based resolution previously found no candidate, and abbreviationForDate returned no value.

* Add a fallback mechanism that uses the system's current UTC offset when a name match fails.

* Ensure correct abbreviation resolution, regardless of the zone name string that the runtime reports.
2026-08-28 19:07:32 -06:00
David Richardson c39f03823a Replace date-string parsing in CPTimeZone with Intl API
* Update abbreviationForDate and _abbreviationForNameAndDate to read the short time zone name directly from Intl.DateTimeFormat(...).formatToParts(). This replaces regular expression parsing of date strings.

 * Update knownTimeZoneNames to use Intl.supportedValuesOf('timeZone') to retrieve the complete IANA time zone set. The method falls back to the legacy 48-city list if Intl is unavailable.

* Replace duplicate regular expression logic in testInitWithNameRespectsDaylightSaving with a direct Intl call.

* Add pinning tests for the recently corrected offsets and the unchanged MSD value.

* Add a feature-detected test for the knownTimeZoneNames upgrade.

* Add two disabled tests to document unresolved gaps: non-English localizedName outputs and ambiguous reverse offset lookups.

* The full IANA-identity redesign as a separate task. Abbreviation-keyed lookups, colliding offsets, and English-only localizedName limitations remain unresolved in this interim fix.
2026-08-28 14:19:46 -06:00
David Richardson f3a04f23f8 Fix incorrect UTC offsets in CPTimeZone abbreviation table
* Correct timeDifferenceFromUTC values for MDT, WAT, NZDT, NZST, and WIT.

* Update MSK to the current constant UTC+3 offset. Russia abolished Daylight Saving Time in 2014.

* Leave MSD unchanged. Add an inline comment explaining that Moscow Summer Time no longer exists and assigning a new offset would fabricate data.

* Track future work to replace this static table with Intl-backed lookup from browsers’ internal values.

This future lookup will include a protocol seam for:
* runtimes without complete timezone data.
* developers who cannot tolerate non-current information from browser update latency or have other individual need.
2026-08-28 13:59:05 -06:00
David RichardsonandGitHub 0089785713 Merge pull request #3281 from enquora/CPTreeNode-improve-tests
Improve and extend CPTreeNode tests

Structural gaps still exist in CPTreeNode, covered as comments in CPTreeNodeTest.
Issue to be created for these.
2026-08-28 13:38:57 -06:00
David Richardson 3a140b07fe Fix child-node index arithmetic and expand CPTreeNode test coverage
CPTreeNode.j:
* Correct index calculations in insertObject:inChildNodesAtIndex: and replaceObjectInChildNodesAtIndex:withObject:. Nodes no longer misplace during same-parent or cross-parent moves.
 * Update _removeChildNode: to call removeObjectFromChildNodesAtIndex:. This ensures detaches notify childNodes observers on the original parent.

CPTreeNodeTest.j:
* Add test coverage for same-parent replacement, cycle rejection on replace-path, KVO during cross-parent moves, mutableChildNodes proxy operations, insertion bounds, non-recursive sorting, isLeaf, and descendantNodeAtIndexPath: edge cases.
* Disable same-parent childNodes KVO test. CPKeyValueObserving.j coalesces nested change notifications on the same key, which drops the inner removal event.
* Disable parentNode KVO test. No setParentNode: method exists to instrument.
* Add inline comments to both disabled tests explaining these mechanisms.
2026-08-28 13:35:27 -06:00
David Richardson 74039c799a Merge branch 'main' into CPTreeNode-improve-tests 2026-08-27 22:05:19 -06:00
David RichardsonandGitHub b3e2ed1071 Merge pull request #3297 from enquora/CPException-fix-missing
Fix undefined function call in CPArray range/argument exceptions
2026-08-27 22:04:25 -06:00
David Richardson 1f8e75388d Fix undefined function call in CPArray range/argument exceptions
Foundation/CPException.j: _CPRaiseRangeException and _CPRaiseInvalidArgumentException called METHOD_CALL_STRING(), undefined anywhere.

Every out-of-bounds CPArray access threw an
uncatchable JS ReferenceError instead of CPRangeException/
CPInvalidArgumentException.
Fixed to call the existing _CPMethodCallString(anObject, aSelector).

Tests/Foundation/CPArrayTest.j, CPMutableArrayTest.j: the defect
was invisible because test_objectAtIndex_ and test_objectsAtIndexes_
checked only that something was thrown, not what.
Added exception-identity checks, and fixed ConcreteArray/ConcreteMutableArray's
objectAtIndex: overrides, which threw a plain string and would have
failed the new checks.
2026-08-27 21:57:37 -06:00
David Richardson ee0c6fdf2d CPTreeNodeTest: fix child-move index arithmetic; correct a flawed test expectation
CPTreeNode: same-parent child moves no longer misplace the moved node — this corrects a defect in the class itself, properly exposed by the test.

Separately, the unit test's expectation for a root node's index path was itself wrong.
Corrected to match the class's existing, contract-correct behavior.

Test unit now runs cleanly.
2026-08-27 16:54:31 -06:00
David Richardson 54b7d24081 Merge branch 'main' into CPTreeNode-improve-tests 2026-08-27 16:02:08 -06:00
daboe01andGitHub bf76c7d2c7 Merge pull request #3241 from daboe01/setBoundsSize-fix
Fixed: scale accumulation in scaleUnitSquareToSize: when setting bounds size
2026-08-27 22:37:45 +02:00
daboe01andGitHub bad82ee587 Merge pull request #3296 from daboe01/pageunloadfix2
fix: replace legacy `unload` event listener cleanup with `pagehide`
2026-08-27 19:40:57 +02:00
daboe01 16beb1a86c fix: replace legacy unload event listener cleanup with pagehide 2026-08-27 19:28:31 +02:00
David Richardson 690b79f113 Revert "Merge pull request #3260 from enquora/modernize-CALayer"
This reverts commit 8134b6dff7, reversing
changes made to 7083f7b268.
2026-08-27 11:20:41 -06:00
David Richardson 4d13785119 Revert "Merge pull request #3257 from enquora/modernize-CABackingStore"
This reverts commit 5bdac195fc, reversing
changes made to 8134b6dff7.
2026-08-27 11:20:30 -06:00
David Richardson 4f29b77d65 Revert "Merge pull request #3261 from enquora/modernize-CPPlatformpasteboard"
This reverts commit 10351c3d4a, reversing
changes made to 5bdac195fc.
2026-08-27 11:20:20 -06:00
David Richardson be08bed01e Revert "Merge pull request #3256 from enquora/modernize-cgcontext-canvas"
This reverts commit e107e2d804, reversing
changes made to 10351c3d4a.
2026-08-27 11:19:56 -06:00
daboe01andGitHub e107e2d804 Merge pull request #3256 from enquora/modernize-cgcontext-canvas
Modernize CGContextCanvas.j: Remove CPP macros and legacy fallbacks
2026-08-27 10:06:21 +02:00
daboe01andGitHub 10351c3d4a Merge pull request #3261 from enquora/modernize-CPPlatformpasteboard
Refactor CPPlatformPasteboard: Remove CPP macros and legacy IE fallbacks
2026-08-27 10:00:33 +02:00
daboe01 67b4798caa formatting 2026-08-26 20:57:53 +02:00
daboe01 301e940236 formatting 2026-08-26 20:56:38 +02:00
daboe01andGitHub 5bdac195fc Merge pull request #3257 from enquora/modernize-CABackingStore
Refactor CABackingStore: Eliminate preprocessor directives and legacy…
2026-08-26 20:52:08 +02:00
daboe01andGitHub 8134b6dff7 Merge pull request #3260 from enquora/modernize-CALayer
Modernize CALayer, remove macro defined access in favour of ivar
2026-08-26 20:50:47 +02:00
daboe01andGitHub 7083f7b268 Merge pull request #3226 from daboe01/CPLanguageModel-and-friends
New: CPSystemLanguageModel and CPLanguageModelSession
2026-08-26 20:48:59 +02:00
daboe01andGitHub 72d2b22088 Merge pull request #3278 from daboe01/disable-googletranslator
new: prevent google translator in cappuccino
2026-08-26 20:47:40 +02:00
daboe01andGitHub acf410776d Merge pull request #3279 from daboe01/whitespace-cleanup
fixed: usage of tab character instead of 4 spaces
2026-08-26 20:46:52 +02:00
daboe01andGitHub 82f82190a1 Merge pull request #3294 from daboe01/right-contraint
new: constrain tab stop marker movement between preceding text and adjacent tab stops
2026-08-26 19:18:04 +02:00
daboe01 f85ac1ea10 new: constrain tab stop marker movement between preceding text and adjacent tab stops 2026-08-26 19:16:12 +02:00
daboe01andGitHub b6a245c852 Merge pull request #3293 from daboe01/tabstop-contraint
new: constrain tab stop marker dragging to preceding text boundary
2026-08-26 19:05:09 +02:00
daboe01 874792fa6e new: constrain tab stop marker dragging to preceding text boundary 2026-08-26 19:01:32 +02:00
daboe01andGitHub 378f7bbda1 Merge pull request #3292 from daboe01/tabstop-fix
fixed: resolve tab stop skipping and dragging positioning
2026-08-26 17:59:09 +02:00
daboe01 ca13595e37 formatting 2026-08-26 17:58:39 +02:00
daboe01 22a43bcfc5 fixed: resolve tab stop skipping and dragging positioning 2026-08-26 17:56:48 +02:00
daboe01andGitHub 4c54e6cd31 Merge pull request #3291 from daboe01/rulerviewfix
fixed: indentation paddle dragging and clientView binding
2026-08-26 17:19:43 +02:00
daboe01 9ffa4decd4 fixed: indentation paddle dragging and clientView binding 2026-08-26 17:06:34 +02:00
daboe01andGitHub 389ad61251 Merge pull request #3290 from daboe01/treecontroller-selection-fix
fixed: fix selection bindings and correct KVO dependencies in CPTreeController
2026-08-25 22:36:07 +02:00
daboe01 d8dd2e8636 improved: manual test for treecontroller 2026-08-25 22:18:37 +02:00
daboe01 8d1429d063 proxy fix 2026-08-25 21:09:51 +02:00
daboe01 d1b5c08272 fixed: KVO observer was missing 2026-08-25 21:03:07 +02:00
daboe01 6ef3cf776f new: selection tests 2026-08-25 20:39:34 +02:00
daboe01 0a6f2e569a fixed: fix selection bindings and correct KVO dependencies in CPTreeController 2026-08-25 20:35:09 +02:00
David Richardson ae409d05c1 Convert tabs to spaces 2026-08-21 09:21:51 -06:00
David Richardson 6b732da749 Merge branch 'main' into CPTreeNode-improve-tests 2026-08-21 08:54:19 -06:00
David RichardsonandGitHub 45741365f4 Merge pull request #3289 from enquora/IvarTest-fix-test-warnings
Include inline comment for IvarTest wrt warning
2026-08-20 22:02:46 -06:00
David Richardson 2c86863258 Include inline comment for IvarTest wrt warning
In this one case, a warning is the expected output and is the only viable way to test the case.
See inline comment at top of IvarTest.j for explanation.
2026-08-20 22:00:53 -06:00
David RichardsonandGitHub 8994806789 Merge pull request #3288 from enquora/CPSetTest-fix-test-warning
Fix static analysis warnings for unused iteration variables in Founda…
2026-08-20 21:23:40 -06:00
David Richardson e51f0df1a6 Fix static analysis warnings for unused iteration variables in Foundation tests
The `for...of` syntax tests in `CPSetTest` and `CPDictionaryTest` iterate over empty collections. This leaves the bound variables unread, which triggers the static analyzer and fails the zero-warning CI policy.

Standard Javascript idioms for handling blank identifiers (such as `_` or pure evaluation via `void`) are either unsupported or cause AST collisions within the Node.js parser. The variables are now explicitly evaluated using native Objective-J message sends. This registers a read operation for the analyzer, preserves the legacy parser's structural expectations, and ensures the runtime state remains pristine.
2026-08-20 21:21:56 -06:00
David RichardsonandGitHub a8a0c64a2a Merge pull request #3287 from enquora/_CPFontPanelPreviewView-address-build-warning
Clear _CPFontPanelPreviewView build warning
2026-08-20 20:15:40 -06:00
David Richardson 5449561fe2 Clear _CPFontPanelPreviewView build warning
CPFontPanel.j used _CPFontPanelPreviewView as an ivar type and instantiated it before its own @implementation, later in the same file, with no forward declaration. Added @class _CPFontPanelPreviewView, alongside the file's existing forward declarations.

This removes all build warnings.
2026-08-20 20:14:14 -06:00
David RichardsonandGitHub 073fae648e Merge pull request #3286 from enquora/CPTabStopType-clear-build-warning
Remove CPTabStopType build warning
2026-08-20 20:07:33 -06:00
David Richardson 6121caf75d Remove CPTabStopType build warning
CPParagraphStyle.j defined CPTabStopType's four constants directly, with a comment noting they were "missing" but no @typedef for the type itself.

_CPRTFParser.j uses CPTabStopType as an ivar type and imports CPParagraphStyle.j directly, so the fix belongs in the defining file.

Added the missing @typedef CPTabStopType, same pattern as CPRulerOrientation.
2026-08-20 20:05:38 -06:00
David RichardsonandGitHub 253d6669eb Merge pull request #3285 from enquora/CPRulerOrientation-address-build-warning
Address CPRulerOrientation build warning
2026-08-20 19:59:14 -06:00
David Richardson 533a1d46eb Address CPRulerOrientation build warning
CPRulerView.j declared CPRulerOrientation's constants directly,
with only a comment claiming it was a typedef.

No @typedef directive existed, so the type was unknown wherever used as an ivar or parameter type.

Added the missing @typedef CPRulerOrientation, matching the pattern already used for CPTextAlignment in CPText.j.
2026-08-20 19:57:35 -06:00
David Richardson c0b4fbfeb1 Merge branch 'main' into CPTreeNode-improve-tests 2026-08-20 16:31:42 -06:00
David RichardsonandGitHub f6d96bbf17 Merge pull request #3284 from enquora/CPTextTab-resolve-build-warnings
Add missing CPTextTab forward declaration in CPRulerView
2026-08-20 16:30:02 -06:00
David Richardson 52658b636a Add missing CPTextTab forward declaration in CPRulerView
CPRulerView.j referenced CPTextTab (isKindOfClass: and alloc/init) without a declaration.

Added @class CPTextTab; to resolve the symbol reference. A forward declaration was utilized rather than importing CPParagraphStyle.j to minimize dependency coupling, despite the absence of a circular import risk.

Resolves build warning.
2026-08-20 16:28:02 -06:00
David RichardsonandGitHub c32994af66 Merge pull request #3283 from enquora/_CPTableTextAttachment-resolve-build-warning
Fix build warnings in _CPTableTextAttachment
2026-08-20 15:35:15 -06:00
David Richardson a1ac821678 Fix build warnings in _CPTableTextAttachment
Replaced the CPTextView.j import with forward declarations for CPTextView and CPTextContainer (the latter previously reached via the removed import).

The previous import graph created a circular dependency (_CPTableTextAttachment.j -> CPTextView.j -> _CPRTFParser.j / _CPRTFProducer.j -> _CPTableTextAttachment.j).

This caused the legacy compiler to emit a class registration warning during clean builds, as the class was unregistered at the time of resolution. Incremental builds masked this warning via cached symbol tables.

Using forward declarations breaks the cycle and ensures warning-free clean and incremental builds.
2026-08-20 15:31:45 -06:00
David RichardsonandGitHub 548ed836a0 Merge pull request #3282 from enquora/CPScrollView-address-build-warnings
Remove CPScrollView build warning
2026-08-19 20:28:51 -06:00
David Richardson 78a0ebab6e Remove CPScrollView build warning
CPRulerView.j did not import CPScrollView.
A direct import would create a cycle, since CPScrollView.j already imports CPRulerView.j.
Added a forward declaration instead.
2026-08-19 20:23:33 -06:00
David Richardson fad987641b Improve and extend CPTreeNode tests 2026-08-19 11:41:39 -06:00
David RichardsonandGitHub 3aa053e7a8 Merge pull request #3280 from enquora/CPOutlineView-build-warnings-cleanup
Fix CPOutlineView build warnings and audit+improve CPTreeNode
2026-08-19 11:05:17 -06:00
David Richardson 7aebf371e9 Back out CPTreeNodeTest for addition under separate PR
GitHub CI uses existing CPTreeNodeTest and fails when new tests are included.

New tests must be added as a separate PR, independently of the changes in CPTreeNode.j
2026-08-19 10:31:15 -06:00
David Richardson ac8ce3eec3 Fix CPOutlineView build warnings and audit+improve CPTreeNode
CPOutlineView.j used CPTreeNode before AppKit.j imported it. This
triggered build warnings. This commit adds an import.

- Add @import "CPTreeNode.j" to CPOutlineView.j.

While addressing this warning, CPTreeNode was audited and found to have an algorithmic complexity regression and multiple structural inconsistencies in the KVO support code.

The test suite for CPTreeNode was Spartan in the extreme.

All of these have been addressed - see inline notes for details.
CPTreeNode is subject to further audit.
2026-08-19 10:11:20 -06:00
daboe01 567ec55875 fixed: usage of tab character instead of 4 spaces 2026-08-18 19:46:22 +02:00
daboe01 59569dcb34 new: prevent google translator in cappuccino 2026-08-17 18:20:09 +02:00
David RichardsonandGitHub 3b09a5bde6 Merge pull request #3277 from enquora/CPStackView-build-warnings-cleanup
Fix CPStackView compile errors and add minimal correctness fixes
2026-08-14 20:12:35 -06:00
David Richardson 01f7f9b601 Fix CPStackView compile errors and add minimal correctness fixes
CPStackView.j emited warnings during builds and tests which constituted
either real errors or indeterminate state.

These included four types that did not exist: CPUserInterfaceLayoutOrientation, CPLayoutAttribute,
CPEdgeInsets, and CPMapTable.

The missing types are now present.

- Add local typedefs for CPUserInterfaceLayoutOrientation and
  CPLayoutAttribute. Numeric values match the equivalent Cocoa
  constants.
- Add CPEdgeInsets as an alias for the existing CGInset struct.
  Add CPEdgeInsetsMake and CPEdgeInsetsEqualToEdgeInsets.
- Import Foundation/CPMapTable.j. CPMapTable already exists in
  Foundation.

This commit also fixes two logic errors found during review.

- arrangedSubviews did not match the true view order after
  insert or remove. Add _rebuildArrangedSubviews and call it
  from every method that changes view order.
- Trailing-gravity layout added one extra spacing gap past the
  last view. This shifted the returned layout boundary. Spacing
  is now added before each view, not after.

This commit adds one new property.

- Add a distribution property with storage and accessors. It
  has no effect on layout yet. This makes the property honest:
  before this change, the type existed but no property did.

The manual test, ./Tests/Manual/CPStackViewTest, used a Narwhal-era
Jakefile and would not build. This has been updated to reflect
usage under the Node toolchain.

This commit adds CPStackView.j to AppKit.j. The file was never
imported. Only the build script's wildcard file list included
it.

This commit adds a header comment to CPStackView.j. The comment
states that the class is a placeholder. It lists known
limitations. It states that a constraint-solver based
replacement is planned.

Why: CPStackView must compile and pass tests before capp-build
can use this tree as a build reference, and a class with unstated
limits invites misuse.
2026-08-14 20:09:17 -06:00
David RichardsonandGitHub 907ff374e6 Merge pull request #3273 from enquora/add-property-copy-test
Add test for @accessors(copy)
2026-08-13 16:51:51 -06:00
David Richardson a786418e19 Add test for @accessors(copy)
@accessors(copy) had no test in ./Tests.
The compiler's copy-accessor setter behaviour was unverified.

Add CopyAccessorTest.j to Tests/Objective-J. It asserts three
properties of the generated setter:
- the stored value is not identical to the assigned object
- the stored value is unaffected by later mutation of the original
- the stored value is equal in content to the original at assignment

This is added both as a matter of good coverage for the existing toolchain and so that new tooling has a reference for a piece of foundational behaviour.
2026-08-13 16:42:57 -06:00
daboe01andGitHub 6448f7ea12 Merge pull request #3272 from daboe01/pageunloadfix
replace unload event listener with pagehide
2026-08-13 14:16:09 +02:00
daboe01 ce2803f354 replace unload event listener with pagehide and add DOM safety checks 2026-08-12 16:58:12 +02:00
David RichardsonandGitHub 7e3d22b10e Merge pull request #3270 from enquora/CPMapTable-fix-build-warning
Fix legacy compiler warnings in CPMapTable
2026-08-12 07:50:43 -06:00
David Richardson 84dab55993 Fix legacy compiler warnings in CPMapTable
Using ES6 destructuring in the `for...of` loop declaration
(`var [key, value] of _map.entries()`) causes the legacy
Objective-J compiler to emit "uninitialized global variable"
warnings for `key` and `value`.

This raises concerns about variable scoping and generates
unacceptable noise in the CI pipeline. To resolve the warnings,
the loop has been restructured to use standard array indexing
inside the loop body.

A TODO has been added to revert to ES6 destructuring once the
legacy compiler is retired.
2026-08-11 13:04:05 -06:00
David Richardson c97839905c Refactor CPPlatformPasteboard: Remove CPP macros and legacy IE fallbacks
As part of the ongoing technical debt cleanup, this commit modernizes
CPPlatformPasteboard.j by removing its reliance on the C-preprocessor
and stripping obsolete Internet Explorer compatibility code.

Key changes:
- Inlined the `SUPPRESS_CAPPUCCINO_CUT_FOR_EVENT` and `SUPPRESS_CAPPUCCINO_PASTE_FOR_EVENT`
  macros directly to standard property assignments.
- Removed the IE `srcElement` fallback in `hasEditableTarget`.
- Removed the IE `attachEvent`/`detachEvent` branch in `createDOMElements`,
  keeping only the standard `addEventListener` path.
- Replaced `var` with `const` and `let` for proper block scoping and immutability.
- Retained the `#if PLATFORM(DOM)` guards to protect the Node.js CI during
  the phased modernization effort.

All changes are annotated with `// MODIFICATION:` comments for clear
review tracking.
2026-07-23 17:22:36 -06:00
David Richardson d34149b236 Modernize CALayer, remove macro defined access in favour of ivar 2026-07-23 17:02:28 -06:00
David Richardson 6e0d2f97cc Refactor CABackingStore: Eliminate preprocessor directives and legacy compatibility
* Remove PIXEL macro and replace with native inline string concatenation.
* Remove CPHTMLCanvasFeature branching and obsolete CGBitmapGraphicsContextCreate fallback.
* Convert var declarations to block-scoped const.
* Standardize global function assignments to explicit C-style declarations.
* Remove unused CPCompatibility.j import.
2026-07-23 14:48:08 -06:00
David Richardson e288ea3690 Modernize CGContextCanvas.j: Remove CPP macros and legacy fallbacks
As part of the ongoing effort to remove the C-preprocessor (CPP) from
the new Lisette compiler pipeline, this commit refactors CGContextCanvas.j
to use native JavaScript and HTML5 Canvas APIs directly.

Key changes:
- Removed all `#define` macros that wrapped standard Canvas API calls
  (e.g., `_CGContextMoveToPointCanvas`). Direct method invocations are
  now used, eliminating the preprocessor pass and simplifying AST generation.
- Removed the `CPJavaScriptCanvasTransformFeature` runtime check and its
  complex mathematical fallbacks (`scale_rotate`, `rotate_scale`, `eigen`).
  Modern target environments universally support native canvas transformations.
- Removed obsolete vendor-prefixed line dash properties (`webkitLineDash`,
  `mozDash`) in favor of the standard `setLineDash`.
- Replaced `var` with `const`/`let` for proper block scoping and immutability.
- Replaced the `ROUND` macro with standard `Math.round`.

These changes strip away 2008-era browser compatibility hacks and micro-optimizations, resulting in cleaner, more maintainable, and strictly browser-targeted code.
2026-07-21 16:54:35 -06:00
daboe01 cff2fd8ed9 optimized version 2026-06-20 17:51:11 +02:00
daboe01 344cbd0660 formatting 2026-06-20 15:01:35 +02:00
daboe01 e6e676c91e more safety checks 2026-06-20 15:00:21 +02:00
daboe01 5fd2f49266 safety checks 2026-06-20 14:55:27 +02:00
daboe01 10fb3e6d31 Fixed: scale accumulation in scaleUnitSquareToSize: when setting bounds size 2026-06-20 14:35:15 +02:00
daboe01 b2c5955e7d Merge remote-tracking branch 'upstream/main' into CPLanguageModel-and-friends 2026-06-17 16:21:24 +02:00
daboe01 2f63a5fd10 formatting 2026-06-17 08:25:24 +02:00
daboe01 b827310c75 Merge remote-tracking branch 'upstream/main' into CPLanguageModel-and-friends 2026-06-17 08:09:34 +02:00
daboe01 227eda9d92 new: spinner in manual test 2026-06-17 07:57:17 +02:00
daboe01 9fe0df74b3 formatting 2026-06-16 20:20:25 +02:00
daboe01 381c1880c2 cleanup 2026-06-16 20:15:34 +02:00
daboe01 5439e5d473 Merge remote-tracking branch 'upstream/main' into CPLanguageModel-and-friends 2026-06-16 19:48:16 +02:00
daboe01 6139216146 new: RTFProducer PROTOCOL METHOD ADDITIONS 2026-06-13 19:53:39 +02:00
daboe01 2c275b279b new: markdown support in manual test 2026-06-13 18:13:51 +02:00
daboe01 44f6e0fded cosmetic fixes 2026-06-13 16:38:40 +02:00
daboe01 e5278fd17e new: run loop management 2026-06-13 16:30:16 +02:00
daboe01 1bd3c93717 formatting 2026-06-13 16:22:34 +02:00
daboe01 afc6691f07 formatting 2026-06-13 16:02:17 +02:00
daboe01 8c41c7b8ac fixed: import semantics 2026-06-13 15:54:07 +02:00
daboe01 5445d42eba Merge remote-tracking branch 'upstream/main' into CPLanguageModel-and-friends 2026-06-13 15:47:01 +02:00
daboe01 8e04b172ce simplification of demo 2026-06-13 15:34:21 +02:00
daboe01 5fff34be91 new: CPLanguageModel and manual test 2026-06-13 14:50:38 +02:00
954 changed files with 40278 additions and 157268 deletions
+1 -1
View File
@@ -16,7 +16,7 @@ jobs:
strategy:
matrix:
node-version: [20.x, 21.x, 22.x, 23.x, 24.x]
node-version: [24.x]
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
steps:
@@ -26,6 +26,8 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v4
with:
node-version: 24.x
- run: echo "${PWD}/dist/cappuccino/bin" >> $GITHUB_PATH
- run: echo "${PWD}/dist/objective-j/bin" >> $GITHUB_PATH
+2
View File
@@ -26,3 +26,5 @@ node_modules
/dist/cappuccino/package.json
/dist/cappuccino/lib
/dist/cappuccino/bin
Tests/Manual/.Frameworks
/Tests/Manual/index.html
+1
View File
@@ -93,6 +93,7 @@
@import "CPSlider.j"
@import "CPSound.j"
@import "CPSplitView.j"
@import "CPStackView.j"
@import "CPStepper.j"
@import "CPTableColumn.j"
@import "CPTableView.j"
+3
View File
@@ -23,6 +23,7 @@
@import "CPButton.j"
@import "CPTableColumn.j"
@import "CPTableView.j"
@import "CPTreeNode.j"
@global CPApp
@@ -2531,6 +2532,8 @@ var colorForDisclosureTriangle = function(isSelected, isHighlighted)
selector:@selector(outlineViewSelectionDidChange:)
name:CPOutlineViewSelectionDidChangeNotification
object:aSource];
return self;
}
+ (void)unbind:(CPString)aBinding forObject:(id)anObject
+135 -18
View File
@@ -20,7 +20,80 @@
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* PLACEHOLDER IMPLEMENTATION — READ BEFORE USE OR MODIFICATION.
*
* This class lays out views by direct, procedural arithmetic. It has no
* constraint solver. It cannot compress, expand, or negotiate space among
* views the way NSStackView does; it only places views at their existing
* frame size, in order, separated by fixed spacing.
*
* Known, accepted limitations:
* - `distribution` is stored but has no effect on layout. Fill,
* FillEqually, FillProportionally, and EqualSpacing are unimplemented.
* - Center-gravity views are clamped against the Leading edge only; if
* Leading + Center + Trailing content overflows the container, Center
* views can overlap Trailing views instead of compressing.
* - `visibilityPriority:forView:` only supports the two extreme values
* (MustHold / NotVisible). Intermediate priorities are accepted but
* have no defined effect.
* - No guarantee is made of correctness beyond what a single manual
* test (Tests/Manual/CPStackViewTest) exercises: orientation, the
* three gravity areas, alignment switching, spacing, and hidden-view
* detachment. Insertion, removal, custom spacing, and the CPCoding
* archive path are implemented but not verified by that test.
*
* This exists to give AppKit a working CPStackView symbol now, not to be
* a durable design. It is expected to be replaced by a constraint-solver
* based implementation (Kiwi.js) when time permits. Do not build on its
* internal layout algorithm as if it were a stable foundation.
*/
@import "CPView.j"
@import <Foundation/CPMapTable.j>
// MARK: -
// MARK: Minimal local type definitions
//
// These types support this file only. They are not shared with the rest
// of AppKit. A future constraint-solver based Auto Layout engine will
// replace them. Numeric values match the equivalent Cocoa constants
// (NSUserInterfaceLayoutOrientation, NSLayoutAttribute) so that a later,
// solver-based CPLayoutAttribute can reuse these numbers without a
// renumbering pass.
@typedef CPUserInterfaceLayoutOrientation
CPUserInterfaceLayoutOrientationHorizontal = 0;
CPUserInterfaceLayoutOrientationVertical = 1;
@typedef CPLayoutAttribute
CPLayoutAttributeLeft = 1;
CPLayoutAttributeRight = 2;
CPLayoutAttributeTop = 3;
CPLayoutAttributeBottom = 4;
CPLayoutAttributeLeading = 5;
CPLayoutAttributeTrailing = 6;
CPLayoutAttributeWidth = 7;
CPLayoutAttributeHeight = 8;
CPLayoutAttributeCenterX = 9;
CPLayoutAttributeCenterY = 10;
@typedef CPEdgeInsets
/*!
Creates a CPEdgeInsets. Argument order matches Cocoa's NSEdgeInsetsMake
(top, left, bottom, right). Storage reuses the existing CGInset struct,
whose field order is (top, right, bottom, left).
*/
function CPEdgeInsetsMake(top, left, bottom, right)
{
return CGInsetMake(top, right, bottom, left);
}
function CPEdgeInsetsEqualToEdgeInsets(lhsInsets, rhsInsets)
{
return CGInsetEqualToInset(lhsInsets, rhsInsets);
}
// Gravity Areas
@typedef CPStackViewGravity
@@ -60,6 +133,7 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
{
CPUserInterfaceLayoutOrientation _orientation;
CPLayoutAttribute _alignment;
CPStackViewDistribution _distribution;
float _spacing;
CPEdgeInsets _edgeInsets;
@@ -99,6 +173,7 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
{
_orientation = CPUserInterfaceLayoutOrientationHorizontal;
_alignment = CPLayoutAttributeCenterY; // Default alignment
_distribution = CPStackViewDistributionGravityAreas;
_spacing = 8.0; // Default Cocoa spacing
_edgeInsets = CPEdgeInsetsMake(0, 0, 0, 0);
_detachesHiddenViews = YES;
@@ -169,6 +244,27 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
[self setNeedsLayout:YES];
}
/*!
The distribution mode for the stack view.
@note Not yet applied to layout. All views are laid out at their
existing frame size regardless of this value, pending the
constraint-solver based layout engine. The value is stored and
returned so client code can read back what was set.
*/
- (CPStackViewDistribution)distribution
{
return _distribution;
}
- (void)setDistribution:(CPStackViewDistribution)aDistribution
{
if (_distribution === aDistribution)
return;
_distribution = aDistribution;
[self setNeedsLayout:YES];
}
/*!
The minimum spacing, in points, between adjacent views in the stack view.
*/
@@ -233,6 +329,21 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
return _viewsLeading; // Leading or Top
}
/*!
Rebuilds _arrangedSubviews from the three gravity containers, in
Leading, Center, Trailing order. Call after any change to a gravity
container so _arrangedSubviews stays a correct, single source of truth
for ordering, rather than an incrementally and separately maintained
(and error-prone) copy.
*/
- (void)_rebuildArrangedSubviews
{
_arrangedSubviews = [[CPMutableArray alloc] init];
[_arrangedSubviews addObjectsFromArray:_viewsLeading];
[_arrangedSubviews addObjectsFromArray:_viewsCenter];
[_arrangedSubviews addObjectsFromArray:_viewsTrailing];
}
/*!
Adds a view to the end of the stack view gravity area.
*/
@@ -245,7 +356,7 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
[self removeView:aView];
[container addObject:aView];
[_arrangedSubviews addObject:aView];
[self _rebuildArrangedSubviews];
// Add as actual subview
if ([aView superview] !== self)
@@ -269,7 +380,7 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
else
[container insertObject:aView atIndex:index];
[_arrangedSubviews addObject:aView];
[self _rebuildArrangedSubviews];
if ([aView superview] !== self)
[self addSubview:aView];
@@ -284,13 +395,9 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
{
var container = [self _containerForGravity:gravity];
// Remove old views from arranged list and superview
// Remove old views from superview
for (var i = 0; i < [container count]; i++)
{
var oldView = container[i];
[oldView removeFromSuperview];
[_arrangedSubviews removeObject:oldView];
}
[container[i] removeFromSuperview];
[container removeAllObjects];
@@ -298,10 +405,10 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
{
var newView = views[i];
[container addObject:newView];
[_arrangedSubviews addObject:newView];
[self addSubview:newView];
}
[self _rebuildArrangedSubviews];
[self setNeedsLayout:YES];
}
@@ -316,7 +423,7 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
[_viewsLeading removeObject:aView];
[_viewsCenter removeObject:aView];
[_viewsTrailing removeObject:aView];
[_arrangedSubviews removeObject:aView];
[self _rebuildArrangedSubviews];
[aView removeFromSuperview];
@@ -531,6 +638,13 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
var limit = (dir === 1) ? count : -1;
var step = (dir === 1) ? 1 : -1;
// Spacing is applied as a gap *before* placing an element (except the
// first placed one), rather than trailing off the end after the last
// element. This keeps the returned cursor at the true content edge,
// with no phantom spacing past the final view.
var hasPlacedAny = false;
var pendingSpacing = 0;
for (; i !== limit; i += step)
{
var view = views[i];
@@ -538,6 +652,9 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
if (_detachesHiddenViews && [view isHidden])
continue;
if (hasPlacedAny)
cursor += (dir === 1) ? pendingSpacing : -pendingSpacing;
var viewFrame = [view frame];
var viewSizePrimary = isVert ? CGRectGetHeight(viewFrame) : CGRectGetWidth(viewFrame);
@@ -617,11 +734,10 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
if (dir === 1) {
originY = cursor;
cursor += sizeH + [self _spacingAfterView:view];
cursor += sizeH;
} else {
cursor -= sizeH;
originY = cursor;
cursor -= [self _spacingAfterView:view];
}
}
else
@@ -633,15 +749,17 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
if (dir === 1) {
originX = cursor;
cursor += sizeW + [self _spacingAfterView:view];
cursor += sizeW;
} else {
cursor -= sizeW;
originX = cursor;
cursor -= [self _spacingAfterView:view];
}
}
[view setFrame:CGRectMake(originX, originY, sizeW, sizeH)];
pendingSpacing = [self _spacingAfterView:view];
hasPlacedAny = true;
}
return cursor;
@@ -657,6 +775,7 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
{
_orientation = [aCoder decodeIntForKey:@"CPStackViewOrientation"];
_alignment = [aCoder decodeIntForKey:@"CPStackViewAlignment"];
_distribution = [aCoder decodeIntForKey:@"CPStackViewDistribution"];
_spacing = [aCoder decodeFloatForKey:@"CPStackViewSpacing"];
_edgeInsets = [aCoder decodeObjectForKey:@"CPStackViewEdgeInsets"]; // Assuming CPEdgeInsets supports obj coding or manual decode
if (!_edgeInsets) _edgeInsets = CPEdgeInsetsMake(0,0,0,0);
@@ -668,10 +787,7 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
_viewsTrailing = [aCoder decodeObjectForKey:@"CPStackViewViewsTrailing"] || [];
// Rebuild arranged subviews cache
_arrangedSubviews = [[CPMutableArray alloc] init];
[_arrangedSubviews addObjectsFromArray:_viewsLeading];
[_arrangedSubviews addObjectsFromArray:_viewsCenter];
[_arrangedSubviews addObjectsFromArray:_viewsTrailing];
[self _rebuildArrangedSubviews];
_customSpacings = [aCoder decodeObjectForKey:@"CPStackViewCustomSpacings"] || [[CPMapTable alloc] init];
_visibilityPriorities = [[CPMapTable alloc] init]; // usually not persisted
@@ -684,6 +800,7 @@ var CPStackViewSpacingUseDefault = 3.40282347e+38; // FLT_MAX
[super encodeWithCoder:aCoder];
[aCoder encodeInt:_orientation forKey:@"CPStackViewOrientation"];
[aCoder encodeInt:_alignment forKey:@"CPStackViewAlignment"];
[aCoder encodeInt:_distribution forKey:@"CPStackViewDistribution"];
[aCoder encodeFloat:_spacing forKey:@"CPStackViewSpacing"];
[aCoder encodeObject:_edgeInsets forKey:@"CPStackViewEdgeInsets"];
[aCoder encodeBool:_detachesHiddenViews forKey:@"CPStackViewDetachesHiddenViews"];
+1
View File
@@ -36,6 +36,7 @@
@class CPLayoutManager
@class CPTextContainer
@class CPFontManager
@class _CPFontPanelPreviewView
/*
Collection indexes
+1 -1
View File
@@ -26,7 +26,7 @@
CPParagraphStyleAttributeName = @"CPParagraphStyleAttributeName";
// Define missing global tab stop type constants
@typedef CPTabStopType
CPLeftTabStopType = 0;
CPRightTabStopType = 1;
CPCenterTabStopType = 2;
+112 -151
View File
@@ -28,17 +28,18 @@
@import "CPMenu.j"
@import "CPMenuItem.j"
// Orientations matching AppKit standards
// typedef enum CPRulerOrientation
CPHorizontalRuler = 0,
CPVerticalRuler = 1,
CPRulerOrientationHorizontal = 0,
CPRulerOrientationVertical = 1
@typedef CPRulerOrientation
CPHorizontalRuler = 0;
CPVerticalRuler = 1;
CPRulerOrientationHorizontal = 0;
CPRulerOrientationVertical = 1;
@class CPRulerView;
@class CPScrollView;
@class CPTextTab;
// MARK: - CPRulerMarker (Interactive Handles with Dynamic Alignment Icons)
// MARK: - CPRulerMarker
@implementation CPRulerMarker : CPView
{
@@ -57,13 +58,18 @@ CPRulerOrientationVertical = 1
_imageValue = anImageValue;
_representedObject = anObject;
// CRITICAL: Subviews must not intercept mouse events so CPRulerView receives all mouseDragged: events
[self setHitTests:NO];
_label = [[CPTextField alloc] initWithFrame:CGRectMake(0, 0, 12, 12)];
[_label setFont:[CPFont systemFontOfSize:10.0]];
[_label setTextColor:[CPColor colorWithWhite:0.2 alpha:1.0]];
[_label setAlignment:CPCenterTextAlignment];
[_label setHitTests:NO];
[self addSubview:_label];
_customHandleView = [[CPView alloc] initWithFrame:CGRectMakeZero()];
[_customHandleView setHitTests:NO];
[self addSubview:_customHandleView];
[self updateMarkerIcon];
@@ -88,8 +94,6 @@ CPRulerOrientationVertical = 1
[self updateMarkerIcon];
}
// Dynamically sets the Unicode triangle direction based on the alignment or indent type,
// or draws custom split-height grab handles for indentation controls.
- (void)updateMarkerIcon
{
var isIndentMarker = (_representedObject === @"CPFirstLineIndent" || _representedObject === @"CPHeadIndent");
@@ -101,28 +105,19 @@ CPRulerOrientationVertical = 1
var frame = [self bounds];
[_customHandleView setFrame:CGRectMake(0, 0, frame.size.width, frame.size.height)];
// Remove old internal rendering to update cleanly
[[_customHandleView subviews] makeObjectsPerformSelector:@selector(removeFromSuperview)];
var isFirstLine = (_representedObject === @"CPFirstLineIndent");
[_customHandleView setBackgroundColor:[CPColor colorWithWhite:0.45 alpha:1.0]];
// Dark outline/border representation
[_customHandleView setBackgroundColor:[CPColor colorWithWhite:0.5 alpha:1.0]];
// Inner fill (top handle is lighter, bottom is slightly darker)
var innerView = [[CPView alloc] initWithFrame:CGRectMake(1.0, 1.0, frame.size.width - 2.0, frame.size.height - 2.0)];
if (isFirstLine)
[innerView setBackgroundColor:[CPColor colorWithWhite:0.92 alpha:1.0]];
else
[innerView setBackgroundColor:[CPColor colorWithWhite:0.80 alpha:1.0]];
var innerView = [[CPView alloc] initWithFrame:CGRectMake(1.0, 1.0, Math.max(1.0, frame.size.width - 2.0), Math.max(1.0, frame.size.height - 2.0))];
[innerView setHitTests:NO];
[innerView setBackgroundColor:isFirstLine ? [CPColor colorWithWhite:0.95 alpha:1.0] : [CPColor colorWithWhite:0.80 alpha:1.0]];
[_customHandleView addSubview:innerView];
// Horizontal indicator line to visually guide drag interactions
var gripLine = [[CPView alloc] initWithFrame:CGRectMake(Math.floor(frame.size.width / 2.0) - 1.0, 2.0, 1.0, frame.size.height - 4.0)];
[gripLine setBackgroundColor:[CPColor colorWithWhite:0.6 alpha:1.0]];
var gripLine = [[CPView alloc] initWithFrame:CGRectMake(Math.floor(frame.size.width / 2.0) - 1.0, 1.0, 1.0, Math.max(1.0, frame.size.height - 3.0))];
[gripLine setHitTests:NO];
[gripLine setBackgroundColor:[CPColor colorWithWhite:0.5 alpha:1.0]];
[innerView addSubview:gripLine];
}
else
@@ -135,33 +130,23 @@ CPRulerOrientationVertical = 1
{
var align = [_representedObject alignment];
if (align === CPLeftTextAlignment)
[_label setStringValue:@"▶"]; // Left-aligned points Right
[_label setStringValue:@"▶"];
else if (align === CPCenterTextAlignment)
[_label setStringValue:@"▼"]; // Center-aligned points Down
[_label setStringValue:@"▼"];
else if (align === CPRightTextAlignment)
[_label setStringValue:@"◀"]; // Right-aligned points Left
}
else if ([_representedObject isKindOfClass:[CPString class]])
{
if (_representedObject === @"CPTailIndent")
[_label setStringValue:@"⥘"]; // Solid downward triangle for tail indent
else
[_label setStringValue:@"⇡"]; // Fallback standard up marker
[_label setStringValue:@"◀"];
}
else
{
[_label setStringValue:@""]; // Fallback standard up marker
[_label setStringValue:@""];
}
}
}
// MARK: -
// MARK: Context Menu Support
- (CPMenu)menuForEvent:(CPEvent)anEvent
{
var menu = [[CPMenu alloc] initWithTitle:@"Marker Context Menu"];
// If the marker represents a standard tab stop, allow changing its type
if ([_representedObject isKindOfClass:[CPTextTab class]])
{
var itemLeft = [menu addItemWithTitle:@"Left Tab Stop" action:@selector(changeTypeToLeft:) keyEquivalent:@""],
@@ -180,17 +165,11 @@ CPRulerOrientationVertical = 1
[menu addItem:[CPMenuItem separatorItem]];
}
// Determine the context-specific delete title
var deleteTitle = @"Delete Tab Stop";
if ([_representedObject isKindOfClass:[CPString class]])
{
if (_representedObject === @"CPFirstLineIndent")
deleteTitle = @"Delete 1st line indentation marker";
else if (_representedObject === @"CPHeadIndent")
deleteTitle = @"Delete head indentation marker";
else if (_representedObject === @"CPTailIndent")
deleteTitle = @"Delete tail indentation marker";
}
if (_representedObject === @"CPFirstLineIndent")
deleteTitle = @"Reset 1st line indentation";
else if (_representedObject === @"CPHeadIndent")
deleteTitle = @"Reset head indentation";
var itemDelete = [menu addItemWithTitle:deleteTitle action:@selector(deleteMarker:) keyEquivalent:@""];
[itemDelete setTarget:self];
@@ -198,66 +177,51 @@ CPRulerOrientationVertical = 1
return menu;
}
- (void)changeTypeToLeft:(id)sender
{
[self _changeAlignment:CPLeftTextAlignment];
}
- (void)changeTypeToCenter:(id)sender
{
[self _changeAlignment:CPCenterTextAlignment];
}
- (void)changeTypeToRight:(id)sender
{
[self _changeAlignment:CPRightTextAlignment];
}
- (void)changeTypeToLeft:(id)sender { [self _changeAlignment:CPLeftTextAlignment]; }
- (void)changeTypeToCenter:(id)sender { [self _changeAlignment:CPCenterTextAlignment]; }
- (void)changeTypeToRight:(id)sender { [self _changeAlignment:CPRightTextAlignment]; }
- (void)_changeAlignment:(CPTextAlignment)alignment
{
if (![_representedObject isKindOfClass:[CPTextTab class]])
return;
var oldTab = _representedObject;
var newTab = [[CPTextTab alloc] initWithType:alignment location:_imageValue];
var oldTab = _representedObject,
newTab = [[CPTextTab alloc] initWithType:alignment location:_imageValue];
// Using setRepresentedObject: automatically updates the marker triangle direction
[self setRepresentedObject:newTab];
var client = [_rulerView clientView];
if (client && [client respondsToSelector:@selector(rulerView:didUpdateMarker:oldTab:)])
{
[client rulerView:_rulerView didUpdateMarker:self oldTab:oldTab];
}
}
- (void)deleteMarker:(id)sender
{
var client = [_rulerView clientView];
if (client && [client respondsToSelector:@selector(rulerView:didRemoveMarker:)])
{
[client rulerView:_rulerView didRemoveMarker:self];
}
[_rulerView removeMarker:self];
}
@end
// MARK: - CPRulerView (Pure DOM + Interactive Engine)
// MARK: - CPRulerView
@implementation CPRulerView : CPView
{
CPScrollView _scrollView @accessors(property=scrollView);
CPRulerOrientation _orientation @accessors(property=orientation);
CPView _clientView @accessors(property=clientView);
CPView _clientView;
float _ruleThickness @accessors(property=ruleThickness);
float _reservedThicknessForMarkers;
CPArray _markers;
// Dragger variables
CPRulerMarker _draggingMarker;
CPRulerMarker _draggingMarker @accessors(getter=draggingMarker);
CGPoint _dragStartPoint;
float _dragStartLocation;
}
@@ -285,7 +249,16 @@ CPRulerOrientationVertical = 1
[self updateRuler];
}
// Markers registration
- (CPView)clientView
{
return _clientView || [_scrollView documentView];
}
- (void)setClientView:(CPView)aView
{
_clientView = aView;
}
- (void)addMarker:(CPRulerMarker)aMarker
{
if ([_markers containsObject:aMarker])
@@ -315,14 +288,20 @@ CPRulerOrientationVertical = 1
[self addSubview:marker];
[self _positionMarker:marker];
}
[self updateRuler];
}
- (CPRulerMarker)_markerAtPoint:(CGPoint)aPoint
{
for (var i = 0; i < [_markers count]; i++)
// Search in reverse order so indent paddles (added last) are prioritized over overlapping 0-location tabs
for (var i = [_markers count] - 1; i >= 0; i--)
{
var marker = [_markers objectAtIndex:i];
if (CGRectContainsPoint([marker frame], aPoint))
var marker = [_markers objectAtIndex:i],
frame = [marker frame];
// Expanded hit-box by 3px on all sides for easy grabbing
var hitFrame = CGRectMake(frame.origin.x - 3.0, frame.origin.y - 2.0, frame.size.width + 6.0, frame.size.height + 4.0);
if (CGRectContainsPoint(hitFrame, aPoint))
return marker;
}
return nil;
@@ -338,55 +317,48 @@ CPRulerOrientationVertical = 1
isHorizontal = (_orientation === CPHorizontalRuler || _orientation === CPRulerOrientationHorizontal),
rulerHeight = CGRectGetHeight([self bounds]),
rulerWidth = CGRectGetWidth([self bounds]),
markerLocation = [aMarker imageValue];
markerLocation = [aMarker imageValue],
rep = [aMarker representedObject];
if (isHorizontal)
{
var x = markerLocation - scrollPoint.x - 6.0, // Center the 12px wide marker
var x = markerLocation - scrollPoint.x - 6.0,
y = rulerHeight - 11.0,
w = 12.0,
h = 12.0;
// Align the First Line Indent (upper half) and Head Indent (lower half) controls
if ([aMarker representedObject] === @"CPFirstLineIndent")
if (rep === @"CPFirstLineIndent")
{
y = 0.0;
h = Math.floor(rulerHeight / 2.0);
}
else if ([aMarker representedObject] === @"CPHeadIndent")
else if (rep === @"CPHeadIndent")
{
y = Math.floor(rulerHeight / 2.0);
h = rulerHeight - y - 1.0; // Subtract 1px to stay cleanly above bottom border
h = rulerHeight - y - 1.0;
}
else
{
// Keep normal horizontal markers within the bounds of the ruler to prevent clipping
if (x < 0.0)
x = 0.0;
else if (x + 12.0 > rulerWidth)
x = rulerWidth - 12.0;
if (x < -6.0) x = -6.0;
else if (x + 12.0 > rulerWidth) x = rulerWidth - 12.0;
}
[aMarker setFrame:CGRectMake(x, y, w, h)];
}
else
{
var x = rulerWidth - 11.0,
y = markerLocation - scrollPoint.y - 6.0;
// Keep vertical marker within the bounds of the ruler to prevent clipping
if (y < 0.0)
y = 0.0;
else if (y + 12.0 > rulerHeight)
y = rulerHeight - 12.0;
if (y < 0.0) y = 0.0;
else if (y + 12.0 > rulerHeight) y = rulerHeight - 12.0;
[aMarker setFrame:CGRectMake(x, y, 12.0, 12.0)];
}
}
// MARK: -
// MARK: Interaction Handlers
// MARK: - Interaction Handlers
- (void)mouseDown:(CPEvent)anEvent
{
@@ -398,7 +370,6 @@ CPRulerOrientationVertical = 1
var rulerLocation = isHorizontal ? (localPoint.x + scrollPoint.x) : (localPoint.y + scrollPoint.y);
// 1. Check if clicked an existing marker
var clickedMarker = [self _markerAtPoint:localPoint];
if (clickedMarker)
{
@@ -406,20 +377,22 @@ CPRulerOrientationVertical = 1
_dragStartPoint = localPoint;
_dragStartLocation = [_draggingMarker imageValue];
}
// 2. Otherwise, create a new marker dynamically where the user clicked
else
{
// Allow client view to constrain initial placement
var client = [self clientView];
if (client && [client respondsToSelector:@selector(rulerView:willAddMarker:atLocation:)])
rulerLocation = [client rulerView:self willAddMarker:nil atLocation:rulerLocation];
var newMarker = [[CPRulerMarker alloc] initWithRulerView:self
markerLocation:rulerLocation
imageValue:rulerLocation
representedObject:nil];
[self addMarker:newMarker];
// Notify the client view (e.g., CPTextView) that a new marker was added
var client = [self clientView];
if (client && [client respondsToSelector:@selector(rulerView:didAddMarker:)])
[client rulerView:self didAddMarker:newMarker];
_draggingMarker = newMarker;
_dragStartPoint = localPoint;
_dragStartLocation = rulerLocation;
@@ -436,34 +409,35 @@ CPRulerOrientationVertical = 1
isHorizontal = (_orientation === CPHorizontalRuler || _orientation === CPRulerOrientationHorizontal);
var delta = isHorizontal ? (localPoint.x - _dragStartPoint.x) : (localPoint.y - _dragStartPoint.y),
newLocation = _dragStartLocation + delta;
newLocation = Math.max(0.0, _dragStartLocation + delta);
if (newLocation < 0) newLocation = 0;
// Constrain marker location to last possible position
var client = [self clientView];
if (client && [client respondsToSelector:@selector(rulerView:willMoveMarker:toLocation:)])
newLocation = [client rulerView:self willMoveMarker:_draggingMarker toLocation:newLocation];
[_draggingMarker setImageValue:newLocation];
// Smoothly redraw ruler and margin bounds on every drag step
[self updateRuler];
// Check if dragged off the ruler (more than 15px off the boundary)
var draggedOff = isHorizontal ? (localPoint.y < -15 || localPoint.y > CGRectGetHeight([self bounds]) + 15)
: (localPoint.x < -15 || localPoint.x > CGRectGetWidth([self bounds]) + 15);
[self _positionMarker:_draggingMarker];
var rep = [_draggingMarker representedObject],
isIndent = (rep === @"CPFirstLineIndent" || rep === @"CPHeadIndent" || rep === @"CPTailIndent");
var draggedOff = !isIndent && (isHorizontal
? (localPoint.y < -15 || localPoint.y > CGRectGetHeight([self bounds]) + 15)
: (localPoint.x < -15 || localPoint.x > CGRectGetWidth([self bounds]) + 15));
if (draggedOff)
{
// Visual feedback: Dim the handle to 40% and turn the triangle icon gray
[_draggingMarker setAlphaValue:0.4];
[[_draggingMarker label] setTextColor:[CPColor grayColor]];
}
else
{
// Restore standard styling when dragged back into the active strip
[_draggingMarker setAlphaValue:1.0];
[[_draggingMarker label] setTextColor:[CPColor colorWithWhite:0.2 alpha:1.0]];
}
// Notify the CPTextView that the marker coordinates shifted
var client = [self clientView];
if (client && [client respondsToSelector:@selector(rulerView:didMoveMarker:)])
[client rulerView:self didMoveMarker:_draggingMarker];
}
@@ -475,10 +449,12 @@ CPRulerOrientationVertical = 1
var localPoint = [self convertPoint:[anEvent locationInWindow] fromView:nil],
isHorizontal = (_orientation === CPHorizontalRuler || _orientation === CPRulerOrientationHorizontal),
// If dragged more than 15 pixels off the ruler, delete the marker
draggedOff = isHorizontal ? (localPoint.y < -15 || localPoint.y > CGRectGetHeight([self bounds]) + 15)
: (localPoint.x < -15 || localPoint.x > CGRectGetWidth([self bounds]) + 15);
rep = [_draggingMarker representedObject],
isIndent = (rep === @"CPFirstLineIndent" || rep === @"CPHeadIndent" || rep === @"CPTailIndent");
var draggedOff = !isIndent && (isHorizontal
? (localPoint.y < -15 || localPoint.y > CGRectGetHeight([self bounds]) + 15)
: (localPoint.x < -15 || localPoint.x > CGRectGetWidth([self bounds]) + 15));
if (draggedOff)
{
@@ -490,22 +466,19 @@ CPRulerOrientationVertical = 1
}
else
{
// Ensure marker style is fully restored if not deleted
[_draggingMarker setAlphaValue:1.0];
[[_draggingMarker label] setTextColor:[CPColor colorWithWhite:0.2 alpha:1.0]];
}
_draggingMarker = nil;
[self updateRuler];
}
// MARK: -
// MARK: DOM Layout Builder
// MARK: - Layout Builder
- (void)updateRuler
{
// Wipe subviews to redraw the dynamic visible tick lines/numbers
[self setSubviews:@[]];
if (!_scrollView)
@@ -524,12 +497,11 @@ CPRulerOrientationVertical = 1
rulerHeight = CGRectGetHeight([self bounds]),
rulerWidth = CGRectGetWidth([self bounds]);
// Draw solid horizontal bottom border (pure, razor-sharp CSS DOM view)
var bottomBorder = [[CPView alloc] initWithFrame:CGRectMake(0, rulerHeight - 1, rulerWidth, 1)];
[bottomBorder setHitTests:NO];
[bottomBorder setBackgroundColor:[CPColor colorWithWhite:0.75 alpha:1.0]];
[self addSubview:bottomBorder];
// Find indent markers to determine background highlight boundaries
var firstLineMarker = nil,
headMarker = nil;
for (var i = 0; i < [_markers count]; i++)
@@ -543,31 +515,30 @@ CPRulerOrientationVertical = 1
var halfHeight = Math.floor(rulerHeight / 2.0);
// Draw First Line Indent background - top half (lighter gray)
if (firstLineMarker)
{
var firstLineX = [firstLineMarker imageValue] - scrollPoint.x;
if (firstLineX > 0)
{
var firstLineBg = [[CPView alloc] initWithFrame:CGRectMake(0, 0, firstLineX, halfHeight)];
[firstLineBg setHitTests:NO];
[firstLineBg setBackgroundColor:[CPColor colorWithWhite:0.93 alpha:1.0]];
[self addSubview:firstLineBg];
}
}
// Draw Head Indent background - bottom half (slightly darker gray)
if (headMarker)
{
var headX = [headMarker imageValue] - scrollPoint.x;
if (headX > 0)
{
var headBg = [[CPView alloc] initWithFrame:CGRectMake(0, halfHeight, headX, rulerHeight - halfHeight - 1.0)];
[headBg setHitTests:NO];
[headBg setBackgroundColor:[CPColor colorWithWhite:0.86 alpha:1.0]];
[self addSubview:headBg];
}
}
// Render ruler tick lines and labels on top of shaded areas
for (var val = start; val <= end; val += 10)
{
if (val < 0) continue;
@@ -577,30 +548,21 @@ CPRulerOrientationVertical = 1
tickHeight = isMajor ? 8.0 : 4.0,
tickY = rulerHeight - tickHeight - 1.0;
// Tick mark CSS line view
var tick = [[CPView alloc] initWithFrame:CGRectMake(screenX, tickY, 1.0, tickHeight)];
[tick setHitTests:NO];
[tick setBackgroundColor:[CPColor colorWithWhite:0.65 alpha:1.0]];
[self addSubview:tick];
// Unit label
if (isMajor)
{
var labelX = screenX - 20.0,
alignment = CPCenterTextAlignment;
// Adjust label frame and alignment if it lands near left/right bounds
if (labelX < 0.0)
{
labelX = Math.max(0.0, screenX);
alignment = CPLeftTextAlignment;
}
else if (labelX + 40.0 > rulerWidth)
{
labelX = rulerWidth - 40.0;
alignment = CPRightTextAlignment;
}
if (labelX < 0.0) { labelX = Math.max(0.0, screenX); alignment = CPLeftTextAlignment; }
else if (labelX + 40.0 > rulerWidth) { labelX = rulerWidth - 40.0; alignment = CPRightTextAlignment; }
var label = [[CPTextField alloc] initWithFrame:CGRectMake(labelX, 1.0, 40.0, 12.0)];
[label setHitTests:NO];
[label setStringValue:[CPString stringWithFormat:@"%d", val]];
[label setFont:[CPFont systemFontOfSize:8.0]];
[label setTextColor:[CPColor colorWithWhite:0.4 alpha:1.0]];
@@ -657,12 +619,11 @@ CPRulerOrientationVertical = 1
}
}
// Reposition and display active markers
// Add back existing markers without re-creating them
for (var i = 0; i < [_markers count]; i++)
{
var marker = [_markers objectAtIndex:i];
if ([marker superview] !== self)
[self addSubview:marker];
[self addSubview:marker];
[self _positionMarker:marker];
}
}
+225 -147
View File
@@ -2472,6 +2472,10 @@ Sets the selection to a range of characters in response to user action.
if (!ruler)
return;
// Do not rebuild markers if user is currently dragging one
if ([ruler draggingMarker])
return;
var selectedRange = [self selectedRange],
paragraphStyle = [CPParagraphStyle defaultParagraphStyle],
currentAttributes = _typingAttributes;
@@ -2480,10 +2484,10 @@ Sets the selection to a range of characters in response to user action.
if (textLength > 0)
{
var charIndex = selectedRange.location;
// Safety bounds checks for cursor placements
if (charIndex >= textLength)
charIndex = textLength - 1;
if (charIndex < 0)
charIndex = 0;
@@ -2497,7 +2501,6 @@ Sets the selection to a range of characters in response to user action.
tabStops = [paragraphStyle tabStops],
count = [tabStops count];
// A. Load existing tab stop markers onto the ruler
for (var i = 0; i < count; i++)
{
var tab = [tabStops objectAtIndex:i],
@@ -2508,7 +2511,6 @@ Sets the selection to a range of characters in response to user action.
[markers addObject:marker];
}
// B. Add Indentation Handles (First line indent & Head indent)
var firstLineMarker = [[CPRulerMarker alloc] initWithRulerView:ruler
markerLocation:[paragraphStyle firstLineHeadIndent]
imageValue:[paragraphStyle firstLineHeadIndent]
@@ -2524,7 +2526,6 @@ Sets the selection to a range of characters in response to user action.
[ruler setMarkers:markers];
}
// Local comparison function helper for tab sorting
var compareTabStops = function(obj1, obj2, context) {
if ([obj1 location] < [obj2 location]) return CPOrderedAscending;
if ([obj1 location] > [obj2 location]) return CPOrderedDescending;
@@ -2533,72 +2534,6 @@ var compareTabStops = function(obj1, obj2, context) {
- (void)rulerView:(CPRulerView)rulerView didAddMarker:(CPRulerMarker)marker
{
if (![self _didBeginEditing] || ![self shouldChangeTextInRange:_selectionRange replacementString:nil])
return;
var selectedRange = [self selectedRange],
paragraphStyle = [CPParagraphStyle defaultParagraphStyle],
currentAttributes = _typingAttributes;
// Retrieve active attributes at the cursor position if there is no selection
var textLength = [_textStorage length],
charIndex = selectedRange.location;
if (textLength > 0)
{
if (charIndex >= textLength)
charIndex = textLength - 1;
if (charIndex < 0)
charIndex = 0;
currentAttributes = [_textStorage attributesAtIndex:charIndex effectiveRange:nil];
}
if ([currentAttributes objectForKey:CPParagraphStyleAttributeName])
paragraphStyle = [currentAttributes objectForKey:CPParagraphStyleAttributeName];
var mutableStyle = [paragraphStyle mutableCopy];
// Create a new Left-aligned tab stop where the user clicked
var newTab = [[CPTextTab alloc] initWithType:CPLeftTextAlignment location:[marker imageValue]],
tabs = [[mutableStyle tabStops] mutableCopy];
[tabs addObject:newTab];
// Sort tabs by location ascending
[tabs sortUsingFunction:compareTabStops context:nil];
[mutableStyle setTabStops:tabs];
[marker setRepresentedObject:newTab];
// Find the target text range to modify (selection or containing paragraph)
var targetRange = selectedRange;
if (targetRange.length === 0)
targetRange = [self selectionRangeForProposedRange:CPMakeRange(targetRange.location, 0) granularity:CPSelectByParagraph];
if (targetRange.length > 0)
{
[_textStorage addAttribute:CPParagraphStyleAttributeName value:mutableStyle range:CPMakeRangeCopy(targetRange)];
[_layoutManager textStorage:_textStorage
edited:0
range:CPMakeRangeCopy(targetRange)
changeInLength:0
invalidatedRange:CPMakeRangeCopy(targetRange)];
}
if (selectedRange.length === 0)
{
[_typingAttributes setObject:mutableStyle forKey:CPParagraphStyleAttributeName];
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self];
}
}
- (void)rulerView:(CPRulerView)rulerView didMoveMarker:(CPRulerMarker)marker
{
if (![self _didBeginEditing] || ![self shouldChangeTextInRange:_selectionRange replacementString:nil])
return;
var selectedRange = [self selectedRange],
paragraphStyle = [CPParagraphStyle defaultParagraphStyle],
currentAttributes = _typingAttributes;
@@ -2610,6 +2545,7 @@ var compareTabStops = function(obj1, obj2, context) {
{
if (charIndex >= textLength)
charIndex = textLength - 1;
if (charIndex < 0)
charIndex = 0;
@@ -2620,69 +2556,31 @@ var compareTabStops = function(obj1, obj2, context) {
paragraphStyle = [currentAttributes objectForKey:CPParagraphStyleAttributeName];
var mutableStyle = [paragraphStyle mutableCopy],
oldTab = [marker representedObject];
newTab = [[CPTextTab alloc] initWithType:CPLeftTextAlignment location:[marker imageValue]],
tabs = [[mutableStyle tabStops] mutableCopy];
if (!oldTab)
return;
[tabs addObject:newTab];
[tabs sortUsingFunction:compareTabStops context:nil];
// A. Handle standard tab stops
if ([oldTab isKindOfClass:[CPTextTab class]])
{
var newTab = [[CPTextTab alloc] initWithType:[oldTab alignment] location:[marker imageValue]],
tabs = [[mutableStyle tabStops] mutableCopy];
[mutableStyle setTabStops:tabs];
[marker setRepresentedObject:newTab];
[tabs removeObject:oldTab];
[tabs addObject:newTab];
// Sort tabs by location ascending
[tabs sortUsingFunction:compareTabStops context:nil];
[mutableStyle setTabStops:tabs];
[marker setRepresentedObject:newTab];
}
// B. Handle Indentation Marker drags (First Line, Left, and Right indents)
else if ([oldTab isKindOfClass:[CPString class]])
{
if (oldTab === @"CPFirstLineIndent")
[mutableStyle setFirstLineHeadIndent:[marker imageValue]];
else if (oldTab === @"CPHeadIndent")
[mutableStyle setHeadIndent:[marker imageValue]];
else if (oldTab === @"CPTailIndent")
[mutableStyle setTailIndent:[marker imageValue]];
}
// Find the target text range to modify (selection or containing paragraph)
var targetRange = selectedRange;
if (targetRange.length === 0)
if (targetRange.length === 0 && textLength > 0)
targetRange = [self selectionRangeForProposedRange:CPMakeRange(targetRange.location, 0) granularity:CPSelectByParagraph];
if (targetRange.length > 0)
{
[_textStorage addAttribute:CPParagraphStyleAttributeName value:mutableStyle range:CPMakeRangeCopy(targetRange)];
[_layoutManager textStorage:_textStorage
edited:0
range:CPMakeRangeCopy(targetRange)
changeInLength:0
invalidatedRange:CPMakeRangeCopy(targetRange)];
[_layoutManager textStorage:_textStorage edited:0 range:CPMakeRangeCopy(targetRange) changeInLength:0 invalidatedRange:CPMakeRangeCopy(targetRange)];
}
if (selectedRange.length === 0)
{
[_typingAttributes setObject:mutableStyle forKey:CPParagraphStyleAttributeName];
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self];
}
[_layoutManager _validateLayoutAndGlyphs];
[self sizeToFit];
[self setNeedsDisplay:YES];
[_typingAttributes setObject:mutableStyle forKey:CPParagraphStyleAttributeName];
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self];
}
- (void)rulerView:(CPRulerView)rulerView didRemoveMarker:(CPRulerMarker)marker
- (void)rulerView:(CPRulerView)rulerView didMoveMarker:(CPRulerMarker)marker
{
if (![self _didBeginEditing] || ![self shouldChangeTextInRange:_selectionRange replacementString:nil])
return;
var selectedRange = [self selectedRange],
paragraphStyle = [CPParagraphStyle defaultParagraphStyle],
currentAttributes = _typingAttributes;
@@ -2694,12 +2592,78 @@ var compareTabStops = function(obj1, obj2, context) {
{
if (charIndex >= textLength)
charIndex = textLength - 1;
if (charIndex < 0)
charIndex = 0;
currentAttributes = [_textStorage attributesAtIndex:charIndex effectiveRange:nil];
}
if ([currentAttributes objectForKey:CPParagraphStyleAttributeName])
paragraphStyle = [currentAttributes objectForKey:CPParagraphStyleAttributeName];
var mutableStyle = [paragraphStyle mutableCopy],
rep = [marker representedObject];
if (!rep)
return;
// A. Handle standard tab stops
if ([rep isKindOfClass:[CPTextTab class]])
{
var newTab = [[CPTextTab alloc] initWithType:[rep alignment] location:[marker imageValue]],
tabs = [[mutableStyle tabStops] mutableCopy];
[tabs removeObject:rep];
[tabs addObject:newTab];
[tabs sortUsingFunction:compareTabStops context:nil];
[mutableStyle setTabStops:tabs];
[marker setRepresentedObject:newTab];
}
// B. Handle Indentation Marker drags directly by string literal
else if (rep === @"CPFirstLineIndent")
[mutableStyle setFirstLineHeadIndent:[marker imageValue]];
else if (rep === @"CPHeadIndent")
[mutableStyle setHeadIndent:[marker imageValue]];
else if (rep === @"CPTailIndent")
[mutableStyle setTailIndent:[marker imageValue]];
var targetRange = selectedRange;
if (targetRange.length === 0 && textLength > 0)
targetRange = [self selectionRangeForProposedRange:CPMakeRange(targetRange.location, 0) granularity:CPSelectByParagraph];
if (targetRange.length > 0)
{
[_textStorage addAttribute:CPParagraphStyleAttributeName value:mutableStyle range:CPMakeRangeCopy(targetRange)];
[_layoutManager textStorage:_textStorage edited:0 range:CPMakeRangeCopy(targetRange) changeInLength:0 invalidatedRange:CPMakeRangeCopy(targetRange)];
}
[_typingAttributes setObject:mutableStyle forKey:CPParagraphStyleAttributeName];
[_layoutManager _validateLayoutAndGlyphs];
[self sizeToFit];
[self setNeedsDisplay:YES];
}
- (void)rulerView:(CPRulerView)rulerView didRemoveMarker:(CPRulerMarker)marker
{
var selectedRange = [self selectedRange],
paragraphStyle = [CPParagraphStyle defaultParagraphStyle],
currentAttributes = _typingAttributes;
var textLength = [_textStorage length],
charIndex = selectedRange.location;
if (textLength > 0)
{
if (charIndex >= textLength) charIndex = textLength - 1;
if (charIndex < 0) charIndex = 0;
currentAttributes = [_textStorage attributesAtIndex:charIndex effectiveRange:nil];
}
if ([currentAttributes objectForKey:CPParagraphStyleAttributeName])
paragraphStyle = [currentAttributes objectForKey:CPParagraphStyleAttributeName];
@@ -2711,31 +2675,20 @@ var compareTabStops = function(obj1, obj2, context) {
var tabs = [[mutableStyle tabStops] mutableCopy];
[tabs removeObject:oldTab];
[mutableStyle setTabStops:tabs];
// Find the target text range to modify (selection or containing paragraph)
var targetRange = selectedRange;
if (targetRange.length === 0)
if (targetRange.length === 0 && textLength > 0)
targetRange = [self selectionRangeForProposedRange:CPMakeRange(targetRange.location, 0) granularity:CPSelectByParagraph];
if (targetRange.length > 0)
{
[_textStorage addAttribute:CPParagraphStyleAttributeName value:mutableStyle range:CPMakeRangeCopy(targetRange)];
[_layoutManager textStorage:_textStorage
edited:0
range:CPMakeRangeCopy(targetRange)
changeInLength:0
invalidatedRange:CPMakeRangeCopy(targetRange)];
[_layoutManager textStorage:_textStorage edited:0 range:CPMakeRangeCopy(targetRange) changeInLength:0 invalidatedRange:CPMakeRangeCopy(targetRange)];
}
if (selectedRange.length === 0)
{
[_typingAttributes setObject:mutableStyle forKey:CPParagraphStyleAttributeName];
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self];
}
[_typingAttributes setObject:mutableStyle forKey:CPParagraphStyleAttributeName];
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self];
}
- (void)rulerView:(CPRulerView)rulerView didUpdateMarker:(CPRulerMarker)marker oldTab:(id)oldTab
@@ -2759,19 +2712,11 @@ var compareTabStops = function(obj1, obj2, context) {
[tabs removeObject:oldTab];
[tabs addObject:newTab];
// Sort tabs ascending
[tabs sortUsingFunction:compareTabStops context:nil];
[mutableStyle setTabStops:tabs];
[_textStorage addAttribute:CPParagraphStyleAttributeName value:mutableStyle range:CPMakeRangeCopy(targetRange)];
[_layoutManager textStorage:_textStorage
edited:0
range:CPMakeRangeCopy(targetRange)
changeInLength:0
invalidatedRange:CPMakeRangeCopy(targetRange)];
[_layoutManager textStorage:_textStorage edited:0 range:CPMakeRangeCopy(targetRange) changeInLength:0 invalidatedRange:CPMakeRangeCopy(targetRange)];
if (selectedRange.length === 0)
{
@@ -2779,12 +2724,145 @@ var compareTabStops = function(obj1, obj2, context) {
[[CPNotificationCenter defaultCenter] postNotificationName:CPTextViewDidChangeTypingAttributesNotification object:self];
}
// Force layouts and view canvas update
[_layoutManager _validateLayoutAndGlyphs];
[self sizeToFit];
[self setNeedsDisplay:YES];
}
- (float)rulerView:(CPRulerView)aRulerView willMoveMarker:(CPRulerMarker)aMarker toLocation:(float)proposedLocation
{
var rep = [aMarker representedObject];
// Indent markers can be dragged anywhere >= 0
if (rep === @"CPFirstLineIndent" || rep === @"CPHeadIndent" || rep === @"CPTailIndent")
return Math.max(0.0, proposedLocation);
var textLength = [_textStorage length];
if (textLength === 0)
return Math.max(0.0, proposedLocation);
var selectedRange = [self selectedRange],
targetRange = selectedRange;
if (targetRange.length === 0)
targetRange = [self selectionRangeForProposedRange:CPMakeRange(targetRange.location, 0) granularity:CPSelectByParagraph];
if (targetRange.length === 0)
return Math.max(0.0, proposedLocation);
[_layoutManager _validateLayoutAndGlyphs];
var theString = [_textStorage string],
minLocation = 0.0,
containerWidth = [_textContainer containerSize].width,
maxLocation = containerWidth,
currentParagraphStyle = [_textStorage attribute:CPParagraphStyleAttributeName atIndex:targetRange.location effectiveRange:nil] || [CPParagraphStyle defaultParagraphStyle],
tabStops = [currentParagraphStyle tabStops] || [],
tabCount = [tabStops count];
// 1. Right boundary: Tail indent (if configured)
var tailIndent = [currentParagraphStyle tailIndent];
if (tailIndent > 0.0)
maxLocation = Math.min(maxLocation, tailIndent);
else if (tailIndent < 0.0)
maxLocation = Math.min(maxLocation, containerWidth + tailIndent);
// 2. Identify active tab stop index
var targetTabIndex = -1;
for (var i = 0; i < tabCount; i++)
{
var tab = [tabStops objectAtIndex:i];
if (tab === rep || ([tab location] === [rep location] && [tab alignment] === [rep alignment]))
{
targetTabIndex = i;
break;
}
}
// 3. Constrain to neighboring tab stops (Left and Right)
var safetySpacer = 2.0;
if (targetTabIndex !== -1)
{
// Left neighbor constraint (previous tab stop)
if (targetTabIndex > 0)
{
var prevTab = [tabStops objectAtIndex:targetTabIndex - 1];
minLocation = Math.max(minLocation, [prevTab location] + safetySpacer);
}
// Right neighbor constraint (next tab stop)
if (targetTabIndex + 1 < tabCount)
{
var nextTab = [tabStops objectAtIndex:targetTabIndex + 1];
maxLocation = Math.min(maxLocation, [nextTab location] - safetySpacer);
}
}
else
{
// For new markers being added between existing tab stops
for (var i = 0; i < tabCount; i++)
{
var tabLoc = [[tabStops objectAtIndex:i] location];
if (tabLoc < proposedLocation)
minLocation = Math.max(minLocation, tabLoc + safetySpacer);
else if (tabLoc > proposedLocation)
maxLocation = Math.min(maxLocation, tabLoc - safetySpacer);
}
}
// 4. Constrain to preceding text on active lines
var start = targetRange.location,
end = CPMaxRange(targetRange),
tabCountInLine = 0,
lineStart = start;
for (var i = start; i < end; i++)
{
var charCode = theString.charCodeAt(i);
if (charCode === 10 || charCode === 13)
{
tabCountInLine = 0;
lineStart = i + 1;
continue;
}
if (charCode === 9) // '\t'
{
if (tabCountInLine === targetTabIndex || targetTabIndex === -1)
{
var precedingX = 0.0;
if (i === lineStart)
{
var isFirstLine = (lineStart === 0 || theString.charCodeAt(lineStart - 1) === 10 || theString.charCodeAt(lineStart - 1) === 13);
precedingX = isFirstLine ? [currentParagraphStyle firstLineHeadIndent] : [currentParagraphStyle headIndent];
}
else
{
var glyphRect = [_layoutManager boundingRectForGlyphRange:CPMakeRange(i - 1, 1) inTextContainer:_textContainer];
precedingX = CGRectGetMaxX(glyphRect);
}
var textLimit = precedingX + safetySpacer;
if (textLimit > minLocation)
minLocation = textLimit;
}
tabCountInLine++;
}
}
if (minLocation > maxLocation)
minLocation = maxLocation;
// 5. Clamp proposedLocation within [minLocation, maxLocation]
return Math.min(maxLocation, Math.max(minLocation, proposedLocation));
}
- (float)rulerView:(CPRulerView)aRulerView willAddMarker:(CPRulerMarker)aMarker atLocation:(float)proposedLocation
{
return [self rulerView:aRulerView willMoveMarker:aMarker toLocation:proposedLocation];
}
@end
@implementation CPTextView (CPTextViewDelegate)
@@ -3088,8 +3166,8 @@ var CPTextViewAllowsUndoKey = @"CPTextViewAllowsUndoKey",
{
var rect = [_textView._layoutManager boundingRectForGlyphRange:CPMakeRange(aLoc, 1) inTextContainer:_textView._textContainer];
if (aLoc >= [_textView._layoutManager numberOfCharacters])
rect.origin.x = CGRectGetMaxX(rect);
if (aLoc >= [_textView._layoutManager numberOfCharacters])
rect.origin.x = CGRectGetMaxX(rect);
[self setRect:rect];
}
+24 -42
View File
@@ -138,32 +138,28 @@ var CPSystemTypesetterFactory,
return [_layoutManager textContainers];
}
// Retrieves correct CPTextTab stop accounting for CPArray properties
// Retrieves correct CPTextTab stop accounting for custom stops and default intervals
- (CPTextTab)textTabForWidth:(double)aWidth writingDirection:(CPWritingDirection)direction
{
var tabStops = [_currentParagraph tabStops];
var tabStops = [_currentParagraph tabStops],
defaultInterval = [_currentParagraph defaultTabInterval] || 28.0;
if (!tabStops)
tabStops = [[CPParagraphStyle defaultParagraphStyle] tabStops];
var l = tabStops ? [tabStops count] : 0;
var l = [tabStops count];
if (l === 0)
return nil;
// Find the first tab stop that is strictly greater than the current width
for (var i = 0; i < l; i++)
// 1. If custom tab stops exist ahead of current position, use the first one encountered
if (l > 0)
{
var tab = [tabStops objectAtIndex:i];
for (var i = 0; i < l; i++)
{
var tab = [tabStops objectAtIndex:i];
if ([tab location] > aWidth)
return tab;
if ([tab location] > aWidth)
return tab;
}
}
// If aWidth exceeds the last tab stop, dynamically calculate the next
// tab location using the default tab interval.
var defaultInterval = [_currentParagraph defaultTabInterval] || 28.0;
var nextLocation = CEIL((aWidth + 1.0) / defaultInterval) * defaultInterval;
// 2. Otherwise (or when all custom tab stops are behind the text), advance to the next default interval
var nextLocation = (Math.floor(aWidth / defaultInterval) + 1) * defaultInterval;
return [[CPTextTab alloc] initWithType:CPLeftTextAlignment location:nextLocation];
}
@@ -205,7 +201,7 @@ var CPSystemTypesetterFactory,
[_layoutManager setLocation:CGPointMake(myX, _lineBase) forStartOfGlyphRange:lineRange];
[_layoutManager _setAdvancements:advancements forGlyphRange:lineRange];
//fix the _lineFragments when fontsizes differ
// fix the _lineFragments when fontsizes differ
var l = _lineFragments.length;
for (var i = 0 ; i < l ; i++)
@@ -251,7 +247,7 @@ var CPSystemTypesetterFactory,
isTabStop = NO,
isAttachment = NO,
isWordWrapped = NO,
numberOfGlyphs= [_textStorage length],
numberOfGlyphs = [_textStorage length],
leading,
numLines = 0,
theString = [_textStorage string],
@@ -293,7 +289,7 @@ var CPSystemTypesetterFactory,
for (; numLines != maxNumLines && glyphIndex < numberOfGlyphs; glyphIndex++)
{
// check whether there any change in the attributes from here on
// check whether there is any change in the attributes from here on
if (!CPLocationInRange(glyphIndex, _attributesRange))
{
_currentAttributes = [_textStorage attributesAtIndex:glyphIndex effectiveRange:_attributesRange];
@@ -327,15 +323,6 @@ var CPSystemTypesetterFactory,
lineOrigin.x = isFirstLineOfParagraph ? [_currentParagraph firstLineHeadIndent] : [_currentParagraph headIndent];
isFirstLineOfLayout = NO;
}
// Calculate the right wrapping margin based on tail indent
var tailIndent = [_currentParagraph tailIndent];
if (tailIndent > 0.0)
rightMargin = tailIndent;
else if (tailIndent < 0.0)
rightMargin = containerSizeWidth + tailIndent;
else
rightMargin = containerSizeWidth;
// Handle the layout's very first line indentation
if (isFirstLineOfLayout)
@@ -419,10 +406,10 @@ var CPSystemTypesetterFactory,
// We are processing characters, so we are no longer at the start of a physical line
isStartOfPhysicalLine = NO;
var currentCharCode = theString.charCodeAt(glyphIndex), // use pure javascript methods for performance reasons
var currentCharCode = theString.charCodeAt(glyphIndex),
rangeWidth = [theString.substr(measuringRange.location, measuringRange.length) sizeWithFont:currentFont inWidth:NULL].width + currentAnchor;
switch (currentCharCode) // faster than sending actionForControlCharacterAtIndex: called for each char.
switch (currentCharCode)
{
case CPAttachmentCharacter:
{
@@ -454,7 +441,8 @@ var CPSystemTypesetterFactory,
}
case 9: // '\t'
{
var nextTab = [self textTabForWidth:rangeWidth + lineOrigin.x writingDirection:0];
// Measure against the actual text position before the tab stop
var nextTab = [self textTabForWidth:prevRangeWidth + lineOrigin.x writingDirection:0];
isTabStop = YES;
@@ -501,7 +489,7 @@ var CPSystemTypesetterFactory,
}
else
{
rangeWidth += 28.0; // standard fallback spacer
rangeWidth = prevRangeWidth + 28.0; // standard fallback spacer
}
break;
}
@@ -511,10 +499,6 @@ var CPSystemTypesetterFactory,
wrapRange._height = _lineHeight;
wrapRange._base = _lineBase;
// Optimization: Start measuring from the next character to avoid O(n^2)
// string width calculation within a line since spaces do not carry ligatures or kerning.
// Only reset the measuring range if the next character is NOT another space.
// This prevents compounded subpixel rounding errors with contiguous spaces.
if (theString.charCodeAt(glyphIndex + 1) !== 32)
{
currentAnchor = rangeWidth;
@@ -544,7 +528,7 @@ var CPSystemTypesetterFactory,
isNewline = YES;
isWordWrapped = YES;
glyphIndex = CPMaxRange(lineRange) - 1; // start the line starts directly at current character
glyphIndex = CPMaxRange(lineRange) - 1;
}
if (isNewline || isTabStop || isAttachment)
@@ -576,8 +560,6 @@ var CPSystemTypesetterFactory,
containerSizeHeight = containerSize.height;
}
// If this is a soft wrap (isWordWrapped), next line gets headIndent.
// If it was a paragraph return, it gets firstLineHeadIndent.
isFirstLineOfParagraph = !isWordWrapped;
lineOrigin.x = isFirstLineOfParagraph ? [_currentParagraph firstLineHeadIndent] : [_currentParagraph headIndent];
@@ -603,7 +585,7 @@ var CPSystemTypesetterFactory,
}
}
// this is to "flush" the remaining characters
// Flush remaining characters
if (lineRange.length)
[self _flushRange:lineRange lineOrigin:lineOrigin currentContainer:_currentTextContainer advancements:advancements lineCount:numLines sameLine:NO];
+1 -1
View File
@@ -277,7 +277,7 @@ function _points2twips(a) { return (a) * 20.0; }
if (!num)
[colorDict setObject:num = [CPNumber numberWithInt:[colorDict count] + 1]
forKey:[color cssString]];
forKey:[color cssString]];
return [num intValue];
}
+2 -1
View File
@@ -19,7 +19,8 @@
*/
@import "CPView.j"
@import "CPTextView.j"
@class CPTextView;
@class CPTextContainer;
@import "CPTextField.j"
@import <Foundation/CPAttributedString.j>
+90 -71
View File
@@ -50,6 +50,9 @@
[self exposeBinding:@"contentArray"];
[self exposeBinding:@"sortDescriptors"];
[self exposeBinding:@"selectionIndexPaths"];
[self exposeBinding:@"selectionIndexPath"];
[self exposeBinding:@"selectedObjects"];
}
+ (CPSet)keyPathsForValuesAffectingContentArray
@@ -59,7 +62,7 @@
+ (CPSet)keyPathsForValuesAffectingArrangedObjects
{
return [CPSet setWithObjects:@"content", @"sortDescriptors", @"childrenKeyPath"];
return [CPSet setWithObjects:@"content", @"contentArray", @"sortDescriptors", @"childrenKeyPath"];
}
+ (CPSet)keyPathsForValuesAffectingSelectionIndexPath
@@ -69,27 +72,27 @@
+ (CPSet)keyPathsForValuesAffectingSelectedObjects
{
return [CPSet setWithObjects:@"selectionIndexPaths"];
return [CPSet setWithObjects:@"selectionIndexPaths", @"arrangedObjects"];
}
+ (CPSet)keyPathsForValuesAffectingSelectedNodes
{
return [CPSet setWithObjects:@"selectionIndexPaths"];
}
+ (CPSet)keyPathsForValuesAffectingCanAddChild
{
return [CPSet setWithObjects:@"selectionIndexPaths"];
return [CPSet setWithObjects:@"selectionIndexPaths", @"arrangedObjects"];
}
+ (CPSet)keyPathsForValuesAffectingCanInsert
{
return [CPSet setWithObjects:@"selectionIndexPaths"];
return [CPSet setWithObjects:@"editable"];
}
+ (CPSet)keyPathsForValuesAffectingCanInsertChild
{
return [CPSet setWithObjects:@"selectionIndexPaths"];
return [CPSet setWithObjects:@"selectionIndexPaths", @"editable"];
}
+ (CPSet)keyPathsForValuesAffectingCanAddChild
{
return [CPSet setWithObjects:@"selectionIndexPaths", @"editable"];
}
- (id)init
@@ -115,7 +118,8 @@
}
- (void)prepareContent
{[self _setContentArray:[CPArray arrayWithObject:[self newObject]]];
{
[self _setContentArray:[CPArray arrayWithObject:[self newObject]]];
}
- (BOOL)preservesSelection { return _preservesSelection; }
@@ -144,7 +148,8 @@
- (void)setChildrenKeyPath:(CPString)aKeyPath
{
if (_childrenKeyPath === aKeyPath) return;
_childrenKeyPath = aKeyPath;[self rearrangeObjects];
_childrenKeyPath = aKeyPath;
[self rearrangeObjects];
}
- (CPString)countKeyPath { return _countKeyPath; }
@@ -166,14 +171,21 @@
if (![value isKindOfClass:[CPArray class]])
value = [CPArray arrayWithObject:value];
if (_contentObject === value)
return;
var oldSelectedObjects = nil,
oldSelectionIndexPaths = nil;
oldSelectionIndexPaths = nil;
if ([self preservesSelection])
oldSelectedObjects = [self selectedObjects];
else
oldSelectionIndexPaths = [self selectionIndexPaths];
[self _selectionWillChange];
[self willChangeValueForKey:@"content"];
[self willChangeValueForKey:@"contentArray"];
_contentObject = value;
[self _rearrangeObjects];
@@ -182,23 +194,32 @@
[self __setSelectedObjects:oldSelectedObjects];
else
[self __setSelectionIndexPaths:oldSelectionIndexPaths avoidEmpty:_avoidsEmptySelection];
[self didChangeValueForKey:@"contentArray"];
[self didChangeValueForKey:@"content"];
[self _selectionDidChange];
}
- (void)_setContentArray:(id)anArray {[self setContent:anArray]; }
- (void)_setContentArray:(id)anArray { [self setContent:anArray]; }
- (id)contentArray { return _contentObject; }
- (id)arrangedObjects { return _arrangedObjects; }
- (void)rearrangeObjects
{
[self _selectionWillChange];
[self willChangeValueForKey:@"arrangedObjects"];
[self _rearrangeObjects];
[self didChangeValueForKey:@"arrangedObjects"];
[self _selectionDidChange];
}
- (void)_rearrangeObjects
{
var oldSelectedObjects = nil,
oldSelectionIndexPaths = nil;
oldSelectionIndexPaths = nil;
if ([self preservesSelection])
oldSelectedObjects = [self selectedObjects];
@@ -216,7 +237,7 @@
- (void)__rebuildArrangedObjectsTree
{
var rootNode = [[CPTreeNode alloc] initWithRepresentedObject:nil],
contentArray = [self contentArray];
contentArray = [self contentArray];
if (contentArray && [contentArray count] > 0)
{
@@ -244,7 +265,7 @@
for (var i = 0; i < count; i++)
{
var obj = [sortedObjects objectAtIndex:i],
node = [[CPTreeNode alloc] initWithRepresentedObject:obj];
node = [[CPTreeNode alloc] initWithRepresentedObject:obj];
if (_childrenKeyPath)
{
@@ -278,11 +299,7 @@
- (BOOL)setSelectionIndexPaths:(CPArray)indexPaths
{
[self _selectionWillChange];
var result = [self __setSelectionIndexPaths:indexPaths avoidEmpty:NO];
[self _selectionDidChange];
return result;
return [self __setSelectionIndexPaths:indexPaths avoidEmpty:NO];
}
- (void)_ensureTreeNodesExistForIndexPaths:(CPArray)indexPaths
@@ -334,7 +351,8 @@
if (needsRebuild)
{
[childNodes removeAllObjects];
var newNodes = [self _buildTreeNodesForObjects:expectedChildObjects];[childNodes addObjectsFromArray:newNodes];
var newNodes = [self _buildTreeNodesForObjects:expectedChildObjects];
[childNodes addObjectsFromArray:newNodes];
}
}
@@ -366,21 +384,13 @@
if ([_selectionIndexPaths isEqualToArray:newPaths])
return NO;
[self _selectionWillChange];
[self willChangeValueForKey:@"selectionIndexPaths"];
_selectionIndexPaths = [newPaths copy];
var binderClass = [[self class] _binderClassForBinding:@"selectionIndexPaths"];
if (binderClass)
{
var binding = [binderClass getBinding:@"selectionIndexPaths" forObject:self];
if (binding)
[binding reverseSetValueFor:@"selectionIndexPaths"];
}
[self didChangeValueForKey:@"selectionIndexPaths"];
[self _selectionDidChange];
return YES;
}
@@ -388,9 +398,7 @@
- (BOOL)addSelectionIndexPaths:(CPArray)indexPaths
{
var newPaths = [_selectionIndexPaths mutableCopy];
[newPaths addObjectsFromArray:indexPaths];
return [self setSelectionIndexPaths:newPaths];
}
@@ -404,7 +412,7 @@
- (CPArray)selectedNodes
{
var nodes = [CPMutableArray array],
count = [_selectionIndexPaths count];
count = [_selectionIndexPaths count];
for (var i = 0; i < count; i++)
{
@@ -418,13 +426,17 @@
- (CPArray)selectedObjects
{
var objects = [CPMutableArray array],
nodes = [self selectedNodes],
count = [nodes count];
nodes = [self selectedNodes],
count = [nodes count];
for (var i = 0; i < count; i++)
[objects addObject:[[nodes objectAtIndex:i] representedObject]];
{
var representedObject = [[nodes objectAtIndex:i] representedObject];
if (representedObject)
[objects addObject:representedObject];
}
return objects;
return [_CPObservableArray arrayWithArray:objects];
}
- (BOOL)__setSelectedObjects:(CPArray)objects
@@ -462,22 +474,22 @@
}
- (BOOL)canInsert { return [self isEditable]; }
- (BOOL)canInsertChild { return [self isEditable] &&[_selectionIndexPaths count] > 0; }
- (BOOL)canInsertChild { return [self isEditable] && [_selectionIndexPaths count] > 0; }
- (BOOL)canAddChild { return [self canInsertChild]; }
- (void)add:(id)sender
{
if (![self canInsert]) return;
var newObject = [self automaticallyPreparesContent] ? [self newObject] :[self _defaultNewObject],
selectionPath = [self selectionIndexPath];
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject],
selectionPath = [self selectionIndexPath];
if (!selectionPath)
selectionPath = [CPIndexPath indexPathWithIndex:[[[self arrangedObjects] childNodes] count]];
var length = [selectionPath length],
lastIndex = [selectionPath indexAtPosition:length - 1],
insertPath = [selectionPath indexPathByRemovingLastIndex];
lastIndex = [selectionPath indexAtPosition:length - 1],
insertPath = [selectionPath indexPathByRemovingLastIndex];
insertPath = [insertPath indexPathByAddingIndex:lastIndex + 1];
@@ -489,10 +501,10 @@
if (![self canAddChild])
return;
var newObject = [self automaticallyPreparesContent] ?[self newObject] : [self _defaultNewObject],
parentNode = [[self arrangedObjects] descendantNodeAtIndexPath:[self selectionIndexPath]],
childCount = [[parentNode childNodes] count],
insertPath = [[self selectionIndexPath] indexPathByAddingIndex:childCount];
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject],
parentNode = [[self arrangedObjects] descendantNodeAtIndexPath:[self selectionIndexPath]],
childCount = [[parentNode childNodes] count],
insertPath = [[self selectionIndexPath] indexPathByAddingIndex:childCount];
[self insertObject:newObject atArrangedObjectIndexPath:insertPath];
}
@@ -501,8 +513,8 @@
{
if (![self canInsert]) return;
var newObject = [self automaticallyPreparesContent] ? [self newObject] :[self _defaultNewObject],
indexPath = [self selectionIndexPath] || [CPIndexPath indexPathWithIndex:0];
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject],
indexPath = [self selectionIndexPath] || [CPIndexPath indexPathWithIndex:0];
[self insertObject:newObject atArrangedObjectIndexPath:indexPath];
}
@@ -512,7 +524,7 @@
if (![self canInsertChild]) return;
var newObject = [self automaticallyPreparesContent] ? [self newObject] : [self _defaultNewObject],
insertPath = [[self selectionIndexPath] indexPathByAddingIndex:0];
insertPath = [[self selectionIndexPath] indexPathByAddingIndex:0];
[self insertObject:newObject atArrangedObjectIndexPath:insertPath];
}
@@ -524,6 +536,7 @@
- (void)insertObjects:(CPArray)objects atArrangedObjectIndexPaths:(CPArray)indexPaths
{
[self _selectionWillChange];
[self willChangeValueForKey:@"content"];
_disableSetContent = YES;
@@ -531,21 +544,22 @@
for (var i = 0; i < count; i++)
{
var object = [objects objectAtIndex:i],
path = [indexPaths objectAtIndex:i],
length = [path length];
path = [indexPaths objectAtIndex:i],
length = [path length];
if (length === 1)
{[_contentObject insertObject:object atIndex:[path indexAtPosition:0]];
{
[_contentObject insertObject:object atIndex:[path indexAtPosition:0]];
}
else
{
var parentPath = [path indexPathByRemovingLastIndex],
parentNode = [[self arrangedObjects] descendantNodeAtIndexPath:parentPath];
parentNode = [[self arrangedObjects] descendantNodeAtIndexPath:parentPath];
if (parentNode)
{
var parentObj = [parentNode representedObject],
childIndex = [path indexAtPosition:length - 1];
childIndex = [path indexAtPosition:length - 1];
var children = [parentObj valueForKeyPath:_childrenKeyPath];
if (!children)
@@ -568,9 +582,11 @@
_disableSetContent = NO;
[self _rearrangeObjects];
if ([self selectsInsertedObjects])[self setSelectionIndexPaths:indexPaths];
if ([self selectsInsertedObjects])
[self setSelectionIndexPaths:indexPaths];
[self didChangeValueForKey:@"content"];
[self _selectionDidChange];
}
- (void)remove:(id)sender
@@ -585,16 +601,17 @@
- (void)removeObjectsAtArrangedObjectIndexPaths:(CPArray)indexPaths
{
[self _selectionWillChange];
[self willChangeValueForKey:@"content"];
_disableSetContent = YES;
var sortedPaths = [indexPaths sortedArrayUsingSelector:@selector(compare:)],
count = [sortedPaths count];
count = [sortedPaths count];
for (var i = count - 1; i >= 0; i--)
{
var path = [sortedPaths objectAtIndex:i],
length = [path length];
length = [path length];
if (length === 1)
{
@@ -603,15 +620,15 @@
else
{
var parentPath = [path indexPathByRemovingLastIndex],
parentNode = [[self arrangedObjects] descendantNodeAtIndexPath:parentPath];
parentNode = [[self arrangedObjects] descendantNodeAtIndexPath:parentPath];
if (parentNode)
{
var parentObj = [parentNode representedObject],
childIndex = [path indexAtPosition:length - 1],
mutableChildren = [parentObj mutableArrayValueForKeyPath:_childrenKeyPath];
childIndex = [path indexAtPosition:length - 1],
mutableChildren = [parentObj mutableArrayValueForKeyPath:_childrenKeyPath];
if (mutableChildren && childIndex <[mutableChildren count])
if (mutableChildren && childIndex < [mutableChildren count])
[mutableChildren removeObjectAtIndex:childIndex];
}
}
@@ -624,6 +641,7 @@
_disableSetContent = NO;
[self _rearrangeObjects];
[self didChangeValueForKey:@"content"];
[self _selectionDidChange];
}
- (void)moveNode:(CPTreeNode)node toIndexPath:(CPIndexPath)indexPath
@@ -632,18 +650,19 @@
}
- (void)moveNodes:(CPArray)nodes toIndexPath:(CPIndexPath)startingIndexPath
{[CPException raise:CPUnsupportedMethodException reason:@"moveNodes:toIndexPath: is not yet implemented in CPTreeController."];
{
[CPException raise:CPUnsupportedMethodException reason:@"moveNodes:toIndexPath: is not yet implemented in CPTreeController."];
}
@end
var CPTreeControllerAvoidsEmptySelection = @"CPTreeControllerAvoidsEmptySelection",
CPTreeControllerPreservesSelection = @"CPTreeControllerPreservesSelection",
CPTreeControllerSelectsInsertedObjects = @"CPTreeControllerSelectsInsertedObjects",
CPTreeControllerAlwaysUsesMultipleValuesMarker = @"CPTreeControllerAlwaysUsesMultipleValuesMarker",
CPTreeControllerChildrenKeyPath = @"CPTreeControllerChildrenKeyPath",
CPTreeControllerCountKeyPath = @"CPTreeControllerCountKeyPath",
CPTreeControllerLeafKeyPath = @"CPTreeControllerLeafKeyPath";
CPTreeControllerPreservesSelection = @"CPTreeControllerPreservesSelection",
CPTreeControllerSelectsInsertedObjects = @"CPTreeControllerSelectsInsertedObjects",
CPTreeControllerAlwaysUsesMultipleValuesMarker = @"CPTreeControllerAlwaysUsesMultipleValuesMarker",
CPTreeControllerChildrenKeyPath = @"CPTreeControllerChildrenKeyPath",
CPTreeControllerCountKeyPath = @"CPTreeControllerCountKeyPath",
CPTreeControllerLeafKeyPath = @"CPTreeControllerLeafKeyPath";
@implementation CPTreeController (CPCoding)
+301 -66
View File
@@ -17,17 +17,41 @@
*
* 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
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
@import <Foundation/CPObject.j>
@import <Foundation/CPIndexPath.j>
@import <Foundation/CPArray.j>
/*
* CPTreeNode implements the NSTreeNode contract.
* The _childNodes array contains only CPTreeNode instances.
* The representedObject property contains the application data.
* The _parentNode and _childNodes properties maintain a strict bidirectional relationship.
* The KVC mutation methods are the public mechanism to change the tree structure.
*/
@implementation CPTreeNode : CPObject
{
id _representedObject @accessors(property=representedObject);
CPTreeNode _parentNode @accessors(property=parentNode);
/*
* KVO notifications on parentNode are never delivered: there is no
* setParentNode: for the swizzling machinery to intercept, so there is
* no selector to instrument, in any code path.
*
* KVO notifications on childNodes are reliable for
* insertObject:inChildNodesAtIndex:, including same-parent and
* cross-parent moves: all detach paths for that method route through
* the KVC accessors. replaceObjectInChildNodesAtIndex:withObject:
* still detaches a same-parent replacement node by mutating
* _childNodes directly, bypassing the KVC proxy for that step; an
* observer of that node's former parent's childNodes can miss that
* specific removal, or see it reported as the wrong kind of change.
* Do not rely on childNodes observation during a same-parent replace;
* rely only on the state after the call returns.
*/
id _representedObject @accessors(readonly, property=representedObject);
CPTreeNode _parentNode @accessors(readonly, property=parentNode);
CPMutableArray _childNodes;
}
@@ -49,27 +73,107 @@
return self;
}
/*
* Route plain init through the designated initializer.
* Without this override, [[CPTreeNode alloc] init] leaves _childNodes unset.
* The first mutation call then fails against an undefined array.
*/
- (id)init
{
return [self initWithRepresentedObject:nil];
}
/*
* Return YES if adding aTreeNode below self makes a cycle.
* This method walks the parent chain.
* The operation time is proportional to the tree depth.
*/
- (BOOL)_wouldCreateCycleWithNode:(CPTreeNode)aTreeNode
{
for (var node = self; node; node = node._parentNode)
{
if (node === aTreeNode)
return YES;
}
return NO;
}
/*
* Enforce the NSTreeNode abstraction boundary.
* All children must be CPTreeNode instances.
*/
- (void)_validateChildNode:(id)aTreeNode
{
if (![aTreeNode isKindOfClass:[CPTreeNode class]])
{
[CPException raise:CPInvalidArgumentException
reason:"CPTreeNode children must be CPTreeNode instances."];
}
}
/*
* Remove a child node by delegating to the public KVC accessor.
* Use this method for internal structural changes across a parent
* boundary, so an observer of this node's childNodes sees the removal.
*/
- (void)_removeChildNode:(CPTreeNode)aNode
{
var index = [_childNodes indexOfObjectIdenticalTo:aNode];
/*
* A caller reaches this method only when aNode.parentNode already equals
* self (see the two call sites below). If self._childNodes does not
* actually contain aNode at that point, the parent/child relationship
* is already broken. indexPath raises for this identical class of
* inconsistency; silently returning here would hide the same problem
* instead of surfacing it.
*/
if (index === CPNotFound)
{
[CPException raise:CPInternalInconsistencyException
reason:"CPTreeNode parent and child relationship is inconsistent."];
}
[self removeObjectFromChildNodesAtIndex:index];
}
- (CPIndexPath)indexPath
{
// If we have a parent, calculate path based on parent's path + our index
if (_parentNode)
if (!_parentNode)
return [CPIndexPath indexPathWithIndexes:[]];
var indexes = [],
node = self;
while (node._parentNode)
{
// Search the parent's child nodes, not our own!
var index = [[_parentNode childNodes] indexOfObjectIdenticalTo:self];
// If the parent is the root (and technically has no path itself in some implementations),
// we might get nil. Handle that gracefully.
var parentPath = [_parentNode indexPath];
if (parentPath)
return [parentPath indexPathByAddingIndex:index];
return [CPIndexPath indexPathWithIndex:index];
var parent = node._parentNode,
index = [parent._childNodes indexOfObjectIdenticalTo:node];
if (index === CPNotFound)
{
[CPException raise:CPInternalInconsistencyException
reason:"CPTreeNode parent and child relationship is inconsistent."];
}
[indexes addObject:index];
node = parent;
}
// If we are the root, we don't have an index path in the context of a tree controller usually,
// or we are [] (empty path). Returning nil is acceptable for the absolute root.
return nil;
/*
* indexes was collected leaf-to-root. Build a second array in
* root-to-leaf order by walking indexes backward. CPArray has no
* -reverse selector; count/objectAtIndex:/addObject: are the verified,
* already-used-elsewhere primitives.
*/
var orderedIndexes = [],
count = [indexes count];
while (count--)
[orderedIndexes addObject:[indexes objectAtIndex:count]];
return [CPIndexPath indexPathWithIndexes:orderedIndexes];
}
- (BOOL)isLeaf
@@ -79,7 +183,10 @@
- (CPArray)childNodes
{
// Return a copy to prevent external modification without KVC
/*
* Return a copy.
* This prevents external changes that bypass the KVC methods.
*/
return [_childNodes copy];
}
@@ -88,78 +195,190 @@
return [self mutableArrayValueForKey:@"childNodes"];
}
// MARK: - KVC Compliance Methods
/*
* KVC compliance methods.
* The mutableArrayValueForKey: method uses these names.
*/
- (void)insertObject:(CPTreeNode)aTreeNode inChildNodesAtIndex:(CPInteger)anIndex
{
// Optional: Auto-detach from old parent if strictly moving nodes
if ([aTreeNode isKindOfClass:[CPTreeNode class]] && aTreeNode._parentNode)
var count = [_childNodes count];
if (anIndex < 0 || anIndex > count)
{
[[aTreeNode._parentNode mutableChildNodes] removeObjectIdenticalTo:aTreeNode];
[CPException raise:CPRangeException
reason:"index (" + anIndex + ") beyond bounds (0 .. " + count + ") for insertObject:inChildNodesAtIndex:"];
}
// Direct ivar access is allowed here since we are inside the class implementation
if ([aTreeNode isKindOfClass:[CPTreeNode class]])
aTreeNode._parentNode = self;
[self _validateChildNode:aTreeNode];
if ([self _wouldCreateCycleWithNode:aTreeNode])
{
[CPException raise:CPInvalidArgumentException
reason:"Inserting a CPTreeNode beneath itself or one of its descendants makes a cycle."];
}
/*
* Detach the node from its old parent first.
* The code validated the index before this change.
*/
if (aTreeNode._parentNode)
{
if (aTreeNode._parentNode === self)
{
var originalIndex = [_childNodes indexOfObjectIdenticalTo:aTreeNode];
/*
* Route the detach through the KVC accessor, not direct array
* mutation, so an observer of childNodes sees the removal.
*/
[self removeObjectFromChildNodesAtIndex:originalIndex];
/*
* No index adjustment here. anIndex is the target position in
* the final array, per the KVC to-many contract. The array
* above is already one element short from the removal, so
* inserting at anIndex against it lands the node correctly.
*/
}
else
{
/*
* Detach the node from its old parent.
* Use the internal method to bypass KVO overhead.
*/
[aTreeNode._parentNode _removeChildNode:aTreeNode];
}
}
aTreeNode._parentNode = self;
[_childNodes insertObject:aTreeNode atIndex:anIndex];
}
- (void)removeObjectFromChildNodesAtIndex:(CPInteger)anIndex
{
var node = [_childNodes objectAtIndex:anIndex];
if ([node isKindOfClass:[CPTreeNode class]])
node._parentNode = nil;
node._parentNode = nil;
[_childNodes removeObjectAtIndex:anIndex];
}
- (void)replaceObjectFromChildNodesAtIndex:(CPInteger)anIndex withObject:(id)aTreeNode
- (void)replaceObjectInChildNodesAtIndex:(CPInteger)anIndex withObject:(CPTreeNode)aTreeNode
{
var oldTreeNode = [_childNodes objectAtIndex:anIndex];
if ([oldTreeNode isKindOfClass:[CPTreeNode class]])
oldTreeNode._parentNode = nil;
if ([aTreeNode isKindOfClass:[CPTreeNode class]])
aTreeNode._parentNode = self;
[self _validateChildNode:aTreeNode];
if (oldTreeNode === aTreeNode)
return;
if ([self _wouldCreateCycleWithNode:aTreeNode])
{
[CPException raise:CPInvalidArgumentException
reason:"Replacing a child with itself or one of its ancestors makes a cycle."];
}
/*
* If the replacement node is already a child of this parent, remove it first.
* The removal shifts the array elements.
* Adjust the target index before the replace operation.
* This matches the Cocoa KVC mutation semantics.
*/
var oldParent = aTreeNode._parentNode;
if (oldParent === self)
{
var replacementIndex = [_childNodes indexOfObjectIdenticalTo:aTreeNode];
/*
* aTreeNode.parentNode already equals self at this point. If
* self._childNodes does not actually contain aTreeNode, the
* parent/child relationship is already broken. indexPath raises
* for this identical class of inconsistency; proceeding here would
* silently tolerate the same problem instead of surfacing it.
*/
if (replacementIndex === CPNotFound)
{
[CPException raise:CPInternalInconsistencyException
reason:"CPTreeNode parent and child relationship is inconsistent."];
}
/*
* Bypass KVO for this internal structural adjustment.
*/
aTreeNode._parentNode = nil;
[_childNodes removeObjectAtIndex:replacementIndex];
/*
* Unlike insertObject:inChildNodesAtIndex:, anIndex here cannot be
* treated as a plain final-array position: replace requires an
* existing slot, it cannot append past the end. The removal above
* already took a slot out of the array ahead of the target
* whenever the replacement's original position was before it.
* Shift anIndex down by one in that case, to keep it pointing at
* the same physical slot the caller named.
*/
if (replacementIndex < anIndex)
--anIndex;
}
else if (oldParent)
{
/*
* Detach the node from its old parent.
* Use the internal method to bypass KVO overhead.
*/
[oldParent _removeChildNode:aTreeNode];
}
oldTreeNode._parentNode = nil;
aTreeNode._parentNode = self;
[_childNodes replaceObjectAtIndex:anIndex withObject:aTreeNode];
}
// MARK: - Convenience Accessors
- (id)objectInChildNodesAtIndex:(CPInteger)anIndex
{
return [_childNodes objectAtIndex:anIndex];
}
- (CPInteger)count
- (CPInteger)countOfChildNodes
{
return [_childNodes count];
}
- (id)objectAtIndex:(CPInteger)anIndex
{
return [_childNodes objectAtIndex:anIndex];
}
// MARK: - Utility
- (void)sortWithSortDescriptors:(CPArray)sortDescriptors recursively:(BOOL)shouldSortRecursively
{
[_childNodes sortUsingDescriptors:sortDescriptors];
if (!shouldSortRecursively)
return;
var count = [_childNodes count];
while (count--)
{
var child = [_childNodes objectAtIndex:count];
if ([child respondsToSelector:@selector(sortWithSortDescriptors:recursively:)])
[child sortWithSortDescriptors:sortDescriptors recursively:YES];
[_childNodes sortUsingDescriptors:sortDescriptors];
return;
}
/*
* Use an explicit stack, not recursion.
* The recursive form is not a tail call.
* The sibling loop continues after each child call returns.
* Only JavaScriptCore performs tail call optimization.
* The explicit stack prevents stack overflow on deep trees.
*/
var stack = [];
[stack addObject:self];
while ([stack count])
{
var node = [stack lastObject];
[stack removeLastObject];
[node._childNodes sortUsingDescriptors:sortDescriptors];
var count = [node._childNodes count];
while (count--)
{
[stack addObject:[node._childNodes objectAtIndex:count]];
}
}
}
@@ -169,17 +388,17 @@
return self;
var node = self,
length = [indexPath length];
length = [indexPath length];
for (var i = 0; i < length; i++)
{
var index = [indexPath indexAtPosition:i],
count = [node count];
if (index >= count || index < 0)
count = [node countOfChildNodes];
if (index < 0 || index >= count)
return nil;
node = [node objectAtIndex:index];
node = [node objectInChildNodesAtIndex:index];
}
return node;
@@ -187,7 +406,6 @@
@end
// Coding implementation remains correct
var CPTreeNodeRepresentedObjectKey = @"CPTreeNodeRepresentedObjectKey",
CPTreeNodeParentNodeKey = @"CPTreeNodeParentNodeKey",
CPTreeNodeChildNodesKey = @"CPTreeNodeChildNodesKey";
@@ -203,10 +421,27 @@ var CPTreeNodeRepresentedObjectKey = @"CPTreeNodeRepresentedObjectKey",
_representedObject = [aCoder decodeObjectForKey:CPTreeNodeRepresentedObjectKey];
_parentNode = [aCoder decodeObjectForKey:CPTreeNodeParentNodeKey];
_childNodes = [aCoder decodeObjectForKey:CPTreeNodeChildNodesKey];
// Safety check to ensure decoding gave us a CPArray
if (!_childNodes)
_childNodes = [[CPMutableArray alloc] init];
_childNodes = [];
if (![_childNodes isKindOfClass:[CPMutableArray class]])
_childNodes = [_childNodes mutableCopy];
/*
* The child array is the authoritative structure.
* Re-establish the parent links.
* This makes the decoded tree match the tree built by the mutation methods.
*/
var count = [_childNodes count];
while (count--)
{
var child = [_childNodes objectAtIndex:count];
[self _validateChildNode:child];
child._parentNode = self;
}
}
return self;
+19 -2
View File
@@ -1474,6 +1474,24 @@ var CPViewHighDPIDrawingEnabled = YES;
origin.y *= size.height / frameSize.height;
}
var newScaleSize;
if (size && size.width !== 0 && size.height !== 0 && frameSize)
newScaleSize = CGSizeMake(frameSize.width / size.width, frameSize.height / size.height);
else
newScaleSize = CGSizeMake(1.0, 1.0);
// Only update and propagate if the scale factor has actually changed
if (!CGSizeEqualToSize(_scaleSize, newScaleSize))
{
[self willChangeValueForKey:@"scaleSize"];
_scaleSize = newScaleSize;
_isScaled = (_scaleSize.width !== 1.0 || _scaleSize.height !== 1.0);
[self didChangeValueForKey:@"scaleSize"];
[self _scaleSizeUnitSquareToSize:CGSizeMake(1.0, 1.0)];
}
if (_layer)
[_layer _owningViewBoundsChanged];
@@ -1487,7 +1505,6 @@ var CPViewHighDPIDrawingEnabled = YES;
[self _updateTrackingAreasWithRecursion:YES];
}
/*!
Notifies subviews that the superview changed size.
@param aSize the size of the old superview
@@ -2606,7 +2623,7 @@ setBoundsOrigin:
*/
- (void)_scaleSizeUnitSquareToSize:(CGSize)aSize
{
_hierarchyScaleSize = CGSizeMakeCopy([_superview _hierarchyScaleSize]);
_hierarchyScaleSize = _superview ? CGSizeMakeCopy([_superview _hierarchyScaleSize]) : CGSizeMake(1.0, 1.0);
if (_isScaled)
{
+2 -2
View File
@@ -481,9 +481,9 @@ _CPPlatformWindowWillCloseNotification = @"_CPPlatformWindowWillCloseNotificatio
_DOMWindow.addEventListener("blur", onBlurEventCallback, NO);
_DOMWindow.addEventListener("focus", onFocusEventCallback, NO);
_DOMWindow.addEventListener("unload", function()
_DOMWindow.addEventListener("pagehide", function()
{
_DOMWindow.removeEventListener("unload", arguments.callee, NO);
_DOMWindow.removeEventListener("pagehide", arguments.callee, NO);
[self blurEvent:nil];
[self _notifyPlatformWindowWillClose];
+19 -16
View File
@@ -109,28 +109,31 @@ var CPDateReferenceDate = new Date(Date.UTC(2001, 0, 1, 0, 0, 0, 0));
}
/*!
Returns a CPDate initialized with a date and time specified by the given
string in international date format YYYY-MM-DD HH:MM:SS ±HHMM (e.g.
2009-11-17 17:52:04 +0000).
Returns a CPDate initialized with a date and time specified by the given
string in international date format YYYY-MM-DD HH:MM:SS ±HHMM (e.g.
2009-11-17 17:52:04 +0000).
The offset is taken verbatim from the string; the result does not depend
on the host's local time zone or any CPTimeZone data. Callers needing an
offset derived from an actual IANA zone (e.g. accounting for DST) are
responsible for resolving it themselves via the browser's Intl API before
constructing the string.
*/
- (id)initWithString:(CPString)description
{
var format = new RegExp("(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2}) ([-+])(\\d{2})(\\d{2})"),
d = description.match(new RegExp(format));
var format = new RegExp("(\\d{4})-(\\d{2})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2}) ([-+])(\\d{2})(\\d{2})"),
d = description.match(format);
if (!d || d.length != 10)
[CPException raise:CPInvalidArgumentException
reason:"initWithString: the string must be in YYYY-MM-DD HH:MM:SS ±HHMM format"];
if (!d || d.length != 10)
[CPException raise:CPInvalidArgumentException
reason:"initWithString: the string must be in YYYY-MM-DD HH:MM:SS ±HHMM format"];
var date = new Date(d[1], d[2] - 1, d[3]),
timeZoneOffset = (Number(d[8]) * 60 + Number(d[9])) * (d[7] === '-' ? 1 : -1);
var timeZoneOffsetMinutes = (Number(d[8]) * 60 + Number(d[9])) * (d[7] === '-' ? 1 : -1),
utcMillis = Date.UTC(Number(d[1]), Number(d[2]) - 1, Number(d[3]),
Number(d[4]), Number(d[5]), Number(d[6]));
date.setHours(d[4]);
date.setMinutes(d[5]);
date.setSeconds(d[6]);
self = new Date(date.getTime() + (timeZoneOffset - date.getTimezoneOffset()) * 60 * 1000);
return self;
self = new Date(utcMillis + timeZoneOffsetMinutes * 60 * 1000);
return self;
}
- (CPTimeInterval)timeIntervalSinceDate:(CPDate)anotherDate
Executable → Regular
+4 -4
View File
@@ -252,8 +252,8 @@ if (Error.prototype._userInfo !== null)
function _CPMethodCallString(anObject, aSelector)
{
var prefix = class_isMetaClass(anObject.isa) ? "+" : "-";
return prefix + "[" + [anObject className] + " " + aSelector + "]: ";
var prefix = class_isMetaClass(anObject.isa) ? "+" : "-";
return prefix + "[" + [anObject className] + " " + aSelector + "]: ";
}
function _CPRaiseInvalidAbstractInvocation(anObject, aSelector)
@@ -264,13 +264,13 @@ function _CPRaiseInvalidAbstractInvocation(anObject, aSelector)
function _CPRaiseInvalidArgumentException(anObject, aSelector, aMessage)
{
[CPException raise:CPInvalidArgumentException
reason:METHOD_CALL_STRING() + aMessage];
reason:_CPMethodCallString(anObject, aSelector) + aMessage];
}
function _CPRaiseRangeException(anObject, aSelector, anIndex, aCount)
{
[CPException raise:CPRangeException
reason:METHOD_CALL_STRING() + "index (" + anIndex + ") beyond bounds (" + aCount + ")"];
reason:_CPMethodCallString(anObject, aSelector) + "index (" + anIndex + ") beyond bounds (" + aCount + ")"];
}
function _CPReportLenientDeprecation(/*Class*/ aClass, /*SEL*/ oldSelector, /*SEL*/ newSelector)
+615
View File
@@ -0,0 +1,615 @@
/*
* CPLanguageModel.j
* Foundation
*
*
* 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.
*/
@import "CPObject.j"
@import "CPString.j"
@import "CPError.j"
@import "CPDictionary.j"
@import "CPBundle.j"
@import "CPUserDefaults.j"
// File-scoped fallback configuration parameters
var CPLanguageModelSessionFallbackServiceType = @"ollama",
CPLanguageModelSessionFallbackEndpoint = @"http://localhost:11434/api/generate",
CPLanguageModelSessionFallbackModel = @"gemma4:e4b",
CPLanguageModelSessionFallbackAPIKey = @"",
CPLanguageModelSessionFallbackAPIKeyUserDefaultKey = @"",
CPLanguageModelSessionEndorsesFallback = NO;
/*!
@ingroup foundation
@class CPSystemLanguageModel
CPSystemLanguageModel provides a standard query interface to inspect the
availability of client-side, on-device large language models (such as Gemma Nano on Chrome)
in the active web browser runtime.
*/
@implementation CPSystemLanguageModel : CPObject
var sharedInstance = nil;
/*!
Returns the singleton system language model monitor.
@return the default CPSystemLanguageModel instance
*/
+ (id)defaultModel
{
if (!sharedInstance)
sharedInstance = [[CPSystemLanguageModel alloc] init];
return sharedInstance;
}
/*!
Asynchronously queries the active browser environment to determine if on-device
language models are supported and readily available to execute prompts.
@param completionHandler a callback block executed with a boolean parameter (supported)
*/
- (void)supportsLocaleWithCompletionHandler:(Function)completionHandler
{
if (typeof window === "undefined" || !completionHandler)
{
if (completionHandler)
completionHandler(NO);
return;
}
(async function() {
var supported = false;
try {
if (window.ai && window.ai.languageModel) {
// Pass language options to align with the creation options
var options = {
expectedInputs: [{ type: "text", languages: ["en"] }],
expectedOutputs: [{ type: "text", languages: ["en"] }]
};
if (typeof window.ai.languageModel.availability === 'function') {
var avail = await window.ai.languageModel.availability(options);
supported = (avail === "readily" || avail === "available" || avail === "after-download");
} else if (typeof window.ai.languageModel.capabilities === 'function') {
var caps = await window.ai.languageModel.capabilities(options);
supported = (caps.available === "readily" || caps.available === "after-download");
} else {
supported = true;
}
}
else if (window.LanguageModel) {
supported = true;
}
} catch (e) {
supported = false;
}
completionHandler(supported);
})();
}
@end
/*!
@ingroup foundation
@class CPLanguageModelSession
CPLanguageModelSession manages an active session with a local on-device
language model. If the active browser does not support on-device models, the session
gracefully and transparently falls back to configured remote server endpoints.
@discussion
CPLanguageModelSession handles text generation prompts. If on-device AI
(like Gemma Nano) is supported by the browser, it is utilized directly.
Otherwise, or if CPLanguageModelSessionEndorsesFallback is configured to YES,
the session automatically falls back to configured network-based providers
(such as local Ollama, Groq, or OpenRouter).
Fallback configurations can be populated globally using the application's Info.plist
via the following keys:
<pre>
CPEndorseLanguageModelFallback - YES to bypass on-device models and force fallback
CPDefaultLanguageModelService - "ollama" | "groq" | "gemini" | "openrouter"
CPDefaultLanguageModelEndpoint - API Endpoint (e.g. Ollama URL)
CPDefaultLanguageModelModel - Model name string
CPDefaultLanguageModelAPIKeyUserDefaultKey - CPUserDefaults key containing the actual API token
CPDefaultLanguageModelAPIKey - Authentication token string (Unsecure direct fallback)
</pre>
*/
@implementation CPLanguageModelSession : CPObject
{
id _chromeSession @accessors(property=chromeSession);
CPString _instructions @accessors(property=instructions);
CPString _fallbackServiceType @accessors(property=fallbackServiceType);
CPString _fallbackEndpoint @accessors(property=fallbackEndpoint);
CPString _fallbackModel @accessors(property=fallbackModel);
CPString _fallbackAPIKey @accessors(property=fallbackAPIKey);
}
/*!
Initializes fallback defaults and "Endorsement" flags from the application's Info.plist.
*/
+ (void)initialize
{
if (self === [CPLanguageModelSession class])
{
var bundle = [CPBundle mainBundle];
CPLanguageModelSessionEndorsesFallback = !![bundle objectForInfoDictionaryKey:@"CPEndorseLanguageModelFallback"];
CPLanguageModelSessionFallbackServiceType = [bundle objectForInfoDictionaryKey:@"CPDefaultLanguageModelService"] || @"ollama";
CPLanguageModelSessionFallbackEndpoint = [bundle objectForInfoDictionaryKey:@"CPDefaultLanguageModelEndpoint"] || @"http://localhost:11434/api/generate";
CPLanguageModelSessionFallbackModel = [bundle objectForInfoDictionaryKey:@"CPDefaultLanguageModelModel"] || @"gemma4:e4b";
CPLanguageModelSessionFallbackAPIKey = [bundle objectForInfoDictionaryKey:@"CPDefaultLanguageModelAPIKey"] || @"";
CPLanguageModelSessionFallbackAPIKeyUserDefaultKey = [bundle objectForInfoDictionaryKey:@"CPDefaultLanguageModelAPIKeyUserDefaultKey"] || @"";
}
}
/*!
Configures whether the session should bypass native browser AI models and force fallback network routing.
@param shouldEndorse YES to bypass native AI; NO to prioritize native AI if available
*/
+ (void)setEndorsesFallback:(BOOL)shouldEndorse
{
CPLanguageModelSessionEndorsesFallback = shouldEndorse;
}
/*!
Indicates if the session bypasses native browser AI models.
@return YES if forced fallback is active; NO otherwise
*/
+ (BOOL)endorsesFallback
{
return CPLanguageModelSessionEndorsesFallback;
}
/*!
Configures fallback details dynamically, overriding any defaults loaded from Info.plist.
@param serviceType the service type (e.g. @"ollama", @"groq", @"gemini", @"openrouter")
@param endpoint the network target URL
@param model the model identifier
@param apiKey the API key string
*/
+ (void)setFallbackServiceType:(CPString)serviceType endpoint:(CPString)endpoint model:(CPString)model apiKey:(CPString)apiKey
{
CPLanguageModelSessionFallbackServiceType = serviceType;
CPLanguageModelSessionFallbackEndpoint = endpoint;
CPLanguageModelSessionFallbackModel = model;
CPLanguageModelSessionFallbackAPIKey = apiKey;
}
/*!
Configures the CPUserDefaults key used to dynamically look up the API key.
@param keyName the user defaults key name containing the actual credentials
*/
+ (void)setFallbackAPIKeyUserDefaultKey:(CPString)keyName
{
CPLanguageModelSessionFallbackAPIKeyUserDefaultKey = keyName;
}
/*!
Gets the CPUserDefaults key name used to dynamically look up the API key.
@return the user defaults key name
*/
+ (CPString)fallbackAPIKeyUserDefaultKey
{
return CPLanguageModelSessionFallbackAPIKeyUserDefaultKey;
}
/*!
Initializes a language model session with specific system instructions.
@param instructions the system instructions or context prompt
@return the initialized session
*/
- (id)initWithInstructions:(CPString)instructions
{
self = [super init];
if (self)
{
_instructions = instructions;
_chromeSession = nil;
_fallbackServiceType = nil;
_fallbackEndpoint = nil;
_fallbackModel = nil;
_fallbackAPIKey = nil;
}
return self;
}
/*!
Initializes a language model session with specific system instructions and an explicit programmatic API key.
@param instructions the system instructions or context prompt
@param apiKey the fallback API key to use specifically for this session
@return the initialized session
*/
- (id)initWithInstructions:(CPString)instructions apiKey:(CPString)apiKey
{
self = [self initWithInstructions:instructions];
if (self)
{
_fallbackAPIKey = apiKey;
}
return self;
}
/*!
Initializes a language model session with instructions and explicit fallback settings.
@param instructions the system instructions or context prompt
@param options dictionary containing custom fallback configuration (e.g. @{ @"serviceType": ..., @"apiKey": ... })
@return the initialized session
*/
- (id)initWithInstructions:(CPString)instructions fallbackOptions:(CPDictionary)options
{
self = [self initWithInstructions:instructions];
if (self)
{
if (options)
{
_fallbackServiceType = [options objectForKey:@"serviceType"];
_fallbackEndpoint = [options objectForKey:@"endpoint"];
_fallbackModel = [options objectForKey:@"model"];
_fallbackAPIKey = [options objectForKey:@"apiKey"];
}
}
return self;
}
/*!
Sends a query prompt to the language model session.
@param prompt the query text to analyze
@param completionHandler a callback receiving the response string or a CPError instance
*/
- (void)respondToPrompt:(CPString)prompt options:(id)options completionHandler:(Function)completionHandler
{
// If the developer forced fallback, bypass native browser execution
if (CPLanguageModelSessionEndorsesFallback)
{
[self _executeRemoteFallbackWithPrompt:prompt options:options completionHandler:completionHandler];
return;
}
if (_chromeSession)
{
[self _executePrompt:prompt options:options completionHandler:completionHandler];
return;
}
var instructions = [self instructions];
[CPLanguageModelSession _getChromeFactoryWithCompletionHandler:function(factory, error) {
if (error) {
[self _executeRemoteFallbackWithPrompt:prompt options:options completionHandler:completionHandler];
return;
}
// Add the required expected input and output parameters
var sessionOptions = {
expectedInputs: [{ type: "text", languages: ["en"] }],
expectedOutputs: [{ type: "text", languages: ["en"] }]
};
if (instructions) {
sessionOptions.systemPrompt = instructions;
}
factory.create(sessionOptions).then(function(session) {
[self setChromeSession:session];
[self _executePrompt:prompt options:options completionHandler:completionHandler];
}).catch(function(err) {
[self _executeRemoteFallbackWithPrompt:prompt options:options completionHandler:completionHandler];
});
}];
}
/*!
Sends a query prompt and streams the response chunk-by-chunk for live UI rendering.
@param prompt the query text to analyze
@param chunkHandler a callback block executed as text increments are received
@param completionHandler a final callback block executed when generation completes
*/
- (void)respondToPrompt:(CPString)prompt
onChunkReceived:(Function)chunkHandler
completed:(Function)completionHandler
{
if (CPLanguageModelSessionEndorsesFallback)
{
[self _executeRemoteFallbackWithPrompt:prompt options:nil completionHandler:function(res, err) {
if (!err && chunkHandler)
chunkHandler(res);
completionHandler(res, err);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
}];
return;
}
if (_chromeSession)
{
[self _executePromptStreaming:prompt onChunkReceived:chunkHandler completed:completionHandler];
return;
}
var instructions = [self instructions];
[CPLanguageModelSession _getChromeFactoryWithCompletionHandler:function(factory, error) {
if (error) {
[self _executeRemoteFallbackWithPrompt:prompt options:nil completionHandler:function(res, err) {
if (!err && chunkHandler)
chunkHandler(res);
completionHandler(res, err);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
}];
return;
}
// Add the required expected input and output parameters
var options = {
expectedInputs: [{ type: "text", languages: ["en"] }],
expectedOutputs: [{ type: "text", languages: ["en"] }]
};
if (instructions)
options.systemPrompt = instructions;
factory.create(options).then(function(session) {
[self setChromeSession:session];
[self _executePromptStreaming:prompt onChunkReceived:chunkHandler completed:completionHandler];
}).catch(function(err)
{
[self _executeRemoteFallbackWithPrompt:prompt options:nil completionHandler:function(res, err) {
if (!err && chunkHandler)
chunkHandler(res);
completionHandler(res, err);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
}];
});
}];
}
/*!
Closes the session and releases associated memory resources on-device.
*/
- (void)destroy
{
if (_chromeSession && typeof _chromeSession.destroy === "function")
{
_chromeSession.destroy();
_chromeSession = nil;
}
}
// MARK: - Private Helper Methods
+ (void)_getChromeFactoryWithCompletionHandler:(Function)completionHandler
{
if (typeof window === "undefined")
{
var cpError = [CPError errorWithDomain:@"CPLanguageModelErrorDomain" code:-1 userInfo:[CPDictionary dictionaryWithObject:@"Execution environment is not a browser window." forKey:CPLocalizedDescriptionKey]];
completionHandler(nil, cpError);
return;
}
if (window.ai && window.ai.languageModel)
completionHandler(window.ai.languageModel, nil);
else if (window.LanguageModel)
completionHandler(window.LanguageModel, nil);
else
completionHandler(nil, [CPError errorWithDomain:@"CPLanguageModelErrorDomain" code:0 userInfo:nil]);
}
- (void)_executePrompt:(CPString)prompt options:(id)options completionHandler:(Function)completionHandler
{
var promptPromise = options ? _chromeSession.prompt(prompt, options) : _chromeSession.prompt(prompt);
promptPromise.then(function(result) {
completionHandler(result, nil);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
}).catch(function(err) {
var cpError = [CPError errorWithDomain:@"CPLanguageModelErrorDomain"
code:2
userInfo:[CPDictionary dictionaryWithObject:err.message forKey:CPLocalizedDescriptionKey]];
completionHandler(nil, cpError);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
});
}
- (CPString)_resolvedFallbackServiceType
{
return _fallbackServiceType || CPLanguageModelSessionFallbackServiceType;
}
- (CPString)_resolvedFallbackEndpoint
{
return _fallbackEndpoint || CPLanguageModelSessionFallbackEndpoint;
}
- (CPString)_resolvedFallbackModel
{
return _fallbackModel || CPLanguageModelSessionFallbackModel;
}
- (CPString)_resolvedFallbackAPIKey
{
// 1. Session instance explicit key has highest priority
if (_fallbackAPIKey)
return _fallbackAPIKey;
// 2. Class fallback API key set programmatically takes second priority
if (CPLanguageModelSessionFallbackAPIKey)
return CPLanguageModelSessionFallbackAPIKey;
// 3. Dynamic lookup from standard user defaults takes final priority
if (CPLanguageModelSessionFallbackAPIKeyUserDefaultKey)
{
var defaults = [CPUserDefaults standardUserDefaults],
apiKey = [defaults objectForKey:CPLanguageModelSessionFallbackAPIKeyUserDefaultKey];
if (apiKey)
return apiKey;
}
return @"";
}
- (void)_executeRemoteFallbackWithPrompt:(CPString)prompt options:(id)options completionHandler:(Function)completionHandler
{
var systemPrompt = [self instructions],
serviceType = [self _resolvedFallbackServiceType],
endpoint = [self _resolvedFallbackEndpoint],
model = [self _resolvedFallbackModel],
apiKey = [self _resolvedFallbackAPIKey];
var reqUrl = @"",
headers = { "Content-Type": "application/json" },
payload = {};
if (serviceType === @"groq")
{
reqUrl = "https://api.groq.com/openai/v1/chat/completions";
headers["Authorization"] = "Bearer " + apiKey;
payload = {
"model": model,
"messages": [
{ "role": "system", "content": systemPrompt },
{ "role": "user", "content": prompt }
],
"temperature": 0
};
}
else if (serviceType === @"gemini")
{
reqUrl = "https://generativelanguage.googleapis.com/v1beta/models/" + model + ":generateContent?key=" + apiKey;
payload = {
"contents": [
{ "parts": [{ "text": systemPrompt + "\n\n" + prompt }] }
],
"generationConfig": { "temperature": 0 }
};
}
else if (serviceType === @"openrouter")
{
reqUrl = "https://openrouter.ai/api/v1/chat/completions";
headers["Authorization"] = "Bearer " + apiKey;
payload = {
"model": model,
"messages": [
{ "role": "system", "content": systemPrompt },
{ "role": "user", "content": prompt }
],
"temperature": 0
};
}
else
{
reqUrl = endpoint || "http://localhost:11434/api/generate";
payload = {
"model": model,
"prompt": systemPrompt + "\n\n" + prompt,
"stream": false,
"options": { "temperature": 0 }
};
}
fetch(reqUrl, {
method: 'POST',
headers: headers,
body: JSON.stringify(payload)
})
.then(function(response) {
if (!response.ok) {
throw new Error("HTTP error! Status: " + response.status);
}
return response.json();
})
.then(function(data) {
var responseText = "";
if (serviceType === "groq" || serviceType === "openrouter") {
responseText = (data.choices && data.choices[0] && data.choices[0].message) ? data.choices[0].message.content : "";
} else if (serviceType === "gemini") {
responseText = (data.candidates && data.candidates[0] && data.candidates[0].content && data.candidates[0].content.parts) ? data.candidates[0].content.parts[0].text : "";
} else {
responseText = data.response || "";
}
completionHandler(responseText, nil);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
})
.catch(function(err) {
var cpError = [CPError errorWithDomain:@"CPLanguageModelErrorDomain"
code:4
userInfo:[CPDictionary dictionaryWithObject:err.message forKey:CPLocalizedDescriptionKey]];
completionHandler(nil, cpError);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
});
}
- (void)_executePromptStreaming:(CPString)prompt
onChunkReceived:(Function)chunkHandler
completed:(Function)completionHandler
{
var stream;
try {
stream = _chromeSession.promptStreaming(prompt);
} catch (err) {
var cpError = [CPError errorWithDomain:@"CPLanguageModelErrorDomain"
code:3
userInfo:[CPDictionary dictionaryWithObject:err.message forKey:CPLocalizedDescriptionKey]];
completionHandler(nil, cpError);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
return;
}
(async function() {
var lastChunk = "";
try {
for await (const chunk of stream) {
lastChunk = chunk;
if (chunkHandler) {
chunkHandler(chunk);
}
}
if (completionHandler)
{
completionHandler(lastChunk, nil);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
}
} catch (err) {
if (completionHandler) {
var cpError = [CPError errorWithDomain:@"CPLanguageModelErrorDomain"
code:2
userInfo:[CPDictionary dictionaryWithObject:err.message forKey:CPLocalizedDescriptionKey]];
completionHandler(nil, cpError);
[[CPRunLoop currentRunLoop] limitDateForMode:CPDefaultRunLoopMode]; // pump run loop to force GUI update
}
}
})();
}
@end
+18 -4
View File
@@ -105,17 +105,31 @@
// MARK: Creating a Dictionary Representation
/*!
Returns a dictionary representation of the map table.
Note: This will only work correctly if all keys are strings.
Returns a dictionary representation of the map table.
Note: This will only work correctly if all keys are strings.
@return A CPDictionary containing the entries of the map table.
@return A CPDictionary containing the entries of the map table.
*/
- (CPDictionary)dictionaryRepresentation
{
var dictionary = [CPDictionary dictionary];
for (var [key, value] of _map.entries())
// TODO: Revert to ES6 destructuring in the loop declaration once the
// legacy compiler is retired. The legacy parser fails to recognize
// `var [key, value]` as a local scope declaration, causing the variables
// to leak to the global object and emitting false-positive "uninitialized
// global variable" warnings, which is unacceptable for CI hygiene.
//
// for (var [key, value] of _map.entries())
// {
// [dictionary setObject:value forKey:key];
// }
for (var entry of _map.entries())
{
var key = entry[0],
value = entry[1];
[dictionary setObject:value forKey:key];
}
+14 -14
View File
@@ -231,8 +231,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
_minimumFractionDigits = 0;
_maximumFractionDigits = 3;
// Invalidate the cached number handler.
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
_numberHandler = nil;
break;
@@ -240,8 +240,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
_minimumFractionDigits = 2;
_maximumFractionDigits = 2;
// Invalidate the cached number handler.
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
_numberHandler = nil;
break;
}
@@ -251,8 +251,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
{
_roundingMode = aRoundingMode;
// Invalidate the cached number handler.
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
_numberHandler = nil;
}
@@ -260,8 +260,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
{
_minimumFractionDigits = aNumber;
// Invalidate the cached number handler.
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
_numberHandler = nil;
}
@@ -269,8 +269,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
{
_maximumFractionDigits = aNumber;
// Invalidate the cached number handler.
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
_numberHandler = nil;
}
@@ -278,8 +278,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
{
_minimum = aNumber;
// Invalidate the cached number handler.
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
_numberHandler = nil;
}
@@ -287,8 +287,8 @@ var NumberRegex = new RegExp('(-)?(\\d*)(\\.(\\d*))?');
{
_maximum = aNumber;
// Invalidate the cached number handler.
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
// It rebuilds on next use.
// Replaces pre-processor directive, which is incompatible with the new compiler
_numberHandler = nil;
}
+370 -370
View File
@@ -23,7 +23,7 @@
*/
- (id)initWithCapacity:(unsigned)aCapacity
{
return [self init];
return [self init];
}
/*!
@@ -32,7 +32,7 @@
*/
+ (id)setWithCapacity:(CPUInteger)aCapacity
{
return [[self alloc] initWithCapacity:aCapacity];
return [[self alloc] initWithCapacity:aCapacity];
}
/*!
@@ -41,16 +41,16 @@
*/
- (void)filterUsingPredicate:(CPPredicate)aPredicate
{
var object,
objectEnumerator = [self objectEnumerator];
var object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
if (![aPredicate evaluateWithObject:object])
{
[self removeObject:object];
}
}
while ((object = [objectEnumerator nextObject]) != nil)
{
if (![aPredicate evaluateWithObject:object])
{
[self removeObject:object];
}
}
}
/*!
@@ -59,7 +59,7 @@
*/
- (void)removeObject:(id)anObject
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
/*!
@@ -68,13 +68,13 @@
*/
- (void)removeObjectsInArray:(CPArray)anArray
{
var index = 0,
count = [anArray count];
var index = 0,
count = [anArray count];
for (; index < count; ++index)
{
[self removeObject:[anArray objectAtIndex:index]];
}
for (; index < count; ++index)
{
[self removeObject:[anArray objectAtIndex:index]];
}
}
/*!
@@ -82,13 +82,13 @@
*/
- (void)removeAllObjects
{
var object,
objectEnumerator = [self objectEnumerator];
var object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
[self removeObject:object];
}
while ((object = [objectEnumerator nextObject]) != nil)
{
[self removeObject:object];
}
}
/*!
@@ -97,12 +97,12 @@
*/
- (void)addObjectsFromArray:(CPArray)objects
{
var count = [objects count];
var count = [objects count];
while (count--)
{
[self addObject:objects[count]];
}
while (count--)
{
[self addObject:objects[count]];
}
}
/*!
@@ -111,13 +111,13 @@
*/
- (void)unionSet:(CPSet)aSet
{
var object,
objectEnumerator = [aSet objectEnumerator];
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
[self addObject:object];
}
while ((object = [objectEnumerator nextObject]) != nil)
{
[self addObject:object];
}
}
/*!
@@ -126,13 +126,13 @@
*/
- (void)minusSet:(CPSet)aSet
{
var object,
objectEnumerator = [aSet objectEnumerator];
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
[self removeObject:object];
}
while ((object = [objectEnumerator nextObject]) != nil)
{
[self removeObject:object];
}
}
/*!
@@ -141,24 +141,24 @@
*/
- (void)intersectSet:(CPSet)aSet
{
var object,
objectEnumerator = [self objectEnumerator],
objectsToRemove = [];
var object,
objectEnumerator = [self objectEnumerator],
objectsToRemove = [];
while ((object = [objectEnumerator nextObject]) != nil)
{
if (![aSet containsObject:object])
{
objectsToRemove.push(object);
}
}
while ((object = [objectEnumerator nextObject]) != nil)
{
if (![aSet containsObject:object])
{
objectsToRemove.push(object);
}
}
var count = [objectsToRemove count];
var count = [objectsToRemove count];
while (count--)
{
[self removeObject:objectsToRemove[count]];
}
while (count--)
{
[self removeObject:objectsToRemove[count]];
}
}
/*!
@@ -167,8 +167,8 @@
*/
- (void)setSet:(CPSet)aSet
{
[self removeAllObjects];
[self unionSet:aSet];
[self removeAllObjects];
[self unionSet:aSet];
}
@end
@@ -180,69 +180,69 @@
- (id)valueForKeyPath:(CPString)aKeyPath
{
if (!aKeyPath)
{
[self valueForUndefinedKey:@"<empty path>"];
}
if (!aKeyPath)
{
[self valueForUndefinedKey:@"<empty path>"];
}
if (aKeyPath.charAt(0) === "@")
{
var dotIndex = aKeyPath.indexOf("."),
operator,
parameter;
if (aKeyPath.charAt(0) === "@")
{
var dotIndex = aKeyPath.indexOf("."),
operator,
parameter;
if (dotIndex !== -1)
{
operator = aKeyPath.substring(1, dotIndex);
parameter = aKeyPath.substring(dotIndex + 1);
}
else
{
operator = aKeyPath.substring(1);
}
if (dotIndex !== -1)
{
operator = aKeyPath.substring(1, dotIndex);
parameter = aKeyPath.substring(dotIndex + 1);
}
else
{
operator = aKeyPath.substring(1);
}
return [_CPCollectionKVCOperator performOperation:operator withCollection:self propertyPath:parameter];
}
else
{
var valuesForKeySet = [CPSet set],
containedObject,
containedObjectValue,
containedObjectEnumerator = [self objectEnumerator];
return [_CPCollectionKVCOperator performOperation:operator withCollection:self propertyPath:parameter];
}
else
{
var valuesForKeySet = [CPSet set],
containedObject,
containedObjectValue,
containedObjectEnumerator = [self objectEnumerator];
while ((containedObject = [containedObjectEnumerator nextObject]) != nil)
{
containedObjectValue = [containedObject valueForKeyPath:aKeyPath];
while ((containedObject = [containedObjectEnumerator nextObject]) != nil)
{
containedObjectValue = [containedObject valueForKeyPath:aKeyPath];
if (containedObjectValue == nil)
{
containedObjectValue = [CPNull null];
}
if (containedObjectValue == nil)
{
containedObjectValue = [CPNull null];
}
[valuesForKeySet addObject:containedObjectValue];
}
[valuesForKeySet addObject:containedObjectValue];
}
return valuesForKeySet;
}
return valuesForKeySet;
}
}
- (id)valueForKey:(CPString)aKey
{
// If the key starts with @, it is an operator path.
// If not, it is a property path that we want applied to all members.
// In either case, valueForKeyPath: handles both scenarios correctly.
return [self valueForKeyPath:aKey];
// If the key starts with @, it is an operator path.
// If not, it is a property path that we want applied to all members.
// In either case, valueForKeyPath: handles both scenarios correctly.
return [self valueForKeyPath:aKey];
}
- (void)setValue:(id)aValue forKey:(CPString)aKey
{
var containedObject,
containedObjectEnumerator = [self objectEnumerator];
var containedObject,
containedObjectEnumerator = [self objectEnumerator];
while ((containedObject = [containedObjectEnumerator nextObject]) != nil)
{
[containedObject setValue:aValue forKey:aKey];
}
while ((containedObject = [containedObjectEnumerator nextObject]) != nil)
{
[containedObject setValue:aValue forKey:aKey];
}
}
@end
@@ -254,22 +254,22 @@
- (id)mutableSetValueForKey:(id)aKey
{
return [[_CPKVCSet alloc] initWithKey:aKey forProxyObject:self];
return [[_CPKVCSet alloc] initWithKey:aKey forProxyObject:self];
}
- (id)mutableSetValueForKeyPath:(id)aKeyPath
{
var dotIndex = aKeyPath.indexOf(".");
var dotIndex = aKeyPath.indexOf(".");
if (dotIndex < 0)
{
return [self mutableSetValueForKey:aKeyPath];
}
if (dotIndex < 0)
{
return [self mutableSetValueForKey:aKeyPath];
}
var firstPart = aKeyPath.substring(0, dotIndex),
lastPart = aKeyPath.substring(dotIndex + 1);
var firstPart = aKeyPath.substring(0, dotIndex),
lastPart = aKeyPath.substring(dotIndex + 1);
return [[self valueForKeyPath:firstPart] mutableSetValueForKeyPath:lastPart];
return [[self valueForKeyPath:firstPart] mutableSetValueForKeyPath:lastPart];
}
@end
@@ -279,387 +279,387 @@
@implementation _CPKVCSet : CPMutableSet
{
id _proxyObject;
id _key;
id _proxyObject;
id _key;
SEL _accessSEL;
Function _access;
SEL _accessSEL;
Function _access;
SEL _setSEL;
Function _set;
SEL _setSEL;
Function _set;
SEL _countSEL;
Function _count;
SEL _countSEL;
Function _count;
SEL _enumeratorSEL;
Function _enumerator;
SEL _enumeratorSEL;
Function _enumerator;
SEL _memberSEL;
Function _member;
SEL _memberSEL;
Function _member;
SEL _addSEL;
Function _add;
SEL _addSEL;
Function _add;
SEL _addManySEL;
Function _addMany;
SEL _addManySEL;
Function _addMany;
SEL _removeSEL;
Function _remove;
SEL _removeSEL;
Function _remove;
SEL _removeManySEL;
Function _removeMany;
SEL _removeManySEL;
Function _removeMany;
SEL _intersectSEL;
Function _intersect;
SEL _intersectSEL;
Function _intersect;
}
+ (id)alloc
{
var set = [CPMutableSet set];
var set = [CPMutableSet set];
set.isa = self;
set.isa = self;
var ivars = class_copyIvarList(self),
count = ivars.length;
var ivars = class_copyIvarList(self),
count = ivars.length;
while (count--)
{
set[ivar_getName(ivars[count])] = nil;
}
while (count--)
{
set[ivar_getName(ivars[count])] = nil;
}
return set;
return set;
}
- (id)initWithKey:(id)aKey forProxyObject:(id)anObject
{
self = [super init];
self = [super init];
_key = aKey;
_proxyObject = anObject;
_key = aKey;
_proxyObject = anObject;
var capitalizedKey = _key.charAt(0).toUpperCase() + _key.substring(1);
var capitalizedKey = _key.charAt(0).toUpperCase() + _key.substring(1);
_accessSEL = sel_getName(_key);
if ([_proxyObject respondsToSelector:_accessSEL])
{
_access = [_proxyObject methodForSelector:_accessSEL];
}
_accessSEL = sel_getName(_key);
if ([_proxyObject respondsToSelector:_accessSEL])
{
_access = [_proxyObject methodForSelector:_accessSEL];
}
_setSEL = sel_getName(@"set" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_setSEL])
{
_set = [_proxyObject methodForSelector:_setSEL];
}
_setSEL = sel_getName(@"set" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_setSEL])
{
_set = [_proxyObject methodForSelector:_setSEL];
}
_countSEL = sel_getName(@"countOf" + capitalizedKey);
if ([_proxyObject respondsToSelector:_countSEL])
{
_count = [_proxyObject methodForSelector:_countSEL];
}
_countSEL = sel_getName(@"countOf" + capitalizedKey);
if ([_proxyObject respondsToSelector:_countSEL])
{
_count = [_proxyObject methodForSelector:_countSEL];
}
_enumeratorSEL = sel_getName(@"enumeratorOf" + capitalizedKey);
if ([_proxyObject respondsToSelector:_enumeratorSEL])
{
_enumerator = [_proxyObject methodForSelector:_enumeratorSEL];
}
_enumeratorSEL = sel_getName(@"enumeratorOf" + capitalizedKey);
if ([_proxyObject respondsToSelector:_enumeratorSEL])
{
_enumerator = [_proxyObject methodForSelector:_enumeratorSEL];
}
_memberSEL = sel_getName(@"memberOf" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_memberSEL])
{
_member = [_proxyObject methodForSelector:_memberSEL];
}
_memberSEL = sel_getName(@"memberOf" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_memberSEL])
{
_member = [_proxyObject methodForSelector:_memberSEL];
}
_addSEL = sel_getName(@"add" + capitalizedKey + "Object:");
if ([_proxyObject respondsToSelector:_addSEL])
{
_add = [_proxyObject methodForSelector:_addSEL];
}
_addSEL = sel_getName(@"add" + capitalizedKey + "Object:");
if ([_proxyObject respondsToSelector:_addSEL])
{
_add = [_proxyObject methodForSelector:_addSEL];
}
_addManySEL = sel_getName(@"add" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_addManySEL])
{
_addMany = [_proxyObject methodForSelector:_addManySEL];
}
_addManySEL = sel_getName(@"add" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_addManySEL])
{
_addMany = [_proxyObject methodForSelector:_addManySEL];
}
_removeSEL = sel_getName(@"remove" + capitalizedKey + "Object:");
if ([_proxyObject respondsToSelector:_removeSEL])
{
_remove = [_proxyObject methodForSelector:_removeSEL];
}
_removeSEL = sel_getName(@"remove" + capitalizedKey + "Object:");
if ([_proxyObject respondsToSelector:_removeSEL])
{
_remove = [_proxyObject methodForSelector:_removeSEL];
}
_removeManySEL = sel_getName(@"remove" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_removeManySEL])
{
_removeMany = [_proxyObject methodForSelector:_removeManySEL];
}
_removeManySEL = sel_getName(@"remove" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_removeManySEL])
{
_removeMany = [_proxyObject methodForSelector:_removeManySEL];
}
_intersectSEL = sel_getName(@"intersect" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_intersectSEL])
{
_intersect = [_proxyObject methodForSelector:_intersectSEL];
}
_intersectSEL = sel_getName(@"intersect" + capitalizedKey + ":");
if ([_proxyObject respondsToSelector:_intersectSEL])
{
_intersect = [_proxyObject methodForSelector:_intersectSEL];
}
return self;
return self;
}
- (id)_representedObject
{
if (_access)
{
return _access(_proxyObject, _accessSEL);
}
if (_access)
{
return _access(_proxyObject, _accessSEL);
}
return [_proxyObject valueForKey:_key];
return [_proxyObject valueForKey:_key];
}
- (void)_setRepresentedObject:(id)anObject
{
if (_set)
{
return _set(_proxyObject, _setSEL, anObject);
}
if (_set)
{
return _set(_proxyObject, _setSEL, anObject);
}
[_proxyObject setValue:anObject forKey:_key];
[_proxyObject setValue:anObject forKey:_key];
}
- (CPUInteger)count
{
if (_count)
{
return _count(_proxyObject, _countSEL);
}
if (_count)
{
return _count(_proxyObject, _countSEL);
}
return [[self _representedObject] count];
return [[self _representedObject] count];
}
- (CPEnumerator)objectEnumerator
{
if (_enumerator)
{
return _enumerator(_proxyObject, _enumeratorSEL);
}
if (_enumerator)
{
return _enumerator(_proxyObject, _enumeratorSEL);
}
return [[self _representedObject] objectEnumerator];
return [[self _representedObject] objectEnumerator];
}
- (id)member:(id)anObject
{
if (_member)
{
return _member(_proxyObject, _memberSEL, anObject);
}
if (_member)
{
return _member(_proxyObject, _memberSEL, anObject);
}
return [[self _representedObject] member:anObject];
return [[self _representedObject] member:anObject];
}
- (void)addObject:(id)anObject
{
if (_add)
{
_add(_proxyObject, _addSEL, anObject);
}
else if (_addMany)
{
var objectSet = [CPSet setWithObject:anObject];
_addMany(_proxyObject, _addManySEL, objectSet);
}
else
{
var target = [[self _representedObject] copy];
[target addObject:anObject];
[self _setRepresentedObject:target];
}
if (_add)
{
_add(_proxyObject, _addSEL, anObject);
}
else if (_addMany)
{
var objectSet = [CPSet setWithObject:anObject];
_addMany(_proxyObject, _addManySEL, objectSet);
}
else
{
var target = [[self _representedObject] copy];
[target addObject:anObject];
[self _setRepresentedObject:target];
}
}
- (void)addObjectsFromArray:(CPArray)objects
{
if (_addMany)
{
var objectSet = [CPSet setWithArray:objects];
_addMany(_proxyObject, _addManySEL, objectSet);
}
else if (_add)
{
var object,
objectEnumerator = [objects objectEnumerator];
if (_addMany)
{
var objectSet = [CPSet setWithArray:objects];
_addMany(_proxyObject, _addManySEL, objectSet);
}
else if (_add)
{
var object,
objectEnumerator = [objects objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_add(_proxyObject, _addSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target addObjectsFromArray:objects];
[self _setRepresentedObject:target];
}
while ((object = [objectEnumerator nextObject]) != nil)
{
_add(_proxyObject, _addSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target addObjectsFromArray:objects];
[self _setRepresentedObject:target];
}
}
- (void)unionSet:(CPSet)aSet
{
if (_addMany)
{
_addMany(_proxyObject, _addManySEL, aSet);
}
else if (_add)
{
var object,
objectEnumerator = [aSet objectEnumerator];
if (_addMany)
{
_addMany(_proxyObject, _addManySEL, aSet);
}
else if (_add)
{
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_add(_proxyObject, _addSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target unionSet:aSet];
[self _setRepresentedObject:target];
}
while ((object = [objectEnumerator nextObject]) != nil)
{
_add(_proxyObject, _addSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target unionSet:aSet];
[self _setRepresentedObject:target];
}
}
- (void)removeObject:(id)anObject
{
if (_remove)
{
_remove(_proxyObject, _removeSEL, anObject);
}
else if (_removeMany)
{
var objectSet = [CPSet setWithObject:anObject];
_removeMany(_proxyObject, _removeManySEL, objectSet);
}
else
{
var target = [[self _representedObject] copy];
[target removeObject:anObject];
[self _setRepresentedObject:target];
}
if (_remove)
{
_remove(_proxyObject, _removeSEL, anObject);
}
else if (_removeMany)
{
var objectSet = [CPSet setWithObject:anObject];
_removeMany(_proxyObject, _removeManySEL, objectSet);
}
else
{
var target = [[self _representedObject] copy];
[target removeObject:anObject];
[self _setRepresentedObject:target];
}
}
- (void)minusSet:(CPSet)aSet
{
if (_removeMany)
{
_removeMany(_proxyObject, _removeManySEL, aSet);
}
else if (_remove)
{
var object,
objectEnumerator = [aSet objectEnumerator];
if (_removeMany)
{
_removeMany(_proxyObject, _removeManySEL, aSet);
}
else if (_remove)
{
var object,
objectEnumerator = [aSet objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_remove(_proxyObject, _removeSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target minusSet:aSet];
[self _setRepresentedObject:target];
}
while ((object = [objectEnumerator nextObject]) != nil)
{
_remove(_proxyObject, _removeSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target minusSet:aSet];
[self _setRepresentedObject:target];
}
}
- (void)removeObjectsInArray:(CPArray)objects
{
if (_removeMany)
{
var objectSet = [CPSet setWithArray:objects];
_removeMany(_proxyObject, _removeManySEL, objectSet);
}
else if (_remove)
{
var object,
objectEnumerator = [objects objectEnumerator];
if (_removeMany)
{
var objectSet = [CPSet setWithArray:objects];
_removeMany(_proxyObject, _removeManySEL, objectSet);
}
else if (_remove)
{
var object,
objectEnumerator = [objects objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_remove(_proxyObject, _removeSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target removeObjectsInArray:objects];
[self _setRepresentedObject:target];
}
while ((object = [objectEnumerator nextObject]) != nil)
{
_remove(_proxyObject, _removeSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target removeObjectsInArray:objects];
[self _setRepresentedObject:target];
}
}
- (void)removeAllObjects
{
if (_removeMany)
{
var allObjectsSet = [[self _representedObject] copy];
_removeMany(_proxyObject, _removeManySEL, allObjectsSet);
}
else if (_remove)
{
var object,
objectEnumerator = [[[self _representedObject] copy] objectEnumerator];
if (_removeMany)
{
var allObjectsSet = [[self _representedObject] copy];
_removeMany(_proxyObject, _removeManySEL, allObjectsSet);
}
else if (_remove)
{
var object,
objectEnumerator = [[[self _representedObject] copy] objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
{
_remove(_proxyObject, _removeSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target removeAllObjects];
[self _setRepresentedObject:target];
}
while ((object = [objectEnumerator nextObject]) != nil)
{
_remove(_proxyObject, _removeSEL, object);
}
}
else
{
var target = [[self _representedObject] copy];
[target removeAllObjects];
[self _setRepresentedObject:target];
}
}
- (void)intersectSet:(CPSet)aSet
{
if (_intersect)
{
_intersect(_proxyObject, _intersectSEL, aSet);
}
else
{
var target = [[self _representedObject] copy];
[target intersectSet:aSet];
[self _setRepresentedObject:target];
}
if (_intersect)
{
_intersect(_proxyObject, _intersectSEL, aSet);
}
else
{
var target = [[self _representedObject] copy];
[target intersectSet:aSet];
[self _setRepresentedObject:target];
}
}
- (void)setSet:(CPSet)set
{
[self _setRepresentedObject:set];
[self _setRepresentedObject:set];
}
- (CPArray)allObjects
{
return [[self _representedObject] allObjects];
return [[self _representedObject] allObjects];
}
- (id)anyObject
{
return [[self _representedObject] anyObject];
return [[self _representedObject] anyObject];
}
- (BOOL)containsObject:(id)anObject
{
return [[self _representedObject] containsObject:anObject];
return [[self _representedObject] containsObject:anObject];
}
- (BOOL)intersectsSet:(CPSet)aSet
{
return [[self _representedObject] intersectsSet:aSet];
return [[self _representedObject] intersectsSet:aSet];
}
- (BOOL)isEqualToSet:(CPSet)aSet
{
return [[self _representedObject] isEqualToSet:aSet];
return [[self _representedObject] isEqualToSet:aSet];
}
- (id)copy
{
return [[self _representedObject] copy];
return [[self _representedObject] copy];
}
@end
+110 -110
View File
@@ -34,263 +34,263 @@
+ (id)alloc
{
if (self === [CPSet class] || self === [CPMutableSet class])
return [_CPPlaceholderSet alloc];
if (self === [CPSet class] || self === [CPMutableSet class])
return [_CPPlaceholderSet alloc];
return [super alloc];
return [super alloc];
}
+ (id)set
{
return [[self alloc] init];
return [[self alloc] init];
}
+ (id)setWithArray:(CPArray)anArray
{
return [[self alloc] initWithArray:anArray];
return [[self alloc] initWithArray:anArray];
}
+ (id)setWithObject:(id)anObject
{
return [[self alloc] initWithObjects:anObject];
return [[self alloc] initWithObjects:anObject];
}
+ (id)setWithObjects:(id)objects count:(CPUInteger)count
{
return [[self alloc] initWithObjects:objects count:count];
return [[self alloc] initWithObjects:objects count:count];
}
+ (id)setWithObjects:(id)anObject, ...
{
var argumentsArray = Array.prototype.slice.apply(arguments);
var argumentsArray = Array.prototype.slice.apply(arguments);
argumentsArray[0] = [self alloc];
argumentsArray[1] = @selector(initWithObjects:);
argumentsArray[0] = [self alloc];
argumentsArray[1] = @selector(initWithObjects:);
return objj_msgSend.apply(this, argumentsArray);
return objj_msgSend.apply(this, argumentsArray);
}
+ (id)setWithSet:(CPSet)set
{
return [[self alloc] initWithSet:set];
return [[self alloc] initWithSet:set];
}
- (id)setByAddingObject:(id)anObject
{
return [[self class] setWithArray:[[self allObjects] arrayByAddingObject:anObject]];
return [[self class] setWithArray:[[self allObjects] arrayByAddingObject:anObject]];
}
- (id)setByAddingObjectsFromSet:(CPSet)aSet
{
return [self setByAddingObjectsFromArray:[aSet allObjects]];
return [self setByAddingObjectsFromArray:[aSet allObjects]];
}
- (id)setByAddingObjectsFromArray:(CPArray)anArray
{
return [[self class] setWithArray:[[self allObjects] arrayByAddingObjectsFromArray:anArray]];
return [[self class] setWithArray:[[self allObjects] arrayByAddingObjectsFromArray:anArray]];
}
- (id)init
{
return [self initWithObjects:nil count:0];
return [self initWithObjects:nil count:0];
}
- (id)initWithArray:(CPArray)anArray
{
return [self initWithObjects:anArray count:[anArray count]];
return [self initWithObjects:anArray count:[anArray count]];
}
- (id)initWithObjects:(id)anObject, ...
{
var index = 2,
count = arguments.length;
var index = 2,
count = arguments.length;
for (; index < count; ++index)
if (arguments[index] === nil)
break;
for (; index < count; ++index)
if (arguments[index] === nil)
break;
return [self initWithObjects:Array.prototype.slice.call(arguments, 2, index) count:index - 2];
return [self initWithObjects:Array.prototype.slice.call(arguments, 2, index) count:index - 2];
}
- (id)initWithObjects:(CPArray)objects count:(CPUInteger)aCount
{
if (self === _CPSharedPlaceholderSet)
return [[_CPConcreteMutableSet alloc] initWithObjects:objects count:aCount];
if (self === _CPSharedPlaceholderSet)
return [[_CPConcreteMutableSet alloc] initWithObjects:objects count:aCount];
return [super init];
return [super init];
}
- (id)initWithSet:(CPSet)aSet
{
return [self initWithArray:[aSet allObjects]];
return [self initWithArray:[aSet allObjects]];
}
- (id)initWithSet:(CPSet)aSet copyItems:(BOOL)shouldCopyItems
{
if (shouldCopyItems)
return [aSet valueForKey:@"copy"];
if (shouldCopyItems)
return [aSet valueForKey:@"copy"];
return [self initWithSet:aSet];
return [self initWithSet:aSet];
}
- (CPUInteger)count
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (CPArray)allObjects
{
var objects = [],
object,
objectEnumerator = [self objectEnumerator];
var objects = [],
object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
objects.push(object);
while ((object = [objectEnumerator nextObject]) != nil)
objects.push(object);
return objects;
return objects;
}
- (id)anyObject
{
return [[self objectEnumerator] nextObject];
return [[self objectEnumerator] nextObject];
}
- (BOOL)containsObject:(id)anObject
{
return [self member:anObject] != nil;
return [self member:anObject] != nil;
}
- (CPSet)filteredSetUsingPredicate:(CPPredicate)aPredicate
{
var objects = [],
object,
objectEnumerator = [self objectEnumerator];
var objects = [],
object,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
if ([aPredicate evaluateWithObject:object])
objects.push(object);
while ((object = [objectEnumerator nextObject]) != nil)
if ([aPredicate evaluateWithObject:object])
objects.push(object);
return [[[self class] alloc] initWithArray:objects];
return [[[self class] alloc] initWithArray:objects];
}
- (void)makeObjectsPerformSelector:(SEL)aSelector
{
[self makeObjectsPerformSelector:aSelector withObjects:nil];
[self makeObjectsPerformSelector:aSelector withObjects:nil];
}
- (void)makeObjectsPerformSelector:(SEL)aSelector withObject:(id)anObject
{
[self makeObjectsPerformSelector:aSelector withObjects:[anObject]];
[self makeObjectsPerformSelector:aSelector withObjects:[anObject]];
}
- (void)makeObjectsPerformSelector:(SEL)aSelector withObjects:(CPArray)objects
{
var object,
objectEnumerator = [self objectEnumerator],
argumentsArray = [nil, aSelector].concat(objects || []);
var object,
objectEnumerator = [self objectEnumerator],
argumentsArray = [nil, aSelector].concat(objects || []);
while ((object = [objectEnumerator nextObject]) != nil)
{
argumentsArray[0] = object;
objj_msgSend.apply(this, argumentsArray);
}
while ((object = [objectEnumerator nextObject]) != nil)
{
argumentsArray[0] = object;
objj_msgSend.apply(this, argumentsArray);
}
}
- (id)member:(id)anObject
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (CPEnumerator)objectEnumerator
{
_CPRaiseInvalidAbstractInvocation(self, _cmd);
_CPRaiseInvalidAbstractInvocation(self, _cmd);
}
- (void)enumerateObjectsUsingBlock:(Function)aFunction
{
var object,
objectEnumerator = [self objectEnumerator],
shouldStop = NO;
var object,
objectEnumerator = [self objectEnumerator],
shouldStop = NO;
while (!shouldStop && (object = [objectEnumerator nextObject]) != nil) {
if (aFunction(object, @ref(shouldStop)) !== undefined) {
throw "DEPRECATED: The method enumerateObjectsUsingBlock: does not support returning a value in the block to stop the iteration.";
}
}
while (!shouldStop && (object = [objectEnumerator nextObject]) != nil) {
if (aFunction(object, @ref(shouldStop)) !== undefined) {
throw "DEPRECATED: The method enumerateObjectsUsingBlock: does not support returning a value in the block to stop the iteration.";
}
}
}
- (CPSet)objectsPassingTest:(Function)aFunction
{
var objects = [],
object = nil,
objectEnumerator = [self objectEnumerator];
var objects = [],
object = nil,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
if (aFunction(object))
objects.push(object);
while ((object = [objectEnumerator nextObject]) != nil)
if (aFunction(object))
objects.push(object);
return [[[self class] alloc] initWithArray:objects];
return [[[self class] alloc] initWithArray:objects];
}
- (BOOL)isSubsetOfSet:(CPSet)aSet
{
var object = nil,
objectEnumerator = [self objectEnumerator];
var object = nil,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
if (![aSet containsObject:object])
return NO;
while ((object = [objectEnumerator nextObject]) != nil)
if (![aSet containsObject:object])
return NO;
return YES;
return YES;
}
- (BOOL)intersectsSet:(CPSet)aSet
{
if (self === aSet)
return [self count] > 0;
if (self === aSet)
return [self count] > 0;
var object = nil,
objectEnumerator = [self objectEnumerator];
var object = nil,
objectEnumerator = [self objectEnumerator];
while ((object = [objectEnumerator nextObject]) != nil)
if ([aSet containsObject:object])
return YES;
while ((object = [objectEnumerator nextObject]) != nil)
if ([aSet containsObject:object])
return YES;
return NO;
return NO;
}
- (CPArray)sortedArrayUsingDescriptors:(CPArray)someSortDescriptors
{
return [[self allObjects] sortedArrayUsingDescriptors:someSortDescriptors];
return [[self allObjects] sortedArrayUsingDescriptors:someSortDescriptors];
}
- (BOOL)isEqualToSet:(CPSet)aSet
{
return [self isEqual:aSet];
return [self isEqual:aSet];
}
- (BOOL)isEqual:(CPSet)aSet
{
return self === aSet ||
[aSet isKindOfClass:[CPSet class]] &&
([self count] === [aSet count] &&
[aSet isSubsetOfSet:self]);
return self === aSet ||
[aSet isKindOfClass:[CPSet class]] &&
([self count] === [aSet count] &&
[aSet isSubsetOfSet:self]);
}
- (CPString)description
{
var string = "{(\n",
objects = [self allObjects],
index = 0,
count = [objects count];
var string = "{(\n",
objects = [self allObjects],
index = 0,
count = [objects count];
for (; index < count; ++index)
{
var object = objects[index];
string += "\t" + String(object).split('\n').join("\n\t") + "\n";
}
for (; index < count; ++index)
{
var object = objects[index];
string += "\t" + String(object).split('\n').join("\n\t") + "\n";
}
return string + ")}";
return string + ")}";
}
@end
@@ -302,12 +302,12 @@
- (id)copy
{
return [[self class] setWithSet:self];
return [[self class] setWithSet:self];
}
- (id)mutableCopy
{
return [self copy];
return [self copy];
}
@end
@@ -321,12 +321,12 @@ var CPSetObjectsKey = @"CPSetObjectsKey";
- (id)initWithCoder:(CPCoder)aCoder
{
return [self initWithArray:[aCoder decodeObjectForKey:CPSetObjectsKey]];
return [self initWithArray:[aCoder decodeObjectForKey:CPSetObjectsKey]];
}
- (void)encodeWithCoder:(CPCoder)aCoder
{
[aCoder encodeObject:[self allObjects] forKey:CPSetObjectsKey];
[aCoder encodeObject:[self allObjects] forKey:CPSetObjectsKey];
}
@end
@@ -342,10 +342,10 @@ var _CPSharedPlaceholderSet = nil;
+ (id)alloc
{
if (!_CPSharedPlaceholderSet)
_CPSharedPlaceholderSet = [super alloc];
if (!_CPSharedPlaceholderSet)
_CPSharedPlaceholderSet = [super alloc];
return _CPSharedPlaceholderSet;
return _CPSharedPlaceholderSet;
}
@end
+126 -87
View File
@@ -47,30 +47,25 @@ var abbreviationDictionary,
function abbreviationForDate(date)
{
// Strategy 1: Parse date.toString() as it's more reliable than toLocaleString.
// Format is usually: "Day Mon dd yyyy hh:mm:ss GMT+XXXX (Time Zone Name)"
var dateString = date.toString();
// First, ask Intl directly for the short time zone name (e.g. "PDT") of
// the runtime's local zone, which correctly reflects DST for this date.
// Replaces the previous date.toString() parenthesis-scraping and
// long-name-to-acronym regex guessing, which broke for locales and
// engines that don't format Date#toString the way that logic assumed.
try {
var parts = new Intl.DateTimeFormat('en-US', { timeZoneName: 'short' }).formatToParts(date),
tzPart = parts.filter(function (p) { return p.type === 'timeZoneName'; })[0];
// Check for a long name within parentheses, e.g., (Pacific Daylight Time)
var longNameMatch = dateString.match(/\(([^)]+)\)/);
if (longNameMatch) {
var timeZoneComponent = longNameMatch[1];
// If the component is already a known abbreviation (e.g., "EST"), return it.
if ([abbreviationDictionary objectForKey:timeZoneComponent]) {
return timeZoneComponent;
}
// If it's a long name (e.g., "Eastern Daylight Time"), create an acronym.
if (timeZoneComponent.indexOf(' ') > -1) {
var generatedAbbr = timeZoneComponent.split(' ').map(function(word) { return word[0]; }).join('');
if ([abbreviationDictionary objectForKey:generatedAbbr]) {
return generatedAbbr;
}
}
if (tzPart && [abbreviationDictionary objectForKey:tzPart.value])
return tzPart.value;
} catch (e) {
// Intl API not supported, or it failed. Fall through to the next attempt.
}
// Strategy 2: If string parsing fails (e.g., for "GMT-04:00"), use the modern and reliable Intl API.
// If that short name isn't one of our known abbreviations (e.g. it
// returned "GMT-04:00" for a zone with no common three/four-letter
// abbreviation), resolve the runtime's IANA zone and pick whichever
// known abbreviation for that zone matches the date's current UTC offset.
try {
var ianaName = new Intl.DateTimeFormat().resolvedOptions().timeZone;
var currentOffset = -date.getTimezoneOffset(); // in minutes
@@ -98,8 +93,22 @@ function abbreviationForDate(date)
if (possibleAbbrs.length > 0) {
return possibleAbbrs[0];
}
// Neither attempt above found a match by name. Fall back to any
// known abbreviation whose stored offset matches the system's
// current UTC offset. Several abbreviations legitimately share an
// offset (GMT, UTC, and WET all correctly resolve to 0, for example),
// so this returns one of them rather than none.
var offsetKeys = [timeDifferenceFromUTC keyEnumerator],
offsetKey;
while (offsetKey = [offsetKeys nextObject]) {
if ([timeDifferenceFromUTC valueForKey:offsetKey] === currentOffset) {
return offsetKey;
}
}
} catch (e) {
// Intl API not supported, or it failed. We cannot proceed with this strategy.
// Intl API not supported, or it failed.
}
// Return nil if no valid abbreviation could be determined.
@@ -108,28 +117,18 @@ function abbreviationForDate(date)
function _abbreviationForNameAndDate(tzName, date)
{
// This is a helper function based on the existing `abbreviationForDate`.
// It determines the abbreviation for a given IANA name based on the provided date,
// which allows it to respect daylight saving time.
// Determines the abbreviation for a given IANA name based on the provided
// date, which allows it to respect daylight saving time. Reads the short
// time zone name directly from Intl.formatToParts, rather than parsing
// a long name out of toLocaleString's locale-formatted output.
try {
var options = {
timeZone: tzName,
timeZoneName: 'long'
};
// The 'en-US' locale provides a predictable format for parsing.
var dateString = date.toLocaleString('en-US', options);
var parts = new Intl.DateTimeFormat('en-US', { timeZone: tzName, timeZoneName: 'short' }).formatToParts(date),
tzPart = parts.filter(function (p) { return p.type === 'timeZoneName'; })[0];
// This regex is copied from the global 'abbreviationForDate' function.
// It strips the date and time, leaving the long time zone name.
var longTZName = dateString.replace(/^([0]?\d|[1][0-2])\/((?:[0]?|[1-2])\d|[3][0-1])\/([2][01]|[1][6-9])\d{2}(,?\s*([0]?\d|[1][0-2])(\:[0-5]\d){1,2})*\s*([aApP][mM]{0,2})?\s*/, "");
// Create the abbreviation from the long name (e.g., "Pacific Daylight Time" -> "PDT")
var abbreviation = longTZName.split(" ").map(function(l) { return l[0]}).join("");
return abbreviation;
return tzPart ? tzPart.value : nil;
} catch (e) {
// The tzName might be invalid for toLocaleString, which throws a RangeError.
// In this case, we can't determine the abbreviation.
// The tzName might be invalid for Intl.DateTimeFormat, which throws a
// RangeError. In this case, we can't determine the abbreviation.
return nil;
}
}
@@ -155,56 +154,94 @@ function _abbreviationForNameAndDate(tzName, date)
return;
knownTimeZoneNames = [
@"America/Halifax",
@"America/Juneau",
@"America/Juneau",
@"America/Argentina/Buenos_Aires",
@"America/Halifax",
@"Asia/Dhaka",
@"America/Sao_Paulo",
@"America/Sao_Paulo",
@"Europe/London",
@"Africa/Addis_Ababa",
@"Africa/Harare",
@"America/Chicago",
@"Europe/Paris",
@"Europe/Paris",
@"America/Santiago",
@"America/Santiago",
@"Africa/Lagos",
@"America/Argentina/Buenos_Aires",
@"America/Bogota",
@"America/Chicago",
@"Africa/Addis_Ababa",
@"America/Denver",
@"America/Halifax",
@"America/Juneau",
@"America/Lima",
@"America/Los_Angeles",
@"America/New_York",
@"Europe/Istanbul",
@"Europe/Istanbul",
@"America/New_York",
@"GMT",
@"America/Santiago",
@"America/Sao_Paulo",
@"Asia/Bangkok",
@"Asia/Calcutta",
@"Asia/Dhaka",
@"Asia/Dubai",
@"Asia/Hong_Kong",
@"Pacific/Honolulu",
@"Asia/Bangkok",
@"Asia/Tehran",
@"Asia/Calcutta",
@"Asia/Tokyo",
@"Asia/Seoul",
@"America/Denver",
@"Europe/Moscow",
@"Europe/Moscow",
@"America/Denver",
@"Pacific/Auckland",
@"Pacific/Auckland",
@"America/Los_Angeles",
@"America/Lima",
@"Asia/Manila",
@"Asia/Jakarta",
@"Asia/Karachi",
@"America/Los_Angeles",
@"Asia/Manila",
@"Asia/Seoul",
@"Asia/Singapore",
@"Asia/Tehran",
@"Asia/Tokyo",
@"Europe/Istanbul",
@"Europe/Lisbon",
@"Europe/London",
@"Europe/Moscow",
@"Europe/Paris",
@"GMT",
@"Pacific/Auckland",
@"Pacific/Honolulu",
@"UTC",
@"Africa/Lagos",
@"Europe/Lisbon",
@"Europe/Lisbon",
@"Asia/Jakarta"
];
// Prefer the runtime's own IANA database, when it exposes one, over the
// hardcoded 48-city list above: it's the full current set, not a snapshot
// that will silently drift the way the hand-maintained tables above have.
if (typeof Intl !== "undefined" && typeof Intl.supportedValuesOf === "function")
{
try
{
var supportedZones = Intl.supportedValuesOf("timeZone");
if (supportedZones && supportedZones.length > 0)
{
var zones = [];
var hasGMT = false;
var hasUTC = false;
var count = supportedZones.length;
// Iterate using primitive property access.
// The array returned by Intl across the runtime bridge may lack
// standard Array prototypes (e.g., slice, indexOf). A standard loop
// ensures safe data extraction into a local array without triggering
// prototype resolution exceptions or relying on CPArray.
for (var i = 0; i < count; i++)
{
var zone = supportedZones[i];
zones[i] = zone;
if (zone === @"GMT")
hasGMT = true;
else if (zone === @"UTC")
hasUTC = true;
}
// Explicitly restore legacy aliases if the host engine omits them.
// Engines adhering strictly to canonical IANA identifiers omit "GMT"
// and "UTC". CPTimeZone's static dictionaries map these directly,
// requiring their presence to initialize localTimeZone in UTC environments.
if (!hasGMT)
zones[zones.length] = @"GMT";
if (!hasUTC)
zones[zones.length] = @"UTC";
knownTimeZoneNames = zones;
}
}
catch (e)
{
// Fall through, keep the hardcoded list above.
}
}
abbreviationDictionary = @{
@"ADT" : @"America/Halifax",
@"AKDT" : @"America/Juneau",
@@ -289,12 +326,14 @@ function _abbreviationForNameAndDate(tzName, date)
@"IST" : 330,
@"JST" : 540,
@"KST" : 540,
@"MDT" : -300,
@"MSD" : 240,
@"MSK" : 240,
@"MDT" : -360,
@"MSD" : 240, // Stale: Russia abolished DST in 2014. No current offset
// is correct for a distinct "Moscow Summer Time"; left
// unfixed rather than fabricated. See CPTimeZone redesign.
@"MSK" : 180,
@"MST" : -420,
@"NZDT" : 900,
@"NZST" : 900,
@"NZDT" : 780,
@"NZST" : 720,
@"PDT" : -420,
@"PET" : -300,
@"PHT" : 480,
@@ -302,10 +341,10 @@ function _abbreviationForNameAndDate(tzName, date)
@"PST" : -480,
@"SGT" : 480,
@"UTC" : 0,
@"WAT" : -540,
@"WAT" : 60,
@"WEST" : 60,
@"WET" : 0,
@"WIT" : 540
@"WIT" : 420
};
var englishLocalizedName = @{
+115 -115
View File
@@ -36,14 +36,14 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
@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;
}
/*!
@@ -51,11 +51,11 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
+ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds invocation:(CPInvocation)anInvocation repeats:(BOOL)shouldRepeat
{
const 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;
}
/*!
@@ -63,11 +63,11 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
+ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
{
const 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;
}
/*!
@@ -75,11 +75,11 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
+ (CPTimer)scheduledTimerWithTimeInterval:(CPTimeInterval)seconds callback:(Function)aFunction repeats:(BOOL)shouldRepeat
{
const 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;
}
/*!
@@ -87,7 +87,7 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
+ (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];
}
/*!
@@ -95,7 +95,7 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
+ (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];
}
/*!
@@ -103,7 +103,7 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
+ (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];
}
/*!
@@ -111,18 +111,18 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (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;
}
/*!
@@ -130,18 +130,18 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (id)initWithFireDate:(CPDate)aDate interval:(CPTimeInterval)seconds target:(id)aTarget selector:(SEL)aSelector userInfo:(id)userInfo repeats:(BOOL)shouldRepeat
{
const 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;
}
/*!
@@ -149,18 +149,18 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (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;
}
/*!
@@ -168,7 +168,7 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (CPTimeInterval)timeInterval
{
return _timeInterval;
return _timeInterval;
}
/*!
@@ -176,7 +176,7 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (CPDate)fireDate
{
return _fireDate;
return _fireDate;
}
/*!
@@ -184,7 +184,7 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (void)setFireDate:(CPDate)aDate
{
_fireDate = aDate;
_fireDate = aDate;
}
/*!
@@ -192,21 +192,21 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (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];
}
/*!
@@ -214,7 +214,7 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (BOOL)isValid
{
return _isValid;
return _isValid;
}
/*!
@@ -222,10 +222,10 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (void)invalidate
{
_isValid = NO;
_userInfo = nil;
_invocation = nil;
_callback = nil;
_isValid = NO;
_userInfo = nil;
_invocation = nil;
_callback = nil;
}
/*!
@@ -233,7 +233,7 @@ const CPTimerDefaultTimeInterval = 0.1;
*/
- (id)userInfo
{
return _userInfo;
return _userInfo;
}
@end
@@ -250,68 +250,68 @@ const CPTimersForTimeoutIDs = {};
const _CPTimerBridgeTimer = function(codeOrFunction, aDelay, shouldRepeat, functionArgs)
{
const timeoutID = CPTimersTimeoutID++;
let theFunction = nil;
const timeoutID = CPTimersTimeoutID++;
let theFunction = nil;
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 (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)
delete CPTimersForTimeoutIDs[timeoutID];
}
}
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)
delete CPTimersForTimeoutIDs[timeoutID];
}
}
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.
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)
{
const timer = CPTimersForTimeoutIDs[aTimeoutID];
window.clearTimeout = function(aTimeoutID)
{
const timer = CPTimersForTimeoutIDs[aTimeoutID];
if (timer)
[timer invalidate];
if (timer)
[timer invalidate];
delete CPTimersForTimeoutIDs[aTimeoutID];
};
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);
};
}
+14 -14
View File
@@ -161,14 +161,14 @@ var CPURLConnectionDelegate = nil;
Typical use can be like this:
- (async @action)doAction:(id)sender {
const { response, data, error } = await [CPURLConnection sendAsynchronousRequest:[CPURLRequest requestWithURL:@"http://cappuccino.dev"]];
if (error == nil) {
//do the stuff...
} else {
// Handle errors
}
}
- (async @action)doAction:(id)sender {
const { response, data, error } = await [CPURLConnection sendAsynchronousRequest:[CPURLRequest requestWithURL:@"http://cappuccino.dev"]];
if (error == nil) {
//do the stuff...
} else {
// Handle errors
}
}
*/
+ (async JSObject /* { response: CPURLResponse, data: CPData, error: CPError } */)sendAsynchronousRequest:(CPURLRequest)aRequest
{
@@ -218,15 +218,15 @@ var CPURLConnectionDelegate = nil;
- (void)_initWithRequest:(CPURLRequest)aRequest
{
_request = aRequest;
_request = aRequest;
_originalRequest = [aRequest copy];
_isCanceled = NO;
_isCanceled = NO;
var URL = [_request URL],
scheme = [URL scheme];
var URL = [_request URL],
scheme = [URL scheme];
// Browsers use "file:", Titanium uses "app:"
_isLocalFileConnection = scheme === "file" ||
// Browsers use "file:", Titanium uses "app:"
_isLocalFileConnection = scheme === "file" ||
((scheme === "http" || scheme === "https") &&
window.location &&
(window.location.protocol === "file:" || window.location.protocol === "app:"));
+7 -1
View File
@@ -1125,8 +1125,14 @@
/*!
Test the speed of set an big array when the old was an empty.
Also test the speed when an empty array is set and the old is an big
Disabled: This method is a macro-benchmark lacking functional assertions.
Absolute wall-clock timings are non-deterministic across disparate CI environments
and pollute standard test output. Actionable performance tracking requires a
dedicated metrics harness. Retained only for ad-hoc local profiling.
*/
- (void)testPerformance
- (void)disabled_testPerformance
{
[self initControllerWithContentBinding];
+68 -68
View File
@@ -12,18 +12,18 @@
*/
@implementation _MockColorPicker : CPObject
{
CPArray _receivedColors;
CPView _view;
CPArray _receivedColors;
CPView _view;
}
- (id)init
{
if (self = [super init])
{
_receivedColors = [];
_view = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
}
return self;
if (self = [super init])
{
_receivedColors = [];
_view = [[CPView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
}
return self;
}
- (void)setColor:(CPColor)aColor
@@ -33,32 +33,32 @@
- (CPArray)receivedColors
{
return _receivedColors;
return _receivedColors;
}
- (CPView)provideNewView:(BOOL)initial
{
return _view;
return _view;
}
@end
@implementation CPColorPanelTest : OJTestCase
{
CPColorPanel _panel;
CPColorPanel _panel;
}
- (void)setUp
{
// Get shared panel and ensure it's initialized
_panel = [CPColorPanel sharedColorPanel];
[_panel _loadContentsIfNecessary];
// Get shared panel and ensure it's initialized
_panel = [CPColorPanel sharedColorPanel];
[_panel _loadContentsIfNecessary];
}
- (void)tearDown
{
// Reset panel state between tests
[_panel setColor:[CPColor whiteColor]];
// Reset panel state between tests
[_panel setColor:[CPColor whiteColor]];
}
/*
@@ -67,10 +67,10 @@
*/
- (void)testSetColorUpdatesOpacitySlider
{
var initialColor = [CPColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.5];[_panel setColor:initialColor];
var initialColor = [CPColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.5];[_panel setColor:initialColor];
[self assert:0.5 equals:[_panel._opacitySlider floatValue]
message:"Opacity slider should match the color's alpha component"];
[self assert:0.5 equals:[_panel._opacitySlider floatValue]
message:"Opacity slider should match the color's alpha component"];
}
/*
@@ -78,19 +78,19 @@
*/
- (void)testOpacityChangeUpdatesColorAlpha
{
var initialColor = [CPColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1.0];
[_panel setColor:initialColor];[_panel._opacitySlider setFloatValue:0.3];
[_panel setOpacity:_panel._opacitySlider];
var initialColor = [CPColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1.0];
[_panel setColor:initialColor];[_panel._opacitySlider setFloatValue:0.3];
[_panel setOpacity:_panel._opacitySlider];
var newColor = [_panel color];
[self assert:0.3 equals:[newColor alphaComponent]
message:"Color's alpha should match slider value"];
var newColor = [_panel color];
[self assert:0.3 equals:[newColor alphaComponent]
message:"Color's alpha should match slider value"];
// RGB components should remain unchanged
var components = [newColor components];
[self assert:0.5 equals:components[0] message:"Red component should be unchanged"];
[self assert:0.5 equals:components[1] message:"Green component should be unchanged"];
[self assert:1.0 equals:components[2] message:"Blue component should be unchanged"];
// RGB components should remain unchanged
var components = [newColor components];
[self assert:0.5 equals:components[0] message:"Red component should be unchanged"];
[self assert:0.5 equals:components[1] message:"Green component should be unchanged"];
[self assert:1.0 equals:components[2] message:"Blue component should be unchanged"];
}
/*
@@ -99,20 +99,20 @@
*/
- (void)testActivePickerNotifiedOfColorChanges
{
var mockPicker = [[_MockColorPicker alloc] init];
_panel._activePicker = mockPicker;
var mockPicker = [[_MockColorPicker alloc] init];
_panel._activePicker = mockPicker;
var color1 = [CPColor redColor];
var color2 = [CPColor blueColor];
var color1 = [CPColor redColor];
var color2 = [CPColor blueColor];
[_panel setColor:color1];
[_panel setColor:color2];
[_panel setColor:color1];
[_panel setColor:color2];
var receivedColors = [mockPicker receivedColors];
[self assert:2 equals:[receivedColors count]
message:"Active picker should receive setColor for each color change"];
[self assert:color1 same:receivedColors[0]];
[self assert:color2 same:receivedColors[1]];
var receivedColors = [mockPicker receivedColors];
[self assert:2 equals:[receivedColors count]
message:"Active picker should receive setColor for each color change"];
[self assert:color1 same:receivedColors[0]];
[self assert:color2 same:receivedColors[1]];
}
/*
@@ -121,21 +121,21 @@
*/
- (void)testPickerNotifiedOnActivation
{
var testColor = [CPColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1.0];
[_panel setColor:testColor];
var testColor = [CPColor colorWithRed:0.5 green:0.5 blue:1.0 alpha:1.0];
[_panel setColor:testColor];
var mockPicker = [[_MockColorPicker alloc] init];
_panel._colorPickers = [mockPicker];
var mockPicker = [[_MockColorPicker alloc] init];
_panel._colorPickers = [mockPicker];
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
[button setTag:0];
var button = [[CPButton alloc] initWithFrame:CGRectMake(0, 0, 32, 32)];
[button setTag:0];
[_panel _setPicker:button];
[_panel _setPicker:button];
var receivedColors = [mockPicker receivedColors];
[self assert:1 equals:[receivedColors count]
message:"Picker should receive setColor when activated"];
[self assert:testColor same:receivedColors[0]];
var receivedColors = [mockPicker receivedColors];
[self assert:1 equals:[receivedColors count]
message:"Picker should receive setColor when activated"];
[self assert:testColor same:receivedColors[0]];
}
/*
@@ -143,11 +143,11 @@
*/
- (void)testOpacityMethodReturnsAlpha
{
var testColor = [CPColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.42];
[_panel setColor:testColor];
var testColor = [CPColor colorWithRed:1.0 green:0.0 blue:0.0 alpha:0.42];
[_panel setColor:testColor];
[self assert:0.42 equals:[_panel opacity]
message:"opacity method should return color's alpha component"];
[self assert:0.42 equals:[_panel opacity]
message:"opacity method should return color's alpha component"];
}
/*
@@ -156,18 +156,18 @@
*/
- (void)testSetColorWithEqualColorDoesNothing
{
var testColor = [CPColor colorWithRed:1.0 green:0.5 blue:0.0 alpha:0.7];
[_panel setColor:testColor];
var testColor = [CPColor colorWithRed:1.0 green:0.5 blue:0.0 alpha:0.7];
[_panel setColor:testColor];
var mockPicker = [[_MockColorPicker alloc] init];
_panel._activePicker = mockPicker;
var mockPicker = [[_MockColorPicker alloc] init];
_panel._activePicker = mockPicker;
// Set same color again
[_panel setColor:testColor];
// Set same color again
[_panel setColor:testColor];
var receivedColors = [mockPicker receivedColors];
[self assert:0 equals:[receivedColors count]
message:"Setting equal color should not trigger picker update"];
var receivedColors = [mockPicker receivedColors];
[self assert:0 equals:[receivedColors count]
message:"Setting equal color should not trigger picker update"];
}
/*
@@ -175,10 +175,10 @@
*/
- (void)testColorMethodReturnsCurrentColor
{
var testColor = [CPColor colorWithRed:0.2 green:0.4 blue:0.6 alpha:0.8];[_panel setColor:testColor];
var testColor = [CPColor colorWithRed:0.2 green:0.4 blue:0.6 alpha:0.8];[_panel setColor:testColor];
[self assert:testColor same:[_panel color]
message:"color method should return current color"];
[self assert:testColor same:[_panel color]
message:"color method should return current color"];
}
@end
+143 -8
View File
@@ -17,22 +17,21 @@
CPTreeController _treeController @accessors(property=treeController);
CPArray _contentArray @accessors(property=contentArray);
CPArray observations;
int aCount @accessors;
CPMutableArray _observedKeyPaths;
}
- (CPArray)makeTestTree
{
var engineering = [OrgNode nodeWithName:@"Engineering"],
marketing = [OrgNode nodeWithName:@"Marketing"];
marketing = [OrgNode nodeWithName:@"Marketing"];
var webTeam = [OrgNode nodeWithName:@"Web Team"],
backendTeam = [OrgNode nodeWithName:@"Backend Team"];
backendTeam = [OrgNode nodeWithName:@"Backend Team"];
[engineering setChildren:[CPMutableArray arrayWithObjects:webTeam, backendTeam]];
var dev1 = [OrgNode nodeWithName:@"Francisco"],
dev2 = [OrgNode nodeWithName:@"Ross"];
dev2 = [OrgNode nodeWithName:@"Ross"];
[webTeam setChildren:[CPMutableArray arrayWithObjects:dev1, dev2]];
@@ -43,12 +42,23 @@
{
[[CPApplication alloc] init];
_observedKeyPaths = [CPMutableArray array];
_contentArray = [self makeTestTree];
_treeController = [[CPTreeController alloc] init];
[_treeController setChildrenKeyPath:@"children"];
[_treeController setContent:[_contentArray copy]];
}
- (void)tearDown
{
_observedKeyPaths = nil;
}
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)aChange context:(id)aContext
{
[_observedKeyPaths addObject:aKeyPath];
}
- (void)testInitWithContent
{
[self assert:[_contentArray count] equals:[[_treeController contentArray] count]];
@@ -99,7 +109,7 @@
[controller insertObject:newDev atArrangedObjectIndexPath:insertPath];
var engineering = [[controller contentArray] objectAtIndex:0],
backendTeam = [[engineering children] objectAtIndex:1];
backendTeam = [[engineering children] objectAtIndex:1];
[self assert:1 equals:[[backendTeam children] count] message:@"Child should be added to the model object's children array"];
[self assert:@"Tom" equals:[[[backendTeam children] objectAtIndex:0] name]];
}
@@ -132,7 +142,7 @@
[controller removeObjectAtArrangedObjectIndexPath:path];
var engineering = [[controller contentArray] objectAtIndex:0],
webTeam = [[engineering children] objectAtIndex:0];
webTeam = [[engineering children] objectAtIndex:0];
[self assert:1 equals:[[webTeam children] count] message:@"Francisco should be removed, leaving only Ross"];
[self assert:@"Ross" equals:[[[webTeam children] objectAtIndex:0] name]];
@@ -176,7 +186,8 @@
// "Marketing" is now at index 0
[self assert:[CPIndexPath indexPathWithIndex:0] equals:[controller selectionIndexPath]];
// Test behavior when AvoidsEmptySelection is NO[controller insertObject:[OrgNode nodeWithName:@"New Dept"] atArrangedObjectIndexPath:[CPIndexPath indexPathWithIndex:1]];
// Test behavior when AvoidsEmptySelection is NO
[controller insertObject:[OrgNode nodeWithName:@"New Dept"] atArrangedObjectIndexPath:[CPIndexPath indexPathWithIndex:1]];
[controller setAvoidsEmptySelection:NO];
// Reselect "Marketing" at index 0
@@ -229,6 +240,130 @@
[self assert:@"Marketing" equals:[[selectedObjects objectAtIndex:0] name]];
}
/*
* New Tests for Selection Bindings, KVO, and UI Action Status
*/
- (void)testExposedBindings
{
var exposedBindings = [CPTreeController exposedBindings];
[self assertTrue:[exposedBindings containsObject:@"contentArray"] message:@"contentArray should be exposed"];
[self assertTrue:[exposedBindings containsObject:@"sortDescriptors"] message:@"sortDescriptors should be exposed"];
[self assertTrue:[exposedBindings containsObject:@"selectionIndexPaths"] message:@"selectionIndexPaths should be exposed"];
[self assertTrue:[exposedBindings containsObject:@"selectionIndexPath"] message:@"selectionIndexPath should be exposed"];
[self assertTrue:[exposedBindings containsObject:@"selectedObjects"] message:@"selectedObjects should be exposed"];
}
- (void)testDetailBindingToSelectionProxyUpdatesOnSelectionChange
{
var controller = [self treeController],
textField = [[CPTextField alloc] init];
[textField bind:@"value" toObject:controller withKeyPath:@"selection.name" options:nil];
// Select "Engineering" (index 0)
[controller setSelectionIndexPath:[CPIndexPath indexPathWithIndex:0]];
[self assert:@"Engineering" equals:[textField stringValue] message:@"Bound view should reflect root selection"];
// Select "Web Team" (index [0, 0])
var nestedPath = [[CPIndexPath indexPathWithIndex:0] indexPathByAddingIndex:0];
[controller setSelectionIndexPath:nestedPath];
[self assert:@"Web Team" equals:[textField stringValue] message:@"Bound view should reflect nested selection"];
// Select "Marketing" (index 1)
[controller setSelectionIndexPath:[CPIndexPath indexPathWithIndex:1]];
[self assert:@"Marketing" equals:[textField stringValue] message:@"Bound view should reflect changed selection"];
}
- (void)testDetailBindingUpdatesOnSetContent
{
var controller = [self treeController],
textField = [[CPTextField alloc] init];
[textField bind:@"value" toObject:controller withKeyPath:@"selection.name" options:nil];
// Select "Engineering"
[controller setSelectionIndexPath:[CPIndexPath indexPathWithIndex:0]];
[self assert:@"Engineering" equals:[textField stringValue]];
// Replace content
var newDept = [OrgNode nodeWithName:@"Design Dept"];
[controller setContent:[CPMutableArray arrayWithObject:newDept]];
// Detail binding should update to the preserved selection or new root
[self assert:@"Design Dept" equals:[textField stringValue] message:@"Detail binding must update when content changes"];
}
- (void)testSelectedObjectsKVOTriggeredOnContentChange
{
var controller = [self treeController];
[controller setSelectionIndexPath:[CPIndexPath indexPathWithIndex:0]];
[controller addObserver:self forKeyPath:@"selectedObjects" options:0 context:nil];
// Set new content
var newDept = [OrgNode nodeWithName:@"Operations"];
[controller setContent:[CPMutableArray arrayWithObject:newDept]];
[self assertTrue:[_observedKeyPaths containsObject:@"selectedObjects"] message:@"selectedObjects KVO notification must fire when content changes"];
[controller removeObserver:self forKeyPath:@"selectedObjects"];
}
- (void)testCanInsertDependsOnEditable
{
var controller = [self treeController];
[controller addObserver:self forKeyPath:@"canInsert" options:0 context:nil];
[controller setEditable:YES];
[self assertTrue:[controller canInsert]];
[_observedKeyPaths removeAllObjects];
[controller setEditable:NO];
[self assertFalse:[controller canInsert] message:@"canInsert should be NO when editable is NO"];
[self assertTrue:[_observedKeyPaths containsObject:@"canInsert"] message:@"canInsert KVO must fire when editable changes"];
[controller removeObserver:self forKeyPath:@"canInsert"];
}
- (void)testCanAddChildAndCanInsertChildDependOnEditableAndSelection
{
var controller = [self treeController];
[controller setEditable:YES];
[controller addObserver:self forKeyPath:@"canAddChild" options:0 context:nil];
[controller addObserver:self forKeyPath:@"canInsertChild" options:0 context:nil];
// Empty selection -> canAddChild/canInsertChild should be NO
[controller setSelectionIndexPaths:[CPArray array]];
[self assertFalse:[controller canAddChild]];
[self assertFalse:[controller canInsertChild]];
[_observedKeyPaths removeAllObjects];
// Select an item -> canAddChild/canInsertChild should become YES
[controller setSelectionIndexPath:[CPIndexPath indexPathWithIndex:0]];
[self assertTrue:[controller canAddChild]];
[self assertTrue:[controller canInsertChild]];
[self assertTrue:[_observedKeyPaths containsObject:@"canAddChild"] message:@"canAddChild KVO should fire on selection change"];
[self assertTrue:[_observedKeyPaths containsObject:@"canInsertChild"] message:@"canInsertChild KVO should fire on selection change"];
[_observedKeyPaths removeAllObjects];
// Set editable to NO -> canAddChild/canInsertChild should become NO
[controller setEditable:NO];
[self assertFalse:[controller canAddChild]];
[self assertFalse:[controller canInsertChild]];
[self assertTrue:[_observedKeyPaths containsObject:@"canAddChild"] message:@"canAddChild KVO should fire when editable changes"];
[self assertTrue:[_observedKeyPaths containsObject:@"canInsertChild"] message:@"canInsertChild KVO should fire when editable changes"];
[controller removeObserver:self forKeyPath:@"canAddChild"];
[controller removeObserver:self forKeyPath:@"canInsertChild"];
}
@end
/*
+474 -10
View File
@@ -1,31 +1,495 @@
@import <AppKit/CPTreeNode.j>
@import <Foundation/CPIndexPath.j>
@import <Foundation/CPSortDescriptor.j>
@import <Foundation/CPKeyedArchiver.j>
@implementation CPTreeNodeTest : OJTestCase
{
CPTreeNode treeNode;
CPTreeNode childNode;
CPTreeNode root;
CPTreeNode child1;
CPTreeNode child2;
CPMutableArray _kvoRecordedChanges;
}
- (void)setUp
{
// This will init the global var CPApp which are used internally in the AppKit
[[CPApplication alloc] init];
treeNode = [CPTreeNode treeNodeWithRepresentedObject:nil];
root = [CPTreeNode treeNodeWithRepresentedObject:@"root"];
child1 = [CPTreeNode treeNodeWithRepresentedObject:@"child1"];
child2 = [CPTreeNode treeNodeWithRepresentedObject:@"child2"];
childNode = [CPTreeNode treeNodeWithRepresentedObject:nil];
[treeNode insertObject:childNode inChildNodesAtIndex:0];
_kvoRecordedChanges = [];
}
/*
* Records each KVO notification delivered to this test instance.
* Used by the KVO-contract tests below.
*/
- (void)observeValueForKeyPath:(CPString)aKeyPath ofObject:(id)anObject change:(CPDictionary)aChange context:(id)aContext
{
[_kvoRecordedChanges addObject:aChange];
}
// 1. creation and representedObject
- (void)testCreationAndRepresentedObject
{
[self assert:@"root" equals:[root representedObject]];
[self assertTrue:([root isKindOfClass:[CPTreeNode class]])];
}
// 2. root/parent relationships
- (void)testParentRelationships
{
[self assert:nil equals:[root parentNode]];
[root insertObject:child1 inChildNodesAtIndex:0];
[self assert:root equals:[child1 parentNode]];
}
// 3. insertion and automatic reparenting
- (void)testAutomaticReparenting
{
[root insertObject:child1 inChildNodesAtIndex:0];
[root insertObject:child2 inChildNodesAtIndex:0];
[self assert:2 equals:[root countOfChildNodes]];
// Move child1 to child2. It should be removed from root automatically.
[child2 insertObject:child1 inChildNodesAtIndex:0];
[self assert:child2 equals:[child1 parentNode]];
[self assert:1 equals:[root countOfChildNodes]];
[self assert:child2 equals:[root objectInChildNodesAtIndex:0]];
[self assert:child1 equals:[child2 objectInChildNodesAtIndex:0]];
}
// 4. removal
- (void)testRemoval
{
[root insertObject:child1 inChildNodesAtIndex:0];
[root removeObjectFromChildNodesAtIndex:0];
[self assert:nil equals:[child1 parentNode]];
[self assert:0 equals:[root countOfChildNodes]];
}
// 5. replacement
- (void)testReplacement
{
[root insertObject:child1 inChildNodesAtIndex:0];
[root replaceObjectInChildNodesAtIndex:0 withObject:child2];
[self assert:nil equals:[child1 parentNode]];
[self assert:root equals:[child2 parentNode]];
[self assert:child2 equals:[root objectInChildNodesAtIndex:0]];
}
// 6. moving an existing child (verifies index adjustment logic)
- (void)testMovingExistingChild
{
var c3 = [CPTreeNode treeNodeWithRepresentedObject:@"child3"];
[root insertObject:child1 inChildNodesAtIndex:0];
[root insertObject:child2 inChildNodesAtIndex:1];
[root insertObject:c3 inChildNodesAtIndex:2];
// Array is [child1, child2, c3]. Move child1 (index 0) to index 2.
[root insertObject:child1 inChildNodesAtIndex:2];
[self assert:child2 equals:[root objectInChildNodesAtIndex:0]];
[self assert:c3 equals:[root objectInChildNodesAtIndex:1]];
[self assert:child1 equals:[root objectInChildNodesAtIndex:2]];
// Move child1 (index 2) back to index 0.
[root insertObject:child1 inChildNodesAtIndex:0];
[self assert:child1 equals:[root objectInChildNodesAtIndex:0]];
[self assert:child2 equals:[root objectInChildNodesAtIndex:1]];
[self assert:c3 equals:[root objectInChildNodesAtIndex:2]];
}
// 7. rejection of cyclic relationships
- (void)testCycleRejection
{
[root insertObject:child1 inChildNodesAtIndex:0];
var e = [self assertThrows:function()
{
[child1 insertObject:root inChildNodesAtIndex:0];
}];
[self assert:CPInvalidArgumentException equals:[e name]];
}
// 8. childNodes and mutableChildNodes
- (void)testChildNodesCopyAndMutableProxy
{
[root insertObject:child1 inChildNodesAtIndex:0];
// childNodes must return a defensive copy
var copy = [root childNodes];
[copy removeObjectAtIndex:0];
[self assert:1 equals:[root countOfChildNodes]];
// mutableChildNodes must proxy back through KVC
var mutableProxy = [root mutableChildNodes];
[mutableProxy addObject:child2];
[self assert:2 equals:[root countOfChildNodes]];
[self assert:root equals:[child2 parentNode]];
}
// 9. index paths
- (void)testIndexPaths
{
[self assert:[CPIndexPath indexPathWithIndexes:[]] equals:[root indexPath]];
[root insertObject:child1 inChildNodesAtIndex:0];
[self assert:[CPIndexPath indexPathWithIndex:0] equals:[child1 indexPath]];
[child1 insertObject:child2 inChildNodesAtIndex:0];
[self assert:[CPIndexPath indexPathWithIndexes:[0, 0]] equals:[child2 indexPath]];
}
// 10. descendant lookup
- (void)testDescendantNodeAtIndexPath
{
var indexPath = [CPIndexPath indexPathWithIndex:0];
[root insertObject:child1 inChildNodesAtIndex:0];
[child1 insertObject:child2 inChildNodesAtIndex:0];
[self assert:childNode equals:[treeNode descendantNodeAtIndexPath:indexPath]];
var path = [CPIndexPath indexPathWithIndexes:[0, 0]];
[self assert:child2 equals:[root descendantNodeAtIndexPath:path]];
indexPath = [CPIndexPath indexPathWithIndex:1];
var invalidPath = [CPIndexPath indexPathWithIndexes:[1, 0]];
[self assert:nil equals:[root descendantNodeAtIndexPath:invalidPath]];
}
[self assert:nil equals:[treeNode descendantNodeAtIndexPath:indexPath]];
// 11. recursive sorting
- (void)testRecursiveSorting
{
var nodeA = [CPTreeNode treeNodeWithRepresentedObject:@"A"];
var nodeC = [CPTreeNode treeNodeWithRepresentedObject:@"C"];
var nodeB = [CPTreeNode treeNodeWithRepresentedObject:@"B"];
[root insertObject:nodeC inChildNodesAtIndex:0];
[root insertObject:nodeA inChildNodesAtIndex:1];
[root insertObject:nodeB inChildNodesAtIndex:2];
// Add children to nodeB to verify recursion
var childB2 = [CPTreeNode treeNodeWithRepresentedObject:@"B2"];
var childB1 = [CPTreeNode treeNodeWithRepresentedObject:@"B1"];
[nodeB insertObject:childB2 inChildNodesAtIndex:0];
[nodeB insertObject:childB1 inChildNodesAtIndex:1];
var sd = [[CPSortDescriptor alloc] initWithKey:@"representedObject" ascending:YES];
[root sortWithSortDescriptors:[sd] recursively:YES];
[self assert:nodeA equals:[root objectInChildNodesAtIndex:0]];
[self assert:nodeB equals:[root objectInChildNodesAtIndex:1]];
[self assert:nodeC equals:[root objectInChildNodesAtIndex:2]];
[self assert:childB1 equals:[nodeB objectInChildNodesAtIndex:0]];
[self assert:childB2 equals:[nodeB objectInChildNodesAtIndex:1]];
}
// 12. NS/Cocoa-style KVC mutation behavior (Strict validation)
- (void)testStrictChildValidation
{
var e = [self assertThrows:function()
{
[root insertObject:[CPObject new] inChildNodesAtIndex:0];
}];
[self assert:CPInvalidArgumentException equals:[e name]];
}
// 13. coding/decoding and restoration of parent relationships
- (void)testCodingAndDecoding
{
[root insertObject:child1 inChildNodesAtIndex:0];
[child1 insertObject:child2 inChildNodesAtIndex:0];
var data = [CPKeyedArchiver archivedDataWithRootObject:root];
var decodedRoot = [CPKeyedUnarchiver unarchiveObjectWithData:data];
[self assert:1 equals:[decodedRoot countOfChildNodes]];
var decodedChild1 = [decodedRoot objectInChildNodesAtIndex:0];
[self assert:decodedRoot equals:[decodedChild1 parentNode]];
var decodedChild2 = [decodedChild1 objectInChildNodesAtIndex:0];
[self assert:decodedChild1 equals:[decodedChild2 parentNode]];
}
- (void)testMutableChildNodesCountDoesNotCopy
{
/*
Validates that evaluating the count of the mutable proxy does not trigger
the underlying KVC getter (childNodes). The getter returns a defensive
copy. Invoking it for a simple count degrades an O(1) operation to O(N)
allocations.
*/
var spy = [[CPTreeNodeCountingSpy alloc] initWithRepresentedObject:@"spy"];
for (var i = 0; i < 50; i++)
[spy insertObject:[CPTreeNode treeNodeWithRepresentedObject:i] inChildNodesAtIndex:i];
[spy setChildNodesCallCount:0];
[[spy mutableChildNodes] count];
[self assert:0 equals:[spy childNodesCallCount]
message:"count via mutableChildNodes should not invoke the copying childNodes accessor"];
}
// 14. replacing at an index with a child already present at a different index (same parent)
- (void)testReplaceExistingChildSameParent
{
var c3 = [CPTreeNode treeNodeWithRepresentedObject:@"child3"];
[root insertObject:child1 inChildNodesAtIndex:0];
[root insertObject:child2 inChildNodesAtIndex:1];
[root insertObject:c3 inChildNodesAtIndex:2];
// Array is [child1, child2, c3]. Replace the object at index 2 (c3) with child1.
[root replaceObjectInChildNodesAtIndex:2 withObject:child1];
[self assert:2 equals:[root countOfChildNodes]];
[self assert:child2 equals:[root objectInChildNodesAtIndex:0]];
[self assert:child1 equals:[root objectInChildNodesAtIndex:1]];
[self assert:root equals:[child1 parentNode]];
[self assert:nil equals:[c3 parentNode]];
}
// 15. rejection of cyclic relationships via replacement
- (void)testReplaceCycleRejection
{
var c3 = [CPTreeNode treeNodeWithRepresentedObject:@"child3"];
[root insertObject:child1 inChildNodesAtIndex:0];
[child1 insertObject:child2 inChildNodesAtIndex:0];
[child2 insertObject:c3 inChildNodesAtIndex:0];
var e = [self assertThrows:function()
{
[child2 replaceObjectInChildNodesAtIndex:0 withObject:root];
}];
[self assert:CPInvalidArgumentException equals:[e name]];
}
// 16. KVO: same-parent move reports a paired removal and insertion on childNodes
//
// Disabled: structurally impossible with the current accessor-call approach.
// _CPKVOProxy coalesces nested willChange/didChange calls for the same key
// on the same object (see _sendNotificationsForKey:changeOptions:isBefore:
// in CPKeyValueObserving.j); the inner Removal bracket opened by
// removeObjectFromChildNodesAtIndex: is silently discarded inside the outer
// Insertion bracket already open on root. Fixing this requires firing a
// single CPKeyValueChangeReplacement instead of two nested accessor calls.
- (void)disabled_testKVONotificationsDuringSameParentMove
{
var c3 = [CPTreeNode treeNodeWithRepresentedObject:@"child3"];
[root insertObject:child1 inChildNodesAtIndex:0];
[root insertObject:child2 inChildNodesAtIndex:1];
[root insertObject:c3 inChildNodesAtIndex:2];
[root addObserver:self forKeyPath:@"childNodes" options:0 context:nil];
// Array is [child1, child2, c3]. Move child1 (index 0) to index 2.
[root insertObject:child1 inChildNodesAtIndex:2];
[root removeObserver:self forKeyPath:@"childNodes"];
var removals = 0,
insertions = 0,
count = [_kvoRecordedChanges count];
for (var i = 0; i < count; i++)
{
var kind = [[_kvoRecordedChanges objectAtIndex:i] objectForKey:CPKeyValueChangeKindKey];
if (kind === CPKeyValueChangeRemoval)
removals++;
else if (kind === CPKeyValueChangeInsertion)
insertions++;
}
[self assert:1 equals:removals
message:"a same-parent move must report a removal at the original position"];
[self assert:1 equals:insertions
message:"a same-parent move must report an insertion at the target position"];
}
// 17. KVO: cross-parent move reports a removal on the old parent and an insertion on the new parent
- (void)testKVONotificationsDuringCrossParentMove
{
[root insertObject:child1 inChildNodesAtIndex:0];
[root addObserver:self forKeyPath:@"childNodes" options:0 context:nil];
[child2 addObserver:self forKeyPath:@"childNodes" options:0 context:nil];
// child1 currently belongs to root. Move it under child2.
[child2 insertObject:child1 inChildNodesAtIndex:0];
[root removeObserver:self forKeyPath:@"childNodes"];
[child2 removeObserver:self forKeyPath:@"childNodes"];
var removals = 0,
insertions = 0,
count = [_kvoRecordedChanges count];
for (var i = 0; i < count; i++)
{
var kind = [[_kvoRecordedChanges objectAtIndex:i] objectForKey:CPKeyValueChangeKindKey];
if (kind === CPKeyValueChangeRemoval)
removals++;
else if (kind === CPKeyValueChangeInsertion)
insertions++;
}
[self assert:1 equals:removals
message:"the old parent's childNodes must report the removal"];
[self assert:1 equals:insertions
message:"the new parent's childNodes must report the insertion"];
}
// 18. KVO: reparenting notifies observers of parentNode
//
// Disabled: parentNode has no setParentNode:, so there is no selector for
// the KVO swizzler to instrument. Not a bug to fix incrementally; requires
// deciding whether parentNode becomes a real settable property.
- (void)disabled_testParentNodeNotifiesOnMove
{
[root insertObject:child1 inChildNodesAtIndex:0];
[child1 addObserver:self forKeyPath:@"parentNode" options:0 context:nil];
// child1 currently belongs to root. Move it under child2.
[child2 insertObject:child1 inChildNodesAtIndex:0];
[child1 removeObserver:self forKeyPath:@"parentNode"];
[self assert:1 equals:[_kvoRecordedChanges count]
message:"reparenting must notify observers of parentNode"];
}
// 19. mutableChildNodes proxy: remove and replace
- (void)testMutableChildNodesProxyRemoveAndReplace
{
[root insertObject:child1 inChildNodesAtIndex:0];
[root insertObject:child2 inChildNodesAtIndex:1];
var proxy = [root mutableChildNodes];
[proxy removeObjectAtIndex:0];
[self assert:1 equals:[root countOfChildNodes]];
[self assert:child2 equals:[root objectInChildNodesAtIndex:0]];
[self assert:nil equals:[child1 parentNode]];
var c3 = [CPTreeNode treeNodeWithRepresentedObject:@"child3"];
[proxy replaceObjectAtIndex:0 withObject:c3];
[self assert:c3 equals:[root objectInChildNodesAtIndex:0]];
[self assert:root equals:[c3 parentNode]];
[self assert:nil equals:[child2 parentNode]];
}
// 20. range validation on insertion
- (void)testInsertOutOfBoundsRaises
{
var e1 = [self assertThrows:function()
{
[root insertObject:child1 inChildNodesAtIndex:-1];
}];
[self assert:CPRangeException equals:[e1 name]];
var e2 = [self assertThrows:function()
{
[root insertObject:child1 inChildNodesAtIndex:1];
}];
[self assert:CPRangeException equals:[e2 name]];
}
// 21. non-recursive sort leaves descendants untouched
- (void)testSortNonRecursive
{
var nodeA = [CPTreeNode treeNodeWithRepresentedObject:@"A"];
var nodeC = [CPTreeNode treeNodeWithRepresentedObject:@"C"];
var nodeB = [CPTreeNode treeNodeWithRepresentedObject:@"B"];
[root insertObject:nodeC inChildNodesAtIndex:0];
[root insertObject:nodeA inChildNodesAtIndex:1];
[root insertObject:nodeB inChildNodesAtIndex:2];
var childB2 = [CPTreeNode treeNodeWithRepresentedObject:@"B2"];
var childB1 = [CPTreeNode treeNodeWithRepresentedObject:@"B1"];
[nodeB insertObject:childB2 inChildNodesAtIndex:0];
[nodeB insertObject:childB1 inChildNodesAtIndex:1];
var sd = [[CPSortDescriptor alloc] initWithKey:@"representedObject" ascending:YES];
[root sortWithSortDescriptors:[sd] recursively:NO];
[self assert:nodeA equals:[root objectInChildNodesAtIndex:0]];
[self assert:nodeB equals:[root objectInChildNodesAtIndex:1]];
[self assert:nodeC equals:[root objectInChildNodesAtIndex:2]];
// Descendants of nodeB must remain in insertion order; only the top level was sorted.
[self assert:childB2 equals:[nodeB objectInChildNodesAtIndex:0]];
[self assert:childB1 equals:[nodeB objectInChildNodesAtIndex:1]];
}
// 22. leaf status
- (void)testIsLeaf
{
[self assertTrue:[root isLeaf]];
[root insertObject:child1 inChildNodesAtIndex:0];
[self assertFalse:[root isLeaf]];
[self assertTrue:[child1 isLeaf]];
}
// 23. descendant lookup for nil and zero-length index paths
- (void)testDescendantNodeAtIndexPathDegenerateCases
{
[root insertObject:child1 inChildNodesAtIndex:0];
[self assert:root equals:[root descendantNodeAtIndexPath:nil]];
[self assert:root equals:[root descendantNodeAtIndexPath:[CPIndexPath indexPathWithIndexes:[]]]];
}
@end
@implementation CPTreeNodeCountingSpy : CPTreeNode
{
CPInteger _childNodesCallCount @accessors(property=childNodesCallCount);
}
- (id)initWithRepresentedObject:(id)anObject
{
self = [super initWithRepresentedObject:anObject];
if (self)
_childNodesCallCount = 0;
return self;
}
- (CPArray)childNodes
{
_childNodesCallCount++;
return [super childNodes];
}
@end
@@ -1,63 +1,63 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<?xml version='1.0' encoding='UTF-8'?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="10117" systemVersion="15E65" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<deployment version="1050" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117"/>
<deployment version="1050" identifier="macosx" />
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="10117" />
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="450" id="451"/>
<outlet property="delegate" destination="450" id="451" />
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder" />
<customObject id="-3" userLabel="Application" customClass="NSObject" />
<menu title="AMainMenu" systemMenu="main" id="29" userLabel="MainMenu">
<items>
<menuItem title="NewApplication" id="56">
<menu key="submenu" title="NewApplication" systemMenu="apple" id="57">
<menuItem title="CPAnimationContextTest" id="56">
<menu key="submenu" title="CPAnimationContextTest" systemMenu="apple" id="57">
<items>
<menuItem title="About NewApplication" id="58">
<modifierMask key="keyEquivalentModifierMask"/>
<menuItem title="About CPAnimationContextTest" id="58">
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142" />
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="236">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" command="YES" />
</menuItem>
<menuItem title="Preferences…" keyEquivalent="," id="129" userLabel="121"/>
<menuItem title="Preferences…" keyEquivalent="," id="129" userLabel="121" />
<menuItem isSeparatorItem="YES" id="143">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" command="YES" />
</menuItem>
<menuItem title="Services" id="131">
<menu key="submenu" title="Services" systemMenu="services" id="130"/>
<menu key="submenu" title="Services" systemMenu="services" id="130" />
</menuItem>
<menuItem isSeparatorItem="YES" id="144">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" command="YES" />
</menuItem>
<menuItem title="Hide NewApplication" keyEquivalent="h" id="134">
<menuItem title="Hide CPAnimationContextTest" keyEquivalent="h" id="134">
<connections>
<action selector="hide:" target="-1" id="367"/>
<action selector="hide:" target="-1" id="367" />
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="145">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES" />
<connections>
<action selector="hideOtherApplications:" target="-1" id="368"/>
<action selector="hideOtherApplications:" target="-1" id="368" />
</connections>
</menuItem>
<menuItem title="Show All" id="150">
<connections>
<action selector="unhideAllApplications:" target="-1" id="370"/>
<action selector="unhideAllApplications:" target="-1" id="370" />
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="149">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" command="YES" />
</menuItem>
<menuItem title="Quit NewApplication" keyEquivalent="q" id="136" userLabel="1111">
<menuItem title="Quit CPAnimationContextTest" keyEquivalent="q" id="136" userLabel="1111">
<connections>
<action selector="terminate:" target="-3" id="449"/>
<action selector="terminate:" target="-3" id="449" />
</connections>
</menuItem>
</items>
@@ -68,12 +68,12 @@
<items>
<menuItem title="New" keyEquivalent="n" id="82" userLabel="9">
<connections>
<action selector="newDocument:" target="-1" id="373"/>
<action selector="newDocument:" target="-1" id="373" />
</connections>
</menuItem>
<menuItem title="Open…" keyEquivalent="o" id="72">
<connections>
<action selector="openDocument:" target="-1" id="374"/>
<action selector="openDocument:" target="-1" id="374" />
</connections>
</menuItem>
<menuItem title="Open Recent" id="124">
@@ -81,49 +81,49 @@
<items>
<menuItem title="Clear Menu" id="126">
<connections>
<action selector="clearRecentDocuments:" target="-1" id="127"/>
<action selector="clearRecentDocuments:" target="-1" id="127" />
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="79" userLabel="7">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" command="YES" />
</menuItem>
<menuItem title="Close" keyEquivalent="w" id="73" userLabel="1">
<connections>
<action selector="performClose:" target="-1" id="193"/>
<action selector="performClose:" target="-1" id="193" />
</connections>
</menuItem>
<menuItem title="Save" keyEquivalent="s" id="75" userLabel="3">
<connections>
<action selector="saveDocument:" target="-1" id="362"/>
<action selector="saveDocument:" target="-1" id="362" />
</connections>
</menuItem>
<menuItem title="Save As…" keyEquivalent="S" id="80" userLabel="8">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES" />
<connections>
<action selector="saveDocumentAs:" target="-1" id="363"/>
<action selector="saveDocumentAs:" target="-1" id="363" />
</connections>
</menuItem>
<menuItem title="Revert to Saved" id="112" userLabel="10">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="revertDocumentToSaved:" target="-1" id="364"/>
<action selector="revertDocumentToSaved:" target="-1" id="364" />
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="74" userLabel="2">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" command="YES" />
</menuItem>
<menuItem title="Page Setup..." keyEquivalent="P" id="77" userLabel="5">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES" />
<connections>
<action selector="runPageLayout:" target="-1" id="87"/>
<action selector="runPageLayout:" target="-1" id="87" />
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="78" userLabel="6">
<connections>
<action selector="print:" target="-1" id="86"/>
<action selector="print:" target="-1" id="86" />
</connections>
</menuItem>
</items>
@@ -134,62 +134,62 @@
<items>
<menuItem title="Undo" keyEquivalent="z" id="207">
<connections>
<action selector="undo:" target="-1" id="223"/>
<action selector="undo:" target="-1" id="223" />
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="215">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES" />
<connections>
<action selector="redo:" target="-1" id="231"/>
<action selector="redo:" target="-1" id="231" />
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="206">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" command="YES" />
</menuItem>
<menuItem title="Cut" keyEquivalent="x" id="199">
<connections>
<action selector="cut:" target="-1" id="228"/>
<action selector="cut:" target="-1" id="228" />
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="197">
<connections>
<action selector="copy:" target="-1" id="224"/>
<action selector="copy:" target="-1" id="224" />
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="203">
<connections>
<action selector="paste:" target="-1" id="226"/>
<action selector="paste:" target="-1" id="226" />
</connections>
</menuItem>
<menuItem title="Delete" id="202">
<connections>
<action selector="delete:" target="-1" id="235"/>
<action selector="delete:" target="-1" id="235" />
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="198">
<connections>
<action selector="selectAll:" target="-1" id="232"/>
<action selector="selectAll:" target="-1" id="232" />
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="214">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" command="YES" />
</menuItem>
<menuItem title="Find" id="218">
<menu key="submenu" title="Find" id="220">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
<connections>
<action selector="performFindPanelAction:" target="-1" id="241"/>
<action selector="performFindPanelAction:" target="-1" id="241" />
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208" />
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES" />
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221" />
<menuItem title="Jump to Selection" keyEquivalent="j" id="210">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
<action selector="centerSelectionInVisibleArea:" target="-1" id="245" />
</connections>
</menuItem>
</items>
@@ -200,22 +200,22 @@
<items>
<menuItem title="Show Spelling…" keyEquivalent=":" id="204">
<connections>
<action selector="showGuessPanel:" target="-1" id="230"/>
<action selector="showGuessPanel:" target="-1" id="230" />
</connections>
</menuItem>
<menuItem title="Check Spelling" keyEquivalent=";" id="201">
<connections>
<action selector="checkSpelling:" target="-1" id="225"/>
<action selector="checkSpelling:" target="-1" id="225" />
</connections>
</menuItem>
<menuItem title="Check Spelling While Typing" id="219">
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
<action selector="toggleContinuousSpellChecking:" target="-1" id="222" />
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="346">
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="347"/>
<action selector="toggleGrammarChecking:" target="-1" id="347" />
</connections>
</menuItem>
</items>
@@ -226,18 +226,18 @@
<items>
<menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
<action selector="toggleSmartInsertDelete:" target="-1" id="355" />
</connections>
</menuItem>
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356" />
</connections>
</menuItem>
<menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES" />
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="357" />
</connections>
</menuItem>
</items>
@@ -248,12 +248,12 @@
<items>
<menuItem title="Start Speaking" id="196">
<connections>
<action selector="startSpeaking:" target="-1" id="233"/>
<action selector="startSpeaking:" target="-1" id="233" />
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="195">
<connections>
<action selector="stopSpeaking:" target="-1" id="227"/>
<action selector="stopSpeaking:" target="-1" id="227" />
</connections>
</menuItem>
</items>
@@ -263,182 +263,182 @@
</menu>
</menuItem>
<menuItem title="Format" id="375">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<menu key="submenu" title="Format" id="376">
<items>
<menuItem title="Font" id="377">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<menu key="submenu" title="Font" systemMenu="font" id="388">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="389"/>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390"/>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391"/>
<menuItem title="Show Fonts" keyEquivalent="t" id="389" />
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390" />
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391" />
<menuItem title="Underline" keyEquivalent="u" id="392">
<connections>
<action selector="underline:" target="-1" id="432"/>
<action selector="underline:" target="-1" id="432" />
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="393"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394"/>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395"/>
<menuItem isSeparatorItem="YES" id="396"/>
<menuItem isSeparatorItem="YES" id="393" />
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394" />
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395" />
<menuItem isSeparatorItem="YES" id="396" />
<menuItem title="Kern" id="397">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<menu key="submenu" title="Kern" id="415">
<items>
<menuItem title="Use Default" id="416">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="useStandardKerning:" target="-1" id="438"/>
<action selector="useStandardKerning:" target="-1" id="438" />
</connections>
</menuItem>
<menuItem title="Use None" id="417">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="turnOffKerning:" target="-1" id="441"/>
<action selector="turnOffKerning:" target="-1" id="441" />
</connections>
</menuItem>
<menuItem title="Tighten" id="418">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="tightenKerning:" target="-1" id="431"/>
<action selector="tightenKerning:" target="-1" id="431" />
</connections>
</menuItem>
<menuItem title="Loosen" id="419">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="loosenKerning:" target="-1" id="435"/>
<action selector="loosenKerning:" target="-1" id="435" />
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligature" id="398">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<menu key="submenu" title="Ligature" id="411">
<items>
<menuItem title="Use Default" id="412">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="useStandardLigatures:" target="-1" id="439"/>
<action selector="useStandardLigatures:" target="-1" id="439" />
</connections>
</menuItem>
<menuItem title="Use None" id="413">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="turnOffLigatures:" target="-1" id="440"/>
<action selector="turnOffLigatures:" target="-1" id="440" />
</connections>
</menuItem>
<menuItem title="Use All" id="414">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="useAllLigatures:" target="-1" id="434"/>
<action selector="useAllLigatures:" target="-1" id="434" />
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="399">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<menu key="submenu" title="Baseline" id="405">
<items>
<menuItem title="Use Default" id="406">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="unscript:" target="-1" id="437"/>
<action selector="unscript:" target="-1" id="437" />
</connections>
</menuItem>
<menuItem title="Superscript" id="407">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="superscript:" target="-1" id="430"/>
<action selector="superscript:" target="-1" id="430" />
</connections>
</menuItem>
<menuItem title="Subscript" id="408">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="subscript:" target="-1" id="429"/>
<action selector="subscript:" target="-1" id="429" />
</connections>
</menuItem>
<menuItem title="Raise" id="409">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="raiseBaseline:" target="-1" id="426"/>
<action selector="raiseBaseline:" target="-1" id="426" />
</connections>
</menuItem>
<menuItem title="Lower" id="410">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="lowerBaseline:" target="-1" id="427"/>
<action selector="lowerBaseline:" target="-1" id="427" />
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="400"/>
<menuItem isSeparatorItem="YES" id="400" />
<menuItem title="Show Colors" keyEquivalent="C" id="401">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="433"/>
<action selector="orderFrontColorPanel:" target="-1" id="433" />
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="402"/>
<menuItem isSeparatorItem="YES" id="402" />
<menuItem title="Copy Style" keyEquivalent="c" id="403">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES" />
<connections>
<action selector="copyFont:" target="-1" id="428"/>
<action selector="copyFont:" target="-1" id="428" />
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="404">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES" />
<connections>
<action selector="pasteFont:" target="-1" id="436"/>
<action selector="pasteFont:" target="-1" id="436" />
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="378">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<menu key="submenu" title="Text" id="379">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="380">
<connections>
<action selector="alignLeft:" target="-1" id="442"/>
<action selector="alignLeft:" target="-1" id="442" />
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="381">
<connections>
<action selector="alignCenter:" target="-1" id="445"/>
<action selector="alignCenter:" target="-1" id="445" />
</connections>
</menuItem>
<menuItem title="Justify" id="382">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="alignJustified:" target="-1" id="443"/>
<action selector="alignJustified:" target="-1" id="443" />
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="383">
<connections>
<action selector="alignRight:" target="-1" id="447"/>
<action selector="alignRight:" target="-1" id="447" />
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="384"/>
<menuItem isSeparatorItem="YES" id="384" />
<menuItem title="Show Ruler" id="385">
<modifierMask key="keyEquivalentModifierMask"/>
<modifierMask key="keyEquivalentModifierMask" />
<connections>
<action selector="toggleRuler:" target="-1" id="446"/>
<action selector="toggleRuler:" target="-1" id="446" />
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="386">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES" />
<connections>
<action selector="copyRuler:" target="-1" id="444"/>
<action selector="copyRuler:" target="-1" id="444" />
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="387">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES" />
<connections>
<action selector="pasteRuler:" target="-1" id="448"/>
<action selector="pasteRuler:" target="-1" id="448" />
</connections>
</menuItem>
</items>
@@ -451,14 +451,14 @@
<menu key="submenu" title="View" id="296">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES" />
<connections>
<action selector="toggleToolbarShown:" target="-1" id="366"/>
<action selector="toggleToolbarShown:" target="-1" id="366" />
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="298">
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
<action selector="runToolbarCustomizationPalette:" target="-1" id="365" />
</connections>
</menuItem>
</items>
@@ -469,20 +469,20 @@
<items>
<menuItem title="Minimize" keyEquivalent="m" id="23">
<connections>
<action selector="performMiniaturize:" target="-1" id="37"/>
<action selector="performMiniaturize:" target="-1" id="37" />
</connections>
</menuItem>
<menuItem title="Zoom" id="239">
<connections>
<action selector="performZoom:" target="-1" id="240"/>
<action selector="performZoom:" target="-1" id="240" />
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="92">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
<modifierMask key="keyEquivalentModifierMask" command="YES" />
</menuItem>
<menuItem title="Bring All to Front" id="5">
<connections>
<action selector="arrangeInFront:" target="-1" id="39"/>
<action selector="arrangeInFront:" target="-1" id="39" />
</connections>
</menuItem>
</items>
@@ -491,9 +491,9 @@
<menuItem title="Help" id="103" userLabel="1">
<menu key="submenu" title="Help" id="106" userLabel="2">
<items>
<menuItem title="NewApplication Help" keyEquivalent="?" id="111">
<menuItem title="CPAnimationContextTest Help" keyEquivalent="?" id="111">
<connections>
<action selector="showHelp:" target="-1" id="360"/>
<action selector="showHelp:" target="-1" id="360" />
</connections>
</menuItem>
</items>
@@ -502,76 +502,76 @@
</items>
</menu>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="371">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="390" width="940" height="1040"/>
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1028"/>
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" />
<windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES" />
<rect key="contentRect" x="335" y="390" width="940" height="1040" />
<rect key="screenRect" x="0.0" y="0.0" width="1680" height="1028" />
<view key="contentView" id="372">
<rect key="frame" x="0.0" y="0.0" width="940" height="1040"/>
<autoresizingMask key="autoresizingMask"/>
<rect key="frame" x="0.0" y="0.0" width="940" height="1040" />
<autoresizingMask key="autoresizingMask" />
<subviews>
<button identifier="run" verticalHuggingPriority="750" id="462">
<rect key="frame" x="332" y="999" width="134" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="332" y="999" width="134" height="32" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
<buttonCell key="cell" type="push" title="Start Animation" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="463">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES"/>
<font key="font" metaFont="system"/>
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES" changeBackground="YES" changeGray="YES" />
<font key="font" metaFont="system" />
</buttonCell>
<connections>
<action selector="test:" target="450" id="0vd-DD-jrm"/>
<action selector="test:" target="450" id="0vd-DD-jrm" />
</connections>
</button>
<customView identifier="draw" id="FJv-ci-zoT" customClass="DrawView">
<rect key="frame" x="29" y="769" width="148" height="115"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="29" y="769" width="148" height="115" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
</customView>
<customView identifier="layout" id="Ff1-j8-ljn" customClass="CustomLayoutView">
<rect key="frame" x="29" y="637" width="148" height="115"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="29" y="637" width="148" height="115" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
<subviews>
<customView identifier="vanilla" id="obD-Te-e4b" customClass="ColorView">
<rect key="frame" x="20" y="52" width="53" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="20" y="52" width="53" height="43" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
</customView>
<customView identifier="vanilla" id="kng-GZ-UzT" customClass="ColorView">
<rect key="frame" x="86" y="52" width="53" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="86" y="52" width="53" height="43" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
</customView>
<customView identifier="vanilla" id="Ofx-KZ-FgQ" customClass="ColorView">
<rect key="frame" x="48" y="6" width="53" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="48" y="6" width="53" height="43" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
</customView>
</subviews>
</customView>
<customView identifier="drawLayout" id="dLm-xl-6jP" customClass="CustomLayoutDrawView">
<rect key="frame" x="29" y="499" width="148" height="115"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="29" y="499" width="148" height="115" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
<subviews>
<customView identifier="vanilla" id="Zo1-nI-BE8" customClass="ColorView">
<rect key="frame" x="12" y="61" width="53" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="12" y="61" width="53" height="43" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
</customView>
<customView identifier="vanilla" id="Ohs-DN-x4k" customClass="ColorView">
<rect key="frame" x="84" y="61" width="53" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="84" y="61" width="53" height="43" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
</customView>
<customView identifier="vanilla" id="OGq-ro-Klu" customClass="ColorView">
<rect key="frame" x="48" y="10" width="53" height="43"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<rect key="frame" x="48" y="10" width="53" height="43" />
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES" />
</customView>
</subviews>
</customView>
</subviews>
</view>
<point key="canvasLocation" x="643" y="761"/>
<point key="canvasLocation" x="643" y="761" />
</window>
<customObject id="450" customClass="AppController">
<connections>
<outlet property="drawView" destination="FJv-ci-zoT" id="HzW-FO-0Sa"/>
<outlet property="layoutDrawView" destination="dLm-xl-6jP" id="6fs-7K-Eeq"/>
<outlet property="layoutView" destination="Ff1-j8-ljn" id="ryQ-pg-Xmz"/>
<outlet property="theWindow" destination="371" id="459"/>
<outlet property="drawView" destination="FJv-ci-zoT" id="HzW-FO-0Sa" />
<outlet property="layoutDrawView" destination="dLm-xl-6jP" id="6fs-7K-Eeq" />
<outlet property="layoutView" destination="Ff1-j8-ljn" id="ryQ-pg-Xmz" />
<outlet property="theWindow" destination="371" id="459" />
</connections>
</customObject>
</objects>
</document>
</document>
+67 -52
View File
@@ -1,3 +1,12 @@
/*
Disabled: This unit suite is a legacy benchmark harness designed for side-by-side
micro-benchmarking of JavaScript array operations and sorting implementations.
It relies on non-deterministic random data, wall-clock timing comparisons, and
Node.js host APIs (fs/path), while providing no deterministic functional assertions
or performance SLAs. Retained strictly for historical context until the native
Go and Lisette toolchain transition is fully finalized.
*/
var fs = require("fs");
var path = require("path");
@@ -17,7 +26,7 @@ function shuffle(o)
};
var ELEMENTS = 100,
REPEATS = 10;
REPEATS = 10;
@implementation CPArrayPerformanceTest : OJTestCase
{
@@ -27,111 +36,117 @@ var ELEMENTS = 100,
- (void)setUp
{
descriptors = [
[CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO],
[CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES]
];
[CPSortDescriptor sortDescriptorWithKey:"a" ascending:NO],
[CPSortDescriptor sortDescriptorWithKey:"b" ascending:YES]
];
}
- (void)testAlmostSortedNumericUsingMergeSort
// Included only to ensure an active test is present and avoid 'no tests' warnings.
- (void)testPlaceholder
{
[self assertTrue:YES message:"Placeholder test to maintain test runner compatibility."];
}
- (void)disabled_testAlmostSortedNumericUsingMergeSort
{
console.log();
CPLog.warn("\nNUMERIC ALMOST SORTED");
var a = [self makeUnsorted],
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors];
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors];
[self checkAlmostSorted:sorted];
}
- (void)testAlmostSortedNumericUsingNativeSort
- (void)disabled_testAlmostSortedNumericUsingNativeSort
{
var a = [self makeUnsorted],
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors];
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors];
[self checkAlmostSorted:sorted];
}
- (void)testRandomNumericUsingMergeSort
- (void)disabled_testRandomNumericUsingMergeSort
{
console.log();
CPLog.warn("\nNUMERIC RANDOM");
var a = [self makeRandomNumeric],
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors];
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors];
[self checkRandomSorted:sorted];
}
- (void)testRandomNumericUsingNativeSort
- (void)disabled_testRandomNumericUsingNativeSort
{
var a = [self makeRandomNumeric],
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors];
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors];
[self checkRandomSorted:sorted];
}
- (void)testRandomTextUsingMergeSort
- (void)disabled_testRandomTextUsingMergeSort
{
console.log();
CPLog.warn("\nTEXT RANDOM");
var a = [self makeRandomText],
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors];
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingDescriptors:) withObject:descriptors];
[self checkRandomSorted:sorted];
}
- (void)testRandomTextUsingNativeSort
- (void)disabled_testRandomTextUsingNativeSort
{
var a = [self makeRandomText],
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors];
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingDescriptors:) withObject:descriptors];
[self checkRandomSorted:sorted];
}
- (void)testAlmostSortedNumericUsingMergeSelectorSort
- (void)disabled_testAlmostSortedNumericUsingMergeSelectorSort
{
console.log();
CPLog.warn("\nNUMERIC ALMOST SORTED (SELECTOR)");
var a = [self makeUnsorted],
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
[self checkAlmostSorted:sorted];
}
- (void)testAlmostSortedNumericUsingNativeSelectorSort
- (void)disabled_testAlmostSortedNumericUsingNativeSelectorSort
{
var a = [self makeUnsorted],
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
[self checkAlmostSorted:sorted];
}
- (void)testRandomNumericUsingMergeSelectorSort
- (void)disabled_testRandomNumericUsingMergeSelectorSort
{
console.log();
CPLog.warn("\nNUMERIC RANDOM (SELECTOR)");
var a = [self makeRandomNumeric],
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
[self checkRandomSorted:sorted];
}
- (void)testRandomNumericUsingNativeSelectorSort
- (void)disabled_testRandomNumericUsingNativeSelectorSort
{
var a = [self makeRandomNumeric],
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
[self checkRandomSorted:sorted];
}
- (void)testRandomTextUsingMergeSelectorSort
- (void)disabled_testRandomTextUsingMergeSelectorSort
{
console.log();
CPLog.warn("\nTEXT RANDOM (SELECTOR)");
var a = [self makeRandomText],
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
sorted = [self sort:a usingSortSelector:@selector(sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
[self checkRandomSorted:sorted];
}
- (void)testRandomTextUsingNativeSelectorSort
- (void)disabled_testRandomTextUsingNativeSelectorSort
{
var a = [self makeRandomText],
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
sorted = [self sort:a usingSortSelector:@selector(_native_sortedArrayUsingSelector:) withObject:@selector(compareAAscendingThenBDescending:)];
[self checkRandomSorted:sorted];
}
- (CPArray)sort:(CPArray)anArray usingSortSelector:(SEL)aSelector withObject:(id)anObject
{
var sorted,
start = (new Date).getTime();
start = (new Date).getTime();
for (var i = 0; i < REPEATS; ++i)
sorted = [anArray performSelector:aSelector withObject:anObject];
@@ -167,8 +182,8 @@ var ELEMENTS = 100,
for (var i = 0; i < ELEMENTS; i++)
{
var s = [Sortable new],
n1 = ROUND(RAND() * ELEMENTS),
n2 = ROUND(RAND() * ELEMENTS);
n1 = ROUND(RAND() * ELEMENTS),
n2 = ROUND(RAND() * ELEMENTS);
[s setA:n1];
[s setB:n2];
@@ -181,9 +196,9 @@ var ELEMENTS = 100,
- (CPArray)makeRandomText
{
var the_big_sort = fs.readFileSync(path.join(path.dirname(__filename), "the_big_sort.txt"), {encoding: 'utf-8'}),
words = the_big_sort.split(" ", ELEMENTS),
wordcount = words.length,
array = [];
words = the_big_sort.split(" ", ELEMENTS),
wordcount = words.length,
array = [];
for (var i = 0; i < wordcount - 1; i++)
{
@@ -227,17 +242,17 @@ var ELEMENTS = 100,
}
}
- (void)testObjectsAtIndexesSpeed
- (void)disabled_testObjectsAtIndexesSpeed
{
REPEATS = 100;
var SIZE = 1000,
c = SIZE,
r = REPEATS,
rr = r,
location = 0,
array = [CPArray array],
indexes = [CPIndexSet indexSet];
c = SIZE,
r = REPEATS,
rr = r,
location = 0,
array = [CPArray array],
indexes = [CPIndexSet indexSet];
while (c--)
array.push("" + c);
@@ -251,8 +266,8 @@ var ELEMENTS = 100,
}
var d = new Date(),
test1,
test2;
test1,
test2;
while (r--)
test1 = [array _previous_objectsAtIndexes:indexes];
var dd = new Date();
@@ -270,25 +285,25 @@ var ELEMENTS = 100,
[self fail:"_CPJavaScriptArray -objectsAtIndexes: returns an wrong value"];
}
- (void)testRemoveObjectIdenticalTo
- (void)disabled_testRemoveObjectIdenticalTo
{
REPEATS = 200;
var SIZE = 33 * 6,
allThings = [],
testSources = [];
allThings = [],
testSources = [];
for (var c = 0; c < SIZE; c++)
allThings.push("" + c);
var someThings = [allThings subarrayWithRange:CPMakeRange(SIZE / 3, SIZE / 3)],
removeThings = [allThings subarrayWithRange:(CPMakeRange(0, 2 * SIZE / 3))];
removeThings = [allThings subarrayWithRange:(CPMakeRange(0, 2 * SIZE / 3))];
for (var r = 0; r < REPEATS * 2; r++)
testSources.push(shuffle(someThings));
var d = new Date(),
test1;
test1;
for (var r = 0; r < REPEATS; r++)
{
test1 = testSources.pop();
@@ -297,7 +312,7 @@ var ELEMENTS = 100,
}
var dd = new Date(),
test2;
test2;
for (var r = 0; r < REPEATS; r++)
{
test2 = testSources.pop();
@@ -352,9 +367,9 @@ var ELEMENTS = 100,
var count = [descriptors count];
self.sort(function(lhs, rhs)
{
{
var i = 0,
result = CPOrderedSame;
result = CPOrderedSame;
while (i < count)
if ((result = [descriptors[i++] compareObject:lhs withObject:rhs]) !== CPOrderedSame)
@@ -376,7 +391,7 @@ var ELEMENTS = 100,
- (CPArray)_native_sortUsingSelector:(SEL)aSelector
{
self.sort(function(lhs, rhs)
{
{
return [lhs performSelector:aSelector withObject:rhs];
});
}
+28 -11
View File
@@ -82,18 +82,26 @@
- (void)test_objectAtIndex_
{
var arrayClass = [[self class] arrayClass],
array = [arrayClass array];
array = [arrayClass array],
e;
[self assertThrows:function () { [array objectAtIndex:-1] }];
[self assertThrows:function () { [array objectAtIndex:0] }];
e = [self assertThrows:function () { [array objectAtIndex:-1] }];
[self assert:CPRangeException equals:[e name]];
e = [self assertThrows:function () { [array objectAtIndex:0] }];
[self assert:CPRangeException equals:[e name]];
var array = [arrayClass arrayWithObjects:0, 1, 2];
[self assertThrows:function () { [array objectAtIndex:-1] }];
e = [self assertThrows:function () { [array objectAtIndex:-1] }];
[self assert:CPRangeException equals:[e name]];
[self assert:[array objectAtIndex:0] same:0];
[self assert:[array objectAtIndex:1] same:1];
[self assert:[array objectAtIndex:2] same:2];
[self assertThrows:function () { [array objectAtIndex:3] }];
e = [self assertThrows:function () { [array objectAtIndex:3] }];
[self assert:CPRangeException equals:[e name]];
}
- (void)test_objectsAtIndexes_
@@ -104,20 +112,29 @@
}
var arrayClass = [[self class] arrayClass],
array = [arrayClass array];
array = [arrayClass array],
e;
[self assertThrows:function () { [array objectsAtIndexes:rangeIndexes(0, 1)] }];
e = [self assertThrows:function () { [array objectsAtIndexes:rangeIndexes(0, 1)] }];
[self assert:CPRangeException equals:[e name]];
var array = [arrayClass arrayWithObjects:0, 1, 2];
[self assert:[array objectsAtIndexes:rangeIndexes(0, 1)] equals:[0]];
[self assert:[array objectsAtIndexes:rangeIndexes(0, 2)] equals:[0, 1]];
[self assert:[array objectsAtIndexes:rangeIndexes(0, 3)] equals:[0, 1, 2]];
[self assertThrows:function () { [array objectsAtIndexes:rangeIndexes(0, 4)] }];
e = [self assertThrows:function () { [array objectsAtIndexes:rangeIndexes(0, 4)] }];
[self assert:CPRangeException equals:[e name]];
[self assert:[array objectsAtIndexes:rangeIndexes(1, 1)] equals:[1]];
[self assert:[array objectsAtIndexes:rangeIndexes(1, 2)] equals:[1, 2]];
[self assertThrows:function () { [array objectsAtIndexes:rangeIndexes(1, 3)] }];
[self assertThrows:function () { [array objectsAtIndexes:rangeIndexes(3, 1)] }];
e = [self assertThrows:function () { [array objectsAtIndexes:rangeIndexes(1, 3)] }];
[self assert:CPRangeException equals:[e name]];
e = [self assertThrows:function () { [array objectsAtIndexes:rangeIndexes(3, 1)] }];
[self assert:CPRangeException equals:[e name]];
}
- (void)test_indexOfObject_
@@ -829,7 +846,7 @@
- (id)objectAtIndex:(CPUInteger)anIndex
{
if (anIndex < 0 || anIndex >= [self count])
throw "range error";
[CPException raise:CPRangeException reason:"index (" + anIndex + ") beyond bounds (" + [self count] + ")"];
return array[anIndex];
}
+6 -1
View File
@@ -490,8 +490,11 @@
var result = [CPMutableDictionary dictionary];
// Test basic for...of iteration
for (var [key, value] of dict)
for (var entry of dict)
{
var key = entry[0],
value = entry[1];
[result setObject:value forKey:key];
}
@@ -514,6 +517,8 @@
var iterations = 0;
for (var entry of emptyDict)
{
// Explicitly evaluate the bound variable to satisfy the static analyzer.
[entry self];
iterations++;
}
[self assert:0 equals:iterations message:@"for...of on an empty dictionary should not iterate"];
+1 -1
View File
@@ -578,7 +578,7 @@
- (id)objectAtIndex:(CPUInteger)anIndex
{
if (anIndex < 0 || anIndex >= [self count])
throw "range error";
[CPException raise:CPRangeException reason:"index (" + anIndex + ") beyond bounds (" + [self count] + ")"];
return array[anIndex];
}
+9
View File
@@ -428,10 +428,19 @@
// 3. Test on an empty set
/*
The bound variable must be explicitly read to satisfy the static analyzer.
Standard JavaScript idioms for unused variables, such as the `_` identifier
or the `void` operator, either fail linting or trigger AST collisions in the
legacy Node.js parser. Evaluating the variable via a standard Objective-J
message send resolves the warning while preserving parser stability.
*/
var emptySet = [CPSet set];
var iterations = 0;
for (var entry of emptySet)
{
[itemsSeen addObject:entry];
iterations++;
}
[self assert:0 equals:iterations message:@"for...of on an empty set should not iterate"];
+78 -5
View File
@@ -357,11 +357,14 @@
var expectedAbbreviationLA;
try {
// This logic mimics the _abbreviationForNameAndDate helper function in CPTimeZone.j
var options = { timeZone: laTimeZoneName, timeZoneName: 'long' };
var dateString = (new Date()).toLocaleString('en-US', options);
var longTZName = dateString.replace(/^([0]?\d|[1][0-2])\/((?:[0]?|[1-2])\d|[3][0-1])\/([2][01]|[1][6-9])\d{2}(,?\s*([0]?\d|[1][0-2])(\:[0-5]\d){1,2})*\s*([aApP][mM]{0,2})?\s*/, "");
expectedAbbreviationLA = longTZName.split(" ").map(function(l) { return l[0]}).join("");
// This logic mirrors the _abbreviationForNameAndDate helper in
// CPTimeZone.j: read the short time zone name directly from Intl,
// as an independent check that CPTimeZone's own use of the same
// API returns the same thing, not a duplicate of an algorithm.
var options = { timeZone: laTimeZoneName, timeZoneName: 'short' };
var parts = new Intl.DateTimeFormat('en-US', options).formatToParts(new Date());
var tzPart = parts.filter(function (p) { return p.type === 'timeZoneName'; })[0];
expectedAbbreviationLA = tzPart ? tzPart.value : nil;
} catch (e) {
[self fail:"Could not determine expected abbreviation for America/Los_Angeles"];
return;
@@ -385,4 +388,74 @@
[self assert:[timeZoneHNL abbreviation] equals:@"HST"];
}
// Pins the six timeDifferenceFromUTC entries corrected on CPTimeZoneTest-fix
// (MDT, MSK, NZDT, NZST, WAT, WIT) through the public API, so a regression
// back to any of the old wrong values is caught directly.
- (void)testCorrectedOffsets
{
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MDT"] secondsFromGMT] equals:(-360 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MSK"] secondsFromGMT] equals:(180 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"NZDT"] secondsFromGMT] equals:(780 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"NZST"] secondsFromGMT] equals:(720 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"WAT"] secondsFromGMT] equals:(60 * 60)];
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"WIT"] secondsFromGMT] equals:(420 * 60)];
}
// MSD ("Moscow Summer Time") has no correct current value: Russia abolished
// DST in 2014, so there is no live distinct summer offset for Moscow to
// assign. This pins the current, intentionally-unfixed value, so any future
// edit to it happens deliberately alongside CPTimeZone.j's comment and the
// tracked Intl redesign, not as a silent, unrelated drift.
- (void)testMSDKnownStaleValue
{
[self assert:[[CPTimeZone timeZoneWithAbbreviation:@"MSD"] secondsFromGMT] equals:(240 * 60)];
}
// knownTimeZoneNames should prefer the runtime's own IANA database over the
// old 48-city hardcoded list, on any engine that exposes
// Intl.supportedValuesOf. Skips itself where that API is unavailable, rather
// than asserting a specific engine capability.
- (void)testKnownTimeZoneNamesUsesIntlWhenAvailable
{
if (typeof Intl === "undefined" || typeof Intl.supportedValuesOf !== "function")
return;
var names = [CPTimeZone knownTimeZoneNames];
[self assertTrue:([names count] > 48)
message:"knownTimeZoneNames should use Intl.supportedValuesOf when available, not the small hardcoded fallback list"];
[self assertTrue:[names containsObject:@"Europe/Berlin"]
message:"a zone absent from the old hardcoded list should be present via Intl"];
}
// Disabled: the "fr" entry in localizedName is an empty dictionary, so any
// lookup for a French locale currently returns nil regardless of style. This
// is the same static, English-only table design as before the Intl
// migration; folding it in was deferred because CPTimeZoneNameStyleStandard/
// DaylightSaving need to force a specific state independent of the current
// date, which Intl.formatToParts(date) alone can't do for an arbitrary date.
// Tracked for the larger IANA-identity redesign, not fixed here.
- (void)disabled_testLocalizedNameFrenchLocale
{
var frenchLocale = [[CPLocale alloc] initWithLocaleIdentifier:@"fr_FR"],
timeZone = [CPTimeZone timeZoneWithAbbreviation:@"PST"];
[self assert:[timeZone localizedName:CPTimeZoneNameStyleStandard locale:frenchLocale] equals:@"Heure normale du Pacifique"];
}
// Disabled: timeZoneForSecondsFromGMT: does a linear scan of
// timeDifferenceFromUTC and returns the first matching key, with no defined
// tie-break when more than one abbreviation shares an offset. MDT and CST
// both now correctly resolve to -360; asking for that offset silently
// returns whichever CPDictionary happens to enumerate first, not a
// documented choice. The idealized contract is that an inherently ambiguous
// query should not silently guess one answer. No fix exists for this within
// the current flat abbreviation-to-offset table design.
- (void)disabled_testTimeZoneForSecondsFromGMTOffsetCollisionIsAmbiguous
{
var timeZone = [CPTimeZone timeZoneForSecondsFromGMT:(-360 * 60)];
[self assert:timeZone equals:nil];
}
@end
-94
View File
@@ -1,94 +0,0 @@
/*
* Jakefile
* ArrayController
*
* Created by Alexander Ljungberg on April 2, 2011.
* Copyright 2011, WireLoad 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 ("ArrayController", function(task)
{
task.setBuildIntermediatesPath(FILE.join("Build", "ArrayController.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setProductName("ArrayController");
task.setIdentifier("com.yourcompany.ArrayController");
task.setVersion("1.0");
task.setAuthor("WireLoad");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("ArrayController");
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", ["ArrayController"], 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", "ArrayController", "index.html")]);
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", "ArrayController", "index.html")]);
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", "ArrayController"));
OS.system(["press", "-f", FILE.join("Build", "Release", "ArrayController"), FILE.join("Build", "Deployment", "ArrayController")]);
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Desktop", "ArrayController"));
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "ArrayController"), FILE.join("Build", "Desktop", "ArrayController", "ArrayController.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
OS.system([FILE.join("Build", "Desktop", "ArrayController", "ArrayController.app", "Contents", "MacOS", "NativeHost"), "-i"]);
});
function printResults(configuration)
{
print("----------------------------");
print(configuration+" app built at path: "+FILE.join("Build", configuration, "ArrayController"));
print("----------------------------");
}
File diff suppressed because one or more lines are too long
@@ -1,941 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<archive type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="7.10">
<data>
<int key="IBDocument.SystemTarget">1050</int>
<string key="IBDocument.SystemVersion">12C60</string>
<string key="IBDocument.InterfaceBuilderVersion">3084</string>
<string key="IBDocument.AppKitVersion">1187.34</string>
<string key="IBDocument.HIToolboxVersion">625.00</string>
<object class="NSMutableDictionary" key="IBDocument.PluginVersions">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin</string>
<string key="NS.object.0">3084</string>
</object>
<object class="NSArray" key="IBDocument.IntegratedClassDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSArrayController</string>
<string>NSButton</string>
<string>NSButtonCell</string>
<string>NSCustomObject</string>
<string>NSScrollView</string>
<string>NSScroller</string>
<string>NSTableHeaderView</string>
<string>NSTableView</string>
<string>NSTextField</string>
<string>NSTextFieldCell</string>
<string>NSView</string>
<string>NSWindowTemplate</string>
</object>
<object class="NSArray" key="IBDocument.PluginDependencies">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
<object class="NSMutableDictionary" key="IBDocument.Metadata">
<string key="NS.key.0">PluginDependencyRecalculationVersion</string>
<integer value="1" key="NS.object.0"/>
</object>
<object class="NSMutableArray" key="IBDocument.RootObjects" id="1048">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSCustomObject" id="1021">
<string key="NSClassName">NSApplication</string>
</object>
<object class="NSCustomObject" id="1014">
<string key="NSClassName">FirstResponder</string>
</object>
<object class="NSCustomObject" id="1050">
<string key="NSClassName">NSApplication</string>
</object>
<object class="NSWindowTemplate" id="972006081">
<int key="NSWindowStyleMask">7</int>
<int key="NSWindowBacking">2</int>
<string key="NSWindowRect">{{335, 445}, {480, 305}}</string>
<int key="NSWTFlags">1946157056</int>
<string key="NSWindowTitle">Window</string>
<string key="NSWindowClass">NSWindow</string>
<nil key="NSViewClass"/>
<nil key="NSUserInterfaceItemIdentifier"/>
<object class="NSView" key="NSWindowView" id="439893737">
<reference key="NSNextResponder"/>
<int key="NSvFlags">256</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSScrollView" id="152379243">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSClipView" id="284793823">
<reference key="NSNextResponder" ref="152379243"/>
<int key="NSvFlags">2304</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSTableView" id="971713637">
<reference key="NSNextResponder" ref="284793823"/>
<int key="NSvFlags">256</int>
<string key="NSFrameSize">{443, 117}</string>
<reference key="NSSuperview" ref="284793823"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="362059487"/>
<bool key="NSEnabled">YES</bool>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
<bool key="NSControlAllowsExpansionToolTips">YES</bool>
<object class="NSTableHeaderView" key="NSHeaderView" id="1058581420">
<reference key="NSNextResponder" ref="791373261"/>
<int key="NSvFlags">256</int>
<string key="NSFrameSize">{443, 17}</string>
<reference key="NSSuperview" ref="791373261"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="284793823"/>
<reference key="NSTableView" ref="971713637"/>
</object>
<object class="_NSCornerView" key="NSCornerView">
<nil key="NSNextResponder"/>
<int key="NSvFlags">-2147483392</int>
<string key="NSFrame">{{224, 0}, {16, 17}}</string>
</object>
<object class="NSMutableArray" key="NSTableColumns">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<double key="NSIntercellSpacingWidth">3</double>
<double key="NSIntercellSpacingHeight">2</double>
<object class="NSColor" key="NSBackgroundColor" id="894569415">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MQA</bytes>
</object>
<object class="NSColor" key="NSGridColor">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">gridColor</string>
<object class="NSColor" key="NSColor">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC41AA</bytes>
</object>
</object>
<double key="NSRowHeight">17</double>
<int key="NSTvFlags">-566231040</int>
<reference key="NSDelegate"/>
<reference key="NSDataSource"/>
<int key="NSColumnAutoresizingStyle">5</int>
<int key="NSDraggingSourceMaskForLocal">15</int>
<int key="NSDraggingSourceMaskForNonLocal">0</int>
<bool key="NSAllowsTypeSelect">YES</bool>
<int key="NSTableViewDraggingDestinationStyle">0</int>
<int key="NSTableViewGroupRowStyle">1</int>
</object>
</object>
<string key="NSFrame">{{1, 17}, {443, 117}}</string>
<reference key="NSSuperview" ref="152379243"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="971713637"/>
<reference key="NSDocView" ref="971713637"/>
<object class="NSColor" key="NSBGColor" id="651906913">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlBackgroundColor</string>
<object class="NSColor" key="NSColor" id="279320231">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MC42NjY2NjY2NjY3AA</bytes>
</object>
</object>
<int key="NScvFlags">4</int>
</object>
<object class="NSScroller" id="362059487">
<reference key="NSNextResponder" ref="152379243"/>
<int key="NSvFlags">-2147483392</int>
<string key="NSFrame">{{224, 17}, {15, 102}}</string>
<reference key="NSSuperview" ref="152379243"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="1056492085"/>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
<reference key="NSTarget" ref="152379243"/>
<string key="NSAction">_doScroller:</string>
<double key="NSCurValue">1</double>
<double key="NSPercent">0.1947367936372757</double>
</object>
<object class="NSScroller" id="1056492085">
<reference key="NSNextResponder" ref="152379243"/>
<int key="NSvFlags">-2147483392</int>
<string key="NSFrame">{{1, 119}, {223, 15}}</string>
<reference key="NSSuperview" ref="152379243"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="351504327"/>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
<int key="NSsFlags">1</int>
<reference key="NSTarget" ref="152379243"/>
<string key="NSAction">_doScroller:</string>
<double key="NSPercent">0.57142859697341919</double>
</object>
<object class="NSClipView" id="791373261">
<reference key="NSNextResponder" ref="152379243"/>
<int key="NSvFlags">2304</int>
<object class="NSMutableArray" key="NSSubviews">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="1058581420"/>
</object>
<string key="NSFrame">{{1, 0}, {443, 17}}</string>
<reference key="NSSuperview" ref="152379243"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="1058581420"/>
<reference key="NSDocView" ref="1058581420"/>
<reference key="NSBGColor" ref="651906913"/>
<int key="NScvFlags">4</int>
</object>
</object>
<string key="NSFrame">{{15, 150}, {445, 135}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="791373261"/>
<int key="NSsFlags">133682</int>
<reference key="NSVScroller" ref="362059487"/>
<reference key="NSHScroller" ref="1056492085"/>
<reference key="NSContentView" ref="284793823"/>
<reference key="NSHeaderClipView" ref="791373261"/>
<bytes key="NSScrollAmts">QSAAAEEgAABBmAAAQZgAAA</bytes>
<double key="NSMinMagnification">0.25</double>
<double key="NSMaxMagnification">4</double>
<double key="NSMagnification">1</double>
</object>
<object class="NSButton" id="351504327">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{9, 110}, {40, 32}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="214480962"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="615437802">
<int key="NSCellFlags">67108864</int>
<int key="NSCellFlags2">134217728</int>
<string key="NSContents"/>
<object class="NSFont" key="NSSupport" id="856882769">
<string key="NSName">LucidaGrande</string>
<double key="NSSize">13</double>
<int key="NSfFlags">1044</int>
</object>
<reference key="NSControlView" ref="351504327"/>
<int key="NSButtonFlags">-2033958912</int>
<int key="NSButtonFlags2">129</int>
<object class="NSCustomResource" key="NSNormalImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSAddTemplate</string>
</object>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
<object class="NSButton" id="214480962">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{49, 110}, {41, 32}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="473445886"/>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="791512344">
<int key="NSCellFlags">67108864</int>
<int key="NSCellFlags2">134217728</int>
<string key="NSContents"/>
<reference key="NSSupport" ref="856882769"/>
<reference key="NSControlView" ref="214480962"/>
<int key="NSButtonFlags">-2033958912</int>
<int key="NSButtonFlags2">129</int>
<object class="NSCustomResource" key="NSNormalImage">
<string key="NSClassName">NSImage</string>
<string key="NSResourceName">NSRemoveTemplate</string>
</object>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
<object class="NSTextField" id="435057252">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{12, 77}, {155, 17}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="314396040"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="992079013">
<int key="NSCellFlags">68157504</int>
<int key="NSCellFlags2">71304192</int>
<string key="NSContents">Selected Name:</string>
<reference key="NSSupport" ref="856882769"/>
<reference key="NSControlView" ref="435057252"/>
<object class="NSColor" key="NSBackgroundColor" id="617443217">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlColor</string>
<reference key="NSColor" ref="279320231"/>
</object>
<object class="NSColor" key="NSTextColor" id="650615687">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">controlTextColor</string>
<object class="NSColor" key="NSColor" id="730639221">
<int key="NSColorSpace">3</int>
<bytes key="NSWhite">MAA</bytes>
</object>
</object>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
<object class="NSTextField" id="794763058">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{66, 47}, {101, 17}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="563826159"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="349060415">
<int key="NSCellFlags">68157504</int>
<int key="NSCellFlags2">71304192</int>
<string key="NSContents">Selected Price:</string>
<reference key="NSSupport" ref="856882769"/>
<reference key="NSControlView" ref="794763058"/>
<reference key="NSBackgroundColor" ref="617443217"/>
<reference key="NSTextColor" ref="650615687"/>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
<object class="NSTextField" id="153336867">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{17, 20}, {150, 17}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="620159090"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="943890587">
<int key="NSCellFlags">68157504</int>
<int key="NSCellFlags2">71304192</int>
<string key="NSContents">Sum of Selected Prices:</string>
<reference key="NSSupport" ref="856882769"/>
<reference key="NSControlView" ref="153336867"/>
<reference key="NSBackgroundColor" ref="617443217"/>
<reference key="NSTextColor" ref="650615687"/>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
<object class="NSTextField" id="620159090">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{174, 20}, {292, 17}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="203129013">
<int key="NSCellFlags">68157504</int>
<int key="NSCellFlags2">272630784</int>
<string key="NSContents">Label</string>
<reference key="NSSupport" ref="856882769"/>
<reference key="NSControlView" ref="620159090"/>
<reference key="NSBackgroundColor" ref="617443217"/>
<reference key="NSTextColor" ref="650615687"/>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
<object class="NSTextField" id="314396040">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{174, 75}, {286, 22}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="794763058"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="66689853">
<int key="NSCellFlags">-1804599231</int>
<int key="NSCellFlags2">272630784</int>
<string key="NSContents"/>
<reference key="NSSupport" ref="856882769"/>
<reference key="NSControlView" ref="314396040"/>
<bool key="NSDrawsBackground">YES</bool>
<object class="NSColor" key="NSBackgroundColor" id="879609906">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">textBackgroundColor</string>
<reference key="NSColor" ref="894569415"/>
</object>
<object class="NSColor" key="NSTextColor" id="949633793">
<int key="NSColorSpace">6</int>
<string key="NSCatalogName">System</string>
<string key="NSColorName">textColor</string>
<reference key="NSColor" ref="730639221"/>
</object>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
<object class="NSTextField" id="563826159">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{174, 45}, {286, 22}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="153336867"/>
<bool key="NSEnabled">YES</bool>
<object class="NSTextFieldCell" key="NSCell" id="841860402">
<int key="NSCellFlags">-1804599231</int>
<int key="NSCellFlags2">272630784</int>
<string key="NSContents"/>
<reference key="NSSupport" ref="856882769"/>
<reference key="NSControlView" ref="563826159"/>
<bool key="NSDrawsBackground">YES</bool>
<reference key="NSBackgroundColor" ref="879609906"/>
<reference key="NSTextColor" ref="949633793"/>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
<object class="NSButton" id="473445886">
<reference key="NSNextResponder" ref="439893737"/>
<int key="NSvFlags">268</int>
<string key="NSFrame">{{90, 110}, {76, 32}}</string>
<reference key="NSSuperview" ref="439893737"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="435057252"/>
<string key="NSReuseIdentifierKey">_NS:9</string>
<bool key="NSEnabled">YES</bool>
<object class="NSButtonCell" key="NSCell" id="120906924">
<int key="NSCellFlags">67108864</int>
<int key="NSCellFlags2">134217728</int>
<string key="NSContents">Insert</string>
<reference key="NSSupport" ref="856882769"/>
<string key="NSCellIdentifier">_NS:9</string>
<reference key="NSControlView" ref="473445886"/>
<int key="NSButtonFlags">-2038284288</int>
<int key="NSButtonFlags2">129</int>
<string key="NSAlternateContents"/>
<string key="NSKeyEquivalent"/>
<int key="NSPeriodicDelay">200</int>
<int key="NSPeriodicInterval">25</int>
</object>
<bool key="NSAllowsLogicalLayoutDirection">NO</bool>
</object>
</object>
<string key="NSFrameSize">{480, 305}</string>
<reference key="NSSuperview"/>
<reference key="NSWindow"/>
<reference key="NSNextKeyView" ref="152379243"/>
</object>
<string key="NSScreenRect">{{0, 0}, {2560, 1418}}</string>
<string key="NSMaxSize">{10000000000000, 10000000000000}</string>
<bool key="NSWindowIsRestorable">YES</bool>
</object>
<object class="NSCustomObject" id="635946545">
<string key="NSClassName">AppController</string>
</object>
<object class="NSArrayController" id="939887647">
<bool key="NSEditable">YES</bool>
<object class="_NSManagedProxy" key="_NSManagedProxy"/>
<bool key="NSAvoidsEmptySelection">YES</bool>
<bool key="NSPreservesSelection">YES</bool>
<bool key="NSSelectsInsertedObjects">YES</bool>
<bool key="NSFilterRestrictsInsertion">YES</bool>
<bool key="NSClearsFilterPredicateOnInsertion">YES</bool>
</object>
</object>
<object class="IBObjectContainer" key="IBDocument.Objects">
<object class="NSMutableArray" key="connectionRecords">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">delegate</string>
<reference key="source" ref="1021"/>
<reference key="destination" ref="635946545"/>
</object>
<int key="connectionID">451</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">theWindow</string>
<reference key="source" ref="635946545"/>
<reference key="destination" ref="972006081"/>
</object>
<int key="connectionID">459</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">tableView</string>
<reference key="source" ref="635946545"/>
<reference key="destination" ref="971713637"/>
</object>
<int key="connectionID">483</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">totalCountField</string>
<reference key="source" ref="635946545"/>
<reference key="destination" ref="620159090"/>
</object>
<int key="connectionID">498</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">selectedNameField</string>
<reference key="source" ref="635946545"/>
<reference key="destination" ref="314396040"/>
</object>
<int key="connectionID">507</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">selectedPriceField</string>
<reference key="source" ref="635946545"/>
<reference key="destination" ref="563826159"/>
</object>
<int key="connectionID">508</int>
</object>
<object class="IBConnectionRecord">
<object class="IBOutletConnection" key="connection">
<string key="label">arrayController</string>
<reference key="source" ref="635946545"/>
<reference key="destination" ref="939887647"/>
</object>
<int key="connectionID">512</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">add:</string>
<reference key="source" ref="939887647"/>
<reference key="destination" ref="351504327"/>
</object>
<int key="connectionID">513</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">remove:</string>
<reference key="source" ref="939887647"/>
<reference key="destination" ref="214480962"/>
</object>
<int key="connectionID">514</int>
</object>
<object class="IBConnectionRecord">
<object class="IBActionConnection" key="connection">
<string key="label">insert:</string>
<reference key="source" ref="939887647"/>
<reference key="destination" ref="473445886"/>
</object>
<int key="connectionID">515</int>
</object>
</object>
<object class="IBMutableOrderedSet" key="objectRecords">
<object class="NSArray" key="orderedObjects">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBObjectRecord">
<int key="objectID">0</int>
<object class="NSArray" key="object" id="1049">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<reference key="children" ref="1048"/>
<nil key="parent"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">-2</int>
<reference key="object" ref="1021"/>
<reference key="parent" ref="1049"/>
<string key="objectName">File's Owner</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-1</int>
<reference key="object" ref="1014"/>
<reference key="parent" ref="1049"/>
<string key="objectName">First Responder</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">-3</int>
<reference key="object" ref="1050"/>
<reference key="parent" ref="1049"/>
<string key="objectName">Application</string>
</object>
<object class="IBObjectRecord">
<int key="objectID">371</int>
<reference key="object" ref="972006081"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="439893737"/>
</object>
<reference key="parent" ref="1049"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">372</int>
<reference key="object" ref="439893737"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="152379243"/>
<reference ref="435057252"/>
<reference ref="314396040"/>
<reference ref="794763058"/>
<reference ref="563826159"/>
<reference ref="153336867"/>
<reference ref="620159090"/>
<reference ref="351504327"/>
<reference ref="214480962"/>
<reference ref="473445886"/>
</object>
<reference key="parent" ref="972006081"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">450</int>
<reference key="object" ref="635946545"/>
<reference key="parent" ref="1049"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">460</int>
<reference key="object" ref="152379243"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="362059487"/>
<reference ref="1056492085"/>
<reference ref="971713637"/>
<reference ref="1058581420"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">461</int>
<reference key="object" ref="362059487"/>
<reference key="parent" ref="152379243"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">462</int>
<reference key="object" ref="1056492085"/>
<reference key="parent" ref="152379243"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">463</int>
<reference key="object" ref="971713637"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
</object>
<reference key="parent" ref="152379243"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">464</int>
<reference key="object" ref="1058581420"/>
<reference key="parent" ref="152379243"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">469</int>
<reference key="object" ref="351504327"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="615437802"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">470</int>
<reference key="object" ref="615437802"/>
<reference key="parent" ref="351504327"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">471</int>
<reference key="object" ref="214480962"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="791512344"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">472</int>
<reference key="object" ref="791512344"/>
<reference key="parent" ref="214480962"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">484</int>
<reference key="object" ref="435057252"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="992079013"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">485</int>
<reference key="object" ref="992079013"/>
<reference key="parent" ref="435057252"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">488</int>
<reference key="object" ref="794763058"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="349060415"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">489</int>
<reference key="object" ref="349060415"/>
<reference key="parent" ref="794763058"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">492</int>
<reference key="object" ref="153336867"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="943890587"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">493</int>
<reference key="object" ref="943890587"/>
<reference key="parent" ref="153336867"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">494</int>
<reference key="object" ref="620159090"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="203129013"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">495</int>
<reference key="object" ref="203129013"/>
<reference key="parent" ref="620159090"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">499</int>
<reference key="object" ref="314396040"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="66689853"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">500</int>
<reference key="object" ref="66689853"/>
<reference key="parent" ref="314396040"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">501</int>
<reference key="object" ref="563826159"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="841860402"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">502</int>
<reference key="object" ref="841860402"/>
<reference key="parent" ref="563826159"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">509</int>
<reference key="object" ref="473445886"/>
<object class="NSMutableArray" key="children">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference ref="120906924"/>
</object>
<reference key="parent" ref="439893737"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">510</int>
<reference key="object" ref="120906924"/>
<reference key="parent" ref="473445886"/>
</object>
<object class="IBObjectRecord">
<int key="objectID">511</int>
<reference key="object" ref="939887647"/>
<reference key="parent" ref="1049"/>
</object>
</object>
</object>
<object class="NSMutableDictionary" key="flattenedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>-1.IBPluginDependency</string>
<string>-2.IBPluginDependency</string>
<string>-3.IBPluginDependency</string>
<string>371.IBPluginDependency</string>
<string>371.IBWindowTemplateEditedContentRect</string>
<string>371.NSWindowTemplate.visibleAtLaunch</string>
<string>372.IBPluginDependency</string>
<string>450.IBPluginDependency</string>
<string>460.IBPluginDependency</string>
<string>461.IBPluginDependency</string>
<string>462.IBPluginDependency</string>
<string>463.IBPluginDependency</string>
<string>464.IBPluginDependency</string>
<string>469.IBPluginDependency</string>
<string>470.IBPluginDependency</string>
<string>471.IBPluginDependency</string>
<string>472.IBPluginDependency</string>
<string>484.IBPluginDependency</string>
<string>485.IBPluginDependency</string>
<string>488.IBPluginDependency</string>
<string>489.IBPluginDependency</string>
<string>492.IBPluginDependency</string>
<string>493.IBPluginDependency</string>
<string>494.IBPluginDependency</string>
<string>495.IBPluginDependency</string>
<string>499.IBPluginDependency</string>
<string>500.IBPluginDependency</string>
<string>501.IBPluginDependency</string>
<string>502.IBPluginDependency</string>
<string>509.IBPluginDependency</string>
<string>510.IBPluginDependency</string>
<string>511.IBPluginDependency</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>{{108, 156}, {480, 305}}</string>
<integer value="1"/>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
<string>com.apple.InterfaceBuilder.CocoaPlugin</string>
</object>
</object>
<object class="NSMutableDictionary" key="unlocalizedProperties">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="1049"/>
<reference key="dict.values" ref="1049"/>
</object>
<nil key="activeLocalization"/>
<object class="NSMutableDictionary" key="localizations">
<bool key="EncodedWithXMLCoder">YES</bool>
<reference key="dict.sortedKeys" ref="1049"/>
<reference key="dict.values" ref="1049"/>
</object>
<nil key="sourceID"/>
<int key="maxID">515</int>
</object>
<object class="IBClassDescriber" key="IBDocument.Classes">
<object class="NSMutableArray" key="referencedPartialClassDescriptions">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBPartialClassDescription">
<string key="className">AppController</string>
<string key="superclassName">NSObject</string>
<object class="NSMutableDictionary" key="outlets">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>arrayController</string>
<string>selectedNameField</string>
<string>selectedPriceField</string>
<string>tableView</string>
<string>theWindow</string>
<string>totalCountField</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSArrayController</string>
<string>NSTextField</string>
<string>NSTextField</string>
<string>NSTableView</string>
<string>NSWindow</string>
<string>NSTextField</string>
</object>
</object>
<object class="NSMutableDictionary" key="toOneOutletInfosByName">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>arrayController</string>
<string>selectedNameField</string>
<string>selectedPriceField</string>
<string>tableView</string>
<string>theWindow</string>
<string>totalCountField</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="IBToOneOutletInfo">
<string key="name">arrayController</string>
<string key="candidateClassName">NSArrayController</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">selectedNameField</string>
<string key="candidateClassName">NSTextField</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">selectedPriceField</string>
<string key="candidateClassName">NSTextField</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">tableView</string>
<string key="candidateClassName">NSTableView</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">theWindow</string>
<string key="candidateClassName">NSWindow</string>
</object>
<object class="IBToOneOutletInfo">
<string key="name">totalCountField</string>
<string key="candidateClassName">NSTextField</string>
</object>
</object>
</object>
<object class="IBClassDescriptionSource" key="sourceIdentifier">
<string key="majorKey">IBProjectSource</string>
<string key="minorKey">./Classes/AppController.h</string>
</object>
</object>
</object>
</object>
<int key="IBDocument.localizationMode">0</int>
<string key="IBDocument.TargetRuntimeIdentifier">IBCocoaFramework</string>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.macosx</string>
<integer value="1050" key="NS.object.0"/>
</object>
<object class="NSMutableDictionary" key="IBDocument.PluginDeclaredDevelopmentDependencies">
<string key="NS.key.0">com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3</string>
<integer value="3000" key="NS.object.0"/>
</object>
<bool key="IBDocument.PluginDeclaredDependenciesTrackSystemTargetVersion">YES</bool>
<int key="IBDocument.defaultPropertyAccessControl">3</int>
<object class="NSMutableDictionary" key="IBDocument.LastKnownImageSizes">
<bool key="EncodedWithXMLCoder">YES</bool>
<object class="NSArray" key="dict.sortedKeys">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>NSAddTemplate</string>
<string>NSRemoveTemplate</string>
</object>
<object class="NSArray" key="dict.values">
<bool key="EncodedWithXMLCoder">YES</bool>
<string>{8, 8}</string>
<string>{8, 8}</string>
</object>
</object>
</data>
</archive>
@@ -6,19 +6,19 @@
* Copyright 2012, Your Company All rights reserved.
*/
var ENV = require("system").env,
FILE = require("file"),
JAKE = require("jake"),
var ENV = process.env,
cp = require("child_process"),
fs = require("fs"),
path = require("path"),
task = JAKE.task,
FileList = JAKE.FileList,
app = require("cappuccino/jake").app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
OS = require("os");
app = CAPPUCCINO.Jake.applicationtask.app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug";
app ("ArrayControllerInitialSelectionTest", function(task)
{
task.setBuildIntermediatesPath(FILE.join("Build", "ArrayControllerInitialSelectionTest.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setBuildIntermediatesPath(path.join("Build", "ArrayControllerInitialSelectionTest.build", configuration));
task.setBuildPath(path.join("Build", configuration));
task.setProductName("ArrayControllerInitialSelectionTest");
task.setIdentifier("com.yourcompany.ArrayControllerInitialSelectionTest");
@@ -26,7 +26,7 @@ app ("ArrayControllerInitialSelectionTest", function(task)
task.setAuthor("Your Company");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("ArrayControllerInitialSelectionTest");
task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**")));
task.setSources((new FileList("**/*.j")).exclude(path.join("Build", "**")));
task.setResources(new FileList("Resources/**"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
@@ -58,36 +58,36 @@ task ("release", function()
task ("run", ["debug"], function()
{
OS.system(["open", FILE.join("Build", "Debug", "ArrayControllerInitialSelectionTest", "index.html")]);
cp.execSync(["open", path.join("Build", "Debug", "ArrayControllerInitialSelectionTest", "index.html")].join(" "), { stdio: "inherit" });
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", "ArrayControllerInitialSelectionTest", "index.html")]);
cp.execSync(["open", path.join("Build", "Release", "ArrayControllerInitialSelectionTest", "index.html")].join(" "), { stdio: "inherit" });
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", "ArrayControllerInitialSelectionTest"));
OS.system(["press", "-f", FILE.join("Build", "Release", "ArrayControllerInitialSelectionTest"), FILE.join("Build", "Deployment", "ArrayControllerInitialSelectionTest")]);
fs.mkdirSync(path.join("Build", "Deployment", "ArrayControllerInitialSelectionTest"), { recursive: true });
cp.execSync(["press", "-f", path.join("Build", "Release", "ArrayControllerInitialSelectionTest"), path.join("Build", "Deployment", "ArrayControllerInitialSelectionTest")].join(" "), { stdio: "inherit" });
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Desktop", "ArrayControllerInitialSelectionTest"));
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "ArrayControllerInitialSelectionTest"), FILE.join("Build", "Desktop", "ArrayControllerInitialSelectionTest", "ArrayControllerInitialSelectionTest.app"));
fs.mkdirSync(path.join("Build", "Desktop", "ArrayControllerInitialSelectionTest"), { recursive: true });
require("@objj/nativehost").buildNativeHost(path.join("Build", "Release", "ArrayControllerInitialSelectionTest"), path.join("Build", "Desktop", "ArrayControllerInitialSelectionTest", "ArrayControllerInitialSelectionTest.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
OS.system([FILE.join("Build", "Desktop", "ArrayControllerInitialSelectionTest", "ArrayControllerInitialSelectionTest.app", "Contents", "MacOS", "NativeHost"), "-i"]);
cp.execSync([path.join("Build", "Desktop", "ArrayControllerInitialSelectionTest", "ArrayControllerInitialSelectionTest.app", "Contents", "MacOS", "NativeHost"), "-i"].join(" "), { stdio: "inherit" });
});
function printResults(configuration)
{
print("----------------------------");
print(configuration+" app built at path: "+FILE.join("Build", configuration, "ArrayControllerInitialSelectionTest"));
print("----------------------------");
console.log("----------------------------");
console.log(configuration+" app built at path: "+path.join("Build", configuration, "ArrayControllerInitialSelectionTest"));
console.log("----------------------------");
}
@@ -6,19 +6,19 @@
* Copyright 2012, Your Company All rights reserved.
*/
var ENV = require("system").env,
FILE = require("file"),
JAKE = require("jake"),
var ENV = process.env,
cp = require("child_process"),
fs = require("fs"),
path = require("path"),
task = JAKE.task,
FileList = JAKE.FileList,
app = require("cappuccino/jake").app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
OS = require("os");
app = CAPPUCCINO.Jake.applicationtask.app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug";
app ("ArrayControllerRemovingFirstTest", function(task)
{
task.setBuildIntermediatesPath(FILE.join("Build", "ArrayControllerRemovingFirstTest.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setBuildIntermediatesPath(path.join("Build", "ArrayControllerRemovingFirstTest.build", configuration));
task.setBuildPath(path.join("Build", configuration));
task.setProductName("ArrayControllerRemovingFirstTest");
task.setIdentifier("com.yourcompany.ArrayControllerRemovingFirstTest");
@@ -26,7 +26,7 @@ app ("ArrayControllerRemovingFirstTest", function(task)
task.setAuthor("Your Company");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("ArrayControllerRemovingFirstTest");
task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**")));
task.setSources((new FileList("**/*.j")).exclude(path.join("Build", "**")));
task.setResources(new FileList("Resources/**"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
@@ -58,36 +58,36 @@ task ("release", function()
task ("run", ["debug"], function()
{
OS.system(["open", FILE.join("Build", "Debug", "ArrayControllerRemovingFirstTest", "index.html")]);
cp.execSync(["open", path.join("Build", "Debug", "ArrayControllerRemovingFirstTest", "index.html")].join(" "), { stdio: "inherit" });
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", "ArrayControllerRemovingFirstTest", "index.html")]);
cp.execSync(["open", path.join("Build", "Release", "ArrayControllerRemovingFirstTest", "index.html")].join(" "), { stdio: "inherit" });
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", "ArrayControllerRemovingFirstTest"));
OS.system(["press", "-f", FILE.join("Build", "Release", "ArrayControllerRemovingFirstTest"), FILE.join("Build", "Deployment", "ArrayControllerRemovingFirstTest")]);
fs.mkdirSync(path.join("Build", "Deployment", "ArrayControllerRemovingFirstTest"), { recursive: true });
cp.execSync(["press", "-f", path.join("Build", "Release", "ArrayControllerRemovingFirstTest"), path.join("Build", "Deployment", "ArrayControllerRemovingFirstTest")].join(" "), { stdio: "inherit" });
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Desktop", "ArrayControllerRemovingFirstTest"));
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "ArrayControllerRemovingFirstTest"), FILE.join("Build", "Desktop", "ArrayControllerRemovingFirstTest", "ArrayControllerRemovingFirstTest.app"));
fs.mkdirSync(path.join("Build", "Desktop", "ArrayControllerRemovingFirstTest"), { recursive: true });
require("@objj/nativehost").buildNativeHost(path.join("Build", "Release", "ArrayControllerRemovingFirstTest"), path.join("Build", "Desktop", "ArrayControllerRemovingFirstTest", "ArrayControllerRemovingFirstTest.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
OS.system([FILE.join("Build", "Desktop", "ArrayControllerRemovingFirstTest", "ArrayControllerRemovingFirstTest.app", "Contents", "MacOS", "NativeHost"), "-i"]);
cp.execSync([path.join("Build", "Desktop", "ArrayControllerRemovingFirstTest", "ArrayControllerRemovingFirstTest.app", "Contents", "MacOS", "NativeHost"), "-i"].join(" "), { stdio: "inherit" });
});
function printResults(configuration)
{
print("----------------------------");
print(configuration+" app built at path: "+FILE.join("Build", configuration, "ArrayControllerRemovingFirstTest"));
print("----------------------------");
console.log("----------------------------");
console.log(configuration+" app built at path: "+path.join("Build", configuration, "ArrayControllerRemovingFirstTest"));
console.log("----------------------------");
}
+94
View File
@@ -0,0 +1,94 @@
/*
* Jakefile
* ArrayControllerTest
*
* Created by Alexander Ljungberg on April 2, 2011.
* Copyright 2011, WireLoad All rights reserved.
*/
var ENV = process.env,
cp = require("child_process"),
fs = require("fs"),
path = require("path"),
task = JAKE.task,
FileList = JAKE.FileList,
app = CAPPUCCINO.Jake.applicationtask.app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug";
app ("ArrayControllerTest", function(task)
{
task.setBuildIntermediatesPath(path.join("Build", "ArrayControllerTest.build", configuration));
task.setBuildPath(path.join("Build", configuration));
task.setProductName("ArrayControllerTest");
task.setIdentifier("com.yourcompany.ArrayControllerTest");
task.setVersion("1.0");
task.setAuthor("WireLoad");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("ArrayControllerTest");
task.setSources((new FileList("**/*.j")).exclude(path.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", ["ArrayControllerTest"], 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()
{
cp.execSync(["open", path.join("Build", "Debug", "ArrayControllerTest", "index.html")].join(" "), { stdio: "inherit" });
});
task ("run-release", ["release"], function()
{
cp.execSync(["open", path.join("Build", "Release", "ArrayControllerTest", "index.html")].join(" "), { stdio: "inherit" });
});
task ("deploy", ["release"], function()
{
fs.mkdirSync(path.join("Build", "Deployment", "ArrayControllerTest"), { recursive: true });
cp.execSync(["press", "-f", path.join("Build", "Release", "ArrayControllerTest"), path.join("Build", "Deployment", "ArrayControllerTest")].join(" "), { stdio: "inherit" });
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
fs.mkdirSync(path.join("Build", "Desktop", "ArrayControllerTest"), { recursive: true });
require("@objj/nativehost").buildNativeHost(path.join("Build", "Release", "ArrayControllerTest"), path.join("Build", "Desktop", "ArrayControllerTest", "ArrayControllerTest.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
cp.execSync([path.join("Build", "Desktop", "ArrayControllerTest", "ArrayControllerTest.app", "Contents", "MacOS", "NativeHost"), "-i"].join(" "), { stdio: "inherit" });
});
function printResults(configuration)
{
console.log("----------------------------");
console.log(configuration+" app built at path: "+path.join("Build", configuration, "ArrayControllerTest"));
console.log("----------------------------");
}
File diff suppressed because one or more lines are too long
@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="25098" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment version="1050" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="25098"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="450" id="451"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" animationBehavior="default" id="371">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="335" y="445" width="480" height="305"/>
<rect key="screenRect" x="0.0" y="0.0" width="1920" height="1050"/>
<view key="contentView" id="372">
<rect key="frame" x="0.0" y="0.0" width="480" height="305"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<scrollView fixedFrame="YES" autohidesScrollers="YES" horizontalLineScroll="19" horizontalPageScroll="10" verticalLineScroll="19" verticalPageScroll="10" usesPredominantAxisScrolling="NO" translatesAutoresizingMaskIntoConstraints="NO" id="460">
<rect key="frame" x="15" y="150" width="445" height="135"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<clipView key="contentView" id="oWr-jb-zqy">
<rect key="frame" x="1" y="1" width="443" height="133"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<tableView verticalHuggingPriority="750" allowsExpansionToolTips="YES" columnAutoresizingStyle="firstColumnOnly" columnSelection="YES" autosaveColumns="NO" headerView="464" id="463">
<rect key="frame" x="0.0" y="0.0" width="443" height="105"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<size key="intercellSpacing" width="3" height="2"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="calibratedWhite"/>
<color key="gridColor" name="gridColor" catalog="System" colorSpace="catalog"/>
</tableView>
</subviews>
</clipView>
<scroller key="horizontalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" horizontal="YES" id="462">
<rect key="frame" x="1" y="119" width="223" height="15"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<scroller key="verticalScroller" hidden="YES" wantsLayer="YES" verticalHuggingPriority="750" doubleValue="1" horizontal="NO" id="461">
<rect key="frame" x="224" y="17" width="15" height="102"/>
<autoresizingMask key="autoresizingMask"/>
</scroller>
<tableHeaderView key="headerView" wantsLayer="YES" id="464">
<rect key="frame" x="0.0" y="0.0" width="443" height="28"/>
<autoresizingMask key="autoresizingMask"/>
</tableHeaderView>
</scrollView>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="469">
<rect key="frame" x="9" y="110" width="40" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" bezelStyle="rounded" image="NSAddTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="470">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="add:" target="511" id="513"/>
</connections>
</button>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="471">
<rect key="frame" x="49" y="110" width="41" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" bezelStyle="rounded" image="NSRemoveTemplate" imagePosition="only" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="472">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="remove:" target="511" id="514"/>
</connections>
</button>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="484">
<rect key="frame" x="12" y="77" width="155" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Selected Name:" id="485">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="488">
<rect key="frame" x="66" y="47" width="101" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Selected Price:" id="489">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="492">
<rect key="frame" x="17" y="20" width="150" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" alignment="right" title="Sum of Selected Prices:" id="493">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="494">
<rect key="frame" x="174" y="20" width="292" height="17"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" sendsActionOnEndEditing="YES" title="Label" id="495">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="499">
<rect key="frame" x="174" y="75" width="286" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="500">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField focusRingType="none" verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="501">
<rect key="frame" x="174" y="45" width="286" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" drawsBackground="YES" id="502">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" fixedFrame="YES" translatesAutoresizingMaskIntoConstraints="NO" id="509">
<rect key="frame" x="90" y="110" width="76" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Insert" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="510">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="insert:" target="511" id="515"/>
</connections>
</button>
</subviews>
</view>
<point key="canvasLocation" x="140" y="142"/>
</window>
<customObject id="450" customClass="AppController">
<connections>
<outlet property="arrayController" destination="511" id="512"/>
<outlet property="selectedNameField" destination="499" id="507"/>
<outlet property="selectedPriceField" destination="501" id="508"/>
<outlet property="tableView" destination="463" id="483"/>
<outlet property="theWindow" destination="371" id="459"/>
<outlet property="totalCountField" destination="494" id="498"/>
</connections>
</customObject>
<arrayController id="511"/>
</objects>
<resources>
<image name="NSAddTemplate" width="18" height="16"/>
<image name="NSRemoveTemplate" width="18" height="4"/>
</resources>
</document>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

-93
View File
@@ -1,93 +0,0 @@
/*
* Jakefile
* AttachedSheet2
*
* Created by Saikat Chakrabarti on March 16, 2010.
* Copyright 2010, gomockingbird.com 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 ("AttachedSheet2", function(task)
{
task.setBuildIntermediatesPath(FILE.join("Build", "AttachedSheet2.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setProductName("AttachedSheet2");
task.setIdentifier("com.gomockingbird.AttachedSheet2");
task.setVersion("1.0");
task.setAuthor("Saikat Chakrabarti and Sheena Pakanati");
task.setEmail("contact @nospam@ gomockingbird.com");
task.setSummary("AttachedSheet2");
task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**")));
task.setResources(new FileList("Resources/*"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
else
task.setCompilerFlags("-O");
});
function printResults(configuration)
{
print("----------------------------")
print(configuration+" app built at path: "+FILE.join("Build", configuration, "AttachedSheet2"));
print("----------------------------")
}
task ("default", ["AttachedSheet2"], 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", "AttachedSheet2", "index.html")]);
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", "AttachedSheet2", "index.html")]);
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", "AttachedSheet2"));
OS.system(["press", "-f", FILE.join("Build", "Release", "AttachedSheet2"), FILE.join("Build", "Deployment", "AttachedSheet2")]);
printResults("Deployment")
});
task ("press", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Press", "AttachedSheet2"));
OS.system(["press", "-f", FILE.join("Build", "Release", "AttachedSheet2"), FILE.join("Build", "Press", "AttachedSheet2")]);
});
task ("flatten", ["press"], function()
{
FILE.mkdirs(FILE.join("Build", "Flatten", "AttachedSheet2"));
OS.system(["flatten", "-f", "--verbose", "--split", "3", "-c", "closure-compiler", FILE.join("Build", "Press", "AttachedSheet2"), FILE.join("Build", "Flatten", "AttachedSheet2")]);
});
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large Load Diff
@@ -1,304 +0,0 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 46;
objects = {
/* Begin PBXBuildFile section */
B210EF7E153B0A8D005D15EE /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B210EF7D153B0A8D005D15EE /* Cocoa.framework */; };
B210EF88153B0A8D005D15EE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = B210EF86153B0A8D005D15EE /* InfoPlist.strings */; };
B210EF8A153B0A8D005D15EE /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = B210EF89153B0A8D005D15EE /* main.m */; };
B210EF8E153B0A8D005D15EE /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = B210EF8C153B0A8D005D15EE /* Credits.rtf */; };
B210EF91153B0A8D005D15EE /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = B210EF90153B0A8D005D15EE /* AppDelegate.m */; };
B2129637153B0B3000E15669 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = B2129636153B0B3000E15669 /* MainMenu.xib */; };
B212963B153B0B4900E15669 /* Window.xib in Resources */ = {isa = PBXBuildFile; fileRef = B2129639153B0B4900E15669 /* Window.xib */; };
B212963C153B0E6A00E15669 /* SheetWindow.m in Sources */ = {isa = PBXBuildFile; fileRef = B2129630153B0ADF00E15669 /* SheetWindow.m */; };
B212963D153B0E6C00E15669 /* SheetWindowController.m in Sources */ = {isa = PBXBuildFile; fileRef = B2129632153B0ADF00E15669 /* SheetWindowController.m */; };
/* End PBXBuildFile section */
/* Begin PBXFileReference section */
B210EF79153B0A8D005D15EE /* TestSheet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TestSheet.app; sourceTree = BUILT_PRODUCTS_DIR; };
B210EF7D153B0A8D005D15EE /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = System/Library/Frameworks/Cocoa.framework; sourceTree = SDKROOT; };
B210EF80153B0A8D005D15EE /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = System/Library/Frameworks/AppKit.framework; sourceTree = SDKROOT; };
B210EF81153B0A8D005D15EE /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
B210EF82153B0A8D005D15EE /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
B210EF85153B0A8D005D15EE /* TestSheet-Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "TestSheet-Info.plist"; sourceTree = "<group>"; };
B210EF87153B0A8D005D15EE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
B210EF89153B0A8D005D15EE /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
B210EF8B153B0A8D005D15EE /* TestSheet-Prefix.pch */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "TestSheet-Prefix.pch"; sourceTree = "<group>"; };
B210EF8D153B0A8D005D15EE /* en */ = {isa = PBXFileReference; lastKnownFileType = text.rtf; name = en; path = en.lproj/Credits.rtf; sourceTree = "<group>"; };
B210EF8F153B0A8D005D15EE /* AppDelegate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
B210EF90153B0A8D005D15EE /* AppDelegate.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = "<group>"; };
B212962F153B0ADF00E15669 /* SheetWindow.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SheetWindow.h; sourceTree = "<group>"; };
B2129630153B0ADF00E15669 /* SheetWindow.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SheetWindow.m; sourceTree = "<group>"; };
B2129631153B0ADF00E15669 /* SheetWindowController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SheetWindowController.h; sourceTree = "<group>"; };
B2129632153B0ADF00E15669 /* SheetWindowController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = SheetWindowController.m; sourceTree = "<group>"; };
B2129636153B0B3000E15669 /* MainMenu.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = MainMenu.xib; path = ../Resources/MainMenu.xib; sourceTree = "<group>"; };
B2129639153B0B4900E15669 /* Window.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Window.xib; path = ../Resources/Window.xib; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
B210EF76153B0A8D005D15EE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B210EF7E153B0A8D005D15EE /* Cocoa.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
B210EF6E153B0A8D005D15EE = {
isa = PBXGroup;
children = (
B2129639153B0B4900E15669 /* Window.xib */,
B2129636153B0B3000E15669 /* MainMenu.xib */,
B210EF83153B0A8D005D15EE /* TestSheet */,
B210EF7C153B0A8D005D15EE /* Frameworks */,
B210EF7A153B0A8D005D15EE /* Products */,
);
sourceTree = "<group>";
};
B210EF7A153B0A8D005D15EE /* Products */ = {
isa = PBXGroup;
children = (
B210EF79153B0A8D005D15EE /* TestSheet.app */,
);
name = Products;
sourceTree = "<group>";
};
B210EF7C153B0A8D005D15EE /* Frameworks */ = {
isa = PBXGroup;
children = (
B210EF7D153B0A8D005D15EE /* Cocoa.framework */,
B210EF7F153B0A8D005D15EE /* Other Frameworks */,
);
name = Frameworks;
sourceTree = "<group>";
};
B210EF7F153B0A8D005D15EE /* Other Frameworks */ = {
isa = PBXGroup;
children = (
B210EF80153B0A8D005D15EE /* AppKit.framework */,
B210EF81153B0A8D005D15EE /* CoreData.framework */,
B210EF82153B0A8D005D15EE /* Foundation.framework */,
);
name = "Other Frameworks";
sourceTree = "<group>";
};
B210EF83153B0A8D005D15EE /* TestSheet */ = {
isa = PBXGroup;
children = (
B210EF8F153B0A8D005D15EE /* AppDelegate.h */,
B210EF90153B0A8D005D15EE /* AppDelegate.m */,
B212962F153B0ADF00E15669 /* SheetWindow.h */,
B2129630153B0ADF00E15669 /* SheetWindow.m */,
B2129631153B0ADF00E15669 /* SheetWindowController.h */,
B2129632153B0ADF00E15669 /* SheetWindowController.m */,
B210EF84153B0A8D005D15EE /* Supporting Files */,
);
path = TestSheet;
sourceTree = "<group>";
};
B210EF84153B0A8D005D15EE /* Supporting Files */ = {
isa = PBXGroup;
children = (
B210EF85153B0A8D005D15EE /* TestSheet-Info.plist */,
B210EF86153B0A8D005D15EE /* InfoPlist.strings */,
B210EF89153B0A8D005D15EE /* main.m */,
B210EF8B153B0A8D005D15EE /* TestSheet-Prefix.pch */,
B210EF8C153B0A8D005D15EE /* Credits.rtf */,
);
name = "Supporting Files";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
B210EF78153B0A8D005D15EE /* TestSheet */ = {
isa = PBXNativeTarget;
buildConfigurationList = B210EF97153B0A8D005D15EE /* Build configuration list for PBXNativeTarget "TestSheet" */;
buildPhases = (
B210EF75153B0A8D005D15EE /* Sources */,
B210EF76153B0A8D005D15EE /* Frameworks */,
B210EF77153B0A8D005D15EE /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = TestSheet;
productName = TestSheet;
productReference = B210EF79153B0A8D005D15EE /* TestSheet.app */;
productType = "com.apple.product-type.application";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
B210EF70153B0A8D005D15EE /* Project object */ = {
isa = PBXProject;
attributes = {
LastUpgradeCheck = 0430;
};
buildConfigurationList = B210EF73153B0A8D005D15EE /* Build configuration list for PBXProject "TestSheet" */;
compatibilityVersion = "Xcode 3.2";
developmentRegion = English;
hasScannedForEncodings = 0;
knownRegions = (
en,
);
mainGroup = B210EF6E153B0A8D005D15EE;
productRefGroup = B210EF7A153B0A8D005D15EE /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
B210EF78153B0A8D005D15EE /* TestSheet */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
B210EF77153B0A8D005D15EE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B210EF88153B0A8D005D15EE /* InfoPlist.strings in Resources */,
B2129637153B0B3000E15669 /* MainMenu.xib in Resources */,
B212963B153B0B4900E15669 /* Window.xib in Resources */,
B210EF8E153B0A8D005D15EE /* Credits.rtf in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
B210EF75153B0A8D005D15EE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B210EF8A153B0A8D005D15EE /* main.m in Sources */,
B210EF91153B0A8D005D15EE /* AppDelegate.m in Sources */,
B212963D153B0E6C00E15669 /* SheetWindowController.m in Sources */,
B212963C153B0E6A00E15669 /* SheetWindow.m in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXVariantGroup section */
B210EF86153B0A8D005D15EE /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
B210EF87153B0A8D005D15EE /* en */,
);
name = InfoPlist.strings;
sourceTree = "<group>";
};
B210EF8C153B0A8D005D15EE /* Credits.rtf */ = {
isa = PBXVariantGroup;
children = (
B210EF8D153B0A8D005D15EE /* en */,
);
name = Credits.rtf;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
B210EF95153B0A8D005D15EE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = NO;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_SYMBOLS_PRIVATE_EXTERN = NO;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
};
name = Debug;
};
B210EF96153B0A8D005D15EE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ARCHS = "$(ARCHS_STANDARD_64_BIT)";
CLANG_ENABLE_OBJC_ARC = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_ENABLE_OBJC_EXCEPTIONS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 10.7;
SDKROOT = macosx;
};
name = Release;
};
B210EF98153B0A8D005D15EE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "TestSheet/TestSheet-Prefix.pch";
INFOPLIST_FILE = "TestSheet/TestSheet-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Debug;
};
B210EF99153B0A8D005D15EE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
GCC_PRECOMPILE_PREFIX_HEADER = YES;
GCC_PREFIX_HEADER = "TestSheet/TestSheet-Prefix.pch";
INFOPLIST_FILE = "TestSheet/TestSheet-Info.plist";
PRODUCT_NAME = "$(TARGET_NAME)";
WRAPPER_EXTENSION = app;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
B210EF73153B0A8D005D15EE /* Build configuration list for PBXProject "TestSheet" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B210EF95153B0A8D005D15EE /* Debug */,
B210EF96153B0A8D005D15EE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B210EF97153B0A8D005D15EE /* Build configuration list for PBXNativeTarget "TestSheet" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B210EF98153B0A8D005D15EE /* Debug */,
B210EF99153B0A8D005D15EE /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = B210EF70153B0A8D005D15EE /* Project object */;
}
@@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:TestSheet.xcodeproj">
</FileRef>
</Workspace>
+93
View File
@@ -0,0 +1,93 @@
/*
* Jakefile
* AttachedSheet2Test
*
* Created by Saikat Chakrabarti on March 16, 2010.
* Copyright 2010, gomockingbird.com All rights reserved.
*/
var ENV = process.env,
cp = require("child_process"),
fs = require("fs"),
path = require("path"),
task = JAKE.task,
FileList = JAKE.FileList,
app = CAPPUCCINO.Jake.applicationtask.app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug";
app ("AttachedSheet2Test", function(task)
{
task.setBuildIntermediatesPath(path.join("Build", "AttachedSheet2Test.build", configuration));
task.setBuildPath(path.join("Build", configuration));
task.setProductName("AttachedSheet2Test");
task.setIdentifier("com.gomockingbird.AttachedSheet2Test");
task.setVersion("1.0");
task.setAuthor("Saikat Chakrabarti and Sheena Pakanati");
task.setEmail("contact @nospam@ gomockingbird.com");
task.setSummary("AttachedSheet2Test");
task.setSources((new FileList("**/*.j")).exclude(path.join("Build", "**")));
task.setResources(new FileList("Resources/*"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
else
task.setCompilerFlags("-O");
});
function printResults(configuration)
{
console.log("----------------------------")
console.log(configuration+" app built at path: "+path.join("Build", configuration, "AttachedSheet2Test"));
console.log("----------------------------")
}
task ("default", ["AttachedSheet2Test"], 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()
{
cp.execSync(["open", path.join("Build", "Debug", "AttachedSheet2Test", "index.html")].join(" "), { stdio: "inherit" });
});
task ("run-release", ["release"], function()
{
cp.execSync(["open", path.join("Build", "Release", "AttachedSheet2Test", "index.html")].join(" "), { stdio: "inherit" });
});
task ("deploy", ["release"], function()
{
fs.mkdirSync(path.join("Build", "Deployment", "AttachedSheet2Test"), { recursive: true });
cp.execSync(["press", "-f", path.join("Build", "Release", "AttachedSheet2Test"), path.join("Build", "Deployment", "AttachedSheet2Test")].join(" "), { stdio: "inherit" });
printResults("Deployment")
});
task ("press", ["release"], function()
{
fs.mkdirSync(path.join("Build", "Press", "AttachedSheet2Test"), { recursive: true });
cp.execSync(["press", "-f", path.join("Build", "Release", "AttachedSheet2Test"), path.join("Build", "Press", "AttachedSheet2Test")].join(" "), { stdio: "inherit" });
});
task ("flatten", ["press"], function()
{
fs.mkdirSync(path.join("Build", "Flatten", "AttachedSheet2Test"), { recursive: true });
cp.execSync(["flatten", "-f", "--verbose", "--split", "3", "-c", "closure-compiler", path.join("Build", "Press", "AttachedSheet2Test"), path.join("Build", "Flatten", "AttachedSheet2Test")].join(" "), { stdio: "inherit" });
});
File diff suppressed because one or more lines are too long
@@ -0,0 +1,489 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="25098" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment version="1050" identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="25098"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
<connections>
<outlet property="delegate" destination="450" id="451"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application" customClass="NSObject"/>
<menu title="AMainMenu" systemMenu="main" id="29" userLabel="MainMenu">
<items>
<menuItem title="NewApplication" id="56" userLabel="AttachedSheet2">
<menu key="submenu" title="NewApplication" systemMenu="apple" id="57">
<items>
<menuItem title="About AttachedSheet2" id="58">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="orderFrontStandardAboutPanel:" target="-2" id="142"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="236">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Preferences…" keyEquivalent="," id="129" userLabel="121"/>
<menuItem isSeparatorItem="YES" id="143">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Services" id="131">
<menu key="submenu" title="Services" systemMenu="services" id="130"/>
</menuItem>
<menuItem isSeparatorItem="YES" id="144">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Hide AttachedSheet2" keyEquivalent="h" id="134">
<connections>
<action selector="hide:" target="-1" id="367"/>
</connections>
</menuItem>
<menuItem title="Hide Others" keyEquivalent="h" id="145">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="hideOtherApplications:" target="-1" id="368"/>
</connections>
</menuItem>
<menuItem title="Show All" id="150">
<connections>
<action selector="unhideAllApplications:" target="-1" id="370"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="149">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Quit AttachedSheet2" keyEquivalent="q" id="136" userLabel="1111">
<connections>
<action selector="terminate:" target="-3" id="449"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="File" id="83">
<menu key="submenu" title="File" id="81">
<items>
<menuItem title="New Window" keyEquivalent="n" id="82" userLabel="9">
<connections>
<action selector="newDocument:" target="-1" id="498"/>
</connections>
</menuItem>
<menuItem title="New Modal Window" keyEquivalent="N" id="488" userLabel="9">
<connections>
<action selector="newModalWindow:" target="-1" id="494"/>
</connections>
</menuItem>
<menuItem title="New Sheet" keyEquivalent="s" id="490" userLabel="9">
<connections>
<action selector="newSheet:" target="-1" id="495"/>
</connections>
</menuItem>
<menuItem title="New Modal Sheet" keyEquivalent="S" id="492" userLabel="9">
<connections>
<action selector="newModalSheet:" target="-1" id="497"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="79" userLabel="7">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Close" keyEquivalent="w" id="73" userLabel="1">
<connections>
<action selector="performClose:" target="-1" id="193"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="74" userLabel="2">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Page Setup..." keyEquivalent="P" id="77" userLabel="5">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="runPageLayout:" target="-1" id="87"/>
</connections>
</menuItem>
<menuItem title="Print…" keyEquivalent="p" id="78" userLabel="6">
<connections>
<action selector="print:" target="-1" id="86"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Edit" id="217">
<menu key="submenu" title="Edit" id="205">
<items>
<menuItem title="Undo" keyEquivalent="z" id="207">
<connections>
<action selector="undo:" target="-1" id="223"/>
</connections>
</menuItem>
<menuItem title="Redo" keyEquivalent="Z" id="215">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="redo:" target="-1" id="231"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="206">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Cut" keyEquivalent="x" id="199">
<connections>
<action selector="cut:" target="-1" id="228"/>
</connections>
</menuItem>
<menuItem title="Copy" keyEquivalent="c" id="197">
<connections>
<action selector="copy:" target="-1" id="224"/>
</connections>
</menuItem>
<menuItem title="Paste" keyEquivalent="v" id="203">
<connections>
<action selector="paste:" target="-1" id="226"/>
</connections>
</menuItem>
<menuItem title="Delete" id="202">
<connections>
<action selector="delete:" target="-1" id="235"/>
</connections>
</menuItem>
<menuItem title="Select All" keyEquivalent="a" id="198">
<connections>
<action selector="selectAll:" target="-1" id="232"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="214">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Find" id="218">
<menu key="submenu" title="Find" id="220">
<items>
<menuItem title="Find…" tag="1" keyEquivalent="f" id="209">
<connections>
<action selector="performFindPanelAction:" target="-1" id="241"/>
</connections>
</menuItem>
<menuItem title="Find Next" tag="2" keyEquivalent="g" id="208"/>
<menuItem title="Find Previous" tag="3" keyEquivalent="G" id="213">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
</menuItem>
<menuItem title="Use Selection for Find" tag="7" keyEquivalent="e" id="221"/>
<menuItem title="Jump to Selection" keyEquivalent="j" id="210">
<connections>
<action selector="centerSelectionInVisibleArea:" target="-1" id="245"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Spelling and Grammar" id="216">
<menu key="submenu" title="Spelling and Grammar" id="200">
<items>
<menuItem title="Show Spelling…" keyEquivalent=":" id="204">
<connections>
<action selector="showGuessPanel:" target="-1" id="230"/>
</connections>
</menuItem>
<menuItem title="Check Spelling" keyEquivalent=";" id="201">
<connections>
<action selector="checkSpelling:" target="-1" id="225"/>
</connections>
</menuItem>
<menuItem title="Check Spelling While Typing" id="219">
<connections>
<action selector="toggleContinuousSpellChecking:" target="-1" id="222"/>
</connections>
</menuItem>
<menuItem title="Check Grammar With Spelling" id="346">
<connections>
<action selector="toggleGrammarChecking:" target="-1" id="347"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Substitutions" id="348">
<menu key="submenu" title="Substitutions" id="349">
<items>
<menuItem title="Smart Copy/Paste" tag="1" keyEquivalent="f" id="350">
<connections>
<action selector="toggleSmartInsertDelete:" target="-1" id="355"/>
</connections>
</menuItem>
<menuItem title="Smart Quotes" tag="2" keyEquivalent="g" id="351">
<connections>
<action selector="toggleAutomaticQuoteSubstitution:" target="-1" id="356"/>
</connections>
</menuItem>
<menuItem title="Smart Links" tag="3" keyEquivalent="G" id="354">
<modifierMask key="keyEquivalentModifierMask" shift="YES" command="YES"/>
<connections>
<action selector="toggleAutomaticLinkDetection:" target="-1" id="357"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Speech" id="211">
<menu key="submenu" title="Speech" id="212">
<items>
<menuItem title="Start Speaking" id="196">
<connections>
<action selector="startSpeaking:" target="-1" id="233"/>
</connections>
</menuItem>
<menuItem title="Stop Speaking" id="195">
<connections>
<action selector="stopSpeaking:" target="-1" id="227"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Format" id="375">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Format" id="376">
<items>
<menuItem title="Font" id="377">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Font" systemMenu="font" id="388">
<items>
<menuItem title="Show Fonts" keyEquivalent="t" id="389"/>
<menuItem title="Bold" tag="2" keyEquivalent="b" id="390"/>
<menuItem title="Italic" tag="1" keyEquivalent="i" id="391"/>
<menuItem title="Underline" keyEquivalent="u" id="392">
<connections>
<action selector="underline:" target="-1" id="432"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="393"/>
<menuItem title="Bigger" tag="3" keyEquivalent="+" id="394"/>
<menuItem title="Smaller" tag="4" keyEquivalent="-" id="395"/>
<menuItem isSeparatorItem="YES" id="396"/>
<menuItem title="Kern" id="397">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Kern" id="415">
<items>
<menuItem title="Use Default" id="416">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardKerning:" target="-1" id="438"/>
</connections>
</menuItem>
<menuItem title="Use None" id="417">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffKerning:" target="-1" id="441"/>
</connections>
</menuItem>
<menuItem title="Tighten" id="418">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="tightenKerning:" target="-1" id="431"/>
</connections>
</menuItem>
<menuItem title="Loosen" id="419">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="loosenKerning:" target="-1" id="435"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Ligature" id="398">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Ligature" id="411">
<items>
<menuItem title="Use Default" id="412">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useStandardLigatures:" target="-1" id="439"/>
</connections>
</menuItem>
<menuItem title="Use None" id="413">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="turnOffLigatures:" target="-1" id="440"/>
</connections>
</menuItem>
<menuItem title="Use All" id="414">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="useAllLigatures:" target="-1" id="434"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Baseline" id="399">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Baseline" id="405">
<items>
<menuItem title="Use Default" id="406">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="unscript:" target="-1" id="437"/>
</connections>
</menuItem>
<menuItem title="Superscript" id="407">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="superscript:" target="-1" id="430"/>
</connections>
</menuItem>
<menuItem title="Subscript" id="408">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="subscript:" target="-1" id="429"/>
</connections>
</menuItem>
<menuItem title="Raise" id="409">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="raiseBaseline:" target="-1" id="426"/>
</connections>
</menuItem>
<menuItem title="Lower" id="410">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="lowerBaseline:" target="-1" id="427"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem isSeparatorItem="YES" id="400"/>
<menuItem title="Show Colors" keyEquivalent="C" id="401">
<connections>
<action selector="orderFrontColorPanel:" target="-1" id="433"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="402"/>
<menuItem title="Copy Style" keyEquivalent="c" id="403">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="copyFont:" target="-1" id="428"/>
</connections>
</menuItem>
<menuItem title="Paste Style" keyEquivalent="v" id="404">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="pasteFont:" target="-1" id="436"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Text" id="378">
<modifierMask key="keyEquivalentModifierMask"/>
<menu key="submenu" title="Text" id="379">
<items>
<menuItem title="Align Left" keyEquivalent="{" id="380">
<connections>
<action selector="alignLeft:" target="-1" id="442"/>
</connections>
</menuItem>
<menuItem title="Center" keyEquivalent="|" id="381">
<connections>
<action selector="alignCenter:" target="-1" id="445"/>
</connections>
</menuItem>
<menuItem title="Justify" id="382">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="alignJustified:" target="-1" id="443"/>
</connections>
</menuItem>
<menuItem title="Align Right" keyEquivalent="}" id="383">
<connections>
<action selector="alignRight:" target="-1" id="447"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="384"/>
<menuItem title="Show Ruler" id="385">
<modifierMask key="keyEquivalentModifierMask"/>
<connections>
<action selector="toggleRuler:" target="-1" id="446"/>
</connections>
</menuItem>
<menuItem title="Copy Ruler" keyEquivalent="c" id="386">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="copyRuler:" target="-1" id="444"/>
</connections>
</menuItem>
<menuItem title="Paste Ruler" keyEquivalent="v" id="387">
<modifierMask key="keyEquivalentModifierMask" control="YES" command="YES"/>
<connections>
<action selector="pasteRuler:" target="-1" id="448"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="View" id="295">
<menu key="submenu" title="View" id="296">
<items>
<menuItem title="Show Toolbar" keyEquivalent="t" id="297">
<modifierMask key="keyEquivalentModifierMask" option="YES" command="YES"/>
<connections>
<action selector="toggleToolbarShown:" target="-1" id="366"/>
</connections>
</menuItem>
<menuItem title="Customize Toolbar…" id="298">
<connections>
<action selector="runToolbarCustomizationPalette:" target="-1" id="365"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Window" id="19">
<menu key="submenu" title="Window" systemMenu="window" id="24">
<items>
<menuItem title="Minimize" keyEquivalent="m" id="23">
<connections>
<action selector="performMiniaturize:" target="-1" id="37"/>
</connections>
</menuItem>
<menuItem title="Zoom" id="239">
<connections>
<action selector="performZoom:" target="-1" id="240"/>
</connections>
</menuItem>
<menuItem isSeparatorItem="YES" id="92">
<modifierMask key="keyEquivalentModifierMask" command="YES"/>
</menuItem>
<menuItem title="Bring All to Front" id="5">
<connections>
<action selector="arrangeInFront:" target="-1" id="39"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
<menuItem title="Help" id="103" userLabel="1">
<menu key="submenu" title="Help" id="106" userLabel="2">
<items>
<menuItem title="NewApplication Help" keyEquivalent="?" id="111">
<connections>
<action selector="showHelp:" target="-1" id="360"/>
</connections>
</menuItem>
</items>
</menu>
</menuItem>
</items>
<point key="canvasLocation" x="140" y="293"/>
</menu>
<customObject id="450" customClass="AppController"/>
</objects>
</document>
File diff suppressed because one or more lines are too long
@@ -0,0 +1,290 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="24506" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="24506"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="SheetWindowController" propertyAccessControl="none">
<connections>
<outlet property="_altCloseButton" destination="159" id="164"/>
<outlet property="_closableMaskButton" destination="186" id="195"/>
<outlet property="_closeButton" destination="19" id="163"/>
<outlet property="_miniaturizableMaskButton" destination="190" id="196"/>
<outlet property="_orderOutAfterCheckbox" destination="166" id="168"/>
<outlet property="_otherCloseButton" destination="161" id="165"/>
<outlet property="_resizableMaskButton" destination="192" id="197"/>
<outlet property="_shadeContentView" destination="201" id="206"/>
<outlet property="_shadeParentWindow" destination="220" id="223"/>
<outlet property="_shadeWindowView" destination="199" id="205"/>
<outlet property="_textField" destination="9" id="246"/>
<outlet property="_titledMaskButton" destination="184" id="194"/>
<outlet property="_tokenField" destination="224" id="243"/>
<outlet property="_windowTypeMatrix" destination="169" id="180"/>
<outlet property="window" destination="3" id="132"/>
</connections>
</customObject>
<customObject id="-1" userLabel="First Responder" customClass="FirstResponder"/>
<customObject id="-3" userLabel="Application"/>
<window title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" animationBehavior="default" id="3">
<windowStyleMask key="styleMask" titled="YES" closable="YES" miniaturizable="YES" resizable="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="71" y="767" width="468" height="458"/>
<rect key="screenRect" x="0.0" y="0.0" width="1440" height="878"/>
<view key="contentView" id="4">
<rect key="frame" x="0.0" y="0.0" width="468" height="458"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<slider verticalHuggingPriority="750" id="6">
<rect key="frame" x="59" y="56" width="96" height="21"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<sliderCell key="cell" continuous="YES" state="on" alignment="left" maxValue="100" doubleValue="50" tickMarkPosition="above" sliderType="linear" id="31">
<font key="font" size="12" name="Helvetica"/>
</sliderCell>
<connections>
<action selector="takeDoubleValueFrom:" target="9" id="33"/>
</connections>
</slider>
<textField verticalHuggingPriority="750" id="9">
<rect key="frame" x="166" y="55" width="96" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" scrollable="YES" lineBreakMode="clipping" selectable="YES" editable="YES" sendsActionOnEndEditing="YES" state="on" borderStyle="bezel" placeholderString="CPTextField" drawsBackground="YES" id="30">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<button verticalHuggingPriority="750" id="19">
<rect key="frame" x="381" y="13" width="80" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="push" title="Close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="20">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
<string key="keyEquivalent" base64-UTF8="YES">
DQ
</string>
</buttonCell>
</button>
<button verticalHuggingPriority="750" id="17">
<rect key="frame" x="14" y="378" width="150" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Modal Window" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="22">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="newModalWindow:" target="-2" id="137"/>
</connections>
</button>
<button verticalHuggingPriority="750" id="16">
<rect key="frame" x="14" y="346" width="150" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Sheet" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="23">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="newSheet:" target="-2" id="138"/>
</connections>
</button>
<button verticalHuggingPriority="750" id="14">
<rect key="frame" x="14" y="410" width="150" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Window" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="25">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="newWindow:" target="-2" id="139"/>
</connections>
</button>
<button verticalHuggingPriority="750" id="11">
<rect key="frame" x="14" y="314" width="150" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Modal Sheet" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="28">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="newModalSheet:" target="-2" id="136"/>
</connections>
</button>
<button verticalHuggingPriority="750" id="145">
<rect key="frame" x="14" y="282" width="150" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Alert Sheet" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="146">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="newAlertSheet:" target="-2" id="198"/>
</connections>
</button>
<button verticalHuggingPriority="750" id="159">
<rect key="frame" x="234" y="13" width="147" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMinX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="push" title="Close Parent Too" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="160">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button verticalHuggingPriority="750" id="161">
<rect key="frame" x="14" y="13" width="122" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<buttonCell key="cell" type="push" title="Chain Sheets" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="162">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button id="166">
<rect key="frame" x="18" y="190" width="329" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="orderOut: after endSheet:" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="167">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button id="199">
<rect key="frame" x="18" y="134" width="329" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Shade Window View of Non-CIB Windows" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="200">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button id="201">
<rect key="frame" x="18" y="114" width="329" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Shade Content View of Non-CIB Window" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="202">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button id="220">
<rect key="frame" x="18" y="94" width="329" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="Shade Parent Window on windowWillBeginSheet:" bezelStyle="regularSquare" imagePosition="left" alignment="left" inset="2" id="221">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button id="184">
<rect key="frame" x="193" y="281" width="174" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="CPTitledWindowMask" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="185">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button id="186">
<rect key="frame" x="193" y="261" width="174" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="CPClosableWindowMask" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="187">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<matrix verticalHuggingPriority="750" allowsEmptySelection="NO" id="169">
<rect key="frame" x="193" y="302" width="261" height="136"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
<size key="cellSize" width="261" height="21"/>
<size key="intercellSpacing" width="4" height="2"/>
<buttonCell key="prototype" type="radio" title="Radio" imagePosition="left" alignment="left" inset="2" id="170">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<cells>
<column>
<buttonCell type="radio" title="CIB Window" imagePosition="left" alignment="left" state="on" tag="1" inset="2" id="171">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<buttonCell type="radio" title="CPHUDBackgroundWindowMask" imagePosition="left" alignment="left" tag="2" inset="2" id="172">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<buttonCell type="radio" title="CPBorderlessWindowMask" imagePosition="left" alignment="left" tag="3" inset="2" id="173">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<buttonCell type="radio" title="CPTexturedBackgroundWindowMask" imagePosition="left" alignment="left" tag="4" inset="2" id="179">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<buttonCell type="radio" title="CPDocModalWindowMask" imagePosition="left" alignment="left" tag="5" inset="2" id="181">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<buttonCell type="radio" title="CIB HUD Window" imagePosition="left" alignment="left" tag="-1" inset="2" id="227">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</column>
</cells>
</matrix>
<button id="190">
<rect key="frame" x="192" y="241" width="210" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="CPMiniaturizableWindowMask" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="191">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<button id="192">
<rect key="frame" x="192" y="221" width="180" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="CPResizableWindowMask" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="193">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
<textField verticalHuggingPriority="750" horizontalCompressionResistancePriority="250" id="203">
<rect key="frame" x="30" y="158" width="368" height="26"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<textFieldCell key="cell" sendsActionOnEndEditing="YES" id="204">
<font key="font" metaFont="system" size="10"/>
<string key="title">It is not specified if endSheet: or orderOut: on sheet should be called first. However, cannot call orderOut: after endSheet: when chaining.</string>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="controlColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<tokenField verticalHuggingPriority="750" id="224">
<rect key="frame" x="275" y="55" width="96" height="22"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<tokenFieldCell key="cell" selectable="YES" editable="YES" borderStyle="bezel" alignment="left" placeholderString="CPTokenField" drawsBackground="YES" allowsEditingTextAttributes="YES" id="225">
<font key="font" metaFont="system"/>
<color key="textColor" name="controlTextColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</tokenFieldCell>
<connections>
<outlet property="delegate" destination="-2" id="226"/>
</connections>
</tokenField>
<button verticalHuggingPriority="750" id="229">
<rect key="frame" x="14" y="249" width="150" height="32"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="push" title="Popover" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="230">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="newPopover:" target="-2" id="232"/>
</connections>
</button>
</subviews>
</view>
<connections>
<outlet property="initialFirstResponder" destination="224" id="245"/>
</connections>
</window>
<button id="188">
<rect key="frame" x="0.0" y="0.0" width="174" height="18"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMinY="YES"/>
<buttonCell key="cell" type="check" title="CPTitledWindowMask" bezelStyle="regularSquare" imagePosition="left" alignment="left" state="on" inset="2" id="189">
<behavior key="behavior" changeContents="YES" doesNotDimImage="YES" lightByContents="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
</button>
</objects>
</document>

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -14,6 +14,6 @@
function main(args, namedArgs)
{
CPLogRegister(CPLogDefault);
CPLogRegister(CPLogDefault);
CPApplicationMain(args, namedArgs);
}

Before

Width:  |  Height:  |  Size: 1.7 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Before

Width:  |  Height:  |  Size: 455 B

After

Width:  |  Height:  |  Size: 455 B

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

@@ -6,19 +6,19 @@
* Copyright 2011, Victory-Heart Productions All rights reserved.
*/
var ENV = require("system").env,
FILE = require("file"),
JAKE = require("jake"),
var ENV = process.env,
cp = require("child_process"),
fs = require("fs"),
path = require("path"),
task = JAKE.task,
FileList = JAKE.FileList,
app = require("cappuccino/jake").app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug",
OS = require("os");
app = CAPPUCCINO.Jake.applicationtask.app,
configuration = ENV["CONFIG"] || ENV["CONFIGURATION"] || ENV["c"] || "Debug";
app ("BindingContentArrayTest", function(task)
{
task.setBuildIntermediatesPath(FILE.join("Build", "BindingContentArrayTest.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setBuildIntermediatesPath(path.join("Build", "BindingContentArrayTest.build", configuration));
task.setBuildPath(path.join("Build", configuration));
task.setProductName("BindingContentArrayTest");
task.setIdentifier("com.aparajita.BindingContentArrayTest");
@@ -26,7 +26,7 @@ app ("BindingContentArrayTest", function(task)
task.setAuthor("Victory-Heart Productions");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("BindingContentArrayTest");
task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**")));
task.setSources((new FileList("**/*.j")).exclude(path.join("Build", "**")));
task.setResources(new FileList("Resources/**"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
@@ -58,36 +58,36 @@ task ("release", function()
task ("run", ["debug"], function()
{
OS.system(["open", FILE.join("Build", "Debug", "BindingContentArrayTest", "index.html")]);
cp.execSync(["open", path.join("Build", "Debug", "BindingContentArrayTest", "index.html")].join(" "), { stdio: "inherit" });
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", "BindingContentArrayTest", "index.html")]);
cp.execSync(["open", path.join("Build", "Release", "BindingContentArrayTest", "index.html")].join(" "), { stdio: "inherit" });
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", "BindingContentArrayTest"));
OS.system(["press", "-f", FILE.join("Build", "Release", "BindingContentArrayTest"), FILE.join("Build", "Deployment", "BindingContentArrayTest")]);
fs.mkdirSync(path.join("Build", "Deployment", "BindingContentArrayTest"), { recursive: true });
cp.execSync(["press", "-f", path.join("Build", "Release", "BindingContentArrayTest"), path.join("Build", "Deployment", "BindingContentArrayTest")].join(" "), { stdio: "inherit" });
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Desktop", "BindingContentArrayTest"));
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "BindingContentArrayTest"), FILE.join("Build", "Desktop", "BindingContentArrayTest", "BindingContentArrayTest.app"));
fs.mkdirSync(path.join("Build", "Desktop", "BindingContentArrayTest"), { recursive: true });
require("@objj/nativehost").buildNativeHost(path.join("Build", "Release", "BindingContentArrayTest"), path.join("Build", "Desktop", "BindingContentArrayTest", "BindingContentArrayTest.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
OS.system([FILE.join("Build", "Desktop", "BindingContentArrayTest", "BindingContentArrayTest.app", "Contents", "MacOS", "NativeHost"), "-i"]);
cp.execSync([path.join("Build", "Desktop", "BindingContentArrayTest", "BindingContentArrayTest.app", "Contents", "MacOS", "NativeHost"), "-i"].join(" "), { stdio: "inherit" });
});
function printResults(configuration)
{
print("----------------------------");
print(configuration+" app built at path: "+FILE.join("Build", configuration, "BindingContentArrayTest"));
print("----------------------------");
console.log("----------------------------");
console.log(configuration+" app built at path: "+path.join("Build", configuration, "BindingContentArrayTest"));
console.log("----------------------------");
}

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

-93
View File
@@ -1,93 +0,0 @@
/*
* Jakefile
* cgcanvascontext
*
* Created by You on February 19, 2013.
* Copyright 2013, 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 ("cgcanvascontext", function(task)
{
task.setBuildIntermediatesPath(FILE.join("Build", "cgcanvascontext.build", configuration));
task.setBuildPath(FILE.join("Build", configuration));
task.setProductName("cgcanvascontext");
task.setIdentifier("com.yourcompany.cgcanvascontext");
task.setVersion("1.0");
task.setAuthor("Your Company");
task.setEmail("feedback @nospam@ yourcompany.com");
task.setSummary("cgcanvascontext");
task.setSources((new FileList("**/*.j")).exclude(FILE.join("Build", "**")));
task.setResources(new FileList("Resources/**"));
task.setIndexFilePath("index.html");
task.setInfoPlistPath("Info.plist");
if (configuration === "Debug")
task.setCompilerFlags("-DDEBUG -g");
else
task.setCompilerFlags("-O");
});
task ("default", ["cgcanvascontext"], 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", "cgcanvascontext", "index.html")]);
});
task ("run-release", ["release"], function()
{
OS.system(["open", FILE.join("Build", "Release", "cgcanvascontext", "index.html")]);
});
task ("deploy", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Deployment", "cgcanvascontext"));
OS.system(["press", "-f", FILE.join("Build", "Release", "cgcanvascontext"), FILE.join("Build", "Deployment", "cgcanvascontext")]);
printResults("Deployment")
});
task ("desktop", ["release"], function()
{
FILE.mkdirs(FILE.join("Build", "Desktop", "cgcanvascontext"));
require("cappuccino/nativehost").buildNativeHost(FILE.join("Build", "Release", "cgcanvascontext"), FILE.join("Build", "Desktop", "cgcanvascontext", "cgcanvascontext.app"));
printResults("Desktop")
});
task ("run-desktop", ["desktop"], function()
{
OS.system([FILE.join("Build", "Desktop", "cgcanvascontext", "cgcanvascontext.app", "Contents", "MacOS", "NativeHost"), "-i"]);
});
function printResults(configuration)
{
print("----------------------------");
print(configuration+" app built at path: "+FILE.join("Build", configuration, "cgcanvascontext"));
print("----------------------------");
}

Some files were not shown because too many files have changed in this diff Show More