mirror of
https://github.com/NativeScript/NativeScript.git
synced 2025-08-15 19:26:42 +08:00

Split type.class from CssTypeSelector to CssCompositeSelector, probably support type#id.class selectors Apply review comments, refactor css-selectors internally Applied refactoring, all tests pass, button does not notify changes Add tests for the css selectors parser. Added tests for css-selectors Added basic implementation of mayMatch and changeMap for css match state Implemented TKUnit.assertDeepEqual to check key and key/values in Map and Set Watch for property and pseudoClass changes Add one child group test Add typings for animations Added mechanism to enable/disable listeners for pseudo classes Count listeners instead of checking handlers, reverse subscription and unsubscription
126 lines
4.5 KiB
TypeScript
126 lines
4.5 KiB
TypeScript
/// <reference path="./css-selector-parser.d.ts" />
|
|
export interface SimpleSelector {
|
|
pos: number;
|
|
type: "" | "*" | "#" | "." | ":" | "[]";
|
|
comb?: "+" | "~" | ">" | " ";
|
|
}
|
|
export interface SimpleIdentifierSelector extends SimpleSelector {
|
|
ident: string;
|
|
}
|
|
export interface UniversalSelector extends SimpleSelector {
|
|
type: "*";
|
|
}
|
|
export interface TypeSelector extends SimpleIdentifierSelector {
|
|
type: "";
|
|
}
|
|
export interface ClassSelector extends SimpleIdentifierSelector {
|
|
type: ".";
|
|
}
|
|
export interface IdSelector extends SimpleIdentifierSelector {
|
|
type: "#";
|
|
}
|
|
export interface PseudoClassSelector extends SimpleIdentifierSelector {
|
|
type: ":";
|
|
}
|
|
export interface AttributeSelector extends SimpleSelector {
|
|
type: "[]";
|
|
prop: string;
|
|
test?: "=" | "^=" | "$=" | "*=" | "=" | "~=" | "|=";
|
|
value?: string;
|
|
}
|
|
|
|
export function isUniversal(sel: SimpleSelector): sel is UniversalSelector {
|
|
return sel.type === "*";
|
|
}
|
|
export function isType(sel: SimpleSelector): sel is TypeSelector {
|
|
return sel.type === "";
|
|
}
|
|
export function isClass(sel: SimpleSelector): sel is ClassSelector {
|
|
return sel.type === ".";
|
|
}
|
|
export function isId(sel: SimpleSelector): sel is IdSelector {
|
|
return sel.type === "#";
|
|
}
|
|
export function isPseudo(sel: SimpleSelector): sel is PseudoClassSelector {
|
|
return sel.type === ":";
|
|
}
|
|
export function isAttribute(sel: SimpleSelector): sel is AttributeSelector {
|
|
return sel.type === "[]";
|
|
}
|
|
|
|
var regex = /(\s*)(?:(\*)|(#|\.|:|\b)([_-\w][_-\w\d]*)|\[\s*([_-\w][_-\w\d]*)\s*(?:(=|\^=|\$=|\*=|\~=|\|=)\s*(?:([_-\w][_-\w\d]*)|"((?:[^\\"]|\\(?:"|n|r|f|\\|0-9a-f))*)"|'((?:[^\\']|\\(?:'|n|r|f|\\|0-9a-f))*)')\s*)?\])(?:\s*(\+|~|>|\s))?/g;
|
|
// no lead ws univ type pref and ident [ prop = ident -or- "string escapes \" \00aaff" -or- 'string escapes \' urf-8: \00aaff' ] combinator
|
|
|
|
export function parse(selector: string): SimpleSelector[] {
|
|
let selectors: any[] = [];
|
|
|
|
var result: RegExpExecArray;
|
|
var lastIndex = regex.lastIndex = 0;
|
|
while(result = regex.exec(selector)) {
|
|
let pos = result.index;
|
|
if (lastIndex !== pos) {
|
|
throw new Error(`Unexpected characters at index, near: ${lastIndex}: ${result.input.substr(lastIndex, 32)}`);
|
|
} else if (!result[0] || result[0].length === 0) {
|
|
throw new Error(`Last selector match got zero character result at index ${lastIndex}, near: ${result.input.substr(lastIndex, 32)}`);
|
|
}
|
|
pos += getLeadingWhiteSpace(result).length;
|
|
lastIndex = regex.lastIndex;
|
|
|
|
var type = getType(result);
|
|
let selector: SimpleSelector | SimpleIdentifierSelector | AttributeSelector;
|
|
switch(type) {
|
|
case "*":
|
|
selector = { pos, type };
|
|
break;
|
|
case "#":
|
|
case ".":
|
|
case ":":
|
|
case "":
|
|
let ident = getIdentifier(result);
|
|
selector = { pos, type, ident };
|
|
break;
|
|
case "[]":
|
|
let prop = getProperty(result);
|
|
let test = getPropertyTest(result);
|
|
// TODO: Unescape escape sequences. Unescape UTF-8 characters.
|
|
let value = getPropertyValue(result);
|
|
selector = test ? { pos, type, prop, test, value } : { pos, type, prop };
|
|
break;
|
|
default:
|
|
throw new Error("Unhandled type.");
|
|
}
|
|
|
|
let comb = getCombinator(result);
|
|
if (comb) {
|
|
selector.comb = comb;
|
|
}
|
|
selectors.push(selector);
|
|
}
|
|
|
|
if (selectors.length > 0) {
|
|
delete selectors[selectors.length - 1].comb;
|
|
}
|
|
return selectors;
|
|
}
|
|
function getLeadingWhiteSpace(result: RegExpExecArray): string {
|
|
return result[1] || "";
|
|
}
|
|
function getType(result: RegExpExecArray): "" | "*" | "." | "#" | ":" | "[]" {
|
|
return <"[]">(result[5] && "[]") || <"*">result[2] || <"" | "." | "#" | ":">result[3];
|
|
}
|
|
function getIdentifier(result: RegExpExecArray): string {
|
|
return result[4];
|
|
}
|
|
function getProperty(result: RegExpExecArray): string {
|
|
return result[5];
|
|
}
|
|
function getPropertyTest(result: RegExpExecArray): string {
|
|
return result[6] || undefined;
|
|
}
|
|
function getPropertyValue(result: RegExpExecArray): string {
|
|
return result[7] || result[8] || result[9];
|
|
}
|
|
function getCombinator(result: RegExpExecArray): "+" | "~" | ">" | " " {
|
|
return <("+" | "~" | ">" | " ")>result[result.length - 1] || undefined;
|
|
}
|