mirror of
https://github.com/wickedest/Mergely.git
synced 2026-03-20 09:38:39 +08:00
Mergely 3.0 that works with CodeMirror 3.0 beta.
This commit is contained in:
15
test/lib/jsunit/jsunit.css
Executable file
15
test/lib/jsunit/jsunit.css
Executable file
@@ -0,0 +1,15 @@
|
||||
body { font-family: courier new; font-size: 0.75em; }
|
||||
table { border-collapse: collapse; width: 100%; text-align: left; vertical-align: top; }
|
||||
|
||||
.stack span,
|
||||
.failed span,
|
||||
.passed span {
|
||||
width: 60px;
|
||||
display: block;
|
||||
text-align: center;
|
||||
font-size: 0.85em;
|
||||
border-radius: 4px; -moz-border-radius: 4px; -webkit-border-radius: 4px;
|
||||
}
|
||||
.failed span { background-color: #FFCC66; border: 1px solid black; color: black; cursor: help; }
|
||||
.passed span { background-color: #66BB66; border: 1px solid green; color: green; }
|
||||
.stack span { background-color: #efefef; border: 1px solid #444; color: #444; cursor: help; }
|
||||
198
test/lib/jsunit/jsunit.js
Executable file
198
test/lib/jsunit/jsunit.js
Executable file
@@ -0,0 +1,198 @@
|
||||
var JsUnit = (function () {
|
||||
var JsUnit = {
|
||||
modules : {},
|
||||
config : {
|
||||
onStartModule: function(module) { },
|
||||
onEndModule: function(module) { },
|
||||
onStartTest: function(module, test) { },
|
||||
onEndTest: function(module, test) { },
|
||||
onResult: function(module, test) { },
|
||||
}
|
||||
};
|
||||
JsUnit.PASSED = 'passed';
|
||||
JsUnit.FAILED = 'failed';
|
||||
|
||||
JsUnit._mixin = function(src, dest) {
|
||||
return $.extend({}, src, dest);
|
||||
}
|
||||
|
||||
JsUnit.module = function(name) {
|
||||
this.modules[name] = {name: name, tests:[]};
|
||||
this._module = name;
|
||||
}
|
||||
|
||||
JsUnit.test = function(name, run) {
|
||||
var module = this.modules[this._module];
|
||||
module.tests.push({
|
||||
name: name,
|
||||
run: run,
|
||||
assertions: []
|
||||
});
|
||||
}
|
||||
|
||||
JsUnit._assert = function(result, value, message, passed) {
|
||||
var stack = [];
|
||||
if (!passed) {
|
||||
if (printStackTrace != undefined) {
|
||||
var trace = printStackTrace();
|
||||
for (key in trace) {
|
||||
stack.push({
|
||||
method: trace[key].split('@')[0],
|
||||
location: trace[key].split('@')[1]
|
||||
});
|
||||
}
|
||||
}
|
||||
else {
|
||||
console.warn('missing required library:','https://raw.github.com/eriwen/javascript-stacktrace/master/stacktrace.js');
|
||||
}
|
||||
}
|
||||
this._running.assertions.push({
|
||||
expected: value,
|
||||
message: message,
|
||||
stack: stack,
|
||||
passed: function() { return passed; }
|
||||
});
|
||||
}
|
||||
|
||||
JsUnit.equal = function(result, value, message) {
|
||||
JsUnit._assert(result, value, message || 'Result was not equal as expected', result == value);
|
||||
}
|
||||
|
||||
JsUnit.notEqual = function(result, value, message) {
|
||||
JsUnit._assert(result, value, message || 'Result did not evaluate to false as expected', result != value);
|
||||
}
|
||||
|
||||
JsUnit.okay = function(result, message) {
|
||||
JsUnit._assert(result, null, message || 'Result did not evaluate to true as expected', !(!result));
|
||||
}
|
||||
JsUnit.throws = function(func, message) {
|
||||
try {
|
||||
func();
|
||||
}
|
||||
catch (err) {
|
||||
return;
|
||||
}
|
||||
JsUnit.okay(false, message || 'Did not throw as expected');
|
||||
}
|
||||
|
||||
JsUnit.run = function(config) {
|
||||
var settings = this._mixin(this.config, config);
|
||||
for (var mkey in this.modules) {
|
||||
var module = this.modules[mkey];
|
||||
settings.onStartModule(module);
|
||||
for (var tkey in module.tests) {
|
||||
var test = module.tests[tkey];
|
||||
var tthis = this;
|
||||
test.rerun = function() {
|
||||
try {
|
||||
settings.onStartTest(module, this);
|
||||
tthis._running = this;
|
||||
this.assertions = [];
|
||||
var success = true;
|
||||
this.passed = function() { return success; }
|
||||
this.run();
|
||||
for (var akey in this.assertions) {
|
||||
var assertion = this.assertions[akey];
|
||||
success &= assertion.passed();
|
||||
}
|
||||
settings.onResult(module, this);
|
||||
settings.onEndTest(module, this);
|
||||
}
|
||||
catch (ex) {
|
||||
console.log('Exception', ex);
|
||||
this.ex = ex;
|
||||
success = false;
|
||||
settings.onResult(module, this);
|
||||
settings.onEndTest(module, this);
|
||||
}
|
||||
}
|
||||
test.rerun();
|
||||
}
|
||||
settings.onEndModule(module);
|
||||
}
|
||||
}
|
||||
|
||||
JsUnit.getCurrentUrl = function(changes) {
|
||||
var options = {
|
||||
protocol: window.document.location.protocol,
|
||||
host: window.document.location.hostname,
|
||||
port: window.document.location.port,
|
||||
path: window.document.location.pathname,
|
||||
user: '',
|
||||
pass: ''
|
||||
};
|
||||
options = JsUnit._mixin(options, changes);
|
||||
var userpass = (options.user && options.pass) ? (options.user + ':' + options.pass + '@') : '';
|
||||
if (options.protocol.substr(-1) != ':') options.protocol += ':';
|
||||
return options.protocol + '//' + userpass + options.host + ':' + options.port + options.path;
|
||||
}
|
||||
|
||||
JsUnit.include = function(file) {
|
||||
if (document.createElement && document.getElementsByTagName) {
|
||||
var head = document.getElementsByTagName('head')[0];
|
||||
var script = document.createElement('script');
|
||||
script.setAttribute('type', 'text/javascript');
|
||||
script.setAttribute('src', file);
|
||||
script.setAttribute('async', 'async');
|
||||
head.appendChild(script);
|
||||
}
|
||||
else {
|
||||
console.error('Failed to include', file);
|
||||
}
|
||||
}
|
||||
|
||||
JsUnit.start = function() {
|
||||
this.run({
|
||||
onStartModule: function(module) {
|
||||
var test;
|
||||
for (var i = 0; i < module.tests.length; ++i) {
|
||||
// add all the tests
|
||||
test = module.tests[i];
|
||||
var node = $('<tr id="' + test.name + '"><td>' + module.name + '</td><td>' + test.name + '</td><td class="state"><span class="details" title=""></span></td><td><button>retry</button></td><td class="stack"></td></tr>');
|
||||
$('#tests').append(node);
|
||||
// closure
|
||||
(function(y) {
|
||||
$('#' + y.name + ' button').click(function(){
|
||||
//test = module.tests[i];
|
||||
console.log('test', y.name);
|
||||
console.log('test.rerun', y, y.rerun);
|
||||
console.log('rerunning', y);
|
||||
y.rerun();
|
||||
return false;
|
||||
});
|
||||
})(test);
|
||||
}
|
||||
},
|
||||
|
||||
onResult: function(module, test) {
|
||||
var id = '#' + test.name;
|
||||
var message = '';
|
||||
var stack = '';
|
||||
var state = 'passed';
|
||||
if (!test.passed()) {
|
||||
$(id + ' .stack').html('<span>stack</span>');
|
||||
state = 'failed';
|
||||
if (test.ex) {
|
||||
message += test.ex + '\n' + test.ex.stack;
|
||||
}
|
||||
for (var i in test.assertions) {
|
||||
var assertion = test.assertions[i];
|
||||
if (!assertion.passed()) {
|
||||
message += assertion.message.replace(/&/g, "&").replace(/>/g, ">").replace(/</g, "<").replace(/"/g, """);
|
||||
message += '<br />';
|
||||
for (var key in assertion.stack) {
|
||||
stack += assertion.stack[key].location + '<br />';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
$(id + ' .state').attr('class', ' state ' + state);
|
||||
$(id + ' .details').attr('title', message).html(state);
|
||||
$(id + ' .stack').attr('title', stack);
|
||||
$(id + ' .stack').tipTip({delay: 100, fadeIn:0, fadeOut:100, defaultPosition: 'left', maxWidth: '640px'});
|
||||
$(id + ' .details').tipTip({delay: 100, fadeIn:0, fadeOut:100, defaultPosition: 'right'})
|
||||
}
|
||||
});
|
||||
}
|
||||
return JsUnit;
|
||||
}());
|
||||
416
test/lib/jsunit/stacktrace.js
Executable file
416
test/lib/jsunit/stacktrace.js
Executable file
@@ -0,0 +1,416 @@
|
||||
// Domain Public by Eric Wendelin http://eriwen.com/ (2008)
|
||||
// Luke Smith http://lucassmith.name/ (2008)
|
||||
// Loic Dachary <loic@dachary.org> (2008)
|
||||
// Johan Euphrosine <proppy@aminche.com> (2008)
|
||||
// Oyvind Sean Kinsey http://kinsey.no/blog (2010)
|
||||
// Victor Homyakov <victor-homyakov@users.sourceforge.net> (2010)
|
||||
|
||||
/**
|
||||
* Main function giving a function stack trace with a forced or passed in Error
|
||||
*
|
||||
* @cfg {Error} e The error to create a stacktrace from (optional)
|
||||
* @cfg {Boolean} guess If we should try to resolve the names of anonymous functions
|
||||
* @return {Array} of Strings with functions, lines, files, and arguments where possible
|
||||
*/
|
||||
function printStackTrace(options) {
|
||||
options = options || {guess: true};
|
||||
var ex = options.e || null, guess = !!options.guess;
|
||||
var p = new printStackTrace.implementation(), result = p.run(ex);
|
||||
return (guess) ? p.guessAnonymousFunctions(result) : result;
|
||||
}
|
||||
|
||||
printStackTrace.implementation = function() {
|
||||
};
|
||||
|
||||
printStackTrace.implementation.prototype = {
|
||||
/**
|
||||
* @param {Error} ex The error to create a stacktrace from (optional)
|
||||
* @param {String} mode Forced mode (optional, mostly for unit tests)
|
||||
*/
|
||||
run: function(ex, mode) {
|
||||
ex = ex || this.createException();
|
||||
// examine exception properties w/o debugger
|
||||
//for (var prop in ex) {alert("Ex['" + prop + "']=" + ex[prop]);}
|
||||
mode = mode || this.mode(ex);
|
||||
if (mode === 'other') {
|
||||
return this.other(arguments.callee);
|
||||
} else {
|
||||
return this[mode](ex);
|
||||
}
|
||||
},
|
||||
|
||||
createException: function() {
|
||||
try {
|
||||
this.undef();
|
||||
} catch (e) {
|
||||
return e;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Mode could differ for different exception, e.g.
|
||||
* exceptions in Chrome may or may not have arguments or stack.
|
||||
*
|
||||
* @return {String} mode of operation for the exception
|
||||
*/
|
||||
mode: function(e) {
|
||||
if (e['arguments'] && e.stack) {
|
||||
return 'chrome';
|
||||
} else if (typeof e.message === 'string' && typeof window !== 'undefined' && window.opera) {
|
||||
// e.message.indexOf("Backtrace:") > -1 -> opera
|
||||
// !e.stacktrace -> opera
|
||||
if (!e.stacktrace) {
|
||||
return 'opera9'; // use e.message
|
||||
}
|
||||
// 'opera#sourceloc' in e -> opera9, opera10a
|
||||
if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) {
|
||||
return 'opera9'; // use e.message
|
||||
}
|
||||
// e.stacktrace && !e.stack -> opera10a
|
||||
if (!e.stack) {
|
||||
return 'opera10a'; // use e.stacktrace
|
||||
}
|
||||
// e.stacktrace && e.stack -> opera10b
|
||||
if (e.stacktrace.indexOf("called from line") < 0) {
|
||||
return 'opera10b'; // use e.stacktrace, format differs from 'opera10a'
|
||||
}
|
||||
// e.stacktrace && e.stack -> opera11
|
||||
return 'opera11'; // use e.stacktrace, format differs from 'opera10a', 'opera10b'
|
||||
} else if (e.stack) {
|
||||
return 'firefox';
|
||||
}
|
||||
return 'other';
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a context, function name, and callback function, overwrite it so that it calls
|
||||
* printStackTrace() first with a callback and then runs the rest of the body.
|
||||
*
|
||||
* @param {Object} context of execution (e.g. window)
|
||||
* @param {String} functionName to instrument
|
||||
* @param {Function} function to call with a stack trace on invocation
|
||||
*/
|
||||
instrumentFunction: function(context, functionName, callback) {
|
||||
context = context || window;
|
||||
var original = context[functionName];
|
||||
context[functionName] = function instrumented() {
|
||||
callback.call(this, printStackTrace().slice(4));
|
||||
return context[functionName]._instrumented.apply(this, arguments);
|
||||
};
|
||||
context[functionName]._instrumented = original;
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a context and function name of a function that has been
|
||||
* instrumented, revert the function to it's original (non-instrumented)
|
||||
* state.
|
||||
*
|
||||
* @param {Object} context of execution (e.g. window)
|
||||
* @param {String} functionName to de-instrument
|
||||
*/
|
||||
deinstrumentFunction: function(context, functionName) {
|
||||
if (context[functionName].constructor === Function &&
|
||||
context[functionName]._instrumented &&
|
||||
context[functionName]._instrumented.constructor === Function) {
|
||||
context[functionName] = context[functionName]._instrumented;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Given an Error object, return a formatted Array based on Chrome's stack string.
|
||||
*
|
||||
* @param e - Error object to inspect
|
||||
* @return Array<String> of function calls, files and line numbers
|
||||
*/
|
||||
chrome: function(e) {
|
||||
var stack = (e.stack + '\n').replace(/^\S[^\(]+?[\n$]/gm, '').
|
||||
replace(/^\s+(at eval )?at\s+/gm, '').
|
||||
replace(/^([^\(]+?)([\n$])/gm, '{anonymous}()@$1$2').
|
||||
replace(/^Object.<anonymous>\s*\(([^\)]+)\)/gm, '{anonymous}()@$1').split('\n');
|
||||
stack.pop();
|
||||
return stack;
|
||||
},
|
||||
|
||||
/**
|
||||
* Given an Error object, return a formatted Array based on Firefox's stack string.
|
||||
*
|
||||
* @param e - Error object to inspect
|
||||
* @return Array<String> of function calls, files and line numbers
|
||||
*/
|
||||
firefox: function(e) {
|
||||
return e.stack.replace(/(?:\n@:0)?\s+$/m, '').replace(/^\(/gm, '{anonymous}(').split('\n');
|
||||
},
|
||||
|
||||
opera11: function(e) {
|
||||
// "Error thrown at line 42, column 12 in <anonymous function>() in file://localhost/G:/js/stacktrace.js:\n"
|
||||
// "Error thrown at line 42, column 12 in <anonymous function: createException>() in file://localhost/G:/js/stacktrace.js:\n"
|
||||
// "called from line 7, column 4 in bar(n) in file://localhost/G:/js/test/functional/testcase1.html:\n"
|
||||
// "called from line 15, column 3 in file://localhost/G:/js/test/functional/testcase1.html:\n"
|
||||
var ANON = '{anonymous}', lineRE = /^.*line (\d+), column (\d+)(?: in (.+))? in (\S+):$/;
|
||||
var lines = e.stacktrace.split('\n'), result = [];
|
||||
|
||||
for (var i = 0, len = lines.length; i < len; i += 2) {
|
||||
var match = lineRE.exec(lines[i]);
|
||||
if (match) {
|
||||
var location = match[4] + ':' + match[1] + ':' + match[2];
|
||||
var fnName = match[3] || "global code";
|
||||
fnName = fnName.replace(/<anonymous function: (\S+)>/, "$1").replace(/<anonymous function>/, ANON);
|
||||
result.push(fnName + '@' + location + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
opera10b: function(e) {
|
||||
// "<anonymous function: run>([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" +
|
||||
// "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" +
|
||||
// "@file://localhost/G:/js/test/functional/testcase1.html:15"
|
||||
var lineRE = /^(.*)@(.+):(\d+)$/;
|
||||
var lines = e.stacktrace.split('\n'), result = [];
|
||||
|
||||
for (var i = 0, len = lines.length; i < len; i++) {
|
||||
var match = lineRE.exec(lines[i]);
|
||||
if (match) {
|
||||
var fnName = match[1]? (match[1] + '()') : "global code";
|
||||
result.push(fnName + '@' + match[2] + ':' + match[3]);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
/**
|
||||
* Given an Error object, return a formatted Array based on Opera 10's stacktrace string.
|
||||
*
|
||||
* @param e - Error object to inspect
|
||||
* @return Array<String> of function calls, files and line numbers
|
||||
*/
|
||||
opera10a: function(e) {
|
||||
// " Line 27 of linked script file://localhost/G:/js/stacktrace.js\n"
|
||||
// " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n"
|
||||
var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)(?:: In function (\S+))?$/i;
|
||||
var lines = e.stacktrace.split('\n'), result = [];
|
||||
|
||||
for (var i = 0, len = lines.length; i < len; i += 2) {
|
||||
var match = lineRE.exec(lines[i]);
|
||||
if (match) {
|
||||
var fnName = match[3] || ANON;
|
||||
result.push(fnName + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// Opera 7.x-9.2x only!
|
||||
opera9: function(e) {
|
||||
// " Line 43 of linked script file://localhost/G:/js/stacktrace.js\n"
|
||||
// " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n"
|
||||
var ANON = '{anonymous}', lineRE = /Line (\d+).*script (?:in )?(\S+)/i;
|
||||
var lines = e.message.split('\n'), result = [];
|
||||
|
||||
for (var i = 2, len = lines.length; i < len; i += 2) {
|
||||
var match = lineRE.exec(lines[i]);
|
||||
if (match) {
|
||||
result.push(ANON + '()@' + match[2] + ':' + match[1] + ' -- ' + lines[i + 1].replace(/^\s+/, ''));
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
},
|
||||
|
||||
// Safari, IE, and others
|
||||
other: function(curr) {
|
||||
var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], fn, args, maxStackSize = 10;
|
||||
while (curr && curr['arguments'] && stack.length < maxStackSize) {
|
||||
fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON;
|
||||
args = Array.prototype.slice.call(curr['arguments'] || []);
|
||||
stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')';
|
||||
curr = curr.caller;
|
||||
}
|
||||
return stack;
|
||||
},
|
||||
|
||||
/**
|
||||
* Given arguments array as a String, subsituting type names for non-string types.
|
||||
*
|
||||
* @param {Arguments} object
|
||||
* @return {Array} of Strings with stringified arguments
|
||||
*/
|
||||
stringifyArguments: function(args) {
|
||||
var result = [];
|
||||
var slice = Array.prototype.slice;
|
||||
for (var i = 0; i < args.length; ++i) {
|
||||
var arg = args[i];
|
||||
if (arg === undefined) {
|
||||
result[i] = 'undefined';
|
||||
} else if (arg === null) {
|
||||
result[i] = 'null';
|
||||
} else if (arg.constructor) {
|
||||
if (arg.constructor === Array) {
|
||||
if (arg.length < 3) {
|
||||
result[i] = '[' + this.stringifyArguments(arg) + ']';
|
||||
} else {
|
||||
result[i] = '[' + this.stringifyArguments(slice.call(arg, 0, 1)) + '...' + this.stringifyArguments(slice.call(arg, -1)) + ']';
|
||||
}
|
||||
} else if (arg.constructor === Object) {
|
||||
result[i] = '#object';
|
||||
} else if (arg.constructor === Function) {
|
||||
result[i] = '#function';
|
||||
} else if (arg.constructor === String) {
|
||||
result[i] = '"' + arg + '"';
|
||||
} else if (arg.constructor === Number) {
|
||||
result[i] = arg;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.join(',');
|
||||
},
|
||||
|
||||
sourceCache: {},
|
||||
|
||||
/**
|
||||
* @return the text from a given URL
|
||||
*/
|
||||
ajax: function(url) {
|
||||
var req = this.createXMLHTTPObject();
|
||||
if (req) {
|
||||
try {
|
||||
req.open('GET', url, false);
|
||||
//req.overrideMimeType('text/plain');
|
||||
//req.overrideMimeType('text/javascript');
|
||||
req.send(null);
|
||||
//return req.status == 200 ? req.responseText : '';
|
||||
return req.responseText;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
return '';
|
||||
},
|
||||
|
||||
/**
|
||||
* Try XHR methods in order and store XHR factory.
|
||||
*
|
||||
* @return <Function> XHR function or equivalent
|
||||
*/
|
||||
createXMLHTTPObject: function() {
|
||||
var xmlhttp, XMLHttpFactories = [
|
||||
function() {
|
||||
return new XMLHttpRequest();
|
||||
}, function() {
|
||||
return new ActiveXObject('Msxml2.XMLHTTP');
|
||||
}, function() {
|
||||
return new ActiveXObject('Msxml3.XMLHTTP');
|
||||
}, function() {
|
||||
return new ActiveXObject('Microsoft.XMLHTTP');
|
||||
}
|
||||
];
|
||||
for (var i = 0; i < XMLHttpFactories.length; i++) {
|
||||
try {
|
||||
xmlhttp = XMLHttpFactories[i]();
|
||||
// Use memoization to cache the factory
|
||||
this.createXMLHTTPObject = XMLHttpFactories[i];
|
||||
return xmlhttp;
|
||||
} catch (e) {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Given a URL, check if it is in the same domain (so we can get the source
|
||||
* via Ajax).
|
||||
*
|
||||
* @param url <String> source url
|
||||
* @return False if we need a cross-domain request
|
||||
*/
|
||||
isSameDomain: function(url) {
|
||||
return typeof location !== "undefined" && url.indexOf(location.hostname) !== -1; // location may not be defined, e.g. when running from nodejs.
|
||||
},
|
||||
|
||||
/**
|
||||
* Get source code from given URL if in the same domain.
|
||||
*
|
||||
* @param url <String> JS source URL
|
||||
* @return <Array> Array of source code lines
|
||||
*/
|
||||
getSource: function(url) {
|
||||
// TODO reuse source from script tags?
|
||||
if (!(url in this.sourceCache)) {
|
||||
this.sourceCache[url] = this.ajax(url).split('\n');
|
||||
}
|
||||
return this.sourceCache[url];
|
||||
},
|
||||
|
||||
guessAnonymousFunctions: function(stack) {
|
||||
for (var i = 0; i < stack.length; ++i) {
|
||||
var reStack = /\{anonymous\}\(.*\)@(.*)/,
|
||||
reRef = /^(.*?)(?::(\d+))(?::(\d+))?(?: -- .+)?$/,
|
||||
frame = stack[i], ref = reStack.exec(frame);
|
||||
|
||||
if (ref) {
|
||||
var m = reRef.exec(ref[1]);
|
||||
if (m) { // If falsey, we did not get any file/line information
|
||||
var file = m[1], lineno = m[2], charno = m[3] || 0;
|
||||
if (file && this.isSameDomain(file) && lineno) {
|
||||
var functionName = this.guessAnonymousFunction(file, lineno, charno);
|
||||
stack[i] = frame.replace('{anonymous}', functionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return stack;
|
||||
},
|
||||
|
||||
guessAnonymousFunction: function(url, lineNo, charNo) {
|
||||
var ret;
|
||||
try {
|
||||
ret = this.findFunctionName(this.getSource(url), lineNo);
|
||||
} catch (e) {
|
||||
ret = 'getSource failed with url: ' + url + ', exception: ' + e.toString();
|
||||
}
|
||||
return ret;
|
||||
},
|
||||
|
||||
findFunctionName: function(source, lineNo) {
|
||||
// FIXME findFunctionName fails for compressed source
|
||||
// (more than one function on the same line)
|
||||
// TODO use captured args
|
||||
// function {name}({args}) m[1]=name m[2]=args
|
||||
var reFunctionDeclaration = /function\s+([^(]*?)\s*\(([^)]*)\)/;
|
||||
// {name} = function ({args}) TODO args capture
|
||||
// /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function(?:[^(]*)/
|
||||
var reFunctionExpression = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*function\b/;
|
||||
// {name} = eval()
|
||||
var reFunctionEvaluation = /['"]?([0-9A-Za-z_]+)['"]?\s*[:=]\s*(?:eval|new Function)\b/;
|
||||
// Walk backwards in the source lines until we find
|
||||
// the line which matches one of the patterns above
|
||||
var code = "", line, maxLines = Math.min(lineNo, 20), m, commentPos;
|
||||
for (var i = 0; i < maxLines; ++i) {
|
||||
// lineNo is 1-based, source[] is 0-based
|
||||
line = source[lineNo - i - 1];
|
||||
commentPos = line.indexOf('//');
|
||||
if (commentPos >= 0) {
|
||||
line = line.substr(0, commentPos);
|
||||
}
|
||||
// TODO check other types of comments? Commented code may lead to false positive
|
||||
if (line) {
|
||||
code = line + code;
|
||||
m = reFunctionExpression.exec(code);
|
||||
if (m && m[1]) {
|
||||
return m[1];
|
||||
}
|
||||
m = reFunctionDeclaration.exec(code);
|
||||
if (m && m[1]) {
|
||||
//return m[1] + "(" + (m[2] || "") + ")";
|
||||
return m[1];
|
||||
}
|
||||
m = reFunctionEvaluation.exec(code);
|
||||
if (m && m[1]) {
|
||||
return m[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
return '(?)';
|
||||
}
|
||||
};
|
||||
113
test/lib/jsunit/tipTip.css
Executable file
113
test/lib/jsunit/tipTip.css
Executable file
@@ -0,0 +1,113 @@
|
||||
/* TipTip CSS - Version 1.2 */
|
||||
|
||||
#tiptip_holder {
|
||||
display: none;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 99999;
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_top {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_bottom {
|
||||
padding-top: 5px;
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_right {
|
||||
padding-left: 5px;
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_left {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
#tiptip_content {
|
||||
font-size: 11px;
|
||||
color: #fff;
|
||||
text-shadow: 0 0 2px #000;
|
||||
padding: 4px 8px;
|
||||
border: 1px solid rgba(255,255,255,0.25);
|
||||
background-color: rgb(25,25,25);
|
||||
background-color: rgba(25,25,25,0.92);
|
||||
background-image: -webkit-gradient(linear, 0% 0%, 0% 100%, from(transparent), to(#000));
|
||||
border-radius: 3px;
|
||||
-webkit-border-radius: 3px;
|
||||
-moz-border-radius: 3px;
|
||||
box-shadow: 0 0 2px #ccc;
|
||||
-webkit-box-shadow: 0 0 2px #ccc;
|
||||
-moz-box-shadow: 0 0 2px #ccc;
|
||||
}
|
||||
|
||||
#tiptip_arrow, #tiptip_arrow_inner {
|
||||
position: absolute;
|
||||
border-color: transparent;
|
||||
border-style: solid;
|
||||
border-width: 6px;
|
||||
height: 0;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_top #tiptip_arrow {
|
||||
border-top-color: #fff;
|
||||
border-top-color: rgba(255,255,255,0.35);
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_bottom #tiptip_arrow {
|
||||
border-bottom-color: #fff;
|
||||
border-bottom-color: rgba(255,255,255,0.35);
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_right #tiptip_arrow {
|
||||
border-right-color: #fff;
|
||||
border-right-color: rgba(255,255,255,0.35);
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_left #tiptip_arrow {
|
||||
border-left-color: #fff;
|
||||
border-left-color: rgba(255,255,255,0.35);
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_top #tiptip_arrow_inner {
|
||||
margin-top: -7px;
|
||||
margin-left: -6px;
|
||||
border-top-color: rgb(25,25,25);
|
||||
border-top-color: rgba(25,25,25,0.92);
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_bottom #tiptip_arrow_inner {
|
||||
margin-top: -5px;
|
||||
margin-left: -6px;
|
||||
border-bottom-color: rgb(25,25,25);
|
||||
border-bottom-color: rgba(25,25,25,0.92);
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_right #tiptip_arrow_inner {
|
||||
margin-top: -6px;
|
||||
margin-left: -5px;
|
||||
border-right-color: rgb(25,25,25);
|
||||
border-right-color: rgba(25,25,25,0.92);
|
||||
}
|
||||
|
||||
#tiptip_holder.tip_left #tiptip_arrow_inner {
|
||||
margin-top: -6px;
|
||||
margin-left: -7px;
|
||||
border-left-color: rgb(25,25,25);
|
||||
border-left-color: rgba(25,25,25,0.92);
|
||||
}
|
||||
|
||||
/* Webkit Hacks */
|
||||
@media screen and (-webkit-min-device-pixel-ratio:0) {
|
||||
#tiptip_content {
|
||||
padding: 4px 8px 5px 8px;
|
||||
background-color: rgba(45,45,45,0.88);
|
||||
}
|
||||
#tiptip_holder.tip_bottom #tiptip_arrow_inner {
|
||||
border-bottom-color: rgba(45,45,45,0.88);
|
||||
}
|
||||
#tiptip_holder.tip_top #tiptip_arrow_inner {
|
||||
border-top-color: rgba(20,20,20,0.92);
|
||||
}
|
||||
}
|
||||
191
test/lib/jsunit/tipTip.js
Executable file
191
test/lib/jsunit/tipTip.js
Executable file
@@ -0,0 +1,191 @@
|
||||
/*
|
||||
* TipTip
|
||||
* Copyright 2010 Drew Wilson
|
||||
* www.drewwilson.com
|
||||
* code.drewwilson.com/entry/tiptip-jquery-plugin
|
||||
*
|
||||
* Version 1.3 - Updated: Mar. 23, 2010
|
||||
*
|
||||
* This Plug-In will create a custom tooltip to replace the default
|
||||
* browser tooltip. It is extremely lightweight and very smart in
|
||||
* that it detects the edges of the browser window and will make sure
|
||||
* the tooltip stays within the current window size. As a result the
|
||||
* tooltip will adjust itself to be displayed above, below, to the left
|
||||
* or to the right depending on what is necessary to stay within the
|
||||
* browser window. It is completely customizable as well via CSS.
|
||||
*
|
||||
* This TipTip jQuery plug-in is dual licensed under the MIT and GPL licenses:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
* http://www.gnu.org/licenses/gpl.html
|
||||
*/
|
||||
|
||||
(function($){
|
||||
$.fn.tipTip = function(options) {
|
||||
var defaults = {
|
||||
activation: "hover",
|
||||
keepAlive: false,
|
||||
maxWidth: "200px",
|
||||
edgeOffset: 3,
|
||||
defaultPosition: "bottom",
|
||||
delay: 400,
|
||||
fadeIn: 200,
|
||||
fadeOut: 200,
|
||||
attribute: "title",
|
||||
content: false, // HTML or String to fill TipTIp with
|
||||
enter: function(){},
|
||||
exit: function(){}
|
||||
};
|
||||
var opts = $.extend(defaults, options);
|
||||
|
||||
// Setup tip tip elements and render them to the DOM
|
||||
if($("#tiptip_holder").length <= 0){
|
||||
var tiptip_holder = $('<div id="tiptip_holder" style="max-width:'+ opts.maxWidth +';"></div>');
|
||||
var tiptip_content = $('<div id="tiptip_content"></div>');
|
||||
var tiptip_arrow = $('<div id="tiptip_arrow"></div>');
|
||||
$("body").append(tiptip_holder.html(tiptip_content).prepend(tiptip_arrow.html('<div id="tiptip_arrow_inner"></div>')));
|
||||
} else {
|
||||
var tiptip_holder = $("#tiptip_holder");
|
||||
var tiptip_content = $("#tiptip_content");
|
||||
var tiptip_arrow = $("#tiptip_arrow");
|
||||
}
|
||||
|
||||
return this.each(function(){
|
||||
var org_elem = $(this);
|
||||
if(opts.content){
|
||||
var org_title = opts.content;
|
||||
} else {
|
||||
var org_title = org_elem.attr(opts.attribute);
|
||||
}
|
||||
if(org_title != ""){
|
||||
if(!opts.content){
|
||||
org_elem.removeAttr(opts.attribute); //remove original Attribute
|
||||
}
|
||||
var timeout = false;
|
||||
|
||||
if(opts.activation == "hover"){
|
||||
org_elem.hover(function(){
|
||||
active_tiptip();
|
||||
}, function(){
|
||||
if(!opts.keepAlive){
|
||||
deactive_tiptip();
|
||||
}
|
||||
});
|
||||
if(opts.keepAlive){
|
||||
tiptip_holder.hover(function(){}, function(){
|
||||
deactive_tiptip();
|
||||
});
|
||||
}
|
||||
} else if(opts.activation == "focus"){
|
||||
org_elem.focus(function(){
|
||||
active_tiptip();
|
||||
}).blur(function(){
|
||||
deactive_tiptip();
|
||||
});
|
||||
} else if(opts.activation == "click"){
|
||||
org_elem.click(function(){
|
||||
active_tiptip();
|
||||
return false;
|
||||
}).hover(function(){},function(){
|
||||
if(!opts.keepAlive){
|
||||
deactive_tiptip();
|
||||
}
|
||||
});
|
||||
if(opts.keepAlive){
|
||||
tiptip_holder.hover(function(){}, function(){
|
||||
deactive_tiptip();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function active_tiptip(){
|
||||
opts.enter.call(this);
|
||||
tiptip_content.html(org_title);
|
||||
tiptip_holder.hide().removeAttr("class").css("margin","0");
|
||||
tiptip_arrow.removeAttr("style");
|
||||
|
||||
var top = parseInt(org_elem.offset()['top']);
|
||||
var left = parseInt(org_elem.offset()['left']);
|
||||
var org_width = parseInt(org_elem.outerWidth());
|
||||
var org_height = parseInt(org_elem.outerHeight());
|
||||
var tip_w = tiptip_holder.outerWidth();
|
||||
var tip_h = tiptip_holder.outerHeight();
|
||||
var w_compare = Math.round((org_width - tip_w) / 2);
|
||||
var h_compare = Math.round((org_height - tip_h) / 2);
|
||||
var marg_left = Math.round(left + w_compare);
|
||||
var marg_top = Math.round(top + org_height + opts.edgeOffset);
|
||||
var t_class = "";
|
||||
var arrow_top = "";
|
||||
var arrow_left = Math.round(tip_w - 12) / 2;
|
||||
|
||||
if(opts.defaultPosition == "bottom"){
|
||||
t_class = "_bottom";
|
||||
} else if(opts.defaultPosition == "top"){
|
||||
t_class = "_top";
|
||||
} else if(opts.defaultPosition == "left"){
|
||||
t_class = "_left";
|
||||
} else if(opts.defaultPosition == "right"){
|
||||
t_class = "_right";
|
||||
}
|
||||
|
||||
var right_compare = (w_compare + left) < parseInt($(window).scrollLeft());
|
||||
var left_compare = (tip_w + left) > parseInt($(window).width());
|
||||
|
||||
if((right_compare && w_compare < 0) || (t_class == "_right" && !left_compare) || (t_class == "_left" && left < (tip_w + opts.edgeOffset + 5))){
|
||||
t_class = "_right";
|
||||
arrow_top = Math.round(tip_h - 13) / 2;
|
||||
arrow_left = -12;
|
||||
marg_left = Math.round(left + org_width + opts.edgeOffset);
|
||||
marg_top = Math.round(top + h_compare);
|
||||
} else if((left_compare && w_compare < 0) || (t_class == "_left" && !right_compare)){
|
||||
t_class = "_left";
|
||||
arrow_top = Math.round(tip_h - 13) / 2;
|
||||
arrow_left = Math.round(tip_w);
|
||||
marg_left = Math.round(left - (tip_w + opts.edgeOffset + 5));
|
||||
marg_top = Math.round(top + h_compare);
|
||||
}
|
||||
|
||||
var top_compare = (top + org_height + opts.edgeOffset + tip_h + 8) > parseInt($(window).height() + $(window).scrollTop());
|
||||
var bottom_compare = ((top + org_height) - (opts.edgeOffset + tip_h + 8)) < 0;
|
||||
|
||||
if(top_compare || (t_class == "_bottom" && top_compare) || (t_class == "_top" && !bottom_compare)){
|
||||
if(t_class == "_top" || t_class == "_bottom"){
|
||||
t_class = "_top";
|
||||
} else {
|
||||
t_class = t_class+"_top";
|
||||
}
|
||||
arrow_top = tip_h;
|
||||
marg_top = Math.round(top - (tip_h + 5 + opts.edgeOffset));
|
||||
} else if(bottom_compare | (t_class == "_top" && bottom_compare) || (t_class == "_bottom" && !top_compare)){
|
||||
if(t_class == "_top" || t_class == "_bottom"){
|
||||
t_class = "_bottom";
|
||||
} else {
|
||||
t_class = t_class+"_bottom";
|
||||
}
|
||||
arrow_top = -12;
|
||||
marg_top = Math.round(top + org_height + opts.edgeOffset);
|
||||
}
|
||||
|
||||
if(t_class == "_right_top" || t_class == "_left_top"){
|
||||
marg_top = marg_top + 5;
|
||||
} else if(t_class == "_right_bottom" || t_class == "_left_bottom"){
|
||||
marg_top = marg_top - 5;
|
||||
}
|
||||
if(t_class == "_left_top" || t_class == "_left_bottom"){
|
||||
marg_left = marg_left + 5;
|
||||
}
|
||||
tiptip_arrow.css({"margin-left": arrow_left+"px", "margin-top": arrow_top+"px"});
|
||||
tiptip_holder.css({"margin-left": marg_left+"px", "margin-top": marg_top+"px"}).attr("class","tip"+t_class);
|
||||
|
||||
if (timeout){ clearTimeout(timeout); }
|
||||
timeout = setTimeout(function(){ tiptip_holder.stop(true,true).fadeIn(opts.fadeIn); }, opts.delay);
|
||||
}
|
||||
|
||||
function deactive_tiptip(){
|
||||
opts.exit.call(this);
|
||||
if (timeout){ clearTimeout(timeout); }
|
||||
tiptip_holder.fadeOut(opts.fadeOut);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})(jQuery);
|
||||
Reference in New Issue
Block a user