From 50f158a6ae282d49bb3cf9cc75cd364939efb2f1 Mon Sep 17 00:00:00 2001 From: Linus Lee Date: Sat, 11 May 2019 14:45:09 -0700 Subject: [PATCH] fix(render): when rendering a reflected HTML attribute, first check if the property is already set, since it may trigger setters inadvertently --- docs/torus.js.html | 1170 ++++++++++++++++--------------- samples/conway/index.min.js | 2 +- samples/graph-calc/index.min.js | 2 +- samples/gravity/index.min.js | 2 +- samples/hn-reader/index.min.js | 2 +- samples/markus/index.min.js | 2 +- samples/mondrian/index.min.js | 2 +- src/torus.js | 4 +- 8 files changed, 595 insertions(+), 591 deletions(-) diff --git a/docs/torus.js.html b/docs/torus.js.html index 3417536..bdd6400 100644 --- a/docs/torus.js.html +++ b/docs/torus.js.html @@ -282,690 +282,692 @@

torus.js annotated source

335                } else if (attrName in node) {
336                    // @debug
337                    render_debug(`Set <${next.tag}> property ${attrName} = ${next.attrs[attrName]}`);
-
338                    node[attrName] = next.attrs[attrName];
-
339                } else {
-
340                    if (next.attrs[attrName] !== previous.attrs[attrName]) {
-
341                        // @debug
-
342                        render_debug(`Set <${next.tag}> attribute "${attrName}" to "${next.attrs[attrName]}"`);
-
343                        node.setAttribute(attrName, next.attrs[attrName]);
-
344                    }
-
345                }
-
346            }
-
347
+
338                    if (previous.attrs[attrName] !== next.attrs[attrName]) {
+
339                        node[attrName] = next.attrs[attrName];
+
340                    }
+
341                } else {
+
342                    if (next.attrs[attrName] !== previous.attrs[attrName]) {
+
343                        // @debug
+
344                        render_debug(`Set <${next.tag}> attribute "${attrName}" to "${next.attrs[attrName]}"`);
+
345                        node.setAttribute(attrName, next.attrs[attrName]);
+
346                    }
+
347                }
+
348            }
+
349

For any attributes that were removed in the new JDOM, also attempt to remove them from the DOM.

-
350            for (const attrName of Object.keys(previous.attrs)) {
-
351                if (next.attrs[attrName] === undefined) {
-
352                    if (attrName in node) {
-
353                        // @debug
-
354                        render_debug(`Remove <${next.tag} property ${attrName}`);
+
352            for (const attrName of Object.keys(previous.attrs)) {
+
353                if (next.attrs[attrName] === undefined) {
+
354                    if (attrName in node) {
+
355                        // @debug
+
356                        render_debug(`Remove <${next.tag} property ${attrName}`);

null seems to be the default for most IDL attrs, but even this isn't entirely consistent. This seems like something we should fix as issues come up, not preemptively search for a cross-browser solution.

-
359                        node[attrName] = null;
-
360                    } else {
-
361                        // @debug
-
362                        render_debug(`Remove <${next.tag}> attribute ${attrName}`);
-
363                        node.removeAttribute(attrName);
-
364                    }
-
365                }
-
366            }
-
367
-
368            diffEvents(next.events, previous.events, (eventName, handlerFn) => {
-
369                // @debug
-
370                render_debug(`Set new ${eventName} event listener on <${next.tag}>`);
-
371                node.addEventListener(eventName, handlerFn);
-
372            });
-
373            diffEvents(previous.events, next.events, (eventName, handlerFn) => {
-
374                // @debug
-
375                render_debug(`Remove ${eventName} event listener on <${next.tag}>`);
-
376                node.removeEventListener(eventName, handlerFn);
-
377            });
-
378
+
361                        node[attrName] = null;
+
362                    } else {
+
363                        // @debug
+
364                        render_debug(`Remove <${next.tag}> attribute ${attrName}`);
+
365                        node.removeAttribute(attrName);
+
366                    }
+
367                }
+
368            }
+
369
+
370            diffEvents(next.events, previous.events, (eventName, handlerFn) => {
+
371                // @debug
+
372                render_debug(`Set new ${eventName} event listener on <${next.tag}>`);
+
373                node.addEventListener(eventName, handlerFn);
+
374            });
+
375            diffEvents(previous.events, next.events, (eventName, handlerFn) => {
+
376                // @debug
+
377                render_debug(`Remove ${eventName} event listener on <${next.tag}>`);
+
378                node.removeEventListener(eventName, handlerFn);
+
379            });
+
380

Render children recursively. These loops are also well optimized, since it's a hot patch of code at runtime. We memoize generated child nodes into this previous._nodes array so we don't have to perform expensive, DOM-touching operations during reconciliation to look up children of the current node in the next render pass. nodeChildren will be updated alongside enqueued DOM mutation operations.

-
385            const prevChildren = previous.children;
-
386            const nextChildren = next.children;
+
387            const prevChildren = previous.children;
+
388            const nextChildren = next.children;

Memoize length lookups.

-
388            const prevLength = prevChildren.length;
-
389            const nextLength = nextChildren.length;
+
390            const prevLength = prevChildren.length;
+
391            const nextLength = nextChildren.length;

Smaller way to check for "if either nextLength or prevLength is greater than zero"

-
391            if (nextLength + prevLength > 0) {
+
393            if (nextLength + prevLength > 0) {

Initialize variables we'll need / reference throughout child reconciliation.

-
393                const nodeChildren = previous._nodes || [];
-
394                const minLength = prevLength < nextLength ? prevLength : nextLength;
-
395
+
395                const nodeChildren = previous._nodes || [];
+
396                const minLength = prevLength < nextLength ? prevLength : nextLength;
+
397

"sync" the common sections of the two children lists.

-
397                let i = 0;
-
398                for (; i < minLength; i ++) {
-
399                    if (prevChildren[i] !== nextChildren[i]) {
-
400                        nodeChildren[i] = render(nodeChildren[i], prevChildren[i], nextChildren[i]);
-
401                    }
-
402                }
+
399                let i = 0;
+
400                for (; i < minLength; i ++) {
+
401                    if (prevChildren[i] !== nextChildren[i]) {
+
402                        nodeChildren[i] = render(nodeChildren[i], prevChildren[i], nextChildren[i]);
+
403                    }
+
404                }

If the new JDOM has more children than the old JDOM, we need to add the extra children.

-
405                if (prevLength < nextLength) {
-
406                    for (; i < nextLength; i ++) {
-
407                        // @begindebug
-
408                        if (nextChildren[i].tagName) {
-
409                            render_debug(`Add child ${printNode(nextChildren[i])}`);
-
410                        } else if (nextChildren[i].tag) {
+
407                if (prevLength < nextLength) {
+
408                    for (; i < nextLength; i ++) {
+
409                        // @begindebug
+
410                        if (nextChildren[i].tagName) {
411                            render_debug(`Add child ${printNode(nextChildren[i])}`);
-
412                        } else {
-
413                            render_debug(`Add child "${nextChildren[i]}"`);
-
414                        }
-
415                        // @enddebug
-
416                        const newChild = render(undefined, undefined, nextChildren[i]);
-
417                        opQueue.push([OP_APPEND, node, newChild]);
-
418                        nodeChildren.push(newChild);
-
419                    }
+
412                        } else if (nextChildren[i].tag) {
+
413                            render_debug(`Add child ${printNode(nextChildren[i])}`);
+
414                        } else {
+
415                            render_debug(`Add child "${nextChildren[i]}"`);
+
416                        }
+
417                        // @enddebug
+
418                        const newChild = render(undefined, undefined, nextChildren[i]);
+
419                        opQueue.push([OP_APPEND, node, newChild]);
+
420                        nodeChildren.push(newChild);
+
421                    }

If the new JDOM has less than or equal number of children to the old JDOM, we'll remove any stragglers.

-
422                } else {
-
423                    for (; i < prevLength; i ++) {
-
424                        // @begindebug
-
425                        if (prevChildren[i].tagName) {
-
426                            render_debug(`Remove child ${printNode(prevChildren[i])}`);
-
427                        } else if (prevChildren[i].tag) {
+
424                } else {
+
425                    for (; i < prevLength; i ++) {
+
426                        // @begindebug
+
427                        if (prevChildren[i].tagName) {
428                            render_debug(`Remove child ${printNode(prevChildren[i])}`);
-
429                        } else {
-
430                            render_debug(`Remove child "${prevChildren[i]}"`);
-
431                        }
-
432                        // @enddebug
+
429                        } else if (prevChildren[i].tag) {
+
430                            render_debug(`Remove child ${printNode(prevChildren[i])}`);
+
431                        } else {
+
432                            render_debug(`Remove child "${prevChildren[i]}"`);
+
433                        }
+
434                        // @enddebug

If we need to remove a child element, removing it from the DOM immediately might lead to race conditions. instead, we add a placeholder and remove the placeholder at the end.

-
437                        opQueue.push([OP_REMOVE, node, nodeChildren[i]]);
-
438                    }
-
439                    nodeChildren.splice(nextLength, prevLength - nextLength);
-
440                }
+
439                        opQueue.push([OP_REMOVE, node, nodeChildren[i]]);
+
440                    }
+
441                    nodeChildren.splice(nextLength, prevLength - nextLength);
+
442                }

Mount nodeChildren onto the up-to-date JDOM, so the next render pass can reference it.

-
443                next._nodes = nodeChildren;
-
444            }
-
445        }
-
446    }
-
447
+
445                next._nodes = nodeChildren;
+
446            }
+
447        }
+
448    }
+
449

We're done rendering the current node, so decrement the render stack counter. If we've reached the top of the render tree, it's time to flush replaced nodes to the DOM before the next frame.

-
452    if (-- render_stack === 0) {
+
454    if (-- render_stack === 0) {

runDOMOperations() can also be called completely asynchronously with utilities like requestIdleCallback, a la Concurrent React, for better responsiveness on larger component trees. This requires a modification to Torus's architecture, so that each set of DOMOperations tasks in the opQueue from one component's render call are flushed to the DOM before the next component's DOMOperations begins, for consistency. This can be achieved with a nested queue layer on top of opQueue. Here, we omit concurrency support today because it's not a great necessity where Torus is used.

-
462        runDOMOperations();
-
463    }
-
464
-
465    return node;
-
466}
-
467
+
464        runDOMOperations();
+
465    }
+
466
+
467    return node;
+
468}
+
469

Shorthand function for the default, empty event object in Component.

-
469const emptyEvent = () => {
-
470    return {
-
471        source: null,
-
472        handler: () => {},
-
473    }
-
474}
-
475
+
471const emptyEvent = () => {
+
472    return {
+
473        source: null,
+
474        handler: () => {},
+
475    }
+
476}
+
477

Torus's Component class

-
477class Component {
-
478
-
479    constructor(...args) {
-
480        this.jdom = undefined;
-
481        this.node = undefined;
-
482        this.event = emptyEvent();
+
479class Component {
+
480
+
481    constructor(...args) {
+
482        this.jdom = undefined;
+
483        this.node = undefined;
+
484        this.event = emptyEvent();

We call init() before render, because it's a common pattern to set and initialize "private" fields in this.init() (at least before the ES-next private fields proposal becomes widely supported.) Frequently, rendering will require private values to be set correctly.

-
487        this.init(...args);
+
489        this.init(...args);

After we run #init(), we want to make sure that every constructed component has a valid #node property. To be efficient, we only render to set #node if it isn't already set yet.

-
491        if (this.node === undefined) {
-
492            this.render();
-
493        }
-
494    }
-
495
+
493        if (this.node === undefined) {
+
494            this.render();
+
495        }
+
496    }
+
497

Component.from() allows us to transform a pure function that maps arguments to a JDOM tree, and promote it into a full-fledged Component class we can compose and use anywhere.

-
499    static from(fn) {
-
500        return class FunctionComponent extends Component {
-
501            init(...args) {
-
502                this.args = args;
-
503            }
-
504            compose() {
-
505                return fn(...this.args);
-
506            }
-
507        }
-
508    }
-
509
+
501    static from(fn) {
+
502        return class FunctionComponent extends Component {
+
503            init(...args) {
+
504                this.args = args;
+
505            }
+
506            compose() {
+
507                return fn(...this.args);
+
508            }
+
509        }
+
510    }
+
511

The default Component#init() is guaranteed to always be a no-op method

-
511    init() {
-
512        // should be overridden
-
513    }
-
514
+
513    init() {
+
514        // should be overridden
+
515    }
+
516

Components usually subscribe to events from a Record, either a view model or a model that maps to business logic. This is shorthand to access that.

-
517    get record() {
-
518        return this.event.source;
-
519    }
-
520
-
521    bind(source, handler) {
-
522        this.unbind();
-
523
-
524        if (source instanceof Evented) {
-
525            this.event = {source, handler};
-
526            source.addHandler(handler);
-
527        } else {
-
528            throw new Error(`cannot bind to ${source}, which is not an instance of Evented.`);
-
529        }
-
530    }
-
531
-
532    unbind() {
-
533        if (this.record) {
-
534            this.record.removeHandler(this.event.handler);
-
535        }
-
536        this.event = emptyEvent();
-
537    }
-
538
+
519    get record() {
+
520        return this.event.source;
+
521    }
+
522
+
523    bind(source, handler) {
+
524        this.unbind();
+
525
+
526        if (source instanceof Evented) {
+
527            this.event = {source, handler};
+
528            source.addHandler(handler);
+
529        } else {
+
530            throw new Error(`cannot bind to ${source}, which is not an instance of Evented.`);
+
531        }
+
532    }
+
533
+
534    unbind() {
+
535        if (this.record) {
+
536            this.record.removeHandler(this.event.handler);
+
537        }
+
538        this.event = emptyEvent();
+
539    }
+
540

We use #remove() to prepare to remove the component from our application entirely. By default, it unsubscribes from all updates. However, the component is still in the render tree -- that's something for the user to decide when to hide.

-
543    remove() {
-
544        this.unbind();
-
545    }
-
546
+
545    remove() {
+
546        this.unbind();
+
547    }
+
548

#compose() is our primary rendering API for components. By default, it renders an invisible comment node.

-
549    compose() {
-
550        return null;
-
551    }
-
552
+
551    compose() {
+
552        return null;
+
553    }
+
554

#preprocess() is an API on the component to allow us to extend Component to give it additional capabilities idiomatically. It consumes the result of #compose() and returns JDOM to be used to actually render the component. See Styled() for a usage example -- it fills similar use cases as React's render props or HOCs.

-
557    preprocess(jdom) {
-
558        return jdom;
-
559    }
-
560
+
559    preprocess(jdom) {
+
560        return jdom;
+
561    }
+
562

#render() is called to actually render the component again to the DOM, and Torus assumes that it's called rarely, only when the component absolutely must update. This obviates the need for something like React's shouldComponentUpdate.

-
564    render(data) {
-
565        // @debug
-
566        render_debug(`Render Component: ${this.constructor.name}`, true);
-
567        data = data || (this.record && this.record.summarize())
-
568        const jdom = this.preprocess(this.compose(data), data);
-
569        if (jdom === undefined) {
+
566    render(data) {
+
567        // @debug
+
568        render_debug(`Render Component: ${this.constructor.name}`, true);
+
569        data = data || (this.record && this.record.summarize())
+
570        const jdom = this.preprocess(this.compose(data), data);
+
571        if (jdom === undefined) {

If the developer accidentally forgets to return the JDOM value from compose, instead of leading to a cryptic DOM API error, show a more friendly warning.

-
573            throw new Error(this.constructor.name + '.compose() returned undefined.');
-
574        }
-
575        try {
-
576            this.node = render(this.node, this.jdom, jdom);
-
577        } catch (e) {
-
578            /* istanbul ignore next: haven't found a reproducible error case that triggers this */
-
579            console.error('rendering error.', e);
-
580        }
-
581        return this.jdom = jdom;
-
582    }
-
583
-
584}
+
575            throw new Error(this.constructor.name + '.compose() returned undefined.');
+
576        }
+
577        try {
+
578            this.node = render(this.node, this.jdom, jdom);
+
579        } catch (e) {
+
580            /* istanbul ignore next: haven't found a reproducible error case that triggers this */
+
581            console.error('rendering error.', e);
+
582        }
+
583        return this.jdom = jdom;
+
584    }
585
+
586}
+
587

We keep track of unique class names already injected into the page's stylesheet, so we don't do redundant style reconciliation.

-
588const injectedClassNames = new Set();
-
589
+
590const injectedClassNames = new Set();
+
591

Global pointer to the stylesheet on the page that Torus uses to insert new CSS rules. It's set the first time a styled component renders.

-
592let styledComponentSheet;
-
593
+
594let styledComponentSheet;
+
595

A weak (garbage-collected keys) cache for mapping styles objects to hashes class names. If we use the css template tag or cache the styles object generated in a component in other ways, it's substantially faster to do a shallow comparison of styles objects and cache unique classnames than to compare the styles objects deeply every time. This cache implements this without a huge memory hit in the case of non-cached styles objects, because WeakMap's keys are garbage collected.

-
601const INJECTED_STYLES_CACHE = new WeakMap();
-
602
+
603const INJECTED_STYLES_CACHE = new WeakMap();
+
604

Fast hash function to map a style rule to a very reasonably unique class name that won't conflict with other classes on the page. Checks the styles cache first.

-
605const generateUniqueClassName = stylesObject => {
-
606    if (!INJECTED_STYLES_CACHE.has(stylesObject)) {
-
607        // Modified from https://github.com/darkskyapp/string-hash/blob/master/index.js
-
608        const str = JSON.stringify(stylesObject);
-
609        let i = str.length;
-
610        let hash = 1989;
-
611        while (i) {
-
612            hash = (hash * 13) ^ str.charCodeAt(-- i);
-
613        }
-
614        INJECTED_STYLES_CACHE.set(stylesObject, '_torus' + (hash >>> 0));
-
615    }
-
616    return INJECTED_STYLES_CACHE.get(stylesObject);
-
617}
-
618
+
607const generateUniqueClassName = stylesObject => {
+
608    if (!INJECTED_STYLES_CACHE.has(stylesObject)) {
+
609        // Modified from https://github.com/darkskyapp/string-hash/blob/master/index.js
+
610        const str = JSON.stringify(stylesObject);
+
611        let i = str.length;
+
612        let hash = 1989;
+
613        while (i) {
+
614            hash = (hash * 13) ^ str.charCodeAt(-- i);
+
615        }
+
616        INJECTED_STYLES_CACHE.set(stylesObject, '_torus' + (hash >>> 0));
+
617    }
+
618    return INJECTED_STYLES_CACHE.get(stylesObject);
+
619}
+
620

We have to construct lots of a{b} syntax in CSS, so here's a shorthand.

-
620const brace = (a, b) => a + '{' + b + '}';
-
621
+
622const brace = (a, b) => a + '{' + b + '}';
+
623

The meat of Styled(). This function maps an ergonomic, dictionary-based set of CSS declarations to an array of CSS rules that can be inserted onto the page stylesheet, and recursively resolves nested CSS, handles keyframes and media queries, and parses other SCSS-like things.

-
626const rulesFromStylesObject = (selector, stylesObject) => {
-
627    let rules = [];
-
628    let selfDeclarations = '';
-
629    for (const prop of Object.keys(stylesObject)) {
-
630        const val = stylesObject[prop];
+
628const rulesFromStylesObject = (selector, stylesObject) => {
+
629    let rules = [];
+
630    let selfDeclarations = '';
+
631    for (const prop of Object.keys(stylesObject)) {
+
632        const val = stylesObject[prop];

CSS declarations that start with '@' are globally namespaced (like @keyframes and @media), so we need to treat them differently.

-
633        if (prop[0] === '@') {
-
634            if (prop.startsWith('@media')) {
-
635                rules.push(brace(prop, rulesFromStylesObject(selector, val).join('')));
-
636            } else  { // @keyframes or @font-face
-
637                rules.push(brace(prop, rulesFromStylesObject('', val).join('')));
-
638            }
-
639        } else {
-
640            if (typeof val === 'object') {
-
641                const commaSeparatedProps = prop.split(',');
-
642                for (const p of commaSeparatedProps) {
+
635        if (prop[0] === '@') {
+
636            if (prop.startsWith('@media')) {
+
637                rules.push(brace(prop, rulesFromStylesObject(selector, val).join('')));
+
638            } else  { // @keyframes or @font-face
+
639                rules.push(brace(prop, rulesFromStylesObject('', val).join('')));
+
640            }
+
641        } else {
+
642            if (typeof val === 'object') {
+
643                const commaSeparatedProps = prop.split(',');
+
644                for (const p of commaSeparatedProps) {

SCSS-like syntax means we use '&' to nest declarations about the parent selector.

-
645                    if (p.includes('&')) {
-
646                        const fullSelector = p.replace(/&/g, selector);
-
647                        rules = rules.concat(rulesFromStylesObject(fullSelector, val));
-
648                    } else {
-
649                        rules = rules.concat(rulesFromStylesObject(selector + ' ' + p, val));
-
650                    }
-
651                }
-
652            } else {
-
653                selfDeclarations += prop + ':' + val + ';';
-
654            }
-
655        }
-
656    }
-
657    if (selfDeclarations) {
+
647                    if (p.includes('&')) {
+
648                        const fullSelector = p.replace(/&/g, selector);
+
649                        rules = rules.concat(rulesFromStylesObject(fullSelector, val));
+
650                    } else {
+
651                        rules = rules.concat(rulesFromStylesObject(selector + ' ' + p, val));
+
652                    }
+
653                }
+
654            } else {
+
655                selfDeclarations += prop + ':' + val + ';';
+
656            }
+
657        }
+
658    }
+
659    if (selfDeclarations) {

We unshift the self declarations to the beginning of the list of rules instead of simply pushing it to the end, because we want the nested rules to have precedence / override rules on self.

-
661        rules.unshift(brace(selector, selfDeclarations));
-
662    }
-
663
-
664    return rules;
-
665}
-
666
+
663        rules.unshift(brace(selector, selfDeclarations));
+
664    }
+
665
+
666    return rules;
+
667}
+
668

Function called once to initialize a stylesheet for Torus to use on every subsequent style render.

-
669const initSheet = () => {
-
670    const styleElement = document.createElement('style');
-
671    styleElement.setAttribute('data-torus', '');
-
672    document.head.appendChild(styleElement);
-
673    styledComponentSheet = styleElement.sheet;
-
674}
-
675
+
671const initSheet = () => {
+
672    const styleElement = document.createElement('style');
+
673    styleElement.setAttribute('data-torus', '');
+
674    document.head.appendChild(styleElement);
+
675    styledComponentSheet = styleElement.sheet;
+
676}
+
677

The preprocessor on Styled() components call this to make sure a given set of CSS rules for a component is inserted into the page stylesheet, but only once for a unique set of rules. We disambiguate by the class name, which is a hash of the CSS rules.

-
680const injectStylesOnce = stylesObject => {
-
681    const className = generateUniqueClassName(stylesObject);
-
682    let sheetLength = 0;
-
683    if (!injectedClassNames.has(className)) {
-
684        if (!styledComponentSheet) {
-
685            initSheet();
-
686        }
-
687        const rules = rulesFromStylesObject('.' + className, stylesObject);
-
688        for (const rule of rules) {
-
689            // @debug
-
690            render_debug(`Add new CSS rule: ${rule}`);
-
691            styledComponentSheet.insertRule(rule, sheetLength ++);
-
692        }
-
693        injectedClassNames.add(className);
-
694    }
-
695    return className;
-
696}
-
697
+
682const injectStylesOnce = stylesObject => {
+
683    const className = generateUniqueClassName(stylesObject);
+
684    let sheetLength = 0;
+
685    if (!injectedClassNames.has(className)) {
+
686        if (!styledComponentSheet) {
+
687            initSheet();
+
688        }
+
689        const rules = rulesFromStylesObject('.' + className, stylesObject);
+
690        for (const rule of rules) {
+
691            // @debug
+
692            render_debug(`Add new CSS rule: ${rule}`);
+
693            styledComponentSheet.insertRule(rule, sheetLength ++);
+
694        }
+
695        injectedClassNames.add(className);
+
696    }
+
697    return className;
+
698}
+
699

Higher-order component to enable styling for any Component class.

-
699const Styled = Base => {
-
700    return class extends Base {
+
701const Styled = Base => {
+
702    return class extends Base {

In a styled component, the #styles() method is passed in the same data as #compose(), and returns a JSON of nested CSS.

-
703        styles() {
-
704            return {};
-
705        }
-
706
-
707        preprocess(jdom, data) {
-
708            if (isObject(jdom)) {
-
709                jdom.attrs = jdom.attrs || {};
-
710                jdom.attrs.class = arrayNormalize(jdom.attrs.class || []);
-
711                jdom.attrs.class.push(injectStylesOnce(this.styles(data)));
-
712            }
-
713            return jdom;
-
714        }
-
715    }
-
716}
-
717
+
705        styles() {
+
706            return {};
+
707        }
+
708
+
709        preprocess(jdom, data) {
+
710            if (isObject(jdom)) {
+
711                jdom.attrs = jdom.attrs || {};
+
712                jdom.attrs.class = arrayNormalize(jdom.attrs.class || []);
+
713                jdom.attrs.class.push(injectStylesOnce(this.styles(data)));
+
714            }
+
715            return jdom;
+
716        }
+
717    }
+
718}
+
719

Torus's generic List implementation, based on Stores. React and similar virtual-dom view libraries depend on key-based reconciliation during render to efficiently render children of long lists. Torus doesn't (yet) have a key-aware reconciler in the diffing algorithm, but List's design obviates the need for keys. Rather than giving the renderer a flat virtual DOM tree to render, List instantiates each individual item component and hands them off to the renderer as full DOM Node elements, so each list item manages its own rendering, and the list component only worries about displaying the list wrapper and a flat list of children items.

-
727
-
728class List extends Component {
-
729
-
730    get itemClass() {
-
731        return Component; // default value, should be overridden
-
732    }
-
733
-
734    init(store, ...itemData) {
-
735        this.store = store;
-
736        this.items = new Map();
-
737        this.filterFn = null;
-
738        this.itemData = itemData;
-
739
-
740        this.bind(this.store, () => this.itemsChanged());
-
741    }
-
742
-
743    itemsChanged() {
+
729
+
730class List extends Component {
+
731
+
732    get itemClass() {
+
733        return Component; // default value, should be overridden
+
734    }
+
735
+
736    init(store, ...itemData) {
+
737        this.store = store;
+
738        this.items = new Map();
+
739        this.filterFn = null;
+
740        this.itemData = itemData;
+
741
+
742        this.bind(this.store, () => this.itemsChanged());
+
743    }
+
744
+
745    itemsChanged() {

For every record in the store, if it isn't already in this.items, add it and its view; if any were removed, also remove it from this.items.

-
747        const data = this.store.summarize();
-
748        const items = this.items;
-
749        for (const record of items.keys()) {
-
750            if (!data.includes(record)) {
-
751                items.get(record).remove();
-
752                items.delete(record);
-
753            }
-
754        }
-
755        for (const record of data) {
-
756            if (!items.has(record)) {
-
757                items.set(
-
758                    record,
+
749        const data = this.store.summarize();
+
750        const items = this.items;
+
751        for (const record of items.keys()) {
+
752            if (!data.includes(record)) {
+
753                items.get(record).remove();
+
754                items.delete(record);
+
755            }
+
756        }
+
757        for (const record of data) {
+
758            if (!items.has(record)) {
+
759                items.set(
+
760                    record,

We pass a callback that takes a record and removes it from the list's store. It's common in UIs for items to have a button that removes the item from the list, so this callback is passed to the item component constructor to facilitate that pattern.

-
763                    new this.itemClass(
-
764                        record,
-
765                        () => this.store.remove(record),
-
766                        ...this.itemData
-
767                    )
-
768                );
-
769            }
-
770        }
-
771
-
772        let sorter = [...items.entries()];
+
765                    new this.itemClass(
+
766                        record,
+
767                        () => this.store.remove(record),
+
768                        ...this.itemData
+
769                    )
+
770                );
+
771            }
+
772        }
+
773
+
774        let sorter = [...items.entries()];

Sort by the provided filter function if there is one

-
774        if (this.filterFn !== null) {
-
775            sorter = sorter.filter(item => this.filterFn(item[0]));
-
776        }
+
776        if (this.filterFn !== null) {
+
777            sorter = sorter.filter(item => this.filterFn(item[0]));
+
778        }

Sort the list the way the associated Store is sorted.

-
778        sorter.sort((a, b) => data.indexOf(a[0]) - data.indexOf(b[0]));
-
779
+
780        sorter.sort((a, b) => data.indexOf(a[0]) - data.indexOf(b[0]));
+
781

Store the new items in a new (insertion-ordered) Map at this.items

-
781        this.items = new Map(sorter);
-
782
-
783        this.render();
-
784    }
-
785
-
786    filter(filterFn) {
-
787        this.filterFn = filterFn;
-
788        this.itemsChanged();
-
789    }
-
790
-
791    unfilter() {
-
792        this.filterFn = null;
-
793        this.itemsChanged();
-
794    }
-
795
-
796    get components() {
-
797        return [...this];
-
798    }
-
799
+
783        this.items = new Map(sorter);
+
784
+
785        this.render();
+
786    }
+
787
+
788    filter(filterFn) {
+
789        this.filterFn = filterFn;
+
790        this.itemsChanged();
+
791    }
+
792
+
793    unfilter() {
+
794        this.filterFn = null;
+
795        this.itemsChanged();
+
796    }
+
797
+
798    get components() {
+
799        return [...this];
+
800    }
+
801

List#nodes returns the HTML nodes for each of its item views, sorted in order. Designed to make writing #compose() easier.

-
802    get nodes() {
-
803        return this.components.map(item => item.node);
-
804    }
-
805
+
804    get nodes() {
+
805        return this.components.map(item => item.node);
+
806    }
+
807

This iterator is called when JavaScript requests an iterator from a list, e.g. when for (const _ of someList) is run.

-
808    [Symbol.iterator]() {
-
809        return this.items.values();
-
810    }
-
811
-
812    remove() {
-
813        super.remove();
+
810    [Symbol.iterator]() {
+
811        return this.items.values();
+
812    }
+
813
+
814    remove() {
+
815        super.remove();

When we remove a list, we also want to call remove() on each child components.

-
816        for (const c of this.items.values()) {
-
817            c.remove();
-
818        }
-
819    }
-
820
+
818        for (const c of this.items.values()) {
+
819            c.remove();
+
820        }
+
821    }
+
822

By default, just render the children views in a <ul/>

-
822    compose() {
-
823        return {
-
824            tag: 'ul',
-
825            children: this.nodes,
-
826        }
-
827    }
-
828
-
829}
+
824    compose() {
+
825        return {
+
826            tag: 'ul',
+
827            children: this.nodes,
+
828        }
+
829    }
830
+
831}
+
832

Higher-order component to create a list component for a given child item component.

-
833const ListOf = itemClass => {
-
834    return class extends List {
-
835        get itemClass() {
-
836            return itemClass;
-
837        }
-
838    }
-
839}
-
840
+
835const ListOf = itemClass => {
+
836    return class extends List {
+
837        get itemClass() {
+
838            return itemClass;
+
839        }
+
840    }
+
841}
+
842

A base class for evented data stores. Not exposed to the public API, but all observables in Torus inherit from Evented.

-
843class Evented {
-
844
-
845    constructor() {
-
846        this.handlers = new Set();
-
847    }
-
848
+
845class Evented {
+
846
+
847    constructor() {
+
848        this.handlers = new Set();
+
849    }
+
850

Base, empty implementation of #summarize() which is overridden in all subclasses. In subclasses, this returns the "summary" of the current state of the event emitter as an object/array.

-
852    summarize() {}
-
853
+
854    summarize() {}
+
855

Whenever something changes, we fire an event to all subscribed listeners, with a summary of its state.

-
856    emitEvent() {
-
857        const summary = this.summarize();
-
858        for (const handler of this.handlers) {
-
859            handler(summary);
-
860        }
-
861    }
-
862
-
863    addHandler(handler) {
-
864        this.handlers.add(handler);
-
865        handler(this.summarize());
-
866    }
-
867
-
868    removeHandler(handler) {
-
869        this.handlers.delete(handler);
-
870    }
-
871
-
872}
+
858    emitEvent() {
+
859        const summary = this.summarize();
+
860        for (const handler of this.handlers) {
+
861            handler(summary);
+
862        }
+
863    }
+
864
+
865    addHandler(handler) {
+
866        this.handlers.add(handler);
+
867        handler(this.summarize());
+
868    }
+
869
+
870    removeHandler(handler) {
+
871        this.handlers.delete(handler);
+
872    }
873
+
874}
+
875

Record is Torus's unit of individual data source, used for view models and Models from business logic.

-
876class Record extends Evented {
-
877
-
878    constructor(id, data = {}) {
-
879        super();
-
880
+
878class Record extends Evented {
+
879
+
880    constructor(id, data = {}) {
+
881        super();
+
882

We can create a Record by either passing in just the properties, or an ID and a dictionary of props. We disambiguate here.

-
883        if (isObject(id)) {
-
884            data = id;
-
885            id = null;
-
886        }
-
887
-
888        this.id = id;
-
889        this.data = data;
-
890    }
-
891
+
885        if (isObject(id)) {
+
886            data = id;
+
887            id = null;
+
888        }
+
889
+
890        this.id = id;
+
891        this.data = data;
+
892    }
+
893

Setter for properties

-
893    update(data) {
-
894        Object.assign(this.data, data);
-
895        this.emitEvent();
-
896    }
-
897
+
895    update(data) {
+
896        Object.assign(this.data, data);
+
897        this.emitEvent();
+
898    }
+
899

Getter

-
899    get(name) {
-
900        return this.data[name];
-
901    }
-
902
+
901    get(name) {
+
902        return this.data[name];
+
903    }
+
904

We summarize a Record by returning a dictionary of all of its properties and the ID

-
905    summarize() {
-
906        return Object.assign(
-
907            {id: this.id},
-
908            this.data
-
909        );
-
910    }
-
911
+
907    summarize() {
+
908        return Object.assign(
+
909            {id: this.id},
+
910            this.data
+
911        );
+
912    }
+
913

The JSON-serialized version of a Record is the same as its summary, since it's a shallow data store with just plain properties.

-
914    serialize() {
-
915        return this.summarize();
-
916    }
-
917
-
918}
+
916    serialize() {
+
917        return this.summarize();
+
918    }
919
+
920}
+
921

A list of Records, represents a collection or a table

-
921class Store extends Evented {
-
922
-
923    constructor(records = []) {
-
924        super();
+
923class Store extends Evented {
+
924
+
925    constructor(records = []) {
+
926        super();

Reset the store's contents with the given records

-
926        this.reset(records);
-
927    }
-
928
-
929    get recordClass() {
-
930        return Record;
-
931    }
-
932
-
933    get comparator() {
-
934        return null;
-
935    }
-
936
+
928        this.reset(records);
+
929    }
+
930
+
931    get recordClass() {
+
932        return Record;
+
933    }
+
934
+
935    get comparator() {
+
936        return null;
+
937    }
+
938

Create and return a new instance of the store's record from the given data.

-
939    create(id, data) {
-
940        return this.add(new this.recordClass(id, data));
-
941    }
-
942
+
941    create(id, data) {
+
942        return this.add(new this.recordClass(id, data));
+
943    }
+
944

Add a given record to this store, also called by #create().

-
944    add(record) {
-
945        this.records.add(record);
-
946        this.emitEvent();
-
947        return record;
-
948    }
-
949
+
946    add(record) {
+
947        this.records.add(record);
+
948        this.emitEvent();
+
949        return record;
+
950    }
+
951

Remove a given record from the store.

-
951    remove(record) {
-
952        this.records.delete(record);
-
953        this.emitEvent();
-
954        return record;
-
955    }
-
956
+
953    remove(record) {
+
954        this.records.delete(record);
+
955        this.emitEvent();
+
956        return record;
+
957    }
+
958

This iterator is called when JavaScript requests an iterator from a store, like when for (const _ of someStore) is run.

-
959    [Symbol.iterator]() {
-
960        return this.records.values();
-
961    }
-
962
+
961    [Symbol.iterator]() {
+
962        return this.records.values();
+
963    }
+
964

Try to find a record with the given ID in the store, and return it. Returns null if not found.

-
965    find(id) {
-
966        for (const record of this.records) {
-
967            if (record.id === id) {
-
968                return record;
-
969            }
-
970        }
-
971        return null;
-
972    }
-
973
-
974    reset(records) {
+
967    find(id) {
+
968        for (const record of this.records) {
+
969            if (record.id === id) {
+
970                return record;
+
971            }
+
972        }
+
973        return null;
+
974    }
+
975
+
976    reset(records) {

Internally, we represent the store as an unordered set. we only order by comparator when we summarize. This prevents us from having to perform sorting checks on every insert/update, and is efficient as long as we don't re-render excessively.

-
979        this.records = new Set(records);
-
980        this.emitEvent();
-
981    }
-
982
-
983    summarize() {
+
981        this.records = new Set(records);
+
982        this.emitEvent();
+
983    }
+
984
+
985    summarize() {

The summary of a store is defined functionally. We just sort the records in our store by the comparator (but we use a list of pairs of cached comparators and records to be fast.

-
987        return [...this.records].map(record => [
-
988            this.comparator ? this.comparator(record) : null,
-
989            record,
-
990        ]).sort((a, b) => {
-
991            if (a[0] < b[0]) {
-
992                return -1;
-
993            } else if (a[0] > b[0]) {
-
994                return 1;
-
995            } else {
-
996                return 0;
-
997            }
-
998        }).map(o => o[1]);
-
999    }
-
1000
+
989        return [...this.records].map(record => [
+
990            this.comparator ? this.comparator(record) : null,
+
991            record,
+
992        ]).sort((a, b) => {
+
993            if (a[0] < b[0]) {
+
994                return -1;
+
995            } else if (a[0] > b[0]) {
+
996                return 1;
+
997            } else {
+
998                return 0;
+
999            }
+
1000        }).map(o => o[1]);
+
1001    }
+
1002

To serialize a store, we serialize each record and put them in a giant list.

-
1003    serialize() {
-
1004        return this.summarize().map(record => record.serialize());
-
1005    }
-
1006
-
1007}
+
1005    serialize() {
+
1006        return this.summarize().map(record => record.serialize());
+
1007    }
1008
+
1009}
+
1010

Higher-order component to create a Store for a given record class.

-
1011const StoreOf = recordClass => {
-
1012    return class extends Store {
-
1013        get recordClass() {
-
1014            return recordClass;
-
1015        }
-
1016    }
-
1017}
-
1018
+
1013const StoreOf = recordClass => {
+
1014    return class extends Store {
+
1015        get recordClass() {
+
1016            return recordClass;
+
1017        }
+
1018    }
+
1019}
+
1020

Helper function for the router. It takes a route string that contains parameters like, /path/:param1/path/:param2 and returns a regular expression to match that route and a list of params in that route.

-
1023const routeStringToRegExp = route => {
-
1024    let match;
-
1025    const paramNames = [];
-
1026    while (match !== null) {
-
1027        match = (/:\w+/).exec(route);
-
1028        if (match) {
-
1029            const paramName = match[0];
-
1030            paramNames.push(paramName.substr(1));
-
1031            route = route.replace(paramName, '(.+)');
-
1032        }
-
1033    }
-
1034    return [new RegExp(route), paramNames];
-
1035}
-
1036
+
1025const routeStringToRegExp = route => {
+
1026    let match;
+
1027    const paramNames = [];
+
1028    while (match !== null) {
+
1029        match = (/:\w+/).exec(route);
+
1030        if (match) {
+
1031            const paramName = match[0];
+
1032            paramNames.push(paramName.substr(1));
+
1033            route = route.replace(paramName, '(.+)');
+
1034        }
+
1035    }
+
1036    return [new RegExp(route), paramNames];
+
1037}
+
1038

Front-end router. A routing component can bind to updates from the Router instead of a Record, and re-render different subviews when the routes change.

-
1040class Router extends Evented {
-
1041
-
1042    constructor(routes) {
-
1043        super();
+
1042class Router extends Evented {
+
1043
+
1044    constructor(routes) {
+
1045        super();

We parse the given dictionary of routes into three things: the name of the route, the route regular expression, and the list of params in that route.

-
1047        this.routes = Object.entries(routes)
-
1048            .map(([name, route]) => [name, ...routeStringToRegExp(route)]);
+
1049        this.routes = Object.entries(routes)
+
1050            .map(([name, route]) => [name, ...routeStringToRegExp(route)]);

Last matched route's information is cached here

-
1050        this.lastMatch = ['', null];
+
1052        this.lastMatch = ['', null];

Whenever the browser pops the history state (i.e. when the user goes back with the back button or forward with the forward button), we need to route again.

-
1054        this._cb = () => this.route(location.pathname);
-
1055        window.addEventListener('popstate', this._cb);
+
1056        this._cb = () => this.route(location.pathname);
+
1057        window.addEventListener('popstate', this._cb);

Route the current URL, if it's already a deep link to a path.

-
1057        this._cb();
-
1058    }
-
1059
+
1059        this._cb();
+
1060    }
+
1061

The "summary" of this Evented (components can bind to this object) is the information about the last route.

-
1062    summarize() {
-
1063        return this.lastMatch;
-
1064    }
-
1065
+
1064    summarize() {
+
1065        return this.lastMatch;
+
1066    }
+
1067

Click events from links can call this.go() with the destination URL to trigger going to a new route without reloading the page. New routes are only added to the session history if the route is indeed new.

-
1069    go(destination, {replace = false} = {}) {
-
1070        if (window.location.pathname !== destination) {
-
1071            if (replace) {
-
1072                history.replaceState(null, document.title, destination);
-
1073            } else {
-
1074                history.pushState(null, document.title, destination);
-
1075            }
-
1076            this.route(destination);
-
1077        }
-
1078    }
-
1079
+
1071    go(destination, {replace = false} = {}) {
+
1072        if (window.location.pathname !== destination) {
+
1073            if (replace) {
+
1074                history.replaceState(null, document.title, destination);
+
1075            } else {
+
1076                history.pushState(null, document.title, destination);
+
1077            }
+
1078            this.route(destination);
+
1079        }
+
1080    }
+
1081

Main procedure to reconcile which of the defined route the current location path matches, and dispatch the right event. Routes are checked in order of declaration.

-
1083    route(path) {
+
1085    route(path) {

Match destination against the route regular expressions

-
1085        for (const [name, routeRe, paramNames] of this.routes) {
-
1086            const match = routeRe.exec(path);
-
1087            if (match !== null) {
-
1088                const result = {};
-
1089                const paramValues = match.slice(1);
+
1087        for (const [name, routeRe, paramNames] of this.routes) {
+
1088            const match = routeRe.exec(path);
+
1089            if (match !== null) {
+
1090                const result = {};
+
1091                const paramValues = match.slice(1);

Given the matched values and parameter names, build a dictionary of params that components can use to re-render based on the route.

-
1093                paramNames.forEach((name, i) => result[name] = paramValues[i]);
-
1094                this.lastMatch = [name, result];
-
1095                break;
-
1096            }
-
1097        }
-
1098        this.emitEvent();
-
1099    }
-
1100
+
1095                paramNames.forEach((name, i) => result[name] = paramValues[i]);
+
1096                this.lastMatch = [name, result];
+
1097                break;
+
1098            }
+
1099        }
+
1100        this.emitEvent();
+
1101    }
+
1102

When we don't want the router to work anymore / stop listening / be gc'd, we can call #remove() to do just that.

-
1103    remove() {
-
1104        window.removeEventListener('popstate', this._cb);
-
1105    }
-
1106
-
1107}
+
1105    remove() {
+
1106        window.removeEventListener('popstate', this._cb);
+
1107    }
1108
+
1109}
+
1110

Torus exposes these public APIs

-
1110const exposedNames = {
+
1112const exposedNames = {

render isn't designed to be a public API and the API might change, but it's exposed to make unit testing easier.

-
1113    render,
-
1114    Component,
-
1115    Styled,
+
1115    render,
+
1116    Component,
+
1117    Styled,

Provide a default, StyledComponent class.

-
1117    StyledComponent: Styled(Component),
-
1118    List,
-
1119    ListOf,
-
1120    Record,
-
1121    Store,
-
1122    StoreOf,
-
1123    Router,
-
1124}
-
1125
+
1119    StyledComponent: Styled(Component),
+
1120    List,
+
1121    ListOf,
+
1122    Record,
+
1123    Store,
+
1124    StoreOf,
+
1125    Router,
+
1126}
+
1127

If there is a global window object, bind API names to it.

-
1127if (typeof window === 'object') {
-
1128    window.Torus = exposedNames;
-
1129}
+
1129if (typeof window === 'object') {
+
1130    window.Torus = exposedNames;
+
1131}

Export public APIs CommonJS-style

-
1131if (typeof module === 'object' && module.exports) {
-
1132    module.exports = exposedNames;
-
1133}
-
1134
+
1133if (typeof module === 'object' && module.exports) {
+
1134    module.exports = exposedNames;
+
1135}
+
1136
diff --git a/samples/conway/index.min.js b/samples/conway/index.min.js index 479e3a4..311f329 100644 --- a/samples/conway/index.min.js +++ b/samples/conway/index.min.js @@ -1 +1 @@ -!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?t[r]=s.attrs[r]:s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file +!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?e.attrs[r]!==s.attrs[r]&&(t[r]=s.attrs[r]):s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file diff --git a/samples/graph-calc/index.min.js b/samples/graph-calc/index.min.js index 479e3a4..311f329 100644 --- a/samples/graph-calc/index.min.js +++ b/samples/graph-calc/index.min.js @@ -1 +1 @@ -!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?t[r]=s.attrs[r]:s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file +!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?e.attrs[r]!==s.attrs[r]&&(t[r]=s.attrs[r]):s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file diff --git a/samples/gravity/index.min.js b/samples/gravity/index.min.js index 479e3a4..311f329 100644 --- a/samples/gravity/index.min.js +++ b/samples/gravity/index.min.js @@ -1 +1 @@ -!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?t[r]=s.attrs[r]:s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file +!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?e.attrs[r]!==s.attrs[r]&&(t[r]=s.attrs[r]):s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file diff --git a/samples/hn-reader/index.min.js b/samples/hn-reader/index.min.js index 479e3a4..311f329 100644 --- a/samples/hn-reader/index.min.js +++ b/samples/hn-reader/index.min.js @@ -1 +1 @@ -!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?t[r]=s.attrs[r]:s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file +!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?e.attrs[r]!==s.attrs[r]&&(t[r]=s.attrs[r]):s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file diff --git a/samples/markus/index.min.js b/samples/markus/index.min.js index 479e3a4..311f329 100644 --- a/samples/markus/index.min.js +++ b/samples/markus/index.min.js @@ -1 +1 @@ -!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?t[r]=s.attrs[r]:s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file +!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?e.attrs[r]!==s.attrs[r]&&(t[r]=s.attrs[r]):s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file diff --git a/samples/mondrian/index.min.js b/samples/mondrian/index.min.js index 479e3a4..311f329 100644 --- a/samples/mondrian/index.min.js +++ b/samples/mondrian/index.min.js @@ -1 +1 @@ -!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?t[r]=s.attrs[r]:s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file +!function(t){var e={};function s(r){if(e[r])return e[r].exports;var n=e[r]={i:r,l:!1,exports:{}};return t[r].call(n.exports,n,n.exports,s),n.l=!0,n.exports}s.m=t,s.c=e,s.d=function(t,e,r){s.o(t,e)||Object.defineProperty(t,e,{enumerable:!0,get:r})},s.r=function(t){"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(t,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(t,"__esModule",{value:!0})},s.t=function(t,e){if(1&e&&(t=s(t)),8&e)return t;if(4&e&&"object"==typeof t&&t&&t.__esModule)return t;var r=Object.create(null);if(s.r(r),Object.defineProperty(r,"default",{enumerable:!0,value:t}),2&e&&"string"!=typeof t)for(var n in t)s.d(r,n,function(e){return t[e]}.bind(null,n));return r},s.n=function(t){var e=t&&t.__esModule?function(){return t.default}:function(){return t};return s.d(e,"a",e),e},s.o=function(t,e){return Object.prototype.hasOwnProperty.call(t,e)},s.p="",s(s.s=0)}([function(t,e,s){const{render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h}=s(1),{jdom:f,css:m}=s(2);t.exports={render:r,Component:n,Styled:o,StyledComponent:i,List:c,ListOf:l,Record:a,Store:d,StoreOf:u,Router:h,jdom:f,css:m}},function(t,e,s){let r=0;const n=t=>null!==t&&"object"==typeof t,o=t=>{void 0===t.attrs&&(t.attrs={}),void 0===t.events&&(t.events={}),void 0===t.children&&(t.children=[])},i=t=>Array.isArray(t)?t:[t],c=()=>document.createComment("");let l=[];const a=0,d=1,u=2,h={replaceChild:()=>{}};const f=(t,e,s)=>{for(const r of Object.keys(t)){const n=i(t[r]),o=i(e[r]||[]);for(const t of n)o.includes(t)||"function"!=typeof t||s(r,t)}},m=(t,e,s)=>{const i=e=>{t&&t!==e&&l.push([u,t,e]),t=e};if(r++,e!==s)if(null===s)i(c());else if("string"==typeof s||"number"==typeof s)"string"==typeof e||"number"==typeof e?t.nodeValue=s:i(document.createTextNode(s));else if(void 0!==s.appendChild)i(s);else{(void 0===t||!n(e)||e&&void 0!==e.appendChild||e.tag!==s.tag)&&(e={tag:null},i(document.createElement(s.tag))),o(e),o(s);for(const r of Object.keys(s.attrs))if("class"===r){const e=s.attrs.class;Array.isArray(e)?t.className=e.join(" "):t.className=e}else if("style"===r){const r=e.attrs.style||{},n=s.attrs.style;for(const e of Object.keys(n))n[e]!==r[e]&&(t.style[e]=n[e]);for(const e of Object.keys(r))void 0===n[e]&&(t.style[e]="")}else r in t?e.attrs[r]!==s.attrs[r]&&(t[r]=s.attrs[r]):s.attrs[r]!==e.attrs[r]&&t.setAttribute(r,s.attrs[r]);for(const r of Object.keys(e.attrs))void 0===s.attrs[r]&&(r in t?t[r]=null:t.removeAttribute(r));f(s.events,e.events,(e,s)=>{t.addEventListener(e,s)}),f(e.events,s.events,(e,s)=>{t.removeEventListener(e,s)});const r=e.children,c=s.children,u=r.length,h=c.length;if(h+u>0){const n=e._nodes||[],o=u({source:null,handler:()=>{}});class b{constructor(...t){this.jdom=void 0,this.node=void 0,this.event=p(),this.init(...t),void 0===this.node&&this.render()}static from(t){return class extends b{init(...t){this.args=t}compose(){return t(...this.args)}}}init(){}get record(){return this.event.source}bind(t,e){if(this.unbind(),!(t instanceof S))throw new Error(`cannot bind to ${t}, which is not an instance of Evented.`);this.event={source:t,handler:e},t.addHandler(e)}unbind(){this.record&&this.record.removeHandler(this.event.handler),this.event=p()}remove(){this.unbind()}compose(){return null}preprocess(t){return t}render(t){t=t||this.record&&this.record.summarize();const e=this.preprocess(this.compose(t),t);if(void 0===e)throw new Error(this.constructor.name+".compose() returned undefined.");try{this.node=m(this.node,this.jdom,e)}catch(t){console.error("rendering error.",t)}return this.jdom=e}}const v=new Set;let g;const y=new WeakMap,x=(t,e)=>t+"{"+e+"}",j=(t,e)=>{let s=[],r="";for(const n of Object.keys(e)){const o=e[n];if("@"===n[0])n.startsWith("@media")?s.push(x(n,j(t,o).join(""))):s.push(x(n,j("",o).join("")));else if("object"==typeof o){const e=n.split(",");for(const r of e)if(r.includes("&")){const e=r.replace(/&/g,t);s=s.concat(j(e,o))}else s=s.concat(j(t+" "+r,o))}else r+=n+":"+o+";"}return r&&s.unshift(x(t,r)),s},w=t=>{const e=(t=>{if(!y.has(t)){const e=JSON.stringify(t);let s=e.length,r=1989;for(;s;)r=13*r^e.charCodeAt(--s);y.set(t,"_torus"+(r>>>0))}return y.get(t)})(t);let s=0;if(!v.has(e)){g||(()=>{const t=document.createElement("style");t.setAttribute("data-torus",""),document.head.appendChild(t),g=t.sheet})();const r=j("."+e,t);for(const t of r)g.insertRule(t,s++);v.add(e)}return e},O=t=>(class extends t{styles(){return{}}preprocess(t,e){return n(t)&&(t.attrs=t.attrs||{},t.attrs.class=i(t.attrs.class||[]),t.attrs.class.push(w(this.styles(e)))),t}});class C extends b{get itemClass(){return b}init(t,...e){this.store=t,this.items=new Map,this.filterFn=null,this.itemData=e,this.bind(this.store,()=>this.itemsChanged())}itemsChanged(){const t=this.store.summarize(),e=this.items;for(const s of e.keys())t.includes(s)||(e.get(s).remove(),e.delete(s));for(const s of t)e.has(s)||e.set(s,new this.itemClass(s,()=>this.store.remove(s),...this.itemData));let s=[...e.entries()];null!==this.filterFn&&(s=s.filter(t=>this.filterFn(t[0]))),s.sort((e,s)=>t.indexOf(e[0])-t.indexOf(s[0])),this.items=new Map(s),this.render()}filter(t){this.filterFn=t,this.itemsChanged()}unfilter(){this.filterFn=null,this.itemsChanged()}get components(){return[...this]}get nodes(){return this.components.map(t=>t.node)}[Symbol.iterator](){return this.items.values()}remove(){super.remove();for(const t of this.items.values())t.remove()}compose(){return{tag:"ul",children:this.nodes}}}class S{constructor(){this.handlers=new Set}summarize(){}emitEvent(){const t=this.summarize();for(const e of this.handlers)e(t)}addHandler(t){this.handlers.add(t),t(this.summarize())}removeHandler(t){this.handlers.delete(t)}}class k extends S{constructor(t,e={}){super(),n(t)&&(e=t,t=null),this.id=t,this.data=e}update(t){Object.assign(this.data,t),this.emitEvent()}get(t){return this.data[t]}summarize(){return Object.assign({id:this.id},this.data)}serialize(){return this.summarize()}}class _ extends S{constructor(t=[]){super(),this.reset(t)}get recordClass(){return k}get comparator(){return null}create(t,e){return this.add(new this.recordClass(t,e))}add(t){return this.records.add(t),this.emitEvent(),t}remove(t){return this.records.delete(t),this.emitEvent(),t}[Symbol.iterator](){return this.records.values()}find(t){for(const e of this.records)if(e.id===t)return e;return null}reset(t){this.records=new Set(t),this.emitEvent()}summarize(){return[...this.records].map(t=>[this.comparator?this.comparator(t):null,t]).sort((t,e)=>t[0]e[0]?1:0).map(t=>t[1])}serialize(){return this.summarize().map(t=>t.serialize())}}const E=t=>{let e;const s=[];for(;null!==e;)if(e=/:\w+/.exec(t)){const r=e[0];s.push(r.substr(1)),t=t.replace(r,"(.+)")}return[new RegExp(t),s]};const A={render:m,Component:b,Styled:O,StyledComponent:O(b),List:C,ListOf:t=>(class extends C{get itemClass(){return t}}),Record:k,Store:_,StoreOf:t=>(class extends _{get recordClass(){return t}}),Router:class extends S{constructor(t){super(),this.routes=Object.entries(t).map(([t,e])=>[t,...E(e)]),this.lastMatch=["",null],this._cb=(()=>this.route(location.pathname)),window.addEventListener("popstate",this._cb),this._cb()}summarize(){return this.lastMatch}go(t,{replace:e=!1}={}){window.location.pathname!==t&&(e?history.replaceState(null,document.title,t):history.pushState(null,document.title,t),this.route(t))}route(t){for(const[e,s,r]of this.routes){const n=s.exec(t);if(null!==n){const t={},s=n.slice(1);r.forEach((e,r)=>t[e]=s[r]),this.lastMatch=[e,t];break}}this.emitEvent()}remove(){window.removeEventListener("popstate",this._cb)}}};"object"==typeof window&&(window.Torus=A),t.exports&&(t.exports=A)},function(t,e,s){const r=t=>null!==t&&"object"==typeof t,n=(t,e)=>t.substr(0,t.length-e.length),o=t=>String.fromCodePoint(+/&#(\w+);/.exec(t)[1]),i=(t,e)=>{let s=t[0];for(let r=1,n=e.length;r<=n;r++)s+=e[r-1]+t[r];return s};class c{constructor(t){this.idx=0,this.content=t,this.len=t.length}next(){const t=this.content[this.idx++];return void 0===t&&(this.idx=this.len),t}back(){this.idx--}readUpto(t){const e=this.content.substr(this.idx).indexOf(t);return this.toNext(e)}readUntil(t){const e=this.content.substr(this.idx).indexOf(t)+t.length;return this.toNext(e)}toNext(t){const e=this.content.substr(this.idx);if(-1===t)return this.idx=this.len,e;{const s=e.substr(0,t);return this.idx+=t,s}}clipEnd(t){return!!this.content.endsWith(t)&&(this.content=n(this.content,t),!0)}}const l=t=>{let e="";for(let s=0,r=t.length;s{if("!"===(t=t.trim())[0])return{jdom:null,selfClosing:!0};if(!t.includes(" ")){const e=t.endsWith("/");return{jdom:{tag:e?n(t,"/"):t,attrs:{},events:{}},selfClosing:e}}const e=new c(t),s=e.clipEnd("/");let r="",o=!1,i=!1;const a=[];let d=0;const u=t=>{(""!==(r=r.trim())||t)&&(a.push({type:d,value:r}),o=!1,r="")};for(let t=e.next();void 0!==t;t=e.next())switch(t){case"=":i?r+=t:(u(),o=!0,d=1);break;case" ":i?r+=t:o||(u(),d=0);break;case"\\":i&&(t=e.next(),r+=t);break;case'"':i?(i=!1,u(!0),d=0):1===d&&(i=!0);break;default:r+=t,o=!1}u();let h="";const f={},m={};h=a.shift().value;let p=null,b=a.shift();const v=()=>{p=b,b=a.shift()};for(;void 0!==b;){if(1===b.type){const t=p.value;let e=b.value.trim();if(t.startsWith("on"))m[t.substr(2)]=[e];else if("class"===t)""!==e&&(f[t]=e.split(" "));else if("style"===t){e.endsWith(";")&&(e=e.substr(0,e.length-1));const s={};for(const t of e.split(";")){const e=t.indexOf(":"),r=t.substr(0,e),n=t.substr(e+1);s[l(r.trim())]=n.trim()}f[t]=s}else f[t]=e;v()}else p&&(f[p.value]=!0);v()}return p&&0===p.type&&(f[p.value]=!0),{jdom:{tag:h,attrs:f,events:m},selfClosing:s}},d=t=>{const e=[];let s=null,r=!1;const n=()=>{r&&""===s.trim()||s&&e.push(s),s=null,r=!1},i=t=>{!1===r&&(n(),r=!0,s=""),s+=t};for(let e=t.next();void 0!==e;e=t.next())if("<"===e){if(n(),"/"===t.next()){t.readUntil(">");break}{t.back();const e=a(t.readUpto(">"));t.next(),s=e&&e.jdom,e.selfClosing||null===s||(s.children=d(t))}}else i("&"===e?o(e+t.readUntil(";")):e);return n(),e},u=new Map,h=/jdom_tpl_obj_\[(\d+)\]/,f=(t,e)=>{if((t=>"string"==typeof t&&t.includes("jdom_tpl_"))(t)){const s=h.exec(t),r=t.split(s[0]),n=s[1],o=f(r[1],e);let i=[];return""!==r[0]&&i.push(r[0]),Array.isArray(e[n])?i=i.concat(e[n]):i.push(e[n]),0!==o.length&&(i=i.concat(o)),i}return""!==t?[t]:[]},m=(t,e)=>{const s=[];for(const n of t)for(const t of f(n,e))r(t)&&v(t,e),s.push(t);const n=s[0],o=s[s.length-1];return"string"==typeof n&&""===n.trim()&&s.shift(),"string"==typeof o&&""===o.trim()&&s.pop(),s},p=(t,e)=>{if(t.length<14)return t;{const s=h.exec(t);if(null===s)return t;if(t.trim()===s[0])return e[s[1]];{const r=t.split(s[0]);return r[0]+e[s[1]]+p(r[1],e)}}},b=(t,e)=>{for(let s=0,r=t.length;s{for(const s of Object.keys(t)){const n=t[s];"string"==typeof n?t[s]=p(n,e):Array.isArray(n)?"children"===s?t.children=m(n,e):b(n,e):r(n)&&v(n,e)}},g=t=>{const e={};let s=0,r=["",""];const n=()=>{"string"==typeof r[1]?e[r[0].trim()]=r[1].trim():e[r[0].trim()]=r[1],r=["",""]};t.readUntil("{");for(let e=t.next();void 0!==e&&"}"!==e;e=t.next()){const o=r[0];switch(e){case'"':case"'":for(r[s]+=e+t.readUntil(e);r[s].endsWith("\\"+e);)r[s]+=t.readUntil(e);break;case":":""===o.trim()||o.includes("&")||o.includes("@")||o.includes(":")?r[s]+=e:s=1;break;case";":s=0,n();break;case"{":t.back(),r[1]=g(t),n();break;default:r[s]+=e}}return""!==r[0].trim()&&n(),e},y=new Map,x={jdom:(t,...e)=>{const s=t.join("jdom_tpl_joiner");try{if(!u.has(s)){const r=e.map((t,e)=>`jdom_tpl_obj_[${e}]`),n=new c(i(t.map(t=>t.replace(/\s+/g," ")),r)),o=d(n)[0],l=typeof o,a=JSON.stringify(o);u.set(s,t=>{if("string"===l)return p(o,t);if("object"===l){const e={},s=JSON.parse(a);return v(Object.assign(e,s),t),e}return null})}return u.get(s)(e)}catch(s){return console.error(`jdom parse error.\ncheck for mismatched brackets, tags, quotes.\n${i(t,e)}\n${s.stack||s}`),""}},css:(t,...e)=>{const s=i(t,e).trim();return y.has(s)||y.set(s,g(new c("{"+s+"}"))),y.get(s)}};"object"==typeof window&&Object.assign(window,x),t.exports&&(t.exports=x)}]); \ No newline at end of file diff --git a/src/torus.js b/src/torus.js index 9be36f1..3cfd9b4 100644 --- a/src/torus.js +++ b/src/torus.js @@ -335,7 +335,9 @@ const render = (node, previous, next) => { } else if (attrName in node) { // @debug render_debug(`Set <${next.tag}> property ${attrName} = ${next.attrs[attrName]}`); - node[attrName] = next.attrs[attrName]; + if (previous.attrs[attrName] !== next.attrs[attrName]) { + node[attrName] = next.attrs[attrName]; + } } else { if (next.attrs[attrName] !== previous.attrs[attrName]) { // @debug