<commit_before>import patchOps from '../client/patchOps';
import domAttrsMutators from '../client/domAttrsMutators';
import { addListener, removeListener, removeListeners } from '../client/events/domEventManager';
import ATTRS_TO_EVENTS from '../client/events/attrsToEvents';
import escapeHtml from '../utils/escapeHtml';
import isInArray from '../utils/isInArray';
import console from '../utils/console';
import { isTrident, isEdge } from '../client/browsers';
import createElement from '../client/utils/createElement';
import createElementByHtml from '../client/utils/createElementByHtml';

const SHORT_TAGS = {
        area : true,
        base : true,
        br : true,
        col : true,
        command : true,
        embed : true,
        hr : true,
        img : true,
        input : true,
        keygen : true,
        link : true,
        menuitem : true,
        meta : true,
        param : true,
        source : true,
        track : true,
        wbr : true
    },
    USE_DOM_STRINGS = isTrident || isEdge;

class TagNode {
    constructor(tag) {
        this.type = TagNode;
        this._tag = tag;
        this._domNode = null;
        this._key = null;
        this._ns = null;
        this._attrs = null;
        this._children = null;
        this._escapeChildren = true;
    }

    getDomNode() {
        return this._domNode;
    }

    key(key) {
        this._key = key;
        return this;
    }

    ns(ns) {
        this._ns = ns;
        return this;
    }

    attrs(attrs) {
        this._attrs = attrs;
        return this;
    }

    children(children) {
        this._children = processChildren(children);
        return this;
    }

    html(html) {
        this._children = html;
        this._escapeChildren = false;
        return this;
    }

    renderToDom(parentNode) {
        if(!this._ns && parentNode && parentNode._ns) {
            this._ns = parentNode._ns;
        }

        const children = this._children;

        if(USE_DOM_STRINGS && children && typeof children !== 'string') {
            const domNode = createElementByHtml(this.renderToString(), this._tag, this._ns);
            this.adoptDom(domNode, parentNode);
            return domNode;
        }

        const domNode = createElement(this._ns, this._tag),
            attrs = this._attrs;

        if(children) {
            if(typeof children === 'string') {
                this._escapeChildren?
                    domNode.textContent = children :
                    domNode.innerHTML = children;
            }
            else {
                let i = 0;
                const len = children.length;

                while(i < len) {
                    domNode.appendChild(children[i++].renderToDom(this));
                }
            }
        }

        if(attrs) {
            let name, value;
            for(name in attrs) {
                (value = attrs[name]) != null &&
                    (ATTRS_TO_EVENTS[name]?
                        addListener(domNode, ATTRS_TO_EVENTS[name], value) :
                        domAttrsMutators(name).set(domNode, name, value));
            }
        }

        return this._domNode = domNode;
    }

    renderToString(ctx) {
        const tag = this._tag,
            ns = this._ns,
            attrs = this._attrs;

        let children = this._children,
            res = '<' + tag;

        if(ns) {
            res += ' xmlns="' + ns + '"';
        }

        if(attrs) {
            let name, value, attrHtml;
            for(name in attrs) {
                value = attrs[name];

                if(value != null) {
                    if(name === 'value') {
                        switch(tag) {
                            case 'textarea':
                                children = value;
                                continue;

                            case 'select':
                                ctx = { value : value, multiple : attrs.multiple };
                                continue;

                            case 'option':
                                if(ctx.multiple? isInArray(ctx.value, value) : ctx.value === value) {
                                    res += ' ' + domAttrsMutators('selected').toString('selected', true);
                                }
                        }
                    }

                    if(!ATTRS_TO_EVENTS[name] && (attrHtml = domAttrsMutators(name).toString(name, value))) {
                        res += ' ' + attrHtml;
                    }
                }
            }
        }

        if(SHORT_TAGS[tag]) {
            res += '/>';
        }
        else {
            res += '>';

            if(children) {
                if(typeof children === 'string') {
                    res += this._escapeChildren?
                        escapeHtml(children) :
                        children;
                }
                else {
                    let i = 0;
                    const len = children.length;

                    while(i < len) {
                        res += children[i++].renderToString(ctx);
                    }
                }
            }

            res += '</' + tag + '>';
        }

        return res;
    }

    adoptDom(domNode, parentNode) {
        if(!this._ns && parentNode && parentNode._ns) {
            this._ns = parentNode._ns;
        }

        this._domNode = domNode;

        const attrs = this._attrs,
            children = this._children;

        if(attrs) {
            let name, value;
            for(name in attrs) {
                if((value = attrs[name]) != null && ATTRS_TO_EVENTS[name]) {
                    addListener(domNode, ATTRS_TO_EVENTS[name], value);
                }
            }
        }

        if(children && typeof children !== 'string') {
            let i = 0;
            const len = children.length;

            if(len) {
                const domChildren = domNode.childNodes;
                while(i < len) {
                    children[i].adoptDom(domChildren[i], this);
                    ++i;
                }
            }
        }
    }

    mount() {
        const children = this._children;

        if(children && typeof children !== 'string') {
            let i = 0;
            const len = children.length;

            while(i < len) {
                children[i++].mount();
            }
        }
    }

    unmount() {
        const children = this._children;

        if(children && typeof children !== 'string') {
            let i = 0;
            const len = children.length;

            while(i < len) {
                children[i++].unmount();
            }
        }

        removeListeners(this._domNode);

        this._domNode = null;
    }

    patch(node, parentNode) {
        if(this === node) {
            return;
        }

        if(!node._ns && parentNode && parentNode._ns) {
            node._ns = parentNode._ns;
        }

        if(this.type !== node.type || this._tag !== node._tag || this._ns !== node._ns) {
            patchOps.replace(parentNode || null, this, node);
            return;
        }

        this._domNode && (node._domNode = this._domNode);

        this._patchChildren(node);
        this._patchAttrs(node);
    }

    _patchChildren(node) {
        const childrenA = this._children,
            childrenB = node._children;

        if(childrenA === childrenB) {
            return;
        }

        const isChildrenAText = typeof childrenA === 'string',
            isChildrenBText = typeof childrenB === 'string';

        if(isChildrenBText) {
            if(isChildrenAText) {
                patchOps.updateText(this, childrenB, node._escapeChildren);
                return;
            }

            childrenA && childrenA.length && patchOps.removeChildren(this);
            childrenB && patchOps.updateText(this, childrenB, node._escapeChildren);

            return;
        }

        if(!childrenB || !childrenB.length) {
            if(childrenA) {
                isChildrenAText?
                    patchOps.removeText(this) :
                    childrenA.length && patchOps.removeChildren(this);
            }

            return;
        }

        if(isChildrenAText && childrenA) {
            patchOps.removeText(this);
        }

        const childrenBLen = childrenB.length;

        if(isChildrenAText || !childrenA || !childrenA.length) {
            let iB = 0;
            while(iB < childrenBLen) {
                patchOps.appendChild(node, childrenB[iB++]);
            }
            return;
        }

        const childrenALen = childrenA.length;

        if(childrenALen === 1 && childrenBLen === 1) {
            childrenA[0].patch(childrenB[0], node);
            return;
        }

        let leftIdxA = 0,
            rightIdxA = childrenALen - 1,
            leftChildA = childrenA[leftIdxA],
            leftChildAKey = leftChildA._key,
            rightChildA = childrenA[rightIdxA],
            rightChildAKey = rightChildA._key,
            leftIdxB = 0,
            rightIdxB = childrenBLen - 1,
            leftChildB = childrenB[leftIdxB],
            leftChildBKey = leftChildB._key,
            rightChildB = childrenB[rightIdxB],
            rightChildBKey = rightChildB._key,
            updateLeftIdxA = false,
            updateRightIdxA = false,
            updateLeftIdxB = false,
            updateRightIdxB = false,
            childrenAIndicesToSkip = {},
            childrenAKeys, foundAChildIdx, foundAChild;

        while(leftIdxA <= rightIdxA && leftIdxB <= rightIdxB) {
            if(childrenAIndicesToSkip[leftIdxA]) {
                updateLeftIdxA = true;
            }
            else if(childrenAIndicesToSkip[rightIdxA]) {
                updateRightIdxA = true;
            }
            else if(leftChildAKey === leftChildBKey) {
                leftChildA.patch(leftChildB, node);
                updateLeftIdxA = true;
                updateLeftIdxB = true;
            }
            else if(rightChildAKey === rightChildBKey) {
                rightChildA.patch(rightChildB, node);
                updateRightIdxA = true;
                updateRightIdxB = true;
            }
            else if(leftChildAKey != null && leftChildAKey === rightChildBKey) {
                patchOps.moveChild(node, leftChildA, rightChildA, true);
                leftChildA.patch(rightChildB, node);
                updateLeftIdxA = true;
                updateRightIdxB = true;
            }
            else if(rightChildAKey != null && rightChildAKey === leftChildBKey) {
                patchOps.moveChild(node, rightChildA, leftChildA, false);
                rightChildA.patch(leftChildB, node);
                updateRightIdxA = true;
                updateLeftIdxB = true;
            }
            else if(leftChildAKey != null && leftChildBKey == null) {
                patchOps.insertChild(node, leftChildB, leftChildA);
                updateLeftIdxB = true;
            }
            else if(leftChildAKey == null && leftChildBKey != null) {
                patchOps.removeChild(node, leftChildA);
                updateLeftIdxA = true;
            }
            else {
                childrenAKeys || (childrenAKeys = buildKeys(childrenA, leftIdxA, rightIdxA));
                if((foundAChildIdx = childrenAKeys[leftChildBKey]) != null) {
                    foundAChild = childrenA[foundAChildIdx];
                    childrenAIndicesToSkip[foundAChildIdx] = true;
                    patchOps.moveChild(node, foundAChild, leftChildA, false);
                    foundAChild.patch(leftChildB, node);
                }
                else {
                    patchOps.insertChild(node, leftChildB, leftChildA);
                }
                updateLeftIdxB = true;
            }

            if(updateLeftIdxA) {
                updateLeftIdxA = false;
                if(++leftIdxA <= rightIdxA) {
                    leftChildA = childrenA[leftIdxA];
                    leftChildAKey = leftChildA._key;
                }
            }

            if(updateRightIdxA) {
                updateRightIdxA = false;
                if(--rightIdxA >= leftIdxA) {
                    rightChildA = childrenA[rightIdxA];
                    rightChildAKey = rightChildA._key;
                }
            }

            if(updateLeftIdxB) {
                updateLeftIdxB = false;
                if(++leftIdxB <= rightIdxB) {
                    leftChildB = childrenB[leftIdxB];
                    leftChildBKey = leftChildB._key;
                }
            }

            if(updateRightIdxB) {
                updateRightIdxB = false;
                if(--rightIdxB >= leftIdxB) {
                    rightChildB = childrenB[rightIdxB];
                    rightChildBKey = rightChildB._key;
                }
            }
        }

        while(leftIdxA <= rightIdxA) {
            if(!childrenAIndicesToSkip[leftIdxA]) {
                patchOps.removeChild(node, childrenA[leftIdxA]);
            }
            ++leftIdxA;
        }

        while(leftIdxB <= rightIdxB) {
            rightIdxB < childrenBLen - 1?
                patchOps.insertChild(node, childrenB[leftIdxB], childrenB[rightIdxB + 1]) :
                patchOps.appendChild(node, childrenB[leftIdxB]);
            ++leftIdxB;
        }
    }

    _patchAttrs(node) {
        const attrsA = this._attrs,
            attrsB = node._attrs;

        if(attrsA === attrsB) {
            return;
        }

        let attrName, attrAVal, attrBVal,
            isAttrAValArray, isAttrBValArray;

        if(attrsB) {
            for(attrName in attrsB) {
                attrBVal = attrsB[attrName];
                if(!attrsA || ((attrAVal = attrsA[attrName]) == null)) {
                    if(attrBVal != null) {
                        patchOps.updateAttr(this, attrName, attrBVal);
                    }
                }
                else if(attrBVal == null) {
                    patchOps.removeAttr(this, attrName);
                }
                else if(typeof attrBVal === 'object' && typeof attrAVal === 'object') {
                    isAttrBValArray = Array.isArray(attrBVal);
                    isAttrAValArray = Array.isArray(attrAVal);
                    if(isAttrBValArray || isAttrAValArray) {
                        if(isAttrBValArray && isAttrAValArray) {
                            this._patchAttrArr(attrName, attrAVal, attrBVal);
                        }
                        else {
                            patchOps.updateAttr(this, attrName, attrBVal);
                        }
                    }
                    else {
                        this._patchAttrObj(attrName, attrAVal, attrBVal);
                    }
                }
                else if(attrAVal !== attrBVal) {
                    patchOps.updateAttr(this, attrName, attrBVal);
                }
            }
        }

        if(attrsA) {
            for(attrName in attrsA) {
                if((!attrsB || !(attrName in attrsB)) && ((attrAVal = attrsA[attrName]) != null)) {
                    patchOps.removeAttr(this, attrName);
                }
            }
        }
    }

    _patchAttrArr(attrName, arrA, arrB) {
        if(arrA === arrB) {
            return;
        }

        const lenA = arrA.length;
        let hasDiff = false;

        if(lenA !== arrB.length) {
            hasDiff = true;
        }
        else {
            let i = 0;
            while(!hasDiff && i < lenA) {
                if(arrA[i] != arrB[i]) {
                    hasDiff = true;
                }
                ++i;
            }
        }

        hasDiff && patchOps.updateAttr(this, attrName, arrB);
    }

    _patchAttrObj(attrName, objA, objB) {
        if(objA === objB) {
            return;
        }

        let hasDiff = false,
            diffObj = {};

        for(let i in objB) {
            if(objA[i] != objB[i]) {
                hasDiff = true;
                diffObj[i] = objB[i];
            }
        }

        for(let i in objA) {
            if(objA[i] != null && !(i in objB)) {
                hasDiff = true;
                diffObj[i] = null;
            }
        }

        hasDiff && patchOps.updateAttr(this, attrName, diffObj);
    }
}

function processChildren(children) {
    if(children == null) {
        return null;
    }

    const typeOfChildren = typeof children;

    if(typeOfChildren === 'object') {
        const res = Array.isArray(children)? children : [children];

        if(process.env.NODE_ENV !== 'production') {
            checkChildren(res);
        }

        return res;
    }

    return typeOfChildren === 'string'?
        children :
        children.toString();
}

function checkChildren(children) {
    const keys = {},
        len = children.length;

    let i = 0,
        child;

    while(i < len) {
        child = children[i++];

        if(typeof child !== 'object') {
            console.error('Error! You mustn\'t use simple child in case of multiple children.');
        }
        //else if(child._key == null) {
        //    if(len > 1) {
        //        console.warn('Warning! You\'re using children without keys.');
        //    }
        //}
        else if(child._key in keys) {
            console.error('Error! Childrens\' keys must be unique across the children');
        }
        else {
            keys[child._key] = true;
        }
    }
}

function buildKeys(children, idxFrom, idxTo) {
    let res = {},
        child;

    while(idxFrom < idxTo) {
        child = children[idxFrom];
        child._key != null && (res[child._key] = idxFrom);
        ++idxFrom;
    }

    return res;
}

export default TagNode;
<commit_msg>Don't check unkeyed children<commit_after>import patchOps from '../client/patchOps';
import domAttrsMutators from '../client/domAttrsMutators';
import { addListener, removeListener, removeListeners } from '../client/events/domEventManager';
import ATTRS_TO_EVENTS from '../client/events/attrsToEvents';
import escapeHtml from '../utils/escapeHtml';
import isInArray from '../utils/isInArray';
import console from '../utils/console';
import { isTrident, isEdge } from '../client/browsers';
import createElement from '../client/utils/createElement';
import createElementByHtml from '../client/utils/createElementByHtml';

const SHORT_TAGS = {
        area : true,
        base : true,
        br : true,
        col : true,
        command : true,
        embed : true,
        hr : true,
        img : true,
        input : true,
        keygen : true,
        link : true,
        menuitem : true,
        meta : true,
        param : true,
        source : true,
        track : true,
        wbr : true
    },
    USE_DOM_STRINGS = isTrident || isEdge;

class TagNode {
    constructor(tag) {
        this.type = TagNode;
        this._tag = tag;
        this._domNode = null;
        this._key = null;
        this._ns = null;
        this._attrs = null;
        this._children = null;
        this._escapeChildren = true;
    }

    getDomNode() {
        return this._domNode;
    }

    key(key) {
        this._key = key;
        return this;
    }

    ns(ns) {
        this._ns = ns;
        return this;
    }

    attrs(attrs) {
        this._attrs = attrs;
        return this;
    }

    children(children) {
        this._children = processChildren(children);
        return this;
    }

    html(html) {
        this._children = html;
        this._escapeChildren = false;
        return this;
    }

    renderToDom(parentNode) {
        if(!this._ns && parentNode && parentNode._ns) {
            this._ns = parentNode._ns;
        }

        const children = this._children;

        if(USE_DOM_STRINGS && children && typeof children !== 'string') {
            const domNode = createElementByHtml(this.renderToString(), this._tag, this._ns);
            this.adoptDom(domNode, parentNode);
            return domNode;
        }

        const domNode = createElement(this._ns, this._tag),
            attrs = this._attrs;

        if(children) {
            if(typeof children === 'string') {
                this._escapeChildren?
                    domNode.textContent = children :
                    domNode.innerHTML = children;
            }
            else {
                let i = 0;
                const len = children.length;

                while(i < len) {
                    domNode.appendChild(children[i++].renderToDom(this));
                }
            }
        }

        if(attrs) {
            let name, value;
            for(name in attrs) {
                (value = attrs[name]) != null &&
                    (ATTRS_TO_EVENTS[name]?
                        addListener(domNode, ATTRS_TO_EVENTS[name], value) :
                        domAttrsMutators(name).set(domNode, name, value));
            }
        }

        return this._domNode = domNode;
    }

    renderToString(ctx) {
        const tag = this._tag,
            ns = this._ns,
            attrs = this._attrs;

        let children = this._children,
            res = '<' + tag;

        if(ns) {
            res += ' xmlns="' + ns + '"';
        }

        if(attrs) {
            let name, value, attrHtml;
            for(name in attrs) {
                value = attrs[name];

                if(value != null) {
                    if(name === 'value') {
                        switch(tag) {
                            case 'textarea':
                                children = value;
                                continue;

                            case 'select':
                                ctx = { value : value, multiple : attrs.multiple };
                                continue;

                            case 'option':
                                if(ctx.multiple? isInArray(ctx.value, value) : ctx.value === value) {
                                    res += ' ' + domAttrsMutators('selected').toString('selected', true);
                                }
                        }
                    }

                    if(!ATTRS_TO_EVENTS[name] && (attrHtml = domAttrsMutators(name).toString(name, value))) {
                        res += ' ' + attrHtml;
                    }
                }
            }
        }

        if(SHORT_TAGS[tag]) {
            res += '/>';
        }
        else {
            res += '>';

            if(children) {
                if(typeof children === 'string') {
                    res += this._escapeChildren?
                        escapeHtml(children) :
                        children;
                }
                else {
                    let i = 0;
                    const len = children.length;

                    while(i < len) {
                        res += children[i++].renderToString(ctx);
                    }
                }
            }

            res += '</' + tag + '>';
        }

        return res;
    }

    adoptDom(domNode, parentNode) {
        if(!this._ns && parentNode && parentNode._ns) {
            this._ns = parentNode._ns;
        }

        this._domNode = domNode;

        const attrs = this._attrs,
            children = this._children;

        if(attrs) {
            let name, value;
            for(name in attrs) {
                if((value = attrs[name]) != null && ATTRS_TO_EVENTS[name]) {
                    addListener(domNode, ATTRS_TO_EVENTS[name], value);
                }
            }
        }

        if(children && typeof children !== 'string') {
            let i = 0;
            const len = children.length;

            if(len) {
                const domChildren = domNode.childNodes;
                while(i < len) {
                    children[i].adoptDom(domChildren[i], this);
                    ++i;
                }
            }
        }
    }

    mount() {
        const children = this._children;

        if(children && typeof children !== 'string') {
            let i = 0;
            const len = children.length;

            while(i < len) {
                children[i++].mount();
            }
        }
    }

    unmount() {
        const children = this._children;

        if(children && typeof children !== 'string') {
            let i = 0;
            const len = children.length;

            while(i < len) {
                children[i++].unmount();
            }
        }

        removeListeners(this._domNode);

        this._domNode = null;
    }

    patch(node, parentNode) {
        if(this === node) {
            return;
        }

        if(!node._ns && parentNode && parentNode._ns) {
            node._ns = parentNode._ns;
        }

        if(this.type !== node.type || this._tag !== node._tag || this._ns !== node._ns) {
            patchOps.replace(parentNode || null, this, node);
            return;
        }

        this._domNode && (node._domNode = this._domNode);

        this._patchChildren(node);
        this._patchAttrs(node);
    }

    _patchChildren(node) {
        const childrenA = this._children,
            childrenB = node._children;

        if(childrenA === childrenB) {
            return;
        }

        const isChildrenAText = typeof childrenA === 'string',
            isChildrenBText = typeof childrenB === 'string';

        if(isChildrenBText) {
            if(isChildrenAText) {
                patchOps.updateText(this, childrenB, node._escapeChildren);
                return;
            }

            childrenA && childrenA.length && patchOps.removeChildren(this);
            childrenB && patchOps.updateText(this, childrenB, node._escapeChildren);

            return;
        }

        if(!childrenB || !childrenB.length) {
            if(childrenA) {
                isChildrenAText?
                    patchOps.removeText(this) :
                    childrenA.length && patchOps.removeChildren(this);
            }

            return;
        }

        if(isChildrenAText && childrenA) {
            patchOps.removeText(this);
        }

        const childrenBLen = childrenB.length;

        if(isChildrenAText || !childrenA || !childrenA.length) {
            let iB = 0;
            while(iB < childrenBLen) {
                patchOps.appendChild(node, childrenB[iB++]);
            }
            return;
        }

        const childrenALen = childrenA.length;

        if(childrenALen === 1 && childrenBLen === 1) {
            childrenA[0].patch(childrenB[0], node);
            return;
        }

        let leftIdxA = 0,
            rightIdxA = childrenALen - 1,
            leftChildA = childrenA[leftIdxA],
            leftChildAKey = leftChildA._key,
            rightChildA = childrenA[rightIdxA],
            rightChildAKey = rightChildA._key,
            leftIdxB = 0,
            rightIdxB = childrenBLen - 1,
            leftChildB = childrenB[leftIdxB],
            leftChildBKey = leftChildB._key,
            rightChildB = childrenB[rightIdxB],
            rightChildBKey = rightChildB._key,
            updateLeftIdxA = false,
            updateRightIdxA = false,
            updateLeftIdxB = false,
            updateRightIdxB = false,
            childrenAIndicesToSkip = {},
            childrenAKeys, foundAChildIdx, foundAChild;

        while(leftIdxA <= rightIdxA && leftIdxB <= rightIdxB) {
            if(childrenAIndicesToSkip[leftIdxA]) {
                updateLeftIdxA = true;
            }
            else if(childrenAIndicesToSkip[rightIdxA]) {
                updateRightIdxA = true;
            }
            else if(leftChildAKey === leftChildBKey) {
                leftChildA.patch(leftChildB, node);
                updateLeftIdxA = true;
                updateLeftIdxB = true;
            }
            else if(rightChildAKey === rightChildBKey) {
                rightChildA.patch(rightChildB, node);
                updateRightIdxA = true;
                updateRightIdxB = true;
            }
            else if(leftChildAKey != null && leftChildAKey === rightChildBKey) {
                patchOps.moveChild(node, leftChildA, rightChildA, true);
                leftChildA.patch(rightChildB, node);
                updateLeftIdxA = true;
                updateRightIdxB = true;
            }
            else if(rightChildAKey != null && rightChildAKey === leftChildBKey) {
                patchOps.moveChild(node, rightChildA, leftChildA, false);
                rightChildA.patch(leftChildB, node);
                updateRightIdxA = true;
                updateLeftIdxB = true;
            }
            else if(leftChildAKey != null && leftChildBKey == null) {
                patchOps.insertChild(node, leftChildB, leftChildA);
                updateLeftIdxB = true;
            }
            else if(leftChildAKey == null && leftChildBKey != null) {
                patchOps.removeChild(node, leftChildA);
                updateLeftIdxA = true;
            }
            else {
                childrenAKeys || (childrenAKeys = buildKeys(childrenA, leftIdxA, rightIdxA));
                if((foundAChildIdx = childrenAKeys[leftChildBKey]) != null) {
                    foundAChild = childrenA[foundAChildIdx];
                    childrenAIndicesToSkip[foundAChildIdx] = true;
                    patchOps.moveChild(node, foundAChild, leftChildA, false);
                    foundAChild.patch(leftChildB, node);
                }
                else {
                    patchOps.insertChild(node, leftChildB, leftChildA);
                }
                updateLeftIdxB = true;
            }

            if(updateLeftIdxA) {
                updateLeftIdxA = false;
                if(++leftIdxA <= rightIdxA) {
                    leftChildA = childrenA[leftIdxA];
                    leftChildAKey = leftChildA._key;
                }
            }

            if(updateRightIdxA) {
                updateRightIdxA = false;
                if(--rightIdxA >= leftIdxA) {
                    rightChildA = childrenA[rightIdxA];
                    rightChildAKey = rightChildA._key;
                }
            }

            if(updateLeftIdxB) {
                updateLeftIdxB = false;
                if(++leftIdxB <= rightIdxB) {
                    leftChildB = childrenB[leftIdxB];
                    leftChildBKey = leftChildB._key;
                }
            }

            if(updateRightIdxB) {
                updateRightIdxB = false;
                if(--rightIdxB >= leftIdxB) {
                    rightChildB = childrenB[rightIdxB];
                    rightChildBKey = rightChildB._key;
                }
            }
        }

        while(leftIdxA <= rightIdxA) {
            if(!childrenAIndicesToSkip[leftIdxA]) {
                patchOps.removeChild(node, childrenA[leftIdxA]);
            }
            ++leftIdxA;
        }

        while(leftIdxB <= rightIdxB) {
            rightIdxB < childrenBLen - 1?
                patchOps.insertChild(node, childrenB[leftIdxB], childrenB[rightIdxB + 1]) :
                patchOps.appendChild(node, childrenB[leftIdxB]);
            ++leftIdxB;
        }
    }

    _patchAttrs(node) {
        const attrsA = this._attrs,
            attrsB = node._attrs;

        if(attrsA === attrsB) {
            return;
        }

        let attrName, attrAVal, attrBVal,
            isAttrAValArray, isAttrBValArray;

        if(attrsB) {
            for(attrName in attrsB) {
                attrBVal = attrsB[attrName];
                if(!attrsA || ((attrAVal = attrsA[attrName]) == null)) {
                    if(attrBVal != null) {
                        patchOps.updateAttr(this, attrName, attrBVal);
                    }
                }
                else if(attrBVal == null) {
                    patchOps.removeAttr(this, attrName);
                }
                else if(typeof attrBVal === 'object' && typeof attrAVal === 'object') {
                    isAttrBValArray = Array.isArray(attrBVal);
                    isAttrAValArray = Array.isArray(attrAVal);
                    if(isAttrBValArray || isAttrAValArray) {
                        if(isAttrBValArray && isAttrAValArray) {
                            this._patchAttrArr(attrName, attrAVal, attrBVal);
                        }
                        else {
                            patchOps.updateAttr(this, attrName, attrBVal);
                        }
                    }
                    else {
                        this._patchAttrObj(attrName, attrAVal, attrBVal);
                    }
                }
                else if(attrAVal !== attrBVal) {
                    patchOps.updateAttr(this, attrName, attrBVal);
                }
            }
        }

        if(attrsA) {
            for(attrName in attrsA) {
                if((!attrsB || !(attrName in attrsB)) && ((attrAVal = attrsA[attrName]) != null)) {
                    patchOps.removeAttr(this, attrName);
                }
            }
        }
    }

    _patchAttrArr(attrName, arrA, arrB) {
        if(arrA === arrB) {
            return;
        }

        const lenA = arrA.length;
        let hasDiff = false;

        if(lenA !== arrB.length) {
            hasDiff = true;
        }
        else {
            let i = 0;
            while(!hasDiff && i < lenA) {
                if(arrA[i] != arrB[i]) {
                    hasDiff = true;
                }
                ++i;
            }
        }

        hasDiff && patchOps.updateAttr(this, attrName, arrB);
    }

    _patchAttrObj(attrName, objA, objB) {
        if(objA === objB) {
            return;
        }

        let hasDiff = false,
            diffObj = {};

        for(let i in objB) {
            if(objA[i] != objB[i]) {
                hasDiff = true;
                diffObj[i] = objB[i];
            }
        }

        for(let i in objA) {
            if(objA[i] != null && !(i in objB)) {
                hasDiff = true;
                diffObj[i] = null;
            }
        }

        hasDiff && patchOps.updateAttr(this, attrName, diffObj);
    }
}

function processChildren(children) {
    if(children == null) {
        return null;
    }

    const typeOfChildren = typeof children;

    if(typeOfChildren === 'object') {
        const res = Array.isArray(children)? children : [children];

        if(process.env.NODE_ENV !== 'production') {
            checkChildren(res);
        }

        return res;
    }

    return typeOfChildren === 'string'?
        children :
        children.toString();
}

function checkChildren(children) {
    const keys = {},
        len = children.length;

    let i = 0,
        child;

    while(i < len) {
        child = children[i++];

        if(typeof child !== 'object') {
            console.error('Error! You mustn\'t use simple child in case of multiple children.');
        }
        //else if(child._key == null) {
        //    if(len > 1) {
        //        console.warn('Warning! You\'re using children without keys.');
        //    }
        //}
        else if(child._key != null) {
            if(child._key in keys) {
                console.error('Error! Childrens\' keys must be unique across the children');
            }
            else {
                keys[child._key] = true;
            }
        }
    }
}

function buildKeys(children, idxFrom, idxTo) {
    let res = {},
        child;

    while(idxFrom < idxTo) {
        child = children[idxFrom];
        child._key != null && (res[child._key] = idxFrom);
        ++idxFrom;
    }

    return res;
}

export default TagNode;
<|endoftext|>


<commit_before>/**
 * App
 */
var almobile = angular.module('almobile',[]);

/**
 * Diretiva que troca vírgula por ponto em números decimais
 */
almobile.directive('isNumber', function () {
	return {
		require: 'ngModel',
		link: function (scope) {
		scope.$watch('wks.number', function(newValue,oldValue) {
                var num = "";
                num = newValue;
                num = num.replace(",",".");
                scope.inputValue = num;
            });
		}
	};
});

/**
 * Controller do App
 */
function TheController ($scope, $timeout) {
    var db;
    if ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB) {
        db = new MyDB();
    } else {
        db  = new MySqlDB();
        db.init();
    }

    /**
     * Inicia as configurações
     */
    $scope.init = function(){
        $scope.trava = false;
        $scope.wkflow = "disciplinas";
        $timeout($scope.refreshList, 400);
    };

    /**
     * Atualiza a lista de disciplinas
     */
    $scope.refreshList = function(){
        $scope.itens = [];
        db.listAll(function (err, value) {
            $scope.itens.push(value);
        });
        $timeout(list, 200);
        //Ainda não sei porque, mas o bind só acontece se eu fizer esse timeout
        function list() {
            console.log("OK");
        }
    }
    /**
     * Abre e fecha o menu do aplicativo
     */
    $scope.toggleMenu = function(){
        console.log(angular.element(document.querySelector( '#menu' )));
        if($scope.wkflow == "menu"){
            $scope.changeView("disciplinas");
        } else {
            $scope.changeView("menu");
        }
    };
    /**
     * Muda a visão atual
     */
    $scope.changeView = function(w){
        $scope.wkflow = w;
    };
    /**
     * Faz o Toggle da nota para o modo de edição
     */
    $scope.setNota = function(nota, id){
        elemsp = document.getElementById("sp"+nota+id);
        elemlb = document.getElementById("lb"+nota+id);
        elemip = document.getElementById("ip"+nota+id);

    	//Escondendo as boxes input.
		var elements = [];
		elements = document.getElementsByClassName("nota");
		for(i=0; i<elements.length;i++){
		     elements[i].style.display = "none";
		}
		if(elemsp.textContent == "OK" && document.activeElement.getAttribute("id") != "ip"+nota+id){
	    	elemsp.textContent = nota;
	    	elemsp.style.background = '#5A6A7A';
			elemlb.style.display = "inline-block";
			$scope.calcula();
            $scope.trava = false;
            db.save($scope.itens[id]);
		} else {
            if (!$scope.trava){
                prepVar(true);
                $scope.trava = true;
            }
	    	elemsp.textContent = "OK";
	    	elemsp.style.background = '#1CAF9A';
			elemip.style.display = "inline-block";
			elemip.focus();
			elemlb.style.display = "none";
		}
    };

    /**
     * Adiciona uma nova disciplina
     */
    $scope.addItem = function(){
        if($scope.novadisc.length > 1) {
            var newItem = {
                    disciplina: $scope.novadisc,
                    n1: {
                        value: 0,
                        calc: false,
                    },
                    n2:  {
                        value: 0,
                        calc: false,
                    },
                    n3:  {
                        value: 0,
                        calc: false,
                    },
                    n4:  {
                        value: 0,
                        calc: false,
                    },
                    status : ''
                };
            db.save(newItem, function(error, result){
                $scope.novadisc = "";
                $scope.itens.push(result);
                $timeout(list, 200);
                //Ainda não sei porque, mas o bind só acontece se eu fizer esse timeout
                function list() {
                    console.log("OK");
                }
            });
        }
    }

    /**
     * Mostra disciplina
     */
    $scope.show = function(id){
        var i=0,
        elms = document.querySelectorAll("#item"+id+" .subitems li");
        block = (elms[0].style.display == "block");

        elem = document.getElementById("item"+id);
    	if (elem.style.backgroundColor == "rgb(255, 245, 245)"){
    		elem.style.backgroundColor = '#A8A8A8';
    	} else {
    		elem.style.backgroundColor = '#FFF5F5';
    	}
         for(i=0;i<elms.length;i++) {
            elms[i].style.display = ((block) ? "none" : "block");
         }
    }

    /**
     * Deleta disciplina
     */
    $scope.delete = function(id, key){
    	//$scope.itens.splice(id,1);
        db.del(key, function(){
            $timeout($scope.refreshList, 200);
        });
    }

    /**
     * Calcula os valores das notas;
     */
    $scope.calcula = function(){
        prepVar();
		for(i=0; i<$scope.itens.length;i++){
			/*Soma N1 + N2 */
			var n12   = $scope.itens[i].n1.value + $scope.itens[i].n2.value,
				total = 0;

            /* Se o aluno informou N1*/
            if($scope.itens[i].n1.value > 0 && $scope.itens[i].n1.value < 10){
                /* Se o aluno informou N2*/
                if($scope.itens[i].n2.value > 0 && $scope.itens[i].n2.value < 10) {
                    /* Se o aluno reprovou direto*/
                    if(!(n12 > 6 && n12 <= 20)){
                        $scope.itens[i].status = "Reprovado";
                    } else{
                    /* Se o aluno não reprovou direto*/
                        $scope.itens[i].status = "";
                        /* Se o aluno passou direto*/
                        if(n12 >= 16){
                            $scope.itens[i].status = "Aprovado";
                        } else if($scope.itens[i].n3.value == 0) {
                        /* Se o aluno não fez N3*/
                            $scope.itens[i].n3.value = (18-(n12)).toFixed(2);
                            $scope.itens[i].n3.calc = 1;
                            $scope.itens[i].status = "Não desanime, a N3 te espera";
                            if ($scope.itens[i].n3.value > 10) {
                                $scope.itens[i].n3.value = (10).toFixed(2);
                                total = n12+10;
                                console.log(total);
                                $scope.itens[i].n4.value = (12 -(total/3)).toFixed(2);
                                $scope.itens[i].n4.calc = 1;
                                $scope.itens[i].status = "Estude! Você não escapa da N4";
                            }
                        } else if($scope.itens[i].n3.value >0 && $scope.itens[i].n3.value < 10) {
                        /* Se o aluno já fez N3*/
                            total = n12+$scope.itens[i].n3.value;
                            if(total >=18) {
                                $scope.itens[i].status = "Aprovado";
                            } else if($scope.itens[i].n4.value > 0) {
                        /* Se o aluno já fez N4*/
                                total = (((total/3)+$scope.itens[i].n4.value)/2).toFixed(2);
                                if(total > 6) {
                                    $scope.itens[i].status = "Aprovado";
                                } else {
                                    $scope.itens[i].status = "Reprovado";
                                }
                            } else {
                        /* Se o aluno não fez N4*/
                                $scope.itens[i].n4.value = (12 -(total/3)).toFixed(2);
                                $scope.itens[i].n4.calc = 1;
                                $scope.itens[i].status = "Estude! Você não escapa da N4";
                            }
                        }
                    }
                } else {
                /* Se o aluno não informou N2*/
                    $scope.itens[i].n4.value = 0;
                    $scope.itens[i].n3.value = 0;
                    $scope.itens[i].n2.value = (16 - $scope.itens[i].n1.value).toFixed(2);
                    $scope.itens[i].n2.calc = 1;
                    if ($scope.itens[i].n2.value > 10) {
                        $scope.itens[i].n3.value = ($scope.itens[i].n2.value - 8).toFixed(2);
                        $scope.itens[i].n3.calc = 1;
                        $scope.itens[i].n2.value = (10).toFixed(2);
                        $scope.itens[i].status = "Não desanime, a N3 te espera";
                    }
                }
            }
		}
        prepVar(false,true);
    }
    /**
     * Prepara as notas para edição ou cálculo
     */
    var prepVar = function(limpa,fixa){
        /*Preparando variáveis*/
        for(i=0; i<$scope.itens.length;i++){
            for (j=1;j<5;j++){
                if($scope.itens[i]['n'+j].calc && limpa){
                    $scope.itens[i]['n'+j].value = 0;
                    $scope.itens[i]['n'+j].calc = 0;
                    $scope.itens[i]['status'] = '';
                } else {
                    if(fixa){
                        $scope.itens[i]['n'+j].value = isNaN(parseFloat($scope.itens[i]['n'+j].value)) ? 0 : parseFloat($scope.itens[i]['n'+j].value).toFixed(2);
                    } else {
                        $scope.itens[i]['n'+j].value = isNaN(parseFloat($scope.itens[i]['n'+j].value)) ? 0 : parseFloat($scope.itens[i]['n'+j].value);
                    }
                }
            }
        }
    };
}
<commit_msg>:chicken:<commit_after>/**
 * App
 */
var almobile = angular.module('almobile',[]);

/**
 * Controller do App
 */
function TheController ($scope, $timeout) {
    var db;
    if ( window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB) {
        db = new MyDB();
    } else {
        db  = new MySqlDB();
        db.init();
    }

    /**
     * Inicia as configurações
     */
    $scope.init = function(){
        $scope.trava = false;
        $scope.wkflow = "disciplinas";
        $timeout($scope.refreshList, 400);
    };

    /**
     * Atualiza a lista de disciplinas
     */
    $scope.refreshList = function(){
        $scope.itens = [];
        db.listAll(function (err, value) {
            $scope.itens.push(value);
        });
        $timeout(list, 200);
        //Ainda não sei porque, mas o bind só acontece se eu fizer esse timeout
        function list() {
            console.log("OK");
        }
    }
    /**
     * Abre e fecha o menu do aplicativo
     */
    $scope.toggleMenu = function(){
        console.log(angular.element(document.querySelector( '#menu' )));
        if($scope.wkflow == "menu"){
            $scope.changeView("disciplinas");
        } else {
            $scope.changeView("menu");
        }
    };
    /**
     * Muda a visão atual
     */
    $scope.changeView = function(w){
        $scope.wkflow = w;
    };
    /**
     * Faz o Toggle da nota para o modo de edição
     */
    $scope.setNota = function(nota, id){
        elemsp = document.getElementById("sp"+nota+id);
        elemlb = document.getElementById("lb"+nota+id);
        elemip = document.getElementById("ip"+nota+id);

    	//Escondendo as boxes input.
		var elements = [];
		elements = document.getElementsByClassName("nota");
		for(i=0; i<elements.length;i++){
		     elements[i].style.display = "none";
		}
		if(elemsp.textContent == "OK" && document.activeElement.getAttribute("id") != "ip"+nota+id){
		$scope.itens[id][nota].value = elemip.value.replace(",",".");
	    	elemsp.textContent = nota;
	    	elemsp.style.background = '#5A6A7A';
			elemlb.style.display = "inline-block";
			$scope.calcula();
            $scope.trava = false;
            db.save($scope.itens[id]);
		} else {
            if (!$scope.trava){
                prepVar(true);
                $scope.trava = true;
            }
	    	elemsp.textContent = "OK";
	    	elemsp.style.background = '#1CAF9A';
			elemip.style.display = "inline-block";
			elemip.focus();
			elemlb.style.display = "none";
		}
    };

    /**
     * Adiciona uma nova disciplina
     */
    $scope.addItem = function(){
        if($scope.novadisc.length > 1) {
            var newItem = {
                    disciplina: $scope.novadisc,
                    n1: {
                        value: 0,
                        calc: false,
                    },
                    n2:  {
                        value: 0,
                        calc: false,
                    },
                    n3:  {
                        value: 0,
                        calc: false,
                    },
                    n4:  {
                        value: 0,
                        calc: false,
                    },
                    status : ''
                };
            db.save(newItem, function(error, result){
                $scope.novadisc = "";
                $scope.itens.push(result);
                $timeout(list, 200);
                //Ainda não sei porque, mas o bind só acontece se eu fizer esse timeout
                function list() {
                    console.log("OK");
                }
            });
        }
    }

    /**
     * Mostra disciplina
     */
    $scope.show = function(id){
        var i=0,
        elms = document.querySelectorAll("#item"+id+" .subitems li");
        block = (elms[0].style.display == "block");

        elem = document.getElementById("item"+id);
    	if (elem.style.backgroundColor == "rgb(255, 245, 245)"){
    		elem.style.backgroundColor = '#A8A8A8';
    	} else {
    		elem.style.backgroundColor = '#FFF5F5';
    	}
         for(i=0;i<elms.length;i++) {
            elms[i].style.display = ((block) ? "none" : "block");
         }
    }

    /**
     * Deleta disciplina
     */
    $scope.delete = function(id, key){
    	//$scope.itens.splice(id,1);
        db.del(key, function(){
            $timeout($scope.refreshList, 200);
        });
    }

    /**
     * Calcula os valores das notas;
     */
    $scope.calcula = function(){
        prepVar();
		for(i=0; i<$scope.itens.length;i++){
			/*Soma N1 + N2 */
			var n12   = $scope.itens[i].n1.value + $scope.itens[i].n2.value,
				total = 0;

            /* Se o aluno informou N1*/
            if($scope.itens[i].n1.value > 0 && $scope.itens[i].n1.value < 10){
                /* Se o aluno informou N2*/
                if($scope.itens[i].n2.value > 0 && $scope.itens[i].n2.value < 10) {
                    /* Se o aluno reprovou direto*/
                    if(!(n12 > 6 && n12 <= 20)){
                        $scope.itens[i].status = "Reprovado";
                    } else{
                    /* Se o aluno não reprovou direto*/
                        $scope.itens[i].status = "";
                        /* Se o aluno passou direto*/
                        if(n12 >= 16){
                            $scope.itens[i].status = "Aprovado";
                        } else if($scope.itens[i].n3.value == 0) {
                        /* Se o aluno não fez N3*/
                            $scope.itens[i].n3.value = (18-(n12)).toFixed(2);
                            $scope.itens[i].n3.calc = 1;
                            $scope.itens[i].status = "Não desanime, a N3 te espera";
                            if ($scope.itens[i].n3.value > 10) {
                                $scope.itens[i].n3.value = (10).toFixed(2);
                                total = n12+10;
                                console.log(total);
                                $scope.itens[i].n4.value = (12 -(total/3)).toFixed(2);
                                $scope.itens[i].n4.calc = 1;
                                $scope.itens[i].status = "Estude! Você não escapa da N4";
                            }
                        } else if($scope.itens[i].n3.value >0 && $scope.itens[i].n3.value < 10) {
                        /* Se o aluno já fez N3*/
                            total = n12+$scope.itens[i].n3.value;
                            if(total >=18) {
                                $scope.itens[i].status = "Aprovado";
                            } else if($scope.itens[i].n4.value > 0) {
                        /* Se o aluno já fez N4*/
                                total = (((total/3)+$scope.itens[i].n4.value)/2).toFixed(2);
                                if(total > 6) {
                                    $scope.itens[i].status = "Aprovado";
                                } else {
                                    $scope.itens[i].status = "Reprovado";
                                }
                            } else {
                        /* Se o aluno não fez N4*/
                                $scope.itens[i].n4.value = (12 -(total/3)).toFixed(2);
                                $scope.itens[i].n4.calc = 1;
                                $scope.itens[i].status = "Estude! Você não escapa da N4";
                            }
                        }
                    }
                } else {
                /* Se o aluno não informou N2*/
                    $scope.itens[i].n4.value = 0;
                    $scope.itens[i].n3.value = 0;
                    $scope.itens[i].n2.value = (16 - $scope.itens[i].n1.value).toFixed(2);
                    $scope.itens[i].n2.calc = 1;
                    if ($scope.itens[i].n2.value > 10) {
                        $scope.itens[i].n3.value = ($scope.itens[i].n2.value - 8).toFixed(2);
                        $scope.itens[i].n3.calc = 1;
                        $scope.itens[i].n2.value = (10).toFixed(2);
                        $scope.itens[i].status = "Não desanime, a N3 te espera";
                    }
                }
            }
		}
        prepVar(false,true);
    }
    /**
     * Prepara as notas para edição ou cálculo
     */
    var prepVar = function(limpa,fixa){
        /*Preparando variáveis*/
        for(i=0; i<$scope.itens.length;i++){
            for (j=1;j<5;j++){
                if($scope.itens[i]['n'+j].calc && limpa){
                    $scope.itens[i]['n'+j].value = 0;
                    $scope.itens[i]['n'+j].calc = 0;
                    $scope.itens[i]['status'] = '';
                } else {
                    if(fixa){
                        $scope.itens[i]['n'+j].value = isNaN(parseFloat($scope.itens[i]['n'+j].value)) ? 0 : parseFloat($scope.itens[i]['n'+j].value).toFixed(2);
                    } else {
                        $scope.itens[i]['n'+j].value = isNaN(parseFloat($scope.itens[i]['n'+j].value)) ? 0 : parseFloat($scope.itens[i]['n'+j].value);
                    }
                }
            }
        }
    };
}
<|endoftext|>


<commit_before>/*
 * file name :
 * author : linwu
 * datetime : 2015-1-22
 */
var cssFactory;
var editor_code_source;
var $btn_analysis = $("#j_btn_analysis");
var $top_level_class = $("#top_level_class");
var $no_extend_class = $("#no_extend_class");
var $status_class = $("#status_class");
var $ignore_class = $("#ignore_class");
var input_default_config = {
    "top_level_class": ["^\.m-"],
    "no_extend_class": ["ui-box", "ui-tab"],
    "status_class": ["show", "hide", "hidden", "cur", "current", "open", "close", "active"],
    "ignore_class": ["^\.f-", "^\.j-", "clearfix"]
};
var input_config = {};

$(function () {
    initTab();
    initEditer();
    initCssFactory();
    initZeroClipboard();

    if (window.localStorage) {
        if (localStorage.getItem("cssFactory_input_config")) {
            input_config = JSON.parse(localStorage.getItem("cssFactory_input_config"));
            $top_level_class.val(input_config.top_level_class);
            $no_extend_class.val(input_config.no_extend_class);
            $status_class.val(input_config.status_class);
            $ignore_class.val(input_config.ignore_class);
            // jquery.tag-editor 初始化
            $(".factory-config .form-control").tagEditor({placeholder: 'Enter tags ...'});
        } else {
            setDefaultValue();
        }
    }else{
        setDefaultValue();
    }

    $("#j_btn_reset").on("click",function () {
        setDefaultValue();
        localStorage.setItem("cssFactory_input_config", JSON.stringify(input_default_config));
    });

    $(".m-cssfactory .ui-tab-nav li").on("click", function () {
        var $this = $(this);
        if ($this.index() == 0) {
            $btn_analysis.removeClass("disabled");
        } else {
            $btn_analysis.addClass("disabled");
        }
    });

    $btn_analysis.on("click", function () {
        var $this = $(this);

        input_config = {
            top_level_class: $top_level_class.val(),
            no_extend_class: $no_extend_class.val(),
            status_class: $status_class.val(),
            ignore_class: $ignore_class.val()
        };
        localStorage.setItem("cssFactory_input_config", JSON.stringify(input_config));

        if (!$this.hasClass("disabled")) {
            $(".m-cssfactory .ui-tab-nav li:eq(1)").trigger("click");
        }
    });

});

/**
 * 初始化选项卡
 * author : lw
 */
function initTab() {
    // 约定, 选项卡导航包含在".ui-tab-nav"内, 对应的选项卡内容必须有".ui-tab-content"样式, 并按导航的顺序排放.
    var fn_tab = function (e, $tab) {
        var $this = $(this);
        var $tab = $this.closest(".ui-tab,.ui-tab-hover");
        var $tab_nav_item = $this;
        var tab_index;

        e.preventDefault();

        while (!$tab_nav_item.parent().hasClass("ui-tab-nav")) {
            $tab_nav_item = $tab_nav_item.parent();
        }
        tab_index = $tab_nav_item.index();

        $tab.find(".ui-tab-content.active,.ui-tab-nav .active").removeClass("active");
        $tab.find(".ui-tab-nav").children().eq(tab_index).add($tab.find(".ui-tab-content").eq(tab_index)).addClass("active");
        if (!!$.fn.lazyload) {
            $tab.find(".ui-tab-content").eq(tab_index).find("img").trigger("appear");
        }
    };
    if ($(".ui-tab").length > 0) {
        $(".ui-tab").on("click", ".ui-tab-nav li", function (e) {
            fn_tab.call(this, e);
        });
    }
    if ($(".ui-tab-hover").length > 0) {
        $(".ui-tab-hover").on("hover", ".ui-tab-nav li", function (e) {
            fn_tab.call(this, e);
        });
    }
}

/**
 * 初始化编辑器
 */
function initEditer() {
    editor_code_source = CodeMirror.fromTextArea(document.getElementById("j_code_source"), {
        lineNumbers: true,
        tabSize: 4,
        lineWrapping: true,
        styleActiveLine: false,
        matchBrackets: false,
        mode: "xml",
        theme: "pastel-on-dark"
    });
    CodeMirror.on(editor_code_source, "change", function (e) {
        $("#j_code_source").val(e.getValue());
    });
}

/**
 * 初始化代码工厂
 */
function initCssFactory() {
    if (window.CssFactory) {
        cssFactory = new CssFactory({
            beforeAnalysis: function () {
                var $top_level_class = $("#top_level_class");
                var $no_extend_class = $("#no_extend_class");
                var $status_class = $("#status_class");
                var $ignore_class = $("#ignore_class");

                cssFactory.config({
                    arr_top_level_class: $top_level_class.val().replace("/\\/g", "\\\\").split(/\s*,\s*/),
                    arr_no_extend_class: $no_extend_class.val().split(/\s*,\s*/),
                    arr_status_class: $status_class.val().split(/\s*,\s*/),
                    arr_ignore_class: $ignore_class.val().split(/\s*,\s*/)
                }, true);
            }
        });
    }
}

function initZeroClipboard() {
    var copy_btn = new ZeroClipboard(document.getElementById("j_btn_copy"));

    copy_btn.on("ready", function (readyEvent) {
        copy_btn.on("aftercopy", function (event) {
            // `this` === `client`
            // `event.target` === the element that was clicked
            $(event.target).text("已复制!").addClass("active");
            setTimeout(function () {
                $(event.target).text("复制代码").removeClass("active");
            }, 3000);
            //console.log("Copied text to clipboard: " + event.data["text/plain"]);
        });
    });
}

function setDefaultValue() {
    $top_level_class.val(input_default_config.top_level_class);
    $no_extend_class.val(input_default_config.no_extend_class);
    $status_class.val(input_default_config.status_class);
    $ignore_class.val(input_default_config.ignore_class);
    // jquery.tag-editor 初始化
    $(".factory-config .form-control").tagEditor("destroy");
    $(".factory-config .form-control").tagEditor({placeholder: 'Enter tags ...'});
}<commit_msg>修复Tag的一个小bug<commit_after>/*
 * file name :
 * author : linwu
 * datetime : 2015-1-22
 */
var cssFactory;
var editor_code_source;
var $btn_analysis = $("#j_btn_analysis");
var $top_level_class = $("#top_level_class");
var $no_extend_class = $("#no_extend_class");
var $status_class = $("#status_class");
var $ignore_class = $("#ignore_class");
var input_default_config = {
    "top_level_class": ["^\.m-"],
    "no_extend_class": ["ui-box", "ui-tab"],
    "status_class": ["show", "hide", "hidden", "cur", "current", "open", "close", "active"],
    "ignore_class": ["^\.f-", "^\.j-", "clearfix"]
};
var input_config = {};

$(function () {
    initTab();
    initEditer();
    initCssFactory();
    initZeroClipboard();

    if (window.localStorage) {
        if (localStorage.getItem("cssFactory_input_config")) {
            input_config = JSON.parse(localStorage.getItem("cssFactory_input_config"));
            $top_level_class.val(input_config.top_level_class);
            $no_extend_class.val(input_config.no_extend_class);
            $status_class.val(input_config.status_class);
            $ignore_class.val(input_config.ignore_class);
            // jquery.tag-editor 初始化
            $(".factory-config .form-control").tagEditor({placeholder: 'Enter tags ...'});
        } else {
            setDefaultValue();
        }
    }else{
        setDefaultValue();
    }

    $("#j_btn_reset").on("click",function () {
        setDefaultValue();
        localStorage.setItem("cssFactory_input_config", JSON.stringify(input_default_config));
    });

    $(".m-cssfactory .ui-tab-nav li").on("click", function () {
        var $this = $(this);
        if ($this.index() == 0) {
            $btn_analysis.removeClass("disabled");
        } else {
            $btn_analysis.addClass("disabled");
        }
    });

    $btn_analysis.on("click", function () {
        var $this = $(this);

        input_config = {
            top_level_class: $top_level_class.val(),
            no_extend_class: $no_extend_class.val(),
            status_class: $status_class.val(),
            ignore_class: $ignore_class.val()
        };
        localStorage.setItem("cssFactory_input_config", JSON.stringify(input_config));

        if (!$this.hasClass("disabled")) {
            $(".m-cssfactory .ui-tab-nav li:eq(1)").trigger("click");
        }
    });

});

/**
 * 初始化选项卡
 * author : lw
 */
function initTab() {
    // 约定, 选项卡导航包含在".ui-tab-nav"内, 对应的选项卡内容必须有".ui-tab-content"样式, 并按导航的顺序排放.
    var fn_tab = function (e, $tab) {
        var $this = $(this);
        var $tab = $this.closest(".ui-tab,.ui-tab-hover");
        var $tab_nav_item = $this;
        var tab_index;

        e.preventDefault();

        while (!$tab_nav_item.parent().hasClass("ui-tab-nav")) {
            $tab_nav_item = $tab_nav_item.parent();
        }
        tab_index = $tab_nav_item.index();

        $tab.find(".ui-tab-content.active,.ui-tab-nav .active").removeClass("active");
        $tab.find(".ui-tab-nav").children().eq(tab_index).add($tab.find(".ui-tab-content").eq(tab_index)).addClass("active");
        if (!!$.fn.lazyload) {
            $tab.find(".ui-tab-content").eq(tab_index).find("img").trigger("appear");
        }
    };
    if ($(".ui-tab").length > 0) {
        $(".ui-tab").on("click", ".ui-tab-nav li", function (e) {
            fn_tab.call(this, e);
        });
    }
    if ($(".ui-tab-hover").length > 0) {
        $(".ui-tab-hover").on("hover", ".ui-tab-nav li", function (e) {
            fn_tab.call(this, e);
        });
    }
}

/**
 * 初始化编辑器
 */
function initEditer() {
    editor_code_source = CodeMirror.fromTextArea(document.getElementById("j_code_source"), {
        lineNumbers: true,
        tabSize: 4,
        lineWrapping: true,
        styleActiveLine: false,
        matchBrackets: false,
        mode: "xml",
        theme: "pastel-on-dark"
    });
    CodeMirror.on(editor_code_source, "change", function (e) {
        $("#j_code_source").val(e.getValue());
    });
}

/**
 * 初始化代码工厂
 */
function initCssFactory() {
    if (window.CssFactory) {
        cssFactory = new CssFactory({
            beforeAnalysis: function () {
                var $top_level_class = $("#top_level_class");
                var $no_extend_class = $("#no_extend_class");
                var $status_class = $("#status_class");
                var $ignore_class = $("#ignore_class");

                cssFactory.config({
                    arr_top_level_class: $top_level_class.val().replace("/\\/g", "\\\\").split(/\s*,\s*/),
                    arr_no_extend_class: $no_extend_class.val().split(/\s*,\s*/),
                    arr_status_class: $status_class.val().split(/\s*,\s*/),
                    arr_ignore_class: $ignore_class.val().split(/\s*,\s*/)
                }, true);
            }
        });
    }
}

function initZeroClipboard() {
    var copy_btn = new ZeroClipboard(document.getElementById("j_btn_copy"));

    copy_btn.on("ready", function (readyEvent) {
        copy_btn.on("aftercopy", function (event) {
            // `this` === `client`
            // `event.target` === the element that was clicked
            $(event.target).text("已复制!").addClass("active");
            setTimeout(function () {
                $(event.target).text("复制代码").removeClass("active");
            }, 3000);
            //console.log("Copied text to clipboard: " + event.data["text/plain"]);
        });
    });
}

function setDefaultValue() {
    $top_level_class.val(input_default_config.top_level_class);
    $no_extend_class.val(input_default_config.no_extend_class);
    $status_class.val(input_default_config.status_class);
    $ignore_class.val(input_default_config.ignore_class);
    // jquery.tag-editor 初始化
    try{
        $(".factory-config .form-control").tagEditor("destroy");
    }catch(e){

    }
    $(".factory-config .form-control").tagEditor({placeholder: 'Enter tags ...'});
}
<|endoftext|>


<commit_before>module.exports = {};

const moment = require('moment');
require('moment-countdown');
require('waypoints/lib/jquery.waypoints.js');
require('waypoints/lib/shortcuts/sticky.js');

// Define the Start & End Date
const eventStartDate = '2016-07-27 18:00:00 GMT-0400';
const eventEndDate = '2016-07-27 20:00:00 GMT-0400';

// Set Clock Value
const setClockNumbers = (date) => {
    if (moment(date).isValid()) {
        const countdown = moment().countdown(date);
        $('.countdown-text.days').text(`${countdown.days} Days`);
        $('.countdown-text.hours').text(`${countdown.hours} Hours`);
        $('.countdown-text.minutes').text(`${countdown.minutes} Minutes`);
        $('.countdown-text.seconds').text(`${countdown.seconds} Seconds`);
    } else {
        $('.countdown-text').each(function() {
            $(this).text(date);
        });
    }
};

const setClockHands = (date) => {
    const r = (el, deg) => {
        el.setAttribute('transform', `rotate(${deg} 50 50)`);
    };
    if (moment(date).isValid()) {
        const countdown = moment().countdown(date);
        r(document.querySelectorAll('#day')[0], 12 * countdown.days);
        r(document.querySelectorAll('#hour')[0], 15 * (countdown.hours) + countdown.minutes / 2);
        r(document.querySelectorAll('#min')[0], 6 * countdown.minutes);
        r(document.querySelectorAll('#sec')[0], 6 * countdown.seconds);
    } else {

        // spin the hands
    }
};

const setCopy = (text) => {
    $('.event-copy').html(text);
};

const setClocks = () => {
    // Define when to switch content
    const beforeEvent = moment().isBefore(eventStartDate);
    const duringEvent = moment().isAfter(eventStartDate) && moment().isBefore(eventEndDate);

    let clockVal;
    let eventCopy;

    if(beforeEvent) {
        clockVal = eventStartDate;
        eventCopy = `The next #CrappyHourNYC is happening on July 27th, 6-8pm at` +
        ` One Mile House (10 Delancey St, NY, NY 10002).`;
    } else if(duringEvent) {
        clockVal = eventEndDate;
        eventCopy = `#CrappyHourNYC is happening right now until 8pm, at ` +
        `One Mile House (10 Delancey St, NY, NY 10002)! If you hurry, you might be ` +
        `able to make it.<br>Sign up below for a reminder about next month's event.`;
    } else {
        clockVal = '88:88';
        eventCopy = `The next #CrappyHourNYC <a href="https://s3.amazonaws.com/f.cl.ly/items/23011K0q47083R2B0F2q/giphy.gif?v=c461a1fd">can't come soon enough</a>.`;
    }

    setClockNumbers(clockVal);
    setClockHands(clockVal);
    setCopy(eventCopy);
};

setInterval(setClocks, 1000);

const updateContentHeight = () => {
    const contentHeight = $('.container-stick').outerHeight();
    const logoHeight = $('.header-above').outerHeight();
    $('.content').css({
        height: (contentHeight + logoHeight)
    });
};

const initializeWaypoint = () => {
    // Create media query
    const desktopQuery = window.matchMedia('(min-width: 760px)');

    const header = $('.header-above').html();
    const wrap = $('.content');

    const waypoint = new Waypoint({
        element: wrap,
        handler: (direction) => {
            if(direction === 'down') {
                $('.header-below').toggleClass('is-scrollable')
                    .toggleClass('is-collapsed').html(header);
                $('.header-above').toggleClass('is-scrollable').html('');
                wrap.addClass('is-scrollable');
                updateContentHeight();
            } else {
                $('.header-below').toggleClass('is-scrollable')
                    .toggleClass('is-collapsed').html('');
                $('.header-above').toggleClass('is-scrollable').html(header);
                wrap.removeClass('is-scrollable');
                updateContentHeight();
            }
        },
        enabled: false
    });

    if (desktopQuery.matches)
        waypoint.enable();

    desktopQuery.addListener(function() {
        if (desktopQuery.matches)
            waypoint.enable();
        else
            waypoint.disable();
    });
};

$(document).ready(function() {
    setClocks();
    updateContentHeight();
    initializeWaypoint();
});
<commit_msg>updated copy (#41)<commit_after>module.exports = {};

const moment = require('moment');
require('moment-countdown');
require('waypoints/lib/jquery.waypoints.js');
require('waypoints/lib/shortcuts/sticky.js');

// Define the Start & End Date
const eventStartDate = '2016-07-27 18:00:00 GMT-0400';
const eventEndDate = '2016-07-27 20:00:00 GMT-0400';

// Set Clock Value
const setClockNumbers = (date) => {
    if (moment(date).isValid()) {
        const countdown = moment().countdown(date);
        $('.countdown-text.days').text(`${countdown.days} Days`);
        $('.countdown-text.hours').text(`${countdown.hours} Hours`);
        $('.countdown-text.minutes').text(`${countdown.minutes} Minutes`);
        $('.countdown-text.seconds').text(`${countdown.seconds} Seconds`);
    } else {
        $('.countdown-text').each(function() {
            $(this).text(date);
        });
    }
};

const setClockHands = (date) => {
    const r = (el, deg) => {
        el.setAttribute('transform', `rotate(${deg} 50 50)`);
    };
    if (moment(date).isValid()) {
        const countdown = moment().countdown(date);
        r(document.querySelectorAll('#day')[0], 12 * countdown.days);
        r(document.querySelectorAll('#hour')[0], 15 * (countdown.hours) + countdown.minutes / 2);
        r(document.querySelectorAll('#min')[0], 6 * countdown.minutes);
        r(document.querySelectorAll('#sec')[0], 6 * countdown.seconds);
    } else {

        // spin the hands
    }
};

const setCopy = (text) => {
    $('.event-copy').html(text);
};

const setClocks = () => {
    // Define when to switch content
    const beforeEvent = moment().isBefore(eventStartDate);
    const duringEvent = moment().isAfter(eventStartDate) && moment().isBefore(eventEndDate);

    let clockVal;
    let eventCopy;

    if(beforeEvent) {
        clockVal = eventStartDate;
        eventCopy = `The next #CrappyHourNYC is happening on July 27th, 6-8pm at` +
        ` One Mile House (10 Delancey St, NY, NY 10002).`;
    } else if(duringEvent) {
        clockVal = eventEndDate;
        eventCopy = `#CrappyHourNYC is happening right now until 8pm, at ` +
        `One Mile House (10 Delancey St, NY, NY 10002)! If you hurry, you might be ` +
        `able to make it.<br>Sign up below for a reminder about next month's event.`;
    } else {
        clockVal = '88:88';
        eventCopy = `#CrappyHourNYC can't come soon enough. Stay tuned.`;
    }

    setClockNumbers(clockVal);
    setClockHands(clockVal);
    setCopy(eventCopy);
};

setInterval(setClocks, 1000);

const updateContentHeight = () => {
    const contentHeight = $('.container-stick').outerHeight();
    const logoHeight = $('.header-above').outerHeight();
    $('.content').css({
        height: (contentHeight + logoHeight)
    });
};

const initializeWaypoint = () => {
    // Create media query
    const desktopQuery = window.matchMedia('(min-width: 760px)');

    const header = $('.header-above').html();
    const wrap = $('.content');

    const waypoint = new Waypoint({
        element: wrap,
        handler: (direction) => {
            if(direction === 'down') {
                $('.header-below').toggleClass('is-scrollable')
                    .toggleClass('is-collapsed').html(header);
                $('.header-above').toggleClass('is-scrollable').html('');
                wrap.addClass('is-scrollable');
                updateContentHeight();
            } else {
                $('.header-below').toggleClass('is-scrollable')
                    .toggleClass('is-collapsed').html('');
                $('.header-above').toggleClass('is-scrollable').html(header);
                wrap.removeClass('is-scrollable');
                updateContentHeight();
            }
        },
        enabled: false
    });

    if (desktopQuery.matches)
        waypoint.enable();

    desktopQuery.addListener(function() {
        if (desktopQuery.matches)
            waypoint.enable();
        else
            waypoint.disable();
    });
};

$(document).ready(function() {
    setClocks();
    updateContentHeight();
    initializeWaypoint();
});
<|endoftext|>


<commit_before>import ip from 'ip';
import {IPreset, SOCKET_CONNECT_TO_DST, PROXY_HANDSHAKE_DONE} from './defs';
import {Proxifier, ATYP_DOMAIN} from '../proxies';

/**
 * @description
 *   proxy traffic
 *
 * @params
 *   host(optional): destination host
 *   port(optional): destination port
 *
 * @examples
 *   proxy mode, using proxifier
 *   {"name": "proxy"}
 *
 *   tunnel mode, relay directly
 *   {
 *     "name": "proxy",
 *     "params": {
 *       "host": "localhost",
 *       "port": 1082
 *     }
 *   }
 */
export default class ProxyPreset extends IPreset {

  _proxy = null;

  handleProxy({buffer, next, broadcast}) {
    if (this._proxy === null) {
      this._proxy = new Proxifier({
        onHandshakeDone: (addr, callback) => {
          const [type, host, port] = [
            addr.type,
            (addr.type === ATYP_DOMAIN) ? addr.host.toString() : ip.toString(addr.host),
            addr.port.readUInt16BE(0)
          ];
          broadcast({
            type: __IS_CLIENT__ ? PROXY_HANDSHAKE_DONE : SOCKET_CONNECT_TO_DST,
            payload: {
              targetAddress: {type, host, port},
              onConnected: () => callback(next)
            }
          });
        }
      });
    }
    if (!this._proxy.isDone()) {
      const feedback = (buf) => next(buf, true);
      this._proxy.makeHandshake(feedback, buffer);
    } else {
      return buffer;
    }
  }

  clientOut(props) {
    return this.handleProxy(props);
  }

  serverIn(props) {
    return this.handleProxy(props);
  }

}
<commit_msg>presets: [unfinished] proxy.js<commit_after>import ip from 'ip';
import {IPreset, SOCKET_CONNECT_TO_DST, PROXY_HANDSHAKE_DONE} from './defs';
import {Proxifier, ATYP_DOMAIN} from '../proxies';
import {isValidHostname, isValidPort} from '../utils';

const MODE_PROXY = 0;
const MODE_TUNNEL = 1;

/**
 * @description
 *   proxy traffic
 *
 * @params
 *   host(optional): destination host
 *   port(optional): destination port
 *
 * @examples
 *   - proxy mode, using proxifier
 *   {"name": "proxy"}
 *
 *   - tunnel mode, relay directly
 *   {
 *     "name": "proxy",
 *     "params": {
 *       "host": "localhost",
 *       "port": 1082
 *     }
 *   }
 */
export default class ProxyPreset extends IPreset {

  _proxy = null;

  _mode = MODE_PROXY;

  _host = null;

  _port = null;

  _isTunnelReady = false;

  constructor({host, port}) {
    super();
    if (typeof host !== 'undefined' && !isValidHostname(host)) {
      throw Error('host is invalid');
    }
    if (typeof port !== 'undefined' && !isValidPort(port)) {
      throw Error('port is invalid');
    }
    if (host && port) {
      this._mode = MODE_TUNNEL;
      this._host = host;
      this._port = port;
    }
  }

  handleProxy({buffer, next, broadcast}) {
    if (this._proxy === null) {
      this._proxy = new Proxifier({
        onHandshakeDone: (addr, callback) => {
          const [type, host, port] = [
            addr.type,
            (addr.type === ATYP_DOMAIN) ? addr.host.toString() : ip.toString(addr.host),
            addr.port.readUInt16BE(0)
          ];
          broadcast({
            type: __IS_CLIENT__ ? PROXY_HANDSHAKE_DONE : SOCKET_CONNECT_TO_DST,
            payload: {
              targetAddress: {type, host, port},
              onConnected: () => callback(next)
            }
          });
        }
      });
    }
    if (!this._proxy.isDone()) {
      const feedback = (buf) => next(buf, true);
      this._proxy.makeHandshake(feedback, buffer);
    } else {
      return buffer;
    }
  }

  clientOut(props) {
    if (this._mode === MODE_TUNNEL && !this._isTunnelReady) {
      const {buffer, broadcast, next} = props;
      broadcast({
        type: SOCKET_CONNECT_TO_DST,
        payload: {
          targetAddress: {type: null, host: this._host, port: this._port},
          onConnected: () => next(buffer)
        }
      });
    }
    return this.handleProxy(props);
  }

  serverIn(props) {
    return this.handleProxy(props);
  }

}
<|endoftext|>


<commit_before>import Component from '@ember/component';
import { observer } from '@ember/object';
import { filter, filterBy, gt, or } from '@ember/object/computed';
import { scheduleOnce } from '@ember/runloop';
import LinkComponent from '@ember/routing/link-component';
import layout from 'ember-bootstrap/templates/components/bs-nav/item';
import ComponentParent from 'ember-bootstrap/mixins/component-parent';
import overrideableCP from 'ember-bootstrap/utils/cp/overrideable';
import { inject as service } from '@ember/service';
import { assert } from '@ember/debug';

/**

 Component for each item within a [Components.Nav](Components.Nav.html) component. Have a look there for examples.

 @class NavItem
 @namespace Components
 @extends Ember.Component
 @uses Mixins.ComponentParent
 @public
 */
export default Component.extend(ComponentParent, {
  layout,
  classNameBindings: ['disabled', 'active'],
  tagName: 'li',
  router: service(),

  /**
   * If set will wrap the item's content in a link to the given route name. Same as the `route` property of `<LinkTo>`,
   * see https://api.emberjs.com/ember/3.10/classes/LinkComponent/properties/route?anchor=route
   *
   * @property route
   * @type {string}
   * @public
   */

  /**
   * The model of a dynamic route. Same as the `model` property of `<LinkTo>`,
   * see https://api.emberjs.com/ember/3.10/classes/LinkComponent/properties/route?anchor=model
   *
   * @property model
   * @type {object|string}
   * @public
   */

  /**
   * The models of a dynamic route. Same as the `models` property of `<LinkTo>`,
   * see https://api.emberjs.com/ember/3.10/classes/LinkComponent/properties/route?anchor=models
   *
   * @property models
   * @type {array}
   * @public
   */

  /**
   * The query params of a dynamic route. Same as the `query` property of `<LinkTo>`,
   * see https://api.emberjs.com/ember/3.10/classes/LinkComponent/properties/route?anchor=query
   *
   * @property query
   * @type {object}
   * @public
   */

  /**
   * Render the nav item as disabled (see [Bootstrap docs](http://getbootstrap.com/components/#nav-disabled-links)).
   * By default it will look at any nested `link-to` components and make itself disabled if there is a disabled link.
   * See the [link-to API](http://emberjs.com/api/classes/Ember.Templates.helpers.html#toc_disabling-the-code-link-to-code-component)
   *
   * @property disabled
   * @type boolean
   * @public
   */
  disabled: overrideableCP('_disabled', function() {
    return this.get('_disabled');
  }),
  _disabled: false,

  /**
   * Render the nav item as active.
   * By default it will look at any nested `link-to` components and make itself active if there is an active link
   * (i.e. the link points to the current route).
   * See the [link-to API](http://emberjs.com/api/classes/Ember.Templates.helpers.html#toc_handling-current-route)
   *
   * @property active
   * @type boolean
   * @public
   */
  active: overrideableCP('_active', 'route', 'model', 'models', 'query', 'router.currentURL', function() {
    let { route, model, models, query } = this.getProperties('route', 'model', 'models', 'query');
    let params = [];

    if (route) {
      params.push(route);
    }

    if (model) {
      params.push(model);
    }

    if (models) {
      params.push(...models);
    }

    if (query) {
      params.push({ queryParams: true, ...query});
    }

    return params.length ? this.get('router').isActive(...params) : this.get('_active');
  }),
  _active: false,

  /**
   * Collection of all `Ember.LinkComponent`s that are children
   *
   * @property childLinks
   * @private
   */
  childLinks: filter('children', function(view) {
    return view instanceof LinkComponent;
  }),

  activeChildLinks: filterBy('childLinks', 'active'),
  hasActiveChildLinks: gt('activeChildLinks.length', 0),

  disabledChildLinks: filterBy('childLinks', 'disabled'),
  hasDisabledChildLinks: gt('disabledChildLinks.length', 0),

  hasLink: or('route', 'model', 'models', 'query'),

  /**
   * Called when clicking the nav item
   *
   * @event onClick
   * @public
   */
  onClick() {},

  click() {
    this.onClick();
  },

  init() {
    this._super(...arguments);
    let { model, models } = this.getProperties('model', 'models');
    assert('You cannot pass both `@model` and `@models` to a nav item component!', !model || !models);

    this.get('activeChildLinks');
    this.get('disabledChildLinks');
  },

  _observeActive: observer('activeChildLinks.[]', function() {
    scheduleOnce('afterRender', this, this._updateActive);
  }),

  _updateActive() {
    this.set('_active', this.get('hasActiveChildLinks'));
  },

  _observeDisabled: observer('disabledChildLinks.[]', function() {
    scheduleOnce('afterRender', this, this._updateDisabled);
  }),

  _updateDisabled() {
    this.set('_disabled', this.get('hasDisabledChildLinks'));
  }
});
<commit_msg>Fix nav.item not applying `active` class correctly for Ember 3.12-beta<commit_after>import Component from '@ember/component';
import { observer } from '@ember/object';
import { filter, filterBy, gt, or } from '@ember/object/computed';
import { scheduleOnce } from '@ember/runloop';
import LinkComponent from '@ember/routing/link-component';
import layout from 'ember-bootstrap/templates/components/bs-nav/item';
import ComponentParent from 'ember-bootstrap/mixins/component-parent';
import overrideableCP from 'ember-bootstrap/utils/cp/overrideable';
import { inject as service } from '@ember/service';
import { assert } from '@ember/debug';

/**

 Component for each item within a [Components.Nav](Components.Nav.html) component. Have a look there for examples.

 @class NavItem
 @namespace Components
 @extends Ember.Component
 @uses Mixins.ComponentParent
 @public
 */
export default Component.extend(ComponentParent, {
  layout,
  classNameBindings: ['disabled', 'active'],
  tagName: 'li',
  router: service(),

  /**
   * If set will wrap the item's content in a link to the given route name. Same as the `route` property of `<LinkTo>`,
   * see https://api.emberjs.com/ember/3.10/classes/LinkComponent/properties/route?anchor=route
   *
   * @property route
   * @type {string}
   * @public
   */

  /**
   * The model of a dynamic route. Same as the `model` property of `<LinkTo>`,
   * see https://api.emberjs.com/ember/3.10/classes/LinkComponent/properties/route?anchor=model
   *
   * @property model
   * @type {object|string}
   * @public
   */

  /**
   * The models of a dynamic route. Same as the `models` property of `<LinkTo>`,
   * see https://api.emberjs.com/ember/3.10/classes/LinkComponent/properties/route?anchor=models
   *
   * @property models
   * @type {array}
   * @public
   */

  /**
   * The query params of a dynamic route. Same as the `query` property of `<LinkTo>`,
   * see https://api.emberjs.com/ember/3.10/classes/LinkComponent/properties/route?anchor=query
   *
   * @property query
   * @type {object}
   * @public
   */

  /**
   * Render the nav item as disabled (see [Bootstrap docs](http://getbootstrap.com/components/#nav-disabled-links)).
   * By default it will look at any nested `link-to` components and make itself disabled if there is a disabled link.
   * See the [link-to API](http://emberjs.com/api/classes/Ember.Templates.helpers.html#toc_disabling-the-code-link-to-code-component)
   *
   * @property disabled
   * @type boolean
   * @public
   */
  disabled: overrideableCP('_disabled', function() {
    return this.get('_disabled');
  }),
  _disabled: false,

  /**
   * Render the nav item as active.
   * By default it will look at any nested `link-to` components and make itself active if there is an active link
   * (i.e. the link points to the current route).
   * See the [link-to API](http://emberjs.com/api/classes/Ember.Templates.helpers.html#toc_handling-current-route)
   *
   * @property active
   * @type boolean
   * @public
   */
  active: overrideableCP('_active', 'route', 'model', 'models', 'query', 'router.currentURL', function() {
    let { route, model, models, query } = this.getProperties('route', 'model', 'models', 'query');
    let params = [];

    if (route) {
      params.push(route);
    }

    if (model) {
      params.push(model);
    }

    if (models) {
      params.push(...models);
    }

    if (query) {
      params.push({ queryParams: true, ...query});
    }

    // workaround for https://github.com/emberjs/ember.js/issues/18264
    // @todo remove once this is resolved
    this.get('router.currentURL');

    return params.length > 0 ? this.get('router').isActive(...params) : this.get('_active');
  }),
  _active: false,

  /**
   * Collection of all `Ember.LinkComponent`s that are children
   *
   * @property childLinks
   * @private
   */
  childLinks: filter('children', function(view) {
    return view instanceof LinkComponent;
  }),

  activeChildLinks: filterBy('childLinks', 'active'),
  hasActiveChildLinks: gt('activeChildLinks.length', 0),

  disabledChildLinks: filterBy('childLinks', 'disabled'),
  hasDisabledChildLinks: gt('disabledChildLinks.length', 0),

  hasLink: or('route', 'model', 'models', 'query'),

  /**
   * Called when clicking the nav item
   *
   * @event onClick
   * @public
   */
  onClick() {},

  click() {
    this.onClick();
  },

  init() {
    this._super(...arguments);
    let { model, models } = this.getProperties('model', 'models');
    assert('You cannot pass both `@model` and `@models` to a nav item component!', !model || !models);

    this.get('activeChildLinks');
    this.get('disabledChildLinks');
  },

  _observeActive: observer('activeChildLinks.[]', function() {
    scheduleOnce('afterRender', this, this._updateActive);
  }),

  _updateActive() {
    this.set('_active', this.get('hasActiveChildLinks'));
  },

  _observeDisabled: observer('disabledChildLinks.[]', function() {
    scheduleOnce('afterRender', this, this._updateDisabled);
  }),

  _updateDisabled() {
    this.set('_disabled', this.get('hasDisabledChildLinks'));
  }
});
<|endoftext|>


<commit_before>import Component from '@ember/component';
import { run } from "@ember/runloop";

export default Component.extend({
  classNames: ['ember-content-editable'],
  classNameBindings: ['clearPlaceholderOnFocus:clear-on-focus'],
  attributeBindings: [
    'contenteditable',
    'placeholder',
    'spellcheck',
    'tabindex',
    'disabled'
  ],

  disabled: false,
  spellcheck: null,
  allowNewlines: true,
  autofocus: false,
  clearPlaceholderOnFocus: false,

  init() {
    this._super();
    this.set('keyWhitelist', [
      8,  // backspace
      27, // escape
      37, // left arrow
      38, // up arrow
      39, // right arrow
      40  // down arrow
    ])
    this._pasteHandler = run.bind(this, this.pasteHandler);
  },

  didInsertElement() {
    this._super(...arguments)

    this._mutationObserver = new MutationObserver(run.bind(this, this.domChanged));
    this._mutationObserver.observe(this.element, {attributes: false, childList: true, characterData: true, subtree: true});
    this.updateDom();

    if (this.get('autofocus')) {
      this.element.focus();
    }

    this.element.addEventListener('paste', this._pasteHandler);
  },

  willDestroyElement() {
    this.element.removeEventListener('paste', this._pasteHandler);
    this._mutationObserver.disconnect();
  },

  domChanged() {
    const text = this.element.innerText;
    this.setProperties({
      value: text,
      _internalValue: text
    });
  },

  didReceiveAttrs() {
    this._super(...arguments);
    this.set('contenteditable', !this.get('disabled'));
  },

  didUpdateAttrs() {
    this._super(...arguments);
    // if update has been initiated by a change of the dom (user entered something) we don't do anything because
    // - value has already been updated by domChanged
    // - the rendered text already shows the current value
    if (this.get('value') != this.get('_internalValue')) {
      this.updateDom();
    }
  },

  updateDom() {
    const value = this.get('value');
    if (value === undefined || value === null) {
      this.element.innerText = '';
    } else {
      this.element.innerText = value;
    }
  },

  keyUp(event) {
    this.get('key-up')(event);
  },

  keyPress(event) {
    // Firefox seems to call this method on backspace and cursor keyboard events, whereas chrome does not.
    // Therefore we keep a whitelist of keyCodes that we allow in case it is necessary.
    const newLength = this.element.innerText.length - this.getSelectionLength();
    if (this.get('maxlength') && newLength >= this.get('maxlength') && !this.get('keyWhitelist').includes(event.keyCode)) {
      event.preventDefault();
      this.get('length-exceeded')(this.element.innerText.length + 1);
      return false;
    }
    this.get('key-press')(event);
  },

  keyDown(event) {
    if (event.keyCode === 27) {
      this.get('escape-press')(event);
    } else if (event.keyCode === 13) {
      this.get('enter')(event);
      if (this.get('allowNewlines')) {
          this.get('insert-newline')(event);
      } else {
        event.preventDefault();
        return false;
      }
    }
    this.get('key-down')(event);
  },

  getSelectionLength() {
    const selection = window.getSelection();
    if (selection && selection.rangeCount > 0) {
      const range = selection.getRangeAt(0);
      return range.endOffset - range.startOffset;
    }
    return 0;
  },

  pasteHandler(event) {
    event.preventDefault();
    // replace any html formatted text with its plain text equivalent
    const text = event.clipboardData.getData("text/plain");
    // check max length
    if (this.get('maxlength')) {
      // a selection will be replaced. substract the length of the selection from the total length
      const selectionLength = this.getSelectionLength();
      const afterPasteLength = text.length + this.element.innerText.length - selectionLength;
      if (afterPasteLength > this.get('maxlength')) {
        this.get('length-exceeded')(afterPasteLength);
        return false;
      }
    }
    document.execCommand("insertHTML", false, text);
    this.get('paste')(text);
  },

  enter() { },

  'escape-press'() { },

  'key-up'() { },

  'key-press'() { },

  'key-down'() { },

  'length-exceeded'() { },

  'insert-newline'() { },

  paste() { }

}).reopenClass({
  positionalParams: ['value']
});
<commit_msg>add ie11 paste support<commit_after>import Component from '@ember/component';
import { run } from "@ember/runloop";

export default Component.extend({
  classNames: ['ember-content-editable'],
  classNameBindings: ['clearPlaceholderOnFocus:clear-on-focus'],
  attributeBindings: [
    'contenteditable',
    'placeholder',
    'spellcheck',
    'tabindex',
    'disabled'
  ],

  disabled: false,
  spellcheck: null,
  allowNewlines: true,
  autofocus: false,
  clearPlaceholderOnFocus: false,

  init() {
    this._super();
    this.set('keyWhitelist', [
      8,  // backspace
      27, // escape
      37, // left arrow
      38, // up arrow
      39, // right arrow
      40  // down arrow
    ])
    this._pasteHandler = run.bind(this, this.pasteHandler);
  },

  didInsertElement() {
    this._super(...arguments)

    this._mutationObserver = new MutationObserver(run.bind(this, this.domChanged));
    this._mutationObserver.observe(this.element, {attributes: false, childList: true, characterData: true, subtree: true});
    this.updateDom();

    if (this.get('autofocus')) {
      this.element.focus();
    }

    this.element.addEventListener('paste', this._pasteHandler);
  },

  willDestroyElement() {
    this.element.removeEventListener('paste', this._pasteHandler);
    this._mutationObserver.disconnect();
  },

  domChanged() {
    const text = this.element.innerText;
    this.setProperties({
      value: text,
      _internalValue: text
    });
  },

  didReceiveAttrs() {
    this._super(...arguments);
    this.set('contenteditable', !this.get('disabled'));
  },

  didUpdateAttrs() {
    this._super(...arguments);
    // if update has been initiated by a change of the dom (user entered something) we don't do anything because
    // - value has already been updated by domChanged
    // - the rendered text already shows the current value
    if (this.get('value') != this.get('_internalValue')) {
      this.updateDom();
    }
  },

  updateDom() {
    const value = this.get('value');
    if (value === undefined || value === null) {
      this.element.innerText = '';
    } else {
      this.element.innerText = value;
    }
  },

  keyUp(event) {
    this.get('key-up')(event);
  },

  keyPress(event) {
    // Firefox seems to call this method on backspace and cursor keyboard events, whereas chrome does not.
    // Therefore we keep a whitelist of keyCodes that we allow in case it is necessary.
    const newLength = this.element.innerText.length - this.getSelectionLength();
    if (this.get('maxlength') && newLength >= this.get('maxlength') && !this.get('keyWhitelist').includes(event.keyCode)) {
      event.preventDefault();
      this.get('length-exceeded')(this.element.innerText.length + 1);
      return false;
    }
    this.get('key-press')(event);
  },

  keyDown(event) {
    if (event.keyCode === 27) {
      this.get('escape-press')(event);
    } else if (event.keyCode === 13) {
      this.get('enter')(event);
      if (this.get('allowNewlines')) {
          this.get('insert-newline')(event);
      } else {
        event.preventDefault();
        return false;
      }
    }
    this.get('key-down')(event);
  },

  getSelectionLength() {
    const selection = window.getSelection();
    if (selection && selection.rangeCount > 0) {
      const range = selection.getRangeAt(0);
      return range.endOffset - range.startOffset;
    }
    return 0;
  },

  pasteHandler(event) {
    event.preventDefault();
    // replace any html formatted text with its plain text equivalent
    let text = '';
    if (event.clipboardData) {
      text = event.clipboardData.getData('text/plain');
    } else if (window.clipboardData) {
      text = window.clipboardData.getData('Text');
    }
    // check max length
    if (this.get('maxlength')) {
      // a selection will be replaced. substract the length of the selection from the total length
      const selectionLength = this.getSelectionLength();
      const afterPasteLength = text.length + this.element.innerText.length - selectionLength;
      if (afterPasteLength > this.get('maxlength')) {
        this.get('length-exceeded')(afterPasteLength);
        return false;
      }
    }
    if (document.queryCommandSupported('insertText')) {
      document.execCommand('insertText', false, text);
    } else {
      let range = document.getSelection().getRangeAt(0);
      range.deleteContents();
      let textNode = document.createTextNode(text);
      range.insertNode(textNode);
      range.selectNodeContents(textNode);
      range.collapse(false);

      let selection = document.getSelection();
      selection.removeAllRanges();
      selection.addRange(range);
    }
    this.get('paste')(text);
  },

  enter() { },

  'escape-press'() { },

  'key-up'() { },

  'key-press'() { },

  'key-down'() { },

  'length-exceeded'() { },

  'insert-newline'() { },

  paste() { }

}).reopenClass({
  positionalParams: ['value']
});
<|endoftext|>


<commit_before>import Ember from 'ember';

export default Ember.SelectOption.extend({
  tagName: 'div',
  classNames: 'item',

  initialized: false,
  initialize: function() {
    _this = this
    Ember.run(function() {
      Ember.run.scheduleOnce('afterRender', _this, _this.set_value);
    });
  }.on('init'),

  set_value: function() {
    var valuePath = this.get('parentView.optionValuePath');

    if (!valuePath) {
      return;
    }
    if (this.$() == null) {
      return;
    }

    this.$().data('value', this.get(valuePath));
    this.set('initialized',true);
  }
});
<commit_msg>var _this<commit_after>import Ember from 'ember';

export default Ember.SelectOption.extend({
  tagName: 'div',
  classNames: 'item',

  initialized: false,
  initialize: function() {
    var _this = this;
    Ember.run(function() {
      Ember.run.scheduleOnce('afterRender', _this, _this.set_value);
    });
  }.on('init'),

  set_value: function() {
    var valuePath = this.get('parentView.optionValuePath');

    if (!valuePath) {
      return;
    }
    if (this.$() == null) {
      return;
    }

    this.$().data('value', this.get(valuePath));
    this.set('initialized',true);
  }
});
<|endoftext|>