attributes.js 4.3 KB

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