Telegram Web, preconfigured for usage in I2P. http://web.telegram.i2p/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

294 lines
8.1 KiB

(function(window, document, io) {
var CONTEXT_URL = 'context.html';
var VERSION = '%KARMA_VERSION%';
var KARMA_URL_ROOT = '%KARMA_URL_ROOT%';
// connect socket.io
// https://github.com/LearnBoost/Socket.IO/wiki/Configuring-Socket.IO
var socket = io.connect('http://' + location.host, {
'reconnection delay': 500,
'reconnection limit': 2000,
'resource': KARMA_URL_ROOT.substr(1) + 'socket.io',
'sync disconnect on unload': true,
'max reconnection attempts': Infinity
});
var browsersElement = document.getElementById('browsers');
socket.on('info', function(browsers) {
var items = [], status;
for (var i = 0; i < browsers.length; i++) {
status = browsers[i].isReady ? 'idle' : 'executing';
items.push('<li class="' + status + '">' + browsers[i].name + ' is ' + status + '</li>');
}
browsersElement.innerHTML = items.join('\n');
});
socket.on('disconnect', function() {
browsersElement.innerHTML = '';
});
var titleElement = document.getElementById('title');
var bannerElement = document.getElementById('banner');
var updateStatus = function(status) {
return function(param) {
var paramStatus = param ? status.replace('$', param) : status;
titleElement.innerHTML = 'Karma v' + VERSION + ' - ' + paramStatus;
bannerElement.className = status === 'connected' ? 'online' : 'offline';
};
};
socket.on('connect', updateStatus('connected'));
socket.on('disconnect', updateStatus('disconnected'));
socket.on('reconnecting', updateStatus('reconnecting in $ ms...'));
socket.on('reconnect', updateStatus('re-connected'));
socket.on('reconnect_failed', updateStatus('failed to reconnect'));
var instanceOf = function(value, constructorName) {
return Object.prototype.toString.apply(value) === '[object ' + constructorName + ']';
};
/* jshint unused: false */
var Karma = function(socket, context, navigator, location) {
var hasError = false;
var store = {};
var self = this;
var resultsBufferLimit = 1;
var resultsBuffer = [];
this.VERSION = VERSION;
this.config = {};
this.setupContext = function(contextWindow) {
if (hasError) {
return;
}
var getConsole = function(currentWindow) {
return currentWindow.console || {
log: function() {},
info: function() {},
warn: function() {},
error: function() {},
debug: function() {}
};
};
contextWindow.__karma__ = this;
// This causes memory leak in Chrome (17.0.963.66)
contextWindow.onerror = function() {
return contextWindow.__karma__.error.apply(contextWindow.__karma__, arguments);
};
contextWindow.onbeforeunload = function(e, b) {
if (context.src !== 'about:blank') {
// TODO(vojta): show what test (with explanation about jasmine.UPDATE_INTERVAL)
contextWindow.__karma__.error('Some of your tests did a full page reload!');
}
};
// patch the console
var localConsole = contextWindow.console = getConsole(contextWindow);
var browserConsoleLog = localConsole.log;
var logMethods = ['log', 'info', 'warn', 'error', 'debug'];
var patchConsoleMethod = function(method) {
var orig = localConsole[method];
if (!orig) {
return;
}
localConsole[method] = function() {
self.log(method, arguments);
return Function.prototype.apply.call(orig, localConsole, arguments);
};
};
for (var i = 0; i < logMethods.length; i++) {
patchConsoleMethod(logMethods[i]);
}
contextWindow.dump = function() {
self.log('dump', arguments);
};
contextWindow.alert = function(msg) {
self.log('alert', [msg]);
};
};
this.log = function(type, args) {
var values = [];
for (var i = 0; i < args.length; i++) {
values.push(this.stringify(args[i], 3));
}
this.info({log: values.join(', '), type: type});
};
this.stringify = function(obj, depth) {
if (depth === 0) {
return '...';
}
if (obj === null) {
return 'null';
}
switch (typeof obj) {
case 'string':
return '\'' + obj + '\'';
case 'undefined':
return 'undefined';
case 'function':
return obj.toString().replace(/\{[\s\S]*\}/, '{ ... }');
case 'boolean':
return obj ? 'true' : 'false';
case 'object':
var strs = [];
if (instanceOf(obj, 'Array')) {
strs.push('[');
for (var i = 0, ii = obj.length; i < ii; i++) {
if (i) {
strs.push(', ');
}
strs.push(this.stringify(obj[i], depth - 1));
}
strs.push(']');
} else if (instanceOf(obj, 'Date')) {
return obj.toString();
} else if (instanceOf(obj, 'Text')) {
return obj.nodeValue;
} else if (instanceOf(obj, 'Comment')) {
return '<!--' + obj.nodeValue + '-->';
} else if (obj.outerHTML) {
return obj.outerHTML;
} else {
strs.push(obj.constructor.name);
strs.push('{');
var first = true;
for(var key in obj) {
if (obj.hasOwnProperty(key)) {
if (first) { first = false; } else { strs.push(', '); }
strs.push(key + ': ' + this.stringify(obj[key], depth - 1));
}
}
strs.push('}');
}
return strs.join('');
default:
return obj;
}
};
var clearContext = function() {
context.src = 'about:blank';
};
// error during js file loading (most likely syntax error)
// we are not going to execute at all
this.error = function(msg, url, line) {
hasError = true;
socket.emit('error', url ? msg + '\nat ' + url + (line ? ':' + line : '') : msg);
this.complete();
return false;
};
this.result = function(result) {
if (resultsBufferLimit === 1) {
return socket.emit('result', result);
}
resultsBuffer.push(result);
if (resultsBuffer.length === resultsBufferLimit) {
socket.emit('result', resultsBuffer);
resultsBuffer = [];
}
};
this.complete = function(result) {
if (resultsBuffer.length) {
socket.emit('result', resultsBuffer);
resultsBuffer = [];
}
// give the browser some time to breath, there could be a page reload, but because a bunch of
// tests could run in the same event loop, we wouldn't notice.
setTimeout(function() {
socket.emit('complete', result || {});
clearContext();
}, 0);
};
this.info = function(info) {
socket.emit('info', info);
};
// all files loaded, let's start the execution
this.loaded = function() {
// has error -> cancel
if (!hasError) {
this.start(this.config);
}
// remove reference to child iframe
this.start = null;
};
this.store = function(key, value) {
if (typeof value === 'undefined') {
return store[key];
}
if (Object.prototype.toString.apply(value) === '[object Array]') {
var s = store[key] = [];
for (var i = 0; i < value.length; i++) {
s.push(value[i]);
}
} else {
// TODO(vojta): clone objects + deep
store[key] = value;
}
};
// supposed to be overriden by the context
// TODO(vojta): support multiple callbacks (queue)
this.start = this.complete;
socket.on('execute', function(cfg) {
// reset hasError and reload the iframe
hasError = false;
self.config = cfg;
context.src = CONTEXT_URL;
// clear the console before run
// works only on FF (Safari, Chrome do not allow to clear console from js source)
if (window.console && window.console.clear) {
window.console.clear();
}
});
// report browser name, id
socket.on('connect', function() {
var transport = socket.socket.transport.name;
// TODO(vojta): make resultsBufferLimit configurable
if (transport === 'websocket' || transport === 'flashsocket') {
resultsBufferLimit = 1;
} else {
resultsBufferLimit = 50;
}
socket.emit('register', {
name: navigator.userAgent,
id: parseInt((location.search.match(/\?id=(.*)/) || [])[1], 10) || null
});
});
};
window.karma = new Karma(socket, document.getElementById('context'), window.navigator, window.location);
})(window, document, window.io);