// 清除供应商前缀缓存的方法 cleanPrefixes: function() { // 将 mVENDOR_PREFIXES 属性置为 null,清除之前缓存的供应商前缀信息 this.mVENDOR_PREFIXES = null; }, // 根据属性获取对应的供应商前缀数组的方法 prefixesForProperty: function(aProperty) { // 检查 mVENDOR_PREFIXES 是否为 null,如果为 null 则进行初始化 if (!this.mVENDOR_PREFIXES) { // 初始化 mVENDOR_PREFIXES 为一个空对象 this.mVENDOR_PREFIXES = {}; // 遍历 kCSS_VENDOR_PREFIXES.properties 数组 for (var i = 0; i < kCSS_VENDOR_PREFIXES.properties.length; i++) { // 获取当前属性 var p = kCSS_VENDOR_PREFIXES.properties[i]; // 检查该属性是否有 gecko 前缀,并且有 webkit、presto 或 trident 前缀之一 if (p.gecko && (p.webkit || p.presto || p.trident)) { // 初始化一个空对象 o 用于存储不同前缀 var o = {}; // 如果 kEXPORTS_FOR_GECKO 为 true,则将 gecko 前缀添加到 o 中 if (this.kEXPORTS_FOR_GECKO) o[p.gecko] = true; // 如果 kEXPORTS_FOR_WEBKIT 为 true 且有 webkit 前缀,则将 webkit 前缀添加到 o 中 if (this.kEXPORTS_FOR_WEBKIT && p.webkit) o[p.webkit] = true; // 如果 kEXPORTS_FOR_PRESTO 为 true 且有 presto 前缀,则将 presto 前缀添加到 o 中 if (this.kEXPORTS_FOR_PRESTO && p.presto) o[p.presto] = true; // 如果 kEXPORTS_FOR_TRIDENT 为 true 且有 trident 前缀,则将 trident 前缀添加到 o 中 if (this.kEXPORTS_FOR_TRIDENT && p.trident) o[p.trident] = true; // 在 mVENDOR_PREFIXES 中为 gecko 前缀创建一个空数组 this.mVENDOR_PREFIXES[p.gecko] = []; // 遍历 o 对象,将其中的前缀添加到 mVENDOR_PREFIXES[p.gecko] 数组中 for (var j in o) this.mVENDOR_PREFIXES[p.gecko].push(j) } } } // 检查传入的属性是否存在于 mVENDOR_PREFIXES 中 if (aProperty in this.mVENDOR_PREFIXES) // 如果存在,则对该属性对应的前缀数组进行排序并返回 return this.mVENDOR_PREFIXES[aProperty].sort(); // 如果不存在,则返回 null return null; }, // 解析颜色停止点的方法 parseColorStop: function(parser, token) { // 调用 parser 的 parseColor 方法解析颜色 var color = parser.parseColor(token); // 初始化位置为空字符串 var position = ""; // 如果颜色解析失败,则返回 null if (!color) return null; // 获取下一个标记 token = parser.getToken(true, true); // 检查标记是否为百分比或指定单位的尺寸 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { // 如果是,则将标记的值赋给位置 position = token.value; // 获取下一个标记 token = parser.getToken(true, true); } // 返回包含颜色和位置的对象 return { color: color, position: position } }, // 解析渐变的方法 parseGradient: function (parser, token) { // 初始化是否为径向渐变的标志为 false var isRadial = false; // 初始化渐变对象,设置是否重复为 false var gradient = { isRepeating: false }; // 检查标记是否不为空 if (token.isNotNull()) { // 检查标记是否为特定的渐变函数 if (token.isFunction("-moz-linear-gradient(") || token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) { // 如果是径向渐变函数,则将 isRadial 标志置为 true if (token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) { gradient.isRadial = true; } // 如果是重复渐变函数,则将 isRepeating 标志置为 true if (token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) { gradient.isRepeating = true; } // 获取下一个标记 token = parser.getToken(true, true); // 初始化是否有渐变线的标志为 false var haveGradientLine = false; // 初始化是否找到水平位置的标志为 false var foundHorizPosition = false; // 初始化是否有角度的标志为 false var haveAngle = false; // 检查标记是否为角度 if (token.isAngle()) { // 如果是,则将角度值赋给渐变对象的 angle 属性 gradient.angle = token.value; // 将 haveGradientLine 标志置为 true haveGradientLine = true; // 将 haveAngle 标志置为 true haveAngle = true; // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否为长度或特定的标识符 if (token.isLength() || token.isIdent("top") || token.isIdent("center") || token.isIdent("bottom") || token.isIdent("left") || token.isIdent("right")) { // 如果是,则将 haveGradientLine 标志置为 true haveGradientLine = true; // 检查标记是否为长度或水平位置标识符 if (token.isLength() || token.isIdent("left") || token.isIdent("right")) { // 如果是,则将 foundHorizPosition 标志置为 true foundHorizPosition = true; } // 将标记的值赋给渐变对象的 position 属性 gradient.position = token.value; // 获取下一个标记 token = parser.getToken(true, true); } // 如果有渐变线 if (haveGradientLine) { // 如果没有角度且标记为角度 if (!haveAngle && token.isAngle()) { // 将角度值赋给渐变对象的 angle 属性 gradient.angle = token.value; // 将 haveAngle 标志置为 true haveAngle = true; // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否符合特定条件 else if (token.isLength() || (foundHorizPosition && (token.isIdent("top") || token.isIdent("center") || token.isIdent("bottom"))) || (!foundHorizPosition && (token.isLength() || token.isIdent("top") || token.isIdent("center") || token.isIdent("bottom") || token.isIdent("left") || token.isIdent("right")))) { // 如果符合条件,则更新渐变对象的 position 属性 gradient.position = ("position" in gradient) ? gradient.position + " ": ""; gradient.position += token.value; // 获取下一个标记 token = parser.getToken(true, true); } // 如果没有角度且标记为角度 if (!haveAngle && token.isAngle()) { // 将角度值赋给渐变对象的 angle 属性 gradient.angle = token.value; // 将 haveAngle 标志置为 true haveAngle = true; // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否为逗号 if (!token.isSymbol(",")) // 如果不是,则返回 null return null; // 获取下一个标记 token = parser.getToken(true, true); } // 如果是径向渐变 if (gradient.isRadial) { // 检查标记是否为圆形或椭圆形标识符 if (token.isIdent("circle") || token.isIdent("ellipse")) { // 如果是,则将标识符的值赋给渐变对象的 shape 属性 gradient.shape = token.value; // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否为特定的尺寸标识符 if (token.isIdent("closest-side") || token.isIdent("closest-corner") || token.isIdent("farthest-side") || token.isIdent("farthest-corner") || token.isIdent("contain") || token.isIdent("cover")) { // 如果是,则将标识符的值赋给渐变对象的 size 属性 gradient.size = token.value; // 获取下一个标记 token = parser.getToken(true, true); } // 如果 shape 属性不存在且标记为圆形或椭圆形标识符 if (!("shape" in gradient) && (token.isIdent("circle") || token.isIdent("ellipse"))) { // 将标识符的值赋给渐变对象的 shape 属性 gradient.shape = token.value; // 获取下一个标记 token = parser.getToken(true, true); } // 如果 shape 或 size 属性存在且标记不是逗号 if ((("shape" in gradient) || ("size" in gradient)) && !token.isSymbol(",")) // 返回 null return null; // 如果 shape 或 size 属性存在 else if (("shape" in gradient) || ("size" in gradient)) // 获取下一个标记 token = parser.getToken(true, true); } // 解析第一个颜色停止点 var stop1 = this.parseColorStop(parser, token); // 如果解析失败,则返回 null if (!stop1) return null; // 获取当前标记 token = parser.currentToken(); // 检查标记是否为逗号 if (!token.isSymbol(",")) // 如果不是,则返回 null return null; // 获取下一个标记 token = parser.getToken(true, true); // 解析第二个颜色停止点 var stop2 = this.parseColorStop(parser, token); // 如果解析失败,则返回 null if (!stop2) return null; // 获取当前标记 token = parser.currentToken(); // 如果标记为逗号 if (token.isSymbol(",")) { // 获取下一个标记 token = parser.getToken(true, true); } // 将前两个颜色停止点添加到渐变对象的 stops 数组中 gradient.stops = [stop1, stop2]; // 循环解析剩余的颜色停止点,直到遇到右括号 while (!token.isSymbol(")")) { // 解析颜色停止点 var colorstop = this.parseColorStop(parser, token); // 如果解析失败,则返回 null if (!colorstop) return null; // 获取当前标记 token = parser.currentToken(); // 检查标记是否为右括号或逗号 if (!token.isSymbol(")") && !token.isSymbol(",")) // 如果不是,则返回 null return null; // 如果标记为逗号 if (token.isSymbol(",")) // 获取下一个标记 token = parser.getToken(true, true); // 将颜色停止点添加到渐变对象的 stops 数组中 gradient.stops.push(colorstop); } // 返回解析好的渐变对象 return gradient; } } // 如果解析失败,则返回 null return null; }, // 解析盒阴影的方法 parseBoxShadows: function(aString) { // 创建一个 CSSParser 实例 var parser = new CSSParser(); // 初始化解析器 parser._init(); // 设置解析器不保留空白字符 parser.mPreserveWS = false; // 设置解析器不保留注释 parser.mPreserveComments = false; // 初始化保留标记数组 parser.mPreservedTokens = []; // 初始化扫描器,传入要解析的字符串 parser.mScanner.init(aString); // 初始化阴影数组 var shadows = []; // 获取第一个标记 var token = parser.getToken(true, true); // 初始化颜色、模糊半径、水平偏移、垂直偏移和扩展半径 var color = "", blurRadius = "0px", offsetX = "0px", offsetY = "0px", spreadRadius = "0px"; // 初始化是否为内阴影的标志为 false var inset = false; // 循环处理标记,直到标记为空 while (token.isNotNull()) { // 检查标记是否为 "none" if (token.isIdent("none")) { // 如果是,则将一个包含 none 为 true 的对象添加到阴影数组中 shadows.push( { none: true } ); // 获取下一个标记 token = parser.getToken(true, true); } else { // 检查标记是否为 "inset" if (token.isIdent('inset')) { // 如果是,则将 inset 标志置为 true inset = true; // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否为百分比或指定单位的尺寸 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { // 如果是,则将标记的值赋给 offsetX var offsetX = token.value; // 获取下一个标记 token = parser.getToken(true, true); } else // 如果不是,则返回空数组 return []; // 如果不是内阴影且标记为 "inset" if (!inset && token.isIdent('inset')) { // 将 inset 标志置为 true inset = true; // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否为百分比或指定单位的尺寸 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { // 如果是,则将标记的值赋给 offsetY var offsetY = token.value; // 获取下一个标记 token = parser.getToken(true, true); } else // 如果不是,则返回空数组 return []; // 如果不是内阴影且标记为 "inset" if (!inset && token.isIdent('inset')) { // 将 inset 标志置为 true inset = true; // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否为百分比或指定单位的尺寸 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { // 如果是,则将标记的值赋给 blurRadius var blurRadius = token.value; // 获取下一个标记 token = parser.getToken(true, true); } // 如果不是内阴影且标记为 "inset" if (!inset && token.isIdent('inset')) { // 将 inset 标志置为 true inset = true; // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否为颜色函数、符号或标识符 if (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent()) { // 如果是,则调用解析器的 parseColor 方法解析颜色 var color = parser.parseColor(token); // 获取下一个标记 token = parser.getToken(true, true); } // 如果不是内阴影且标记为 "inset" if (!inset && token.isIdent('inset')) { // 将 inset 标志置为 true inset = true; // 获取下一个标记 token = parser.getToken(true, true); } // 将解析好的阴影信息添加到阴影数组中 shadows.push( { none: false, color: color, offsetX: offsetX, offsetY: offsetY, blurRadius: blurRadius, spreadRadius: spreadRadius } ); // 检查标记是否为逗号 if (token.isSymbol(",")) { // 如果是,则重置相关变量 inset = false; color = ""; blurRadius = "0px"; spreadRadius = "0px" offsetX = "0px"; offsetY = "0px"; // 获取下一个标记 token = parser.getToken(true, true); } else if (!token.isNotNull()) // 如果标记为空,则返回阴影数组 return shadows; else // 否则返回空数组 return []; } } // 返回阴影数组 return shadows; }, // 解析文本阴影的方法 parseTextShadows: function(aString) { // 创建一个 CSSParser 实例 var parser = new CSSParser(); // 初始化解析器 parser._init(); // 设置解析器不保留空白字符 parser.mPreserveWS = false; // 设置解析器不保留注释 parser.mPreserveComments = false; // 初始化保留标记数组 parser.mPreservedTokens = []; // 初始化扫描器,传入要解析的字符串 parser.mScanner.init(aString); // 初始化阴影数组 var shadows = []; // 获取第一个标记 var token = parser.getToken(true, true); // 初始化颜色、模糊半径、水平偏移和垂直偏移 var color = "", blurRadius = "0px", offsetX = "0px", offsetY = "0px"; // 循环处理标记,直到标记为空 while (token.isNotNull()) { // 检查标记是否为 "none" if (token.isIdent("none")) { // 如果是,则将一个包含 none 为 true 的对象添加到阴影数组中 shadows.push( { none: true } ); // 获取下一个标记 token = parser.getToken(true, true); } else { // 检查标记是否为颜色函数、符号或标识符 if (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent()) { // 如果是,则调用解析器的 parseColor 方法解析颜色 var color = parser.parseColor(token); // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否为百分比或指定单位的尺寸 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { // 如果是,则将标记的值赋给 offsetX var offsetX = token.value; // 获取下一个标记 token = parser.getToken(true, true); } else // 如果不是,则返回空数组 return []; // 检查标记是否为百分比或指定单位的尺寸 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { // 如果是,则将标记的值赋给 offsetY var offsetY = token.value; // 获取下一个标记 token = parser.getToken(true, true); } else // 如果不是,则返回空数组 return []; // 检查标记是否为百分比或指定单位的尺寸 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { // 如果是,则将标记的值赋给 blurRadius var blurRadius = token.value; // 获取下一个标记 token = parser.getToken(true, true); } // 如果颜色为空且标记为颜色函数、符号或标识符 if (!color && (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent())) { // 调用解析器的 parseColor 方法解析颜色 var color = parser.parseColor(token); // 获取下一个标记 token = parser.getToken(true, true); } // 将解析好的阴影信息添加到阴影数组中 shadows.push( { none: false, color: color, offsetX: offsetX, offsetY: offsetY, blurRadius: blurRadius } ); // 检查标记是否为逗号 if (token.isSymbol(",")) { // 如果是,则重置相关变量 color = ""; blurRadius = "0px"; offsetX = "0px"; offsetY = "0px"; // 获取下一个标记 token = parser.getToken(true, true); } else if (!token.isNotNull()) // 如果标记为空,则返回阴影数组 return shadows; else // 否则返回空数组 return []; } } // 返回阴影数组 return shadows; }, // 解析背景图像的方法 parseBackgroundImages: function(aString) { // 创建一个 CSSParser 实例 var parser = new CSSParser(); // 初始化解析器 parser._init(); // 设置解析器不保留空白字符 parser.mPreserveWS = false; // 设置解析器不保留注释 parser.mPreserveComments = false; // 初始化保留标记数组 parser.mPreservedTokens = []; // 初始化扫描器,传入要解析的字符串 parser.mScanner.init(aString); // 初始化背景数组 var backgrounds = []; // 获取第一个标记 var token = parser.getToken(true, true); // 循环处理标记,直到标记为空 while (token.isNotNull()) { /*if (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent()) { // 如果标记为颜色相关,解析颜色并添加到背景数组中 var color = parser.parseColor(token); backgrounds.push( { type: "color", value: color }); token = parser.getToken(true, true); } else */ // 检查标记是否为 url 函数 if (token.isFunction("url(")) { // 获取下一个标记 token = parser.getToken(true, true); // 调用解析器的 parseURL 方法解析 URL var urlContent = parser.parseURL(token); // 将解析好的 URL 信息添加到背景数组中 backgrounds.push( { type: "image", value: "url(" + urlContent }); // 获取下一个标记 token = parser.getToken(true, true); } // 检查标记是否为渐变函数 else if (token.isFunction("-moz-linear-gradient(") || token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) { // 调用 parseGradient 方法解析渐变 var gradient = this.parseGradient(parser, token); // 根据渐变类型将渐变信息添加到背景数组中 backgrounds.push( { type: gradient.isRadial ? "radial-gradient" : "linear-gradient", value: gradient }); // 获取下一个标记 token = parser.getToken(true, true); } else // 如果标记不符合要求,则返回 null return null; // 检查标记是否为逗号 if (token.isSymbol(",")) { // 获取下一个标记 token = parser.getToken(true, true); // 如果标记为空,则返回 null if (!token.isNotNull()) return null; } } // 返回背景数组 return backgrounds; }, // 序列化渐变对象为字符串的方法 serializeGradient: function(gradient) { // 根据渐变类型和是否重复构建渐变字符串的开头部分 var s = gradient.isRadial ? (gradient.isRepeating ? "-moz-repeating-radial-gradient(" : "-moz-radial-gradient(" ) : (gradient.isRepeating ? "-moz-repeating-linear-gradient(" : "-moz-linear-gradient(" ); // 如果渐变有角度或位置信息 if (gradient.angle || gradient.position) s += (gradient.angle ? gradient.angle + " ": "") + (gradient.position ? gradient.position : "") + ", "; // 如果是径向渐变且有形状或尺寸信息 if (gradient.isRadial && (gradient.shape || gradient.size)) s += (gradient.shape ? gradient.shape : "") + " " + (gradient.size ? gradient.size : "") + ", "; // 遍历渐变的颜色停止点数组 for (var i = 0; i < gradient.stops.length; i++) { // 获取当前颜色停止点 var colorstop = gradient.stops[i]; // 将颜色停止点信息添加到渐变字符串中 s += colorstop.color + (colorstop.position ? " " + colorstop.position : ""); // 如果不是最后一个颜色停止点,则添加逗号 if (i != gradient.stops.length -1) s += ", "; } // 添加右括号完成渐变字符串 s += ")"; // 返回序列化后的渐变字符串 return s; }, // 解析边框图像的方法 parseBorderImage: function(aString) { // 创建一个 CSSParser 实例 var parser = new CSSParser(); // 初始化解析器 parser._init(); // 设置解析器不保留空白字符 parser.mPreserveWS = false; // 设置解析器不保留注释 parser.mPreserveComments = false; // 初始化保留标记数组 parser.mPreservedTokens = []; // 初始化扫描器,传入要解析的字符串 parser.mScanner.init(aString); // 初始化边框图像对象 var borderImage = {url: "", offsets: [], widths: [], sizes: []}; // 获取第一个标记 var token = parser.getToken(true, true); // 检查标记是否为 url 函数 if (token.isFunction("url(")) { // 获取下一个标记 token = parser.getToken(true, true); // 调用解析器的 parseURL 方法解析 URL var urlContent = parser.parseURL(token); // 如果 URL 解析成功 if (urlContent) { // 处理 URL 字符串,去除首尾引号 borderImage.url = urlContent.substr(0, urlContent.length - 1).trim(); if ((borderImage.url[0] == '"' && borderImage.url[borderImage.url.length - 1] == '"') || (borderImage.url[0] == "'" && borderImage.url[borderImage.url.length - 1] == "'")) borderImage.url = borderImage.url.substr(1, borderImage.url.length - 2); } else // 如果 URL 解析失败,则返回 null return null; } else // 如果标记不是 url 函数,则返回 null return null; // 获取下一个标记 token = parser.getToken(true, true); // 检查标记是否为数字或百分比 if (token.isNumber() || token.isPercentage()) // 如果是,则将标记的值添加到边框图像的 offsets 数组中 borderImage.offsets.push(token.value); else // 如果不是,则返回 null return null; // 循环处理后续的标记,最多处理 3 个 var i; for (i= 0; i < 3; i++) { // 获取下一个标记 token = parser.getToken(true, true); // 检查标记是否为数字或百分比 if (token.isNumber() || token.isPercentage()) // 如果是,则将标记的值添加到边框图像的 offsets 数组中 borderImage.offsets.push(token.value); else // 如果不是,则跳出循环 break; } // 如果处理了 3 个标记,则获取下一个标记 if (i == 3) token = parser.getToken(true, true); // 检查标记是否为斜杠 if (token.isSymbol("/")) { // 获取下一个标记 token = parser.getToken(true, true); // 检查标记是否为尺寸、数字 0 或特定的边框宽度名称 if (token.isDimension() || token.isNumber("0") || (token.isIdent() && token.value in parser.kBORDER_WIDTH_NAMES)) // 如果是,则将标记的值添加到边框图像的 widths 数组中 borderImage.widths.push(token.value); else // 如果不是,则返回 null return null; // 循环处理后续的标记,最多处理 3 个 for (var i = 0; i < 3; i++) { // 获取下一个标记 token = parser.getToken(true, true); // 检查标记是否为尺寸、数字 0 或特定的边框宽度名称 if (token.isDimension() || token.isNumber("0") || (token.isIdent() && token.value in parser.kBORDER_WIDTH_NAMES)) // 如果是,则将标记的值添加到边框图像的 widths 数组中 borderImage.widths.push(token.value); else // 如果不是,则跳出循环 break; } // 如果处理了 3 个标记,则获取下一个标记 if (i == 3) token = parser.getToken(true, true); } // 循环处理后续的标记,最多处理 2 个 for (var i = 0; i < 2; i++) { // 检查标记是否为特定的尺寸标识符 if (token.isIdent("stretch") || token.isIdent("repeat") || token.isIdent("round")) // 如果是,则将标记的值添加到边框图像的 sizes 数组中 borderImage.sizes.push(token.value); else if (!token.isNotNull()) // 如果标记为空,则返回边框图像对象 return borderImage; else // 如果标记不符合要求,则返回 null return null; // 获取下一个标记 token = parser.getToken(true, true); } // 如果标记为空,则返回边框图像对象 if (!token.isNotNull()) return borderImage; // 如果解析失败,则返回 null return null; }, // 解析媒体查询的方法 parseMediaQuery: function(aString) { // 定义一个包含所有有效约束条件的对象 var kCONSTRAINTS = { "width": true, "min-width": true, "max-width": true, "height": true, "min-height": true, "max-height": true, "device-width": true, "min-device-width": true, "max-device-width": true, "device-height": true, "min-device-height": true, "max-device-height": true, "orientation": true, "aspect-ratio": true, "min-aspect-ratio": true, "max-aspect-ratio": true, "device-aspect-ratio": true, "min-device-aspect-ratio": true, "max-device-aspect-ratio": true, "color": true, "min-color": true, "max-color": true, "color-index": true, "min-color-index": true, "max-color-index": true, "monochrome": true, "min-monochrome": true, "max-monochrome": true, "resolution": true, "min-resolution // 模拟 kCSS_VENDOR_PREFIXES const kCSS_VENDOR_PREFIXES = { properties: [ { gecko: '-moz-example', webkit: '-webkit-example', presto: '-o-example', trident: '-ms-example' } ] }; // 模拟 CSSParser 类 class CSSParser { constructor() { this.mScanner = { init: function (aString) { // 初始化扫描器逻辑 } }; } _init() { // 初始化解析器逻辑 } getToken() { // 模拟获取标记逻辑 return { isNotNull: function () { return true; }, isFunction: function (name) { return false; }, isAngle: function () { return false; }, isLength: function () { return false; }, isIdent: function (value) { return false; }, isSymbol: function (symbol) { return false; }, isPercentage: function () { return false; }, isDimensionOfUnit: function (unit) { return false; }, parseColor: function (token) { return 'color'; }, currentToken: function () { return { isSymbol: function (symbol) { return false; } }; }, parseURL: function (token) { return 'url-content'; } }; } } // 定义对象 const obj = { kEXPORTS_FOR_GECKO: true, kEXPORTS_FOR_WEBKIT: true, kEXPORTS_FOR_PRESTO: true, kEXPORTS_FOR_TRIDENT: true, mVENDOR_PREFIXES: null, // 清除前缀缓存 cleanPrefixes: function () { this.mVENDOR_PREFIXES = null; }, // 获取属性的前缀列表 prefixesForProperty: function (aProperty) { if (!this.mVENDOR_PREFIXES) { this.mVENDOR_PREFIXES = {}; // 遍历所有属性前缀 for (var i = 0; i < kCSS_VENDOR_PREFIXES.properties.length; i++) { var p = kCSS_VENDOR_PREFIXES.properties[i]; if (p.gecko && (p.webkit || p.presto || p.trident)) { var o = {}; // 根据导出标志添加前缀 if (this.kEXPORTS_FOR_GECKO) o[p.gecko] = true; if (this.kEXPORTS_FOR_WEBKIT && p.webkit) o[p.webkit] = true; if (this.kEXPORTS_FOR_PRESTO && p.presto) o[p.presto] = true; if (this.kEXPORTS_FOR_TRIDENT && p.trident) o[p.trident] = true; this.mVENDOR_PREFIXES[p.gecko] = []; for (var j in o) this.mVENDOR_PREFIXES[p.gecko].push(j) } } } if (aProperty in this.mVENDOR_PREFIXES) return this.mVENDOR_PREFIXES[aProperty].sort(); return null; }, // 解析颜色停止点 parseColorStop: function (parser, token) { var color = parser.parseColor(token); var position = ""; if (!color) return null; token = parser.getToken(true, true); // 检查是否有位置信息 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { position = token.value; token = parser.getToken(true, true); } return { color: color, position: position } }, // 解析渐变 parseGradient: function (parser, token) { var isRadial = false; var gradient = { isRepeating: false }; if (token.isNotNull()) { if (token.isFunction("-moz-linear-gradient(") || token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) { if (token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) { gradient.isRadial = true; } if (token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) { gradient.isRepeating = true; } token = parser.getToken(true, true); var haveGradientLine = false; var foundHorizPosition = false; var haveAngle = false; // 检查是否有角度信息 if (token.isAngle()) { gradient.angle = token.value; haveGradientLine = true; haveAngle = true; token = parser.getToken(true, true); } // 检查是否有位置信息 if (token.isLength() || token.isIdent("top") || token.isIdent("center") || token.isIdent("bottom") || token.isIdent("left") || token.isIdent("right")) { haveGradientLine = true; if (token.isLength() || token.isIdent("left") || token.isIdent("right")) { foundHorizPosition = true; } gradient.position = token.value; token = parser.getToken(true, true); } if (haveGradientLine) { if (!haveAngle && token.isAngle()) { gradient.angle = token.value; haveAngle = true; token = parser.getToken(true, true); } else if (token.isLength() || (foundHorizPosition && (token.isIdent("top") || token.isIdent("center") || token.isIdent("bottom"))) || (!foundHorizPosition && (token.isLength() || token.isIdent("top") || token.isIdent("center") || token.isIdent("bottom") || token.isIdent("left") || token.isIdent("right")))) { gradient.position = ("position" in gradient) ? gradient.position + " " : ""; gradient.position += token.value; token = parser.getToken(true, true); } if (!haveAngle && token.isAngle()) { gradient.angle = token.value; haveAngle = true; token = parser.getToken(true, true); } if (!token.isSymbol(",")) return null; token = parser.getToken(true, true); } if (gradient.isRadial) { // 检查是否有形状信息 if (token.isIdent("circle") || token.isIdent("ellipse")) { gradient.shape = token.value; token = parser.getToken(true, true); } // 检查是否有大小信息 if (token.isIdent("closest-side") || token.isIdent("closest-corner") || token.isIdent("farthest-side") || token.isIdent("farthest-corner") || token.isIdent("contain") || token.isIdent("cover")) { gradient.size = token.value; token = parser.getToken(true, true); } if (!("shape" in gradient) && (token.isIdent("circle") || token.isIdent("ellipse"))) { gradient.shape = token.value; token = parser.getToken(true, true); } if ((("shape" in gradient) || ("size" in gradient)) && !token.isSymbol(",")) return null; else if (("shape" in gradient) || ("size" in gradient)) token = parser.getToken(true, true); } var stop1 = this.parseColorStop(parser, token); if (!stop1) return null; token = parser.currentToken(); if (!token.isSymbol(",")) return null; token = parser.getToken(true, true); var stop2 = this.parseColorStop(parser, token); if (!stop2) return null; token = parser.currentToken(); if (token.isSymbol(",")) { token = parser.getToken(true, true); } gradient.stops = [stop1, stop2]; // 解析所有颜色停止点 while (!token.isSymbol(")")) { var colorstop = this.parseColorStop(parser, token); if (!colorstop) return null; token = parser.currentToken(); if (!token.isSymbol(")") && !token.isSymbol(",")) return null; if (token.isSymbol(",")) token = parser.getToken(true, true); gradient.stops.push(colorstop); } return gradient; } } return null; }, // 解析盒子阴影 parseBoxShadows: function (aString) { var parser = new CSSParser(); parser._init(); parser.mPreserveWS = false; parser.mPreserveComments = false; parser.mPreservedTokens = []; parser.mScanner.init(aString); var shadows = []; var token = parser.getToken(true, true); var color = "", blurRadius = "0px", offsetX = "0px", offsetY = "0px", spreadRadius = "0px"; var inset = false; while (token.isNotNull()) { if (token.isIdent("none")) { shadows.push({ none: true }); token = parser.getToken(true, true); } else { // 检查是否有 inset 关键字 if (token.isIdent('inset')) { inset = true; token = parser.getToken(true, true); } // 解析偏移量 X if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { var offsetX = token.value; token = parser.getToken(true, true); } else return []; if (!inset && token.isIdent('inset')) { inset = true; token = parser.getToken(true, true); } // 解析偏移量 Y if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { var offsetY = token.value; token = parser.getToken(true, true); } else return []; if (!inset && token.isIdent('inset')) { inset = true; token = parser.getToken(true, true); } // 解析模糊半径 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { var blurRadius = token.value; token = parser.getToken(true, true); } if (!inset && token.isIdent('inset')) { inset = true; token = parser.getToken(true, true); } // 解析颜色 if (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent()) { var color = parser.parseColor(token); token = parser.getToken(true, true); } if (!inset && token.isIdent('inset')) { inset = true; token = parser.getToken(true, true); } shadows.push({ none: false, color: color, offsetX: offsetX, offsetY: offsetY, blurRadius: blurRadius, spreadRadius: spreadRadius }); if (token.isSymbol(",")) { inset = false; color = ""; blurRadius = "0px"; spreadRadius = "0px" offsetX = "0px"; offsetY = "0px"; token = parser.getToken(true, true); } else if (!token.isNotNull()) return shadows; else return []; } } return shadows; }, // 解析文本阴影 parseTextShadows: function (aString) { var parser = new CSSParser(); parser._init(); parser.mPreserveWS = false; parser.mPreserveComments = false; parser.mPreservedTokens = []; parser.mScanner.init(aString); var shadows = []; var token = parser.getToken(true, true); var color = "", blurRadius = "0px", offsetX = "0px", offsetY = "0px"; while (token.isNotNull()) { if (token.isIdent("none")) { shadows.push({ none: true }); token = parser.getToken(true, true); } else { // 解析颜色 if (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent()) { var color = parser.parseColor(token); token = parser.getToken(true, true); } // 解析偏移量 X if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { var offsetX = token.value; token = parser.getToken(true, true); } else return []; // 解析偏移量 Y if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { var offsetY = token.value; token = parser.getToken(true, true); } else return []; // 解析模糊半径 if (token.isPercentage() || token.isDimensionOfUnit("cm") || token.isDimensionOfUnit("mm") || token.isDimensionOfUnit("in") || token.isDimensionOfUnit("pc") || token.isDimensionOfUnit("px") || token.isDimensionOfUnit("em") || token.isDimensionOfUnit("ex") || token.isDimensionOfUnit("pt")) { var blurRadius = token.value; token = parser.getToken(true, true); } if (!color && (token.isFunction("rgb(") || token.isFunction("rgba(") || token.isFunction("hsl(") || token.isFunction("hsla(") || token.isSymbol("#") || token.isIdent())) { var color = parser.parseColor(token); token = parser.getToken(true, true); } shadows.push({ none: false, color: color, offsetX: offsetX, offsetY: offsetY, blurRadius: blurRadius }); if (token.isSymbol(",")) { color = ""; blurRadius = "0px"; offsetX = "0px"; offsetY = "0px"; token = parser.getToken(true, true); } else if (!token.isNotNull()) return shadows; else return []; } } return shadows; }, // 解析背景图像 parseBackgroundImages: function (aString) { var parser = new CSSParser(); parser._init(); parser.mPreserveWS = false; parser.mPreserveComments = false; parser.mPreservedTokens = []; parser.mScanner.init(aString); var backgrounds = []; var token = parser.getToken(true, true); while (token.isNotNull()) { if (token.isFunction("url(")) { token = parser.getToken(true, true); var urlContent = parser.parseURL(token); backgrounds.push({ type: "image", value: "url(" + urlContent }); token = parser.getToken(true, true); } else if (token.isFunction("-moz-linear-gradient(") || token.isFunction("-moz-radial-gradient(") || token.isFunction("-moz-repeating-linear-gradient(") || token.isFunction("-moz-repeating-radial-gradient(")) { var gradient = this.parseGradient(parser, token); backgrounds.push({ type: gradient.isRadial ? "radial-gradient" : "linear-gradient", value: gradient }); token = parser.getToken(true, true); } else return null; if (token.isSymbol(",")) { token = parser.getToken(true, true); if (!token.isNotNull()) return null; } } return backgrounds; }, // 序列化渐变 serializeGradient: function (gradient) { var s = gradient.isRadial ? (gradient.isRepeating ? "-moz-repeating-radial-gradient(" : "-moz-radial-gradient(" ) : (gradient.isRepeating ? "-moz-repeating-linear-gradient(" : "-moz-linear-gradient(" ); if (gradient.angle || gradient.position) s += (gradient.angle ? gradient.angle + " " : "") + (gradient.position ? gradient.position : "") + ", "; if (gradient.isRadial && (gradient.shape || gradient.size)) s += (gradient.shape ? gradient.shape : "") + " " + (gradient.size ? gradient.size : "") + ", "; for (var i = 0; i < gradient.stops.length; i++) { var colorstop = gradient.stops[i]; s += colorstop.color + (colorstop.position ? " " + colorstop.position : ""); if (i != gradient.stops.length - 1) s += ", "; } s += ")"; return s; }, // 解析边框图像 parseBorderImage: function (aString) { var parser = new CSSParser(); parser._init(); parser.mPreserveWS = false; parser.mPreserveComments = false; parser.mPreservedTokens = []; parser.mScanner.init(aString); var borderImage = { url: "", offsets: [], widths: [], sizes: [] }; var token = parser.getToken(true, true); if (token.isFunction("url(")) { token = parser.getToken(true, true); var urlContent = parser.parseURL(token); if (urlContent) { borderImage.url = urlContent.substr(0, urlContent.length - 1).trim(); if ((borderImage.url[0] == '"' && borderImage.url[borderImage.url.length - 1] == '"') || (borderImage.url[0] == "'" && borderImage.url[borderImage.url.length - 1] == "'")) borderImage.url = borderImage.url.substr(1, borderImage.url.length - 2); } else return null; } else return null; token = parser.getToken(true, true); if (token.isNumber() || token.isPercentage()) borderImage.offsets.push(token.value); else return null; var i; for (i = 0; i < 3; i++) { token = parser.getToken(true, true); if (token.isNumber() || token.isPercentage()) borderImage.offsets.push(token.value); else break; } if (i == 3) token = parser.getToken(true, true); if (token.isSymbol("/")) { token = parser.getToken(true, true); if (token.isDimension() || token.isNumber("0") || (token.isIdent() && token.value in parser.kBORDER_WIDTH_NAMES)) borderImage.widths.push(token.value); else return null; for (var i = 0; i < 3; i++) { token = parser.getToken(true, true); if (token.isDimension() || token.isNumber("0") || (token.isIdent() && token.value in parser.kBORDER_WIDTH_NAMES)) borderImage.widths.push(token.value); else break; } if (i == 3) token = parser.getToken(true, true); } for (var i = 0; i < 2; i++) { if (token.isIdent("stretch") || token.isIdent("repeat") || token.isIdent("round")) borderImage.sizes.push(token.value); else if (!token.isNotNull()) return borderImage; else return null; token = parser.getToken(true, true); } if (!token.isNotNull()) return borderImage; return null; }, // 解析媒体查询 parseMediaQuery: function (aString) { var kCONSTRAINTS = { "width": true, "min-width": true, "max-width": true, "height": true, "min-height": true, "max-height": true, "device-width": true, "min-device-width": true, "max-device-width": true, "device-height": true, "min-device-height": true, "max-device-height": true, "orientation": true, "aspect-ratio": true, "min-aspect-ratio": true, "max-aspect-ratio": true, "device-aspect-ratio": true, "min-device-aspect-ratio": true, "max-device-aspect-ratio": true, "color": true, "min-color": true, "max-color": true, "color-index": true, "min-color-index": true, "max-color-index": true, "monochrome": true, "min-monochrome": true, "max-monochrome": true, "resolution": true, "min-resolution": true, "max-resolution": true, "scan": true, "grid": true }; var parser = new CSSParser(); parser._init(); parser.mPreserveWS = false; parser.mPreserveComments = false; parser.mPreservedTokens = []; parser.mScanner.init(aString); var m = { amplifier: "", medium: "", constraints: [] }; var token = parser.getToken(true, true); if (token.isIdent("all") || token.isIdent("aural") || token.isIdent("braille") || token.isIdent("handheld") || token.isIdent("print") || token.isIdent("projection") || token.isIdent("screen") || token.isIdent("tty") || token.isIdent("tv")) { m.medium = token.value; token = parser.getToken(true, true); } else if (token.isIdent("not") || token.isIdent("only")) { m.amplifier = token.value; token = parser.getToken(true, true); if (token.isIdent("all") || token.isIdent("aural") || token.isIdent("braille") || token.isIdent("handheld") || token.isIdent("print") || token.isIdent("projection") || token.isIdent("screen") || token.isIdent("tty") || token.isIdent("tv")) { m.medium = token.value; token = parser.getToken(true, true); } else return null; } if (m.medium) { if (!token.isNotNull()) return m; if (token.isIdent("and")) { token = parser.getToken(true, true); } else return null; } while (token.isSymbol("(")) { token = parser.getToken(true, true); if (token.isIdent() && (token.value in kCONSTRAINTS)) { var constraint = token.value; token = parser.getToken(true, true); if (token.isSymbol(":")) { token = parser.getToken(true, true); var values = []; while (!token.isSymbol(")")) { values.push(token.value); token = parser.getToken(true, true); } if (token.isSymbol(")")) { m.constraints.push({ constraint: constraint, value: values }); token = parser.getToken(true, true); if (token.isNotNull()) { if (token.isIdent("and")) { token = parser.getToken(true, true); } else return null; } else return m; } else return null; } else if (token.isSymbol(")")) { m.constraints.push({ constraint: constraint, value: null }); token = parser.getToken(true, true); if (token.isNotNull()) { if (token.isIdent("and")) { token = parser.getToken(true, true); } else return null; } else return m; } else return null; } else return null; } return m; } }; // 简单测试 console.log(obj.prefixesForProperty('-moz-example')); // 解析命名空间规则 parseNamespaceRule: function(aToken, aSheet) { // 获取当前行号,通过 CountLF 函数统计已扫描内容中的换行符数量 var currentLine = CountLF(this.mScanner.getAlreadyScanned()); // 初始化规则字符串,赋值为当前 token 的值 var s = aToken.value; // 标记规则是否有效,初始为 false var valid = false; // 保存当前解析状态 this.preserveState(); // 获取下一个 token var token = this.getToken(true, true); if (token.isNotNull()) { // 初始化前缀为空字符串 var prefix = ""; // 初始化 URL 为空字符串 var url = ""; if (token.isIdent()) { // 如果 token 是标识符,将其赋值给前缀 prefix = token.value; // 将前缀添加到规则字符串中 s += " " + prefix; // 获取下一个 token token = this.getToken(true, true); } if (token) { // 标记是否找到 URL,初始为 false var foundURL = false; if (token.isString()) { // 如果 token 是字符串,标记找到 URL foundURL = true; // 将字符串赋值给 URL url = token.value; // 将 URL 添加到规则字符串中 s += " " + url; } else if (token.isFunction("url(")) { // 如果 token 是 url 函数,获取下一个 token token = this.getToken(true, true); // 解析 URL 内容 var urlContent = this.parseURL(token); if (urlContent) { // 如果解析成功,将 url 内容添加到 URL 中 url += "url(" + urlContent; // 标记找到 URL foundURL = true; // 将 url 内容添加到规则字符串中 s += " " + urlContent; } } } if (foundURL) { // 如果找到 URL,获取下一个 token token = this.getToken(true, true); if (token.isSymbol(";")) { // 如果 token 是分号,将分号添加到规则字符串中 s += ";"; // 忘记之前保存的状态 this.forgetState(); // 创建一个新的 jscsspNamespaceRule 对象 var rule = new jscsspNamespaceRule(); // 设置规则的当前行号 rule.currentLine = currentLine; // 设置规则的解析后的 CSS 文本 rule.parsedCssText = s; // 设置规则的前缀 rule.prefix = prefix; // 设置规则的 URL rule.url = url; // 设置规则的父样式表 rule.parentStyleSheet = aSheet; // 将规则添加到父样式表的 CSS 规则列表中 aSheet.cssRules.push(rule); // 返回 true 表示规则解析成功 return true; } } } // 恢复之前保存的状态 this.restoreState(); // 将未知的 @namespace 规则添加到样式表中 this.addUnknownAtRule(aSheet, "@namespace"); // 返回 false 表示规则解析失败 return false; }, // 解析字体规则 parseFontFaceRule: function(aToken, aSheet) { // 获取当前行号,通过 CountLF 函数统计已扫描内容中的换行符数量 var currentLine = CountLF(this.mScanner.getAlreadyScanned()); // 初始化规则字符串,赋值为当前 token 的值 var s = aToken.value; // 标记规则是否有效,初始为 false var valid = false; // 初始化描述符数组 var descriptors = []; // 保存当前解析状态 this.preserveState(); // 获取下一个 token var token = this.getToken(true, true); if (token.isNotNull()) { // 期望遇到块开始符号 { if (token.isSymbol("{")) { // 将块开始符号添加到规则字符串中 s += " " + token.value; // 获取下一个 token var token = this.getToken(true, false); while (true) { if (token.isSymbol("}")) { // 如果遇到块结束符号 },将其添加到规则字符串中 s += "}"; // 标记规则有效 valid = true; // 跳出循环 break; } else { // 解析声明 var d = this.parseDeclaration(token, descriptors, false, false, aSheet); // 将声明添加到规则字符串中 s += ((d && descriptors.length) ? " " : "") + d; } // 获取下一个 token token = this.getToken(true, false); } } } if (valid) { // 如果规则有效,忘记之前保存的状态 this.forgetState(); // 创建一个新的 jscsspFontFaceRule 对象 var rule = new jscsspFontFaceRule(); // 设置规则的当前行号 rule.currentLine = currentLine; // 设置规则的解析后的 CSS 文本 rule.parsedCssText = s; // 设置规则的描述符 rule.descriptors = descriptors; // 设置规则的父样式表 rule.parentStyleSheet = aSheet; // 将规则添加到父样式表的 CSS 规则列表中 aSheet.cssRules.push(rule); // 返回 true 表示规则解析成功 return true; } // 恢复之前保存的状态 this.restoreState(); // 返回 false 表示规则解析失败 return false; }, // 解析页面规则 parsePageRule: function(aToken, aSheet) { // 获取当前行号,通过 CountLF 函数统计已扫描内容中的换行符数量 var currentLine = CountLF(this.mScanner.getAlreadyScanned()); // 初始化规则字符串,赋值为当前 token 的值 var s = aToken.value; // 标记规则是否有效,初始为 false var valid = false; // 初始化声明数组 var declarations = []; // 保存当前解析状态 this.preserveState(); // 获取下一个 token var token = this.getToken(true, true); // 初始化页面选择器为空字符串 var pageSelector = ""; if (token.isSymbol(":") || token.isIdent()) { if (token.isSymbol(":")) { // 如果 token 是冒号,将其添加到页面选择器中 pageSelector = ":"; // 获取下一个 token token = this.getToken(false, false); } if (token.isIdent()) { // 如果 token 是标识符,将其添加到页面选择器中 pageSelector += token.value; // 将页面选择器添加到规则字符串中 s += " " + pageSelector; // 获取下一个 token token = this.getToken(true, true); } } if (token.isNotNull()) { // 期望遇到块开始符号 { if (token.isSymbol("{")) { // 将块开始符号添加到规则字符串中 s += " " + token.value; // 获取下一个 token var token = this.getToken(true, false); while (true) { if (token.isSymbol("}")) { // 如果遇到块结束符号 },将其添加到规则字符串中 s += "}"; // 标记规则有效 valid = true; // 跳出循环 break; } else { // 解析声明 var d = this.parseDeclaration(token, declarations, true, true, aSheet); // 将声明添加到规则字符串中 s += ((d && declarations.length) ? " " : "") + d; } // 获取下一个 token token = this.getToken(true, false); } } } if (valid) { // 如果规则有效,忘记之前保存的状态 this.forgetState(); // 创建一个新的 jscsspPageRule 对象 var rule = new jscsspPageRule(); // 设置规则的当前行号 rule.currentLine = currentLine; // 设置规则的解析后的 CSS 文本 rule.parsedCssText = s; // 设置规则的页面选择器 rule.pageSelector = pageSelector; // 设置规则的声明 rule.declarations = declarations; // 设置规则的父样式表 rule.parentStyleSheet = aSheet; // 将规则添加到父样式表的 CSS 规则列表中 aSheet.cssRules.push(rule); // 返回 true 表示规则解析成功 return true; } // 恢复之前保存的状态 this.restoreState(); // 返回 false 表示规则解析失败 return false; }, // 解析默认属性值 parseDefaultPropertyValue: function(token, aDecl, aAcceptPriority, descriptor, aSheet) { // 初始化属性值文本为空字符串 var valueText = ""; // 初始化块栈,用于处理嵌套的块 var blocks = []; // 标记是否找到优先级,初始为 false var foundPriority = false; // 初始化值数组 var values = []; while (token.isNotNull()) { if ((token.isSymbol(";") || token.isSymbol("}") || token.isSymbol("!")) && !blocks.length) { if (token.isSymbol("}")) { // 如果遇到块结束符号 },将其放回扫描器 this.ungetToken(); } // 跳出循环 break; } if (token.isIdent(this.kINHERIT)) { if (values.length) { // 如果值数组不为空,返回空字符串表示解析失败 return ""; } else { // 将 inherit 赋值给属性值文本 valueText = this.kINHERIT; // 创建一个新的 jscsspVariable 对象表示 inherit 值 var value = new jscsspVariable(kJscsspINHERIT_VALUE, aSheet); // 将值添加到值数组中 values.push(value); // 获取下一个 token token = this.getToken(true, true); // 跳出循环 break; } } else if (token.isSymbol("{") || token.isSymbol("(") || token.isSymbol("[")) { // 如果遇到块开始符号,将其添加到块栈中 blocks.push(token.value); } else if (token.isSymbol("}") || token.isSymbol("]")) { if (blocks.length) { // 获取块栈顶元素 var ontop = blocks[blocks.length - 1]; if ((token.isSymbol("}") && ontop == "{") || (token.isSymbol(")") && ontop == "(") || (token.isSymbol("]") && ontop == "[")) { // 如果匹配到对应的块结束符号,将栈顶元素弹出 blocks.pop(); } } } // 处理函数调用 if (token.isFunction()) { if (token.isFunction("var(")) { // 如果是 var 函数,获取下一个 token token = this.getToken(true, true); if (token.isIdent()) { // 如果是标识符,获取变量名 var name = token.value; // 获取下一个 token token = this.getToken(true, true); if (token.isSymbol(")")) { // 如果是右括号,创建一个新的 jscsspVariable 对象表示变量值 var value = new jscsspVariable(kJscsspVARIABLE_VALUE, aSheet); // 将变量名添加到属性值文本中 valueText += "var(" + name + ")"; // 设置变量名 value.name = name; // 将值添加到值数组中 values.push(value); } else { // 解析失败,返回空字符串 return ""; } } else { // 解析失败,返回空字符串 return ""; } } else { // 如果是其他函数,获取函数名 var fn = token.value; // 获取下一个 token token = this.getToken(false, true); // 解析函数参数 var arg = this.parseFunctionArgument(token); if (arg) { // 如果解析成功,将函数名和参数添加到属性值文本中 valueText += fn + arg; // 创建一个新的 jscsspVariable 对象表示函数值 var value = new jscsspVariable(kJscsspPRIMITIVE_VALUE, aSheet); // 设置函数值 value.value = fn + arg; // 将值添加到值数组中 values.push(value); } else { // 解析失败,返回空字符串 return ""; } } } else if (token.isSymbol("#")) { // 如果是 # 符号,解析颜色值 var color = this.parseColor(token); if (color) { // 如果解析成功,将颜色值添加到属性值文本中 valueText += color; // 创建一个新的 jscsspVariable 对象表示颜色值 var value = new jscsspVariable(kJscsspPRIMITIVE_VALUE, aSheet); // 设置颜色值 value.value = color; // 将值添加到值数组中 values.push(value); } else { // 解析失败,返回空字符串 return ""; } } else if (!token.isWhiteSpace() && !token.isSymbol(",")) { // 如果不是空白字符或逗号,创建一个新的 jscsspVariable 对象表示原始值 var value = new jscsspVariable(kJscsspPRIMITIVE_VALUE, aSheet); // 设置原始值 value.value = token.value; // 将值添加到值数组中 values.push(value); // 将原始值添加到属性值文本中 valueText += token.value; } else { // 将空白字符或逗号添加到属性值文本中 valueText += token.value; } // 获取下一个 token token = this.getToken(false, true); } if (values.length && valueText) { // 如果值数组和属性值文本都不为空,忘记之前保存的状态 this.forgetState(); // 创建一个新的 jscsspDeclaration 对象并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValuesArray(descriptor, values, valueText)); // 返回属性值文本 return valueText; } // 解析失败,返回空字符串 return ""; }, // 解析边距或内边距简写属性 parseMarginOrPaddingShorthand: function(token, aDecl, aAcceptPriority, aProperty) { // 初始化顶部边距或内边距为 null var top = null; // 初始化底部边距或内边距为 null var bottom = null; // 初始化左边距或内边距为 null var left = null; // 初始化右边距或内边距为 null var right = null; // 初始化值数组 var values = []; while (true) { if (!token.isNotNull()) { // 如果 token 为空,跳出循环 break; } if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) { if (token.isSymbol("}")) { // 如果遇到块结束符号 },将其放回扫描器 this.ungetToken(); } // 跳出循环 break; } else if (!values.length && token.isIdent(this.kINHERIT)) { // 如果值数组为空且 token 是 inherit,将 inherit 添加到值数组中 values.push(token.value); // 获取下一个 token token = this.getToken(true, true); // 跳出循环 break; } else if (token.isDimension() || token.isNumber("0") || token.isPercentage() || token.isIdent("auto")) { // 如果 token 是尺寸、数字 0、百分比或 auto,将其添加到值数组中 values.push(token.value); } else { // 解析失败,返回空字符串 return ""; } // 获取下一个 token token = this.getToken(true, true); } // 获取值数组的长度 var count = values.length; switch (count) { case 1: // 如果只有一个值,将其赋值给顶部、底部、左边和右边的边距或内边距 top = values[0]; bottom = top; left = top; right = top; break; case 2: // 如果有两个值,将第一个值赋值给顶部和底部,第二个值赋值给左边和右边 top = values[0]; bottom = top; left = values[1]; right = left; break; case 3: // 如果有三个值,将第一个值赋值给顶部,第二个值赋值给左边和右边,第三个值赋值给底部 top = values[0]; left = values[1]; right = left; bottom = values[2]; break; case 4: // 如果有四个值,分别赋值给顶部、右边、底部和左边 top = values[0]; right = values[1]; bottom = values[2]; left = values[3]; break; default: // 解析失败,返回空字符串 return ""; } // 忘记之前保存的状态 this.forgetState(); // 创建顶部边距或内边距的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue(aProperty + "-top", top)); // 创建右边距或内边距的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue(aProperty + "-right", right)); // 创建底部边距或内边距的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue(aProperty + "-bottom", bottom)); // 创建左边距或内边距的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue(aProperty + "-left", left)); // 返回边距或内边距的值 return top + " " + right + " " + bottom + " " + left; }, // 解析边框颜色简写属性 parseBorderColorShorthand: function(token, aDecl, aAcceptPriority) { // 初始化顶部边框颜色为 null var top = null; // 初始化底部边框颜色为 null var bottom = null; // 初始化左边框颜色为 null var left = null; // 初始化右边框颜色为 null var right = null; // 初始化值数组 var values = []; while (true) { if (!token.isNotNull()) { // 如果 token 为空,跳出循环 break; } if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) { if (token.isSymbol("}")) { // 如果遇到块结束符号 },将其放回扫描器 this.ungetToken(); } // 跳出循环 break; } else if (!values.length && token.isIdent(this.kINHERIT)) { // 如果值数组为空且 token 是 inherit,将 inherit 添加到值数组中 values.push(token.value); // 获取下一个 token token = this.getToken(true, true); // 跳出循环 break; } else { // 解析颜色值 var color = this.parseColor(token); if (color) { // 如果解析成功,将颜色值添加到值数组中 values.push(color); } else { // 解析失败,返回空字符串 return ""; } } // 获取下一个 token token = this.getToken(true, true); } // 获取值数组的长度 var count = values.length; switch (count) { case 1: // 如果只有一个值,将其赋值给顶部、底部、左边和右边的边框颜色 top = values[0]; bottom = top; left = top; right = top; break; case 2: // 如果有两个值,将第一个值赋值给顶部和底部,第二个值赋值给左边和右边 top = values[0]; bottom = top; left = values[1]; right = left; break; case 3: // 如果有三个值,将第一个值赋值给顶部,第二个值赋值给左边和右边,第三个值赋值给底部 top = values[0]; left = values[1]; right = left; bottom = values[2]; break; case 4: // 如果有四个值,分别赋值给顶部、右边、底部和左边 top = values[0]; right = values[1]; bottom = values[2]; left = values[3]; break; default: // 解析失败,返回空字符串 return ""; } // 忘记之前保存的状态 this.forgetState(); // 创建顶部边框颜色的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("border-top-color", top)); // 创建右边框颜色的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("border-right-color", right)); // 创建底部边框颜色的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("border-bottom-color", bottom)); // 创建左边框颜色的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("border-left-color", left)); // 返回边框颜色的值 return top + " " + right + " " + bottom + " " + left; }, // 解析提示音简写属性 parseCueShorthand: function(token, declarations, aAcceptPriority) { // 初始化提示音开始为空字符串 var before = ""; // 初始化提示音结束为空字符串 var after = ""; // 初始化值数组 var values = []; while (true) { if (!token.isNotNull()) { // 如果 token 为空,跳出循环 break; } if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) { if (token.isSymbol("}")) { // 如果遇到块结束符号 },将其放回扫描器 this.ungetToken(); } // 跳出循环 break; } else if (!values.length && token.isIdent(this.kINHERIT)) { // 如果值数组为空且 token 是 inherit,将 inherit 添加到值数组中 values.push(token.value); } else if (token.isIdent("none")) { // 如果 token 是 none,将其添加到值数组中 values.push(token.value); } else if (token.isFunction("url(")) { // 如果 token 是 url 函数,获取下一个 token var token = this.getToken(true, true); // 解析 URL 内容 var urlContent = this.parseURL(token); if (urlContent) { // 如果解析成功,将 url 内容添加到值数组中 values.push("url(" + urlContent); } else { // 解析失败,返回空字符串 return ""; } } else { // 解析失败,返回空字符串 return ""; } // 获取下一个 token token = this.getToken(true, true); } // 获取值数组的长度 var count = values.length; switch (count) { case 1: // 如果只有一个值,将其赋值给提示音开始和结束 before = values[0]; after = before; break; case 2: // 如果有两个值,分别赋值给提示音开始和结束 before = values[0]; after = values[1]; break; default: // 解析失败,返回空字符串 return ""; } // 忘记之前保存的状态 this.forgetState(); // 创建提示音开始的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("cue-before", before)); // 创建提示音结束的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("cue-after", after)); // 返回提示音的值 return before + " " + after; }, // 解析暂停时间简写属性 parsePauseShorthand: function(token, declarations, aAcceptPriority) { // 初始化暂停开始时间为空字符串 var before = ""; // 初始化暂停结束时间为空字符串 var after = ""; // 初始化值数组 var values = []; while (true) { if (!token.isNotNull()) { // 如果 token 为空,跳出循环 break; } if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) { if (token.isSymbol("}")) { // 如果遇到块结束符号 },将其放回扫描器 this.ungetToken(); } // 跳出循环 break; } else if (!values.length && token.isIdent(this.kINHERIT)) { // 如果值数组为空且 token 是 inherit,将 inherit 添加到值数组中 values.push(token.value); } else if (token.isDimensionOfUnit("ms") || token.isDimensionOfUnit("s") || token.isPercentage() || token.isNumber("0")) { // 如果 token 是毫秒、秒、百分比或数字 0,将其添加到值数组中 values.push(token.value); } else { // 解析失败,返回空字符串 return ""; } // 获取下一个 token token = this.getToken(true, true); } // 获取值数组的长度 var count = values.length; switch (count) { case 1: // 如果只有一个值,将其赋值给暂停开始和结束时间 before = values[0]; after = before; break; case 2: // 如果有两个值,分别赋值给暂停开始和结束时间 before = values[0]; after = values[1]; break; default: // 解析失败,返回空字符串 return ""; } // 忘记之前保存的状态 this.forgetState(); // 创建暂停开始时间的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("pause-before", before)); // 创建暂停结束时间的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("pause-after", after)); // 返回暂停时间的值 return before + " " + after; }, // 解析边框宽度简写属性 parseBorderWidthShorthand: function(token, aDecl, aAcceptPriority) { // 初始化顶部边框宽度为 null var top = null; // 初始化底部边框宽度为 null var bottom = null; // 初始化左边框宽度为 null var left = null; // 初始化右边框宽度为 null var right = null; // 初始化值数组 var values = []; while (true) { if (!token.isNotNull()) { // 如果 token 为空,跳出循环 break; } if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) { if (token.isSymbol("}")) { // 如果遇到块结束符号 },将其放回扫描器 this.ungetToken(); } // 跳出循环 break; } else if (!values.length && token.isIdent(this.kINHERIT)) { // 如果值数组为空且 token 是 inherit,将 inherit 添加到值数组中 values.push(token.value); } else if (token.isDimension() || token.isNumber("0") || (token.isIdent() && token.value in this.kBORDER_WIDTH_NAMES)) { // 如果 token 是尺寸、数字 0 或边框宽度名称,将其添加到值数组中 values.push(token.value); } else { // 解析失败,返回空字符串 return ""; } // 获取下一个 token token = this.getToken(true, true); } // 获取值数组的长度 var count = values.length; switch (count) { case 1: // 如果只有一个值,将其赋值给顶部、底部、左边和右边的边框宽度 top = values[0]; bottom = top; left = top; right = top; break; case 2: // 如果有两个值,将第一个值赋值给顶部和底部,第二个值赋值给左边和右边 top = values[0]; bottom = top; left = values[1]; right = left; break; case 3: // 如果有三个值,将第一个值赋值给顶部,第二个值赋值给左边和右边,第三个值赋值给底部 top = values[0]; left = values[1]; right = left; bottom = values[2]; break; case 4: // 如果有四个值,分别赋值给顶部、右边、底部和左边 top = values[0]; right = values[1]; bottom = values[2]; left = values[3]; break; default: // 解析失败,返回空字符串 return ""; } // 忘记之前保存的状态 this.forgetState(); // 创建顶部边框宽度的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("border-top-width", top)); // 创建右边框宽度的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("border-right-width", right)); // 创建底部边框宽度的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("border-bottom-width", bottom)); // 创建左边框宽度的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFromValue("border-left-width", left)); // 返回边框宽度的值 return top + " " + right + " " + bottom + " " + left; }, // 解析边框样式简写属性 parseBorderStyleShorthand: function(token, aDecl, aAcceptPriority) { // 初始化顶部边框样式为 null var top = null; // 初始化底部边框样式为 null var bottom = null; // 初始化左边框样式为 null var left = null; // 初始化右边框样式为 null var right = null; // 初始化值数组 var values = []; while (true) { if (!token.isNotNull()) { // 如果 token 为空,跳出循环 break; } if (token.isSymbol(";") || (aAcceptPriority && token.isSymbol("!")) || token.isSymbol("}")) { if (token.isSymbol("}")) { // 如果遇到块结束符号 },将其放回扫描器 this.ungetToken(); } // 跳出循环 break; } else if (!values.length && token.isIdent(this.kINHERIT)) { // 如果值数组为空且 token 是 inherit,将 inherit 添加到值数组中 values.push(token.value); } else if (token.isIdent() && token.value in this.kBORDER_STYLE_NAMES) { // 如果 token 是边框样式名称,将其添加到值数组中 values.push(token.value); } else { // 解析失败,返回空字符串 return ""; } // 获取下一个 token token = this.getToken(true, true); } // 获取值数组的长度 var count = values.length; switch (count) { case 1: // 如果只有一个值,将其赋值给顶部、底部、左边和右边的边框样式 top = values[0]; bottom = top; left = top; right = top; break; case 2: // 如果有两个值,将第一个值赋值给顶部和底部,第二个值赋值给左边和右边 top = values[0]; bottom = top; left = values[1]; right = left; break; case 3: // 如果有三个值,将第一个值赋值给顶部,第二个值赋值给左边和右边,第三个值赋值给底部 top = values[0]; left = values[1]; right = left; bottom = values[2]; break; case 4: // 如果有四个值,分别赋值给顶部、右边、底部和左边 top = values[0]; right = values[1]; bottom = values[2]; left = values[3]; break; default: // 解析失败,返回空字符串 return ""; } // 忘记之前保存的状态 this.forgetState(); // 创建顶部边框样式的声明并添加到声明数组中 aDecl.push(this._createJscsspDeclarationFrom 以下是为你提供的代码注释: ```javascript // 解析媒体规则的函数 // aToken: 当前的 token // aSheet: 当前的样式表 parseMediaRule: function(aToken, aSheet) { // 开启媒体查询模式 this.mScanner.mMediaQueryMode = true; // 计算当前行数 var currentLine = CountLF(this.mScanner.getAlreadyScanned()); // 获取当前 token 的值 var s = aToken.value; // 标记规则是否有效 var valid = false; // 创建一个新的媒体规则对象 var mediaRule = new jscsspMediaRule(); // 设置媒体规则的当前行数 mediaRule.currentLine = currentLine; // 保存当前状态 this.preserveState(); // 获取下一个 token var token = this.getToken(true, true); // 标记是否找到媒体类型 var foundMedia = false; // 循环处理 token while (token.isNotNull()) { if (token.isIdent()) { // 找到媒体类型 foundMedia = true; // 拼接媒体类型到字符串 s 中 s += " " + token.value; // 将媒体类型添加到媒体规则的媒体列表中 mediaRule.media.push(token.value); // 获取下一个 token token = this.getToken(true, true); if (token.isSymbol(",")) { // 如果是逗号,拼接逗号到字符串 s 中 s += ","; } else { if (token.isSymbol("{")) // 如果是左花括号,将 token 放回队列 this.ungetToken(); else { // 错误处理,将 token 类型设为 NULL_TYPE token.type = jscsspToken.NULL_TYPE; break; } } } else if (token.isSymbol("{")) // 如果是左花括号,跳出循环 break; else if (foundMedia) { // 如果已经找到媒体类型,但当前 token 不符合要求,将 token 类型设为 NULL_TYPE token.type = jscsspToken.NULL_TYPE; // 不是有效的媒体列表,跳出循环 break; } // 获取下一个 token token = this.getToken(true, true); } if (token.isSymbol("{") && mediaRule.media.length) { // 如果是左花括号且媒体列表不为空,开始解析样式规则 s += " { "; // 获取下一个 token token = this.getToken(true, false); while (token.isNotNull()) { if (token.isComment() && this.mPreserveComments) { // 如果是注释且需要保留注释,拼接注释到字符串 s 中 s += " " + token.value; // 创建一个新的注释对象 var comment = new jscsspComment(); // 设置注释对象的解析后的 CSS 文本 comment.parsedCssText = token.value; // 将注释对象添加到媒体规则的 CSS 规则列表中 mediaRule.cssRules.push(comment); } else if (token.isSymbol("}")) { // 如果是右花括号,标记规则有效 valid = true; break; } else { // 解析样式规则 var r = this.parseStyleRule(token, mediaRule, true); if (r) // 如果解析成功,拼接解析结果到字符串 s 中 s += r; } // 获取下一个 token token = this.getToken(true, false); } } if (valid) { // 如果规则有效,忘记保存的状态 this.forgetState(); // 设置媒体规则的解析后的 CSS 文本 mediaRule.parsedCssText = s; // 将媒体规则添加到样式表的 CSS 规则列表中 aSheet.cssRules.push(mediaRule); return true; } // 恢复保存的状态 this.restoreState(); return false; }, // 去除字符串首尾空格的函数 // str: 要处理的字符串 trim11: function(str) { // 去除字符串开头的空格 str = str.replace(/^\s+/, ''); // 从字符串末尾开始遍历 for (var i = str.length - 1; i >= 0; i--) { if (/\S/.test( str.charAt(i) )) { // 如果当前字符不是空格,截取字符串 str = str.substring(0, i + 1); break; } } return str; }, // 解析样式规则的函数 // aToken: 当前的 token // aOwner: 规则的所有者 // aIsInsideMediaRule: 是否在媒体规则内部 parseStyleRule: function(aToken, aOwner, aIsInsideMediaRule) { // 计算当前行数 var currentLine = CountLF(this.mScanner.getAlreadyScanned()); // 保存当前状态 this.preserveState(); // 解析选择器 var selector = this.parseSelector(aToken, false); // 标记规则是否有效 var valid = false; // 声明列表 var declarations = []; if (selector) { // 去除选择器首尾空格 selector = this.trim11(selector.selector); // 选择器字符串 var s = selector; // 获取下一个 token var token = this.getToken(true, true); if (token.isSymbol("{")) { // 如果是左花括号,拼接左花括号到字符串 s 中 s += " { "; // 获取下一个 token var token = this.getToken(true, false); while (true) { if (!token.isNotNull()) { // 如果 token 为空,标记规则有效 valid = true; break; } if (token.isSymbol("}")) { // 如果是右花括号,拼接右花括号到字符串 s 中,标记规则有效 s += "}"; valid = true; break; } else { // 解析声明 var d = this.parseDeclaration(token, declarations, true, true, aOwner); s += ((d && declarations.length) ? " " : "") + d; } // 获取下一个 token token = this.getToken(true, false); } } } else { // 选择器无效,整个规则无效 } if (valid) { // 如果规则有效,创建一个新的样式规则对象 var rule = new jscsspStyleRule(); // 设置样式规则的当前行数 rule.currentLine = currentLine; // 设置样式规则的解析后的 CSS 文本 rule.parsedCssText = s; // 设置样式规则的声明列表 rule.declarations = declarations; // 设置样式规则的选择器文本 rule.mSelectorText = selector; if (aIsInsideMediaRule) // 如果在媒体规则内部,设置样式规则的父规则 rule.parentRule = aOwner; else // 否则,设置样式规则的父样式表 rule.parentStyleSheet = aOwner; // 将样式规则添加到所有者的 CSS 规则列表中 aOwner.cssRules.push(rule); return s; } // 恢复保存的状态 this.restoreState(); // 获取当前 token 的值 s = this.currentToken().value; // 添加未知的 @ 规则 this.addUnknownAtRule(aOwner, s); return ""; }, // 解析选择器的函数 // aToken: 当前的 token // aParseSelectorOnly: 是否只解析选择器 parseSelector: function(aToken, aParseSelectorOnly) { // 选择器字符串 var s = ""; // 选择器的优先级 var specificity = {a: 0, b: 0, c: 0, d: 0}; // 标记是否是选择器链的第一个元素 var isFirstInChain = true; // 当前 token var token = aToken; // 标记选择器是否有效 var valid = false; // 标记是否找到组合器 var combinatorFound = false; while (true) { if (!token.isNotNull()) { if (aParseSelectorOnly) // 如果只解析选择器,返回选择器和优先级 return {selector: s, specificity: specificity }; return ""; } if (!aParseSelectorOnly && token.isSymbol("{")) { // 如果不是只解析选择器且遇到左花括号,结束选择器解析 valid = !combinatorFound; if (valid) this.ungetToken(); break; } if (token.isSymbol(",")) { // 如果是逗号,拼接逗号到字符串 s 中 s += token.value; // 标记是选择器链的第一个元素 isFirstInChain = true; // 标记未找到组合器 combinatorFound = false; // 获取下一个 token token = this.getToken(false, true); continue; } // 处理组合器和分组 else if (!combinatorFound && (token.isWhiteSpace() || token.isSymbol(">") || token.isSymbol("+") || token.isSymbol("~"))) { if (token.isWhiteSpace()) { // 如果是空格,拼接空格到字符串 s 中 s += " "; // 查看下一个 token var nextToken = this.lookAhead(true, true); if (!nextToken.isNotNull()) { if (aParseSelectorOnly) // 如果只解析选择器,返回选择器和优先级 return {selector: s, specificity: specificity }; return ""; } if (nextToken.isSymbol(">") || nextToken.isSymbol("+") || nextToken.isSymbol("~")) { // 如果下一个 token 是组合器,获取下一个 token 并拼接 token = this.getToken(true, true); s += token.value + " "; // 标记找到组合器 combinatorFound = true; } } else { // 如果不是空格,拼接组合器到字符串 s 中 s += token.value; // 标记找到组合器 combinatorFound = true; } // 标记是选择器链的第一个元素 isFirstInChain = true; // 获取下一个 token token = this.getToken(true, true); continue; } else { // 解析简单选择器 var simpleSelector = this.parseSimpleSelector(token, isFirstInChain, true); if (!simpleSelector) // 如果解析失败,跳出循环 break; // 拼接简单选择器到字符串 s 中 s += simpleSelector.selector; // 更新选择器的优先级 specificity.b += simpleSelector.specificity.b; specificity.c += simpleSelector.specificity.c; specificity.d += simpleSelector.specificity.d; // 标记不是选择器链的第一个元素 isFirstInChain = false; // 标记未找到组合器 combinatorFound = false; } // 获取下一个 token token = this.getToken(false, true); } if (valid) { // 如果选择器有效,返回选择器和优先级 return {selector: s, specificity: specificity }; } return ""; }, // 判断是否为伪元素的函数 // aIdent: 要判断的标识符 isPseudoElement: function(aIdent) { switch (aIdent) { case "first-letter": case "first-line": case "before": case "after": case "marker": return true; break; default: return false; break; } }, // 解析简单选择器的函数 // token: 当前的 token // isFirstInChain: 是否是选择器链的第一个元素 // canNegate: 是否可以使用否定伪类 parseSimpleSelector: function(token, isFirstInChain, canNegate) { // 简单选择器字符串 var s = ""; // 简单选择器的优先级 var specificity = {a: 0, b: 0, c: 0, d: 0}; if (isFirstInChain && (token.isSymbol("*") || token.isSymbol("|") || token.isIdent())) { // 如果是选择器链的第一个元素,处理类型或通用选择器 if (token.isSymbol("*") || token.isIdent()) { // 可能是前缀或通用选择器 s += token.value; // 标记是否为标识符 var isIdent = token.isIdent(); // 获取下一个 token token = this.getToken(false, true); if (token.isSymbol("|")) { // 如果是竖线,拼接竖线到字符串 s 中 s += token.value; // 获取下一个 token token = this.getToken(false, true); if (token.isIdent() || token.isSymbol("*")) { // 如果是标识符或星号,拼接标识符或星号到字符串 s 中 s += token.value; if (token.isIdent()) // 如果是标识符,更新优先级 specificity.d++; } else // 错误处理,返回 null return null; } else { // 将 token 放回队列 this.ungetToken(); if (isIdent) // 如果是标识符,更新优先级 specificity.d++; } } else if (token.isSymbol("|")) { // 如果是竖线,拼接竖线到字符串 s 中 s += token.value; // 获取下一个 token token = this.getToken(false, true); if (token.isIdent() || token.isSymbol("*")) { // 如果是标识符或星号,拼接标识符或星号到字符串 s 中 s += token.value; if (token.isIdent()) // 如果是标识符,更新优先级 specificity.d++; } else // 错误处理,返回 null return null; } } else if (token.isSymbol(".") || token.isSymbol("#")) { // 如果是点号或井号,处理类选择器或 ID 选择器 var isClass = token.isSymbol("."); // 拼接点号或井号到字符串 s 中 s += token.value; // 获取下一个 token token = this.getToken(false, true); if (token.isIdent()) { // 如果是标识符,拼接标识符到字符串 s 中 s += token.value; if (isClass) // 如果是类选择器,更新优先级 specificity.c++; else // 如果是 ID 选择器,更新优先级 specificity.b++; } else // 错误处理,返回 null return null; } else if (token.isSymbol(":")) { // 如果是冒号,处理伪类或伪元素 s += token.value; // 获取下一个 token token = this.getToken(false, true); if (token.isSymbol(":")) { // 如果是双冒号,拼接双冒号到字符串 s 中 s += token.value; // 获取下一个 token token = this.getToken(false, true); } if (token.isIdent()) { // 如果是标识符,拼接标识符到字符串 s 中 s += token.value; if (this.isPseudoElement(token.value)) // 如果是伪元素,更新优先级 specificity.d++; else // 如果是伪类,更新优先级 specificity.c++; } else if (token.isFunction()) { // 如果是函数,拼接函数到字符串 s 中 s += token.value; if (token.isFunction(":not(")) { // 如果是 :not() 函数 if (!canNegate) // 如果不允许使用否定伪类,返回 null return null; // 获取下一个 token token = this.getToken(true, true); // 解析简单选择器 var simpleSelector = this.parseSimpleSelector(token, isFirstInChain, false); if (!simpleSelector) // 如果解析失败,返回 null return null; else { // 拼接简单选择器到字符串 s 中 s += simpleSelector.selector; // 获取下一个 token token = this.getToken(true, true); if (token.isSymbol(")")) // 如果是右括号,拼接右括号到字符串 s 中 s += ")"; else // 错误处理,返回 null return null; } // 更新优先级 specificity.c++; } else { while (true) { // 处理函数参数 token = this.getToken(false, true); if (token.isSymbol(")")) { // 如果是右括号,拼接右括号到字符串 s 中 s += ")"; break; } else // 拼接参数到字符串 s 中 s += token.value; } // 更新优先级 specificity.c++; } } else // 错误处理,返回 null return null; } else if (token.isSymbol("[")) { // 如果是左方括号,处理属性选择器 s += "["; // 获取下一个 token token = this.getToken(true, true); if (token.isIdent() || token.isSymbol("*")) { // 如果是标识符或星号,拼接标识符或星号到字符串 s 中 s += token.value; // 获取下一个 token var nextToken = this.getToken(true, true); if (token.isSymbol("|")) { // 如果是竖线,拼接竖线到字符串 s 中 s += "|"; // 获取下一个 token token = this.getToken(true, true); if (token.isIdent()) // 如果是标识符,拼接标识符到字符串 s 中 s += token.value; else // 错误处理,返回 null return null; } else // 将 token 放回队列 this.ungetToken(); } else if (token.isSymbol("|")) { // 如果是竖线,拼接竖线到字符串 s 中 s += "|"; // 获取下一个 token token = this.getToken(true, true); if (token.isIdent()) // 如果是标识符,拼接标识符到字符串 s 中 s += token.value; else // 错误处理,返回 null return null; } else // 错误处理,返回 null return null; // 处理属性选择器的操作符 token = this.getToken(true, true); if (token.isIncludes() || token.isDashmatch() || token.isBeginsmatch() || token.isEndsmatch() || token.isContainsmatch() || token.isSymbol("=")) { // 如果是操作符,拼接操作符到字符串 s 中 s += token.value; // 获取下一个 token token = this.getToken(true, true); if (token.isString() || token.isIdent()) { // 如果是字符串或标识符,拼接字符串或标识符到字符串 s 中 s += token.value; // 获取下一个 token token = this.getToken(true, true); } else // 错误处理,返回 null return null; if (token.isSymbol("]")) { // 如果是右方括号,拼接右方括号到字符串 s 中 s += token.value; // 更新优先级 specificity.c++; } else // 错误处理,返回 null return null; } else if (token.isSymbol("]")) { // 如果是右方括号,拼接右方括号到字符串 s 中 s += token.value; // 更新优先级 specificity.c++; } else // 错误处理,返回 null return null; } else if (token.isWhiteSpace()) { // 如果是空格,查看下一个 token var t = this.lookAhead(true, true); if (t.isSymbol('{')) // 如果下一个 token 是左花括号,返回空字符串 return "" } if (s) // 如果简单选择器字符串不为空,返回简单选择器和优先级 return {selector: s, specificity: specificity }; return null; }, // 保存当前状态的函数 preserveState: function() { // 将当前 token 保存到保存的 token 列表中 this.mPreservedTokens.push(this.currentToken()); // 保存扫描器的状态 this.mScanner.preserveState(); }, // 恢复保存状态的函数 restoreState: function() { if (this.mPreservedTokens.length) { // 如果保存的 token 列表不为空,恢复扫描器的状态 this.mScanner.restoreState(); // 从保存的 token 列表中取出最后一个 token this.mToken = this.mPreservedTokens.pop(); } }, // 忘记保存状态的函数 forgetState: function() { if (this.mPreservedTokens.length) { // 如果保存的 token 列表不为空,忘记扫描器的状态 this.mScanner.forgetState(); // 从保存的 token 列表中取出最后一个 token this.mPreservedTokens.pop(); } }, // 解析 CSS 字符串的函数 // aString: 要解析的 CSS 字符串 // aTryToPreserveWhitespaces: 是否尝试保留空格 // aTryToPreserveComments: 是否尝试保留注释 parse: function(aString, aTryToPreserveWhitespaces, aTryToPreserveComments) { if (!aString) // 如果字符串为空,返回 null return null; // 设置是否保留空格 this.mPreserveWS = aTryToPreserveWhitespaces; // 设置是否保留注释 this.mPreserveComments = aTryToPreserveComments; // 初始化保存的 token 列表 this.mPreservedTokens = []; // 初始化扫描器 this.mScanner.init(aString); // 创建一个新的样式表对象 var sheet = new jscsspStylesheet(); // @charset 规则只能出现在样式表的开头 var token = this.getToken(false, false); if (!token.isNotNull()) return; if (token.isAtRule("@charset")) { // 解析 @charset 规则 this.parseCharsetRule(token, sheet); // 获取下一个 token token = this.getToken(false, false); } // 标记是否找到样式规则 var foundStyleRules = false; // 标记是否找到 @import 规则 var foundImportRules = false; // 标记是否找到 @namespace 规则 var foundNameSpaceRules = false; while (true) { if (!token.isNotNull()) // 如果 token 为空,跳出循环 break; if (token.isWhiteSpace()) { if (aTryToPreserveWhitespaces) // 如果需要保留空格,添加空格到样式表中 this.addWhitespace(sheet, token.value); } else if (token.isComment()) { if (this.mPreserveComments) // 如果需要保留注释,添加注释到样式表中 this.addComment(sheet, token.value); } else if (token.isAtRule()) { if (token.isAtRule("@variables")) { // 如果是 @variables 规则 if (!foundImportRules && !foundStyleRules) // 如果还没有找到 @import 规则和样式规则,解析 @variables 规则 this.parseVariablesRule(token, sheet); else { // 错误处理,报告错误并添加未知的 @ 规则 this.reportError(kVARIABLES_RULE_POSITION); this.addUnknownAtRule(sheet, token.value); } } else if (token.isAtRule("@import")) { // 如果是 @import 规则 // @import 规则必须出现在所有样式规则和 @namespace 规则之前 if (!foundStyleRules && !foundNameSpaceRules) // 如果还没有找到样式规则和 @namespace 规则,解析 @import 规则 foundImportRules = this.parseImportRule(token, sheet); else { // 错误处理,报告错误并添加未知的 @ 规则 this.reportError(kIMPORT_RULE_POSITION); this.addUnknownAtRule(sheet, token.value); } } else if (token.isAtRule("@namespace")) { // 如果是 @namespace 规则 // @namespace 规则必须出现在所有样式规则之后,所有 @import 规则之前 if (!foundStyleRules) // 如果还没有找到样式规则,解析 @namespace 规则 foundNameSpaceRules = this.parseNamespaceRule(token, sheet); else { // 错误处理,报告错误并添加未知的 @ 规则 this.reportError(kNAMESPACE_RULE_POSITION); this.addUnknownAtRule(sheet, token.value); } } else if (token.isAtRule("@font-face")) { // 如果是 @font-face 规则 if (this.parseFontFaceRule(token, sheet)) // 如果解析成功,标记找到样式规则 foundStyleRules = true; else // 解析失败,添加未知的 @ 规则 this.addUnknownAtRule(sheet, token.value); } else if (token.isAtRule("@page")) { // 如果是 @page 规则 if (this.parsePageRule(token, sheet)) // 如果解析成功,标记找到样式规则 foundStyleRules = true; else // 解析失败,添加未知的 @ 规则 this.addUnknownAtRule(sheet, token.value); } else if (token.isAtRule("@media")) { // 如果是 @media 规则 if (this.parseMediaRule(token, sheet)) // 如果解析成功,标记找到样式规则 foundStyleRules = true; else // 解析失败,添加未知的 @ 规则 this.addUnknownAtRule(sheet, token.value); } else if (token.isAtRule("@keyframes")) { // 如果是 @keyframes 规则 if (!this.parseKeyframesRule(token, sheet)) // 如果解析失败,添加未知的 @ 规则 this.addUnknownAtRule(sheet, token.value); } else if (token.isAtRule("@charset")) { // 如果是 @charset 规则出现在非开头位置,报告错误并添加未知的 @ 规则 this.reportError(kCHARSET_RULE_CHARSET_SOF); this.addUnknownAtRule(sheet, token.value); } else { // 未知的 @ 规则,报告错误并添加未知的 @ 规则 this.reportError(kUNKNOWN_AT_RULE); this.addUnknownAtRule(sheet, token.value); } } else // 普通样式规则 { // 解析样式规则 var ruleText = this.parseStyleRule(token, sheet, false); if (ruleText) // 如果解析成功,标记找到样式规则 foundStyleRules = true; } // 获取下一个 token token = this.getToken(false); } return sheet; } }; // jscsspToken 构造函数 // aType: token 的类型 // aValue: token 的值 // aUnit: token 的单位 function jscsspToken(aType, aValue, aUnit) { // 设置 token 的类型 this.type = aType; // 设置 token 的值 this.value = aValue; // 设置 token 的单位 this.unit = aUnit; } // 定义 token 的类型 jscsspToken.NULL_TYPE = 0; jscsspToken.WHITESPACE_TYPE = 1; jscsspToken.STRING_TYPE = 2; jscsspToken.COMMENT_TYPE = 3; jscsspToken.NUMBER_TYPE = 4; jscsspToken.IDENT_TYPE = 5; jscsspToken.FUNCTION_TYPE = 6; jscsspToken.ATRULE_TYPE = 7; jscsspToken.INCLUDES_TYPE = 8; jscsspToken.DASHMATCH_TYPE = 9; jscsspToken.BEGINSMATCH_TYPE = 10; jscsspToken.ENDSMATCH_TYPE = 11; jscsspToken.CONTAINSMATCH_TYPE = 12; jscsspToken.SYMBOL_TYPE = 13; jscsspToken.DIMENSION_TYPE = 14; jscsspToken.PERCENTAGE_TYPE = 15; jscsspToken.HEX_TYPE = 16; // jscsspToken 原型对象 jscsspToken.prototype = { // 判断 token 是否不为空的函数 isNotNull: function () { return this.type; }, // 判断 token 是否为指定类型和值的函数 _isOfType: function (aType, aValue) { return (this.type == aType && (!aValue || this.value.toLowerCase() == aValue)); }, // 判断 token 是否为空格的函数 isWhiteSpace: function(w) { return this._isOfType(jscsspToken.WHITESPACE_TYPE, w); }, // 判断 token 是否为字符串的函数 isString: function() { return this._isOfType(jscsspToken.STRING_TYPE); }, // 判断 token 是否为注释的函数 isComment: function() { return this._isOfType(jscsspToken.COMMENT_TYPE); }, // 判断 token 是否为数字的函数 isNumber: function(n) { return this._isOfType(jscsspToken.NUMBER_TYPE, n); }, // 判断 token 是否为符号的函数 isSymbol: function(c) { return this._isOfType(jscsspToken.SYMBOL_TYPE, c); }, // 判断 token 是否为标识符的函数 isIdent: function(i) { return this._isOfType(jscsspToken.IDENT_TYPE, i); }, // 判断 token 是否为函数的函数 isFunction: function(f) { return this._isOfType(jscsspToken.FUNCTION_TYPE, f); }, // 判断 token 是否为 @ 规则的函数 isAtRule: function(a) { return this._isOfType(jscsspToken.ATRULE_TYPE, a); }, // 判断 token 是否为包含操作符的函数 isIncludes: function() { return this._isOfType(jscsspToken.INCLUDES_TYPE); }, // 判断 token 是否为连字符匹配操作符的函数 isDashmatch: function() { return this._isOfType(jscsspToken.DASHMATCH_TYPE); }, // 判断 token 是否为开头匹配操作符的函数 isBeginsmatch: function() { return this._isOfType(jscsspToken.BEGINSMATCH_TYPE); }, // 判断 token 是否为结尾匹配操作符的函数 isEndsmatch: function() { return this._isOfType(jscsspToken.ENDSMATCH_TYPE); }, // 判断 token 是否为包含匹配操作符的函数 isContainsmatch: function() { return this._isOfType(jscsspToken.CONTAINSMATCH_TYPE); }, // 判断 token 是否为符号的函数 isSymbol: function(c) { return this._isOfType(jscsspToken.SYMBOL_TYPE, c); }, // 判断 token 是否为维度的函数 isDimension: function() { return this._isOfType(jscsspToken.DIMENSION_TYPE); }, // 判断 token 是否为百分比的函数 isPercentage: function() { return this._isOfType(jscsspToken.PERCENTAGE_TYPE); }, // 判断 token 是否为十六进制的函数 isHex: function() { return this._isOfType(jscsspToken.HEX_TYPE); }, // 判断 token 是否为指定单位的维度的函数 isDimensionOfUnit: function(aUnit) { return (this.isDimension() && this.unit == aUnit); }, // 判断 token 是否为长度的函数 isLength: function() { return (this.isPercentage() || this.isDimensionOfUnit("cm") || this.isDimensionOfUnit("mm") || this.isDimensionOfUnit("in") || this.isDimensionOfUnit("pc") || this.isDimensionOfUnit("px") || this.isDimensionOfUnit("em") || this.isDimensionOfUnit("ex") || this.isDimensionOfUnit("pt")); }, // 判断 token 是否为角度的函数 isAngle: function() { return (this.isDimensionOfUnit("deg") || this.isDimensionOfUnit("rad") || this.isDimensionOfUnit("grad")); } } // 定义规则类型常量 var kJscsspUNKNOWN_RULE = 0; var kJscsspSTYLE_RULE = 1 var kJscsspCHARSET_RULE = 2; var kJscsspIMPORT_RULE = 3; var kJscsspMEDIA_RULE = 4; var kJscsspFONT_FACE_RULE = 5; var kJscsspPAGE_RULE = 6; var kJscsspKEYFRAMES_RULE = 7; var kJscsspKEYFRAME_RULE = 8; var kJscsspNAMESPACE_RULE = 100; var kJscsspCOMMENT = 101; var kJscsspWHITE_SPACE