// Helper for tokenizing the contents of a CSS selector block module.exports = (function() { var createTokenPrototype = function (processable) { var important = '!important'; // Constructor for tokens function Token (prop, p2, p3) { this.prop = prop; if (typeof(p2) === 'string') { this.value = p2; this.isImportant = p3; } else { this.value = processable[prop].defaultValue; this.isImportant = p2; } } Token.prototype.prop = null; Token.prototype.value = null; Token.prototype.granularValues = null; Token.prototype.components = null; Token.prototype.position = null; Token.prototype.isImportant = false; Token.prototype.isDirty = false; Token.prototype.isShorthand = false; Token.prototype.isIrrelevant = false; Token.prototype.isReal = true; Token.prototype.isMarkedForDeletion = false; // Tells if this token is a component of the other one Token.prototype.isComponentOf = function (other) { if (!processable[this.prop] || !processable[other.prop]) return false; if (!(processable[other.prop].components instanceof Array) || !processable[other.prop].components.length) return false; return processable[other.prop].components.indexOf(this.prop) >= 0; }; // Clones a token Token.prototype.clone = function (isImportant) { var token = new Token(this.prop, this.value, (typeof(isImportant) !== 'undefined' ? isImportant : this.isImportant)); return token; }; // Creates an irrelevant token with the same prop Token.prototype.cloneIrrelevant = function (isImportant) { var token = Token.makeDefault(this.prop, (typeof(isImportant) !== 'undefined' ? isImportant : this.isImportant)); token.isIrrelevant = true; return token; }; // Creates an array of property tokens with their default values Token.makeDefaults = function (props, important) { return props.map(function(prop) { return new Token(prop, important); }); }; // Parses one CSS property declaration into a token Token.tokenizeOne = function (fullProp) { // Find first colon var colonPos = fullProp.indexOf(':'); if (colonPos < 0) { // This property doesn't have a colon, it's invalid. Let's keep it intact anyway. return new Token('', fullProp); } // Parse parts of the property var prop = fullProp.substr(0, colonPos).trim(); var value = fullProp.substr(colonPos + 1).trim(); var isImportant = false; var importantPos = value.indexOf(important); // Check if the property is important if (importantPos >= 1 && importantPos === value.length - important.length) { value = value.substr(0, importantPos).trim(); isImportant = true; } // Return result var result = new Token(prop, value, isImportant); // If this is a shorthand, break up its values // NOTE: we need to do this for all shorthands because otherwise we couldn't remove default values from them if (processable[prop] && processable[prop].isShorthand) { result.isShorthand = true; result.components = processable[prop].breakUp(result); result.isDirty = true; } return result; }; // Breaks up a string of CSS property declarations into tokens so that they can be handled more easily Token.tokenize = function (input) { // Split the input by semicolons and parse the parts var tokens = input.split(';').map(Token.tokenizeOne); return tokens; }; // Transforms tokens back into CSS properties Token.detokenize = function (tokens) { // If by mistake the input is not an array, make it an array if (!(tokens instanceof Array)) { tokens = [tokens]; } var result = ''; // This step takes care of putting together the components of shorthands // NOTE: this is necessary to do for every shorthand, otherwise we couldn't remove their default values for (var i = 0; i < tokens.length; i++) { var t = tokens[i]; if (t.isShorthand && t.isDirty) { var news = processable[t.prop].putTogether(t.prop, t.components, t.isImportant); Array.prototype.splice.apply(tokens, [i, 1].concat(news)); t.isDirty = false; i--; continue; } if (t.prop) result += t.prop + ':'; if (t.value) result += t.value; if (t.isImportant) result += important; result += ';'; } return result.substr(0, result.length - 1); }; // Gets the final (detokenized) length of the given tokens Token.getDetokenizedLength = function (tokens) { // If by mistake the input is not an array, make it an array if (!(tokens instanceof Array)) { tokens = [tokens]; } var result = 0; // This step takes care of putting together the components of shorthands // NOTE: this is necessary to do for every shorthand, otherwise we couldn't remove their default values for (var i = 0; i < tokens.length; i++) { var t = tokens[i]; if (t.isShorthand && t.isDirty) { var news = processable[t.prop].putTogether(t.prop, t.components, t.isImportant); Array.prototype.splice.apply(tokens, [i, 1].concat(news)); t.isDirty = false; i--; continue; } if (t.prop) { result += t.prop.length + 1; } if (t.value) { result += t.value.length; } if (t.isImportant) { result += important.length; } } return result; }; return Token; }; return { createTokenPrototype: createTokenPrototype }; })();