@maccesar/titools 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS-TEMPLATE.md +173 -0
- package/README.md +867 -0
- package/agents/ti-researcher.md +108 -0
- package/bin/titools.js +53 -0
- package/lib/commands/agents.js +126 -0
- package/lib/commands/install.js +188 -0
- package/lib/commands/uninstall.js +215 -0
- package/lib/commands/update.js +159 -0
- package/lib/config.js +119 -0
- package/lib/downloader.js +153 -0
- package/lib/installer.js +253 -0
- package/lib/platform.js +108 -0
- package/lib/symlink.js +142 -0
- package/lib/utils.js +270 -0
- package/package.json +67 -0
- package/skills/alloy-expert/SKILL.md +247 -0
- package/skills/alloy-expert/assets/ControllerAutoCleanup.js +182 -0
- package/skills/alloy-expert/references/alloy-structure.md +381 -0
- package/skills/alloy-expert/references/anti-patterns.md +133 -0
- package/skills/alloy-expert/references/code-conventions.md +469 -0
- package/skills/alloy-expert/references/contracts.md +280 -0
- package/skills/alloy-expert/references/controller-patterns.md +520 -0
- package/skills/alloy-expert/references/error-handling.md +484 -0
- package/skills/alloy-expert/references/examples.md +735 -0
- package/skills/alloy-expert/references/migration-patterns.md +298 -0
- package/skills/alloy-expert/references/patterns.md +448 -0
- package/skills/alloy-expert/references/performance-patterns.md +855 -0
- package/skills/alloy-expert/references/security-patterns.md +847 -0
- package/skills/alloy-expert/references/state-management.md +779 -0
- package/skills/alloy-expert/references/testing.md +872 -0
- package/skills/alloy-guides/SKILL.md +214 -0
- package/skills/alloy-guides/references/CLI_TASKS.md +243 -0
- package/skills/alloy-guides/references/CONCEPTS.md +191 -0
- package/skills/alloy-guides/references/CONTROLLERS.md +298 -0
- package/skills/alloy-guides/references/MODELS.md +1028 -0
- package/skills/alloy-guides/references/PURGETSS.md +56 -0
- package/skills/alloy-guides/references/VIEWS_DYNAMIC.md +242 -0
- package/skills/alloy-guides/references/VIEWS_STYLES.md +388 -0
- package/skills/alloy-guides/references/VIEWS_WITHOUT_CONTROLLERS.md +109 -0
- package/skills/alloy-guides/references/VIEWS_XML.md +558 -0
- package/skills/alloy-guides/references/WIDGETS.md +176 -0
- package/skills/alloy-howtos/SKILL.md +203 -0
- package/skills/alloy-howtos/references/best_practices.md +138 -0
- package/skills/alloy-howtos/references/cli_reference.md +253 -0
- package/skills/alloy-howtos/references/config_files.md +87 -0
- package/skills/alloy-howtos/references/custom_tags.md +147 -0
- package/skills/alloy-howtos/references/debugging_troubleshooting.md +101 -0
- package/skills/alloy-howtos/references/samples.md +167 -0
- package/skills/purgetss/SKILL.md +442 -0
- package/skills/purgetss/assets/purgetss.config.cjs +17 -0
- package/skills/purgetss/references/EXAMPLES.md +247 -0
- package/skills/purgetss/references/animation-system.md +1294 -0
- package/skills/purgetss/references/apply-directive.md +375 -0
- package/skills/purgetss/references/arbitrary-values.md +612 -0
- package/skills/purgetss/references/class-index.md +1350 -0
- package/skills/purgetss/references/cli-commands.md +948 -0
- package/skills/purgetss/references/configurable-properties.md +654 -0
- package/skills/purgetss/references/custom-rules.md +161 -0
- package/skills/purgetss/references/customization-deep-dive.md +722 -0
- package/skills/purgetss/references/dynamic-component-creation.md +489 -0
- package/skills/purgetss/references/grid-layout.md +455 -0
- package/skills/purgetss/references/icon-fonts.md +609 -0
- package/skills/purgetss/references/installation-setup.md +366 -0
- package/skills/purgetss/references/opacity-modifier.md +291 -0
- package/skills/purgetss/references/platform-modifiers.md +479 -0
- package/skills/purgetss/references/smart-mappings.md +42 -0
- package/skills/purgetss/references/titanium-resets.md +359 -0
- package/skills/purgetss/references/ui-ux-design.md +1526 -0
- package/skills/ti-guides/SKILL.md +94 -0
- package/skills/ti-guides/references/advanced-data-and-images.md +19 -0
- package/skills/ti-guides/references/alloy-cli-advanced.md +84 -0
- package/skills/ti-guides/references/alloy-data-mastery.md +29 -0
- package/skills/ti-guides/references/alloy-widgets-and-themes.md +19 -0
- package/skills/ti-guides/references/android-manifest.md +97 -0
- package/skills/ti-guides/references/app-distribution.md +258 -0
- package/skills/ti-guides/references/application-frameworks.md +377 -0
- package/skills/ti-guides/references/cli-reference.md +402 -0
- package/skills/ti-guides/references/coding-best-practices.md +102 -0
- package/skills/ti-guides/references/commonjs-advanced.md +134 -0
- package/skills/ti-guides/references/hello-world.md +100 -0
- package/skills/ti-guides/references/hyperloop-native-access.md +62 -0
- package/skills/ti-guides/references/javascript-primer.md +411 -0
- package/skills/ti-guides/references/reserved-words.md +36 -0
- package/skills/ti-guides/references/resources.md +183 -0
- package/skills/ti-guides/references/style-and-conventions.md +48 -0
- package/skills/ti-guides/references/tiapp-config.md +609 -0
- package/skills/ti-howtos/SKILL.md +174 -0
- package/skills/ti-howtos/references/android-platform-deep-dives.md +658 -0
- package/skills/ti-howtos/references/automation-fastlane-appium.md +95 -0
- package/skills/ti-howtos/references/buffer-codec-streams.md +140 -0
- package/skills/ti-howtos/references/cross-platform-development.md +348 -0
- package/skills/ti-howtos/references/debugging-profiling.md +543 -0
- package/skills/ti-howtos/references/extending-titanium.md +723 -0
- package/skills/ti-howtos/references/google-maps-v2.md +169 -0
- package/skills/ti-howtos/references/ios-map-kit.md +143 -0
- package/skills/ti-howtos/references/ios-platform-deep-dives.md +783 -0
- package/skills/ti-howtos/references/local-data-sources.md +301 -0
- package/skills/ti-howtos/references/location-and-maps.md +252 -0
- package/skills/ti-howtos/references/media-apis.md +210 -0
- package/skills/ti-howtos/references/notification-services.md +599 -0
- package/skills/ti-howtos/references/remote-data-sources.md +349 -0
- package/skills/ti-howtos/references/tutorials.md +502 -0
- package/skills/ti-howtos/references/using-modules.md +237 -0
- package/skills/ti-howtos/references/web-content-integration.md +307 -0
- package/skills/ti-howtos/references/webpack-build-pipeline.md +78 -0
- package/skills/ti-ui/SKILL.md +179 -0
- package/skills/ti-ui/references/accessibility-deep-dive.md +242 -0
- package/skills/ti-ui/references/animation-and-matrices.md +599 -0
- package/skills/ti-ui/references/application-structures.md +655 -0
- package/skills/ti-ui/references/custom-fonts-styling.md +579 -0
- package/skills/ti-ui/references/event-handling.md +393 -0
- package/skills/ti-ui/references/gestures.md +473 -0
- package/skills/ti-ui/references/icons-and-splash-screens.md +409 -0
- package/skills/ti-ui/references/layouts-and-positioning.md +462 -0
- package/skills/ti-ui/references/listviews-and-performance.md +619 -0
- package/skills/ti-ui/references/orientation.md +362 -0
- package/skills/ti-ui/references/platform-ui-android.md +635 -0
- package/skills/ti-ui/references/platform-ui-ios.md +469 -0
- package/skills/ti-ui/references/scrolling-views.md +252 -0
- package/skills/ti-ui/references/tableviews.md +568 -0
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: alloy-expert
|
|
3
|
+
description: "Titanium Alloy architecture and implementation expert. Use when designing, reviewing, analyzing, or examining Alloy project structure, creating controllers/views/services, choosing models vs collections, implementing communication patterns, handling memory cleanup, testing, auditing code, or migrating legacy apps. Detects Alloy vs Classic projects automatically."
|
|
4
|
+
argument-hint: "[architecture-topic]"
|
|
5
|
+
allowed-tools: Read, Grep, Glob, Edit, Write, Bash(git *), Bash(node *)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Titanium Alloy Expert
|
|
9
|
+
|
|
10
|
+
Complete architectural and implementation guidance for building scalable, maintainable Titanium Alloy applications with PurgeTSS styling.
|
|
11
|
+
|
|
12
|
+
## Project Detection
|
|
13
|
+
|
|
14
|
+
:::info AUTO-DETECTS ALLOY VS CLASSIC PROJECTS
|
|
15
|
+
This skill automatically detects project type when invoked and provides appropriate guidance.
|
|
16
|
+
|
|
17
|
+
**Detection occurs automatically** - no manual command needed.
|
|
18
|
+
|
|
19
|
+
**Alloy project indicators:**
|
|
20
|
+
- `app/` folder (MVC structure)
|
|
21
|
+
- `app/views/`, `app/controllers/` folders
|
|
22
|
+
- `alloy.jmk` or `config.json` files
|
|
23
|
+
|
|
24
|
+
**Classic project indicators:**
|
|
25
|
+
- `Resources/` folder with `app.js` at root
|
|
26
|
+
- No `app/` folder structure
|
|
27
|
+
|
|
28
|
+
**Behavior based on detection:**
|
|
29
|
+
- **Alloy detected** → Provides Alloy-specific architecture patterns, MVC folder structure, Backbone.js patterns
|
|
30
|
+
- **Classic detected** → Indicates incompatibility, does not suggest Alloy-specific patterns, recommends migration or Classic resources
|
|
31
|
+
- **Unknown** → Asks user to clarify project type
|
|
32
|
+
:::
|
|
33
|
+
|
|
34
|
+
## Table of Contents
|
|
35
|
+
|
|
36
|
+
- [Titanium Alloy Expert](#titanium-alloy-expert)
|
|
37
|
+
- [Project Detection](#project-detection)
|
|
38
|
+
- [Table of Contents](#table-of-contents)
|
|
39
|
+
- [Workflow](#workflow)
|
|
40
|
+
- [Quick Start Example](#quick-start-example)
|
|
41
|
+
- [Code Standards (Low Freedom)](#code-standards-low-freedom)
|
|
42
|
+
- [PurgeTSS Rules (Low Freedom)](#purgetss-rules-low-freedom)
|
|
43
|
+
- [Quick Decision Matrix](#quick-decision-matrix)
|
|
44
|
+
- [Reference Guides (Progressive Disclosure)](#reference-guides-progressive-disclosure)
|
|
45
|
+
- [Architecture](#architecture)
|
|
46
|
+
- [Implementation](#implementation)
|
|
47
|
+
- [Quality \& Reliability](#quality--reliability)
|
|
48
|
+
- [Performance \& Security](#performance--security)
|
|
49
|
+
- [Migration](#migration)
|
|
50
|
+
- [Specialized Titanium Skills](#specialized-titanium-skills)
|
|
51
|
+
- [Guiding Principles](#guiding-principles)
|
|
52
|
+
- [Response Format](#response-format)
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Workflow
|
|
57
|
+
|
|
58
|
+
1. **Architecture**: Define structure (`lib/api`, `lib/services`, `lib/helpers`)
|
|
59
|
+
2. **Data Strategy**: Choose Models (SQLite) or Collections (API)
|
|
60
|
+
3. **Contracts**: Define I/O specs for cross-layer communication
|
|
61
|
+
4. **Implementation**: Write XML views + ES6+ controllers
|
|
62
|
+
5. **Quality**: Testing, error handling, logging, performance
|
|
63
|
+
6. **Cleanup**: Implement `cleanup()` pattern for memory management
|
|
64
|
+
|
|
65
|
+
## Quick Start Example
|
|
66
|
+
|
|
67
|
+
Minimal example following all conventions:
|
|
68
|
+
|
|
69
|
+
**View (views/user/card.xml)**
|
|
70
|
+
```xml
|
|
71
|
+
<Alloy>
|
|
72
|
+
<View class="m-2 rounded-xl bg-white shadow-md">
|
|
73
|
+
<View class="horizontal m-3 w-screen">
|
|
74
|
+
<Label class="fa-solid fa-user text-2xl text-blue-500" />
|
|
75
|
+
<Label id="name" class="ml-3 text-lg font-bold" />
|
|
76
|
+
</View>
|
|
77
|
+
<Button class="mx-3 mb-3 h-10 w-screen rounded-md bg-blue-600 text-white"
|
|
78
|
+
title="L('view_profile')"
|
|
79
|
+
onClick="onViewProfile"
|
|
80
|
+
/>
|
|
81
|
+
</View>
|
|
82
|
+
</Alloy>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**Controller (controllers/user/card.js)**
|
|
86
|
+
```javascript
|
|
87
|
+
const { Navigation } = require('lib/services/navigation')
|
|
88
|
+
|
|
89
|
+
function init() {
|
|
90
|
+
const user = $.args.user
|
|
91
|
+
$.name.text = user.name
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function onViewProfile() {
|
|
95
|
+
Navigation.open('user/profile', { userId: $.args.user.id })
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function cleanup() {
|
|
99
|
+
$.destroy()
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
$.cleanup = cleanup
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
**Service (lib/services/navigation.js)**
|
|
106
|
+
```javascript
|
|
107
|
+
exports.Navigation = {
|
|
108
|
+
open(route, params = {}) {
|
|
109
|
+
const controller = Alloy.createController(route, params)
|
|
110
|
+
const view = controller.getView()
|
|
111
|
+
|
|
112
|
+
view.addEventListener('close', function() {
|
|
113
|
+
if (controller.cleanup) controller.cleanup()
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
view.open()
|
|
117
|
+
return controller
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
## Code Standards (Low Freedom)
|
|
123
|
+
|
|
124
|
+
- **NO SEMICOLONS**: Let ASI handle it
|
|
125
|
+
- **MODERN SYNTAX**: `const/let`, destructuring, template literals
|
|
126
|
+
- **applyProperties()**: Batch UI updates to minimize bridge crossings
|
|
127
|
+
- **MEMORY CLEANUP**: Every controller with global listeners MUST have `$.cleanup = cleanup`
|
|
128
|
+
- **ERROR HANDLING**: Use AppError classes, log with context, never swallow errors
|
|
129
|
+
- **TESTABLE**: Inject dependencies, avoid hard coupling
|
|
130
|
+
|
|
131
|
+
## PurgeTSS Rules (Low Freedom)
|
|
132
|
+
|
|
133
|
+
:::danger CRITICAL: Platform-Specific Properties Require Modifiers
|
|
134
|
+
Using `Ti.UI.iOS.*` or `Ti.UI.Android.*` properties WITHOUT platform modifiers causes cross-platform compilation failures.
|
|
135
|
+
|
|
136
|
+
**Example of the damage:**
|
|
137
|
+
```tss
|
|
138
|
+
// ❌ WRONG - Adds Ti.UI.iOS to Android project
|
|
139
|
+
"#mainWindow": {
|
|
140
|
+
statusBarStyle: Ti.UI.iOS.StatusBar.LIGHT_CONTENT // FAILS on Android!
|
|
141
|
+
}
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
**CORRECT - Always use platform modifiers:**
|
|
145
|
+
```tss
|
|
146
|
+
// ✅ CORRECT - Only adds to iOS
|
|
147
|
+
"#mainWindow[platform=ios]": {
|
|
148
|
+
statusBarStyle: Ti.UI.iOS.StatusBar.LIGHT_CONTENT
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
**Properties that ALWAYS require platform modifiers:**
|
|
153
|
+
- iOS: `statusBarStyle`, `modalStyle`, `modalTransitionStyle`, any `Ti.UI.iOS.*`
|
|
154
|
+
- Android: `actionBar` configuration, any `Ti.UI.Android.*` constant
|
|
155
|
+
|
|
156
|
+
**Available modifiers:** `[platform=ios]`, `[platform=android]`, `[formFactor=handheld]`, `[formFactor=tablet]`, `[if=Alloy.Globals.customVar]`
|
|
157
|
+
|
|
158
|
+
**For more platform-specific patterns, see** [Platform Modifiers (purgetss)](skills/purgetss/references/platform-modifiers.md) or [Platform UI guides (ti-ui)](skills/ti-ui/references/platform-ui-ios.md).
|
|
159
|
+
:::
|
|
160
|
+
|
|
161
|
+
| WRONG | CORRECT | Why |
|
|
162
|
+
| ------------------------------ | ------------------- | ----------------------------- |
|
|
163
|
+
| `flex-row` | `horizontal` | Flexbox not supported |
|
|
164
|
+
| `flex-col` | `vertical` | Flexbox not supported |
|
|
165
|
+
| `p-4` on View | `m-4` on children | No padding on containers |
|
|
166
|
+
| `justify-*` | margins/positioning | Flexbox not supported |
|
|
167
|
+
| `items-center` (for centering) | layout + sizing | Different meaning in Titanium |
|
|
168
|
+
| `rounded-full` (for circle) | `rounded-full-12` | Need size suffix (12×4=48px) |
|
|
169
|
+
| `border-[1px]` | `border-(1)` | Parentheses, not brackets |
|
|
170
|
+
|
|
171
|
+
**Note on `w-full` vs `w-screen`:**
|
|
172
|
+
- `w-full` → `width: '100%'` (100% of parent container) - exists and valid
|
|
173
|
+
- `w-screen` → `width: Ti.UI.FILL` (fills all available space) - use for full-width elements
|
|
174
|
+
|
|
175
|
+
## Quick Decision Matrix
|
|
176
|
+
|
|
177
|
+
| Question | Answer |
|
|
178
|
+
| ---------------------------------- | -------------------------------------------------------------- |
|
|
179
|
+
| How to create a new Alloy project? | **`ti create -t app --alloy`** (not `--classic` + `alloy new`) |
|
|
180
|
+
| Where does API call go? | `lib/api/` |
|
|
181
|
+
| Where does business logic go? | `lib/services/` |
|
|
182
|
+
| Where do I store auth tokens? | Keychain (iOS) / KeyStore (Android) via service |
|
|
183
|
+
| Models or Collections? | Collections for API data, Models for SQLite persistence |
|
|
184
|
+
| Ti.App.fireEvent or EventBus? | **Always EventBus** (Backbone.Events) |
|
|
185
|
+
| Direct navigation or service? | **Always Navigation service** (auto cleanup) |
|
|
186
|
+
| Manual TSS or PurgeTSS? | **Always PurgeTSS utility classes** |
|
|
187
|
+
| Controller 100+ lines? | Extract logic to services |
|
|
188
|
+
|
|
189
|
+
## Reference Guides (Progressive Disclosure)
|
|
190
|
+
|
|
191
|
+
### Architecture
|
|
192
|
+
- **[Structure & Organization](references/alloy-structure.md)**: Models vs Collections, folder maps, styling strategies, automatic cleanup
|
|
193
|
+
- **[ControllerAutoCleanup.js](assets/ControllerAutoCleanup.js)**: Drop-in utility for automatic controller cleanup (prevents memory leaks)
|
|
194
|
+
- **[Architectural Patterns](references/patterns.md)**: Repository, Service Layer, Event Bus, Factory, Singleton
|
|
195
|
+
- **[Contracts & Communication](references/contracts.md)**: Layer interaction examples and JSDoc specs
|
|
196
|
+
- **[Anti-Patterns](references/anti-patterns.md)**: Fat controllers, flexbox classes, memory leaks, direct API calls
|
|
197
|
+
|
|
198
|
+
### Implementation
|
|
199
|
+
- **[Code Conventions](references/code-conventions.md)**: ES6 features, PurgeTSS usage, accessibility
|
|
200
|
+
- **[Controller Patterns](references/controller-patterns.md)**: Templates, animation, dynamic styling
|
|
201
|
+
- **[Examples](references/examples.md)**: API clients, SQL models, full screen examples
|
|
202
|
+
|
|
203
|
+
### Quality & Reliability
|
|
204
|
+
- **[Testing](references/testing.md)**: Unit testing, mocking patterns, controller testing, test helpers
|
|
205
|
+
- **[Error Handling & Logging](references/error-handling.md)**: AppError classes, Logger service, validation
|
|
206
|
+
|
|
207
|
+
### Performance & Security
|
|
208
|
+
- **[Performance Patterns](references/performance-patterns.md)**: ListView, bridge optimization, memory management, lazy loading
|
|
209
|
+
- **[Security Patterns](references/security-patterns.md)**: Token storage, certificate pinning, encryption, OWASP
|
|
210
|
+
- **[State Management](references/state-management.md)**: Centralized store, reactive patterns, synchronization
|
|
211
|
+
|
|
212
|
+
### Migration
|
|
213
|
+
- **[Migration Patterns](references/migration-patterns.md)**: Step-by-step guide for modernizing legacy apps
|
|
214
|
+
|
|
215
|
+
## Specialized Titanium Skills
|
|
216
|
+
|
|
217
|
+
For specific feature implementations, defer to these specialized skills:
|
|
218
|
+
|
|
219
|
+
| Task | Use This Skill |
|
|
220
|
+
| ----------------------------------------------------- | -------------- |
|
|
221
|
+
| PurgeTSS setup, advanced styling, animations | `purgetss` |
|
|
222
|
+
| Location, Maps, Push Notifications, Media APIs | `ti-howtos` |
|
|
223
|
+
| UI layouts, ListViews, gestures, platform-specific UI | `ti-ui` |
|
|
224
|
+
| Alloy CLI, configuration files, debugging | `alloy-howtos` |
|
|
225
|
+
| Alloy MVC complete reference | `alloy-guides` |
|
|
226
|
+
| Hyperloop, native modules, app distribution | `ti-guides` |
|
|
227
|
+
|
|
228
|
+
## Guiding Principles
|
|
229
|
+
|
|
230
|
+
1. **Thin Controllers**: Max 100 lines. Delegate to services.
|
|
231
|
+
2. **Single Source of Truth**: One state store, not scattered Properties.
|
|
232
|
+
3. **Always Cleanup**: Every listener added = listener removed in `cleanup()`.
|
|
233
|
+
4. **Never Block UI**: All API/DB calls are async with loading states.
|
|
234
|
+
5. **Fail Gracefully**: Centralized error handling with user-friendly messages.
|
|
235
|
+
|
|
236
|
+
## Response Format
|
|
237
|
+
|
|
238
|
+
**For Architecture Questions:**
|
|
239
|
+
1. Decision: What should be done
|
|
240
|
+
2. Justification: Technical rationale
|
|
241
|
+
3. Structure: File and folder placement
|
|
242
|
+
4. Contract: Clear I/O specification
|
|
243
|
+
|
|
244
|
+
**For Implementation Tasks:**
|
|
245
|
+
1. Code First: Show implementation immediately
|
|
246
|
+
2. Minimal Comments: Explain only the "Why" for complex logic
|
|
247
|
+
3. No Explanations: Deliver exactly what was asked concisely
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ControllerAutoCleanup - Automatic Controller Cleanup for Alloy
|
|
3
|
+
*
|
|
4
|
+
* Monkey-patches Alloy.createController to automatically cleanup
|
|
5
|
+
* controllers when their views are closed, preventing memory leaks.
|
|
6
|
+
*
|
|
7
|
+
* INSTALLATION:
|
|
8
|
+
* 1. Copy this file to your app: app/lib/ControllerAutoCleanup.js
|
|
9
|
+
* 2. Add as first line in alloy.js: require('ControllerAutoCleanup')
|
|
10
|
+
*
|
|
11
|
+
* No other code changes needed - cleanup happens automatically.
|
|
12
|
+
*
|
|
13
|
+
* @see https://github.com/jasonkneen/AlloyXL original inspiration
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
'use strict'
|
|
17
|
+
|
|
18
|
+
// Logger (optional - remove if not using)
|
|
19
|
+
let log = null
|
|
20
|
+
try {
|
|
21
|
+
log = require('Logger')?.create('CONTROLLER_CLEANUP')
|
|
22
|
+
} catch (e) {
|
|
23
|
+
// Fallback simple logger
|
|
24
|
+
log = {
|
|
25
|
+
debug: (msg, data) => Ti.API.debug(`[ControllerCleanup] ${msg}`, data),
|
|
26
|
+
info: (msg, data) => Ti.API.info(`[ControllerCleanup] ${msg}`, data),
|
|
27
|
+
warn: (msg, data) => Ti.API.warn(`[ControllerCleanup] ${msg}`, data),
|
|
28
|
+
error: (msg, data) => Ti.API.error(`[ControllerCleanup] ${msg}`, data)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Store original Alloy.createController
|
|
33
|
+
const originalCreateController = Alloy.createController
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Recursively cleanup a controller and all its child views
|
|
37
|
+
* @param {Object} controller - Alloy controller to cleanup
|
|
38
|
+
*/
|
|
39
|
+
function cleanupController(controller) {
|
|
40
|
+
if (!controller) return
|
|
41
|
+
|
|
42
|
+
// Cleanup child views first (recursive)
|
|
43
|
+
if (controller.__views) {
|
|
44
|
+
Object.values(controller.__views).forEach((childView) => {
|
|
45
|
+
if (childView && typeof childView === 'object') {
|
|
46
|
+
cleanupController(childView)
|
|
47
|
+
}
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Only cleanup actual Alloy controllers (not plain views)
|
|
52
|
+
if (controller.__iamalloy) {
|
|
53
|
+
// Remove all Backbone event listeners
|
|
54
|
+
if (typeof controller.off === 'function') {
|
|
55
|
+
controller.off()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Call Alloy's destroy method
|
|
59
|
+
if (typeof controller.destroy === 'function') {
|
|
60
|
+
controller.destroy()
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
log?.debug('Controller cleaned up', { id: controller.id })
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Clear reference
|
|
67
|
+
controller = null
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Enhanced Alloy.createController with automatic cleanup
|
|
72
|
+
* @param {string} name - Controller name/path
|
|
73
|
+
* @param {Object} args - Arguments to pass to controller
|
|
74
|
+
* @returns {Object} Alloy controller
|
|
75
|
+
*/
|
|
76
|
+
function createController(name, args = {}) {
|
|
77
|
+
try {
|
|
78
|
+
const controller = originalCreateController(name, args)
|
|
79
|
+
|
|
80
|
+
// Only process controllers with views
|
|
81
|
+
if (!controller.__views || Object.keys(controller.__views).length === 0) {
|
|
82
|
+
return controller
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const view = controller.getView()
|
|
86
|
+
const controllerName = name.split('/').pop() || name
|
|
87
|
+
const viewId = view?.id
|
|
88
|
+
|
|
89
|
+
// Track controllers globally (optional, for debugging)
|
|
90
|
+
Alloy.Controllers = Alloy.Controllers || {}
|
|
91
|
+
|
|
92
|
+
// Warn about controller name conflicts
|
|
93
|
+
if (Alloy.Controllers[controllerName] && !viewId) {
|
|
94
|
+
log?.warn('Controller name conflict - consider using unique IDs', {
|
|
95
|
+
controller: controllerName,
|
|
96
|
+
path: controller.__controllerPath
|
|
97
|
+
})
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Use view ID as key if available, otherwise controller name
|
|
101
|
+
const registryKey = viewId || controllerName
|
|
102
|
+
Alloy.Controllers[registryKey] = controller
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Add `once()` helper - listen to event one time only
|
|
106
|
+
* @param {string} eventName - Event name
|
|
107
|
+
* @param {Function} callback - Callback function
|
|
108
|
+
* @returns {Object} controller for chaining
|
|
109
|
+
*/
|
|
110
|
+
controller.once = function (eventName, callback) {
|
|
111
|
+
const wrapper = (...args) => {
|
|
112
|
+
controller.off(eventName, wrapper)
|
|
113
|
+
callback(...args)
|
|
114
|
+
}
|
|
115
|
+
controller.on(eventName, wrapper)
|
|
116
|
+
return controller
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Only add auto-cleanup to views with addEventListener
|
|
120
|
+
if (view && typeof view.addEventListener === 'function') {
|
|
121
|
+
// Window-like views with open() method
|
|
122
|
+
if (typeof view.open === 'function') {
|
|
123
|
+
view.addEventListener('open', function onOpen(e) {
|
|
124
|
+
view.removeEventListener('open', onOpen)
|
|
125
|
+
controller.trigger('open', e)
|
|
126
|
+
log?.debug('Controller opened', { name: controllerName })
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
view.addEventListener('close', function onClose() {
|
|
130
|
+
view.removeEventListener('close', onClose)
|
|
131
|
+
view = null
|
|
132
|
+
|
|
133
|
+
cleanupController(controller)
|
|
134
|
+
delete Alloy.Controllers[registryKey]
|
|
135
|
+
|
|
136
|
+
log?.debug('Controller cleaned up', { name: controllerName })
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
view.addEventListener('postlayout', function onPostLayout(e) {
|
|
140
|
+
view.removeEventListener('postlayout', onPostLayout)
|
|
141
|
+
controller.trigger('postlayout', e)
|
|
142
|
+
log?.debug('Controller layout finished', { name: controllerName })
|
|
143
|
+
})
|
|
144
|
+
} else {
|
|
145
|
+
// View-like views without open() (Widget, etc.)
|
|
146
|
+
view.addEventListener('postlayout', function onPostLayout(e) {
|
|
147
|
+
view.removeEventListener('postlayout', onPostLayout)
|
|
148
|
+
controller.trigger('postlayout', e)
|
|
149
|
+
log?.debug('View layout finished', { name: controllerName })
|
|
150
|
+
})
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return controller
|
|
155
|
+
|
|
156
|
+
} catch (error) {
|
|
157
|
+
log?.error('Error creating controller', {
|
|
158
|
+
name,
|
|
159
|
+
error: error.message,
|
|
160
|
+
stack: error.stack
|
|
161
|
+
})
|
|
162
|
+
throw error
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// Replace Alloy.createController with enhanced version
|
|
167
|
+
Alloy.createController = createController
|
|
168
|
+
|
|
169
|
+
log?.info('ControllerAutoCleanup loaded - automatic cleanup enabled')
|
|
170
|
+
|
|
171
|
+
// Export createController for direct use if needed
|
|
172
|
+
exports.createController = createController
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* USAGE:
|
|
176
|
+
*
|
|
177
|
+
* // alloy.js - Load as first line
|
|
178
|
+
* require('ControllerAutoCleanup')
|
|
179
|
+
*
|
|
180
|
+
* // All controllers now auto-cleanup on close
|
|
181
|
+
* Alloy.createController('profile').getView().open()
|
|
182
|
+
*/
|