170 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			170 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
var isCustomProperty = require('../../utils/names').isCustomProperty;
 | 
						|
var TYPE = require('../../tokenizer').TYPE;
 | 
						|
var rawMode = require('./Raw').mode;
 | 
						|
 | 
						|
var IDENT = TYPE.Ident;
 | 
						|
var HASH = TYPE.Hash;
 | 
						|
var COLON = TYPE.Colon;
 | 
						|
var SEMICOLON = TYPE.Semicolon;
 | 
						|
var DELIM = TYPE.Delim;
 | 
						|
var WHITESPACE = TYPE.WhiteSpace;
 | 
						|
var EXCLAMATIONMARK = 0x0021; // U+0021 EXCLAMATION MARK (!)
 | 
						|
var NUMBERSIGN = 0x0023;      // U+0023 NUMBER SIGN (#)
 | 
						|
var DOLLARSIGN = 0x0024;      // U+0024 DOLLAR SIGN ($)
 | 
						|
var AMPERSAND = 0x0026;       // U+0026 ANPERSAND (&)
 | 
						|
var ASTERISK = 0x002A;        // U+002A ASTERISK (*)
 | 
						|
var PLUSSIGN = 0x002B;        // U+002B PLUS SIGN (+)
 | 
						|
var SOLIDUS = 0x002F;         // U+002F SOLIDUS (/)
 | 
						|
 | 
						|
function consumeValueRaw(startToken) {
 | 
						|
    return this.Raw(startToken, rawMode.exclamationMarkOrSemicolon, true);
 | 
						|
}
 | 
						|
 | 
						|
function consumeCustomPropertyRaw(startToken) {
 | 
						|
    return this.Raw(startToken, rawMode.exclamationMarkOrSemicolon, false);
 | 
						|
}
 | 
						|
 | 
						|
function consumeValue() {
 | 
						|
    var startValueToken = this.scanner.tokenIndex;
 | 
						|
    var value = this.Value();
 | 
						|
 | 
						|
    if (value.type !== 'Raw' &&
 | 
						|
        this.scanner.eof === false &&
 | 
						|
        this.scanner.tokenType !== SEMICOLON &&
 | 
						|
        this.scanner.isDelim(EXCLAMATIONMARK) === false &&
 | 
						|
        this.scanner.isBalanceEdge(startValueToken) === false) {
 | 
						|
        this.error();
 | 
						|
    }
 | 
						|
 | 
						|
    return value;
 | 
						|
}
 | 
						|
 | 
						|
module.exports = {
 | 
						|
    name: 'Declaration',
 | 
						|
    structure: {
 | 
						|
        important: [Boolean, String],
 | 
						|
        property: String,
 | 
						|
        value: ['Value', 'Raw']
 | 
						|
    },
 | 
						|
    parse: function() {
 | 
						|
        var start = this.scanner.tokenStart;
 | 
						|
        var startToken = this.scanner.tokenIndex;
 | 
						|
        var property = readProperty.call(this);
 | 
						|
        var customProperty = isCustomProperty(property);
 | 
						|
        var parseValue = customProperty ? this.parseCustomProperty : this.parseValue;
 | 
						|
        var consumeRaw = customProperty ? consumeCustomPropertyRaw : consumeValueRaw;
 | 
						|
        var important = false;
 | 
						|
        var value;
 | 
						|
 | 
						|
        this.scanner.skipSC();
 | 
						|
        this.eat(COLON);
 | 
						|
 | 
						|
        const valueStart = this.scanner.tokenIndex;
 | 
						|
 | 
						|
        if (!customProperty) {
 | 
						|
            this.scanner.skipSC();
 | 
						|
        }
 | 
						|
 | 
						|
        if (parseValue) {
 | 
						|
            value = this.parseWithFallback(consumeValue, consumeRaw);
 | 
						|
        } else {
 | 
						|
            value = consumeRaw.call(this, this.scanner.tokenIndex);
 | 
						|
        }
 | 
						|
 | 
						|
        if (customProperty && value.type === 'Value' && value.children.isEmpty()) {
 | 
						|
            for (let offset = valueStart - this.scanner.tokenIndex; offset <= 0; offset++) {
 | 
						|
                if (this.scanner.lookupType(offset) === WHITESPACE) {
 | 
						|
                    value.children.appendData({
 | 
						|
                        type: 'WhiteSpace',
 | 
						|
                        loc: null,
 | 
						|
                        value: ' '
 | 
						|
                    });
 | 
						|
                    break;
 | 
						|
                }
 | 
						|
            }
 | 
						|
        }
 | 
						|
 | 
						|
        if (this.scanner.isDelim(EXCLAMATIONMARK)) {
 | 
						|
            important = getImportant.call(this);
 | 
						|
            this.scanner.skipSC();
 | 
						|
        }
 | 
						|
 | 
						|
        // Do not include semicolon to range per spec
 | 
						|
        // https://drafts.csswg.org/css-syntax/#declaration-diagram
 | 
						|
 | 
						|
        if (this.scanner.eof === false &&
 | 
						|
            this.scanner.tokenType !== SEMICOLON &&
 | 
						|
            this.scanner.isBalanceEdge(startToken) === false) {
 | 
						|
            this.error();
 | 
						|
        }
 | 
						|
 | 
						|
        return {
 | 
						|
            type: 'Declaration',
 | 
						|
            loc: this.getLocation(start, this.scanner.tokenStart),
 | 
						|
            important: important,
 | 
						|
            property: property,
 | 
						|
            value: value
 | 
						|
        };
 | 
						|
    },
 | 
						|
    generate: function(node) {
 | 
						|
        this.chunk(node.property);
 | 
						|
        this.chunk(':');
 | 
						|
        this.node(node.value);
 | 
						|
 | 
						|
        if (node.important) {
 | 
						|
            this.chunk(node.important === true ? '!important' : '!' + node.important);
 | 
						|
        }
 | 
						|
    },
 | 
						|
    walkContext: 'declaration'
 | 
						|
};
 | 
						|
 | 
						|
function readProperty() {
 | 
						|
    var start = this.scanner.tokenStart;
 | 
						|
    var prefix = 0;
 | 
						|
 | 
						|
    // hacks
 | 
						|
    if (this.scanner.tokenType === DELIM) {
 | 
						|
        switch (this.scanner.source.charCodeAt(this.scanner.tokenStart)) {
 | 
						|
            case ASTERISK:
 | 
						|
            case DOLLARSIGN:
 | 
						|
            case PLUSSIGN:
 | 
						|
            case NUMBERSIGN:
 | 
						|
            case AMPERSAND:
 | 
						|
                this.scanner.next();
 | 
						|
                break;
 | 
						|
 | 
						|
            // TODO: not sure we should support this hack
 | 
						|
            case SOLIDUS:
 | 
						|
                this.scanner.next();
 | 
						|
                if (this.scanner.isDelim(SOLIDUS)) {
 | 
						|
                    this.scanner.next();
 | 
						|
                }
 | 
						|
                break;
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    if (prefix) {
 | 
						|
        this.scanner.skip(prefix);
 | 
						|
    }
 | 
						|
 | 
						|
    if (this.scanner.tokenType === HASH) {
 | 
						|
        this.eat(HASH);
 | 
						|
    } else {
 | 
						|
        this.eat(IDENT);
 | 
						|
    }
 | 
						|
 | 
						|
    return this.scanner.substrToCursor(start);
 | 
						|
}
 | 
						|
 | 
						|
// ! ws* important
 | 
						|
function getImportant() {
 | 
						|
    this.eat(DELIM);
 | 
						|
    this.scanner.skipSC();
 | 
						|
 | 
						|
    var important = this.consume(IDENT);
 | 
						|
 | 
						|
    // store original value in case it differ from `important`
 | 
						|
    // for better original source restoring and hacks like `!ie` support
 | 
						|
    return important === 'important' ? true : important;
 | 
						|
}
 |