123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184 |
-
- // 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
- };
-
- })();
|