Skip to content

Commit

Permalink
console
Browse files Browse the repository at this point in the history
  • Loading branch information
estelle committed Mar 19, 2012
1 parent 4172f8d commit e71ddd3
Show file tree
Hide file tree
Showing 14 changed files with 3,518 additions and 0 deletions.
164 changes: 164 additions & 0 deletions console/EventSource.js
@@ -0,0 +1,164 @@
;(function (global) {

if ("EventSource" in window) return;

var reTrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g;

var EventSource = function (url) {
var eventsource = this,
interval = 500, // polling interval
lastEventId = null,
cache = '';

if (!url || typeof url != 'string') {
throw new SyntaxError('Not enough arguments');
}

this.URL = url;
this.readyState = this.CONNECTING;
this._pollTimer = null;
this._xhr = null;

function pollAgain() {
eventsource._pollTimer = setTimeout(function () {
poll.call(eventsource);
}, interval);
}

function poll() {
try { // force hiding of the error message... insane?
if (eventsource.readyState == eventsource.CLOSED) return;

var xhr = new XMLHttpRequest();
xhr.open('GET', eventsource.URL, true);
xhr.setRequestHeader('Accept', 'text/event-stream');
xhr.setRequestHeader('Cache-Control', 'no-cache');

// we must make use of this on the server side if we're working with Android - because they don't trigger
// readychange until the server connection is closed
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');

if (lastEventId != null) xhr.setRequestHeader('Last-Event-ID', lastEventId);
cache = '';

xhr.timeout = 50000;
xhr.onreadystatechange = function () {
if ((this.readyState == 3 || this.readyState == 4) && this.status == 200) {
// on success
if (eventsource.readyState == eventsource.CONNECTING) {
eventsource.readyState = eventsource.OPEN;
eventsource.dispatchEvent('open', { type: 'open' });
}

// process this.responseText
var parts = this.responseText.substr(cache.length).split("\n"),
data = [],
i = 0,
line = '';

cache = this.responseText;

// TODO handle 'event' (for buffer name), retry
for (; i < parts.length; i++) {
line = parts[i].replace(reTrim, '');
if (line.indexOf('data') == 0) {
data.push(line.replace(/data:?\s*/, ''));
} else if (line.indexOf('id:') == 0) {
lastEventId = line.replace(/id:?\s*/, '');
} else if (line.indexOf('id') == 0) { // this resets the id
lastEventId = null;
} else if (line == '') {
if (data.length) {
var event = new MessageEvent(data.join('\n'), eventsource.url, lastEventId);
eventsource.dispatchEvent('message', event);
data = [];
}
}
}

if (this.readyState == 4) pollAgain();
// don't need to poll again, because we're long-loading
} else if (eventsource.readyState !== eventsource.CLOSED) {
if (this.readyState == 4) { // and some other status
// dispatch error
eventsource.readyState = eventsource.CONNECTING;
eventsource.dispatchEvent('error', { type: 'error' });
pollAgain();
} else if (this.readyState == 0) { // likely aborted
pollAgain();
}
}
};

xhr.send();

setTimeout(function () {
if (true || xhr.readyState == 3) xhr.abort();
}, xhr.timeout);

eventsource._xhr = xhr;

} catch (e) { // in an attempt to silence the errors
eventsource.dispatchEvent('error', { type: 'error', data: e.message }); // ???
}
};

poll(); // init now
};

EventSource.prototype = {
close: function () {
// closes the connection - disabling the polling
this.readyState = this.CLOSED;
clearInterval(this._pollTimer);
this._xhr.abort();
},
CONNECTING: 0,
OPEN: 1,
CLOSED: 2,
dispatchEvent: function (type, event) {
var handlers = this['_' + type + 'Handlers'];
if (handlers) {
for (var i = 0; i < handlers.length; i++) {
handlers.call(this, event);
}
}

if (this['on' + type]) {
this['on' + type].call(this, event);
}
},
addEventListener: function (type, handler) {
if (!this['_' + type + 'Handlers']) {
this['_' + type + 'Handlers'] = [];
}

this['_' + type + 'Handlers'].push(handler);
},
removeEventListener: function () {
// TODO
},
onerror: null,
onmessage: null,
onopen: null,
readyState: 0,
URL: ''
};

var MessageEvent = function (data, origin, lastEventId) {
this.data = data;
this.origin = origin;
this.lastEventId = lastEventId || '';
};

MessageEvent.prototype = {
data: null,
type: 'message',
lastEventId: '',
origin: ''
};

if ('module' in global) module.exports = EventSource;
global.EventSource = EventSource;

})(this);
82 changes: 82 additions & 0 deletions console/codecomplete.js
@@ -0,0 +1,82 @@
var ccCache = {};
var ccPosition = false;

function getProps(cmd) {
var surpress = {};

if (!ccCache[cmd]) {
try {
// surpress alert boxes because they'll actually do something when we're looking
// up properties inside of the command we're running
surpress.alert = sandboxframe.contentWindow.alert;
sandboxframe.contentWindow.alert = function () {};

// loop through all of the properties available on the command (that's evaled)
ccCache[cmd] = sandboxframe.contentWindow.eval('console.props(' + cmd + ')').sort();

// return alert back to it's former self
sandboxframe.contentWindow.alert = surpress.alert;
} catch (e) {
ccCache[cmd] = [];
}

// if the return value is undefined, then it means there's no props, so we'll
// empty the code completion
if (ccCache[cmd][0] == 'undefined') ccOptions[cmd] = [];
ccPosition = 0;
}

return ccCache[cmd];
}

function codeComplete(event) {
var cmd = cursor.textContent.split(/[;\s]+/g).pop(),
which = whichKey(event),
cc,
props = [];

if (cmd) {
if (cmd.substr(-1) == '.') {
// get the command without the '.' so we can eval it and lookup the properties
cmd = cmd.substr(0, cmd.length - 1);

props = getProps(cmd);
} else {
props = getProps(cmd);
}

if (props.length) {
if (which == 9) { // tabbing cycles through the code completion
if (event.shiftKey) {
// backwards
ccPosition = ccPosition == 0 ? props.length - 1 : ccPosition-1;
} else {
ccPosition = ccPosition == props.length - 1 ? 0 : ccPosition+1;
}

} else {
ccPosition = 0;
}

// position the code completion next to the cursor
if (!cursor.nextSibling) {
cc = document.createElement('span');
cc.className = 'suggest';
exec.appendChild(cc);
}

cursor.nextSibling.innerHTML = props[ccPosition];
exec.value = exec.textContent;

if (which == 9) return false;
}
} else {
ccPosition = false;
}

if (ccPosition === false && cursor.nextSibling) {
exec.removeChild(cursor.nextSibling);
}

exec.value = exec.textContent;
}

0 comments on commit e71ddd3

Please sign in to comment.