309 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			309 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /**
 | |
|  **      ==============================
 | |
|  **       O           O      O   OOOO
 | |
|  **       O           O     O O  O   O
 | |
|  **       O           O     O O  O   O
 | |
|  **       OOOO   OOOO O     OOO  OOOO
 | |
|  **       O   O       O    O   O O   O
 | |
|  **       O   O       O    O   O O   O
 | |
|  **       OOOO        OOOO O   O OOOO
 | |
|  **      ==============================
 | |
|  **      Dr. Stefan Bosse http://www.bsslab.de
 | |
|  **
 | |
|  **      COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
 | |
|  **                 BY THE AUTHOR(S).
 | |
|  **                 THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
 | |
|  **                 MODIFIED, OR OTHERWISE USED IN A CONTEXT
 | |
|  **                 OUTSIDE OF THE SOFTWARE SYSTEM.
 | |
|  **
 | |
|  **    $AUTHORS:     Christopher Jeffrey and contributors, Stefan Bosse
 | |
|  **    $INITIAL:     (C) 2013-2015, Christopher Jeffrey and contributors
 | |
|  **    $MODIFIED:    sbosse (2017-2021).
 | |
|  **    $VERSION:     1.2.1
 | |
|  **
 | |
|  **    $INFO:
 | |
|  *
 | |
|  * node.js - base abstract node for blessed
 | |
|  *
 | |
|  **    $ENDOFINFO
 | |
|  */
 | |
| 
 | |
| /**
 | |
|  * Modules
 | |
|  */
 | |
| var Comp = Require('com/compat');
 | |
| 
 | |
| var EventEmitter = Require('term/events').EventEmitter;
 | |
| 
 | |
| /**
 | |
|  * Node
 | |
|  */
 | |
| 
 | |
| function Node(options) {
 | |
|   var self = this;
 | |
|   var Screen = Require('term/widgets/screen');
 | |
| 
 | |
|   if (!instanceOf(this,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;
 | |
| inheritPrototype(Node,EventEmitter);
 | |
| 
 | |
| 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;
 |