Neighbour Problem Manager

    squared
    TypeScript icon, indicating that this package has built-in type declarations

    4.0.5 • Public • Published

    squared 4.0

    Installation (global js variable: squared)

    Option #1 - Node.js

    GitHub

    > git clone https://github.com/anpham6/squared
    > cd squared
    
    > npm install
    
    > npm run build
      <!-- OR -->
    > npm run dev-build
    
    > cd ..
    > git clone https://github.com/anpham6/squared-express
    > cd squared-express
    
    > npm install
    
    > npm run prod
    > npm run deploy
      <!-- OR -->
    > npm run dev

    NPM

    > npm install squared  
    > cd node_modules/squared

    GitHub & NPM

    <!-- npm run express-rw (read/write) -->
    
    > squared.[json|yml] (configure)  
    > node serve.js [--help]
    
    > http://localhost:3000

    Option #2 - Browser

     * https://unpkg.com/squared/dist/squared.min.js
     * https://unpkg.com/squared/dist/squared.base-dom.min.js
     * https://unpkg.com/squared/dist/vdom.framework.min.js

     * https://unpkg.com/squared/dist/squared.min.js
     * https://unpkg.com/squared/dist/vdom-lite.framework.min.js

    ALL: Usage

    Library files are in the /dist folder. A minimum of two files are required to run squared.

    1. squared
    2. squared-base - required: except vdom-lite
    3. squared-svg - optional
    4. framework (e.g. android | chrome | vdom | vdom-lite)
    5. extensions - optional

    Usable combinations: 1-2-4 + 1-2-4-5 + 1-2-3-4-5 + 1-vdom-lite

    File bundles for common combinations are available in the /dist/bundles folder and do not require a call to setFramework.

    Example: android

    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. Using "parseDocumentSync" is not recommended when your page has either images or fonts.

    <script src="/dist/squared.min.js"></script>
    <script src="/dist/squared.base.min.js"></script>
    <script src="/dist/squared.svg.min.js"></script> <!-- Optional -->
    <script src="/dist/android.framework.min.js"></script>
    <script>
        squared.settings.targetAPI = 31; // Optional
    
        document.addEventListener("DOMContentLoaded", async () => {
            squared.setFramework(android, /* settings */);
    
            await squared.parseDocument(): Node // document.body (default)
            // OR
            await squared.parseDocument(/* HTMLElement */, /* "fragment-id" */, /* ...etc */): Node[]
            // OR
            await squared.parseDocument(
                { // Custom settings do not affect other layouts
                    element: document.body,
                    projectId: "sqd1", // Default is "_" (optional) (v4.0)
                    enabledMultiline: false,
                    enabledSubstitute: true,
                    include: ["android.substitute"], // Automatically removed after finalize
                    exclude: ["squared.list", "squared.grid"], // Disabled only during parseDocument
                    customizationsBaseAPI: -1,
                    observe(mutations, observer, settings) { // Uses MutationObserver
                        squared.reset(); // Required when calling "parseDocument" after a File action
                        squared.parseDocument(settings).then(() => {
                            squared.copyTo("/path/project", { modified: true } /* optional */).then(response => console.log(response));
                        });
                    },
                    afterCascade(sessionId, node) { /* Restore previous state */ },
                    beforeRender(layout: LayoutUI<NodeUI>) { /* Edit initial values */ },
                    afterFinalize(node: NodeUI) { /* Edit controller values */ }
                },
                { // Only "element" is required
                    element: "fragment-1",
                    projectId: "sqd1", // Implicit
                    pathname: "app/src/main/res/layout-land",
                    filename: "fragment.xml",
                    baseLayoutAsFragment: true, // ["com.example.fragment", "fragment-tag"]
                    beforeCascade(sessionId) {
                        document.getElementById("fragment-id").style.display = "block"; // Use inline styles
                    }
                }
            );
            await squared.parseDocument(
                {
                    element: "fragment-2",
                    projectId: "sqd2", // Explicit
                    enabledFragment: true,
                    fragmentableElements: [
                        { selector: "main", name: "com.example.fragment", filename: "fragment.xml" }, // document.querySelector
                        "main > article", // document.querySelectorAll (declarative double nested fragments are invalid)
                    ],
                    options: {
                        "android.resource.fragment": {
                            dynamicNestedFragments: true // FragmentContainerView or FrameLayout as the container (name and tag are ignored)
                        }
                    }
                }
            );
            // OR - Chromium 73
            squared.prefetch("css").then(() => squared.parseDocument()); // Cross-origin support
            Promise.all(
                squared.prefetch("css", true), // All stylesheets
                squared.prefetch("css", "./undetected.css", element.shadowRoot),
                squared.prefetch("svg", "http://embedded.example.com/icon.svg", "../images/android.svg")
            )
            .then(() => squared.parseDocument());
    
            // Modify attributes
    
            const body = squared.findDocumentNode(document.body);
            body.android("layout_width", "match_parent");
            body.lockAttr("android", "layout_width"); // Optional
    
            await squared.close(/* projectId */); // Next call to parseDocument will reset project (optional)
    
            // File actions - implicitly calls "close"
    
            await squared.save(/* "sqd1" */ ); // Uses defaults from settings
            // OR
            await squared.saveAs(/* archive filename */, /* options from squared-express */); // { projectId: "sqd1" } (v4.0)
            // OR
            await squared.copyTo(/* directory */, /* options */);
            await squared.copyTo(/* directory */, { modified: true }); // Can be used with observe (optional)
            // OR
            await squared.appendTo(/* archive location */, /* options */);
    
            // Other features
    
            squared.observe();
            // OR
            await squared.observeSrc(
                "link[rel=stylesheet]", // HTMLElement
                (ev, element) => {
                    squared.reset();
                    squared.parseDocument().then(() => squared.copyTo("/path/project"));
                },
                { port: 8080, secure: false, action: "reload" /* "hot" */, expires: "1h" } // squared.json: "observe" (optional)
            );
    
            squared.reset(/* projectId */); // Start new "parseDocument" session (optional)
        });
    </script>

    NOTE: Calling "save" or "copy" 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.

    Example: chrome

    You have the same features as VDOM framework but you can also bundle assets using HTML syntax and the provided Express server. It is adequate for most applications and gives you the ability to see your application first and to build it last.

    <script src="/dist/squared.min.js"></script>
    <script src="/dist/squared.base.min.js"></script>
    <script src="/dist/chrome.framework.min.js"></script>
    <script>
        document.addEventListener("DOMContentLoaded", async () => {
            squared.setFramework(chrome, /* settings */);
    
            await squared.save(); // Uses defaults from settings
            // OR
            await squared.saveAs(/* archive filename */, /* options from squared-express + squared-functions */);
            // OR
            await squared.copyTo(/* directory */, /* options */);
            // OR
            await squared.appendTo(/* archive location */, /* options */);
    
            // Observe
    
            await squared.copyTo(/* directory */, { useOriginalHtmlPage: false, observe: /* Same as Android */ | true /* Auto-reload */ }).then(() => squared.observe());
        });
    </script>

    Example: vdom

    VDOM is a minimal framework (47kb gzipped) and is especially useful for debugging. The "lite" version is about half the bundle size and is recommended for most browser applications.

    <script src="/dist/squared.min.js"></script>
    <script src="/dist/squared.base-dom.min.js"></script>
    <script src="/dist/vdom.framework.min.js"></script>
    <script>
        document.addEventListener("DOMContentLoaded", async () => {
            squared.setFramework(vdom, /* settings */);
    
            const element = squared.querySelector("body", true /* synchronous */);
            // OR
            const elements = await squared.querySelectorAll("*");
            // OR
            const element = squared.fromElement(document.body, true /* synchronous */);
            // OR
            const elements = await squared.getElementById("content-id").querySelectorAll("*");
        });
    </script>

    There are ES2017 minified versions (*.min.js) and also ES2017 non-minified versions. Browsers without ES2017 are not being supported to fully take advantage of async/await.

    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.

    Gulp installation is required in order to use "outputTasks". Further instructions can be found in the squared-functions repository.

    Example: android

    squared.settings = {
        targetAPI: 31,
        resolutionDPI: 160, // 320dpi = 2560x1600
        resolutionScreenWidth: 1280,
        resolutionScreenHeight: 800,
        framesPerSecond: 60,
        supportRTL: true,
        supportNegativeLeftTop: true,
        preloadImages: true,
        preloadFonts: true,
        preloadLocalFonts: true, // v4.0 (Chromium 103)
        preloadCustomElements: true,
        enabledSVG: true,
        enabledMultiline: true,
        enabledViewModel: true,
        enabledIncludes: false,
        enabledFragment: false,
        enabledSubstitute: false,
        enabledCompose: false,
        dataBindableElements: [], // { selector, attr, expression, namespace?, twoWay? } (see Data Binding section)
        includableElements: [], // { selectorStart, selectorEnd, pathname?, filename?, merge?, viewModel? }
        substitutableElements: [], // { selector, tag, tagChild?, renderChildren?, autoLayout? }
        fragmentableElements: [], // selector | ExtensionFragmentElement
        composableElements: [], // selector or property (see Jetpack Compose section)
        baseLayoutAsFragment: false | true | "fragment-name" | ["fragment-name", "fragment-tag"] | { selector, pathname?, filename?, name?, tag? }: ExtensionFragmentElement,
        baseLayoutToolsIgnore: "", // "TooManyViews, HardcodedText"
        fontMeasureAdjust: 0.75, // wider < 0 | thinner > 0 (data-android-font-measure-adjust)
        lineHeightAdjust: 1.1, // shorter < 1 | taller > 1 (data-android-line-height-adjust)
        preferMaterialDesign: false,
        createDownloadableFonts: true,
        createElementMap: false,
        pierceShadowRoot: true,
        lockElementSettings: true, // Past versions were unocked (v4.0)
        customizationsBaseAPI: 0, // All: 0 | None: -1 (Multiple: [0, 31])
        removeDeprecatedAttributes: false, // Keep: ["enabled", "singleLine"]
        removeUnusedResourceViewId: false,
        idNamingStyle: "android" | "html", // Use element tagName
        customizationsOverwritePrivilege: true,
        outputMainFileName: "activity_main.xml",
        outputFragmentFileName: "fragment_main.xml",
        /* Project v4.0 - parseDocument (first only) */
        manifestPackage: "", // OR: RequestData<{ namespace: "android.application.id" }>
        manifestLabelAppName: "android",
        manifestThemeName: "AppTheme",
        manifestParentThemeName: "Theme.AppCompat.Light.NoActionBar",
        manifestActivityName: ".MainActivity",
        outputDocumentEditing: true,
        outputDocumentCSS: [], // CSS properties to be processed at host server (e.g. "boxShadow")
        outputDirectory: "app/src/main",
        createManifest: false, // Update AndroidManifest.xml
        createBuildDependencies: false | true | "ktx", // Update build.gradle
    
        // Not customizable with parseDocument
        builtInExtensions: [
            "squared.accessibility",
            "android.delegate.background",
            "android.delegate.negative-x",
            "android.delegate.positive-x",
            "android.delegate.max-width-height",
            "android.delegate.percent",
            "android.delegate.scrollbar",
            "android.delegate.radiogroup",
            "android.delegate.multiline",
            "squared.relative",
            "squared.css-grid",
            "squared.flexbox",
            "squared.table",
            "squared.column",
            "squared.list",
            "squared.grid",
            "squared.sprite",
            "squared.whitespace",
            "android.resource.background",
            "android.resource.svg",
            "android.resource.strings",
            "android.resource.fonts",
            "android.resource.dimens",
            "android.resource.styles",
            "android.resource.data",
    
            /* EXCLUDED (breaks layout) */
            "android.resource.includes", // enabledIncludes
            "android.substitute", // enabledSubstitute
            "android.resource.fragment", // enabledFragment
            "jetpack.compose.view" // enabledCompose
        ],
        compressImages: false, // TinyPNG API Key <https://tinypng.com/developers>
        convertImages: "", // png | jpeg | webp | gif | bmp
        showAttributes: true,
        convertPixels: "dp" | "pt" | "in" | "mm",
        insertSpaces: 4,
        showErrorMessages: false,
        formatUUID: "8-4-4-4-12",
        formatDictionary: "0123456789abcdef",
        outputDocumentHandler: "android",
        outputEmptyCopyDirectory: false, // Sub directories within target directory (OR: RequestData<{ emptyDir: false }>)
        outputTasks: {}, // { "**/drawable/*.xml": { handler: "gulp", task: "minify" } }
        outputWatch: {}, // { "**/drawable/*.png": true, "**/drawable/*.jpg": { interval: 1000, expires: "2h" } } (only raw assets)
        outputArchiveName: "android-xml",
        outputArchiveFormat: "zip", // zip | tar | 7z | gz
        outputArchiveCache: false // Downloadable URL in ResponseData<downloadUrl>
    };
    
    // Optional
    squared.settings = {
        outputConfigName: "sqd.config",
        observePort: 8080,
        observeSecurePort: 8443,
        observeExpires: "1h", // Server defaults will be used
        broadcastPort: 3080, // v4.0
        broadcastSecurePort: 3443
    }

    Example: chrome

    squared.settings = {
        preloadImages: false,
        preloadFonts: false,
        preloadLocalFonts: false,
        preloadCustomElements: false,
        excludePlainText: true,
        createElementMap: true,
        pierceShadowRoot: true,
    
        // Not customizable with parseDocument
        builtInExtensions: [],
        showErrorMessages: false,
        webSocketPort: 80,
        webSocketSecurePort: 443,
        formatUUID: "8-4-4-4-12",
        formatDictionary: "0123456789abcdef",
        outputDocumentHandler: "chrome",
        outputEmptyCopyDirectory: false,
        outputTasks: {}, // { "*.js": [{ handler: "gulp", task: "minify" }, { handler: "gulp", task: "beautify" }] }
        outputWatch: {}, // { "*": true }
        outputArchiveName: "chrome-data",
        outputArchiveFormat: "zip",
        outputArchiveCache: false
    };
    
    // Optional (Same as Android)

    Example: vdom

    squared.settings = {
        createElementMap: true,
        pierceShadowRoot: false,
    
        // Not customizable with parseDocument
        builtInExtensions: [],
        showErrorMessages: false
    };

    ALL: Local Storage

    Custom named user settings per framework can be saved to local storage and reloaded across all pages in the same domain. Extensions are configured using the same procedure.

    // Save
    squared.setFramework(android, { compressImages: true }, "android-example");
    
    // Load
    squared.setFramework(android, "android-example", true); // "cache" can also be used as the last argument
    // Save
    await squared.copyTo("/path/project", { /* options will be saved */ }, "copy-example", true);  // "overwrite" is required when using local storage
    
    // Load
    await squared.copyTo("/path/project", { /* takes precedence */ }, "http://localhost:3000/copy-to/base-config.json"); // Object.assign({ base-config.json }, options)
    await squared.copyTo("/path/project", { /* takes precedence */ }, "copy-example"); // Object.assign({ copy-example }, options)
    
    await squared.copyTo("/path/project", "http://localhost:3000/copy-to/base-config.json"); // options = { base-config.json }
    await squared.copyTo("/path/project", "copy-example"); // options = { copy_example }

    ALL: Public Properties and Methods

    There is no official documentation for this project. The entire source code including TypeScript definitions are available on GitHub if you need further clarification.

    .settings // See user preferences section
    
    setFramework(app: {}, options?: PlainObject, setting?: string, cache?: boolean) // Install application interpreter
    setFramework(app: {}, loadName: string, cache?: boolean) // Load settings from Local Storage
    
    // http - hostname(:port)? | https - hostname:443
    setHostname(value: string /* http(s)?://hostname(:port)? */) // Use another cors-enabled server for processing files (--cors <origin> | squared.json <https://github.com/expressjs/cors>)
    
    setEndpoint(name: string, value: string) // Set pathname for serverless cloud functions (ASSETS_COPY | ASSETS_ARCHIVE | LOADER_DATA | WEBSOCKET_OBSERVE)
    setLocalAddress(...values: string[]) // Additional hostnames which are interpreted as localhost (e.g. 127.0.0.1)
    
    prefetch(type: "css" | "javascript" | "image" | "svg", all?: boolean, ...targets: unknown[]) // Cross-origin support for CSS
    
    parseDocument(...elements: (HTMLElement | string | ElementSettings)[]) // See installation section (Promise)
    parseDocumentSync(...elements: (HTMLElement | string | ElementSettings)[]) // Skips preloadImages and preloadFonts (synchronous)
    
    latest(count?: number) // Most recent parseDocument session ids (1 newest / -1 oldest: string, other: string[])
    
    save(projectId?: string) // Save current session to a new archive using default settings
    close(projectId?: string) // Close current session (optional)
    reset(projectId?: string) // Clear cache and reopen new session
    clear() // Clear all data stored in memory (v4.0)
    
    toString() // Current framework loaded
    
    add(...names: (string | Extension | ExtensionRequestObject)[]) // See extension configuration section
    remove(...names: (string | Extension)[]) // Remove extensions by namespace or control
    get(...names: (string | Extension)[]) // Retrieve extensions by namespace or control
    attr(name: string | Extension, attrName: string, value?: unknown) // Set or get extension options attribute value
    apply(name: string | Extension, options: PlainObject, setting?: string) // See extension configuration section
    
    extend(functionMap: {}, framework?: /* 0 - ALL | 1 - vdom | 2 - android | 4 - chrome */) // Add extension functions and properties to Node prototype
    
    observe(value?: boolean | MutationObserverInit) // Start after DOM and third-party libraries initialization (https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver/observe)
    broadcast(callback: BroadcastMessageCallback, options: FileBroadcastOptions | string) // Redirect stdout messages to DevTools console (v4.0)
    
    // Promise (Recommended "cache": createElementMap - true)
    
    getElementById(value: string, sync?: boolean, cache?: boolean) // sync - false | cache - true (default)
    querySelector(value: string, sync?: boolean, cache?: boolean)
    querySelectorAll(value: string, sync?: boolean, cache?: boolean)
    
    fromElement(element: HTMLElement | string, sync?: boolean, cache?: boolean) // sync - false | cache - false (default)
    fromNode(node: Node, sync?: boolean, cache?: boolean)
    findDocumentNode(element: HTMLElement | string /* querySelector | elementId | controlId */, all?: boolean) // Use before saving to modify internal Node attributes
    
    observeSrc(element: HTMLElement | string /* querySelector */, callback: WebSocketMessageChange, options?: FileObserveOptions) // Can be used to watch any element with externally hosted files (src/href)
    observeSrc(element: HTMLElement | string, options: FileObserveOptions) // Uses location.reload (reload - true)

    Packaging methods will return a Promise and require either node-express or squared-apache installed. These features are not supported when the framework is VDOM.

    saveAs(filename: string, options?: {}, setting?: string, overwrite?: boolean) // Save current session as a new archive
    saveFiles(filename: string, options: {}, setting?: string, overwrite?: boolean) // Create new archive from FileAsset[]
    
    // Required (local archives): --disk-read | --unc-read | --access-all (command-line)
    
    appendTo(pathname: string, options?: {}, setting?: string, overwrite?: boolean) // Create new archive from a preexisting archive and current session
    appendFiles(pathname: string, options: {}, setting?: string, overwrite?: boolean) // Create new archive from a preexisting archive and FileAsset[]
    
    // Required (all): --disk-write | --unc-write | --access-all (command-line)
    
    copyTo(pathname: string, options?: {}, setting?: string, overwrite?: boolean) // Copy current session to local 
    copyFiles(pathname: string, options: {}, setting?: string, overwrite?: boolean) // Copy FileAsset[] to local 

    ALL: Extending Node object

    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.

    squared.extend({
        _id: 1,
        altId: {
            get() {
                return this._id;
            },
            set(value) {
                this._id += value;
            }
        },
        customId: {
            value: 2,
            configurable: false,
            enumerable: false
        },
        addEvent(eventName, callback) {
            this.element.addEventListener(eventName, callback);
        }
    });
    
    const body = await squared.fromElement(document.body);
    body.altId = 2; // body.altId: 3
    body.addEvent("click", function (ev) {
        this.classList.toggle("example");
    });

    ANDROID: Public Methods

    android.setViewModel(data: {}, sessionId?: string) // Object data for layout bindings
    android.setViewModelByProject(data: {}, projectId?: string) // v4.0
    android.removeObserver(element: HTMLElement) // Disconnect an observed element from "parseDocument"
    android.addXmlNs(name: string, uri: string) // Add global namespaces for third-party controls
    android.addDependency(group: string, name: string, version: string) // Add application dependency implementation (build.gradle)
    android.addDependencyByProject(projectId: string, group: string, name: string, version: string) // v4.0
    android.customize(build: number, tagNameOrWidget: string, options: {}) // Global attributes applied to specific views
    android.loadCustomizations(name: string) // Load customizations from Local Storage
    android.saveCustomizations(name: string) // Save "customize" data into Local Storage (includes xmlns)
    android.resetCustomizations() // All session customizations are deleted
    android.addFontProvider(authority: string, package: string, certs: string[], webFonts: string | {}) // Add additional Web fonts (Google Fonts already included)
    android.setResolutionByDeviceName(value: string) // Settings prefixed with "resolution" (e.g. Pixel C)
    android.getLocalSettings() // Modify controller styles and parsing rules
    // NOTE: squared.settings.targetAPI is always parsed (Except: customizationsBaseAPI = -1)
    
    android.customize(android.lib.constant.BUILD_VERSION.ALL /* 0 */, "Button", {
        android: {
            minWidth: "35px",
            minHeight: "25px"
        },
        "_": { // Non-namespaced attributes
            style: "@style/Widget.MaterialComponents.Button.TextButton"
        }
    });
    
    android.customize(android.lib.constant.BUILD_VERSION.KITKAT /* 19 */, "svg", {
        android: {
            "[src]": "app:srcCompat" // Change namespace to "app"
        }
    });
    
    // Local Storage
    android.saveCustomizations("customize-example"); // Save at least once in one layout
    
    android.loadCustomizations("customize-example"); // Load in any other layout
    android.addXmlNs("tools", "http://schemas.android.com/tools");
    
    android.customize(16 /* Jelly Bean */, "ImageView", {
        tools: {
            ignore: "ContentDescription",
            targetApi: "16"
        }
    });

    ANDROID: Static Methods

    Project resources can include additional values that are required during compilation. TypeScript definitions are available in the "types/android" directory.

    squared.parseDocument().then(node => {
        const resourceId = node.localSettings.resourceId;
        android.base.Resource.addString(resourceId, value, /* name */);
        android.base.Resource.addArray(resourceId, name, items);
        android.base.Resource.addColor(resourceId, color);
        android.base.Resource.addDimen(resourceId, name, value);
        android.base.Resource.addTheme(resourceId, theme);
        squared.save();
    });

    ANDROID: Data Binding

    View model data can be applied to most HTML elements using the dataset attribute. Different view models can be used for every "parseDocument" session.

    Leaving the sessionId empty sets the default view model for the entire project.

    // NOTE: latest(undefined = 1): string (1: most recent sessionId | -1: first sessionId)
    
    squared.parseDocument(/* "mainview" */, /* "subview" */).then(nodes => {
        const sessions = squared.latest(2); // ["1", "2", "3"] => ["2", "3"]
        android.setViewModel(
            {
                import: ["java.util.Map", "java.util.List"],
                variable: [
                    { name: "user", type: "com.example.User" },
                    { name: "list", type: "List&lt;String>" },
                    { name: "map", type: "Map&lt;String, String>" },
                    { name: "index", type: "int" },
                    { name: "key", type: "String" }
                ]
            },
            nodes[0].sessionId || sessions[0]
        );
        android.setViewModel(
            {
                import: ["java.util.Map"],
                variable: [
                    { name: "map", type: "Map&lt;String, String>" }
                ]
            },
            nodes[1].sessionId || sessions[1] // Used when there are multiple layouts (optional)
        );
    });
    
    squared.parseDocument({
        element: "main",
        enabledViewModel: true, // Optional
        dataBindableElements: [
            {
                selector: "#first_name",
                namespace: 'android', // "android" is default
                attr: 'text',
                expression: "user.firstName"
            },
            {
                selector: "#last_name",
                attr: 'text',
                expression: "user.lastName"
            },
            {
                selector: "#remember_me",
                attr: 'checked',
                expression: "user.rememberMe",
                twoWay: true
            }
        ],
        data: {
            viewModel: {
                import: /* Same */,
                variable: [/* Same */]
            }
        } 
    });
    
    squared.save();

    Inlining is also supported and might be more convenient for simple layouts. JavaScript is recommended when you are calling "parseDocument" multiple times.

    data-viewmodel-{namespace}-{attribute} -> data-viewmodel-android-text

    These two additional output parameters are required when using the "data-viewmodel" prefix.

    <div id="main">
        <label>Name:</label>
        <input id="first_name" type="text" data-viewmodel-android-text="user.firstName" />
        <input id="last_name" type="text" data-viewmodel-android-text="user.lastName" />
        <input id="remember_me" type="checkbox" data-viewmodel-android-checked="=user.rememberMe" /> <!-- "=" for two-way binding -->
    </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&lt;String&gt;" /> <!-- List<String> -->
            <variable name="map" type="Map&lt;String, String&gt;" /> <!-- Map<String, String> -->
            <variable name="index" type="int" />
            <variable name="key" type="String" />
        </data>
        <LinearLayout android:id="@+id/main">
            <TextView android:text="Name:" />
            <EditText
                android:id="@+id/first_name"
                android:inputType="text"
                android:text="@{user.firstName}" />
            <EditText
                android:id="@+id/last_name"
                android:inputType="text"
                android:text="@{user.lastName}" />
            <CheckBox
                android:id="@+id/remember_me"
                android:checked="@={user.rememberMe}" />
        </LinearLayout>
    </layout>

    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 id="item1">Item 1</div>
        <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>
        <div id="item3">Item 3</div>
        <div id="item4" data-android-include-end="true">Item 4</div>
        <div id="item5" data-android-include="filename2" data-android-include-end="true" data-android-include-viewmodel="exampleData">Item 5</div> <!-- viewModel (optional) -->
    </div>
    // v4.0
    
    android.setViewModelByProject({ variable: [{ name: "exampleData", type: "com.example.ExampleData" }] }, "sqd1"); // Default is "_" (optional)
    
    squared.parseDocument({
        element: document.body,
        projectId: "sqd1", // Affects all layouts in same project
        enabledIncludes: true,
        includableElements: [
            {
                selectorStart: '#item2',
                selectorEnd: '#item4',
                pathname: 'app/src/main/res/layout-land',
                filename: 'filename1.xml',
                merge: true // Multiple elements will auto-merge (optional)
            },
            {
                selectorStart: '#item5',
                selectorEnd: '#item5',
                filename: 'filename2',
                viewModel: "exampleData" // One element only (merge=false)
            }
        ]
    });

    NOTE: By sessionId has precedence when associating a view model.

    <LinearLayout>
        <TextView>Item 1</TextView>
        <include layout="@layout/filename1" />
        <include layout="@layout/filename2" app:exampleData="@{exampleData}" />
    </LinearLayout>
    <!-- res/layout/activity_main.xml -->
    
    <merge>
        <TextView>Item 2</TextView>
        <TextView>Item 3</TextView>
        <TextView>Item 4</TextView>
    </merge>
    <!-- res/layout-land/filename1.xml -->
    
    <layout>
        <data>
            <variable name="exampleData" type="com.example.ExampleData" />
        </data>
        <TextView>Item 5</TextView>
    </layout>
    <!-- res/layout/filename2.xml -->

    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.

    NOTE: "data-pathname-android" AND "data-filename-android" can also be used with any "parseDocument" base element.

    ANDROID: Redirecting Output Location

    Sometimes it is necessary to append elements into other containers when trying to design a UI which will look identical on the Android device. Redirection will fail if the target "location" is not a block/container element.

    <div>
        <span>Item 1</span>
        <span data-android-target="location">Item 2</span>
        <span data-android-target="location" data-android-target-index="1">Item 3</span>
    <div>
    <ul id="location">
        <li>Item 4</li>
        <li>Item 5</li>
        <!-- span -->
    </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.

    ANDROID: Custom Attributes

    System or extension generated attributes can be overridden preceding finalization. They will only be visible on the declared framework.

    data-android-attr-{namespace}? -> default: "android"

    <div id="customId"
        data-android-attr="layout_width::match_parent;layout_height::match_parent"
        data-android-attr-app="layout_scrollFlags::scroll|exitUntilCollapsed">
    </div>
    <LinearLayout
        android:id="@+id/customId"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_scrollFlags="scroll|exitUntilCollapsed" />
    const node = squared.findDocumentNode('customId'); // querySelector is supported
    node.android("layout_width", "match_parent");
    node.android("layout_height", "match_parent");
    node.app("layout_scrollFlags", "scroll|exitUntilCollapsed");

    ANDROID: SVG animations

    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: Jetpack Compose

    Most mobile applications do not have a deeply nested hierarchy and are generally better to implement using declarative programming.

    squared.settings.composableElements = ["main", "#content", "--boxShadow", "--height=300px"];
    squared.settings.createBuildDependencies = true; // Optional

    You can also do it using the "android.substitute" extension directly inside the HTML element.

    // android.substitute is only be used here to demonstrate using extensions
    
    squared.add("android.substitute", {
        element: {
            content: { android: { layout_width: "match_parent" } } // Optional
        }
    });
    
    const items = squared.attr("android.substitute", "viewAttributes"); // Optional
    items.push("hint", "buttonTint");
    /* OR */
    squared.attr("android.substitute", "viewAttributes", items.concat(["hint", "buttonTint"])); // Attributes to preserve (default: android.view.View)
    squared.attr("android.substitute", "attributeMapping", { "android:src": "app:srcCompat", "icon": "navigationIcon" /* android */ });
    
    squared.parseDocument({
        element: document.body,
        substitutableElements: [
            {
                selector: "#content",
                tag: "androidx.compose.ui.platform.ComposeView",
                renderChildren: false
            }
        ],
    
        // Some extensions have convenience properties
        enabledSubstitute: true,
        /* OR */
        include: ["android.substitute"]
    });
    <body>
        <header style="height: 100px"></header>
        <main id="content"
              data-use="android.substitute"
              data-android-substitute-tag="androidx.compose.ui.platform.ComposeView"
              style="height: 300px; box-shadow: 10px 5px 5px black;">
            <!-- Interior elements are not rendered -->
        </main>
        <footer style="height: 80px"></footer>
    </body>

    Compose will remove child elements by default. You can preserve them by explictly using the renderChildren property. (data-android-substitute-render-children="true")

    <div id="fragment"
        data-use="android.substitute"
        data-android-substitute-tag="androidx.fragment.app.FragmentContainerView"
        data-android-substitute-render-children="false"
        data-android-attr="name::com.github.fragment;tag::example">
        <!-- Interior elements are not rendered -->
    </div>

    You can also use "android.substitute" to create fragments within a layout similar to Compose.

    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.

    squared.parseDocument({
        element: document.body,
        include: ["android.substitute"], // OR: settings.enabledSubstitute
        substitutableElements: [
            {
                selector: "#navigation",
                tag: "com.google.android.material.tabs.TabLayout",
                tagChild: "com.google.android.material.tabs.TabItem",
                tagChildAttr: {
                    android: {
                        layout_height: "match_parent"
                    }
                },
                renderChildren: true, // Optional
                autoLayout: true
            }
        ]
    });
    <ul id="navigation"
        data-use-android="android.substitute"
        data-android-attr="layout_height::match_parent"
        data-android-substitute-tag="com.google.android.material.tabs.TabLayout"
        data-android-substitute-tag-child="com.google.android.material.tabs.TabItem"
        data-android-substitute-tag-child-attr="layout_height::match_parent"
        data-android-substitute-auto-layout="true">
        <li>TAB 1</li>
        <li>TAB 2</li>
        <li>TAB 3</li>
    </ul>
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/navigation"
        android:layout_height="match_parent"
        android:layout_width="wrap_content">
        <com.google.android.material.tabs.TabItem
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:text="@string/tab_1" />
        <com.google.android.material.tabs.TabItem
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:text="@string/tab_2" />
        <com.google.android.material.tabs.TabItem
            android:layout_height="match_parent"
            android:layout_width="wrap_content"
            android:text="@string/tab_3" />
    </com.google.android.material.tabs.TabLayout>

    ANDROID: Downloadable Fonts

    Google Fonts are pre-installed and can be used without any additional configuration.

    NOTE: WOFF2 format is supported as a local resource font.

    <!-- build.gradle -->
    dependencies {
        implementation 'androidx.appcompat:appcompat:1.4.1' <!-- createBuildDependencies = true (optional) -->
        <!-- OR -->
        implementation 'com.android.support:appcompat-v7:28.0.0'
    }
    
    <!-- AndroidManifest.xml -->
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <!-- createManifest = true (optional) -->
        <application android:theme="@style/AppTheme">
            <meta-data android:name="preloaded_fonts" android:resource="@array/preloaded_fonts" />
        </application>
    </manifest>
    // https://developers.google.com/fonts/docs/developer_api
    
    squared.setFramework(android);
    
    await android.addFontProvider(
        "com.google.android.gms.fonts",
        "com.google.android.gms",
        ["MIIEqDCCA5CgAwIBAgIJANWFuGx9007...", "MIIEQzCCAyugAwIBAgIJAMLgh0Zk..."],
        "https://www.googleapis.com/webfonts/v1/webfonts?key=1234567890" // JSON object is synchronous
    );
    
    squared.parseDocument();

    ANDROID: 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="CONSTRAINT | LAYOUT | ALIGNMENT | ACCESSIBILITY | LOCALIZATION | CUSTOMIZATION | OPTIMIZATION | 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: Broadcasting (v4.0)

    Console messages (stdout) from squared-express v2.2 can be sent to the browser through DevTools console.

    squared.broadcast(result => { console.log(result.value); }, "111-111-111"); // System messages from squared-express (optional)
    squared.broadcast(result => { console.log(result.value); }, "222-222-222"); // Messages from "sqd1" project
    
    await squared.copyTo("/path/project/sqd1", {
        projectId: "sqd1", // Optional
        log: { useColor: true }, // Chromium 99 (optional)
        broadcastId: "222-222-222"
    });

    ALL: Extension Configuration

    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.

    // Create an extension
    class Sample extends squared.base.Extension {
        options = {
            attributeName: [];
        };
    
        constructor(name, framework = 0, options = {}) { // 0 - ALL | 1 - vdom | 2 - android | 4 - chrome (framework)
            super(name, framework, options);
        }
    
        processNode(node: NodeUI) {
            const data = this.project.get(node.element, node.localSettings.projectId); // v4.0
            if (data) {
                node.each((child, index) => child.element.title = data[index]);
            }
        }
    }
    
    // Install an extension
    const sample = new Sample("widget.example.com", 0, {/* Same as configure */});
    squared.add(sample);
    // OR
    squared.add([sample, {/* config */}]);
    
    // Configure an extension
    squared.attr("widget.example.com", "attributeName", ["width", "height"]); // typeof is enforced and will only set existing attributes
    
    // Add project data (v4.0)
    const ext = squared.get("widget.example.com");
    ext.project.set(element, await fetch(url), "sqd1" /* projectId (optional) */); // Same as Map<unknown, unknown> interface with additional trailing "projectId" argument

    Some extensions have a few settings which can be configured. Usually default settings are to achieve the best overall rendering accuracy with the browser.

    LICENSE

    MIT

    Install

    npm i squared

    DownloadsWeekly Downloads

    547

    Version

    4.0.5

    License

    MIT

    Unpacked Size

    9.18 MB

    Total Files

    83

    Last publish

    Collaborators

    • anpham6