/** * Windows are elements of a Page object * * @param x - (int) starting x of it's parent [1..] * @param y - (int) starting y of it's parent [1..] * @param width - (int) full width of the window (text content will be smaller if there are scroll bars/boarder) * @param height - (int) full height of the window (text content will be smaller if there are scroll bars/boarder) * @param name - (string) internal name for the window (useful for debugging) * @param parent - (object) parent of this window * @param debug - (int) debug mode, which fills the window with debug content * @constructor * * Pages have the following attributes: * - bx/by - (int) right/bottom most boundary of the window representing the start + width/height of the window * - child - (array) children in this window * - height - (int) Window's height * - name - (string) Windows name (useful for internal debugging) * - parent - (object) Parent that this window belongs to * - x/y - (int) start position of the window * - visible - (bool) whether this window is visible * - width - (int) Window's width * - z - (int) Window's depth indicator * * Windows have the following public functions * - build - Compile the frame for rendering * - debug - Useful for debugging with properties of this Window * - draw - Draw a part of this Window * - drawline - Draw a y line for this Window * - visibleChildren - Children that will be included when this window is rendered */ function Window(x,y,width,height,name,parent,debug) { this.__properties__ = { x: undefined, // X offset of parent that the canvas starts [1..width] y: undefined, // Y offset of parent that the canvas starts [1..height] z: 0, // Window top-bottom position, higher z is shown [0..] ox: 0, // When canvas width > width, this is the offset we display [0..] oy: 0, // When canvas height > height, this is the offset we display [0..] width: undefined, // Display Width + (1 char if scrollbars = true) height: undefined, // Display Height canvaswidth: undefined, // Width of Canvas (default display width) canvasheight: undefined, // Height of Canvas (default display height) content: [], // Window content - starting at 0,0 = 1,1 visible: true, // Is this window visible }; /* this.__settings__ = { checkbounds: true, // Can this frame move outside of the parent v_scroll: true, // Can the content scroll vertically (takes up 1 line) [AUTO DETERMINE IF canvas > width] h_scroll: false, // Can the content scroll horizontally (takes up 1 char) [AUTO DETERMINE IF canvas > height] delay: 0, // Delay while rendering word_wrap: false, // Word wrap content pageable: false, // Overflowed content is rendered with the next page }; */ this.__relations__ = { parent: undefined, child: [], }; /* this.__position__ = { cursor: undefined, }; */ /* Validation to implement: + X BOUNDARY - x cannot be < parent.x if checkbounds is true [when moving window] - x+width(-1 if h_scroll is true) cannot be greater than parent.width if checkbounds is true - v_scroll must be true for canvaswidth > width - when scrolling ox cannot be > width-x - when layout.pageable is true, next page will only have windows included that have a y in the range ie: if height is 44 (window is 22), next page is 23-44 and will only include children where y=23-44 + Y BOUNDARY - y cannot be < parent.y if checkbounds is true [when moving window] - y+height(-1 if v_scroll is true) cannot be greater than parent.height if checkbounds is true - h_scroll must be true for canvasheight > height - when scrolling oy cannot be > height-y - when layout.pageable is true, children height cannot be greater than parent.height - y. */ function init(x,y,width,height,name,parent,debug) { if (parent instanceof Window) { this.z = parent.__relations__.child.length+1; this.parent = parent; parent.child = this; // Check that our height/widths is not outside of our parent if ((this.x < 1) || (width > this.parent.width)) throw new Error('Window: '+name+' width ['+width+'] is beyond our parent\'s width ['+this.parent.width+'].'); if ((x > this.parent.bx) || (x+width-1 > this.parent.bx)) throw new Error('Window: '+name+' start x ['+x+'] and width ['+width+'] is beyond our parent\'s end x ['+this.parent.bx+'].'); if ((this.y < 1) || (height > this.parent.height)) throw new Error('Window: '+name+' height ['+height+'] is beyond our parent\'s height ['+this.parent.height+'].'); if ((y > this.parent.by) || (y+height-1 > this.parent.by)) throw new Error('Window: '+name+' start y ['+y+'] and height ['+height+'] is beyond our parent\'s end y ['+this.parent.by+'].'); } else if (parent instanceof Page) { this.parent = parent; } else { throw new Error('INVALID Parent Type: '+parent); } this.name = name; this.x = x; this.y = y; this.width = this.canvaswidth = width; this.height = this.canvasheight = height; if (debug) { this.canvaswidth = width*2; this.canvasheight = height*2; } // Fill with data for(var y=1;y<=this.canvasheight;y++) { for(var x=1;x<=this.canvaswidth;x++) { if (this.content[y] === undefined) this.content[y] = []; this.content[y][x] = debug ? new Char((x > this.width) || (y > this.height) ? this.name[0].toUpperCase() : this.name[0].toLowerCase(),undefined) : new Char(undefined,undefined); } } } // Windows boundary (right most width) of parent [1..] Window.prototype.__defineGetter__('bx',function() { return this.__properties__.x+this.__properties__.width-1; }); // Windows boundary (bottom most height) of parent [1..] Window.prototype.__defineGetter__('by',function() { return this.__properties__.y+this.__properties__.height-1; }); /* // Can this window be moved outside of the parents visible area Window.prototype.__defineGetter__('checkbounds',function() { return this.__settings__.checkbounds; }); Window.prototype.__defineSetter__('checkbounds',function(bool) { this.__settings__.checkbounds = bool; }); */ // Management of children objects, in highest z order Window.prototype.__defineGetter__('child',function() { // Return the children sorted in z order lowest to highest return this.__relations__.child.sort(function(a,b) { return (a.__properties__.z < b.__properties__.z) ? -1 : ((b.__properties__.z < a.__properties__.z) ? 1 : 0); }); }); Window.prototype.__defineSetter__('child',function(obj) { if(obj instanceof Window) { this.__relations__.child.push(obj); } else throw new Error('child not an instance of Window()'); }); // Window content Window.prototype.__defineGetter__('content',function() { return this.__properties__.content; }); Window.prototype.__defineSetter__('content',function(string) { if (this.__properties__.content) throw new Error('content already DEFINED'); return this.__properties__.content = string; }); // Window canvas height Window.prototype.__defineGetter__('canvasheight',function() { return this.__properties__.canvasheight; }); Window.prototype.__defineSetter__('canvasheight',function(int) { if (this.__properties__.canvasheight) throw new Error('canvasheight already DEFINED'); return this.__properties__.canvasheight = int; }); // Window canvas width Window.prototype.__defineGetter__('canvaswidth',function() { return this.__properties__.canvaswidth; }); Window.prototype.__defineSetter__('canvaswidth',function(int) { if (this.__properties__.canvaswidth) throw new Error('canvaswidth already DEFINED'); return this.__properties__.canvaswidth = int; }); // Window name Window.prototype.__defineGetter__('name',function() { return this.__properties__.name; }); Window.prototype.__defineSetter__('name',function(string) { if (this.__properties__.name) throw new Error('name already DEFINED'); return this.__properties__.name = string; }); Window.prototype.__defineGetter__('ox',function() { return this.__properties__.ox; }); Window.prototype.__defineSetter__('ox',function(int) { if (this.__properties__.ox) throw new Error('ox already DEFINED'); this.__properties__.ox = int; }); Window.prototype.__defineGetter__('oy',function() { return this.__properties__.oy; }); Window.prototype.__defineSetter__('oy',function(int) { if (this.__properties__.oy) throw new Error('oy already DEFINED'); this.__properties__.oy = int; }); // Parent window object Window.prototype.__defineGetter__('parent',function() { return this.__relations__.parent; }); Window.prototype.__defineSetter__('parent',function(obj) { if (this.__relations__.parent) throw new Error('parent already DEFINED'); return this.__relations__.parent = obj; }); // Window's display height Window.prototype.__defineGetter__('height',function() { return this.__properties__.height; }); Window.prototype.__defineSetter__('height',function(int) { if (this.__properties__.height) throw new Error('height already DEFINED'); this.__properties__.height = int; }); // Window's display width Window.prototype.__defineGetter__('width',function() { return this.__properties__.width; }); Window.prototype.__defineSetter__('width',function(int) { if (this.__properties__.width) throw new Error('width already DEFINED'); this.__properties__.width = int; }); // Window's start position on it's parent (width) Window.prototype.__defineGetter__('x',function() { return this.__properties__.x; }); Window.prototype.__defineSetter__('x',function(int) { if (this.__properties__.x) throw new Error('x already DEFINED'); this.__properties__.x = int; }); // Window's start position on it's parent (height) Window.prototype.__defineGetter__('y',function() { return this.__properties__.y; }); Window.prototype.__defineSetter__('y',function(int) { if (this.__properties__.y) throw new Error('y already DEFINED'); this.__properties__.y = int; }); // Is the current window visible Window.prototype.__defineGetter__('visible',function() { return this.__properties__.visible; }); Window.prototype.__defineSetter__('visible',function(bool) { if (typeof bool !== 'boolean') throw new Error('visible expected a true/false'); this.__properties__.visible = bool; }); // What position is this window (highest is visible) Window.prototype.__defineSetter__('z',function(int) { if (this.__properties__.z) throw new Error('z already DEFINED'); this.__properties__.z = int; }); Window.prototype.__defineGetter__('z',function() { return this.__properties__.z; }); /** * Build this window, returning an array of Char that will be rendered by Page * * @param xoffset - (int) This windows x position for its parent * @param yoffset - (int) This windows y position for its parent * @param debug - (int) debug mode, which fills the window with debug content * @returns {*[]} */ Window.prototype.build = function(xoffset,yoffset,debug) { var display = []; if (debug) { writeln('********* ['+this.name+'] *********'); writeln('name :'+this.name); writeln('xoff :'+xoffset); writeln('yoff :'+yoffset); writeln('x :'+this.x); writeln('bx :'+this.bx) writeln('ox :'+this.ox); writeln('y :'+this.y); writeln('by :'+this.by) writeln('oy :'+this.oy); writeln('lines :'+this.content.length); writeln('content:'+JSON.stringify(Object.keys(this.content).join(','))); } if (debug) writeln('-------------'); for (y=1;y<=this.height;y++) { if (debug) write(padright(y,2,0)+':'); var sy = this.y-1+y+yoffset-1; for (x=1;x<=this.width;x++) { if (debug) writeln('- Checking :'+this.name+', y:'+(y+this.oy)+', x:'+(x+this.ox)); var sx = this.x-1+x+xoffset-1; if (display[sy] === undefined) display[sy] = []; if ((this.content[y+this.oy] !== undefined) && (this.content[y+this.oy][x+this.ox] !== undefined)) { display[sy][sx] = this.content[y+this.oy][x+this.ox]; if (debug) writeln('- storing in y:'+(sy)+', x:'+(sx)+', ch:'+display[sy][sx].ch); } else { //display[sy][sx] = new Char(null,BG_BLACK|LIGHTGRAY); display[sy][sx] = new Char(); if (debug) writeln('- nothing for y:'+(sy)+', x:'+(sx)+', ch:'+display[sy][sx].ch); } } if (debug) writeln(); } if (debug) { writeln('Window:'+this.name+', has ['+this.child.filter(function(child) { return child.visible; }).length+'] children'); this.child.forEach(function(child) { writeln(' - child:'+child.name+', visible:'+child.visible); }) } // Fill the array with our values var that = this; this.child.filter(function(child) { return child.visible; }).forEach(function(child) { if (debug) { writeln('=========== ['+child.name+'] ============='); writeln('xoff :'+xoffset); writeln('yoff :'+yoffset); writeln('this.x :'+that.x); writeln('this.y :'+that.y); } draw = child.build(that.x+xoffset-1,that.y+yoffset-1,debug); if (debug) { writeln('draw y:'+JSON.stringify(Object.keys(draw).join(','))); writeln('draw 1:'+JSON.stringify(Object.keys(draw[1]).join(','))); } for (var y in draw) for (var x in draw[y]) { if (display[y] === undefined) display[y] = []; display[y][x] = draw[y][x]; } if (debug) { writeln('draw 1:'+JSON.stringify(Object.keys(draw[1]).join(','))); writeln('=========== END ['+child.name+'] =============') } }) if (debug) { writeln('this.name:'+this.name); writeln('this.y:'+this.y); writeln('display now:'+Object.keys(display[this.y]).join(',')); writeln('********* END ['+this.name+'] *********'); } return display; } Window.prototype.debug = function(text) { return '- '+text+': '+this.name+'('+this.x+'->'+(this.bx)+') width:'+this.width+' ['+this.y+'=>'+this.by+'] with z:'+this.z; }; /** * Render this window * * @param start - (int) Starting x position * @param end - (int) Ending x position * @param y - (int) Line to render * @param color - (bool) Whether to include color * @returns {{x: number, content: string}} */ Window.prototype.draw = function(start,end,y,color) { var content = ''; for (x=start;x<=end;x++) { var rx = this.ox+x; var ry = this.oy+y; // Check if we have an attribute to draw if (! (ry in this.content) || ! (rx in this.content[ry])) { content += ' '; continue; } if (color === undefined || color === true) { // Only write a new attribute if it has changed if ((this.last === undefined) || (this.last !== this.content[ry][rx].attr)) { this.last = this.content[ry][rx].attr; content += (this.last === null ? BG_BLACK|LIGHTGRAY : this.last); } } try { content += (this.content[ry][rx].ch !== null ? this.content[ry][rx].ch : ' '); } catch (e) { writeln(e); writeln('---'); writeln('x:'+(x-this.x)); writeln('y:'+(y-this.y)); writeln('ox:'+this.ox); writeln('oy:'+this.oy); writeln('rx:'+rx); writeln('ry:'+ry); exit(); } } return { content: content, x: end - start + 1 }; } /** * DRAW a line for this Window * * @param startx - the start position to render this window [1..] * @param endx - the stop position to stop rendinering this window [1..] * @param y - the current windows line number [1..] * @param color - output color * @param debug - turn on debugging * * Other Attributes: * @param x - Our current X position * * When rendering, for each children on a y axis, the one with a character in x and the highest z wins. * * = Thus, if there is only 1 child on a y line, we render that child.width, with optional padding on the left/right. * * = If there are more children: * + build an array of each childs x starting position sorted by z (asc). * + if we need, we pad until the first child to rendered * + after the child is rendered, we pad on the right if it didnt render to window.width * * + we render the a.x -> a.bx, unless there are other children with a higher z and it's x < a.bx * + "x" keeps track of where we are * + repeat until all children are processed, repeating from the begining again if necessary where children with a lower z have a wider width */ Window.prototype.drawline = function(startx,endx,y,color,debug) { // Some sanity checking if (startx < 1) throw new Error('Nope, startx < 1:'+startx); if (endx < startx) throw new Error('Nope, endx:'+endx+' < startx:'+startx); if (y < 1) throw new Error('Nope, y < 1:'+y); // Advance x if required var x = startx; // Get any of our children var children = this.visibleChildren(); // Find children with something on this line. var that = this; var kids = children.filter(function(child) { return (y-that.y+1 >= child.y) && (y-that.y+1 <= child.by) && (child.x < endx) && (child.bx > startx); }); var draw; var content = ''; if (debug !== false && (y > debug)) { writeln() writeln('********* drawline for ['+this.name+'] ***********'); writeln('* chars :'+startx+'->'+endx); writeln('* line :'+y); writeln('* parents start line :'+(this.parent.y !== undefined ? this.parent.y : '-no parent-')); writeln(this.debug('drawline')); writeln('* we have children :'+children.length); children.forEach(function(child) { if (y > debug) writeln(child.debug('CHILD')); }) writeln('* relevant :'+kids.length); kids.forEach(function(child) { writeln(child.debug('KID')); }) writeln('*************************************'+'*'.repeat(this.name.length)); } var child = null; // Only 1 child, so we render the line with it if (kids.length === 1) { child = kids.pop(); if (debug !== false && (y > debug)) { writeln(': Only 1 child :'+child.name); } if (child.x > x) { if (debug !== false && (y > debug)) { writeln(': Padding until :'+x+'->'+(endx < child.x-1 ? endx : child.x-1)+' on:'+(y-this.y+1)+' with:'+this.name); writeln(': Padded :'+((endx < child.x-1 ? endx : child.x-1)-x)); } draw = this.draw(x,endx < child.x-1 ? endx : child.x-1,y-this.y+1,color); content += draw.content; x += draw.x; if (debug !== false && (y > debug)) writeln(); } if (debug !== false && (y > debug)) { writeln(child.debug('THIS')); writeln(': x :'+x); writeln(': endx :'+endx); writeln(': size of child :'+(child.bx-child.x+1)); writeln(': draw :'+(x-child.x+1)+'->'+((endx-startx+1 < child.width ? endx : child.bx)-child.x+1)+' on:'+(y-this.y+1)) } if (x < endx) { draw = child.drawline(x-child.x+1,(endx < child.bx ? endx : child.bx)-child.x+1,y-this.y+1,color,(debug !== false ? debug-this.y+1 : debug)); content += draw.content; x += draw.x; } if (debug !== false && (y > debug)) writeln(); } else if (kids.length !== 0) { if (debug !== false && (y > debug)) writeln('| multiple children :'+kids.length); // Sort the kids in x start order kids = kids.sort(function(a,b) { return (a.x < b.x) ? -1 : ((b.x < a.x) ? 1 : 0); }); if (debug !== false) kids.forEach(function(child) { if (y > debug) writeln(child.debug('SORT')); }) var c = 0; // Our kids index var C = null; // If we need to come back and reprocess the list while (c < kids.length) { child = kids[c++]; var drawendx = endx; if (debug !== false && (y > debug)) { writeln('| -------------- ['+child.name+'] -----------'); writeln('| x :'+x); writeln('| child.x :'+child.x); writeln('| child.bx:'+child.bx); writeln('| startx :'+startx); writeln(child.debug('CHILD')); } // If this child cannot be rendered, skip it (because it is underneath another window) if (x > child.bx) { if (debug !== false && (y > debug)) writeln('| skipping:'+child.name); continue; } // Pad the beginning of this child if (child.x > x) { if (debug !== false && (y > debug)) { writeln('| Padding :'+x+'->'+(child.x-1)+' for: '+this.name); } draw = this.draw(x,child.x-1,y-this.y+1,color); content += draw.content; x += draw.x; if (debug !== false && (y > debug)) writeln(); } /* // If this child cannot be rendered, skip it if (x < child.x) { if (debug !== false && (y > debug)) writeln('| skipping:'+child.name); continue; } */ if (kids[c] !== undefined) { if (debug !== false && (y > debug)) writeln(kids[c].debug('AFTER')); // If the next child has a higher z and the same x skip this one if ((kids[c].x === x) && (kids[c].z > child.z)) { if (debug !== false && (y > debug)) writeln('|- Skipping:'+child.name); C = kids[c].x; continue; } if (debug !== false && (y > debug)) writeln('| FILTERING: x le:'+x+' bx lt:'+(child.bx)+' and z gt:'+child.z); var nextkid = kids.filter(function(kid) { if (debug !== false && (y > debug)) writeln('| - EVAL: '+kid.name+': x:'+(kid.x)+' bx:'+(kid.bx)+' and z:'+kid.z); return ((kid.bx > x+startx-1)) && (kid.x < child.bx) && (kid.z > child.z); }); if (debug !== false && (y > debug)) writeln('| Got next children: '+nextkid.length); if (debug !== false) nextkid.forEach(function(child) { if (y > debug) writeln(child.debug('NEXT')); }) // If a next child who starts before we finish and has a higher z, we'll stop at that next child if (nextkid.length) { // If nextkid should already be showing, we'll skip to it if (x > nextkid[0].x) { C = nextkid[0].bx; if (debug !== false && (y > debug)) writeln('| NEXTKID should have started, skipping to it:'+nextkid[0].x+' (C:'+C+')'); continue; } drawendx = (endx-child.x+1 < nextkid[0].x-1 ? endx : nextkid[0].x-1); if (debug !== false && (y > debug)) { writeln('| x :'+x); writeln('| startx :'+startx); writeln('| childx :'+child.x); } if (debug !== false && (y > debug)) { writeln(nextkid[0].debug('NEXTKID')); writeln('| draw partial width of me:'+child.name+', drawendx:'+drawendx+' because nextkid starts:'+nextkid[0].name+' at:'+nextkid[0].x); } // If I need to continue after next kid, we'll come back if ((! C) && (nextkid[0].bx < child.bx)) { C = nextkid[0].bx; if (debug !== false && (y > debug)) writeln('| coming back to:'+child.name+' at:'+C); } // If C is set, we need to push it out. if (C) C = drawendx; // No next children } else { if (debug !== false && (y > debug)) { writeln('| x :'+x); writeln('| startx :'+startx); writeln('| endx :'+endx); writeln('| child :'+child.name); writeln('| childx :'+child.x); writeln('| childbx:'+child.bx); writeln('| calcsta:'+(endx-child.x+1)); } drawendx = (endx-child.x+1 < child.bx ? x+child.width : child.bx); if (debug !== false && (y > debug)) writeln('| draw full width of me:'+child.name+', from:'+(x-child.x+1)+' until:'+drawendx); } // No other children } else { if (debug !== false && (y > debug)) { writeln('| No other child'); writeln(child.debug('DRAW')); } /* // If there is a gap, we'll need to pad it. if (x < child.x) { write(':'.repeat(xx=child.x-x)); x += xx; } */ drawendx = (endx-child.x+1 < child.width ? endx : child.bx); if (debug !== false && (y > debug)) { writeln('| will render:'+child.name+', from:'+(x-child.x+1)+'->'+(drawendx-child.x+1)+' on:'+(y-this.y+1)); } } if (x < endx) { draw = child.drawline(x-child.x+1,(x < endx ? drawendx : child.bx)-child.x+1,y-this.y+1,color,(debug !== false ? debug-this.y+1 : debug)); content += draw.content; x += draw.x; } if (debug !== false && (y > debug)) { writeln(); writeln('| C :'+C); writeln('| x :'+x); } if (C && (x > C)) { c = 0; C = null; if (debug !== false && (y > debug)) writeln('! Resetting c back'); } } } if (debug !== false && (y > debug)) { writeln('= x :'+x); writeln('= startx :'+startx); } // Pad the beginning of this child if (x < startx) { if (debug !== false && (y > debug)) writeln('! Padding until:'+this.x); write(' '.repeat(xx=this.x-startx)); x += xx; } // Render our item if (debug !== false && (y > debug)) writeln('= drawing:'+this.name+' ('+x+'->'+endx+')'); draw = this.draw(x,endx,y-this.y+1,color); content += draw.content; x += draw.x; if (debug !== false && (y > debug)) { writeln(); writeln('- DONE, x now :'+x+' (endx:'+endx+') for:'+this.name); writeln('- DREW :'+(x-startx)); writeln('************** end for ['+this.name+'] ***********'); } if (debug !== false && y > debug+1) exit(); return { content: content, x: endx-startx+1 }; } // Return the visible children (child should have sort by z) Window.prototype.visibleChildren = function() { return this.child.filter(function(child) { return child.visible; }); } init.apply(this,arguments); } /** * Each character in a window * * @todo Need to add a Viewdata implementation * @param ch * @param attr * @param ext - tex = ANSItex, vtx = ViewDasta * @constructor */ function Char(ch,attr) { this.__properties__ = { attr: attr, // - Attributes for the character (ie: color) ch: ch, // - Character to be shown //changed: false, // - Has this ch or attr been changed? }; /** * Return the color codes required to draw the current character * * @todo Implement Viewdata * @param last - last rendered char * @param ext - service we are rendering for * @param debug - debug mode * @returns {string|undefined} */ Char.prototype.attribute = function(last,ext,debug) { // If our attr is undefined, we'll return if (this.attr === undefined) return; // @todo This condition appears to fail? if (this.attr === null) throw new Error('Attributes shouldnt be null'); var ansi = []; var c; var l; var r = ''; if (debug) { writeln(); writeln('- last:'+last+', this:'+this.attr); } switch (ext) { case 'tex': if (debug) { writeln(' - this BG_BLACK:'+(this.attr & BG_BLACK)); writeln(' - last BG_BLACK:'+(last & BG_BLACK)); writeln(' - this HIGH:'+(this.attr & HIGH)); writeln(' - last HIGH:'+(last & HIGH)); writeln(' - this BLINK:'+(this.attr & BLINK)); writeln(' - last BLINK:'+(last & BLINK)); } if ( (((this.attr & BG_BLACK) !== (last & BG_BLACK)) || ((this.attr & HIGH) !== (last & HIGH)) || ((this.attr & BLINK) !== (last & BLINK)))) { ansi.push('0'); last = BG_BLACK|LIGHTGRAY; } if ((this.attr & HIGH) && ((this.attr & HIGH) !== (last & HIGH))) { ansi.push('1'); } if ((this.attr & BLINK) && ((this.attr & BLINK) !== (last & BLINK))) { ansi.push('5'); } c = (this.attr & 0x07); l = (last & 0x07); // Foreground switch (c) { case BLACK: r = 30; break; case RED: r = 31; break; case GREEN: r = 32; break; case BROWN: r = 33; break; case BLUE: r = 34; break; case MAGENTA: r = 35; break; case CYAN: r = 36; break; case LIGHTGRAY: r = 37; break; } //writeln('r:'+r+', l:'+l+', c:'+c); if (r && (c !== l)) ansi.push(r); // Background if (this.attr & 0x70) { c = (this.attr & 0x70); l = (last & 0x70); switch (this.attr & 0x70) { case BG_BLACK: r = 40; break; case BG_RED: r = 41; break; case BG_GREEN: r = 42; break; case BG_BROWN: r = 43; break; case BG_BLUE: r = 44; break; case BG_MAGENTA: r = 45; break; case BG_CYAN: r = 46; break; case BG_LIGHTGRAY: r = 47 break; } if (r && (c !== l)) ansi.push(r); } if (false && debug) writeln(' - ansi:'+ansi); return ansi.length ? (debug ? '': '\x1b')+'['+ansi.join(';')+'m' : undefined; case 'vtx': log(LOG_DEBUG,'+ last:'+last+', attr ('+this.attr+')'); switch (this.attr) { // \x08 case BLINK: r = VIEWDATA_BLINK; break; // \x09 case STEADY: r = VIEWDATA_STEADY; break; // \x0c case NORMAL: r = VIEWDATA_NORMAL; break; // \x0d case DOUBLE: r = VIEWDATA_DOUBLE; break; // \x18 case CONCEAL: r = VIEWDATA_CONCEAL; break; // \x19 case BLOCKS: r = VIEWDATA_BLOCKS; break; // \x1a case SEPARATED: r = VIEWDATA_SEPARATED; break; // \x1c case BLACKBACK: r = VIEWDATA_BLACKBACK; break; // \x1d case NEWBACK: r = VIEWDATA_NEWBACK; break; // \x1e case HOLD: r = VIEWDATA_HOLD; break; // \x1f case RELEASE: r = VIEWDATA_REVEAL; break; // Not handled // \x0a-b,\x0e-f,\x1b case 0xff00: return '?'; default: var mosiac = (this.attr & MOSIAC); c = (this.attr & 0x07); // Color control \x00-\x07, \x10-\x17 switch (c) { case BLACK: r = VIEWDATA_BLACKBACK; break; case RED: r = mosiac ? VIEWDATA_MOSIAC_RED : VIEWDATA_RED; break; case GREEN: r = mosiac ? VIEWDATA_MOSIAC_GREEN : VIEWDATA_GREEN; break; case BROWN: r = mosiac ? VIEWDATA_MOSIAC_YELLOW : VIEWDATA_YELLOW; break; case BLUE: r = mosiac ? VIEWDATA_MOSIAC_BLUE : VIEWDATA_BLUE; break; case MAGENTA: r = mosiac ? VIEWDATA_MOSIAC_MAGENTA : VIEWDATA_MAGENTA; break; case CYAN: r = mosiac ? VIEWDATA_MOSIAC_CYAN : VIEWDATA_CYAN; break; case LIGHTGRAY: r = mosiac ? VIEWDATA_MOSIAC_WHITE : VIEWDATA_WHITE; break; default: log(LOG_DEBUG,'Not a color?:'+c); return '?'; } } log(LOG_DEBUG,'= result:'+r.charCodeAt(0)+', ('+r+')'); return ESC+r; default: throw new Error(ext+': has not been implemented'); } }; Char.prototype.__defineGetter__('attr',function() { return this.__properties__.attr; }); Char.prototype.__defineGetter__('ch',function() { return this.__properties__.ch; }); Char.prototype.__defineSetter__('ch',function(char) { if (typeof char !== 'string') throw new Error('ch is not a string') if (char.length !== 1) throw new Error('ch can only be 1 character'); this.__properties__.ch = char; }) }