squared 0.2.12 → 0.2.14

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.
data/README.md CHANGED
@@ -1,1351 +1,382 @@
1
- # squared 5.4
1
+ # squared 0.2
2
2
 
3
- ## Documentation
3
+ * [source](https://github.com/anpham6/squared-ruby)
4
+ * [manifest](https://github.com/anpham6/squared-repo)
5
+ * [docs](https://squared.readthedocs.io)
4
6
 
5
- ### HTML
7
+ ## Version Compatibility
6
8
 
7
- * [squared](https://squared.readthedocs.io)
8
- * [E-mc](https://e-mc.readthedocs.io)
9
+ | Date | squared | Min | Max |
10
+ | :--------: | ------: | -----: | -----: |
11
+ | 2024-12-07 | 0.1.0 | 2.4.0 | 3.3.6 |
12
+ | 2025-01-07 | 0.2.0 | 2.4.0 | 3.4.0 |
9
13
 
10
- > [!NOTE]
11
- > Content in the README was migrated into `Read the Docs`. The file is no longer fully maintained.
12
-
13
- ### README
14
-
15
- * [squared-express](https://github.com/anpham6/squared-express#readme)
16
- * [E-mc](https://github.com/anpham6/e-mc#readme)
17
- * [Pi-r](https://github.com/anpham6/pi-r#readme)
14
+ The range chart indicates the latest Ruby tested against at the time of release.
18
15
 
19
16
  ## Installation
20
17
 
21
- * NodeJS 16 LTS
22
-
23
- ### NPX
24
-
25
18
  ```sh
26
- > npm init
27
- > npm i sqd-cli sqd-serve
28
-
29
- > npx sqd init
30
- # OR
31
- > npx sqd init --public --local-serve # Same as squared-express
32
-
33
- > npx sqd serve --access-all
34
- # OR
35
- > node serve.cjs --access-all
19
+ gem install squared
36
20
  ```
37
21
 
38
- ```sh
39
- > npm init
40
- > npm i squared sqd-serve
22
+ ### Optional
41
23
 
42
- > mkdir dist html
43
- > cp -r ./node_modules/squared/dist/* ./dist
44
- > cp ./node_modules/squared/html/* ./html # optional
45
- > cp ./node_modules/sqd-serve/config/json/* . # yaml
46
-
47
- > npx serve
48
- ```
49
-
50
- ### GitHub
24
+ * [Repo](https://source.android.com/docs/setup/reference/repo)
25
+ * Python 3.6
26
+ * Not compatible with Windows
51
27
 
52
28
  ```sh
53
- > git clone https://github.com/anpham6/squared
54
- > cd squared
55
-
56
- > npm i
57
- > npm run prod
58
-
59
- > cd ..
60
-
61
- > git clone https://github.com/anpham6/squared-express
62
- > cd squared-express
63
-
64
- > npm i
65
- > npm run prod
66
- > npm run deploy # deploy:yaml
67
-
68
- > cd ../squared
69
-
70
- > node serve.cjs --access-all # squared.{json,yml}
29
+ mkdir -p ~/.bin
30
+ PATH="${HOME}/.bin:${PATH}"
31
+ curl https://storage.googleapis.com/git-repo-downloads/repo > ~/.bin/repo
32
+ chmod a+rx ~/.bin/repo
71
33
  ```
72
34
 
73
- ### Repo
74
-
75
- * Python 3.6+
76
- * [Commands](https://source.android.com/docs/setup/reference/repo)
35
+ ## Example - Rakefile
77
36
 
78
- #### Install
37
+ Projects from any accessible folder can be added relative to the parent directory (e.g. *REPO_ROOT*) or absolutely. The same Rakefile can also manage other similarly cloned `Repo` repositories remotely by setting the `REPO_ROOT` environment variable to the location. Missing projects will simply be excluded from the task runner.
79
38
 
80
- ```sh
81
- mkdir -p ~/bin/repo
82
- PATH="${HOME}/bin:${PATH}"
39
+ ```ruby
40
+ require "squared"
83
41
 
84
- curl https://storage.googleapis.com/git-repo-downloads/repo > ~/bin/repo
85
- chmod a+rx ~/bin/repo
42
+ require "squared/workspace"
43
+ require "squared/workspace/repo" # Optional
44
+ require "squared/workspace/project/node" #
45
+ require "squared/workspace/project/python" #
46
+ require "squared/workspace/project/ruby" #
86
47
  # OR
87
- scripts/repo-install.sh ~/bin
88
- ```
89
-
90
- #### Usage
91
-
92
- ```sh
93
- mkdir workspaces
94
- cd workspaces
95
-
96
- repo init -u https://github.com/anpham6/squared-repo -m latest.xml
97
- repo sync -j4
98
-
99
- cd squared
100
- npm i
101
- ```
102
-
103
- #### Ruby
104
-
105
- Workspace management uses [Ruby](https://www.ruby-lang.org/en/documentation/installation) for syncing and building. It is not installed by default on most operating systems.
106
-
107
- ```sh
108
- mkdir workspaces
109
- cd workspaces # REPO_ROOT
110
-
111
- wget https://unpkg.com/squared/Rakefile
48
+ require "squared/app" # All workspace related modules
112
49
 
113
- rake -T # List tasks
50
+ # NODE_ENV = production
114
51
 
115
- # REPO_BUILD={dev,prod}
116
- # PIPE_FAIL=1
117
- rake repo:init # nightly
118
- rake repo:init[latest] # squared
119
- rake repo:init[0.11.x] # e-mc
52
+ # REPO_ROOT = /workspaces #
53
+ # REPO_HOME = /workspaces/squared # Dir.pwd
54
+ # rake = /workspaces/squared/Rakefile # main?
120
55
  # OR
121
- REPO_ROOT=/tmp/123 NODE_INSTALL=pnpm repo:init
122
- # OR - without Repo
123
- rake clone:node # master
124
- rake clone # node + docs
125
- ```
126
-
127
- > [!TIP]
128
- > Use the supplied Rakefile inside the **squared** project folder once the source has been downloaded.
129
-
130
- #### Docker
131
-
132
- ```sh
133
- # NODE_TAG=latest
134
- # RUBY_VERSION=2.4.0-3.3.0
135
- # MANIFEST=nightly,staging,prod,android
136
- # BUILD={dev,prod}
137
- # DEV={0,1,local}
138
- # DOCS=any
139
- # PIPE_FAIL={0,1}
140
- docker build -t squared --build-arg MANIFEST=prod --build-arg SQUARED=prod .
141
- docker build -t node --build-arg NODE_TAG=20 --build-arg NODE_INSTALL=pnpm -f Dockerfile.slim . # no docs
142
- # OR
143
- # RUBY_TAG=latest
144
- # NODE_VERSION=22.x
145
- docker build -t ruby --build-arg RUBY_TAG=3.0 --build-arg NODE_VERSION=20.x --build-arg PIPE_FAIL=0 -f Dockerfile.ruby .
146
-
147
- # Express
148
- docker run -it --name express --rm -p 127.0.0.1:80:80 \
149
- --mount type=bind,src=${PWD},dst=/workspaces/squared/.config \
150
- --mount type=bind,src=${PWD}/html,dst=/workspaces/squared/www \
151
- squared
152
-
153
- # Terminal
154
- docker run -it --name debian squared /bin/bash # irb
155
- ```
156
-
157
- ### Browser
158
-
159
- * Download (squared@version): https://unpkg.com/squared
160
- * Global JS variable: **squared**
161
- * ES2018
162
-
163
- > - https://unpkg.com/squared/dist/squared.min.js
164
- > - https://unpkg.com/squared/dist/squared.base-dom.min.js
165
- > - https://unpkg.com/squared/dist/vdom.framework.min.js
166
-
167
- > * https://unpkg.com/squared/dist/squared.min.js
168
- > * https://unpkg.com/squared/dist/vdom-lite.framework.min.js
169
-
170
- ## Usage
171
-
172
- Library files are in the `/dist` folder. A minimum of **two** files are required to run *squared*.
173
-
174
- 1. squared
175
- 2. squared-base - *required: except vdom-lite*
176
- 3. **squared-svg** - *optional*
177
- 4. framework (e.g. **android** | **chrome** | vdom | vdom-lite)
178
- 5. extensions - *optional*
179
-
180
- Usable combinations: 1-2-4 + 1-2-4-5 + 1-2-3-4-5 + 1-vdom-lite
181
-
182
- File bundles for common combinations are available in the `/dist/bundles` folder and do not require a call to **setFramework**.
183
-
184
- > [!WARNING]
185
- > Libraries in bold are transpiled with **ES2020**.
186
-
187
- ### Example: android
188
-
189
- The primary function `parseDocument` can be called on multiple elements and multiple times per session. The application will continuously and progressively build the layout files into a single entity with combined shared resources.
190
-
191
- * ES2020
192
-
193
- > [!CAUTION]
194
- > Using `parseDocumentSync` is not recommended when your page has images or fonts.
195
-
196
- ```html
197
- <script src="/dist/squared.min.js"></script>
198
- <script src="/dist/squared.base.min.js"></script>
199
- <script src="/dist/squared.svg.min.js"></script>
200
- <script src="/dist/android.framework.min.js"></script>
201
- <script>
202
- squared.settings.targetAPI = 35; // Optional
203
-
204
- document.addEventListener("DOMContentLoaded", async () => {
205
- squared.setFramework(android, {/* settings */});
206
-
207
- await squared.parseDocument(): Node // document.body (default)
208
- // OR
209
- await squared.parseDocument(/* HTMLElement */, /* "fragment-id" */, /* ...etc */): Node[]
210
- // OR
211
- await squared.parseDocument(
212
- { // Custom settings do not affect other layouts
213
- element: document.body,
214
- projectId: "project-1", // Default is "_"
215
- resourceQualifier: "land", // "res/*" folder suffix
216
- /* OR */
217
- resourceQualifier: {
218
- suffix: "land", // Used for "true" or "undefined" groups (optional)
219
- layout: true, // Will copy to "res/layout-land" when "suffix" is defined
220
- string: undefined, // Will copy to default location "res/values" when "suffix" is undefined
221
- font: false, // Will not copy anything to "res/font" or "res/font-land"
222
- image: "hdpi", // Will copy to "res/drawable-hdpi"
223
- video: "w720dp", // Will copy to "res/raw-w720dp"
224
- audio: "w720dp", // Same as "video" and treated separately
225
- animation: "v34", // Will copy to "res/anim-v34"
226
- menu: "" // Will copy to default location "res/menu"
227
- /* integer + fraction + array + color + dimension + style + theme = Same as "string" */
228
- },
229
- enabledMultiline: false,
230
- enabledSubstitute: true,
231
- include: ["android.substitute"], // Automatically removed after finalize
232
- exclude: ["squared.list", "squared.grid"], // Disabled only during parseDocument
233
- excludeQuery: [{
234
- selector: "main > article", // Hide elements
235
- /* OR */
236
- resource: squared.base.lib.constant.NODE_RESOURCE.BOX_STYLE, // Exclusions during processing
237
- procedure: squared.base.lib.constant.NODE_PROCEDURE.OPTIMIZATION,
238
- section: squared.base.lib.constant.APP_SECTION.DOM_TRAVERSE
239
- }],
240
- customizationsBaseAPI: -1,
241
- observe(mutations, observer, settings) { // Uses MutationObserver
242
- squared.reset(); // Required when calling "parseDocument" after a File action
243
- squared.parseDocument(settings).then(() => {
244
- squared.copyTo("/path/project", { modified: true }).then(response => console.log(response));
245
- });
246
- },
247
- afterCascade(sessionId, node) {/* Restore previous state */},
248
- beforeRender(layout: LayoutUI) {/* Edit initial values */},
249
- afterFinalize(node: NodeUI) {/* Edit controller values */}
250
- },
251
- { // Only "element" is required
252
- element: "fragment-1",
253
- projectId: "project-1", // Implicit once projectId is not "_"
254
- resourceQualifier: "land",
255
- pathname: "app/src/main/res/layout-hdpi", // Will not be overridden by resourceQualifier "land"
256
- filename: "fragment.xml",
257
- baseLayoutAsFragment: {
258
- name: "androidx.navigation.fragment.NavHostFragment",
259
- documentId: "main_content",
260
- app: {
261
- navGraph: "@navigation/product_list_graph",
262
- defaultNavHost: "true"
263
- }
264
- },
265
- beforeCascade(sessionId) {
266
- document.getElementById("fragment-id").style.display = "block"; // Use inline styles
267
- }
268
- }
269
- );
270
- await squared.parseDocument({
271
- element: "fragment-2",
272
- projectId: "sqd2", // Explicit
273
- resourceQualifier: "port", // Will not conflict with projectId "project-1"
274
- enabledFragment: true,
275
- fragmentableElements: [
276
- {
277
- selector: "main", // querySelector
278
- name: "androidx.navigation.fragment.NavHostFragment",
279
- filename: "navigation.xml",
280
- documentId: "main_content"
281
- },
282
- "main > article" // Declarative double nested fragments are invalid (querySelectorAll)
283
- ],
284
- options: {
285
- "android.resource.fragment": {
286
- dynamicNestedFragments: true // FragmentContainerView or FrameLayout as the container (name and tag are ignored)
287
- }
288
- }
289
- });
290
- // OR - Chromium
291
- squared.prefetch("css").then(() => squared.parseDocument()); // Cross-origin support
292
- Promise.all(
293
- squared.prefetch("css", true), // All stylesheets
294
- squared.prefetch("css", "./undetected.css", element.shadowRoot),
295
- squared.prefetch("svg", "http://embedded.example.com/icon.svg", "../images/android.svg")
296
- )
297
- .then(() => squared.parseDocument());
298
-
299
- // Modify attributes
300
-
301
- const body = squared.findDocumentNode(document.body);
302
- body.android("layout_width", "match_parent");
303
- body.lockAttr("android", "layout_width");
304
-
305
- await squared.close(/* projectId */); // Next call to parseDocument will reset project (optional)
306
-
307
- squared.kill("30s").then(result => {/* killed when result > 0 */}); // Abort next request in 30 seconds
308
-
309
- // File actions - implicitly calls "close"
310
-
311
- await squared.save(/* "project-1" */, /* broadcastId | timeout */); // Uses defaults from settings
312
- // OR
313
- await squared.saveAs(/* archive filename */, { projectId: "project-1" });
314
- await squared.saveAs(/* archive filename */, { timeout: 10 }); // Kills request if not complete in 10 seconds
315
- await squared.saveAs(/* archive filename */, { throwErrors: true }).catch(err => console.log(err)); // Will cancel partial archive download
316
- // OR
317
- await squared.copyTo(/* directory */, {/* options */});
318
- await squared.copyTo(/* directory */, { modified: true }); // Can be used with observe
319
- // OR
320
- await squared.appendTo(/* archive location */, {/* options */});
321
-
322
- // Other features
323
-
324
- squared.observe();
325
- // OR
326
- await squared.observeSrc(
327
- "link[rel=stylesheet]", // HTMLElement
328
- (ev, element) => {
329
- squared.reset();
330
- squared.parseDocument().then(() => squared.copyTo("/path/project"));
331
- },
332
- { port: 8080, secure: false, action: "reload" /* "hot" */, expires: "1h" } // squared.json: "observe"
333
- );
334
-
335
- squared.reset(/* projectId */); // Start new "parseDocument" session (optional)
336
- });
337
- </script>
338
- ```
339
-
340
- > [!CAUTION]
341
- > Calling `saveAs` or `copyTo` methods before the images have completely loaded can cause them to be excluded from the generated layout. In these cases you should use the asynchronous `parseDocument` method to set a callback for your commands.
342
-
343
- ### Example: chrome
344
-
345
- Used primarly for developing single page layouts but can also bundle assets using query selector syntax. It is adequate for most projects and gives you the ability to develop your application as a module in place.
346
-
347
- * ES2020
348
-
349
- ```html
350
- <script src="/dist/squared.min.js"></script>
351
- <script src="/dist/squared.base.min.js"></script>
352
- <script src="/dist/chrome.framework.min.js"></script>
353
- <script>
354
- document.addEventListener("DOMContentLoaded", async () => {
355
- squared.setFramework(chrome, {/* settings */});
356
-
357
- await squared.save(); // Uses defaults from settings
358
- // OR
359
- await squared.saveAs(/* archive filename */, {/* options */});
360
- // OR
361
- await squared.copyTo(/* directory */, {/* options */});
362
- // OR
363
- await squared.appendTo(/* archive location */, {/* options */});
364
-
365
- // Observe
366
- await squared.copyTo(/* directory */, { useOriginalHtmlPage: false, observe: /* Same as Android */ | true /* Auto-reload */}).then(() => squared.observe());
367
- });
368
- </script>
369
- ```
370
-
371
- ### Example: vdom
372
-
373
- The most minimal framework possible (*55kb gzipped*) and can be useful when debugging through DevTools. The `lite` version is about half the bundle size and is recommended for most browser applications.
374
-
375
- * ES2018
376
-
377
- ```html
378
- <script src="/dist/squared.min.js"></script>
379
- <script src="/dist/squared.base-dom.min.js"></script>
380
- <script src="/dist/vdom.framework.min.js"></script>
381
- <script>
382
- document.addEventListener("DOMContentLoaded", async () => {
383
- squared.setFramework(vdom, {/* settings */});
384
-
385
- const element = squared.querySelector("body", true /* synchronous */);
386
- // OR
387
- const elements = await squared.querySelectorAll("*");
388
- // OR
389
- const element = squared.fromElement(document.body, true /* synchronous */);
390
- // OR
391
- const elements = await squared.getElementById("content-id").querySelectorAll("*");
392
- });
393
- </script>
394
- ```
395
-
396
- There are **ES2018** minified versions (\*.min.js) and also **ES2018** non-minified versions.
397
-
398
- ## User Settings
399
-
400
- These settings are available in the global variable `squared` to customize your desired output structure. Each framework shares a common set of settings and also a subset of their own settings.
401
-
402
- ### Example: android
403
-
404
- - [Read the Docs](https://squared.readthedocs.io/en/latest/settings/android.html)
405
-
406
- ```javascript
407
- squared.settings = {
408
- targetAPI: 35,
409
- supportRTL: true,
410
- supportNegativeLeftTop: true,
411
- preloadImages: true,
412
- preloadFonts: true,
413
- preloadLocalFonts: true, // Chromium
414
- preloadCustomElements: true,
415
- enabledSVG: true,
416
- enabledMultiline: true,
417
- enabledViewModel: true,
418
- enabledIncludes: false,
419
- enabledFragment: false,
420
- enabledSubstitute: false,
421
- enabledCompose: false,
422
- dataBindableElements: [], // { selector, attr, expression, namespace?, twoWay? } (see Data Binding section)
423
- includableElements: [], // { selectorStart, selectorEnd, pathname?, filename?, merge?, viewModel? }
424
- substitutableElements: [], // { selector, tag, tagChild?, renderChildren?, autoLayout? }
425
- fragmentableElements: [], // selector | ExtensionFragmentElement
426
- composableElements: [], // selector or property (see Jetpack Compose section)
427
- baseLayoutAsFragment: false | "fragment-name" | ["fragment-name", "fragment-tag"] | { selector, pathname?, filename?, name?, tag? }, // ExtensionFragmentElement
428
- baseLayoutToolsIgnore: "", // Android Studio (e.g. "TooManyViews, HardcodedText")
429
- fontMeasureAdjust: 0.75, // thicker < 0 | thinner > 0 (data-android-font-measure-adjust)
430
- lineHeightAdjust: 1.1, // shorter < 1 | taller > 1 (data-android-line-height-adjust)
431
- preferMaterialDesign: false | "MaterialComponents" | "Material3", // Default is "Material3"
432
- createDownloadableFonts: true,
433
- createElementMap: false,
434
- pierceShadowRoot: true,
435
- adaptStyleMap: true, // Use rendered values for output
436
- lockElementSettings: true,
437
- customizationsBaseAPI: 0, // 0 - All | -1 - None
438
- customizationsBaseAPI: [0, 33, 34], // Multiple
439
- removeDeprecatedAttributes: true, // Remove all
440
- removeDeprecatedAttributes: ["enabled", "singleLine"], // Remove all except "enabled" + "singleLine"
441
- removeUnusedResourceViewId: false,
442
- idNamingStyle: "android",
443
- idNamingStyle: "html", // Use element tagName
444
- idNamingStyle: {
445
- "__default__": "html", // Optional
446
- "DIV": "comments", // HTML is uppercase (comments_1 then comments_2)
447
- "svg": ["vector", 0], // SVG elements areis lowercase (vector_0 then vector_1)
448
- "#text": "text", // Plain text
449
- "::first-letter": "dropcap", // Pseudo element
450
- "main > section": ["content", 1, 2], // content_1 then content_3
451
- "form input[type=submit]": function(node) {
452
- return "submit_" + node.id;
453
- }
454
- },
455
- customizationsOverwritePrivilege: true,
456
- outputMainFileName: "activity_main.xml",
457
- outputFragmentFileName: "fragment_main.xml",
458
- /* Project - parseDocument (first only) */
459
- resourceQualifier: "", // "land" -> "res/layout-land" | "port" -> "res/layout-port" (appended to every "res" folder)
460
- resourceSystemColors: {
461
- "system_accent1_100": "white", // Will be converted to ARGB
462
- "system_accent1_200": ['#ff0000', 0.75], // opacity
463
- "system_accent1_300": squared.lib.color.parseColor("#000", 1)
464
- },
465
- manifestPackage: "", // OR: RequestData<{ namespace: "android.application.id" }>
466
- manifestLabelAppName: "android",
467
- manifestThemeName: "AppTheme",
468
- manifestParentThemeName: "Theme.AppCompat.Light.NoActionBar",
469
- manifestActivityName: ".MainActivity",
470
- outputDocumentEditing: true,
471
- outputDocumentCSS: [], // CSS properties to be processed at server (e.g. "boxShadow")
472
- outputDirectory: "app/src/main",
473
- createManifest: false, // Update AndroidManifest.xml
474
- createBuildDependencies: false | "ktx" | "baseline-profile" | ["ktx", "baseline-profile"], // Update build.gradle
475
-
476
- // Not customizable with parseDocument
477
- builtInExtensions: [
478
- "squared.accessibility",
479
- "android.delegate.background",
480
- "android.delegate.negative-x",
481
- "android.delegate.positive-x",
482
- "android.delegate.max-width-height",
483
- "android.delegate.percent",
484
- "android.delegate.content",
485
- "android.delegate.scrollbar",
486
- "android.delegate.radiogroup",
487
- "android.delegate.multiline",
488
- "squared.relative",
489
- "squared.css-grid",
490
- "squared.flexbox",
491
- "squared.table",
492
- "squared.column",
493
- "squared.list",
494
- "squared.grid",
495
- "squared.sprite",
496
- "squared.whitespace",
497
- "android.resource.background",
498
- "android.resource.svg",
499
- "android.resource.strings",
500
- "android.resource.fonts",
501
- "android.resource.dimens",
502
- "android.resource.styles",
503
- "android.resource.data",
504
-
505
- /* EXCLUDED (breaks layout) */
506
- "android.resource.includes", // enabledIncludes
507
- "android.substitute", // enabledSubstitute
508
- "android.resource.fragment", // enabledFragment
509
- "jetpack.compose.view" // enabledCompose
510
- ],
511
- compressImages: false, // TinyPNG API Key <https://tinypng.com/developers>
512
- compressImages: "****************", // API key
513
- convertImages: "", // png | jpeg | webp | gif | bmp
514
- showAttributes: true,
515
- showAttributes: {
516
- "hyphenationFrequency": "full", // Replace all ("android" is the default namespace)
517
- "android:fontFeatureSettings": null, // Delete all
518
- "app:menu": [
519
- "@menu/menu_1", "@menu/menu_2", // Replace with "@menu/menu_2" when value is "@menu/menu_1"
520
- "@menu/menu_3", null // Delete attribute when value is "@menu/menu_3"
521
- ],
522
- /* OR */
523
- "app:menu": {
524
- "@menu/menu_1": "@menu/menu_2",
525
- "@menu/menu_3": null
526
- }
527
- },
528
- showComments: false | ["boxShadow"] | { self: ["boxShadow"], nextSibling: ["marginBottom"], previousSibling: ["marginTop"], parent: ["position", "top", "left"] }, // TODO in layout.xml
529
- showComments: { include: { tagName: true | ["button"], attributes: true | ["style"], dataset: false, bounds: true }, self: ["boxShadow", ".className"] },
530
- showErrorMessages: false,
531
- convertPixels: "dp", // "sp" | "pt" | "in" | "mm"
532
- convertLineHeight: "sp", // "dp" | "pt" | "in" | "mm"
533
- convertEntities: ["numeric"], // v5.4
534
- convertEntities: ["codepoints", {/* JSON (last) */}], // https://html.spec.whatwg.org/entities.json
535
- insertSpaces: 4,
536
- outputDocumentHandler: "android",
537
- outputEmptyCopyDirectory: false, // Sub directories within target directory (OR: RequestData<{ emptyDir: false }>)
538
- outputSummaryModal: false | "path/summary.css" | ".status-4 { color: purple; }",
539
- outputTasks: {
540
- "**/drawable/*.xml": { handler: "gulp", task: "minify" }
541
- },
542
- outputWatch: {
543
- "**/drawable/*.png": true,
544
- "**/drawable/*.jpg": { interval: 1000, expires: "2h" }
545
- },
546
- outputArchiveName: "android-xml",
547
- outputArchiveFormat: "zip", // tar | 7z | gz
548
- outputArchiveCache: false // Downloadable URL in ResponseData<downloadUrl>
549
- };
550
-
551
- // Optional
552
- squared.settings = {
553
- resolutionDPI: 160, // 320dpi = 2560x1600
554
- resolutionScreenWidth: 1280,
555
- resolutionScreenHeight: 800,
556
- framesPerSecond: 60,
557
- useShapeGeometryBox: true, // Bounding box uses native SVG method getBbox
558
- formatUUID: "8-4-4-4-12", // UUID: 8-4-[12345]3-[89ab]3-12
559
- formatDictionary: "0123456789abcdef",
560
- outputConfigName: "sqd.config",
561
- observePort: 8080,
562
- observeSecurePort: 8443,
563
- observeExpires: "1h", // Server defaults will be used
564
- broadcastPort: 3080,
565
- broadcastSecurePort: 3443
566
- };
567
- ```
568
-
569
- ### Example: chrome
570
-
571
- - [Read the Docs](https://squared.readthedocs.io/en/latest/settings/chrome.html)
572
-
573
- ```javascript
574
- squared.settings = {
575
- preloadImages: false,
576
- preloadFonts: false,
577
- preloadLocalFonts: false,
578
- preloadCustomElements: false,
579
- excludePlainText: true,
580
- createElementMap: true,
581
- pierceShadowRoot: true,
582
- adaptStyleMap: false,
583
- builtInExtensions: [],
584
- showErrorMessages: false,
585
- webSocketPort: 80,
586
- webSocketSecurePort: 443,
587
- outputDocumentHandler: "chrome",
588
- outputEmptyCopyDirectory: false,
589
- outputSummaryModal: false,
590
- outputTasks: {
591
- "*.js": [{ handler: "gulp", task: "minify" }, { handler: "gulp", task: "beautify" }]
592
- },
593
- outputWatch: { "*": true },
594
- outputArchiveName: "chrome-data",
595
- outputArchiveFormat: "zip",
596
- outputArchiveCache: false
597
- };
598
-
599
- // Optional (Same as Android)
600
-
601
- ```
602
-
603
- ### Example: vdom
604
-
605
- - [Read the Docs](https://squared.readthedocs.io/en/latest/settings/vdom.html)
606
-
607
- ```javascript
608
- squared.settings = {
609
- createElementMap: true,
610
- pierceShadowRoot: false,
611
- adaptStyleMap: false,
612
- builtInExtensions: [],
613
- showErrorMessages: false
614
- };
615
- ```
616
-
617
- ## Local Storage
618
-
619
- Custom named user settings per framework can be saved to local storage as JSON and reused across all pages in the same domain. Extensions are configured using the same procedure.
620
-
621
- ```javascript
622
- // Save
623
- squared.setFramework(android, { compressImages: true }, "android-example");
624
-
625
- // Load
626
- squared.setFramework(android, "android-example");
627
- ```
628
-
629
- ```javascript
630
- // Save
631
- await squared.copyTo("/path/project", {/* options will be saved */}, "copy-example", true); // Will overwrite and not merge with previously saved settings
632
-
633
- // Load
634
- await squared.copyTo("/path/project", {/* takes precedence */}, "http://localhost:3000/copy-to/base-config.json"); // Object.assign({ base-config.json }, options)
635
- await squared.copyTo("/path/project", {/* takes precedence */}, "copy-example"); // Object.assign({ copy-example }, options)
636
-
637
- await squared.copyTo("/path/project", "http://localhost:3000/copy-to/base-config.json"); // options = { base-config.json }
638
- await squared.copyTo("/path/project", "copy-example"); // options = { copy_example }
639
- ```
640
-
641
- ## Public Properties and Methods
642
-
643
- - [Read the Docs](https://squared.readthedocs.io/en/latest/methods/squared.html)
644
-
645
- ```javascript
646
- .settings // See user preferences section
647
-
648
- setFramework(app: {}, options?: PlainObject, setting?: string, cache?: boolean) // Install application interpreter
649
- setFramework(app: {}, loadName: string, cache?: boolean) // Load settings from local storage
650
-
651
- // http - hostname(:port)? | https - hostname:443
652
- setHostname(value: string /* http(s)://hostname(:port) */) // Use another cors-enabled server for processing files (--cors <origin>)
653
-
654
- setEndpoint(name: string, value: string) // Set alternate pathname for API v1 functions (ASSETS_COPY | ASSETS_ARCHIVE | LOADER_DATA | THREADS_KILL | WEBSOCKET_OBSERVE)
655
- setLocalAddress(...values: (string | URL | Location)[]) // Additional hostnames which are interpreted as localhost (e.g. http://127.0.0.1)
656
-
657
- prefetch(type: "css" | "javascript" | "image" | "svg", all?: boolean, ...targets: unknown[]) // Cross-origin support for CSS
658
-
659
- parseDocument(...elements: (HTMLElement | string | ElementSettings)[]) // See installation section (Promise)
660
- parseDocumentSync(...elements: (HTMLElement | string | ElementSettings)[]) // Skips preloadImages and preloadFonts (synchronous)
661
-
662
- latest(count?: number) // Most recent parseDocument session ids (1 newest / -1 oldest: string, other: string[])
663
-
664
- auth(token: string) // Set JWT authorization token for all requests
665
-
666
- save(projectId?: string) // Save current session to a new archive using default settings
667
- save(projectId?: string, broadcastId?: string)
668
- save(projectId?: string, timeout?: number)
669
-
670
- close(projectId?: string) // Close current session
671
- reset(projectId?: string) // Clear cache and reopen new session
672
- clear() // Clear all data stored in memory
673
-
674
- toString() // Current framework loaded
675
- toString(projectId: string) // await squared.close(projectId) (required)
676
-
677
- add(...names: (string | Extension | ExtensionRequestObject)[]) // See extension configuration section
678
- remove(...names: (string | Extension)[]) // Remove extensions by namespace or control
679
- get(...names: string[]) // Retrieve extensions by namespace
680
- attr(name: string | Extension, attrName: string, value?: unknown) // Set or get extension options attribute value
681
- apply(name: string | Extension, options: PlainObject, setting?: string) // See extension configuration section
682
-
683
- extend(functionMap: {}, framework?: /* 0 - ALL | 1 - vdom | 2 - android | 4 - chrome */) // Add extension functions and properties to Node prototype
684
-
685
- observe(value?: boolean | MutationObserverInit) // Start after DOM and third-party libraries initialization
686
- broadcast(callback: BroadcastMessageCallback, options: FileBroadcastOptions | string) // Redirect stdout messages to DevTools console
687
-
688
- // Promise (Recommended "cache": createElementMap - true)
689
-
690
- getElementById(value: string, sync?: boolean, cache?: boolean) // sync - false | cache - true (default)
691
- querySelector(value: string, sync?: boolean, cache?: boolean)
692
- querySelectorAll(value: string, sync?: boolean, cache?: boolean)
693
-
694
- fromElement(element: HTMLElement | string, sync?: boolean, cache?: boolean) // sync - false | cache - false (default)
695
- fromNode(node: Node, sync?: boolean, cache?: boolean)
696
- findDocumentNode(element: HTMLElement | string /* querySelector | elementId | controlId */, all?: boolean) // Use before saving to modify internal Node attributes
697
-
698
- observeSrc(element: HTMLElement | string /* querySelector */, callback: WebSocketMessageChange, options?: FileObserveOptions) // Can be used to watch any element with externally hosted files (src/href)
699
- observeSrc(element: HTMLElement | string, options: FileObserveOptions) // Uses location.reload (reload - true)
700
- ```
701
-
702
- Packaging methods will return a Promise and requires a squared-express installation. These features are not supported when the framework is VDOM.
703
-
704
- ```javascript
705
- saveAs(filename: string, options?: {}, setting?: string, overwrite?: boolean) // Save current session as a new archive
706
- saveFiles(filename: string, options: {}, setting?: string, overwrite?: boolean) // Create new archive from FileAsset[]
707
-
708
- // Required (local archives): --disk-read | --unc-read | --access-all (command-line)
709
-
710
- appendTo(pathname: string, options?: {}, setting?: string, overwrite?: boolean) // Create new archive from a preexisting archive and current session
711
- appendFiles(pathname: string, options: {}, setting?: string, overwrite?: boolean) // Create new archive from a preexisting archive and FileAsset[]
712
-
713
- // Required (all): --disk-write | --unc-write | --access-all (command-line)
714
-
715
- copyTo(pathname: string | string[], options?: {}, setting?: string, overwrite?: boolean) // Copy current session to local
716
- copyFiles(pathname: string | string[], options: {}, setting?: string, overwrite?: boolean) // Copy FileAsset[] to local
717
-
718
- kill(pid: number, timeout?: number) // Use -1 or options.pid (set by system) + seconds
719
- kill(timeout: string)
720
- kill() // Terminate previous request
721
- kill(0) // By username (auth required)
722
- kill(-1, 10) // Terminate previous request in 10 seconds
723
- kill(NaN, 10) // Terminate in 10 seconds (next request) ("timeout" is required)
724
- kill("10s") // Only "s" + "ms" (next request)
725
- ```
726
-
727
- ## Extending Node object
728
-
729
- You can add functions and initial variables to the Node object including overwriting preexisting class definitions per framework. Accessor properties are also supported using the *get/set* object syntax.
730
-
731
- ```javascript
732
- squared.extend({
733
- _id: 1,
734
- altId: {
735
- get() {
736
- return this._id;
737
- },
738
- set(value) {
739
- this._id += value;
740
- }
741
- },
742
- customId: {
743
- value: 2,
744
- configurable: false,
745
- enumerable: false
746
- },
747
- addEvent(eventName, callback) {
748
- this.element.addEventListener(eventName, callback);
56
+ # REPO_ROOT = /workspaces # Dir.pwd
57
+ # rake = /workspaces/Rakefile #
58
+ # REPO_HOME = /workspaces/squared # main: "squared"
59
+
60
+ # pathname = /workspaces/pathname
61
+ # optparse = /workspaces/optparse
62
+ # log = /workspaces/logger
63
+ # emc = /workspaces/e-mc
64
+ # pir = /workspaces/pi-r
65
+ # squared = /workspaces/squared
66
+ # cli = /workspaces/squared/publish/sqd-cli
67
+ # sqd-serve = /workspaces/squared/publish/sqd-serve
68
+ # sqd = /workspaces/squared/sqd
69
+
70
+ Workspace::Application
71
+ .new(Dir.pwd, main: "squared") # Dir.pwd? (main? is implicitly basename)
72
+ .banner("group", "project", styles: ["yellow", "black"], border: "bold") # name | project | path | ref | group? | parent? | version?
73
+ .repo("https://github.com/anpham6/squared-repo", "nightly", script: ["build:dev", "build:prod"], ref: :node) # Repo (optional)
74
+ .run("rake install", ref: :ruby)
75
+ .depend(false, group: "default")
76
+ .clean("rake clean", group: "default")
77
+ .clean(["build/"], group: "app")
78
+ .log({ file: "tmp/%Y-%m-%d.log", level: "debug" }, group: "app")
79
+ .add("pathname", run: "rake compile", copy: "rake install", test: "rake test", group: "default", env: { # Ruby (with C extensions)
80
+ "CFLAGS" => "-fPIC -O1"
81
+ })
82
+ .add("optparse", doc: "rake rdoc", group: "default") # Uses bundler/gem_tasks (without C extensions)
83
+ .add("logger", copy: { from: "lib", glob: "**/*.rb", into: "~/.rvm/gems/ruby-3.4.0/gems/logger-1.6.1" }, clean: ["tmp/"]) # autodetect: true
84
+ .add("e-mc", "emc", copy: { from: "publish", scope: "@e-mc", also: [:pir, "squared-express/"] }, ref: :node) # Node
85
+ .add("pi-r", "pir", copy: { from: "publish", scope: "@pi-r" }, clean: ["publish/**/*.js", "tmp/"]) # Trailing slash required for directories
86
+ .add("squared", script: ["build:stage1", "build:stage2"], group: "app") do # Copy target (main)
87
+ add("publish/sqd-cli", "cli", exclude: [:git]) # rake cli:build
88
+ add("publish/sqd-serve") # rake sqd-serve:build
89
+ add("publish/sqd-admin", group: "sqd", exclude: [:base])
90
+ # OR
91
+ with(exclude: [:base]) { add("publish/*", "packages") } # rake packages:sqd-serve:build
92
+ # OR
93
+ add(["publish/sqd-cli", "publish/sqd-serve", "publish/sqd-admin"], true, exclude: [:base]) # rake squared:sqd-serve:build
94
+ end
95
+ .add("squared/sqd", exclude: :git, pass: [:node, 'checkout', 'bump']) do # Skip initialize(:node) + squared:checkout:* + squared:bump:*
96
+ variable_set :script, "build:sqd" # Override detection
97
+ variable_set :depend, false
98
+ variable_set :clean, ["build/sqd/"]
99
+ end
100
+ .style("banner", 255.255) # 256 colors (fg | fg.bg | -0.bg)
101
+ .build(default: "build", parallel: ["pull", "fetch", "rebase", "copy", "clean", /^outdated:/]) do |workspace|
102
+ workspace
103
+ .enable_aixterm
104
+ .style({
105
+ banner: ["bright_cyan", "bold", "bright_black!"],
106
+ border: "bright_white"
107
+ })
108
+ end
109
+
110
+ # default = /workspaces/ruby/*
111
+ # pathname = /workspaces/ruby/pathname
112
+ # optparse = /workspaces/ruby/optparse
113
+ # logger = /workspaces/ruby/logger
114
+ # android = /workspaces/android-docs
115
+ # chrome = /workspaces/chrome-docs
116
+
117
+ Workspace::Application
118
+ .new(ENV["SQUARED_HOME"], prefix: "rb", common: false) # Local styles
119
+ .group("ruby", "default", run: "rake build", copy: "rake install", clean: "rake clean", ref: :ruby, override: {
120
+ pathname: {
121
+ run: "rake compile" # rake rb:pathname:build
749
122
  }
750
- });
751
- squared.setFramework(vdom);
752
-
753
- const body = await squared.fromElement(document.body);
754
- body.altId = 2; // body.altId: 3
755
- body.addEvent("click", function (ev) {
756
- this.classList.toggle("example");
757
- });
758
- ```
759
-
760
- ## Forwarding Request
761
-
762
- Using another identical remote server to build the project when performing a `saveAs` or `copyTo` request can be achieved by changing only the origin address.
763
-
764
- ```javascript
765
- squared.setHostname("http://hostname:8000");
766
- // OR
767
- squared.setHostname(); // Reset to window.location (e.g. localhost:3000)
768
-
769
- await squared.saveAs("chrome.zip"); // Current browser
770
- // OR
771
- await squared.copyTo("/path/project"); // Remote server
772
- ```
773
-
774
- ## Broadcasting
775
-
776
- Console messages (stdout) can be sent to the browser console instead through DevTools.
777
-
778
- ```javascript
779
- squared.broadcast(result => { console.log(result.value); }, "111-111-111"); // System messages from squared-express
780
- squared.broadcast(result => { console.log(result.value); }, "222-222-222"); // Messages from "project-1" project
781
- squared.broadcast(result => { console.log(result.value); }, { socketId: "333-333-333", socketKey: "socket_id" }); // Messages sent from another channel (default is "socketId")
782
-
783
- await squared.copyTo("/path/project/project-1", {
784
- projectId: "project-1",
785
- log: { useColor: true }, // Chromium
786
- broadcastId: "222-222-222" // Specific use alias for "socketId"
787
- });
788
- ```
789
-
790
- ## Extension Configuration
791
-
792
- Layout rendering can be customized using extensions as the program was built to be nearly completely modular. Some of the common layouts already have built-in extensions which you can load or unload based on your preference.
793
-
794
- ```typescript
795
- // Create an extension
796
- class Sample extends squared.base.Extension {
797
- options = {
798
- attributeName: [];
799
- };
800
-
801
- constructor(name, framework = 0, options = {}) { // 0 - ALL | 1 - vdom | 2 - android | 4 - chrome (framework)
802
- super(name, framework, options);
803
- }
804
-
805
- processNode(node: NodeUI) {
806
- const data = this.project.get(node.element, node.localSettings.projectId);
807
- if (data) {
808
- node.each((child, index) => child.element.title = data[index]);
809
- }
123
+ })
124
+ .with(:python) do # ref=Symbol | group=String
125
+ banner([:name, ": ", :version], "path") # chrome-docs: 0.1.0 | /workspaces/chrome-docs
126
+ doc("make html") # rake rb:doc:python
127
+ run(false) # rake rb:build:python (disabled)
128
+ exclude(%i[base git]) # Project::Git.ref (superclass)
129
+ add("android-docs", "android") # rake rb:android:doc
130
+ add("chrome-docs", "chrome") # rake rb:chrome:doc
131
+ end
132
+ .style("inline", "bold")
133
+ .build
134
+ ```
135
+
136
+ **NOTE**: The use of "**ref**" (class name) is only necessary when initializing an empty directory (e.g. *rake repo:init*).
137
+
138
+ ### Clone
139
+
140
+ The task is only active when the project directory is empty or does not exist.
141
+
142
+ ```ruby
143
+ Workspace::Application
144
+ .new(main: "squared")
145
+ .git(
146
+ "emc": "https://github.com/anpham6/e-mc", # rake emc:clone
147
+ "pir": { # rake pir:clone
148
+ uri: "https://github.com/anpham6/pi-r", #
149
+ options: { #
150
+ "origin": "github", # --origin='github'
151
+ "recurse-submodules": false, # --no-recurse-submodules
152
+ "shallow-exclude": ["v0.0.1", "v0.0.2"] # --shallow-exclude='v0.0.1' --shallow-exclude='v0.0.2'
153
+ }
810
154
  }
811
- }
812
-
813
- // Install an extension
814
- const sample = new Sample("widget.example.com", 0, {/* Same as configure */});
815
- squared.add(sample);
816
- // OR
817
- squared.add([sample, {/* config */}]);
818
-
819
- // Configure an extension
820
- squared.attr("widget.example.com", "attributeName", ["width", "height"]); // typeof is enforced and will only set existing attributes
821
-
822
- // Add project data
823
- const ext = squared.get("widget.example.com");
824
-
825
- ext.project.set(element, await fetch(url?id=1)); // Map interface with optional "projectId" argument
826
- ext.project.set(element, await fetch(url?id=2), "project-1");
827
-
828
- const data = ext.project.get(element, "project-2"); // Returns data from default project (id=1)
829
- ```
830
-
831
- Some extensions have a few settings which can be configured. The default settings usually achieve the best overall rendering accuracy without noticeably affecting performance.
832
-
833
- ## ANDROID
834
-
835
- ### Public Methods
836
-
837
- - [Read the Docs](https://squared.readthedocs.io/en/latest/methods/android.html)
838
-
839
- ```javascript
840
- android.setViewModel(data: {}, sessionId?: string) // Object data for layout bindings
841
- android.setViewModelByProject(data: {}, projectId?: string)
842
- android.removeObserver(element: HTMLElement) // Disconnect an observed element from "parseDocument"
843
- android.addXmlNs(name: string, uri: string) // Add global namespaces for third-party controls
844
- android.addDependency(group: string, name: string, version?: string, type?: number) // Add application dependency implementation (build.gradle)
845
- android.addDependencyByProject(projectId: string, group: string, name: string, version?: string, type?: number) // DEPENDENCY_TYPE: 0 - implementation 1 - api 2 - compileOnly 3 - compileOnlyApi 4 - runtimeOnly 5 - testImplementation 8 - androidTestImplementation
846
- android.customize(build: number, tagNameOrWidget: string, options: {}) // Global attributes applied to specific views
847
- android.loadCustomizations(name: string) // Load customizations from Local Storage
848
- android.saveCustomizations(name: string) // Save "customize" data into Local Storage (includes xmlns)
849
- android.resetCustomizations() // All session customizations are deleted
850
- android.addFontProvider(authority: string, package: string, certs: string[], webFonts: string | {}) // Add additional Web fonts (Google Fonts already included)
851
- android.setResolutionByDeviceName(value: string) // Settings prefixed with "resolution" (e.g. Pixel C)
852
- android.getLocalSettings() // Modify controller styles and parsing rules
853
- ```
854
-
855
- ```javascript
856
- // NOTE: squared.settings.targetAPI is always parsed (Except: customizationsBaseAPI = -1)
857
-
858
- android.customize(android.lib.constant.BUILD_VERSION.ALL /* 0 */, "Button", {
859
- android: {
860
- minWidth: "35px",
861
- minHeight: "25px"
155
+ )
156
+ .git("squared", "/path/to/squared", options: { local: true }) # Relative paths resolve from workspace root
157
+ .git(
158
+ {
159
+ emc: { uri: "e-mc", options: { "depth": 2 } }, # https://github.com/anpham6/e-mc
160
+ pir: "pi-r" # Maps task alias to repository folder
862
161
  },
863
- "_": { // Non-namespaced attributes
864
- style: "@style/Widget.Material3.Button.TextButton"
865
- }
866
- });
867
-
868
- android.customize(android.lib.constant.BUILD_VERSION.KITKAT /* 19 */, "svg", {
869
- android: {
870
- "[src]": "app:srcCompat" // Change namespace to "app"
162
+ base: "https://github.com/anpham6", # Required
163
+ repo: ["squared", "android-docs", "chrome-docs"], # https://github.com/anpham6/squared
164
+ options: { # Only "repo"
165
+ "depth": 1,
166
+ "quiet": true
871
167
  }
872
- });
873
-
874
- // Local Storage
875
- android.saveCustomizations("customize-example"); // Save at least once in one layout
876
-
877
- android.loadCustomizations("customize-example"); // Load in any other layout
168
+ )
169
+ .with(:node) do # rake clone:node
170
+ add("e-mc", "emc") # https://github.com/anpham6/e-mc
171
+ add("pi-r", "pir") # https://github.com/anpham6/pi-r
172
+ add("squared") # https://github.com/anpham6/squared
173
+ end
174
+ .with(:python) do # rake clone:python
175
+ add("android-docs")
176
+ add("chrome-docs")
177
+ end
178
+ .git("https://github.com/anpham6") # Uses already defined root projects
179
+ .git("https://github.com/anpham6", ["emc", "pir"]) # Targets any defined project
180
+ .build(parallel: ["clone"]) # rake clone + rake clone:sync
181
+ ```
182
+
183
+ ### Graph
184
+
185
+ ```ruby
186
+ Workspace::Application
187
+ .new(main: "squared")
188
+ .graph(["depend"], ref: :git) # Optional
189
+ .with(:python) do
190
+ add("android-docs", "android")
191
+ add("chrome-docs", "chrome", graph: "android")
192
+ end
193
+ .with(:node) do
194
+ graph(["build", "copy"], on: { # Overrides "git"
195
+ first: proc { puts "1" },
196
+ last: proc { puts "2" }
197
+ })
198
+ add("e-mc", "emc") do
199
+ first("build", "emc:clean", "emc:depend") # rake emc:clean; rake emc:depend; rake emc:build; puts "123";
200
+ last("build", out: "123") { |out: nil| puts out } #
201
+ error("build") { |err: nil| log.debug err } #
202
+ end
203
+ add("pi-r", "pir", graph: "emc", first: {
204
+ build: proc { puts self.name } # puts "pir"
205
+ })
206
+ add("squared-express", "express", graph: "pir")
207
+ add("squared", graph: ["chrome", "express"]) do
208
+ first("git:ls-files") { puts path }
209
+ last("git:ls-files") { puts workspace.root }
210
+ end
211
+ end
212
+ .with(:ruby) do
213
+ add("pathname")
214
+ add("fileutils", graph: "pathname")
215
+ add("optparse")
216
+ add("rake", graph: ["fileutils", "optparse"])
217
+ banner(command: false) # Always hide banner
218
+ end
219
+ .build
878
220
  ```
879
221
 
880
- ```javascript
881
- android.addXmlNs("tools", "http://schemas.android.com/tools");
882
-
883
- android.customize(16 /* Jelly Bean */, "ImageView", {
884
- tools: {
885
- ignore: "ContentDescription",
886
- targetApi: "16"
887
- }
888
- });
889
- ```
890
-
891
- ### Static Methods
892
-
893
- Project resources can include additional values that are required during compilation. TypeScript definitions are available in the `types/android` directory.
894
-
895
- ```javascript
896
- squared.parseDocument().then(node => {
897
- const resourceId = node.localSettings.resourceId;
898
- android.base.Resource.addString(resourceId, value, /* name */);
899
- android.base.Resource.addArray(resourceId, name, items);
900
- android.base.Resource.addColor(resourceId, color);
901
- android.base.Resource.addDimen(resourceId, name, value);
902
- android.base.Resource.addTheme(resourceId, theme);
903
- squared.save();
904
- });
905
- ```
906
-
907
- ### Data Binding
908
-
909
- View model data can be applied to most HTML elements using the dataset attribute. Different view models can be used for every `parseDocument` session.
910
-
911
- Leaving the `sessionId` empty uses the default view model which is searched last for all projects when attempting a bind.
912
-
913
- ```javascript
914
- // NOTE: latest(undefined = 1): string (1: most recent sessionId | -1: first sessionId)
915
-
916
- squared.parseDocument("id-1", "id-2", "id-3").then(nodes => {
917
- const sessions = squared.latest(2); // ["00001", "00002", "00003"] => ["00002", "00003"]
918
- android.setViewModel(
919
- {
920
- import: ["java.util.Map", "java.util.List"],
921
- variable: [
922
- { name: "user", type: "com.example.User" },
923
- { name: "list", type: "List&lt;String>" },
924
- { name: "map", type: "Map&lt;String, String>" },
925
- { name: "index", type: "int" },
926
- { name: "key", type: "String" }
927
- ]
928
- },
929
- sessions[0] // nodes[1].sessionId
930
- );
931
- android.setViewModel(
932
- {
933
- import: ["java.util.Map"],
934
- variable: [
935
- { name: "map", type: "Map&lt;String, String>" }
936
- ]
937
- },
938
- sessions[1] // nodes[2].sessionId
939
- );
940
- });
941
-
942
- squared.parseDocument({
943
- element: "main",
944
- enabledViewModel: true,
945
- dataBindableElements: [
946
- {
947
- selector: "#first_name",
948
- namespace: "android", // "android" is default
949
- attr: "text",
950
- expression: "user.firstName"
951
- },
952
- {
953
- selector: "#last_name",
954
- attr: "text",
955
- expression: "user.lastName"
956
- },
957
- {
958
- selector: "#remember_me",
959
- attr: "checked",
960
- expression: "user.rememberMe",
961
- twoWay: true
962
- }
963
- ],
964
- data: {
965
- viewModel: {
966
- import: ["java.util.Map"],
967
- variable: [
968
- { name: "map", type: "Map&lt;String, String>" }
969
- ]
970
- }
971
- }
972
- });
973
-
974
- squared.save();
975
- ```
976
-
977
- Inlining is also supported and might be more convenient for simple layouts. JavaScript is recommended when you are calling `parseDocument` multiple times.
978
-
979
- ```
980
- data-viewmodel-{namespace}-{attribute} -> data-viewmodel-android-text
981
- ```
982
-
983
- These two additional output parameters are required when using the "**data-viewmodel**" prefix.
984
-
985
- ```html
986
- <div id="main">
987
- <label>Name:</label>
988
- <input id="first_name" type="text" data-viewmodel-android-text="user.firstName" />
989
- <input id="last_name" type="text" data-viewmodel-android-text="user.lastName" />
990
- <input id="remember_me" type="checkbox" data-viewmodel-android-checked="=user.rememberMe" /> <!-- "=" for two-way binding -->
991
- </div>
992
- ```
993
-
994
- ```xml
995
- <layout>
996
- <data>
997
- <import type="java.util.Map" />
998
- <import type="java.util.List" />
999
- <variable name="user" type="com.example.User" />
1000
- <variable name="list" type="List&lt;String&gt;" />
1001
- <variable name="map" type="Map&lt;String, String&gt;" />
1002
- <variable name="index" type="int" />
1003
- <variable name="key" type="String" />
1004
- </data>
1005
- <LinearLayout android:id="@+id/main">
1006
- <TextView android:text="Name:" />
1007
- <EditText
1008
- android:id="@+id/first_name"
1009
- android:inputType="text"
1010
- android:text="@{user.firstName}" />
1011
- <EditText
1012
- android:id="@+id/last_name"
1013
- android:inputType="text"
1014
- android:text="@{user.lastName}" />
1015
- <CheckBox
1016
- android:id="@+id/remember_me"
1017
- android:checked="@={user.rememberMe}" />
1018
- </LinearLayout>
1019
- </layout>
1020
- ```
1021
-
1022
- ### Layout Includes / Merge Tag
1023
-
1024
- Some applications can benefit from using includes or merge tags to share common templates. Nested includes is supported.
1025
-
1026
- ```html
1027
- <div>
1028
- <div id="item1">Item 1</div>
1029
- <div id="item2" data-android-include-start="true" data-android-include-merge="true" data-pathname-android="app/src/main/res/layout-land" data-filename-android="filename1.xml">Item 2</div>
1030
- <div id="item3">Item 3</div>
1031
- <div id="item4" data-android-include-end="true">Item 4</div>
1032
- <div id="item5" data-android-include="filename2" data-android-include-end="true" data-android-include-viewmodel="exampleData">Item 5</div> <!-- viewModel -->
1033
- </div>
1034
- ```
1035
-
1036
- ```javascript
1037
- android.setViewModelByProject({ variable: [{ name: "exampleData", type: "com.example.ExampleData" }] }, "project-1"); // Default is "_"
1038
-
1039
- squared.parseDocument({
1040
- element: document.body,
1041
- projectId: "project-1", // Affects all layouts in same project
1042
- enabledIncludes: true,
1043
- includableElements: [
1044
- {
1045
- selectorStart: "#item2",
1046
- selectorEnd: "#item4",
1047
- pathname: "app/src/main/res/layout-land",
1048
- filename: "filename1.xml",
1049
- merge: true // Multiple elements will auto-merge
1050
- },
1051
- {
1052
- selectorStart: "#item5",
1053
- selectorEnd: "#item5",
1054
- filename: "filename2",
1055
- viewModel: "exampleData" // One element only (merge=false)
1056
- }
1057
- ]
1058
- });
222
+ ```sh
223
+ rake pir:graph # emc + pir
224
+ rake express:graph # emc + pir + express
225
+ rake chrome:graph # android + chrome
226
+ rake graph:python # same
227
+ rake squared:graph # android + chrome + emc + pir + express + squared
228
+ rake graph:node # same
229
+ rake rake:graph # pathname + fileutils + optparse + rake
230
+ rake graph:ruby # same
231
+ rake graph # graph:node + graph:ruby
1059
232
  ```
1060
- > [!NOTE]
1061
- > By sessionId has precedence when associating a view model.
1062
-
1063
- ```xml
1064
- <LinearLayout>
1065
- <TextView>Item 1</TextView>
1066
- <include layout="@layout/filename1" />
1067
- <include layout="@layout/filename2" app:exampleData="@{exampleData}" />
1068
- </LinearLayout>
1069
- <!-- res/layout/activity_main.xml -->
1070
233
 
1071
- <merge>
1072
- <TextView>Item 2</TextView>
1073
- <TextView>Item 3</TextView>
1074
- <TextView>Item 4</TextView>
1075
- </merge>
1076
- <!-- res/layout-land/filename1.xml -->
234
+ ### Batch
1077
235
 
1078
- <layout>
1079
- <data>
1080
- <variable name="exampleData" type="com.example.ExampleData" />
1081
- </data>
1082
- <TextView>Item 5</TextView>
1083
- </layout>
1084
- <!-- res/layout/filename2.xml -->
236
+ ```ruby
237
+ Workspace::Series.batch(:ruby, :node, {
238
+ stage: %i[graph test],
239
+ reset: %i[stash pull]
240
+ })
1085
241
  ```
1086
242
 
1087
- The attributes "**data-android-include-start**" and "**data-android-include-end**" can only be applied to elements which share the same parent container. See `/demos/gradient.html` for usage instructions.
1088
-
1089
- > [!TIP]
1090
- > "**data-pathname-android**" AND "**data-filename-android**" can also be used with any `parseDocument` base element.
1091
-
1092
- ### Redirecting Output Location
243
+ ### Rename
1093
244
 
1094
- Sometimes it is necessary to extract elements and append them into other containers for it to look identical on the Android device. Redirection will fail if the *target location* is not a block/container element.
1095
-
1096
- ```html
1097
- <div>
1098
- <span>Item 1</span>
1099
- <span data-android-target="location">Item 2</span>
1100
- <span data-android-target="location" data-android-target-index="1">Item 3</span>
1101
- <div>
1102
- <ul id="location">
1103
- <li>Item 4</li>
1104
- <li>Item 5</li>
1105
- <!-- span -->
1106
- </ul>
1107
- ```
1108
-
1109
- ```xml
1110
- <LinearLayout>
1111
- <TextView>Item 1</TextView>
1112
- </LinearLayout>
1113
- <LinearLayout>
1114
- <TextView>Item 4</TextView>
1115
- <TextView>Item 3</TextView>
1116
- <TextView>Item 5</TextView>
1117
- <TextView>Item 2</TextView>
1118
- </LinearLayout>
245
+ ```ruby
246
+ Workspace::Series.rename("depend", "install")
1119
247
  ```
1120
248
 
1121
- Using `target` into a ConstraintLayout or RelativeLayout container will not include automatic positioning.
249
+ ## Usage
1122
250
 
1123
- ### Custom Attributes
251
+ ```sh
252
+ rake -T # List tasks
253
+ rake # rake status (usually "build")
1124
254
 
1125
- System or extension generated attributes can be overridden preceding finalization. They will only be visible on the declared framework.
255
+ # GIT_OPTIONS=rebase
256
+ rake pull # All except "default" + "app"
257
+ rake pull:ruby # pathname + optparse + logger
258
+ rake pull:default # pathname + optparse
259
+ rake pull:app # squared
260
+ rake pull:node # emc + pir + squared
1126
261
 
1127
- ```
1128
- data-android-attr-{namespace}? -> Default is "android"
1129
- ```
262
+ rake build # All except "android"
263
+ rake doc # optparse + android
264
+ rake depend # All except "default"
1130
265
 
1131
- ```html
1132
- <div id="customId"
1133
- data-android-attr="layout_width::match_parent;layout_height::match_parent"
1134
- data-android-attr-app="layout_scrollFlags::scroll|exitUntilCollapsed">
1135
- </div>
1136
- ```
266
+ rake build:ruby # rake compile + rake install + rake install
1137
267
 
1138
- ```xml
1139
- <LinearLayout
1140
- android:id="@+id/customId"
1141
- android:layout_width="match_parent"
1142
- android:layout_height="match_parent"
1143
- app:layout_scrollFlags="scroll|exitUntilCollapsed" />
1144
- ```
268
+ rake clean # All except "default" + "app"
269
+ rake clean:ruby # rake clean + rake clean + ["tmp/"]
270
+ rake clean:default # rake clean + rake clean + skip
271
+ rake clean:app # none + skip + ["build/"]
272
+ rake clean:node # none + ["publish/**/*.js", "tmp/"] + ["build/"]
1145
273
 
1146
- ```javascript
1147
- const node = squared.findDocumentNode("customId"); // querySelector is supported
1148
- node.android("layout_width", "match_parent");
1149
- node.android("layout_height", "match_parent");
1150
- node.app("layout_scrollFlags", "scroll|exitUntilCollapsed");
274
+ rake squared:run[#] # List scripts (node)
275
+ rake squared:rake[#] # List tasks (ruby)
1151
276
  ```
1152
277
 
1153
- ### SVG animations
1154
-
1155
- Only the XML based layout and resource files can be viewed on the Android device/emulator without any Java/Kotlin backend code. To play animations in the emulator you also have to `start` the animation in *MainActivity.java*.
1156
-
1157
- ```java
1158
- import android.graphics.drawable.Animatable;
1159
-
1160
- android.widget.ImageView imageView1 = findViewById(R.id.imageview_1);
1161
- if (imageView1 != null) {
1162
- Animatable animatable = (Animatable) imageView1.getDrawable();
1163
- animatable.start();
1164
- }
278
+ ```sh
279
+ rake build:app # squared + cli + sqd-serve
280
+ rake squared:build:workspace # cli + sqd-serve
281
+ rake pull:sqd # sqd-admin
282
+ rake squared:pull:workspace # sqd-serve + sqd-admin
283
+ rake squared:outdated:workspace # cli + sqd-serve + sqd-admin
1165
284
  ```
1166
285
 
1167
- ### Jetpack Compose
1168
-
1169
- Most mobile applications do not have a deeply nested hierarchy and are generally better to implement using declarative programming.
286
+ ## Methods
1170
287
 
1171
- ```javascript
1172
- squared.settings.composableElements = ["main", "#content", "--boxShadow", "--height=300px", {
1173
- selector: "main",
1174
- android: {
1175
- layout_height: "match_parent"
1176
- },
1177
- tools: {
1178
- composableName: "com.example.compose.Preview"
1179
- }
1180
- }];
1181
- squared.settings.createBuildDependencies = true; // Optional
1182
- ```
288
+ Task:
1183
289
 
1184
- You can also do it using the "**android.substitute**" extension directly inside the HTML element.
290
+ * run
291
+ * depend
292
+ * graph
293
+ * test
294
+ * doc
295
+ * clean
1185
296
 
1186
- ```javascript
1187
- // android.substitute is only used here to demonstrate using extensions
297
+ Non-task:
1188
298
 
1189
- squared.add(["android.substitute", {
1190
- element: {
1191
- content: { android: { layout_width: "match_parent" } }
1192
- }
1193
- }]);
299
+ * log
300
+ * exclude
1194
301
 
1195
- const items = squared.attr("android.substitute", "viewAttributes");
1196
- items.push("hint", "buttonTint");
1197
- /* OR */
1198
- squared.attr("android.substitute", "viewAttributes", items.concat(["hint", "buttonTint"])); // Attributes to preserve (default is "android.view.View")
1199
- squared.attr("android.substitute", "attributeMapping", { "android:src": "app:srcCompat", "icon": "navigationIcon" /* android */});
302
+ ## Styles
1200
303
 
1201
- squared.parseDocument({
1202
- element: document.body,
1203
- substitutableElements: [{
1204
- selector: "#content",
1205
- tag: "androidx.compose.ui.platform.ComposeView",
1206
- renderChildren: false
1207
- }],
304
+ * banner
305
+ * border
306
+ * header
307
+ * active
308
+ * inline
309
+ * current
310
+ * major
311
+ * red
312
+ * yellow
313
+ * green
1208
314
 
1209
- // Some extensions have convenience properties
1210
- enabledSubstitute: true,
1211
- /* OR */
1212
- include: ["android.substitute"]
1213
- });
1214
- ```
315
+ ## Environment
1215
316
 
1216
- ```html
1217
- <body>
1218
- <header style="height: 100px"></header>
1219
- <main id="content"
1220
- data-use="android.substitute"
1221
- data-android-substitute-tag="androidx.compose.ui.platform.ComposeView"
1222
- style="height: 300px; box-shadow: 10px 5px 5px black;">
1223
- <!-- Interior elements are not rendered -->
1224
- </main>
1225
- <footer style="height: 80px"></footer>
1226
- </body>
1227
- ```
317
+ ### Path
1228
318
 
1229
- Compose will remove child elements by default. You can preserve them by explictly using the renderChildren property. (data-android-substitute-render-children="true")
319
+ All project executable programs can have their binary path set to a non-global alias.
1230
320
 
1231
- ```html
1232
- <div id="fragment"
1233
- data-use="android.substitute"
1234
- data-android-substitute-tag="androidx.fragment.app.FragmentContainerView"
1235
- data-android-substitute-render-children="false"
1236
- data-android-attr="name::com.github.fragment;tag::example">
1237
- <!-- Interior elements are not rendered -->
1238
- </div>
321
+ ```ruby
322
+ Common::PATH.merge!({
323
+ GIT: '/usr/bin/git',
324
+ GEM: '~/.rvm/rubies/ruby-3.4.0/bin/gem',
325
+ BUNDLE: '~/.rvm/gems/ruby-3.4.0/bin/bundle',
326
+ RAKE: '~/.rvm/gems/ruby-3.4.0/bin/rake',
327
+ NPM: '/opt/node/v22.0.0/bin/npm',
328
+ PYTHON: "#{ENV['PYTHONPATH']}/bin/python"
329
+ })
1239
330
  ```
1240
331
 
1241
- You can also use "**android.substitute**" to create fragments within a layout similar to Compose.
332
+ ### Build
1242
333
 
1243
- Usually you do not render child elements when using Compose View. There are some cases where it can be used effectively to reproduce the desired layout.
334
+ ```ruby
335
+ # :env :run :opts
336
+ # LD_LIBRARY_PATH="path/to/lib" CFLAGS="-Wall" gcc a.c -o a.o -c
337
+ BUILD_${NAME} # gcc a.c -o a.o
338
+ BUILD_${NAME}_OPTS # -c
339
+ BUILD_${NAME}_ENV # {"LD_LIBRARY_PATH":"path/to/lib","CFLAGS":"-Wall"} (hash/json)
1244
340
 
1245
- ```javascript
1246
- squared.parseDocument({
1247
- element: document.body,
1248
- include: ["android.substitute"], // OR: settings.enabledSubstitute
1249
- substitutableElements: [{
1250
- selector: "#navigation",
1251
- tag: "com.google.android.material.tabs.TabLayout",
1252
- tagChild: "com.google.android.material.tabs.TabItem",
1253
- tagChildAttr: {
1254
- android: {
1255
- layout_height: "match_parent"
1256
- }
1257
- },
1258
- renderChildren: true,
1259
- autoLayout: true
1260
- }]
1261
- });
1262
- ```
1263
-
1264
- ```html
1265
- <ul id="navigation"
1266
- data-use-android="android.substitute"
1267
- data-android-attr="layout_height::match_parent"
1268
- data-android-substitute-tag="com.google.android.material.tabs.TabLayout"
1269
- data-android-substitute-tag-child="com.google.android.material.tabs.TabItem"
1270
- data-android-substitute-tag-child-attr="layout_height::match_parent"
1271
- data-android-substitute-auto-layout="true">
1272
- <li>TAB 1</li>
1273
- <li>TAB 2</li>
1274
- <li>TAB 3</li>
1275
- </ul>
1276
- ```
341
+ # :env :opts :script
342
+ # NODE_ENV="production" NO_COLOR="1" npm run --loglevel=error --workspaces=false build:dev
343
+ BUILD_${NAME} # build:dev
344
+ BUILD_${NAME}_OPTS # --loglevel=error --workspaces=false
345
+ BUILD_${NAME}_ENV # {"NODE_ENV":"production","NO_COLOR":"1"} (hash/json)
346
+ BUILD_${NAME}_DEV # pattern,0,1 (:dev)
347
+ BUILD_${NAME}_PROD # pattern,0,1 (:prod)
1277
348
 
1278
- ```xml
1279
- <com.google.android.material.tabs.TabLayout
1280
- android:id="@+id/navigation"
1281
- android:layout_height="match_parent"
1282
- android:layout_width="wrap_content">
1283
- <com.google.android.material.tabs.TabItem
1284
- android:layout_height="match_parent"
1285
- android:layout_width="wrap_content"
1286
- android:text="@string/tab_1" />
1287
- <com.google.android.material.tabs.TabItem
1288
- android:layout_height="match_parent"
1289
- android:layout_width="wrap_content"
1290
- android:text="@string/tab_2" />
1291
- <com.google.android.material.tabs.TabItem
1292
- android:layout_height="match_parent"
1293
- android:layout_width="wrap_content"
1294
- android:text="@string/tab_3" />
1295
- </com.google.android.material.tabs.TabLayout>
349
+ BUILD_${NAME}=0 # skip project
1296
350
  ```
1297
351
 
1298
- ### Downloadable Fonts
1299
-
1300
- Google Fonts are pre-installed and can be used without any additional configuration.
352
+ These options also support the project specific suffix `${NAME}`. (e.g. LOG_FILE_SQUARED)
1301
353
 
1302
- * [Guide](https://developer.android.com/guide/topics/ui/look-and-feel/downloadable-fonts)
354
+ ### Logger
1303
355
 
1304
- ```xml
1305
- <!-- build.gradle -->
1306
- dependencies {
1307
- implementation 'androidx.appcompat:appcompat:1.6.0' <!-- createBuildDependencies = true -->
1308
- <!-- OR -->
1309
- implementation 'com.android.support:appcompat-v7:28.0.0'
1310
- }
1311
-
1312
- <!-- AndroidManifest.xml -->
1313
- <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <!-- createManifest = true -->
1314
- <application android:theme="@style/AppTheme">
1315
- <meta-data android:name="preloaded_fonts" android:resource="@array/preloaded_fonts" />
1316
- </application>
1317
- </manifest>
1318
- ```
1319
-
1320
- ```javascript
1321
- // https://developers.google.com/fonts/docs/developer_api
1322
-
1323
- await android.addFontProvider(
1324
- "com.google.android.gms.fonts",
1325
- "com.google.android.gms",
1326
- ["MIIEqDCCA5CgAwIBAgIJANWFuGx9007...", "MIIEQzCCAyugAwIBAgIJAMLgh0Zk..."],
1327
- "https://www.googleapis.com/webfonts/v1/webfonts?key=1234567890" // JSON object is synchronous
1328
- );
1329
- /* OR */
1330
- squared.attr("android.resource.fonts", "installGoogleFonts", false); // Use browser and local fonts only
356
+ ```ruby
357
+ LOG_FILE # %Y-%m-%d.log
358
+ # OR
359
+ LOG_AUTO # year,y,month,m,day,d,1
360
+ # Optional
361
+ LOG_DIR # exist?
362
+ LOG_LEVEL # See gem "logger"
363
+ LOG_COLUMNS # terminal width (default: 80)
1331
364
  ```
1332
365
 
1333
- ### Excluding Procedures / Applied Attributes
1334
-
1335
- Most attributes can be excluded from the generated XML using the dataset feature in HTML. One or more can be applied to any tag using the OR "**|**" operator. These may cause warnings when you compile your project and should only be used when an extension has their custom attributes overwritten.
1336
-
1337
- NOTE: Defining an element "**id**" will prevent it from being removed during the optimization phase.
366
+ ### Repo
1338
367
 
1339
- ```html
1340
- <div data-exclude-section="DOM_TRAVERSE | EXTENSION | RENDER | ALL"
1341
- data-exclude-procedure="CONSTRAINT | LAYOUT | ALIGNMENT | ACCESSIBILITY | LOCALIZATION | CUSTOMIZATION | OPTIMIZATION | ALL"
1342
- data-exclude-resource="BOX_STYLE | BOX_SPACING | FONT_STYLE | VALUE_STRING | IMAGE_SOURCE | ASSET | ALL"
1343
- data-exclude-optimization="EXCLUDE | INHERIT | ALIGNMENT | POSITION | DIMENSION | MARGIN | PADDING | BASELINE | WHITESPACE | TRANSLATE | TRANSFORM | SCALING">
1344
- </div>
1345
- <div>
1346
- <span data-exclude-resource="FONT_STYLE">content</span>
1347
- <input id="cb1" type="checkbox" data-exclude-procedure="ACCESSIBILITY"><label for="cb1">checkbox text</label>
1348
- </div>
368
+ ```ruby
369
+ REPO_ROOT # parent dir
370
+ REPO_HOME # project dir (main)
371
+ REPO_BUILD # run,script
372
+ REPO_GROUP # string
373
+ REPO_REF # e.g. ruby,node
374
+ REPO_DEV # pattern,0,1
375
+ REPO_PROD # pattern,0,1
376
+ REPO_WARN # 0,1
377
+ REPO_SYNC # 0,1
378
+ REPO_MANIFEST # e.g. latest,nightly,prod
379
+ REPO_TIMEOUT # confirm dialog (seconds)
1349
380
  ```
1350
381
 
1351
382
  ## LICENSE