node.js 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. /**
  2. * node.js - base abstract node for blessed
  3. * Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
  4. * https://github.com/chjj/blessed
  5. */
  6. /**
  7. * Modules
  8. */
  9. var EventEmitter = require('../events').EventEmitter;
  10. /**
  11. * Node
  12. */
  13. function Node(options) {
  14. var self = this;
  15. var Screen = require('./screen');
  16. if (!(this instanceof Node)) {
  17. return new Node(options);
  18. }
  19. EventEmitter.call(this);
  20. options = options || {};
  21. this.options = options;
  22. this.screen = this.screen || options.screen;
  23. if (!this.screen) {
  24. if (this.type === 'screen') {
  25. this.screen = this;
  26. } else if (Screen.total === 1) {
  27. this.screen = Screen.global;
  28. } else if (options.parent) {
  29. this.screen = options.parent;
  30. while (this.screen && this.screen.type !== 'screen') {
  31. this.screen = this.screen.parent;
  32. }
  33. } else if (Screen.total) {
  34. // This _should_ work in most cases as long as the element is appended
  35. // synchronously after the screen's creation. Throw error if not.
  36. this.screen = Screen.instances[Screen.instances.length - 1];
  37. process.nextTick(function() {
  38. if (!self.parent) {
  39. throw new Error('Element (' + self.type + ')'
  40. + ' was not appended synchronously after the'
  41. + ' screen\'s creation. Please set a `parent`'
  42. + ' or `screen` option in the element\'s constructor'
  43. + ' if you are going to use multiple screens and'
  44. + ' append the element later.');
  45. }
  46. });
  47. } else {
  48. throw new Error('No active screen.');
  49. }
  50. }
  51. this.parent = options.parent || null;
  52. this.children = [];
  53. this.$ = this._ = this.data = {};
  54. this.uid = Node.uid++;
  55. this.index = this.index != null ? this.index : -1;
  56. if (this.type !== 'screen') {
  57. this.detached = true;
  58. }
  59. if (this.parent) {
  60. this.parent.append(this);
  61. }
  62. (options.children || []).forEach(this.append.bind(this));
  63. }
  64. Node.uid = 0;
  65. Node.prototype.__proto__ = EventEmitter.prototype;
  66. Node.prototype.type = 'node';
  67. Node.prototype.insert = function(element, i) {
  68. var self = this;
  69. if (element.screen && element.screen !== this.screen) {
  70. throw new Error('Cannot switch a node\'s screen.');
  71. }
  72. element.detach();
  73. element.parent = this;
  74. element.screen = this.screen;
  75. if (i === 0) {
  76. this.children.unshift(element);
  77. } else if (i === this.children.length) {
  78. this.children.push(element);
  79. } else {
  80. this.children.splice(i, 0, element);
  81. }
  82. element.emit('reparent', this);
  83. this.emit('adopt', element);
  84. (function emit(el) {
  85. var n = el.detached !== self.detached;
  86. el.detached = self.detached;
  87. if (n) el.emit('attach');
  88. el.children.forEach(emit);
  89. })(element);
  90. if (!this.screen.focused) {
  91. this.screen.focused = element;
  92. }
  93. };
  94. Node.prototype.prepend = function(element) {
  95. this.insert(element, 0);
  96. };
  97. Node.prototype.append = function(element) {
  98. this.insert(element, this.children.length);
  99. };
  100. Node.prototype.insertBefore = function(element, other) {
  101. var i = this.children.indexOf(other);
  102. if (~i) this.insert(element, i);
  103. };
  104. Node.prototype.insertAfter = function(element, other) {
  105. var i = this.children.indexOf(other);
  106. if (~i) this.insert(element, i + 1);
  107. };
  108. Node.prototype.remove = function(element) {
  109. if (element.parent !== this) return;
  110. var i = this.children.indexOf(element);
  111. if (!~i) return;
  112. element.clearPos();
  113. element.parent = null;
  114. this.children.splice(i, 1);
  115. i = this.screen.clickable.indexOf(element);
  116. if (~i) this.screen.clickable.splice(i, 1);
  117. i = this.screen.keyable.indexOf(element);
  118. if (~i) this.screen.keyable.splice(i, 1);
  119. element.emit('reparent', null);
  120. this.emit('remove', element);
  121. (function emit(el) {
  122. var n = el.detached !== true;
  123. el.detached = true;
  124. if (n) el.emit('detach');
  125. el.children.forEach(emit);
  126. })(element);
  127. if (this.screen.focused === element) {
  128. this.screen.rewindFocus();
  129. }
  130. };
  131. Node.prototype.detach = function() {
  132. if (this.parent) this.parent.remove(this);
  133. };
  134. Node.prototype.free = function() {
  135. return;
  136. };
  137. Node.prototype.destroy = function() {
  138. this.detach();
  139. this.forDescendants(function(el) {
  140. el.free();
  141. el.destroyed = true;
  142. el.emit('destroy');
  143. }, this);
  144. };
  145. Node.prototype.forDescendants = function(iter, s) {
  146. if (s) iter(this);
  147. this.children.forEach(function emit(el) {
  148. iter(el);
  149. el.children.forEach(emit);
  150. });
  151. };
  152. Node.prototype.forAncestors = function(iter, s) {
  153. var el = this;
  154. if (s) iter(this);
  155. while (el = el.parent) {
  156. iter(el);
  157. }
  158. };
  159. Node.prototype.collectDescendants = function(s) {
  160. var out = [];
  161. this.forDescendants(function(el) {
  162. out.push(el);
  163. }, s);
  164. return out;
  165. };
  166. Node.prototype.collectAncestors = function(s) {
  167. var out = [];
  168. this.forAncestors(function(el) {
  169. out.push(el);
  170. }, s);
  171. return out;
  172. };
  173. Node.prototype.emitDescendants = function() {
  174. var args = Array.prototype.slice(arguments)
  175. , iter;
  176. if (typeof args[args.length - 1] === 'function') {
  177. iter = args.pop();
  178. }
  179. return this.forDescendants(function(el) {
  180. if (iter) iter(el);
  181. el.emit.apply(el, args);
  182. }, true);
  183. };
  184. Node.prototype.emitAncestors = function() {
  185. var args = Array.prototype.slice(arguments)
  186. , iter;
  187. if (typeof args[args.length - 1] === 'function') {
  188. iter = args.pop();
  189. }
  190. return this.forAncestors(function(el) {
  191. if (iter) iter(el);
  192. el.emit.apply(el, args);
  193. }, true);
  194. };
  195. Node.prototype.hasDescendant = function(target) {
  196. return (function find(el) {
  197. for (var i = 0; i < el.children.length; i++) {
  198. if (el.children[i] === target) {
  199. return true;
  200. }
  201. if (find(el.children[i]) === true) {
  202. return true;
  203. }
  204. }
  205. return false;
  206. })(this);
  207. };
  208. Node.prototype.hasAncestor = function(target) {
  209. var el = this;
  210. while (el = el.parent) {
  211. if (el === target) return true;
  212. }
  213. return false;
  214. };
  215. Node.prototype.get = function(name, value) {
  216. if (this.data.hasOwnProperty(name)) {
  217. return this.data[name];
  218. }
  219. return value;
  220. };
  221. Node.prototype.set = function(name, value) {
  222. return this.data[name] = value;
  223. };
  224. /**
  225. * Expose
  226. */
  227. module.exports = Node;