'use strict'; const types = require('../../tokenizer/types.cjs'); const DOLLARSIGN = 0x0024; // U+0024 DOLLAR SIGN ($) const ASTERISK = 0x002A; // U+002A ASTERISK (*) const EQUALSSIGN = 0x003D; // U+003D EQUALS SIGN (=) const CIRCUMFLEXACCENT = 0x005E; // U+005E (^) const VERTICALLINE = 0x007C; // U+007C VERTICAL LINE (|) const TILDE = 0x007E; // U+007E TILDE (~) function getAttributeName() { if (this.eof) { this.error('Unexpected end of input'); } const start = this.tokenStart; let expectIdent = false; if (this.isDelim(ASTERISK)) { expectIdent = true; this.next(); } else if (!this.isDelim(VERTICALLINE)) { this.eat(types.Ident); } if (this.isDelim(VERTICALLINE)) { if (this.charCodeAt(this.tokenStart + 1) !== EQUALSSIGN) { this.next(); this.eat(types.Ident); } else if (expectIdent) { this.error('Identifier is expected', this.tokenEnd); } } else if (expectIdent) { this.error('Vertical line is expected'); } return { type: 'Identifier', loc: this.getLocation(start, this.tokenStart), name: this.substrToCursor(start) }; } function getOperator() { const start = this.tokenStart; const code = this.charCodeAt(start); if (code !== EQUALSSIGN && // = code !== TILDE && // ~= code !== CIRCUMFLEXACCENT && // ^= code !== DOLLARSIGN && // $= code !== ASTERISK && // *= code !== VERTICALLINE // |= ) { this.error('Attribute selector (=, ~=, ^=, $=, *=, |=) is expected'); } this.next(); if (code !== EQUALSSIGN) { if (!this.isDelim(EQUALSSIGN)) { this.error('Equal sign is expected'); } this.next(); } return this.substrToCursor(start); } // '[' ']' // '[' [ | ] ? ']' const name = 'AttributeSelector'; const structure = { name: 'Identifier', matcher: [String, null], value: ['String', 'Identifier', null], flags: [String, null] }; function parse() { const start = this.tokenStart; let name; let matcher = null; let value = null; let flags = null; this.eat(types.LeftSquareBracket); this.skipSC(); name = getAttributeName.call(this); this.skipSC(); if (this.tokenType !== types.RightSquareBracket) { // avoid case `[name i]` if (this.tokenType !== types.Ident) { matcher = getOperator.call(this); this.skipSC(); value = this.tokenType === types.String ? this.String() : this.Identifier(); this.skipSC(); } // attribute flags if (this.tokenType === types.Ident) { flags = this.consume(types.Ident); this.skipSC(); } } this.eat(types.RightSquareBracket); return { type: 'AttributeSelector', loc: this.getLocation(start, this.tokenStart), name, matcher, value, flags }; } function generate(node) { this.token(types.Delim, '['); this.node(node.name); if (node.matcher !== null) { this.tokenize(node.matcher); this.node(node.value); } if (node.flags !== null) { this.token(types.Ident, node.flags); } this.token(types.Delim, ']'); } exports.generate = generate; exports.name = name; exports.parse = parse; exports.structure = structure;