Software zum Installieren eines Smart-Mirror Frameworks , zum Nutzen von hochschulrelevanten Informationen, auf einem Raspberry-Pi.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

specificity.js 7.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229
  1. (function (global, factory) {
  2. typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
  3. typeof define === 'function' && define.amd ? define(['exports'], factory) :
  4. (factory((global.SPECIFICITY = {})));
  5. }(this, (function (exports) { 'use strict';
  6. // Calculate the specificity for a selector by dividing it into simple selectors and counting them
  7. var calculate = function(input) {
  8. var selectors,
  9. selector,
  10. i,
  11. len,
  12. results = [];
  13. // Separate input by commas
  14. selectors = input.split(',');
  15. for (i = 0, len = selectors.length; i < len; i += 1) {
  16. selector = selectors[i];
  17. if (selector.length > 0) {
  18. results.push(calculateSingle(selector));
  19. }
  20. }
  21. return results;
  22. };
  23. /**
  24. * Calculates the specificity of CSS selectors
  25. * http://www.w3.org/TR/css3-selectors/#specificity
  26. *
  27. * Returns an object with the following properties:
  28. * - selector: the input
  29. * - specificity: e.g. 0,1,0,0
  30. * - parts: array with details about each part of the selector that counts towards the specificity
  31. * - specificityArray: e.g. [0, 1, 0, 0]
  32. */
  33. var calculateSingle = function(input) {
  34. var selector = input,
  35. findMatch,
  36. typeCount = {
  37. 'a': 0,
  38. 'b': 0,
  39. 'c': 0
  40. },
  41. parts = [],
  42. // The following regular expressions assume that selectors matching the preceding regular expressions have been removed
  43. attributeRegex = /(\[[^\]]+\])/g,
  44. idRegex = /(#[^\#\s\+>~\.\[:\)]+)/g,
  45. classRegex = /(\.[^\s\+>~\.\[:\)]+)/g,
  46. pseudoElementRegex = /(::[^\s\+>~\.\[:]+|:first-line|:first-letter|:before|:after)/gi,
  47. // A regex for pseudo classes with brackets - :nth-child(), :nth-last-child(), :nth-of-type(), :nth-last-type(), :lang()
  48. // The negation psuedo class (:not) is filtered out because specificity is calculated on its argument
  49. // :global and :local are filtered out - they look like psuedo classes but are an identifier for CSS Modules
  50. pseudoClassWithBracketsRegex = /(:(?!not|global|local)[\w-]+\([^\)]*\))/gi,
  51. // A regex for other pseudo classes, which don't have brackets
  52. pseudoClassRegex = /(:(?!not|global|local)[^\s\+>~\.\[:]+)/g,
  53. elementRegex = /([^\s\+>~\.\[:]+)/g;
  54. // Find matches for a regular expression in a string and push their details to parts
  55. // Type is "a" for IDs, "b" for classes, attributes and pseudo-classes and "c" for elements and pseudo-elements
  56. findMatch = function(regex, type) {
  57. var matches, i, len, match, index, length;
  58. if (regex.test(selector)) {
  59. matches = selector.match(regex);
  60. for (i = 0, len = matches.length; i < len; i += 1) {
  61. typeCount[type] += 1;
  62. match = matches[i];
  63. index = selector.indexOf(match);
  64. length = match.length;
  65. parts.push({
  66. selector: input.substr(index, length),
  67. type: type,
  68. index: index,
  69. length: length
  70. });
  71. // Replace this simple selector with whitespace so it won't be counted in further simple selectors
  72. selector = selector.replace(match, Array(length + 1).join(' '));
  73. }
  74. }
  75. };
  76. // Replace escaped characters with plain text, using the "A" character
  77. // https://www.w3.org/TR/CSS21/syndata.html#characters
  78. (function() {
  79. var replaceWithPlainText = function(regex) {
  80. var matches, i, len, match;
  81. if (regex.test(selector)) {
  82. matches = selector.match(regex);
  83. for (i = 0, len = matches.length; i < len; i += 1) {
  84. match = matches[i];
  85. selector = selector.replace(match, Array(match.length + 1).join('A'));
  86. }
  87. }
  88. },
  89. // Matches a backslash followed by six hexadecimal digits followed by an optional single whitespace character
  90. escapeHexadecimalRegex = /\\[0-9A-Fa-f]{6}\s?/g,
  91. // Matches a backslash followed by fewer than six hexadecimal digits followed by a mandatory single whitespace character
  92. escapeHexadecimalRegex2 = /\\[0-9A-Fa-f]{1,5}\s/g,
  93. // Matches a backslash followed by any character
  94. escapeSpecialCharacter = /\\./g;
  95. replaceWithPlainText(escapeHexadecimalRegex);
  96. replaceWithPlainText(escapeHexadecimalRegex2);
  97. replaceWithPlainText(escapeSpecialCharacter);
  98. }());
  99. // Remove anything after a left brace in case a user has pasted in a rule, not just a selector
  100. (function() {
  101. var regex = /{[^]*/gm,
  102. matches, i, len, match;
  103. if (regex.test(selector)) {
  104. matches = selector.match(regex);
  105. for (i = 0, len = matches.length; i < len; i += 1) {
  106. match = matches[i];
  107. selector = selector.replace(match, Array(match.length + 1).join(' '));
  108. }
  109. }
  110. }());
  111. // Add attribute selectors to parts collection (type b)
  112. findMatch(attributeRegex, 'b');
  113. // Add ID selectors to parts collection (type a)
  114. findMatch(idRegex, 'a');
  115. // Add class selectors to parts collection (type b)
  116. findMatch(classRegex, 'b');
  117. // Add pseudo-element selectors to parts collection (type c)
  118. findMatch(pseudoElementRegex, 'c');
  119. // Add pseudo-class selectors to parts collection (type b)
  120. findMatch(pseudoClassWithBracketsRegex, 'b');
  121. findMatch(pseudoClassRegex, 'b');
  122. // Remove universal selector and separator characters
  123. selector = selector.replace(/[\*\s\+>~]/g, ' ');
  124. // Remove any stray dots or hashes which aren't attached to words
  125. // These may be present if the user is live-editing this selector
  126. selector = selector.replace(/[#\.]/g, ' ');
  127. // Remove the negation psuedo-class (:not) but leave its argument because specificity is calculated on its argument
  128. // Remove non-standard :local and :global CSS Module identifiers because they do not effect the specificity
  129. selector = selector.replace(/:not/g, ' ');
  130. selector = selector.replace(/:local/g, ' ');
  131. selector = selector.replace(/:global/g, ' ');
  132. selector = selector.replace(/[\(\)]/g, ' ');
  133. // The only things left should be element selectors (type c)
  134. findMatch(elementRegex, 'c');
  135. // Order the parts in the order they appear in the original selector
  136. // This is neater for external apps to deal with
  137. parts.sort(function(a, b) {
  138. return a.index - b.index;
  139. });
  140. return {
  141. selector: input,
  142. specificity: '0,' + typeCount.a.toString() + ',' + typeCount.b.toString() + ',' + typeCount.c.toString(),
  143. specificityArray: [0, typeCount.a, typeCount.b, typeCount.c],
  144. parts: parts
  145. };
  146. };
  147. /**
  148. * Compares two CSS selectors for specificity
  149. * Alternatively you can replace one of the CSS selectors with a specificity array
  150. *
  151. * - it returns -1 if a has a lower specificity than b
  152. * - it returns 1 if a has a higher specificity than b
  153. * - it returns 0 if a has the same specificity than b
  154. */
  155. var compare = function(a, b) {
  156. var aSpecificity,
  157. bSpecificity,
  158. i;
  159. if (typeof a ==='string') {
  160. if (a.indexOf(',') !== -1) {
  161. throw 'Invalid CSS selector';
  162. } else {
  163. aSpecificity = calculateSingle(a)['specificityArray'];
  164. }
  165. } else if (Array.isArray(a)) {
  166. if (a.filter(function(e) { return (typeof e === 'number'); }).length !== 4) {
  167. throw 'Invalid specificity array';
  168. } else {
  169. aSpecificity = a;
  170. }
  171. } else {
  172. throw 'Invalid CSS selector or specificity array';
  173. }
  174. if (typeof b ==='string') {
  175. if (b.indexOf(',') !== -1) {
  176. throw 'Invalid CSS selector';
  177. } else {
  178. bSpecificity = calculateSingle(b)['specificityArray'];
  179. }
  180. } else if (Array.isArray(b)) {
  181. if (b.filter(function(e) { return (typeof e === 'number'); }).length !== 4) {
  182. throw 'Invalid specificity array';
  183. } else {
  184. bSpecificity = b;
  185. }
  186. } else {
  187. throw 'Invalid CSS selector or specificity array';
  188. }
  189. for (i = 0; i < 4; i += 1) {
  190. if (aSpecificity[i] < bSpecificity[i]) {
  191. return -1;
  192. } else if (aSpecificity[i] > bSpecificity[i]) {
  193. return 1;
  194. }
  195. }
  196. return 0;
  197. };
  198. exports.calculate = calculate;
  199. exports.compare = compare;
  200. Object.defineProperty(exports, '__esModule', { value: true });
  201. })));