.gitignore added
[dotfiles/.git] / .config / coc / extensions / node_modules / coc-prettier / node_modules / specificity / specificity.js
diff --git a/.config/coc/extensions/node_modules/coc-prettier/node_modules/specificity/specificity.js b/.config/coc/extensions/node_modules/coc-prettier/node_modules/specificity/specificity.js
new file mode 100644 (file)
index 0000000..7bcf56e
--- /dev/null
@@ -0,0 +1,232 @@
+var SPECIFICITY = (function() {
+       var calculate,
+               calculateSingle,
+               compare;
+
+       // Calculate the specificity for a selector by dividing it into simple selectors and counting them
+       calculate = function(input) {
+               var selectors,
+                       selector,
+                       i,
+                       len,
+                       results = [];
+
+               // Separate input by commas
+               selectors = input.split(',');
+
+               for (i = 0, len = selectors.length; i < len; i += 1) {
+                       selector = selectors[i];
+                       if (selector.length > 0) {
+                               results.push(calculateSingle(selector));
+                       }
+               }
+
+               return results;
+       };
+
+       /**
+        * Calculates the specificity of CSS selectors
+        * http://www.w3.org/TR/css3-selectors/#specificity
+        *
+        * Returns an object with the following properties:
+        *  - selector: the input
+        *  - specificity: e.g. 0,1,0,0
+        *  - parts: array with details about each part of the selector that counts towards the specificity
+        *  - specificityArray: e.g. [0, 1, 0, 0]
+        */
+       calculateSingle = function(input) {
+               var selector = input,
+                       findMatch,
+                       typeCount = {
+                               'a': 0,
+                               'b': 0,
+                               'c': 0
+                       },
+                       parts = [],
+                       // The following regular expressions assume that selectors matching the preceding regular expressions have been removed
+                       attributeRegex = /(\[[^\]]+\])/g,
+                       idRegex = /(#[^\#\s\+>~\.\[:]+)/g,
+                       classRegex = /(\.[^\s\+>~\.\[:]+)/g,
+                       pseudoElementRegex = /(::[^\s\+>~\.\[:]+|:first-line|:first-letter|:before|:after)/gi,
+                       // A regex for pseudo classes with brackets - :nth-child(), :nth-last-child(), :nth-of-type(), :nth-last-type(), :lang()
+                       pseudoClassWithBracketsRegex = /(:[\w-]+\([^\)]*\))/gi,
+                       // A regex for other pseudo classes, which don't have brackets
+                       pseudoClassRegex = /(:[^\s\+>~\.\[:]+)/g,
+                       elementRegex = /([^\s\+>~\.\[:]+)/g;
+
+               // Find matches for a regular expression in a string and push their details to parts
+               // Type is "a" for IDs, "b" for classes, attributes and pseudo-classes and "c" for elements and pseudo-elements
+               findMatch = function(regex, type) {
+                       var matches, i, len, match, index, length;
+                       if (regex.test(selector)) {
+                               matches = selector.match(regex);
+                               for (i = 0, len = matches.length; i < len; i += 1) {
+                                       typeCount[type] += 1;
+                                       match = matches[i];
+                                       index = selector.indexOf(match);
+                                       length = match.length;
+                                       parts.push({
+                                               selector: input.substr(index, length),
+                                               type: type,
+                                               index: index,
+                                               length: length
+                                       });
+                                       // Replace this simple selector with whitespace so it won't be counted in further simple selectors
+                                       selector = selector.replace(match, Array(length + 1).join(' '));
+                               }
+                       }
+               };
+
+               // Replace escaped characters with plain text, using the "A" character
+               // https://www.w3.org/TR/CSS21/syndata.html#characters
+               (function() {
+                       var replaceWithPlainText = function(regex) {
+                                       var matches, i, len, match;
+                                       if (regex.test(selector)) {
+                                               matches = selector.match(regex);
+                                               for (i = 0, len = matches.length; i < len; i += 1) {
+                                                       match = matches[i];
+                                                       selector = selector.replace(match, Array(match.length + 1).join('A'));
+                                               }
+                                       }
+                               },
+                               // Matches a backslash followed by six hexadecimal digits followed by an optional single whitespace character
+                               escapeHexadecimalRegex = /\\[0-9A-Fa-f]{6}\s?/g,
+                               // Matches a backslash followed by fewer than six hexadecimal digits followed by a mandatory single whitespace character
+                               escapeHexadecimalRegex2 = /\\[0-9A-Fa-f]{1,5}\s/g,
+                               // Matches a backslash followed by any character
+                               escapeSpecialCharacter = /\\./g;
+
+                       replaceWithPlainText(escapeHexadecimalRegex);
+                       replaceWithPlainText(escapeHexadecimalRegex2);
+                       replaceWithPlainText(escapeSpecialCharacter);
+               }());
+
+               // Remove the negation psuedo-class (:not) but leave its argument because specificity is calculated on its argument
+               (function() {
+                       var regex = /:not\(([^\)]*)\)/g;
+                       if (regex.test(selector)) {
+                               selector = selector.replace(regex, '     $1 ');
+                       }
+               }());
+
+               // Remove anything after a left brace in case a user has pasted in a rule, not just a selector
+               (function() {
+                       var regex = /{[^]*/gm,
+                               matches, i, len, match;
+                       if (regex.test(selector)) {
+                               matches = selector.match(regex);
+                               for (i = 0, len = matches.length; i < len; i += 1) {
+                                       match = matches[i];
+                                       selector = selector.replace(match, Array(match.length + 1).join(' '));
+                               }
+                       }
+               }());
+
+               // Add attribute selectors to parts collection (type b)
+               findMatch(attributeRegex, 'b');
+
+               // Add ID selectors to parts collection (type a)
+               findMatch(idRegex, 'a');
+
+               // Add class selectors to parts collection (type b)
+               findMatch(classRegex, 'b');
+
+               // Add pseudo-element selectors to parts collection (type c)
+               findMatch(pseudoElementRegex, 'c');
+
+               // Add pseudo-class selectors to parts collection (type b)
+               findMatch(pseudoClassWithBracketsRegex, 'b');
+               findMatch(pseudoClassRegex, 'b');
+
+               // Remove universal selector and separator characters
+               selector = selector.replace(/[\*\s\+>~]/g, ' ');
+
+               // Remove any stray dots or hashes which aren't attached to words
+               // These may be present if the user is live-editing this selector
+               selector = selector.replace(/[#\.]/g, ' ');
+
+               // The only things left should be element selectors (type c)
+               findMatch(elementRegex, 'c');
+
+               // Order the parts in the order they appear in the original selector
+               // This is neater for external apps to deal with
+               parts.sort(function(a, b) {
+                       return a.index - b.index;
+               });
+
+               return {
+                       selector: input,
+                       specificity: '0,' + typeCount.a.toString() + ',' + typeCount.b.toString() + ',' + typeCount.c.toString(),
+                       specificityArray: [0, typeCount.a, typeCount.b, typeCount.c],
+                       parts: parts
+               };
+       };
+
+       /**
+        * Compares two CSS selectors for specificity
+        * Alternatively you can replace one of the CSS selectors with a specificity array
+        *
+        *  - it returns -1 if a has a lower specificity than b
+        *  - it returns 1 if a has a higher specificity than b
+        *  - it returns 0 if a has the same specificity than b
+        */
+       compare = function(a, b) {
+               var aSpecificity,
+                       bSpecificity,
+                       i;
+
+               if (typeof a ==='string') {
+                       if (a.indexOf(',') !== -1) {
+                               throw 'Invalid CSS selector';
+                       } else {
+                               aSpecificity = calculateSingle(a)['specificityArray'];
+                       }
+               } else if (Array.isArray(a)) {
+                       if (a.filter(function(e) { return (typeof e === 'number'); }).length !== 4) {
+                               throw 'Invalid specificity array';
+                       } else {
+                               aSpecificity = a;
+                       }
+               } else {
+                       throw 'Invalid CSS selector or specificity array';
+               }
+
+               if (typeof b ==='string') {
+                       if (b.indexOf(',') !== -1) {
+                               throw 'Invalid CSS selector';
+                       } else {
+                               bSpecificity = calculateSingle(b)['specificityArray'];
+                       }
+               } else if (Array.isArray(b)) {
+                       if (b.filter(function(e) { return (typeof e === 'number'); }).length !== 4) {
+                               throw 'Invalid specificity array';
+                       } else {
+                               bSpecificity = b;
+                       }
+               } else {
+                       throw 'Invalid CSS selector or specificity array';
+               }
+
+               for (i = 0; i < 4; i += 1) {
+                       if (aSpecificity[i] < bSpecificity[i]) {
+                               return -1;
+                       } else if (aSpecificity[i] > bSpecificity[i]) {
+                               return 1;
+                       }
+               }
+
+               return 0;
+       };
+
+       return {
+               calculate: calculate,
+               compare: compare
+       };
+}());
+
+// Export for Node JS
+if (typeof exports !== 'undefined') {
+       exports.calculate = SPECIFICITY.calculate;
+       exports.compare = SPECIFICITY.compare;
+}