attributes.js 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. var falseFunc = require("boolbase").falseFunc;
  2. //https://github.com/slevithan/XRegExp/blob/master/src/xregexp.js#L469
  3. var reChars = /[-[\]{}()*+?.,\\^$|#\s]/g;
  4. /*
  5. attribute selectors
  6. */
  7. var attributeRules = {
  8. __proto__: null,
  9. equals: function(next, data, options) {
  10. var name = data.name;
  11. var value = data.value;
  12. var adapter = options.adapter;
  13. if (data.ignoreCase) {
  14. value = value.toLowerCase();
  15. return function equalsIC(elem) {
  16. var attr = adapter.getAttributeValue(elem, name);
  17. return attr != null && attr.toLowerCase() === value && next(elem);
  18. };
  19. }
  20. return function equals(elem) {
  21. return adapter.getAttributeValue(elem, name) === value && next(elem);
  22. };
  23. },
  24. hyphen: function(next, data, options) {
  25. var name = data.name;
  26. var value = data.value;
  27. var len = value.length;
  28. var adapter = options.adapter;
  29. if (data.ignoreCase) {
  30. value = value.toLowerCase();
  31. return function hyphenIC(elem) {
  32. var attr = adapter.getAttributeValue(elem, name);
  33. return (
  34. attr != null &&
  35. (attr.length === len || attr.charAt(len) === "-") &&
  36. attr.substr(0, len).toLowerCase() === value &&
  37. next(elem)
  38. );
  39. };
  40. }
  41. return function hyphen(elem) {
  42. var attr = adapter.getAttributeValue(elem, name);
  43. return (
  44. attr != null &&
  45. attr.substr(0, len) === value &&
  46. (attr.length === len || attr.charAt(len) === "-") &&
  47. next(elem)
  48. );
  49. };
  50. },
  51. element: function(next, data, options) {
  52. var name = data.name;
  53. var value = data.value;
  54. var adapter = options.adapter;
  55. if (/\s/.test(value)) {
  56. return falseFunc;
  57. }
  58. value = value.replace(reChars, "\\$&");
  59. var pattern = "(?:^|\\s)" + value + "(?:$|\\s)",
  60. flags = data.ignoreCase ? "i" : "",
  61. regex = new RegExp(pattern, flags);
  62. return function element(elem) {
  63. var attr = adapter.getAttributeValue(elem, name);
  64. return attr != null && regex.test(attr) && next(elem);
  65. };
  66. },
  67. exists: function(next, data, options) {
  68. var name = data.name;
  69. var adapter = options.adapter;
  70. return function exists(elem) {
  71. return adapter.hasAttrib(elem, name) && next(elem);
  72. };
  73. },
  74. start: function(next, data, options) {
  75. var name = data.name;
  76. var value = data.value;
  77. var len = value.length;
  78. var adapter = options.adapter;
  79. if (len === 0) {
  80. return falseFunc;
  81. }
  82. if (data.ignoreCase) {
  83. value = value.toLowerCase();
  84. return function startIC(elem) {
  85. var attr = adapter.getAttributeValue(elem, name);
  86. return attr != null && attr.substr(0, len).toLowerCase() === value && next(elem);
  87. };
  88. }
  89. return function start(elem) {
  90. var attr = adapter.getAttributeValue(elem, name);
  91. return attr != null && attr.substr(0, len) === value && next(elem);
  92. };
  93. },
  94. end: function(next, data, options) {
  95. var name = data.name;
  96. var value = data.value;
  97. var len = -value.length;
  98. var adapter = options.adapter;
  99. if (len === 0) {
  100. return falseFunc;
  101. }
  102. if (data.ignoreCase) {
  103. value = value.toLowerCase();
  104. return function endIC(elem) {
  105. var attr = adapter.getAttributeValue(elem, name);
  106. return attr != null && attr.substr(len).toLowerCase() === value && next(elem);
  107. };
  108. }
  109. return function end(elem) {
  110. var attr = adapter.getAttributeValue(elem, name);
  111. return attr != null && attr.substr(len) === value && next(elem);
  112. };
  113. },
  114. any: function(next, data, options) {
  115. var name = data.name;
  116. var value = data.value;
  117. var adapter = options.adapter;
  118. if (value === "") {
  119. return falseFunc;
  120. }
  121. if (data.ignoreCase) {
  122. var regex = new RegExp(value.replace(reChars, "\\$&"), "i");
  123. return function anyIC(elem) {
  124. var attr = adapter.getAttributeValue(elem, name);
  125. return attr != null && regex.test(attr) && next(elem);
  126. };
  127. }
  128. return function any(elem) {
  129. var attr = adapter.getAttributeValue(elem, name);
  130. return attr != null && attr.indexOf(value) >= 0 && next(elem);
  131. };
  132. },
  133. not: function(next, data, options) {
  134. var name = data.name;
  135. var value = data.value;
  136. var adapter = options.adapter;
  137. if (value === "") {
  138. return function notEmpty(elem) {
  139. return !!adapter.getAttributeValue(elem, name) && next(elem);
  140. };
  141. } else if (data.ignoreCase) {
  142. value = value.toLowerCase();
  143. return function notIC(elem) {
  144. var attr = adapter.getAttributeValue(elem, name);
  145. return attr != null && attr.toLowerCase() !== value && next(elem);
  146. };
  147. }
  148. return function not(elem) {
  149. return adapter.getAttributeValue(elem, name) !== value && next(elem);
  150. };
  151. }
  152. };
  153. module.exports = {
  154. compile: function(next, data, options) {
  155. if (options && options.strict && (data.ignoreCase || data.action === "not")) {
  156. throw new Error("Unsupported attribute selector");
  157. }
  158. return attributeRules[data.action](next, data, options);
  159. },
  160. rules: attributeRules
  161. };