Modules (188)

HTMLUtils

Description

Dependencies

Functions

Private

_extractAttrVal

context editor:{CodeMirror},pos:{ch:{string},line:{number}},token:{object}
Returns: val:{string},offset:{number}
    function _extractAttrVal(ctx) {
        var attrValue = ctx.token.string,
            startChar = attrValue.charAt(0),
            endChar = attrValue.charAt(attrValue.length - 1),
            offset = TokenUtils.offsetInToken(ctx),
            foundEqualSign = false;

        //If this is a fully quoted value, return the whole
        //thing regardless of position
        if (attrValue.length > 1 &&
                (startChar === "'" || startChar === '"') &&
                endChar === startChar) {

            // Find an equal sign before the end quote. If found,
            // then the user may be entering an attribute value right before
            // another attribute and we're getting a false balanced string.
            // An example of this case is  where the
            // cursor is right after the first double quote.
            foundEqualSign = (attrValue.match(/\=\s*['"]$/) !== null);

            if (!foundEqualSign) {
                //strip the quotes and return;
                attrValue = attrValue.substring(1, attrValue.length - 1);
                offset = offset - 1 > attrValue.length ? attrValue.length : offset - 1;
                return {val: attrValue, offset: offset, quoteChar: startChar, hasEndQuote: true};
            }
        }

        if (foundEqualSign) {
            var spaceIndex = attrValue.indexOf(" "),
                bracketIndex = attrValue.indexOf(">"),
                upToIndex = (spaceIndex !== -1 && spaceIndex < bracketIndex) ? spaceIndex : bracketIndex;
            attrValue = attrValue.substring(0, (upToIndex > offset) ? upToIndex : offset);
        } else if (offset > 0 && (startChar === "'" || startChar === '"')) {
            //The att value is getting edit in progress. There is possible extra
            //stuff in this token state since the quote isn't closed, so we assume
            //the stuff from the quote to the current pos is definitely in the attribute
            //value.
            attrValue = attrValue.substring(0, offset);
        }

        //If the attrValue start with a quote, trim that now
        startChar = attrValue.charAt(0);
        if (startChar === "'" || startChar === '"') {
            attrValue = attrValue.substring(1);
            offset--;
        } else {
            startChar = "";
            // Make attr value empty and set offset to zero if it has the ">"
            // which is the closing of the tag.
            if (endChar === ">") {
                attrValue = "";
                offset = 0;
            }
        }

        return {val: attrValue, offset: offset, quoteChar: startChar, hasEndQuote: false};
    }
Private

_extractTagName

context editor:{CodeMirror},pos:{ch:{string},line:{number}},token:{object}
Returns: string
    function _extractTagName(ctx) {
        var mode = ctx.editor.getMode(),
            innerModeData = CodeMirror.innerMode(mode, ctx.token.state);

        if (ctx.token.type === "tag bracket") {
            return innerModeData.state.tagName;
        }

        // If the ctx is inside the tag name of an end tag, innerModeData.state.tagName is
        // undefined. So return token string as the tag name.
        return innerModeData.state.tagName || ctx.token.string;
    }
Private

_getTagInfoStartingFromAttrName

context editor:{CodeMirror},pos:{ch:{string},line:{number}},token:{object}
isPriorAttr boolean
indicates whether we're getting info for a prior attribute
Returns: string
    function _getTagInfoStartingFromAttrName(ctx, isPriorAttr) {
        //Verify We're in the attribute name, move forward and try to extract the rest of
        //the info. If the user it typing the attr the rest might not be here
        if (isPriorAttr === false && ctx.token.type !== "attribute") {
            return createTagInfo();
        }

        var tagName = _extractTagName(ctx);
        var attrName = ctx.token.string;
        var offset = TokenUtils.offsetInToken(ctx);

        if (!TokenUtils.moveSkippingWhitespace(TokenUtils.moveNextToken, ctx) || ctx.token.string !== "=") {
            // If we're checking for a prior attribute and the next token we get is a tag or an html comment or
            // an undefined token class, then we've already scanned past our original cursor location.
            // So just return an empty tag info.
            if (isPriorAttr &&
                    (!ctx.token.type ||
                    (ctx.token.type && ctx.token.type !== "attribute" &&
                        ctx.token.type.indexOf("error") === -1 &&
                        ctx.token.string.indexOf("<") !== -1))) {
                return createTagInfo();
            }
            return createTagInfo(ATTR_NAME, offset, tagName, attrName);
        }

        if (!TokenUtils.moveSkippingWhitespace(TokenUtils.moveNextToken, ctx)) {
            return createTagInfo(ATTR_NAME, offset, tagName, attrName);
        }
        //this should be the attrvalue
        var attrInfo = _extractAttrVal(ctx),
            attrVal = attrInfo.val,
            quoteChar = attrInfo.quoteChar,
            hasEndQuote = attrInfo.hasEndQuote;

        return createTagInfo(ATTR_NAME, offset, tagName, attrName, attrVal, true, quoteChar, hasEndQuote);
    }
Private

_getTagInfoStartingFromAttrValue

context editor:{CodeMirror},pos:{ch:{string},line:{number}},token:{object}
Returns: string
    function _getTagInfoStartingFromAttrValue(ctx) {
        // Assume we in the attr value
        // and validate that by going backwards
        var attrInfo = _extractAttrVal(ctx),
            attrVal = attrInfo.val,
            offset = attrInfo.offset,
            quoteChar = attrInfo.quoteChar,
            hasEndQuote = attrInfo.hasEndQuote,
            strLength = ctx.token.string.length;

        if ((ctx.token.type === "string" || ctx.token.type === "error") &&
                ctx.pos.ch === ctx.token.end && strLength > 1) {
            var firstChar = ctx.token.string[0],
                lastChar = ctx.token.string[strLength - 1];

            // We get here only when the cursor is immediately on the right of the end quote
            // of an attribute value. So we want to return an empty tag info so that the caller
            // can dismiss the code hint popup if it is still open.
            if (firstChar === lastChar && (firstChar === "'" || firstChar === "\"")) {
                return createTagInfo();
            }
        }
        
        //Skip all the 'string' tokens backwards. Required to reach to the first line 
        //of multiline HTML attribute value.
        while (TokenUtils.moveSkippingWhitespace(TokenUtils.movePrevToken, ctx)) {
            if (ctx.token.type !== "string") {
                break;
            }
        }

        //As we have skipped all the string tokens, make a forward navigation to move to the
        //first 'string token so that in next backward navigation we can find '='.
        TokenUtils.moveSkippingWhitespace(TokenUtils.moveNextToken, ctx);

        //Move to the prev token, and check if it's "="
        if (!TokenUtils.moveSkippingWhitespace(TokenUtils.movePrevToken, ctx) || ctx.token.string !== "=") {
            return createTagInfo();
        }

        //Move to the prev token, and check if it's an attribute
        if (!TokenUtils.moveSkippingWhitespace(TokenUtils.movePrevToken, ctx) || ctx.token.type !== "attribute") {
            return createTagInfo();
        }

        var attrName = ctx.token.string;
        var tagName = _extractTagName(ctx);

        //We're good.
        return createTagInfo(ATTR_VALUE, offset, tagName, attrName, attrVal, true, quoteChar, hasEndQuote);
    }
Public API

createTagInfo

Creates a tagInfo object and assures all the values are entered or are empty strings

tokenType optional string
what is getting edited and should be hinted
offset optional number
where the cursor is for the part getting hinted
tagName optional string
The name of the tag
attrName optional string
The name of the attribute
attrValue optional string
The value of the attribute
Returns: {tagName:string,attr:{name:string,value:string,valueAssigned:boolean,quoteChar:string,hasEndQuote:boolean},position:{tokenType:string,offset:number
}} A tagInfo object with some context about the current tag hint.
    function createTagInfo(tokenType, offset, tagName, attrName, attrValue, valueAssigned, quoteChar, hasEndQuote) {
        return { tagName: tagName || "",
                 attr:
                    { name: attrName || "",
                      value: attrValue || "",
                      valueAssigned: valueAssigned || false,
                      quoteChar: quoteChar || "",
                      hasEndQuote: hasEndQuote || false },
                 position:
                    { tokenType: tokenType || "",
                      offset: offset || 0 } };
    }
Public API

findBlocks

Returns an Array of info about all blocks whose token mode name matches that passed in, in the given Editor's HTML document (assumes the Editor contains HTML text).

editor non-nullable Editor
the editor containing the HTML text
modeName string
the mode name of the tokens to look for
Returns: Array.<{start:{line:number,ch:number},end:{line:number,ch:number},text:string}>
    function findBlocks(editor, modeName) {
        // Start scanning from beginning of file
        var ctx = TokenUtils.getInitialContext(editor._codeMirror, {line: 0, ch: 0}),
            blocks = [],
            currentBlock = null,
            inBlock = false,
            outerMode = editor._codeMirror.getMode(),
            tokenModeName,
            previousMode;

        while (TokenUtils.moveNextToken(ctx, false)) {
            tokenModeName = CodeMirror.innerMode(outerMode, ctx.token.state).mode.name;
            if (inBlock) {
                if (!currentBlock.end) {
                    // Handle empty blocks
                    currentBlock.end = currentBlock.start;
                }
                // Check for end of this block
                if (tokenModeName === previousMode) {
                    // currentBlock.end is already set to pos of the last token by now
                    currentBlock.text = editor.document.getRange(currentBlock.start, currentBlock.end);
                    inBlock = false;
                } else {
                    currentBlock.end = { line: ctx.pos.line, ch: ctx.pos.ch };
                }
            } else {
                // Check for start of a block
                if (tokenModeName === modeName) {
                    currentBlock = {
                        start: { line: ctx.pos.line, ch: ctx.pos.ch }
                    };
                    blocks.push(currentBlock);
                    inBlock = true;
                } else {
                    previousMode = tokenModeName;
                }
                // else, random token: ignore
            }
        }

        return blocks;
    }
Public API

findStyleBlocks

Returns an Array of info about all