squared
Installation (global js variable: squared)
Option #1 (easier):
GitHub
> git clone https://github.com/anpham6/squared
> cd squared
> npm install
> npm run prod -OR- npm run dev
> squared.settings.json (configure)
> node app.js
NPM
> npm install squared
> cd node_modules/squared
> squared.settings.json (configure)
> node app.js
Option #2 (more features):
> git clone https://github.com/anpham6/squared-apache
> cd squared-apache
> squared.settings (configure)
> gradlew run
<script src="/dist/squared.min.js"></script>
<script src="/dist/squared.base.min.js"></script>
<script src="/dist/squared.svg.min.js"></script>
<script src="/dist/android.framework.min.js"></script>
<script>
squared.settings.targetAPI = 29;
document.addEventListener('DOMContentLoaded', function() {
squared.setFramework(android);
squared.parseDocument(, , );
squared.close();
squared.saveToArchive(, );
squared.copyToDisk(, );
squared.appendToArchive(, );
squared.reset();
});
</script>
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.
Library files are in the /dist folder. A minimum of three files are required to run squared.
- squared
- squared-base
- squared-svg - optional
- framework (e.g. android | chrome)
- extensions (e.g. android.widget) - optional
Usable combinations: 1-2-4 + 1-2-4-5 + 1-2-3-4-5 + 1-3
There are ES6 minified versions (*.min.js) and also ES6 non-minified versions. Browsers which do not support at least ES6 (under 5%) are not being supported to take advantage of async/await and also for code readability.
NOTE: Calling "save" or "write" methods before the images have completely loaded can sometimes cause them to be excluded from the generated layout. In these cases you should use the "parseDocument" promise method "then" to set a callback for your commands.
document.addEventListener('DOMContentLoaded', function() {
squared.setFramework(android);
squared.parseDocument(, ).then(function() {
squared.close();
squared.saveToArchive();
});
});
*** External CSS files cannot be parsed when loading HTML pages using the file:/// protocol (hard drive) with Chrome 64 or higher. Loading the HTML page from a web server (http://localhost) or embedding the CSS files into a <style> tag can get you past this security restriction. You can also use your preferred browser Safari/Edge/Firefox. The latest version of Chrome is ideally what you should use to generate the production version of your program. ***
ALL: User Settings
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.
Example: android
squared.settings = {
builtInExtensions: [
'android.delegate.background',
'android.delegate.negative-x',
'android.delegate.positive-x',
'android.delegate.max-width-height',
'android.delegate.percent',
'android.delegate.css-grid',
'android.delegate.scrollbar',
'android.delegate.radiogroup',
'squared.accessibility',
'squared.relative',
'squared.css-grid',
'squared.flexbox',
'squared.table',
'squared.column',
'squared.list',
'squared.verticalalign',
'squared.grid',
'squared.sprite',
'squared.whitespace',
'android.resource.svg',
'android.resource.background',
'android.resource.strings',
'android.resource.fonts',
'android.resource.dimens',
'android.resource.styles',
'android.resource.data',
'android.resource.includes'
],
targetAPI: 29,
resolutionDPI: 160,
resolutionScreenWidth: 1280,
resolutionScreenHeight: 900,
framesPerSecond: 60,
supportRTL: true,
preloadImages: true,
compressImages: false,
convertImages: '',
supportNegativeLeftTop: true,
exclusionsDisabled: false,
customizationsOverwritePrivilege: true,
showAttributes: true,
createQuerySelectorMap: false,
convertPixels: 'dp',
insertSpaces: 4,
autoCloseOnWrite: true,
showErrorMessages: true,
manifestLabelAppName: 'android',
manifestThemeName: 'AppTheme',
manifestParentThemeName: 'Theme.AppCompat.Light.NoActionBar',
outputMainFileName: 'activity_main.xml',
outputDirectory: 'app/src/main',
outputEmptyCopyDirectory: false,
outputArchiveName: 'android-xml',
outputArchiveFormat: 'zip',
};
Example: chrome
squared.settings = {
builtInExtensions: [
'chrome.compress.png',
'chrome.compress.jpeg',
'chrome.compress.brotli',
'chrome.compress.gzip',
'chrome.convert.png',
'chrome.convert.jpeg',
'chrome.convert.bmp',
'chrome.convert.gif',
'chrome.convert.tiff'
],
preloadImages: false,
compressImages: false,
excludePlainText: true,
createQuerySelectorMap: true,
showErrorMessages: false,
outputFileExclusions: ['squared.*', 'chrome.framework.*'],
outputEmptyCopyDirectory: false,
outputArchiveName: 'chrome-data',
outputArchiveFormat: 'zip',
};
ALL: Public Properties and Methods
There is no official documentation as this project is still in early development. The entire source code is available on GitHub if you need further clarification.
.settings
setFramework(module: {}, cached?: boolean)
setViewModel(data?: {})
parseDocument()
parseDocumentAsync()
ready()
close()
reset()
saveToArchive(filename?: string, options?: {})
createFrom(format: string, options: {})
appendToArchive(pathname: string, options?: {})
appendFromArchive(pathname: string, options: {})
copyToDisk(directory: string, options?: {})
toString()
include(extension: string | squared.base.Extension, options?: {})
retrieve(name: string)
configure(name: string, options: {})
exclude(name: string)
ANDROID: Public System Methods
squared.system.customize(build: number, widget: string, options: {})
squared.system.addXmlNs(name: string, uri: string)
squared.system.copyLayoutAllXml(directory: string, options?: {})
squared.system.copyResourceAllXml(directory: string, options?: {})
squared.system.copyResourceAnimXml(directory: string, options?: {})
squared.system.copyResourceArrayXml(directory: string, options?: {})
squared.system.copyResourceColorXml(directory: string, options?: {})
squared.system.copyResourceDimenXml(directory: string, options?: {})
squared.system.copyResourceDrawableXml(directory: string, options?: {})
squared.system.copyResourceFontXml(directory: string, options?: {})
squared.system.copyResourceStringXml(directory: string, options?: {})
squared.system.copyResourceStyleXml(directory: string, options?: {})
squared.system.saveLayoutAllXml(filename?: string, options?: {})
squared.system.saveResourceAllXml(filename?: string, options?: {})
squared.system.saveResourceAnimXml(filename?: string, options?: {})
squared.system.saveResourceArrayXml(filename?: string, options?: {})
squared.system.saveResourceColorXml(filename?: string, options?: {})
squared.system.saveResourceDimenXml(filename?: string, options?: {})
squared.system.saveResourceDrawableXml(filename?: string, options?: {})
squared.system.saveResourceFontXml(filename?: string, options?: {})
squared.system.saveResourceStringXml(filename?: string, options?: {})
squared.system.saveResourceStyleXml(filename?: string, options?: {})
squared.system.writeLayoutAllXml()
squared.system.writeResourceAllXml()
squared.system.writeResourceAnimXml()
squared.system.writeResourceArrayXml()
squared.system.writeResourceColorXml()
squared.system.writeResourceDimenXml()
squared.system.writeResourceDrawableXml()
squared.system.writeResourceFontXml()
squared.system.writeResourceStringXml()
squared.system.writeResourceStyleXml()
squared.system.copyResourceDrawableImage(directory: string, options?: {})
squared.system.saveResourceDrawableImage(filename?: string, options?: {})
squared.system.writeResourceDrawableImage()
squared.system.copyResourceRawVideo(directory: string, options?: {})
squared.system.saveResourceRawVideo(filename?: string, options?: {})
squared.system.writeResourceRawVideo()
squared.system.copyResourceRawAudio(directory: string, options?: {})
squared.system.saveResourceRawAudio(filename?: string, options?: {})
squared.system.writeResourceRawAudio()
squared.system.customize(squared.settings.targetAPI, 'Button', {
android: {
minWidth: '35px',
minHeight: '25px'
}
});
squared.settings.outputArchiveFormat = '7z';
squared.saveToArchive('archive1', {
assets: [
{
pathname: 'app/src/main/res/drawable',
filename: 'ic_launcher_background.xml',
uri: 'http://localhost:3000/examples/common/ic_launcher_background.xml',
compress: [{ format: 'gz', level: 9 }, { format: 'br' }, { format: 'bz2' }, { format: 'lzma' }, { format: 'zstd' }, { format: 'lz4' }]
}
],
exclusions: {
pathname: ['app/build', 'app/libs'],
filename: ['ic_launcher_foreground.xml'],
extension: ['iml', 'pro'],
pattern: ['outputs', 'grad.+\\.', '\\.git']
}
});
CHROME: Public Methods
await chrome.getElementById(value: string, cache?: boolean)
await chrome.querySelector(value: string, cache?: boolean)
await chrome.querySelectorAll(value: string, cache?: boolean)
await chrome.getElement(element: HTMLElement, cache?: boolean)
chrome.saveAsWebPage(filename?: string, options?: {}): void
Public System Methods
The system methods querySelector and querySelectorAll can also be called from every Node object and provide the same functionality as the similarly named DOM methods.
squared.system.getElementById(value: string, cache?: boolean)
squared.system.querySelector(value: string, cache?: boolean)
squared.system.querySelectorAll(value: string, cache?: boolean)
squared.system.getElement(element: HTMLElement, cache?: boolean)
squared.system.getElementMap()
squared.system.clearElementMap()
squared.system.copyHtmlPage(directory: string, options?: {})
squared.system.copyScriptAssets(directory: string, options?: {})
squared.system.copyLinkAssets(directory: string, options?: {})
squared.system.copyImageAssets(directory: string, options?: {})
squared.system.copyVideoAssets(directory: string, options?: {})
squared.system.copyAudioAssets(directory: string, options?: {})
squared.system.copyFontAssets(directory: string, options?: {})
squared.system.saveHtmlPage(filename?: string, options?: {})
squared.system.saveScriptAssets(filename?: string, options?: {})
squared.system.saveLinkAssets(filename?: string, options?: {})
squared.system.saveImageAssets(filename?: string, options?: {})
squared.system.saveVideoAssets(filename?: string, options?: {})
squared.system.saveAudioAssets(filename?: string, options?: {})
squared.system.saveFontAssets(filename?: string, options?: {})
ALL: Excluding Procedures / Applied Attributes
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 in cases when an extension has their custom attributes overwritten.
<div data-exclude-section="DOM_TRAVERSE | EXTENSION | RENDER | ALL"
data-exclude-procedure="LAYOUT | ALIGNMENT | OPTIMIZATION | CUSTOMIZATION | ACCESSIBILITY | LOCALIZATION | ALL"
data-exclude-resource="BOX_STYLE | BOX_SPACING | FONT_STYLE | VALUE_STRING | IMAGE_SOURCE | ASSET | ALL">
</div>
<div>
<span data-exclude-resource="FONT_STYLE">content</span>
<input id="cb1" type="checkbox" data-exclude-procedure="ACCESSIBILITY"><label for="cb1">checkbox text</label>
</div>
ALL: Extension Configuration (example: android)
Layout rendering can also 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.
<script src="/dist/extensions/android.widget.coordinator.min.js"></script>
<script src="/dist/extensions/android.widget.menu.min.js"></script>
<script src="/dist/extensions/android.widget.toolbar.min.js"></script>
<script>
squared.configure('android.widget.toolbar', {
'elementId': {
appBar: {
android: {
theme: '@style/ThemeOverlay.AppCompat.Dark.ActionBar'
}
}
}
});
class Sample extends squared.base.Extension {
constructor(name, framework = 0, options = {}) {
super(name, framework, options);
}
}
const sample = new Sample('your.namespace.sample', 0, { });
squared.include(sample);
</script>
ALL: Layouts and binding expressions (example: android)
ViewModel data can be applied to most HTML elements using the dataset attribute.
squared.setViewModel({
import: ['java.util.Map', 'java.util.List'],
variable: [
{ name: 'user', type: 'com.example.User' },
{ name: 'list', type: 'List<String>' },
{ name: 'map', type: 'Map<String, String>' },
{ name: 'index', type: 'int' },
{ name: 'key', type: 'String' }
]
});
Two additional output parameters are required with the "data-viewmodel" prefix.
data-viewmodel-{namespace}-{attribute} -> data-viewmodel-android-text
<div>
<label>Name:</label>
<input type="text" data-viewmodel-android-text="user.firstName" />
<input type="text" data-viewmodel-android-text="user.lastName" />
</div>
<layout>
<data>
<import type="java.util.Map" />
<import type="java.util.List" />
<variable name="user" type="com.example.User" />
<variable name="list" type="List<String>" />
<variable name="map" type="Map<String, String>" />
<variable name="index" type="int" />
<variable name="key" type="String" />
</data>
<LinearLayout>
<TextView
android:text="Name:" />
<EditText
android:inputType="text"
android:text="@{user.firstName}" />
<EditText
android:inputType="text"
android:text="@{user.lastName}" />
</LinearLayout>
</layout>
ALL: Custom Attributes (example: android)
System or extension generated attributes can be overridden preceding final output. They will only be visible on the declared framework.
data-{framework}-attr-{namespace}? -> default: "android"
<div
data-android-attr="layout_width::match_parent;layout_height::match_parent"
data-android-attr-app="layout_scrollFlags::scroll|exitUntilCollapsed">
</div>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed" />
ALL: Redirecting Output Location
It is sometimes necessary to append elements into other containers when trying to design a UI which will look identical on the target device. Redirection will fail if the target "location" is not a block/container element.
<div>
<span>Item 1</span>
<span data-target="location">Item 2</span>
<span data-target="location" data-target-index="1">Item 3</span>
<div>
<ul id="location">
<li>Item 4</li>
<li>Item 5</li>
</ul>
<LinearLayout>
<TextView>Item 1</TextView>
</LinearLayout>
<LinearLayout>
<TextView>Item 4</TextView>
<TextView>Item 3</TextView>
<TextView>Item 5</TextView>
<TextView>Item 2</TextView>
</LinearLayout>
Using "target" into a ConstraintLayout or RelativeLayout container will not include automatic positioning.
ALL: Jimp (node-express) / ImageJ (squared-apache)
Image conversion can be achieved using the mimeType property in a RequestAsset object. The supported formats are:
- png
- jpeg
- bmp
- gif (node-express: readonly)
- tiff (node-express: readonly)
{saveAsExtension}:image/{format}
const options = {
assets: [
{
pathname: 'images',
filename: 'pencil.png',
mimeType: 'jpeg:image/png',
uri: 'http://localhost:3000/demos/images/pencil.png'
},
{
pathname: 'images',
filename: 'pencil.png',
mimeType: 'bmp:image/png',
uri: 'http://localhost:3000/demos/images/pencil.png'
}
]
};
Placing an @ symbol (@png:image/jpeg) before the mime type will remove the original file from the package. The % symbol (%png:image/jpeg) will choose the smaller of the two files. You can also use these commands in the Android framework with the setting "convertImages".
ANDROID: Layout Includes / Merge Tag
Some applications can benefit from using includes or merge tags to share common templates. Merge is the default behavior and can be disabled using the "false" attribute value. Nested includes are also supported.
<div>
<div>Item 1</div>
<div data-android-include="filename1" data-android-include-merge="false">Item 2</div>
<div>Item 3</div>
<div data-android-include-end="true">Item 4</div>
<div data-android-include="filename2" data-android-include-end="true">Item 5</div>
</div>
<LinearLayout>
<TextView>Item 1</TextView>
<include layout="@layout/filename1" />
<include layout="@layout/filename2" />
</LinearLayout>
<merge>
<TextView>Item 2</TextView>
<TextView>Item 3</TextView>
<TextView>Item 4</TextView>
</merge>
<TextView>Item 5</TextView>
The attributes "android-include" and "android-include-end" can only be applied to elements which share the same parent container. See /demos/gradient.html for usage instructions.
ANDROID: SVG animations with CSS/SMIL
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 you also have to "start" the animation in MainActivity.java.
import android.graphics.drawable.Animatable;
android.widget.ImageView imageView1 = findViewById(R.id.imageview_1);
if (imageView1 != null) {
Animatable animatable = (Animatable) imageView1.getDrawable();
animatable.start();
}
ANDROID: Extension Widgets
See /android/widget/*.html for usage instructions in the squared-apache https://github.com/anpham6/squared-apache project.
-
android.external
-
android.substitute
-
android.constraint.guideline
-
android.widget.coordinator
-
android.widget.floatingactionbutton
-
android.widget.menu
-
android.widget.bottomnavigation
-
android.widget.toolbar
-
android.widget.drawer
LICENSE
MIT