123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- /**
- * node.js - base abstract node for blessed
- * Copyright (c) 2013-2015, Christopher Jeffrey and contributors (MIT License).
- * https://github.com/chjj/blessed
- */
- /**
- * Modules
- */
- var EventEmitter = require('../events').EventEmitter;
- /**
- * Node
- */
- function Node(options) {
- var self = this;
- var Screen = require('./screen');
- if (!(this instanceof Node)) {
- return new Node(options);
- }
- EventEmitter.call(this);
- options = options || {};
- this.options = options;
- this.screen = this.screen || options.screen;
- if (!this.screen) {
- if (this.type === 'screen') {
- this.screen = this;
- } else if (Screen.total === 1) {
- this.screen = Screen.global;
- } else if (options.parent) {
- this.screen = options.parent;
- while (this.screen && this.screen.type !== 'screen') {
- this.screen = this.screen.parent;
- }
- } else if (Screen.total) {
- // This _should_ work in most cases as long as the element is appended
- // synchronously after the screen's creation. Throw error if not.
- this.screen = Screen.instances[Screen.instances.length - 1];
- process.nextTick(function() {
- if (!self.parent) {
- throw new Error('Element (' + self.type + ')'
- + ' was not appended synchronously after the'
- + ' screen\'s creation. Please set a `parent`'
- + ' or `screen` option in the element\'s constructor'
- + ' if you are going to use multiple screens and'
- + ' append the element later.');
- }
- });
- } else {
- throw new Error('No active screen.');
- }
- }
- this.parent = options.parent || null;
- this.children = [];
- this.$ = this._ = this.data = {};
- this.uid = Node.uid++;
- this.index = this.index != null ? this.index : -1;
- if (this.type !== 'screen') {
- this.detached = true;
- }
- if (this.parent) {
- this.parent.append(this);
- }
- (options.children || []).forEach(this.append.bind(this));
- }
- Node.uid = 0;
- Node.prototype.__proto__ = EventEmitter.prototype;
- Node.prototype.type = 'node';
- Node.prototype.insert = function(element, i) {
- var self = this;
- if (element.screen && element.screen !== this.screen) {
- throw new Error('Cannot switch a node\'s screen.');
- }
- element.detach();
- element.parent = this;
- element.screen = this.screen;
- if (i === 0) {
- this.children.unshift(element);
- } else if (i === this.children.length) {
- this.children.push(element);
- } else {
- this.children.splice(i, 0, element);
- }
- element.emit('reparent', this);
- this.emit('adopt', element);
- (function emit(el) {
- var n = el.detached !== self.detached;
- el.detached = self.detached;
- if (n) el.emit('attach');
- el.children.forEach(emit);
- })(element);
- if (!this.screen.focused) {
- this.screen.focused = element;
- }
- };
- Node.prototype.prepend = function(element) {
- this.insert(element, 0);
- };
- Node.prototype.append = function(element) {
- this.insert(element, this.children.length);
- };
- Node.prototype.insertBefore = function(element, other) {
- var i = this.children.indexOf(other);
- if (~i) this.insert(element, i);
- };
- Node.prototype.insertAfter = function(element, other) {
- var i = this.children.indexOf(other);
- if (~i) this.insert(element, i + 1);
- };
- Node.prototype.remove = function(element) {
- if (element.parent !== this) return;
- var i = this.children.indexOf(element);
- if (!~i) return;
- element.clearPos();
- element.parent = null;
- this.children.splice(i, 1);
- i = this.screen.clickable.indexOf(element);
- if (~i) this.screen.clickable.splice(i, 1);
- i = this.screen.keyable.indexOf(element);
- if (~i) this.screen.keyable.splice(i, 1);
- element.emit('reparent', null);
- this.emit('remove', element);
- (function emit(el) {
- var n = el.detached !== true;
- el.detached = true;
- if (n) el.emit('detach');
- el.children.forEach(emit);
- })(element);
- if (this.screen.focused === element) {
- this.screen.rewindFocus();
- }
- };
- Node.prototype.detach = function() {
- if (this.parent) this.parent.remove(this);
- };
- Node.prototype.free = function() {
- return;
- };
- Node.prototype.destroy = function() {
- this.detach();
- this.forDescendants(function(el) {
- el.free();
- el.destroyed = true;
- el.emit('destroy');
- }, this);
- };
- Node.prototype.forDescendants = function(iter, s) {
- if (s) iter(this);
- this.children.forEach(function emit(el) {
- iter(el);
- el.children.forEach(emit);
- });
- };
- Node.prototype.forAncestors = function(iter, s) {
- var el = this;
- if (s) iter(this);
- while (el = el.parent) {
- iter(el);
- }
- };
- Node.prototype.collectDescendants = function(s) {
- var out = [];
- this.forDescendants(function(el) {
- out.push(el);
- }, s);
- return out;
- };
- Node.prototype.collectAncestors = function(s) {
- var out = [];
- this.forAncestors(function(el) {
- out.push(el);
- }, s);
- return out;
- };
- Node.prototype.emitDescendants = function() {
- var args = Array.prototype.slice(arguments)
- , iter;
- if (typeof args[args.length - 1] === 'function') {
- iter = args.pop();
- }
- return this.forDescendants(function(el) {
- if (iter) iter(el);
- el.emit.apply(el, args);
- }, true);
- };
- Node.prototype.emitAncestors = function() {
- var args = Array.prototype.slice(arguments)
- , iter;
- if (typeof args[args.length - 1] === 'function') {
- iter = args.pop();
- }
- return this.forAncestors(function(el) {
- if (iter) iter(el);
- el.emit.apply(el, args);
- }, true);
- };
- Node.prototype.hasDescendant = function(target) {
- return (function find(el) {
- for (var i = 0; i < el.children.length; i++) {
- if (el.children[i] === target) {
- return true;
- }
- if (find(el.children[i]) === true) {
- return true;
- }
- }
- return false;
- })(this);
- };
- Node.prototype.hasAncestor = function(target) {
- var el = this;
- while (el = el.parent) {
- if (el === target) return true;
- }
- return false;
- };
- Node.prototype.get = function(name, value) {
- if (this.data.hasOwnProperty(name)) {
- return this.data[name];
- }
- return value;
- };
- Node.prototype.set = function(name, value) {
- return this.data[name] = value;
- };
- /**
- * Expose
- */
- module.exports = Node;
|