|
|
- "use strict";
-
- var Parser = require("fastparse");
-
- function commentMatch(match, content) {
- this.value.nodes.push({
- type: "comment",
- content: content
- });
- }
-
- function spacingMatch(match) {
- var item = this.value.nodes[this.value.nodes.length - 1];
- item.after = (item.after || "") + match;
- }
-
- function initialSpacingMatch(match) {
- this.value.before = match;
- }
-
- function endSpacingMatch(match) {
- this.value.after = match;
- }
-
- function unescapeString(content) {
- return content.replace(/\\(?:([a-fA-F0-9]{1,6})|(.))/g, function(all, unicode, otherCharacter) {
- if (otherCharacter) {
- return otherCharacter;
- }
-
- var C = parseInt(unicode, 16);
- if(C < 0x10000) {
- return String.fromCharCode(C);
- } else {
- return String.fromCharCode(Math.floor((C - 0x10000) / 0x400) + 0xD800) +
- String.fromCharCode((C - 0x10000) % 0x400 + 0xDC00);
- }
- });
- }
-
- function stringMatch(match, content) {
- var value = unescapeString(content);
- this.value.nodes.push({
- type: "string",
- value: value,
- stringType: match[0]
- });
- }
-
- function commaMatch(match, spacing) {
- var newValue = {
- type: "value",
- nodes: []
- };
- if(spacing) {
- newValue.before = spacing;
- }
- this.root.nodes.push(newValue);
- this.value = newValue;
- }
-
- function itemMatch(match) {
- this.value.nodes.push({
- type: "item",
- name: match
- });
- }
-
- function nestedItemMatch(match, name, spacing) {
- this.stack.push(this.root);
- this.root = {
- type: "nested-item",
- name: name,
- nodes: [
- { type: "value", nodes: [] }
- ]
- };
- if(spacing) {
- this.root.nodes[0].before = spacing;
- }
- this.value.nodes.push(this.root);
- this.value = this.root.nodes[0];
- }
-
- function nestedItemEndMatch(match, spacing, remaining) {
- if(this.stack.length === 0) {
- if(spacing) {
- var item = this.value.nodes[this.value.nodes.length - 1];
- item.after = (item.after || "") + spacing;
- }
- this.value.nodes.push({
- type: "invalid",
- value: remaining
- });
- } else {
- if(spacing) {
- this.value.after = spacing;
- }
- this.root = this.stack.pop();
- this.value = this.root.nodes[this.root.nodes.length - 1];
- }
- }
-
- function urlMatch(match, innerSpacingBefore, content, innerSpacingAfter) {
- var item = {
- type: "url"
- };
- if(innerSpacingBefore) {
- item.innerSpacingBefore = innerSpacingBefore;
- }
- if(innerSpacingAfter) {
- item.innerSpacingAfter = innerSpacingAfter;
- }
- switch(content[0]) {
- case "\"":
- item.stringType = "\"";
- item.url = unescapeString(content.substr(1, content.length - 2));
- break;
- case "'":
- item.stringType = "'";
- item.url = unescapeString(content.substr(1, content.length - 2));
- break;
- default:
- item.url = unescapeString(content);
- break;
- }
- this.value.nodes.push(item);
- }
-
- var parser = new Parser({
- decl: {
- "^\\s+": initialSpacingMatch,
- "/\\*([\\s\\S]*?)\\*/": commentMatch,
- "\"((?:[^\\\\\"]|\\\\.)*)\"": stringMatch,
- "'((?:[^\\\\']|\\\\.)*)'": stringMatch,
- "url\\((\\s*)(\"(?:[^\\\\\"]|\\\\.)*\")(\\s*)\\)": urlMatch,
- "url\\((\\s*)('(?:[^\\\\']|\\\\.)*')(\\s*)\\)": urlMatch,
- "url\\((\\s*)((?:[^\\\\)'\"]|\\\\.)*)(\\s*)\\)": urlMatch,
- "([\\w\-]+)\\((\\s*)": nestedItemMatch,
- "(\\s*)(\\))": nestedItemEndMatch,
- ",(\\s*)": commaMatch,
- "\\s+$": endSpacingMatch,
- "\\s+": spacingMatch,
- "[^\\s,\)]+": itemMatch
- }
- });
-
- function parseValues(str) {
- var valueNode = {
- type: "value",
- nodes: []
- };
- var rootNode = {
- type: "values",
- nodes: [
- valueNode
- ]
- };
- parser.parse("decl", str, {
- stack: [],
- root: rootNode,
- value: valueNode
- });
- return rootNode;
- }
-
- module.exports = parseValues;
|