<commit_before>/**
 * xmlpure.js
 *
 * Building upon and improving the CodeMirror 2 XML parser
 * @author: Dror BG (deebug.dev@gmail.com)
 * @date: August, 2011
 */

CodeMirror.defineMode("xmlpure", function(config, parserConfig) {
    // constants
    var STYLE_ERROR = "error";
    var STYLE_INSTRUCTION = "comment";
    var STYLE_COMMENT = "comment";
    var STYLE_ELEMENT_NAME = "tag";
    var STYLE_ATTRIBUTE = "attribute";
    var STYLE_WORD = "string";
    var STYLE_TEXT = "atom";

    var TAG_INSTRUCTION = "!instruction";
    var TAG_CDATA = "!cdata";
    var TAG_COMMENT = "!comment";
    var TAG_TEXT = "!text";

    var doNotIndent = {
        "!cdata": true,
        "!comment": true,
        "!text": true,
        "!instruction": true
    };

    // options
    var indentUnit = config.indentUnit;

    ///////////////////////////////////////////////////////////////////////////
    // helper functions

    // chain a parser to another parser
    function chain(stream, state, parser) {
        state.tokenize = parser;
        return parser(stream, state);
    }

    // parse a block (comment, CDATA or text)
    function inBlock(style, terminator, nextTokenize) {
        return function(stream, state) {
            while (!stream.eol()) {
                if (stream.match(terminator)) {
                    popContext(state);
                    state.tokenize = nextTokenize;
                    break;
                }
                stream.next();
            }
            return style;
        };
    }

    // go down a level in the document
    // (hint: look at who calls this function to know what the contexts are)
    function pushContext(state, tagName) {
        var noIndent = doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.doIndent);
        var newContext = {
            tagName: tagName,
            prev: state.context,
            indent: state.context ? state.context.indent + indentUnit : 0,
            lineNumber: state.lineNumber,
            indented: state.indented,
            noIndent: noIndent
        };
        state.context = newContext;
    }

    // go up a level in the document
    function popContext(state) {
        if (state.context) {
            var oldContext = state.context;
            state.context = oldContext.prev;
            return oldContext;
        }

        // we shouldn't be here - it means we didn't have a context to pop
        return null;
    }

    // return true if the current token is seperated from the tokens before it
    // which means either this is the start of the line, or there is at least
    // one space or tab character behind the token
    // otherwise returns false
    function isTokenSeparated(stream) {
        return stream.sol() ||
            stream.string.charAt(stream.start - 1) == " " ||
            stream.string.charAt(stream.start - 1) == "\t";
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: document
    //
    // an XML document can contain:
    // - a single declaration (if defined, it must be the very first line)
    // - exactly one root element
    // @todo try to actually limit the number of root elements to 1
    // - zero or more comments
    function parseDocument(stream, state) {
        if(stream.eat("<")) {
            if(stream.eat("?")) {
                // processing instruction
                pushContext(state, TAG_INSTRUCTION);
                state.tokenize = parseProcessingInstructionStartTag;
                return STYLE_INSTRUCTION;
            } else if(stream.match("!--")) {
                // new context: comment
                pushContext(state, TAG_COMMENT);
                return chain(stream, state, inBlock(STYLE_COMMENT, "-->", parseDocument));
            } else if(stream.eatSpace() || stream.eol() ) {
                stream.skipToEnd();
                return STYLE_ERROR;
            } else {
                // element
                state.tokenize = parseElementTagName;
                return STYLE_ELEMENT_NAME;
            }
        }

        // error on line
        stream.skipToEnd();
        return STYLE_ERROR;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: XML element start-tag or end-tag
    //
    // - element start-tag can contain attributes
    // - element start-tag may self-close (or start an element block if it doesn't)
    // - element end-tag can contain only the tag name
    function parseElementTagName(stream, state) {
        // get the name of the tag
        var startPos = stream.pos;
        if(stream.match(/^[a-zA-Z_:][-a-zA-Z0-9_:.]*/)) {
            // element start-tag
            var tagName = stream.string.substring(startPos, stream.pos);
            pushContext(state, tagName);
            state.tokenize = parseElement;
            return STYLE_ELEMENT_NAME;
        } else if(stream.match(/^\/[a-zA-Z_:][-a-zA-Z0-9_:.]*( )*>/)) {
            // element end-tag
            var endTagName = stream.string.substring(startPos + 1, stream.pos - 1).trim();
            var oldContext = popContext(state);
            state.tokenize = state.context == null ? parseDocument : parseElementBlock;
            if(oldContext == null || endTagName != oldContext.tagName) {
                // the start and end tag names should match - error
                return STYLE_ERROR;
            }
            return STYLE_ELEMENT_NAME;
        } else {
            // no tag name - error
            state.tokenize = state.context == null ? parseDocument : parseElementBlock;
            stream.eatWhile(/[^>]/);
            stream.eat(">");
            return STYLE_ERROR;
        }

        stream.skipToEnd();
        return null;
    }

    function parseElement(stream, state) {
        if(stream.match(/^\/>/)) {
            // self-closing tag
            popContext(state);
            state.tokenize = state.context == null ? parseDocument : parseElementBlock;
            return STYLE_ELEMENT_NAME;
        } else if(stream.eat(/^>/)) {
            state.tokenize = parseElementBlock;
            return STYLE_ELEMENT_NAME;
        } else if(isTokenSeparated(stream) && stream.match(/^[a-zA-Z_:][-a-zA-Z0-9_:.]*( )*=/)) {
            // attribute
            state.tokenize = parseAttribute;
            return STYLE_ATTRIBUTE;
        }

        // no other options - this is an error
        state.tokenize = state.context == null ? parseDocument : parseDocument;
        stream.eatWhile(/[^>]/);
        stream.eat(">");
        return STYLE_ERROR;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: attribute
    //
    // attribute values may contain everything, except:
    // - the ending quote (with ' or ") - this marks the end of the value
    // - the character "<" - should never appear
    // - ampersand ("&") - unless it starts a reference: a string that ends with a semi-colon (";")
    // ---> note: this parser is lax in what may be put into a reference string,
    // ---> consult http://www.w3.org/TR/REC-xml/#NT-Reference if you want to make it tighter
    function parseAttribute(stream, state) {
        var quote = stream.next();
        if(quote != "\"" && quote != "'") {
            // attribute must be quoted
            stream.skipToEnd();
            state.tokenize = parseElement;
            return STYLE_ERROR;
        }

        state.tokParams.quote = quote;
        state.tokenize = parseAttributeValue;
        return STYLE_WORD;
    }

    // @todo: find out whether this attribute value spans multiple lines,
    //        and if so, push a context for it in order not to indent it
    //        (or something of the sort..)
    function parseAttributeValue(stream, state) {
        var ch = "";
        while(!stream.eol()) {
            ch = stream.next();
            if(ch == state.tokParams.quote) {
                // end quote found
                state.tokenize = parseElement;
                return STYLE_WORD;
            } else if(ch == "<") {
                // can't have less-than signs in an attribute value, ever
                stream.skipToEnd()
                state.tokenize = parseElement;
                return STYLE_ERROR;
            } else if(ch == "&") {
                // reference - look for a semi-colon, or return error if none found
                ch = stream.next();

                // make sure that semi-colon isn't right after the ampersand
                if(ch == ';') {
                    stream.skipToEnd()
                    state.tokenize = parseElement;
                    return STYLE_ERROR;
                }

                // make sure no less-than characters slipped in
                while(!stream.eol() && ch != ";") {
                    if(ch == "<") {
                        // can't have less-than signs in an attribute value, ever
                        stream.skipToEnd()
                        state.tokenize = parseElement;
                        return STYLE_ERROR;
                    }
                    ch = stream.next();
                }
                if(stream.eol() && ch != ";") {
                    // no ampersand found - error
                    stream.skipToEnd();
                    state.tokenize = parseElement;
                    return STYLE_ERROR;
                }
            }
        }

        // attribute value continues to next line
        return STYLE_WORD;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: element block
    //
    // a block can contain:
    // - elements
    // - text
    // - CDATA sections
    // - comments
    function parseElementBlock(stream, state) {
        if(stream.eat("<")) {
            if(stream.match("?")) {
                pushContext(state, TAG_INSTRUCTION);
                state.tokenize = parseProcessingInstructionStartTag;
                return STYLE_INSTRUCTION;
            } else if(stream.match("!--")) {
                // new context: comment
                pushContext(state, TAG_COMMENT);
                return chain(stream, state, inBlock(STYLE_COMMENT, "-->",
                    state.context == null ? parseDocument : parseElementBlock));
            } else if(stream.match("![CDATA[")) {
                // new context: CDATA section
                pushContext(state, TAG_CDATA);
                return chain(stream, state, inBlock(STYLE_TEXT, "]]>",
                    state.context == null ? parseDocument : parseElementBlock));
            } else if(stream.eatSpace() || stream.eol() ) {
                stream.skipToEnd();
                return STYLE_ERROR;
            } else {
                // element
                state.tokenize = parseElementTagName;
                return STYLE_ELEMENT_NAME;
            }
        } else {
            // new context: text
            pushContext(state, TAG_TEXT);
            state.tokenize = parseText;
            return null;
        }

        state.tokenize = state.context == null ? parseDocument : parseElementBlock;
        stream.skipToEnd();
        return null;
    }

    function parseText(stream, state) {
        stream.eatWhile(/[^<]/);
        if(!stream.eol()) {
            // we cannot possibly be in the document context,
            // just inside an element block
            popContext(state);
            state.tokenize = parseElementBlock;
        }
        return STYLE_TEXT;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: XML processing instructions
    //
    // XML processing instructions (PIs) allow documents to contain instructions for applications.
    // PI format: <?name data?>
    // - 'name' can be anything other than 'xml' (case-insensitive)
    // - 'data' can be anything which doesn't contain '?>'
    // XML declaration is a special PI (see XML declaration context below)
    function parseProcessingInstructionStartTag(stream, state) {
        if(stream.match("xml", true, true)) {
            // xml declaration
            if(state.lineNumber > 1 || stream.pos > 5) {
                state.tokenize = parseDocument;
                stream.skipToEnd();
                return STYLE_ERROR;
            } else {
                state.tokenize = parseDeclarationVersion;
                return STYLE_INSTRUCTION;
            }
        }

        // regular processing instruction
        if(isTokenSeparated(stream) || stream.match("?>")) {
            // we have a space after the start-tag, or nothing but the end-tag
            // either way - error!
            state.tokenize = parseDocument;
            stream.skipToEnd();
            return STYLE_ERROR;
        }

        state.tokenize = parseProcessingInstructionBody;
        return STYLE_INSTRUCTION;
    }

    function parseProcessingInstructionBody(stream, state) {
        stream.eatWhile(/[^?]/);
        if(stream.eat("?")) {
            if(stream.eat(">")) {
                popContext(state);
                state.tokenize = state.context == null ? parseDocument : parseElementBlock;
            }
        }
        return STYLE_INSTRUCTION;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: XML declaration
    //
    // XML declaration is of the following format:
    // <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
    // - must start at the first character of the first line
    // - may span multiple lines
    // - must include 'version'
    // - may include 'encoding' and 'standalone' (in that order after 'version')
    // - attribute names must be lowercase
    // - cannot contain anything else on the line
    function parseDeclarationVersion(stream, state) {
        state.tokenize = parseDeclarationEncoding;

        if(isTokenSeparated(stream) && stream.match(/^version( )*=( )*"([a-zA-Z0-9_.:]|\-)+"/)) {
            return STYLE_INSTRUCTION;
        }
        stream.skipToEnd();
        return STYLE_ERROR;
    }

    function parseDeclarationEncoding(stream, state) {
        state.tokenize = parseDeclarationStandalone;

        if(isTokenSeparated(stream) && stream.match(/^encoding( )*=( )*"[A-Za-z]([A-Za-z0-9._]|\-)*"/)) {
            return STYLE_INSTRUCTION;
        }
        return null;
    }

    function parseDeclarationStandalone(stream, state) {
        state.tokenize = parseDeclarationEndTag;

        if(isTokenSeparated(stream) && stream.match(/^standalone( )*=( )*"(yes|no)"/)) {
            return STYLE_INSTRUCTION;
        }
        return null;
    }

    function parseDeclarationEndTag(stream, state) {
        state.tokenize = parseDocument;

        if(stream.match("?>") && stream.eol()) {
            popContext(state);
            return STYLE_INSTRUCTION;
        }
        stream.skipToEnd();
        return STYLE_ERROR;
    }

    ///////////////////////////////////////////////////////////////////////////
    // returned object
    return {
        electricChars: "/[",

        startState: function() {
            return {
                tokenize: parseDocument,
                tokParams: {},
                lineNumber: 0,
                lineError: false,
                context: null,
                indented: 0
            };
        },

        token: function(stream, state) {
            if(stream.sol()) {
                // initialize a new line
                state.lineNumber++;
                state.lineError = false;
                state.indented = stream.indentation();
            }

            // eat all (the spaces) you can
            if(stream.eatSpace()) return null;

            // run the current tokenize function, according to the state
            var style = state.tokenize(stream, state);

            // is there an error somewhere in the line?
            state.lineError = (state.lineError || style == "error");

            return style;
        },

        blankLine: function(state) {
            // blank lines are lines too!
            state.lineNumber++;
            state.lineError = false;
        },

        indent: function(state, textAfter) {
            if(state.context) {
                if(state.context.noIndent == true) {
                    // do not indent - no return value at all
                    return;
                }
                if(textAfter.match(/^<\/.*/)) {
                    // end-tag - indent back to last context
                    return state.context.indent;
                }
                if(textAfter.match(/^<!\[CDATA\[/)) {
                    // a stand-alone CDATA start-tag - indent back to column 0
                    return 0;
                }
                // indent to last context + regular indent unit
                return state.context.indent + indentUnit;
            }
            return 0;
        },

        compareStates: function(a, b) {
            if (a.indented != b.indented) return false;
            for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
                if (!ca || !cb) return ca == cb;
                if (ca.tagName != cb.tagName) return false;
            }
        }
    };
});

CodeMirror.defineMIME("application/xml", "purexml");
CodeMirror.defineMIME("text/xml", "purexml");
<commit_msg>add EXML Entity style (thanks Yannick)<commit_after>/**
 * xmlpure.js
 *
 * Building upon and improving the CodeMirror 2 XML parser
 * @author: Dror BG (deebug.dev@gmail.com)
 * @date: August, 2011
 */

CodeMirror.defineMode("xmlpure", function(config, parserConfig) {
    // constants
    var STYLE_ERROR = "error";
    var STYLE_INSTRUCTION = "comment";
    var STYLE_COMMENT = "comment";
    var STYLE_ELEMENT_NAME = "tag";
    var STYLE_ATTRIBUTE = "attribute";
    var STYLE_WORD = "string";
    var STYLE_TEXT = "atom";
    var STYLE_ENTITIES = "string";

    var TAG_INSTRUCTION = "!instruction";
    var TAG_CDATA = "!cdata";
    var TAG_COMMENT = "!comment";
    var TAG_TEXT = "!text";

    var doNotIndent = {
        "!cdata": true,
        "!comment": true,
        "!text": true,
        "!instruction": true
    };

    // options
    var indentUnit = config.indentUnit;

    ///////////////////////////////////////////////////////////////////////////
    // helper functions

    // chain a parser to another parser
    function chain(stream, state, parser) {
        state.tokenize = parser;
        return parser(stream, state);
    }

    // parse a block (comment, CDATA or text)
    function inBlock(style, terminator, nextTokenize) {
        return function(stream, state) {
            while (!stream.eol()) {
                if (stream.match(terminator)) {
                    popContext(state);
                    state.tokenize = nextTokenize;
                    break;
                }
                stream.next();
            }
            return style;
        };
    }

    // go down a level in the document
    // (hint: look at who calls this function to know what the contexts are)
    function pushContext(state, tagName) {
        var noIndent = doNotIndent.hasOwnProperty(tagName) || (state.context && state.context.doIndent);
        var newContext = {
            tagName: tagName,
            prev: state.context,
            indent: state.context ? state.context.indent + indentUnit : 0,
            lineNumber: state.lineNumber,
            indented: state.indented,
            noIndent: noIndent
        };
        state.context = newContext;
    }

    // go up a level in the document
    function popContext(state) {
        if (state.context) {
            var oldContext = state.context;
            state.context = oldContext.prev;
            return oldContext;
        }

        // we shouldn't be here - it means we didn't have a context to pop
        return null;
    }

    // return true if the current token is seperated from the tokens before it
    // which means either this is the start of the line, or there is at least
    // one space or tab character behind the token
    // otherwise returns false
    function isTokenSeparated(stream) {
        return stream.sol() ||
            stream.string.charAt(stream.start - 1) == " " ||
            stream.string.charAt(stream.start - 1) == "\t";
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: document
    //
    // an XML document can contain:
    // - a single declaration (if defined, it must be the very first line)
    // - exactly one root element
    // @todo try to actually limit the number of root elements to 1
    // - zero or more comments
    function parseDocument(stream, state) {
        if(stream.eat("<")) {
            if(stream.eat("?")) {
                // processing instruction
                pushContext(state, TAG_INSTRUCTION);
                state.tokenize = parseProcessingInstructionStartTag;
                return STYLE_INSTRUCTION;
            } else if(stream.match("!--")) {
                // new context: comment
                pushContext(state, TAG_COMMENT);
                return chain(stream, state, inBlock(STYLE_COMMENT, "-->", parseDocument));
            } else if(stream.eatSpace() || stream.eol() ) {
                stream.skipToEnd();
                return STYLE_ERROR;
            } else {
                // element
                state.tokenize = parseElementTagName;
                return STYLE_ELEMENT_NAME;
            }
        }

        // error on line
        stream.skipToEnd();
        return STYLE_ERROR;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: XML element start-tag or end-tag
    //
    // - element start-tag can contain attributes
    // - element start-tag may self-close (or start an element block if it doesn't)
    // - element end-tag can contain only the tag name
    function parseElementTagName(stream, state) {
        // get the name of the tag
        var startPos = stream.pos;
        if(stream.match(/^[a-zA-Z_:][-a-zA-Z0-9_:.]*/)) {
            // element start-tag
            var tagName = stream.string.substring(startPos, stream.pos);
            pushContext(state, tagName);
            state.tokenize = parseElement;
            return STYLE_ELEMENT_NAME;
        } else if(stream.match(/^\/[a-zA-Z_:][-a-zA-Z0-9_:.]*( )*>/)) {
            // element end-tag
            var endTagName = stream.string.substring(startPos + 1, stream.pos - 1).trim();
            var oldContext = popContext(state);
            state.tokenize = state.context == null ? parseDocument : parseElementBlock;
            if(oldContext == null || endTagName != oldContext.tagName) {
                // the start and end tag names should match - error
                return STYLE_ERROR;
            }
            return STYLE_ELEMENT_NAME;
        } else {
            // no tag name - error
            state.tokenize = state.context == null ? parseDocument : parseElementBlock;
            stream.eatWhile(/[^>]/);
            stream.eat(">");
            return STYLE_ERROR;
        }

        stream.skipToEnd();
        return null;
    }

    function parseElement(stream, state) {
        if(stream.match(/^\/>/)) {
            // self-closing tag
            popContext(state);
            state.tokenize = state.context == null ? parseDocument : parseElementBlock;
            return STYLE_ELEMENT_NAME;
        } else if(stream.eat(/^>/)) {
            state.tokenize = parseElementBlock;
            return STYLE_ELEMENT_NAME;
        } else if(isTokenSeparated(stream) && stream.match(/^[a-zA-Z_:][-a-zA-Z0-9_:.]*( )*=/)) {
            // attribute
            state.tokenize = parseAttribute;
            return STYLE_ATTRIBUTE;
        }

        // no other options - this is an error
        state.tokenize = state.context == null ? parseDocument : parseDocument;
        stream.eatWhile(/[^>]/);
        stream.eat(">");
        return STYLE_ERROR;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: attribute
    //
    // attribute values may contain everything, except:
    // - the ending quote (with ' or ") - this marks the end of the value
    // - the character "<" - should never appear
    // - ampersand ("&") - unless it starts a reference: a string that ends with a semi-colon (";")
    // ---> note: this parser is lax in what may be put into a reference string,
    // ---> consult http://www.w3.org/TR/REC-xml/#NT-Reference if you want to make it tighter
    function parseAttribute(stream, state) {
        var quote = stream.next();
        if(quote != "\"" && quote != "'") {
            // attribute must be quoted
            stream.skipToEnd();
            state.tokenize = parseElement;
            return STYLE_ERROR;
        }

        state.tokParams.quote = quote;
        state.tokenize = parseAttributeValue;
        return STYLE_WORD;
    }

    // @todo: find out whether this attribute value spans multiple lines,
    //        and if so, push a context for it in order not to indent it
    //        (or something of the sort..)
    function parseAttributeValue(stream, state) {
        var ch = "";
        while(!stream.eol()) {
            ch = stream.next();
            if(ch == state.tokParams.quote) {
                // end quote found
                state.tokenize = parseElement;
                return STYLE_WORD;
            } else if(ch == "<") {
                // can't have less-than signs in an attribute value, ever
                stream.skipToEnd()
                state.tokenize = parseElement;
                return STYLE_ERROR;
            } else if(ch == "&") {
                // reference - look for a semi-colon, or return error if none found
                ch = stream.next();

                // make sure that semi-colon isn't right after the ampersand
                if(ch == ';') {
                    stream.skipToEnd()
                    state.tokenize = parseElement;
                    return STYLE_ERROR;
                }

                // make sure no less-than characters slipped in
                while(!stream.eol() && ch != ";") {
                    if(ch == "<") {
                        // can't have less-than signs in an attribute value, ever
                        stream.skipToEnd()
                        state.tokenize = parseElement;
                        return STYLE_ERROR;
                    }
                    ch = stream.next();
                }
                if(stream.eol() && ch != ";") {
                    // no ampersand found - error
                    stream.skipToEnd();
                    state.tokenize = parseElement;
                    return STYLE_ERROR;
                }
            }
        }

        // attribute value continues to next line
        return STYLE_WORD;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: element block
    //
    // a block can contain:
    // - elements
    // - text
    // - CDATA sections
    // - comments
    function parseElementBlock(stream, state) {
        if(stream.eat("<")) {
            if(stream.match("?")) {
                pushContext(state, TAG_INSTRUCTION);
                state.tokenize = parseProcessingInstructionStartTag;
                return STYLE_INSTRUCTION;
            } else if(stream.match("!--")) {
                // new context: comment
                pushContext(state, TAG_COMMENT);
                return chain(stream, state, inBlock(STYLE_COMMENT, "-->",
                    state.context == null ? parseDocument : parseElementBlock));
            } else if(stream.match("![CDATA[")) {
                // new context: CDATA section
                pushContext(state, TAG_CDATA);
                return chain(stream, state, inBlock(STYLE_TEXT, "]]>",
                    state.context == null ? parseDocument : parseElementBlock));
            } else if(stream.eatSpace() || stream.eol() ) {
                stream.skipToEnd();
                return STYLE_ERROR;
            } else {
                // element
                state.tokenize = parseElementTagName;
                return STYLE_ELEMENT_NAME;
            }
        } else if(stream.eat("&")) {
            stream.eatWhile(/[^;]/);
            stream.eat(";");
            return STYLE_ENTITIES;
        } else {
            // new context: text
            pushContext(state, TAG_TEXT);
            state.tokenize = parseText;
            return null;
        }

        state.tokenize = state.context == null ? parseDocument : parseElementBlock;
        stream.skipToEnd();
        return null;
    }

    function parseText(stream, state) {
        stream.eatWhile(/[^<]/);
        if(!stream.eol()) {
            // we cannot possibly be in the document context,
            // just inside an element block
            popContext(state);
            state.tokenize = parseElementBlock;
        }
        return STYLE_TEXT;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: XML processing instructions
    //
    // XML processing instructions (PIs) allow documents to contain instructions for applications.
    // PI format: <?name data?>
    // - 'name' can be anything other than 'xml' (case-insensitive)
    // - 'data' can be anything which doesn't contain '?>'
    // XML declaration is a special PI (see XML declaration context below)
    function parseProcessingInstructionStartTag(stream, state) {
        if(stream.match("xml", true, true)) {
            // xml declaration
            if(state.lineNumber > 1 || stream.pos > 5) {
                state.tokenize = parseDocument;
                stream.skipToEnd();
                return STYLE_ERROR;
            } else {
                state.tokenize = parseDeclarationVersion;
                return STYLE_INSTRUCTION;
            }
        }

        // regular processing instruction
        if(isTokenSeparated(stream) || stream.match("?>")) {
            // we have a space after the start-tag, or nothing but the end-tag
            // either way - error!
            state.tokenize = parseDocument;
            stream.skipToEnd();
            return STYLE_ERROR;
        }

        state.tokenize = parseProcessingInstructionBody;
        return STYLE_INSTRUCTION;
    }

    function parseProcessingInstructionBody(stream, state) {
        stream.eatWhile(/[^?]/);
        if(stream.eat("?")) {
            if(stream.eat(">")) {
                popContext(state);
                state.tokenize = state.context == null ? parseDocument : parseElementBlock;
            }
        }
        return STYLE_INSTRUCTION;
    }

    ///////////////////////////////////////////////////////////////////////////
    // context: XML declaration
    //
    // XML declaration is of the following format:
    // <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
    // - must start at the first character of the first line
    // - may span multiple lines
    // - must include 'version'
    // - may include 'encoding' and 'standalone' (in that order after 'version')
    // - attribute names must be lowercase
    // - cannot contain anything else on the line
    function parseDeclarationVersion(stream, state) {
        state.tokenize = parseDeclarationEncoding;

        if(isTokenSeparated(stream) && stream.match(/^version( )*=( )*"([a-zA-Z0-9_.:]|\-)+"/)) {
            return STYLE_INSTRUCTION;
        }
        stream.skipToEnd();
        return STYLE_ERROR;
    }

    function parseDeclarationEncoding(stream, state) {
        state.tokenize = parseDeclarationStandalone;

        if(isTokenSeparated(stream) && stream.match(/^encoding( )*=( )*"[A-Za-z]([A-Za-z0-9._]|\-)*"/)) {
            return STYLE_INSTRUCTION;
        }
        return null;
    }

    function parseDeclarationStandalone(stream, state) {
        state.tokenize = parseDeclarationEndTag;

        if(isTokenSeparated(stream) && stream.match(/^standalone( )*=( )*"(yes|no)"/)) {
            return STYLE_INSTRUCTION;
        }
        return null;
    }

    function parseDeclarationEndTag(stream, state) {
        state.tokenize = parseDocument;

        if(stream.match("?>") && stream.eol()) {
            popContext(state);
            return STYLE_INSTRUCTION;
        }
        stream.skipToEnd();
        return STYLE_ERROR;
    }

    ///////////////////////////////////////////////////////////////////////////
    // returned object
    return {
        electricChars: "/[",

        startState: function() {
            return {
                tokenize: parseDocument,
                tokParams: {},
                lineNumber: 0,
                lineError: false,
                context: null,
                indented: 0
            };
        },

        token: function(stream, state) {
            if(stream.sol()) {
                // initialize a new line
                state.lineNumber++;
                state.lineError = false;
                state.indented = stream.indentation();
            }

            // eat all (the spaces) you can
            if(stream.eatSpace()) return null;

            // run the current tokenize function, according to the state
            var style = state.tokenize(stream, state);

            // is there an error somewhere in the line?
            state.lineError = (state.lineError || style == "error");

            return style;
        },

        blankLine: function(state) {
            // blank lines are lines too!
            state.lineNumber++;
            state.lineError = false;
        },

        indent: function(state, textAfter) {
            if(state.context) {
                if(state.context.noIndent == true) {
                    // do not indent - no return value at all
                    return;
                }
                if(textAfter.match(/^<\/.*/)) {
                    // end-tag - indent back to last context
                    return state.context.indent;
                }
                if(textAfter.match(/^<!\[CDATA\[/)) {
                    // a stand-alone CDATA start-tag - indent back to column 0
                    return 0;
                }
                // indent to last context + regular indent unit
                return state.context.indent + indentUnit;
            }
            return 0;
        },

        compareStates: function(a, b) {
            if (a.indented != b.indented) return false;
            for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
                if (!ca || !cb) return ca == cb;
                if (ca.tagName != cb.tagName) return false;
            }
        }
    };
});

CodeMirror.defineMIME("application/xml", "purexml");
CodeMirror.defineMIME("text/xml", "purexml");
<|endoftext|>


<commit_before>'use strict';

const MenuItem = require('../../models/menu_item');

function getMenuData() {
  return [
    new MenuItem('About Us', null, [
      new MenuItem('Mission', '/mission'),
      new MenuItem('FAQ', '/faq'),
      new MenuItem('Contact', '/contact')
    ]),
    new MenuItem('Survey', 'http://www.transmentalhealthsurvey.org'),
    new MenuItem('Get Involved', null, [
      new MenuItem('Ways to Help', '/help'),
      new MenuItem('Volunteer', 'http://apply.translifeline.org'),
      new MenuItem('Host A Fundraiser', '/fundraiser'),
      new MenuItem('Host Resources', '/host-resources')
    ]),
    new MenuItem('Donate', '/donate'),
    new MenuItem('Store', 'http://www.redbubble.com/people/translifeline/shop'),
    new MenuItem('Blog', 'https://medium.com/@Translifeline'),
    new MenuItem('Login', 'http://hotline.translifeline.org/login')
  ];
}

module.exports = getMenuData;
<commit_msg>Remove host links from menu<commit_after>'use strict';

const MenuItem = require('../../models/menu_item');

function getMenuData() {
  return [
    new MenuItem('About Us', null, [
      new MenuItem('Mission', '/mission'),
      new MenuItem('FAQ', '/faq'),
      new MenuItem('Contact', '/contact')
    ]),
    new MenuItem('Survey', 'http://www.transmentalhealthsurvey.org'),
    new MenuItem('Get Involved', null, [
      new MenuItem('Ways to Help', '/help'),
      new MenuItem('Volunteer', 'http://apply.translifeline.org')
    ]),
    new MenuItem('Donate', '/donate'),
    new MenuItem('Store', 'http://www.redbubble.com/people/translifeline/shop'),
    new MenuItem('Blog', 'https://medium.com/@Translifeline'),
    new MenuItem('Login', 'http://hotline.translifeline.org/login')
  ];
}

module.exports = getMenuData;
<|endoftext|>


<commit_before>require('babel-core/register');
var gulp, gulpTasks;

gulp = require('gulp');
gulpTasks = require('./public/js/gulp/tasks.js');

gulp.task('js:build', gulpTasks.build.js);
gulp.task('js:watch', gulpTasks.watch.js);
gulp.task('build:js', gulpTasks.build.js);
gulp.task('watch:js', gulpTasks.watch.js);

gulp.task('sass:build', gulpTasks.build.sass);
gulp.task('sass:watch', gulpTasks.watch.sass);
gulp.task('build:sass', gulpTasks.build.sass);
gulp.task('watch:sass', gulpTasks.watch.sass);

gulp.task('assets:build', ['build:js', 'build:sass']);
gulp.task('assets:watch', ['watch:js', 'watch:sass']);
gulp.task('build:assets', ['build:js', 'build:sass']);
gulp.task('watch:assets', ['watch:js', 'watch:sass']);

gulp.task('deploy:clean', gulpTasks.deploy.clean);
gulp.task('deploy:build', gulpTasks.deploy.prep);
gulp.task('deploy:prep', ['deploy:clean'], gulpTasks.deploy.prep);
gulp.task('deploy', ['deploy:prep'], gulpTasks.deploy.prod)
<commit_msg>feat(gulpfile.js) lint gulpfile and use strict mode<commit_after>'use strict';

require('babel-core/register');
let gulp, gulpTasks;

gulp = require('gulp');
gulpTasks = require('./public/js/gulp/tasks.js');

gulp.task('js:build', gulpTasks.build.js);
gulp.task('js:watch', gulpTasks.watch.js);
gulp.task('build:js', gulpTasks.build.js);
gulp.task('watch:js', gulpTasks.watch.js);

gulp.task('sass:build', gulpTasks.build.sass);
gulp.task('sass:watch', gulpTasks.watch.sass);
gulp.task('build:sass', gulpTasks.build.sass);
gulp.task('watch:sass', gulpTasks.watch.sass);

gulp.task('assets:build', ['build:js', 'build:sass']);
gulp.task('assets:watch', ['watch:js', 'watch:sass']);
gulp.task('build:assets', ['build:js', 'build:sass']);
gulp.task('watch:assets', ['watch:js', 'watch:sass']);

gulp.task('deploy:clean', gulpTasks.deploy.clean);
gulp.task('deploy:build', gulpTasks.deploy.prep);
gulp.task('deploy:prep', ['deploy:clean'], gulpTasks.deploy.prep);
gulp.task('deploy', ['deploy:prep'], gulpTasks.deploy.prod);
<|endoftext|>


<commit_before>'use strict';

var Promise = require('bluebird');
const oadaLib = require('../../libs/oada-lib-arangodb');
const kf = require('kafka-node');
const pointer = require('json-pointer');
const hash = require('object-hash');
const debug = require('debug')('write-handler');
const error = require('debug')('write-handler:error');
const info = require('debug')('write-handler:info');

var config = require('./config');

var client = new kf.Client(config.get('kafka:broker'), 'http-handler');
var offset = Promise.promisifyAll(new kf.Offset(client));
var producer = Promise.promisifyAll(new kf.Producer(client, {
    partitionerType: 0 //kf.Producer.PARTITIONER_TYPES.keyed
}));
var consumer = new kf.ConsumerGroup({
    host: config.get('kafka:broker'),
    groupId: 'write-handlers',
    fromOffset: 'latest'
}, [config.get('kafka:topics:writeRequest')]);

producer = producer
    .onAsync('ready')
    .return(producer);

consumer.on('message', handleMsg);

function handleMsg(msg) {
    // TODO: Check scope/permission?
    var req = JSON.parse(msg.value);
    req.source = req.source || '';
    var id = req['resource_id'];

    // Get body and check permission in parallel
    var body = Promise.try(function getBody() {
        return req.body || oadaLib.putBodies.getPutBody(req.bodyid);
    });
    var permitted = Promise.try(function checkPermissions() {
        if (req.source === 'rev-graph-update') {
            // no need to check permission for rev graph updates
            return;
        }

        if (id) { // Only run checks if resource exists
            // TODO: Support sharing (i.e., not just owner has permission)
            var start = new Date().getTime();
            info(`Checking permissions of resource "${id}".`);
            return oadaLib.resources.getResource(id, '_meta/_owner')
                .then(function checkOwner(owner) {
                  var end = new Date().getTime();
                  info(`Got owner of resource "${id}" from arango. +${end-start}ms`);
                    if (owner !== req['user_id']) {
                        return Promise.reject(new Error('permission'));
                    }
                });
        }
    });

    var upsert = Promise.join(body, permitted, function doUpsert(body) {
        var path = req['path_leftover'].replace(/\/*$/, '');
        var obj = {};
        var ts = Date.now();
        // TODO: Sanitize keys?

        // Create new resource
        if (!id) {
            let parts = pointer.parse(path);

            id = 'resources/' + parts[1];
            path = pointer.compile(parts.slice(2));

            // Initialize resource stuff
            obj = {
                '_type': req['content_type'],
                '_meta': {
                    '_id': id + '/_meta',
                    '_type': req['content_type'],
                    '_owner': req['user_id'],
                    'stats': {
                        'createdBy': req['user_id'],
                        'created': ts
                    },
                }
            };
        }
        var start = new Date().getTime();
        info(`PUTing to "${path}" in resource "${id}"`);
        // Create object to recursively merge into the resource
        if (path) {
            pointer.set(obj, path, body);
        } else {
            obj = Object.assign(body, obj);
        }

        // Update meta
        var meta = {
            'modifiedBy': req['user_id'],
            'modified': ts
        };
        obj['_meta'] = Object.assign(obj['_meta'] || {}, meta);

        // Precompute new rev
        var rev = msg.offset + '-' + hash(obj, {algorithm: 'md5'});
        obj['_rev'] = rev;
        pointer.set(obj, '/_meta/_rev', rev);

        // Compute new change
        var change = {
            '_id': id + '/_meta/_changes',
            '_rev': rev,
            [rev]: {
                'merge': Object.assign({}, obj),
                'userId': req['user_id'],
                'authorizationID': req['authorizationid']
            },
        };
        obj['_meta'] = Object.assign({'_changes': change}, obj['_meta']);

        // Update rev of meta?
        obj['_meta']['_rev'] = rev;

        return oadaLib.resources.putResource(id, obj).return(rev);
    }).then(function respond(rev) {
        var end = new Date().getTime();
        info(`Finished PUTing to "${path}". + ${end-start}ms`);
        return producer.call('sendAsync', [{
            topic: config.get('kafka:topics:httpResponse'),
            partition: req['resp_partition'],
            messages: JSON.stringify({
                'msgtype': 'write-response',
                'code': 'success',
                'resource_id': id,
                '_rev': rev,
                'user_id': req['user_id'],
                'authorizationid': req['authorizationid'],
                'connection_id': req['connection_id'],
            })
        }]);
    }).catch(function respondErr(err) {
        error(err);
        return producer.call('sendAsync', [{
            topic: config.get('kafka:topics:httpResponse'),
            partition: req['resp_partition'],
            messages: JSON.stringify({
                'msgtype': 'write-response',
                'code': err.message || 'error',
                'user_id': req['user_id'],
                'authorizationid': req['authorizationid'],
                'connection_id': req['connection_id'],
            })
        }]);
    });

    var cleanup = body.then(() => oadaLib.putBodies.removePutBody(req.bodyid));
    return Promise.join(upsert, cleanup);
}
<commit_msg>revised debug statement<commit_after>'use strict';

var Promise = require('bluebird');
const oadaLib = require('../../libs/oada-lib-arangodb');
const kf = require('kafka-node');
const pointer = require('json-pointer');
const hash = require('object-hash');
const debug = require('debug')('write-handler');
const error = require('debug')('write-handler:error');
const info = require('debug')('write-handler:info');

var config = require('./config');

var client = new kf.Client(config.get('kafka:broker'), 'http-handler');
var offset = Promise.promisifyAll(new kf.Offset(client));
var producer = Promise.promisifyAll(new kf.Producer(client, {
    partitionerType: 0 //kf.Producer.PARTITIONER_TYPES.keyed
}));
var consumer = new kf.ConsumerGroup({
    host: config.get('kafka:broker'),
    groupId: 'write-handlers',
    fromOffset: 'latest'
}, [config.get('kafka:topics:writeRequest')]);

producer = producer
    .onAsync('ready')
    .return(producer);

consumer.on('message', handleMsg);

function handleMsg(msg) {
    // TODO: Check scope/permission?
    var req = JSON.parse(msg.value);
    req.source = req.source || '';
    var id = req['resource_id'];

    // Get body and check permission in parallel
    var body = Promise.try(function getBody() {
        return req.body || oadaLib.putBodies.getPutBody(req.bodyid);
    });
    var permitted = Promise.try(function checkPermissions() {
        if (req.source === 'rev-graph-update') {
            // no need to check permission for rev graph updates
            return;
        }

        if (id) { // Only run checks if resource exists
            // TODO: Support sharing (i.e., not just owner has permission)
            var start = new Date().getTime();
            info(`Checking permissions of resource "${id}".`);
            return oadaLib.resources.getResource(id, '_meta/_owner')
                .then(function checkOwner(owner) {
                  var end = new Date().getTime();
                  info(`Got owner of resource "${id}" from arango. +${end-start}ms`);
                    if (owner !== req['user_id']) {
                        return Promise.reject(new Error('permission'));
                    }
                });
        }
    });

    var start = new Date().getTime();
    info(`PUTing to "${req['path_leftover']}" in resource "${id}"`);
    var upsert = Promise.join(body, permitted, function doUpsert(body) {
        var path = req['path_leftover'].replace(/\/*$/, '');
        var obj = {};
        var ts = Date.now();
        // TODO: Sanitize keys?

        // Create new resource
        if (!id) {
            let parts = pointer.parse(path);

            id = 'resources/' + parts[1];
            path = pointer.compile(parts.slice(2));

            // Initialize resource stuff
            obj = {
                '_type': req['content_type'],
                '_meta': {
                    '_id': id + '/_meta',
                    '_type': req['content_type'],
                    '_owner': req['user_id'],
                    'stats': {
                        'createdBy': req['user_id'],
                        'created': ts
                    },
                }
            };
        }
        // Create object to recursively merge into the resource
        if (path) {
            pointer.set(obj, path, body);
        } else {
            obj = Object.assign(body, obj);
        }

        // Update meta
        var meta = {
            'modifiedBy': req['user_id'],
            'modified': ts
        };
        obj['_meta'] = Object.assign(obj['_meta'] || {}, meta);

        // Precompute new rev
        var rev = msg.offset + '-' + hash(obj, {algorithm: 'md5'});
        obj['_rev'] = rev;
        pointer.set(obj, '/_meta/_rev', rev);

        // Compute new change
        var change = {
            '_id': id + '/_meta/_changes',
            '_rev': rev,
            [rev]: {
                'merge': Object.assign({}, obj),
                'userId': req['user_id'],
                'authorizationID': req['authorizationid']
            },
        };
        obj['_meta'] = Object.assign({'_changes': change}, obj['_meta']);

        // Update rev of meta?
        obj['_meta']['_rev'] = rev;

        return oadaLib.resources.putResource(id, obj).return(rev);
    }).then(function respond(rev) {
        var end = new Date().getTime();
        info(`Finished PUTing to "${req['path_leftover']}". + ${end-start}ms`);
        return producer.call('sendAsync', [{
            topic: config.get('kafka:topics:httpResponse'),
            partition: req['resp_partition'],
            messages: JSON.stringify({
                'msgtype': 'write-response',
                'code': 'success',
                'resource_id': id,
                '_rev': rev,
                'user_id': req['user_id'],
                'authorizationid': req['authorizationid'],
                'connection_id': req['connection_id'],
            })
        }]);
    }).catch(function respondErr(err) {
        error(err);
        return producer.call('sendAsync', [{
            topic: config.get('kafka:topics:httpResponse'),
            partition: req['resp_partition'],
            messages: JSON.stringify({
                'msgtype': 'write-response',
                'code': err.message || 'error',
                'user_id': req['user_id'],
                'authorizationid': req['authorizationid'],
                'connection_id': req['connection_id'],
            })
        }]);
    });

    var cleanup = body.then(() => oadaLib.putBodies.removePutBody(req.bodyid));
    return Promise.join(upsert, cleanup);
}
<|endoftext|>


<commit_before>/*global inject:false */
'use strict';

(function () {
    /*
     Simple mock for socket.io
     see: https://github.com/btford/angular-socket-io-seed/issues/4
     thanks to https://github.com/southdesign for the idea
     */
    var sockMock = function($rootScope) {
        this.events = {};
        this.emits = {};

        // Mock init, which removed event listeners to the real code
        this.init = function () {};

        // intercept 'on' calls and capture the callbacks
        this.on = function (eventName, callback) {
            if (!this.events[eventName]) this.events[eventName] = [];
            this.events[eventName].push(callback);
        };

        // intercept 'emit' calls from the client and record them to assert against in the test
        this.emit = function (eventName) {
            var args = Array.prototype.slice.call(arguments, 1);

            if (!this.emits[eventName])
                this.emits[eventName] = [];
            this.emits[eventName].push(args);
        };

        //simulate an inbound message to the socket from the server (only called from the test)
        this.receive = function (eventName) {
            var args = Array.prototype.slice.call(arguments, 1);

            if (this.events[eventName]) {
                angular.forEach(this.events[eventName], function (callback) {
                    $rootScope.$apply(function () {
                        callback.apply(this, args);
                    });
                });
            }
        };
    };

    var mockTasklistService = function () {
        this.data = [
            {
                content: '6',
                created: '2014-11-04T08:04:26.526Z',
                title: '6'
            }
        ];
        var that = this;
        this.init = function () {
            var defer = $q.defer();

            defer.resolve(that.data);

            return defer.promise;
        };
    };

    ddescribe('Controller: Tasklist', function () {
        var scope, socketMock;

        beforeEach(function () {
            var mockTasklistService = {};
            module('mean');
            module('mean.system');
            module('mean.tasklist', function ($provide) {
                $provide.value('restService', mockTasklistService);
            });

            inject(function($q) {
                mockTasklistService.data = [
                    {
                        id: 0,
                        name: 'Angular'
                    },
                    {
                        id: 1,
                        name: 'Ember'
                    },
                    {
                        id: 2,
                        name: 'Backbone'
                    },
                    {
                        id: 3,
                        name: 'React'
                    }
                ];
                // Just overwriting the actual service calls so we can test the controller, not the service
                mockTasklistService.getAll = function() {
                    var defer = $q.defer();

                    defer.resolve(this.data);

                    return defer.promise;
                };

                mockTasklistService.create = function(name) {
                    var defer = $q.defer();

                    var id = this.data.length;

                    var item = {
                        id: id,
                        name: name
                    };

                    this.data.push(item);
                    defer.resolve(item);

                    return defer.promise;
                };
            });
        });

        //mock the controller for the same reason and include $rootScope and $controller
        beforeEach(inject(function($rootScope, $controller){
            //create an empty scope
            scope = $rootScope.$new();

            socketMock = new sockMock($rootScope);

            //declare the controller and inject our empty scope
            $controller('TasklistController', {$scope: scope, SocketService: socketMock});
        }));

        it('should accept emitted tasks and add them to tasks array', function(){
            socketMock.receive('newTask', {
                data: {
                    $$hashKey: 'object:42',
                    user: '5434f0215d1bbcf87764b996',
                    title: 'test title',
                    content: 'test content'
                }
            });
            // Make sure on emit that tasks get pushed
            expect(scope.tasks).toEqual([{
                $$hashKey: 'object:42',
                user: '5434f0215d1bbcf87764b996',
                title: 'test title',
                content: 'test content'
            }]);
        });

        it('should immediately call Tasklist.init() and add the return to $scope.tasks', function () {

        });
    });
}());
<commit_msg>TaskController testing<commit_after>/*global inject:false */
'use strict';

(function () {
    /*
     Simple mock for socket.io
     see: https://github.com/btford/angular-socket-io-seed/issues/4
     thanks to https://github.com/southdesign for the idea
     */
    var sockMock = function($rootScope) {
        this.events = {};
        this.emits = {};

        // Mock init, which removed event listeners to the real code
        this.init = function () {};

        // intercept 'on' calls and capture the callbacks
        this.on = function (eventName, callback) {
            if (!this.events[eventName]) this.events[eventName] = [];
            this.events[eventName].push(callback);
        };

        // intercept 'emit' calls from the client and record them to assert against in the test
        this.emit = function (eventName) {
            var args = Array.prototype.slice.call(arguments, 1);

            if (!this.emits[eventName])
                this.emits[eventName] = [];
            this.emits[eventName].push(args);
        };

        //simulate an inbound message to the socket from the server (only called from the test)
        this.receive = function (eventName) {
            var args = Array.prototype.slice.call(arguments, 1);

            if (this.events[eventName]) {
                angular.forEach(this.events[eventName], function (callback) {
                    $rootScope.$apply(function () {
                        callback.apply(this, args);
                    });
                });
            }
        };
    };

    /**
     * Mock the initial querying of tasks
     *
     * @param $q
     */
    var mockTasklistServiceFunc = function ($q) {
        this.data = [
            {
                __v: 0,
                _id: "5458888a70b39cf36ca711e7",
                content: "testContent",
                created: "2014-11-04T08:04:26.526Z",
                title: "testTitle",
                user: {
                    _id: "5434f0215d1bbcf87764b996",
                    name: "Logan Etherton",
                    username: "loganetherton"
                }
            },
            {
                __v: 0,
                _id: "545882cc37f38bcf69f0b82d",
                content: "testContent2",
                created: "2014-11-04T08:04:26.526Z",
                title: "testTitle2",
                user: {
                    _id: "5434f0215d1bbcf87764b996",
                    name: "Logan Etherton",
                    username: "loganetherton"
                }
            }
        ];
        var that = this;
        this.init = function () {
            var defer = $q.defer();

            defer.resolve(that);

            return defer.promise;
        };
    };

    /**
     * @todo When I include both the mock socket and mock Tasklist service, it seems to lose access to the socket. Why?
     */
    describe('Controller: Tasklist', function () {
        var scope, socketMock;

        beforeEach(function () {
            module('mean');
            module('mean.system');
            module('mean.tasklist');
        });

        //mock the controller for the same reason and include $rootScope and $controller
        beforeEach(inject(function($rootScope, $controller, $q){
            //create an empty scope
            scope = $rootScope.$new();

            socketMock = new sockMock($rootScope);

            var TasklistService = new mockTasklistServiceFunc($q);

            // Declare controller, inject mock socket and mock tasklist service
            $controller('TasklistController', {$scope: scope, TasklistService: TasklistService, SocketService: socketMock});

            scope.$digest();
        }));

        it('should get access to global tasklist strings object', function () {
            expect(typeof scope.strings).toEqual();
        });

        it('should immediately call Tasklist.init() and add the return to $scope.tasks', function () {
            expect(scope.tasks).toEqual([
                {
                    __v: 0,
                    _id: "5458888a70b39cf36ca711e7",
                    content: "testContent",
                    created: "2014-11-04T08:04:26.526Z",
                    title: "testTitle",
                    user: {
                        _id: "5434f0215d1bbcf87764b996",
                        name: "Logan Etherton",
                        username: "loganetherton"
                    }
                },
                {
                    __v: 0,
                    _id: "545882cc37f38bcf69f0b82d",
                    content: "testContent2",
                    created: "2014-11-04T08:04:26.526Z",
                    title: "testTitle2",
                    user: {
                        _id: "5434f0215d1bbcf87764b996",
                        name: "Logan Etherton",
                        username: "loganetherton"
                    }
                }
            ]);
        });

        it('should accept emitted tasks and add them to tasks array', function(){
            socketMock.receive('newTask', {
                data: {
                    $$hashKey: 'object:42',
                    user: '5434f0215d1bbcf87764b996',
                    title: 'test title',
                    content: 'test content'
                }
            });
            // Emitted tasks should get pushed onto the tasks array
            expect(scope.tasks).toEqual(
                [{
                     $$hashKey: 'object:42',
                     user: '5434f0215d1bbcf87764b996',
                     title: 'test title',
                     content: 'test content'
                 }, {
                     __v: 0,
                     _id: '5458888a70b39cf36ca711e7',
                     content: 'testContent',
                     created: '2014-11-04T08:04:26.526Z',
                     title: 'testTitle',
                     user: {
                         _id: '5434f0215d1bbcf87764b996', name: 'Logan Etherton', username: 'loganetherton'
                     }
                 }, {
                     __v: 0,
                     _id: '545882cc37f38bcf69f0b82d',
                     content: 'testContent2',
                     created: '2014-11-04T08:04:26.526Z',
                     title: 'testTitle2',
                     user: {
                         _id: '5434f0215d1bbcf87764b996', name: 'Logan Etherton', username: 'loganetherton'
                     }
                 }
                ]
            );
        });
    });
}());
<|endoftext|>


<commit_before>import { DataAnalytics } from '@entrylabs/tool';
import _find from 'lodash/find';
import DataTableSource from './source/DataTableSource';

class DataTable {
    #tables = [];
    #view;
    constructor(view) {
        this.#view = view;
        this.#generateView();
        console.log('c');
    }

    get tables() {
        return this.#tables;
    }

    getSource(id) {
        if (!id) {
            console.warn('empty argument');
            return null;
        }
        return _find(this.#tables, { id });
    }

    addSource(table) {
        let data = table || { name: '데이터 테이블' };
        data.name = Entry.getOrderedName(data.name, this.#tables, 'name');
        if (!(data instanceof DataTableSource)) {
            data = new DataTableSource(data);
        }

        this.#tables.unshift(data);
        Entry.playground.reloadPlayground();
    }

    removeSource({ id: targetId }) {
        this.#tables = this.#tables.filter(({ id }) => id === targetId);
        // Entry.playground.reloadPlayground();
    }

    #generateView() {
        this.dataAnalytics = new DataAnalytics({ container: this.#view });
    }
}

export default DataTable;
<commit_msg>default name 추가.<commit_after>import { DataAnalytics } from '@entrylabs/tool';
import _find from 'lodash/find';
import DataTableSource from './source/DataTableSource';

class DataTable {
    #tables = [];
    #view;
    constructor(view) {
        this.#view = view;
        this.#generateView();
        console.log('c');
    }

    get tables() {
        return this.#tables;
    }

    getSource(id) {
        if (!id) {
            console.warn('empty argument');
            return null;
        }
        return _find(this.#tables, { id });
    }

    addSource(table) {
        let data = table || { name: Lang.Workspace.data_table };
        data.name = Entry.getOrderedName(data.name, this.#tables, 'name');
        if (!(data instanceof DataTableSource)) {
            data = new DataTableSource(data);
        }

        this.#tables.unshift(data);
        Entry.playground.reloadPlayground();
    }

    removeSource({ id: targetId }) {
        this.#tables = this.#tables.filter(({ id }) => id === targetId);
        // Entry.playground.reloadPlayground();
    }

    #generateView() {
        this.dataAnalytics = new DataAnalytics({ container: this.#view });
    }
}

export default DataTable;
<|endoftext|>


<commit_before>/* This is the file where all / most of the Biblia league
 * code will be stored.
*/
exports.commands = {
	biblia: function(target, room, user, connection) {
		if (!this.canTalk()) return;
		if (!this.canBroadcast()) return;
		var command = toId(target);
		var reply = "";
		switch (command) {
			case 'site':
			case 'website':
				reply +=		'Our website can be found here.';
			break;
		}
		this.sendReplyBox(reply);
	}
};
<commit_msg>Update biblia-stuff.js<commit_after>/* Biblia League plugin
 * A fun script that holds all
 * of the league ranks in a JSON
 * file to be accessed at our leisure
 * By: panpawn
 */

var fs = require('fs');
var serialize = require('node-serialize');
var leagueRanks = {};
var leagueName = "Biblia";
var leagueRanksToHave = ["e4", "champ", "gl", "purgatory"];

function loadLeague() {
	try {
		leagueRanks = serialize.unserialize(fs.readFileSync('config/biblia-league-ranks.json', 'utf8'));
		Object.merge(Core.biblia, leagueRanks);
	} catch (e) {};
}
setTimeout(function(){loadLeague();},1000);

function saveLeague() {
	try {
		fs.writeFileSync('config/biblia-league-ranks.json',serialize.serialize(leagueRanks));
		Object.merge(Core.biblia, leagueRanks);
	} catch (e) {};
}

exports.commands = {
	biblia: function (target, room, user) {
		if (!target) target = 'help';
		if (room.id !== 'thebiblialeague') return false;
		var parts = target.split(',');
		for (var u in parts) parts[u] = parts[u].trim();
		try {
			switch (toId(parts[0])) {
				case 'give':
				case 'giverank':
					if (!parts[1] || !parts[2]) return this.sendReply("ERROR!  Usage: /biblia give, [user], [rank] - Gives a user a league rank.");
					if (!this.can('roommod')) return this.sendReply("Only room owners and up can give a " + leagueName + " rank!");
					var targetUser = toId(parts[1]);
					if (Core.biblia[targetUser]) return this.sendReply("ERROR! The user " + targetUser + " already has a league rank!");
					if (!toId(parts[2]).match(leagueRanksToHave)) return this.sendReplyBox("Ahhhh!  You didn't enter a valid league rank! (" + leagueRanksToHave + ")");
					leagueRanks[targetUser] = Core.biblia[targetUser] = toId(parts[2]); //shouldn't have to take the id here, this is for safety precautions
					saveLeague();
					this.sendReply(targetUser + " was given the league rank of " + parts[2]);
					this.logModCommand(targetUser + " was given the league rank of " + parts[2]);
					room.add(targetUser + " was given the league rank of " + parts[2] + " by " + user.name);
					break;
				case 'take':
				case 'takerank':
					if (!this.can('roommod')) return this.sendReply("Only room owners and up can take a " + leagueName + " rank!");
					if (!parts[1]) return this.sendReply("Usage: /biblia take, [user] - Removes a users rank.");
					var targetUser = toId(parts[1]);
					if (!Core.biblia[targetUser]) return this.sendReply("ERROR!  The user " + targetUser + " does not have an existing rank to remove!");
					delete Core.biblia[targetUser];
					delete leagueRanks[targetUser];
					saveEmotes();
					this.sendReply(targetUser + "'s league rank was removed.");
					this.logModCommand(targetUser + "'s league rank was removed by " + user.name);
					room.add(targetUser + "'s league rank was removed by " + user.name);
					break;
				case 'view':
				case 'show':
					if (!this.canBroadcast()) return;
					var rank = Core.biblia[toId(parts[1])];
					if (!Core.biblia[rank]) reutrn this.sendReply("User " + rank + " does not have a " + leagueName + " rank.");
					var img = "";
					switch (rank) {
						case 'e4':
							img = '';
							braek;
						case 'champ':
							img = '';
							break;
						case 'gl':
							img = '';
							break;
						case 'purgatory':
							img = '';
							break;
						default:
							img = '';
					}
					return this.sendReplyBox("Rank: " rank.substring(0,1).toUpperCase() + "
" + img);
					break;
				case 'help':
				default:
					if (!this.canBroadcast()) return;
					return this.sendReplyBox(
						"Biblia commands:
" +
						"/biblia give, [user], [rank] - Gives someone a league rank. Requires # and up.
" +
						"/biblia remove, [user] - Removes that user's league rank.  Requires # and up.
" +
						"/biblia show, [user] - Shows that user's league rank according to the biblia script."
					);
			}
		} catch (e) {
			console.log("AH!  THE BIBLIA SCRIPT HAS SELF DESTRUCTED!");
		}
	}
};
<|endoftext|>


<commit_before>'use strict'
var Base = require('vigour-js/lib/base')
var Observable = require('vigour-js/lib/observable')
var Element = require('../')
var Prop = require('../../property')

var isEmpty = require('vigour-js/lib/util/is/empty')

function $map (map, path, nolog) {
  // context map? top level render key
  // not enough combine it with path
  if (this.hasOwnProperty('storedmap')) {
    // console.log(path)
    return this.storedmap // not allways correct but good in 99% of the cases
  }
  // this can be optmized a lot!
  var maker
  if (!map) {
    path = []
    maker = true
    map = this.storedmap = {}
    if (this.$) {
      map.$ = this.$
      if (this.$collection) {
        map.$collection = this.ChildConstructor.prototype.$map(void 0, void 0, true)
      }
    } else {
      map.$context = true
    }
  }
  function each (prop, key) {
    var isProp = prop instanceof Prop
    if (prop.$) {
      var p = path.concat([key])
      var mymap = map
      if (!isProp && mymap.$context) {
        delete mymap.$context
      }
      for (var i = 0, len = p.length; i < len; i++) {
        if (mymap[p[i]]) {
          mymap = mymap[p[i]]
          if (!isProp && mymap.$context) {
            delete mymap.$context
          }
        } else {
          mymap = mymap[p[i]] = {}
          if (isProp) {
            mymap.$context = true
          }
        }
        if (i === p.length - 1) {
          mymap.$ = prop.$
          if (prop.$collection) {
            mymap.$collection = prop.ChildConstructor.prototype.$map(void 0, void 0, true)
          }
        }
      }
    }
    prop.$map(map, path.concat([key]))
  }
  this.each(each, (p) => p instanceof Element || p instanceof Prop)
  if (maker && !nolog) {
    // console.log(JSON.stringify(map, false, 2))
  }
  return map
}

function defBind () {
  return (this._contextLevel === 1 ? this._context : this._parent).parseValue()
}

// function walkcntx (parent§) {

// }

function getit (field) {
  // save ram by storing in a map !
  return function fieldbind () {
    var self

    var parent = this._contextLevel === 1 ? this._context : this._parent
    while (parent) {
      self = parent._input
      if (self) {
        let a = self.retrieve && self.retrieve(field)
        return a ? a.parseValue() : 'smurky:' + field
      }
      parent = parent._contextLevel === 1 ? parent._context : parent._parent // context if context else stop
    }
    return ''
  }
}

Prop.prototype.set({
  properties: {
    $storedmap: true,
    $ (val) {
      this.$ = val
      if (val === true) {
        this.set(defBind)
      } else {
        this.set(getit(val))
      }
    }
  },
  define: {
    $map: $map,
    origin: {
      get () {
        // without context polution!
        var field = this.$
        var reference = this
        if (field) {
          let self
          let parent = this._contextLevel === 1 ? (this._context || this._parent) : this._parent
          while (parent) {
            self = parent._input
            if (self) {
              let a = self.retrieve && self.retrieve(field)
              if (a) {
                return a
              }
            }
            parent = parent._contextLevel === 1 ? (parent._context || parent._parent) : parent._parent
          }
        } else {
          while (reference._input instanceof Base) {
            reference = reference._input
          }
        }
        return reference
      }
    }
  }
})

exports.define = {
  $map: $map,
  $subscribeProperty (p, target, event) {
    var element = this
    if (p.$) {
      let cntxt = element._context
      let level = element._contextLevel
      let pCntxt
      let store
      if (element._context) {
        cntxt
        store = element.storeContext()
      }
      if (p._context) {
        pCntxt = true
      }
      let subscontext = cntxt ? store[store.length - 1] : element
      target.subscribe(p.$, 'data', [ function (data, event) {
        // Element
        if (cntxt) {
          element.applyContext(store)
          p._context = cntxt
          p._contextLevel = level ? level + 1 : 1
          // element._context = cntxt
          // element._contextLevel = level
        } else if (pCntxt) {
          p._context = element
          p._contextLevel = 1
        }

        let node = element.getNode()

        if (node) {
          p.render(node, event, element)
        }
      }, subscontext ], subscontext.uid + '.' + this.uid + element._path.join('.'))
    }
  },
  renderData (target, oldVal, event, old$, map) {
    // console.log('yo renderData', target.path)
    var element = this
    if (!map) {
      map = this.$map()
    }
    // console.log('yo renderData', target.path, map)

    walk(map, element, target, void 0, event)
    function walk (obj, element, target, rerenderinherit, event) {
      if (!target) {
        // trarget is null unsubscribe --- target is different -- unsubscribe nested
        return
      }
      if (obj.$context) {
        // if (rerenderinherit) {
        // }
        for (let i in obj) {
          if (element[i] && element[i] instanceof Prop) {

            element.$subscribeProperty(element[i], target)
            if (rerenderinherit) {
              let nodex = element.getNode()
              if (nodex) {
                element[i].render(nodex, event, element)
              }
            }
          } else {
            if (element[i]) {
              walk(obj[i], element[i], target, rerenderinherit, event)
            }
          }
        }
      } else {
        if (element._context) {
          element = element.resolveContext(void 0, event, element._context) // moet nog met false
        }

        if (element.listensOnAttach && element._input !== target) {
          element.listensOnAttach.each(function (p, i) {
            // this can become 1000x faster (also dont do for nested)
            var keep
            for (var j in p.attach) {
              if (p.attach[j] && p.attach[j][1] === element) {
                // if (p.attach[j][0] === thisone) {
                //   keep = true
                // }
              }
            }
            // have to handle collection as well!
            if (!keep) {
              console.error('REMOVE AAAA', i, element.listensOnAttach[i].attach.keys())
              // element.listensOnAttach[i].remove()
            }
          })
        }

        if (obj.$) {
          if (obj.$collection) {
            target.subscribe(obj.$, 'property', [
              function (data, event) {
                // xxx
                var origin = this.origin
                if (origin._input === null) {
                  element.each(function (p) {
                    p instanceof Element && p.remove(event)
                  })
                } else {
                  if (element._input !== origin) {
                    element.each(function (p) {
                      p instanceof Element && p.remove(event)
                    })
                  } else if (data && (data.removed)) {
                    element._input = origin
                    // console.log('xxx???')
                    if (data.removed) {
                      for (let n in data.removed) {
                        if (element[data.removed[n]]) {
                          element[data.removed[n]].remove(event)
                        }
                      }
                    }
                    // if (data.added) {
                    //   for (let n in data.added) {
                    //     if (origin[data.added[n]]) {
                    //       iterator(origin[data.added[n]], data.added[n])
                    //     }
                    //   }
                    // }
                  }

                  element._input = origin
                  origin.each(iterator)

                  function iterator (property, key) {
                    if (!property) {
                      return
                    }
                    var elem = element[key]
                    var origin = property.origin
                    if (origin && ((!elem || elem._input !== origin) && origin._input !== null)) {
                      element.set({ [key]: origin }, event)
                      if (!element[key].$) {
                        element[key].renderData(origin, oldVal, event)
                      }
                    }
                  }
                }
              }, element
            ], element.uid, true, event)
          } else {
            // console.log('subscribe bitch')
            // first --- different
            let stamp = event.stamp
            target.subscribe(obj.$, 'data', [
              function thisone (data, event) {
                var origin = this.origin
                // console.error('target!', element.path)
                var rerender
                // this check is not enough!
                if (element._input && this.origin !== element._input || (stamp === event.stamp && oldVal)) {
                  if (element.listensOnAttach) {
                    element.listensOnAttach.each(function (p, i) {
                      // this can become 1000x faster (also dont do for nested)
                      var keep
                      for (var j in p.attach) {
                        if (p.attach[j] && p.attach[j][1] === element) {
                          if (p.attach[j][0] === thisone) {
                            keep = true
                          }
                        }
                      }
                      // have to handle collection as well!
                      if (!keep) {
                        console.error('REMOVE BBB')
                        element.listensOnAttach[i].remove()
                      }
                    })
                  }
                  // console.log('SWITCH')
                  if (origin && origin._input !== null) {
                    rerender = element.getNode()
                  }
                }
                element._input = origin //eslint-disable-line
                if (!rerenderinherit && element.getNode()) {
                  rerender = element.getNode()
                }
                for (let i in obj) {
                  if (element[i] && element[i] instanceof Prop) {
                    element.$subscribeProperty(element[i], origin)
                    if (rerender || rerenderinherit) {
                      element[i].render(rerender || element.getNode(), event, element)
                    }
                  } else if (i !== '$') {
                    // this is super annoying
                    walk(obj[i], element[i], origin, rerender || rerenderinherit, event)
                  }
                }
                if (rerender && !rerenderinherit) {
                  // this is only render props
                  // console.error('!!!rerender!!!', element) // only do for top!
                  // render(element, rerender.parentNode, Element, event, element.engine)
                }
              }, element
            ], element.uid, true, event)
          }
        } else {
          for (let i in obj) {
            if (i !== '$' && i !== '$collection') {
              if (element[i] && element[i] instanceof Prop) {
                element.$subscribeProperty(element[i], target)
                // element[i].
                if (rerenderinherit) {
                  element[i].render(element.getNode(), event, element)
                }
              } else {
                walk(obj[i], element[i], target, rerenderinherit, event)
              }
            }
          }
        }
      }
    }
    // return
  },
  handleReference (val, event, oldVal) {
    var valIsObservable = val instanceof Observable
    if (valIsObservable) {
      this._input = val
      if (this.$) {
        this.renderData(val, oldVal, event)
      }
    } else if (oldVal instanceof Observable) {
      if (this.$) {
        this.renderData(val, oldVal, event)
      }
    }
  }
}

exports.properties = {
  $storedmap: true,
  $collection (val, event) {
    this.$collection = val
    this.properties.$.call(this, val, event)
  },
  $ (val, event) {
    this.$ = val
  }
}
<commit_msg>extra check in collection<commit_after>'use strict'
var Base = require('vigour-js/lib/base')
var Observable = require('vigour-js/lib/observable')
var Element = require('../')
var Prop = require('../../property')

var isEmpty = require('vigour-js/lib/util/is/empty')

function $map (map, path, nolog) {
  // context map? top level render key
  // not enough combine it with path
  if (this.hasOwnProperty('storedmap')) {
    // console.log(path)
    return this.storedmap // not allways correct but good in 99% of the cases
  }
  // this can be optmized a lot!
  var maker
  if (!map) {
    path = []
    maker = true
    map = this.storedmap = {}
    if (this.$) {
      map.$ = this.$
      if (this.$collection) {
        map.$collection = this.ChildConstructor.prototype.$map(void 0, void 0, true)
      }
    } else {
      map.$context = true
    }
  }
  function each (prop, key) {
    var isProp = prop instanceof Prop
    if (prop.$) {
      var p = path.concat([key])
      var mymap = map
      if (!isProp && mymap.$context) {
        delete mymap.$context
      }
      for (var i = 0, len = p.length; i < len; i++) {
        if (mymap[p[i]]) {
          mymap = mymap[p[i]]
          if (!isProp && mymap.$context) {
            delete mymap.$context
          }
        } else {
          mymap = mymap[p[i]] = {}
          if (isProp) {
            mymap.$context = true
          }
        }
        if (i === p.length - 1) {
          mymap.$ = prop.$
          if (prop.$collection) {
            mymap.$collection = prop.ChildConstructor.prototype.$map(void 0, void 0, true)
          }
        }
      }
    }
    prop.$map(map, path.concat([key]))
  }
  this.each(each, (p) => p instanceof Element || p instanceof Prop)
  if (maker && !nolog) {
    // console.log(JSON.stringify(map, false, 2))
  }
  return map
}

function defBind () {
  return (this._contextLevel === 1 ? this._context : this._parent).parseValue()
}

// function walkcntx (parent§) {

// }

function getit (field) {
  // save ram by storing in a map !
  return function fieldbind () {
    var self

    var parent = this._contextLevel === 1 ? this._context : this._parent
    while (parent) {
      self = parent._input
      if (self) {
        let a = self.retrieve && self.retrieve(field)
        return a ? a.parseValue() : 'smurky:' + field
      }
      parent = parent._contextLevel === 1 ? parent._context : parent._parent // context if context else stop
    }
    return ''
  }
}

Prop.prototype.set({
  properties: {
    $storedmap: true,
    $ (val) {
      this.$ = val
      if (val === true) {
        this.set(defBind)
      } else {
        this.set(getit(val))
      }
    }
  },
  define: {
    $map: $map,
    origin: {
      get () {
        // without context polution!
        var field = this.$
        var reference = this
        if (field) {
          let self
          let parent = this._contextLevel === 1 ? (this._context || this._parent) : this._parent
          while (parent) {
            self = parent._input
            if (self) {
              let a = self.retrieve && self.retrieve(field)
              if (a) {
                return a
              }
            }
            parent = parent._contextLevel === 1 ? (parent._context || parent._parent) : parent._parent
          }
        } else {
          while (reference._input instanceof Base) {
            reference = reference._input
          }
        }
        return reference
      }
    }
  }
})

exports.define = {
  $map: $map,
  $subscribeProperty (p, target, event) {
    var element = this
    if (p.$) {
      let cntxt = element._context
      let level = element._contextLevel
      let pCntxt
      let store
      if (element._context) {
        cntxt
        store = element.storeContext()
      }
      if (p._context) {
        pCntxt = true
      }
      let subscontext = cntxt ? store[store.length - 1] : element
      target.subscribe(p.$, 'data', [ function (data, event) {
        // Element
        if (cntxt) {
          element.applyContext(store)
          p._context = cntxt
          p._contextLevel = level ? level + 1 : 1
          // element._context = cntxt
          // element._contextLevel = level
        } else if (pCntxt) {
          p._context = element
          p._contextLevel = 1
        }

        let node = element.getNode()

        if (node) {
          p.render(node, event, element)
        }
      }, subscontext ], subscontext.uid + '.' + this.uid + element._path.join('.'))
    }
  },
  renderData (target, oldVal, event, old$, map) {
    // console.log('yo renderData', target.path)
    var element = this
    if (!map) {
      map = this.$map()
    }
    // console.log('yo renderData', target.path, map)

    walk(map, element, target, void 0, event)
    function walk (obj, element, target, rerenderinherit, event) {
      if (!target) {
        // trarget is null unsubscribe --- target is different -- unsubscribe nested
        return
      }
      if (obj.$context) {
        // if (rerenderinherit) {
        // }
        for (let i in obj) {
          if (element[i] && element[i] instanceof Prop) {

            element.$subscribeProperty(element[i], target)
            if (rerenderinherit) {
              let nodex = element.getNode()
              if (nodex) {
                element[i].render(nodex, event, element)
              }
            }
          } else {
            if (element[i]) {
              walk(obj[i], element[i], target, rerenderinherit, event)
            }
          }
        }
      } else {
        if (element._context) {
          element = element.resolveContext(void 0, event, element._context) // moet nog met false
        }

        if (element.listensOnAttach && element._input !== target) {
          element.listensOnAttach.each(function (p, i) {
            // this can become 1000x faster (also dont do for nested)
            var keep
            for (var j in p.attach) {
              if (p.attach[j] && p.attach[j][1] === element) {
                // if (p.attach[j][0] === thisone) {
                //   keep = true
                // }
              }
            }
            // have to handle collection as well!
            if (!keep) {
              console.error('REMOVE AAAA', i, element.listensOnAttach[i].attach.keys())
              // element.listensOnAttach[i].remove()
            }
          })
        }

        if (obj.$) {
          if (obj.$collection) {
            target.subscribe(obj.$, 'property', [
              function (data, event) {
                // xxx
                var origin = this.origin
                if (origin._input === null) {
                  element.each(function (p) {
                    p instanceof Element && p.remove(event)
                  })
                } else {
                  if (element._input !== origin) {
                    element.each(function (p) {
                      p instanceof Element && p.remove(event)
                    })
                  } else if (data && (data.removed)) {
                    element._input = origin
                    // console.log('xxx???')
                    if (data.removed) {
                      for (let n in data.removed) {
                        if (element[data.removed[n]]) {
                          element[data.removed[n]].remove(event)
                        }
                      }
                    }
                    // if (data.added) {
                    //   for (let n in data.added) {
                    //     if (origin[data.added[n]]) {
                    //       iterator(origin[data.added[n]], data.added[n])
                    //     }
                    //   }
                    // }
                  }

                  element._input = origin
                  origin.each(iterator)

                  element.each((p, k) => {
                    if (p instanceof Element && !origin[k]) {
                      element[k].remove(event)
                    }
                  })

                  function iterator (property, key) {
                    if (!property) {
                      return
                    }
                    var elem = element[key]
                    var origin = property.origin
                    if (origin && ((!elem || elem._input !== origin) && origin._input !== null)) {
                      element.set({ [key]: origin }, event)
                      if (!element[key].$) {
                        element[key].renderData(origin, oldVal, event)
                      }
                    }
                  }
                }
              }, element
            ], element.uid, true, event)
          } else {
            // console.log('subscribe bitch')
            // first --- different
            let stamp = event.stamp
            target.subscribe(obj.$, 'data', [
              function thisone (data, event) {
                var origin = this.origin
                // console.error('target!', element.path)
                var rerender
                // this check is not enough!
                if (element._input && this.origin !== element._input || (stamp === event.stamp && oldVal)) {
                  if (element.listensOnAttach) {
                    element.listensOnAttach.each(function (p, i) {
                      // this can become 1000x faster (also dont do for nested)
                      var keep
                      for (var j in p.attach) {
                        if (p.attach[j] && p.attach[j][1] === element) {
                          if (p.attach[j][0] === thisone) {
                            keep = true
                          }
                        }
                      }
                      // have to handle collection as well!
                      if (!keep) {
                        console.error('REMOVE BBB')
                        element.listensOnAttach[i].remove()
                      }
                    })
                  }
                  // console.log('SWITCH')
                  if (origin && origin._input !== null) {
                    rerender = element.getNode()
                  }
                }
                element._input = origin //eslint-disable-line
                if (!rerenderinherit && element.getNode()) {
                  rerender = element.getNode()
                }
                for (let i in obj) {
                  if (element[i] && element[i] instanceof Prop) {
                    element.$subscribeProperty(element[i], origin)
                    if (rerender || rerenderinherit) {
                      element[i].render(rerender || element.getNode(), event, element)
                    }
                  } else if (i !== '$') {
                    // this is super annoying
                    walk(obj[i], element[i], origin, rerender || rerenderinherit, event)
                  }
                }
                if (rerender && !rerenderinherit) {
                  // this is only render props
                  // console.error('!!!rerender!!!', element) // only do for top!
                  // render(element, rerender.parentNode, Element, event, element.engine)
                }
              }, element
            ], element.uid, true, event)
          }
        } else {
          for (let i in obj) {
            if (i !== '$' && i !== '$collection') {
              if (element[i] && element[i] instanceof Prop) {
                element.$subscribeProperty(element[i], target)
                // element[i].
                if (rerenderinherit) {
                  element[i].render(element.getNode(), event, element)
                }
              } else {
                walk(obj[i], element[i], target, rerenderinherit, event)
              }
            }
          }
        }
      }
    }
    // return
  },
  handleReference (val, event, oldVal) {
    var valIsObservable = val instanceof Observable
    if (valIsObservable) {
      this._input = val
      if (this.$) {
        this.renderData(val, oldVal, event)
      }
    } else if (oldVal instanceof Observable) {
      if (this.$) {
        this.renderData(val, oldVal, event)
      }
    }
  }
}

exports.properties = {
  $storedmap: true,
  $collection (val, event) {
    this.$collection = val
    this.properties.$.call(this, val, event)
  },
  $ (val, event) {
    this.$ = val
  }
}
<|endoftext|>