Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
126 changes: 117 additions & 9 deletions debugger.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,102 @@ var file_get_contents = function(f,mode){return (!file_exists(f))? '' : require(
var get_constr = function(v){ return(v===null)?"[object Null]":Object.prototype.toString.call(v); };
// var fnprefix = (["FUNCTION"].concat(process.hrtime()).concat(process.hrtime())).join('.'); // ONLY WORKS IN LATER Vs
var fnprefix = 'TYPE_FUNC_'+(new Date().getTime());

var util = require("util");
var stringify = require('./stringify.js');
var jsencr = function(o){ var e = []; return stringify(o, function(k,v){
if(typeof(v)==='function') return fnprefix+v.toString();
if(typeof(v)!=='object' || v===null) return v;
for(var i in e){ if(e[i]===v){ return "Circular"; }};
e.push(v); return v;
}); };

function objGetPropertyDescriptor(obj, key) {
while(true) {
var desc = Object.getOwnPropertyDescriptor(obj, key);
if(desc) return desc;
obj = Object.getPrototypeOf(obj);
if(obj === Object) return null;
}
}

var jsencr = function(o, rawRoot) {
function objDescription(o) {
var value = {};
var id = objCache.register(o);
value.type = "object";
value.typename = "Object";
if(o.constructor && o.constructor.name)
value.typename = o.constructor.name;
value.str = "[" + value.typename + "]";
value.objid = id;
value.keys = [];
value.attribs = []; // each item is a 2-tuple. _[0] says whether this is static. if static, _[1] is the value.
for(var key in o) {
// Add all keys which are strings or int. Otherwise, don't for now for simplification.
if(!util.isString(key) && !util.isNumber(key)) continue;
value.keys.push(key);
var prop = objGetPropertyDescriptor(o, key);
if(!prop || !prop.get)
value.attribs.push([true, o[key]]);
else
value.attribs.push([false, undefined]);
}
return value;
}
function funcDesc(f) {
var value = {};
value.type = "function";
value.typename = "Function";
value.name = f.name;
if(value.name) value.str = "[Function: " + f.name + "]";
else value.str = "[Function]";
return value;
}
var e = [];
return stringify(o, function(k,v){
if(typeof(v)==='function') return funcDesc(o);
if(typeof(v)!=='object' || v===null) return v;
for(var i in e){ if(e[i]===v){ return "Circular"; }};
e.push(v);
if(util.isArray(v)) return v;
if(v === o && rawRoot) return v;
return objDescription(v);
});
};

var assert = require("assert");

function getRandomId() {
return Math.floor((new Date()).valueOf() + new Date(2000,0,0).valueOf()*Math.random());
}

function createObjCache() {
var objToId = new WeakMap();
var idToObj = new Map();

return {
register: register,
get: get
};

function register(obj) {
if(objToId.has(obj)) return objToId.get(obj);
var newId;
while(true) {
newId = getRandomId();
if(!idToObj.has(newId)) break;
}
objToId.set(obj, newId);
idToObj.set(newId, obj);
return newId;
}

function get(id) {
return idToObj.get(Number(id));
}
}

var objCache = createObjCache();

function clear() {
// reset obj cache
objCache = createObjCache();
}

var dbg = {

Expand Down Expand Up @@ -41,7 +129,8 @@ var dbg = {
broadcastSSE: function(t, a){
clearTimeout(dbg.pendingBroadcast);

var data = { t:t, a:a };
a = Array.prototype.slice.call(a, 0);; // make it an array
var data = [t,a];
dbg.queued.push(data);

var sendFn = function(){
Expand Down Expand Up @@ -151,16 +240,35 @@ var dbg = {
r.type = (typeof(r.cnt)==='object' && r.cnt!==null) ?
get_constr(r.cnt) :
typeof(r.cnt);
}catch(e){ r.error=e.toString(); }
}catch(e){ r.error= "" + (e.stack || e); }

s.end(jsencr(r));
s.end(jsencr(r, true));
} else if(typeof(post.getsug)==='string'){

try{ var r = jsencr(dbg.getsug(JSON.parse(post.getsug))); }
catch(e){ var r = "[]"; }

s.writeHead(200, {'Content-type': dbg.mimes['txt'], 'Content-length': r.length});
s.end(r);
} else if(post.clear) {
clear();
s.end();
} else if(post.dynget) {
var objid = post.dynget;
var key = post.key;
var r = {error:false};

try{
var obj = objCache.get(objid);
if(!obj)
r.error = "Object not found. Probably we have restarted the session.";
else
r.cnt = obj[key];
} catch(e) {
r.error= "" + (e.stack || e);
}

s.end(jsencr(r, true));
}else{
return dbg.serve500(s,'Command was not found');
}
Expand Down
39 changes: 20 additions & 19 deletions scripts/console.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,42 +14,43 @@ $(document).ready(function(){
var encd = function(v){ return $('<div />').text(v).html();};
var decd = function(v){ return $('<div />').html(v).text();};

window.clearConsole = function(){ c.val(''); return vw.html('');};

var treefiy_obj = function(o, n, c){
var r = { data:"<span class='fn' title='"+n+"'>"+n+"</span>", state:!!c ? 'open' : 'closed', children:[] };
for(var i in o){
if(typeof(o[i])==='object' && o[i]!==null)
o[i] = treefiy_obj(o[i], i);
else{
var val = encd(o[i]);
// if(val.length>100) val = '<span>'+val.substr(0,100)+'... (length: '+val.length+')</span>';
o[i] = "<span class='fn' title='"+i+"'>"+i+"</span><span class='undef'>"+val+'</span>';
};
r.children.push(o[i]);
};
return r;
};
window.encodeHTML = function(s) {
s = s.replace(/&/g, '&amp;');
s = s.replace(/</g, '&lt;');
s = s.replace(/"/g, '&quot;');
s = s.replace(/(\r\n|\n|\r)/gm, '<br>');
s = s.replace(/ /g, '&nbsp;');
s = s.replace(/\t/g, '&emsp;'); // not exactly...
return s;
}

window.clearConsole = function() {
$.ajax({url:'./', type:'POST', dataType:'text', data:{'clear':true}, complete:function(r) {
if(r.status!==200) return showAnError('Bad response from server ('+r.status+')');

c.val('');
return vw.html('');
}});
};
window.focusLastMessage = function(){ vwscr.scrollTo(0,vw.height()); };

window.showAnError = function(err, type){
if(typeof(type)!=='string') type='error';
var resp = $('<div class="'+type+' output"> </div>').appendTo(vw);
resp.html(' <span class="eicon">W</span> '+encd(err));
resp.html(' <span class="eicon">W</span> '+encodeHTML(err));

focusLastMessage();
};

window.showAResponse = function(r){
var resp = $('<div class="output"></div>').appendTo(vw);
var tpo = typeof(r.cnt);
if(typeof(r.fnprefix)==='string') window.fnprefix = r.fnprefix;
var tpo = typeof(r.cnt);
if(r.cnt===null) tpo = 'null';

switch(tpo){
case 'object':
createTreeFromObj({'[object Object]':r.cnt}, $('.autoexpand').is('.sel')).appendTo(resp);
createTreeFromObj(r.cnt, $('.autoexpand').is('.sel')).appendTo(resp);
break;
default:
$(formatStaticValue(r.cnt,false)).appendTo(resp);
Expand Down
142 changes: 112 additions & 30 deletions scripts/localtree.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ window.formatStaticValue = function(data, nobrk){
var encd = function(v){ return $('<div />').text(v).html();};
var fnprefix = typeof(window.fnprefix)=='string' ? window.fnprefix : '{'+(new Date().getTime()) + '#!@';

if(type==='object') return false;
if(data===null) type='null';
if(type==='string' && typeof fnprefix !== 'undefined' && data.indexOf(fnprefix+'function')===0 && data.lastIndexOf(fnprefix+'function')===0){
type='function';
Expand All @@ -21,50 +20,133 @@ window.formatStaticValue = function(data, nobrk){
return '<span class="'+type+'">'+(typeof(data)!=='boolean'?data:(!!data?'true':'false'))+'</span>';
case 'string':
case 'function':
var d = encd(data.toString());
if(!!!nobrk) d = d.replace(/\r\n/gmi,"<br />").replace(/\n/gmi,"<br />").replace(/\t/gmi,tabSpaces);
var d = encodeHTML(data.toString());

var r = '<span class="str'+(!!nobrk?' nobrk':'')+'">' + (d)+'</span>';
return (type=='string' ? '<span>&quot;</span>'+r+'<span>&quot;</span>' : r);
default:
return '<span class="undef">unknown '+type+'</span>';
};
};

window.createTreeFromObj = function(obj,autoexpand){
function appendAttrib(k, d, container, autoexpand) {
if(typeof(d)!=='object' || d===null){
$('<div class="header"><span class="fn">'+k+'</span>'+formatStaticValue(d,false) +' </div>').appendTo(container);
}else if(d.type == "function"){
$('<div class="header"><span class="fn">'+k+'</span><span class="str">'+encodeHTML(d.str)+' </span></div>').appendTo(container);
}else{
var li = $('<div class="expandable"></div>').appendTo(container);
var hdr = $('<div class="header"></div>').appendTo(li);
var arrow = $('<span class="arrow-right arrow-collapsed">&#9658;</span>').appendTo(hdr);
var key = $('<span class="fn">' + k +'</span>').appendTo(hdr);
var descStr = d.str;
if(!descStr && d.constructor) descStr = "[" + d.constructor.name + "]";
if(!descStr) descStr = "<unknown object>";
var desc = $('<span class="str">' + encodeHTML(descStr) + ' </span>').appendTo(hdr);

var expand = function(){
var tgt = li.find('>.object');
if(tgt.length && tgt.is(':visible')){
tgt.hide();
arrow.removeClass('arrow-expanded').addClass('arrow-collapsed').html('&#9658;');
}else{
if(!tgt.length) tgt = createTreeFromObj(d).appendTo(li);
tgt.show();
arrow.removeClass('arrow-collapsed').addClass('arrow-expanded').html('&#9660;');
};
if(typeof(doResizeWin)=='function') doResizeWin();
};

arrow.click(function(){ if(!$('.dotstruct').is('.sel')) expand(); });
hdr.click(function(){ if($('.dotstruct').is('.sel')) expand(); });
if(!!autoexpand) expand();
};
}


window.createTreeFromDynObj = function(obj,autoexpand){
if(typeof(obj)!=='object' || null==obj)
return false;

var ul = $('<ul class="object"></ul>');
var keys = []; for(var i in obj) keys.push(i); if(typeof(obj.length)=='undefined') keys.sort();

for(var i=0; i<keys.length; i++)(function(d,k){
if(obj.type == "function")
return '<span class="str">'+encodeHTML(obj.str)+'</span>';

var ul = $('<ul class="object"></ul>');

if(typeof(d)!=='object' || d===null){
$('<li class="nobrk header"><span class="fn">'+k+'</span> '+formatStaticValue(d,1) +' </li>').appendTo(ul);
}else{
var li = $('<li class="nobrk expandable"></li>').appendTo(ul);
function appendAttribGeneric(k, attr) {
var li = $('<li class="nobrk"></li>').appendTo(ul);

if(!attr[0]) {
var hdr = $('<div class="header"></div>').appendTo(li);
var arrow = $('<span class="arrow-right arrow-collapsed">&#9658;</span>').appendTo(hdr);
var key = $('<span class="fn">' + k +'</span>').appendTo(hdr)
var getCmd = $('<span class="tool">&lt;get&gt;</span>').appendTo(hdr);

var expand = function(){
var tgt = li.find('>.object');
if(tgt.length && tgt.is(':visible')){
tgt.hide();
arrow.removeClass('arrow-expanded').addClass('arrow-collapsed').html('&#9658;');
}else{
if(!tgt.length) tgt = createTreeFromObj(d).appendTo(li);
tgt.show();
arrow.removeClass('arrow-collapsed').addClass('arrow-expanded').html('&#9660;');
};
if(typeof(doResizeWin)=='function') doResizeWin();
};
function showError(msg) {
var resp = $('<div class="error"></div>').appendTo(hdr);
resp.html(' <span class="eicon">W</span> ' + encodeHTML(msg));
}

arrow.click(function(){ if(!$('.dotstruct').is('.sel')) expand(); });
hdr.click(function(){ if($('.dotstruct').is('.sel')) expand(); });
if(!!autoexpand) expand();
};
function getAttrib() {
getCmd.remove();
var waitSpan = $("<span>...</span>").appendTo(hdr);

$.ajax({url:'./', type:'POST', dataType:'text', data:{dynget:obj.objid, key:k}, complete:function(r){
waitSpan.remove(); // remove old

if(r.status!==200) {
showError("Bad server response " + r.status);
return;
}

try{ var pa = JSON.parse(r.responseText); }catch(e){
showError('Failed to parse response ('+e+')');
return;
};

if(pa.error) {
showError(pa.error);
return;
}

hdr.remove();
appendAttrib(k, pa.cnt, li, true);

// re-add get-cmd to new header
hdr = li.find(">div")[0];
tgt = li.find(".header")[0];
getCmd.text("<re-get>");
getCmd.appendTo(tgt);
getCmd.click(getAttrib);
}});
};
getCmd.click(getAttrib);
hdr.click(getAttrib);
return;
}

}(obj[keys[i]], keys[i]));
var d = attr[1];
appendAttrib(k, d, li, autoexpand);
}

for(var i=0; i<obj.keys.length; i++)
appendAttribGeneric(obj.keys[i], obj.attribs[i]);

return ul;
}


window.createTreeFromRawObj = function(obj, autoexpand) {
var ul = $('<ul class="object"></ul>');
for(var key in obj) {
var li = $('<li class="nobrk"></li>').appendTo(ul);
appendAttrib(key, obj[key], li, autoexpand);
}
return ul;
}

window.createTreeFromArray = window.createTreeFromRawObj;

window.createTreeFromObj = function(obj, autoexpand) {
if(Array.isArray(obj)) return window.createTreeFromArray(obj, autoexpand);
return createTreeFromDynObj(obj, autoexpand);
}
Loading