PageRenderTime 88ms CodeModel.GetById 28ms RepoModel.GetById 1ms app.codeStats 0ms

/cdf-core/cdf/js/components/filter/lib/backbone.treemodel.js

https://gitlab.com/Tiger66639/cdf
JavaScript | 268 lines | 148 code | 30 blank | 90 comment | 49 complexity | 46b1d66246cd0f9c68abfb920bbb5987 MD5 | raw file
  1. /*
  2. Copyright (c) 2013 Dominick Pham <dominick@dph.am>
  3. Permission is hereby granted, free of charge, to any person obtaining
  4. a copy of this software and associated documentation files (the
  5. "Software"), to deal in the Software without restriction, including
  6. without limitation the rights to use, copy, modify, merge, publish,
  7. distribute, sublicense, and/or sell copies of the Software, and to
  8. permit persons to whom the Software is furnished to do so, subject to
  9. the following conditions:
  10. The above copyright notice and this permission notice shall be
  11. included in all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  13. EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  14. MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  15. NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  16. LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  17. OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  18. WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  19. */
  20. (function() {
  21. var ArrMethods = {
  22. where: function(attrs) {
  23. var nodes = [];
  24. _.each(this, function(model) {
  25. nodes = nodes.concat(model.where(attrs));
  26. });
  27. return wrapArray(_.uniq(nodes));
  28. }
  29. };
  30. var wrapArray = function(array) { return _.extend(array, ArrMethods); };
  31. var TreeModel = Backbone.TreeModel = Backbone.Model.extend({
  32. nodesAttribute: 'nodes',
  33. constructor: function tree(node) {
  34. Backbone.Model.prototype.constructor.apply(this, arguments);
  35. // Don't let the TreeCollection assume that it knows what model it should use.
  36. // We may be utilizing an extended TreeModel here.
  37. this._nodes = new TreeCollection(undefined, {
  38. model: this.constructor
  39. });
  40. this._nodes.parent = this;
  41. if(node && node[this.nodesAttribute]){
  42. this.add(node[this.nodesAttribute]);
  43. this.unset(this.nodesAttribute);
  44. }
  45. //Pass the events to the root node.
  46. this._nodes.on("all", function(event, model, collection, options) {
  47. this.root().trigger.apply(this.root(), arguments);
  48. }, this);
  49. },
  50. /**
  51. * returns JSON object representing tree, account for branch changes
  52. */
  53. toJSON: function() {
  54. var jsonObj = Backbone.Model.prototype.toJSON.apply(this, arguments);
  55. var children = this._nodes.toJSON();
  56. if(children.length) jsonObj[this.nodesAttribute] = children;
  57. return jsonObj;
  58. },
  59. /**
  60. * returns descendant matching :id
  61. */
  62. find: function(id) { return this.findWhere({id: id}); },
  63. /**
  64. * return first matched descendant
  65. */
  66. findWhere: function(attrs) { return this.where(attrs, true); },
  67. /**
  68. * return all matched descendants
  69. */
  70. where: function(attrs, first, excludeCurrentNode) {
  71. var nodes = [], matchedNode;
  72. // manual (non-collection method) check on the current node
  73. if(!excludeCurrentNode && _.where([this.toJSON()], attrs)[0]) nodes.push(this);
  74. if(first) {
  75. // return if first/current node is a match
  76. if(nodes[0]) return nodes[0];
  77. // return first matched node in children collection
  78. matchedNode = this._nodes.where(attrs, true);
  79. if(_.isArray(matchedNode)) matchedNode = matchedNode[0];
  80. if(matchedNode instanceof Backbone.Model) return matchedNode;
  81. // recursive call on children nodes
  82. for(var i=0, len=this._nodes.length; i<len; i++) {
  83. matchedNode = this._nodes.at(i).where(attrs, true, true);
  84. if(matchedNode) return matchedNode;
  85. }
  86. } else {
  87. // add all matched children
  88. nodes = nodes.concat(this._nodes.where(attrs));
  89. // recursive call on children nodes
  90. this._nodes.each(function(node) {
  91. nodes = nodes.concat(node.where(attrs, false, true));
  92. });
  93. // return all matched nodes
  94. return wrapArray(nodes);
  95. }
  96. },
  97. /**
  98. * returns true if current node is root node
  99. */
  100. isRoot: function() { return this.parent() === null; },
  101. /**
  102. * returns the root for any node
  103. */
  104. root: function() { return this.parent() && this.parent().root() || this; },
  105. /**
  106. * checks if current node contains argument node
  107. */
  108. contains: function(node) {
  109. if(!node || !(node.isRoot && node.parent) || node.isRoot()) return false;
  110. var parent = node.parent();
  111. return (parent === this) || this.contains(parent);
  112. },
  113. /**
  114. * returns the parent node
  115. */
  116. parent: function() { return this.collection && this.collection.parent || null; },
  117. /**
  118. * returns the children Backbone Collection if children nodes exist
  119. */
  120. nodes: function() { return this._nodes.length && this._nodes || null; },
  121. /**
  122. * returns index of node relative to collection
  123. */
  124. index: function() {
  125. if(this.isRoot()) return null;
  126. return this.collection.indexOf(this);
  127. },
  128. /**
  129. * returns the node to the right
  130. */
  131. next: function() {
  132. if(this.isRoot()) return null;
  133. var currentIndex = this.index();
  134. if(currentIndex < this.collection.length-1) {
  135. return this.collection.at(currentIndex+1);
  136. } else {
  137. return null;
  138. }
  139. },
  140. /**
  141. * returns the node to the left
  142. */
  143. prev: function() {
  144. if(this.isRoot()) return null;
  145. var currentIndex = this.index();
  146. if(currentIndex > 0) {
  147. return this.collection.at(currentIndex-1);
  148. } else {
  149. return null;
  150. }
  151. },
  152. /**
  153. * removes current node if no attributes arguments is passed,
  154. * otherswise remove matched nodes or first matched node
  155. */
  156. remove: function(attrs, first) {
  157. if(!attrs) {
  158. if(this.isRoot()) return false; // can't remove root node
  159. this.collection.remove(this);
  160. return true;
  161. } else {
  162. if(first) {
  163. this.where(attrs, true).remove();
  164. } else {
  165. _.each(this.where(attrs), function(node) {
  166. if(node.collection) node.remove();
  167. });
  168. }
  169. return this;
  170. }
  171. },
  172. /**
  173. * removes all children nodes
  174. */
  175. empty: function() {
  176. this._nodes.reset();
  177. return this;
  178. },
  179. /**
  180. * add child/children nodes to Backbone Collection
  181. */
  182. add: function(node) {
  183. if(node instanceof Backbone.Model && node.collection) node.collection.remove(node);
  184. this._nodes.add.apply(this._nodes, arguments);
  185. return this;
  186. },
  187. /**
  188. * inserts a node before the current node
  189. */
  190. insertBefore: function(node) {
  191. if(!this.isRoot()) {
  192. if(node instanceof Backbone.Model && node.collection) node.collection.remove(node);
  193. this.parent().add(node, {at: this.index()});
  194. }
  195. return this;
  196. },
  197. /**
  198. * inserts a node after the current node
  199. */
  200. insertAfter: function(node) {
  201. if(!this.isRoot()) {
  202. if(node instanceof Backbone.Model && node.collection) node.collection.remove(node);
  203. this.parent().add(node, {at: this.index()+1});
  204. }
  205. return this;
  206. },
  207. /**
  208. * shorthand for getting/inserting nodes before
  209. */
  210. before: function(nodes) {
  211. if(nodes) return this.insertBefore(nodes);
  212. return this.prev();
  213. },
  214. /**
  215. * shorthand for getting/inserting nodes before
  216. */
  217. after: function(nodes) {
  218. if(nodes) return this.insertAfter(nodes);
  219. return this.next();
  220. }
  221. });
  222. var TreeCollection = Backbone.TreeCollection = Backbone.Collection.extend({
  223. where: function(attrs, opts) {
  224. if(opts && opts.deep) {
  225. var nodes = [];
  226. this.each(function(model) {
  227. nodes = nodes.concat(model.where(attrs));
  228. });
  229. return wrapArray(nodes);
  230. } else {
  231. return Backbone.Collection.prototype.where.apply(this, arguments);
  232. }
  233. }
  234. });
  235. }).call(this);