query.js 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /*!
  2. * Stylus - Query
  3. * Copyright (c) Automattic <developer.wordpress.com>
  4. * MIT Licensed
  5. */
  6. /**
  7. * Module dependencies.
  8. */
  9. var Node = require('./node');
  10. /**
  11. * Initialize a new `Query`.
  12. *
  13. * @api public
  14. */
  15. var Query = module.exports = function Query(){
  16. Node.call(this);
  17. this.nodes = [];
  18. this.type = '';
  19. this.predicate = '';
  20. };
  21. /**
  22. * Inherit from `Node.prototype`.
  23. */
  24. Query.prototype.__proto__ = Node.prototype;
  25. /**
  26. * Return a clone of this node.
  27. *
  28. * @return {Node}
  29. * @api public
  30. */
  31. Query.prototype.clone = function(parent){
  32. var clone = new Query;
  33. clone.predicate = this.predicate;
  34. clone.type = this.type;
  35. for (var i = 0, len = this.nodes.length; i < len; ++i) {
  36. clone.push(this.nodes[i].clone(parent, clone));
  37. }
  38. clone.lineno = this.lineno;
  39. clone.column = this.column;
  40. clone.filename = this.filename;
  41. return clone;
  42. };
  43. /**
  44. * Push the given `feature`.
  45. *
  46. * @param {Feature} feature
  47. * @api public
  48. */
  49. Query.prototype.push = function(feature){
  50. this.nodes.push(feature);
  51. };
  52. /**
  53. * Return resolved type of this query.
  54. *
  55. * @return {String}
  56. * @api private
  57. */
  58. Query.prototype.__defineGetter__('resolvedType', function(){
  59. if (this.type) {
  60. return this.type.nodeName
  61. ? this.type.string
  62. : this.type;
  63. }
  64. });
  65. /**
  66. * Return resolved predicate of this query.
  67. *
  68. * @return {String}
  69. * @api private
  70. */
  71. Query.prototype.__defineGetter__('resolvedPredicate', function(){
  72. if (this.predicate) {
  73. return this.predicate.nodeName
  74. ? this.predicate.string
  75. : this.predicate;
  76. }
  77. });
  78. /**
  79. * Merges this query with the `other`.
  80. *
  81. * @param {Query} other
  82. * @return {Query}
  83. * @api private
  84. */
  85. Query.prototype.merge = function(other){
  86. var query = new Query
  87. , p1 = this.resolvedPredicate
  88. , p2 = other.resolvedPredicate
  89. , t1 = this.resolvedType
  90. , t2 = other.resolvedType
  91. , type, pred;
  92. // Stolen from Sass :D
  93. t1 = t1 || t2;
  94. t2 = t2 || t1;
  95. if (('not' == p1) ^ ('not' == p2)) {
  96. if (t1 == t2) return;
  97. type = ('not' == p1) ? t2 : t1;
  98. pred = ('not' == p1) ? p2 : p1;
  99. } else if (('not' == p1) && ('not' == p2)) {
  100. if (t1 != t2) return;
  101. type = t1;
  102. pred = 'not';
  103. } else if (t1 != t2) {
  104. return;
  105. } else {
  106. type = t1;
  107. pred = p1 || p2;
  108. }
  109. query.predicate = pred;
  110. query.type = type;
  111. query.nodes = this.nodes.concat(other.nodes);
  112. return query;
  113. };
  114. /**
  115. * Return "<a> and <b> and <c>"
  116. *
  117. * @return {String}
  118. * @api public
  119. */
  120. Query.prototype.toString = function(){
  121. var pred = this.predicate ? this.predicate + ' ' : ''
  122. , type = this.type || ''
  123. , len = this.nodes.length
  124. , str = pred + type;
  125. if (len) {
  126. str += (type && ' and ') + this.nodes.map(function(expr){
  127. return expr.toString();
  128. }).join(' and ');
  129. }
  130. return str;
  131. };
  132. /**
  133. * Return a JSON representation of this node.
  134. *
  135. * @return {Object}
  136. * @api public
  137. */
  138. Query.prototype.toJSON = function(){
  139. return {
  140. __type: 'Query',
  141. predicate: this.predicate,
  142. type: this.type,
  143. nodes: this.nodes,
  144. lineno: this.lineno,
  145. column: this.column,
  146. filename: this.filename
  147. };
  148. };