diff --git a/bower.json b/bower.json index e88fede5c6e66..2cb9c963e440b 100644 --- a/bower.json +++ b/bower.json @@ -35,7 +35,8 @@ "jquery": "~2.1.0", "moment": "~2.5.1", "require-css": "~0.1.2", - "angular-bootstrap": "~0.10.0" + "angular-bootstrap": "~0.10.0", + "stacktrace.js": "https://github.com/stacktracejs/stacktrace.js.git#~0.6.0" }, "devDependencies": {} } diff --git a/src/bower_components/stacktrace.js/.bower.json b/src/bower_components/stacktrace.js/.bower.json new file mode 100644 index 0000000000000..2d9740fa7277e --- /dev/null +++ b/src/bower_components/stacktrace.js/.bower.json @@ -0,0 +1,22 @@ +{ + "name": "stacktrace.js", + "version": "0.6.0", + "main": "./stacktrace.js", + "dependencies": {}, + "ignore": [ + "**/.*", + "node_modules", + "components" + ], + "homepage": "https://github.com/stacktracejs/stacktrace.js", + "_release": "0.6.0", + "_resolution": { + "type": "version", + "tag": "v0.6.0", + "commit": "02b5def17d157b5e56d2bb0e175e48c5c1272aaa" + }, + "_source": "https://github.com/stacktracejs/stacktrace.js.git", + "_target": "~0.6.0", + "_originalSource": "https://github.com/stacktracejs/stacktrace.js.git", + "_direct": true +} \ No newline at end of file diff --git a/src/bower_components/stacktrace.js/CHANGELOG.md b/src/bower_components/stacktrace.js/CHANGELOG.md new file mode 100644 index 0000000000000..b991253c10245 --- /dev/null +++ b/src/bower_components/stacktrace.js/CHANGELOG.md @@ -0,0 +1,16 @@ +## v0.6.0 + +* Added AMD support using a UMD pattern (thanks @jeffrose) + +## v0.5.3 + +* Fix Chrome 27 detection; Chrome no longer has Error#arguments + +## v0.5.1 + +* Fix Bower integration; Added proper bower.json file + +## v0.5.0 + +* Lots and lots of stuff + diff --git a/src/bower_components/stacktrace.js/CONTRIBUTING.md b/src/bower_components/stacktrace.js/CONTRIBUTING.md new file mode 100644 index 0000000000000..4282f4c09854f --- /dev/null +++ b/src/bower_components/stacktrace.js/CONTRIBUTING.md @@ -0,0 +1,16 @@ +## Making contributions +When submitting your pull requests, please do the following to make it easier to incorporate your changes: + +* Include unit and/or functional tests that validate changes you're making. +* Run unit tests in the latest IE, Firefox, Chrome, Safari and Opera and make sure they pass. +* Rebase your changes onto origin/HEAD if you can do so cleanly. +* If submitting additional functionality, provide an example of how to use it. +* Please keep code style consistent with surrounding code. + +## Testing +There are a few ways to run tests: + +* You can run tests in PhantomJS by simply running `gradlew test` from your favorite shell. +* Run tests with JSTestDriver using `gradlew jstd` +* Point any browser to `≤project dir>/test/TestStacktrace.html` for unit tests +* Point your browser to `≤project dir>/test/functional/index.html` for more real-world functional tests diff --git a/src/bower_components/stacktrace.js/LICENSE.txt b/src/bower_components/stacktrace.js/LICENSE.txt new file mode 100644 index 0000000000000..0501ed4f3909f --- /dev/null +++ b/src/bower_components/stacktrace.js/LICENSE.txt @@ -0,0 +1,6 @@ +Domain Public by Eric Wendelin http://eriwen.com/ (2008) + Luke Smith http://lucassmith.name/ (2008) + Loic Dachary (2008) + Johan Euphrosine (2008) + Oyvind Sean Kinsey http://kinsey.no/blog (2010) + Victor Homyakov (2010) diff --git a/src/bower_components/stacktrace.js/README.md b/src/bower_components/stacktrace.js/README.md new file mode 100644 index 0000000000000..7ca6e9675e04e --- /dev/null +++ b/src/bower_components/stacktrace.js/README.md @@ -0,0 +1,82 @@ +# Welcome to stacktrace.js! [![Code Climate](https://codeclimate.com/github/eriwen/javascript-stacktrace.png)](https://codeclimate.com/github/eriwen/javascript-stacktrace) +A JavaScript tool that allows you to debug your JavaScript by giving you a [stack trace](http://en.wikipedia.org/wiki/Stack_trace) of function calls leading to an error (or any condition you specify) + +# How do I use stacktrace.js? # +Just include stacktrace.js file on your page, and call it like so: + +```html + + +``` + +You can also pass in your own Error to get a stacktrace *not available in IE or Safari 5-* + +```html + + +``` + +Note that error message is not included in stack trace. + +Bookmarklet available on the [project home page](http://stacktracejs.com). + +# Function Instrumentation # +You can now have any (public or privileged) function give you a stacktrace when it is called: + +```javascript +function logStackTrace(stack) { + console.log(stack.join('\n')); +} +var p = new printStackTrace.implementation(); +p.instrumentFunction(this, 'baz', logStackTrace); + +function foo() { + var a = 1; + bar(); +} +function bar() { + baz(); +} +foo(); //Will log a stacktrace when 'baz()' is called containing 'foo()'! + +p.deinstrumentFunction(this, 'baz'); //Remove function instrumentation +``` + +# What browsers does stacktrace.js support? # +It is currently tested and working on: + + - Firefox (and Iceweasel) 0.9+ + - Google Chrome 1+ + - Safari 3.0+ (including iOS 1+) + - Opera 7+ + - IE 5.5+ + - Konqueror 3.5+ + - Flock 1.0+ + - SeaMonkey 1.0+ + - K-Meleon 1.5.3+ + - Epiphany 2.28.0+ + - Iceape 1.1+ + +## Contributions [![Stories in Ready](http://badge.waffle.io/eriwen/javascript-stacktrace.png)](http://waffle.io/eriwen/javascript-stacktrace) + +This project is made possible due to the efforts of these fine people: + +* [Eric Wendelin](http://eriwen.com) +* [Luke Smith](http://lucassmith.name/) +* Loic Dachary +* Johan Euphrosine +* Øyvind Sean Kinsey +* Victor Homyakov diff --git a/src/bower_components/stacktrace.js/bower.json b/src/bower_components/stacktrace.js/bower.json new file mode 100644 index 0000000000000..1ba9f56df64e7 --- /dev/null +++ b/src/bower_components/stacktrace.js/bower.json @@ -0,0 +1,11 @@ +{ + "name": "stacktrace.js", + "version": "0.6.0", + "main": "./stacktrace.js", + "dependencies": {}, + "ignore": [ + "**/.*", + "node_modules", + "components" + ] +} diff --git a/src/bower_components/stacktrace.js/build.gradle b/src/bower_components/stacktrace.js/build.gradle new file mode 100644 index 0000000000000..5ad5f61fb2fea --- /dev/null +++ b/src/bower_components/stacktrace.js/build.gradle @@ -0,0 +1,123 @@ +buildscript { + repositories { + mavenLocal() + mavenCentral() + } + dependencies { + classpath 'com.eriwen:gradle-js-plugin:1.1' + } +} + +apply plugin: 'js' + +defaultTasks 'all' +buildDir = 'target' + +def srcFile = 'stacktrace.js' +def destFile = "${buildDir}/stacktrace-min.js" +def testDir = 'test' + +repositories { + mavenRepo url: 'http://repository.springsource.com/maven/bundles/release' + mavenCentral() +} + +task clean(type: Delete) { + delete buildDir +} + +task init(type: Directory, dependsOn: 'clean', description: 'Creates artifact output directories') { + outputs.dir(buildDir) + doLast { + file(buildDir).mkdirs() + } +} + +task wrapper(type: Wrapper) { + gradleVersion = '1.1' +} + +task jshintz(dependsOn: 'init', description: 'runs jshint on all non-test and lib JS files') { + doLast { + def command = "jshint ${new File('stacktrace.js').canonicalPath} --config jshint.json --jslint-reporter" + new File("${buildDir}/jshint.xml").write(command.execute().text) + } +} + +task jsduck(type: Exec, dependsOn: 'init', description: 'Generates jsduck documentation') { + inputs.file file(srcFile) + outputs.file file("${buildDir}/docs") + + commandLine = ['jsduck', srcFile, '--output', "${buildDir}/docs"] + ignoreExitValue = true +} + +minifyJs { + dependsOn << 'init' + source = file(srcFile) + dest = file(destFile) + closure { + warningLevel = 'QUIET' + } +} + +gzipJs { + source = minifyJs + dest = file(destFile) +} + +task test(dependsOn: 'init') << { + description = 'run QUnit tests and create JUnit test reports' + + def specs = [] + new File(testDir).eachFile { + if (it.name.endsWith('.html')) { + specs << it + } + } + + def phantomJsPath = "which phantomjs".execute().text.trim() + def startTime = new Date().time + def numFailures = 0 + def testsFailed = false + specs.each { File spec -> + print "Running ${spec.name}..." + + def outputFile = "${buildDir}/TEST-${spec.name.replace('-', '').replace('.html', '.xml')}" + ant.exec(outputproperty: 'cmdOut', errorproperty: 'cmdErr', + resultproperty: 'exitCode', failonerror: 'false', executable: phantomJsPath) { + arg(value: 'test/lib/phantomjs-qunit-runner.js') + arg(value: spec.canonicalPath) + } + // Check exit code + if (ant.project.properties.exitCode != '0') { + testsFailed = true + numFailures++ + println 'FAILED' + } else { + println 'PASSED' + } + + new File(outputFile).write(ant.project.properties.cmdOut) + } + + println "QUnit tests completed in ${new Date().time - startTime}ms" + println "QUnit Tests ${testsFailed ? 'FAILED' : 'PASSED'} - view reports in ${buildDir}" + ant.fail(if: testsFailed, message: 'JS Tests Failed') +} + +task jstd(type: Exec, dependsOn: 'init', description: 'runs JS tests through JsTestDriver') { + // Default to MacOS and check for other environments + def firefoxPath = '/Applications/Firefox.app/Contents/MacOS/firefox' + if ("uname".execute().text.trim() != 'Darwin') { + firefoxPath = "which firefox".execute().text.trim() + } + + commandLine = ['/usr/bin/env', 'DISPLAY=:1', 'java', '-jar', "test/lib/JsTestDriver-1.3.3d.jar", '--config', "test/jsTestDriver.conf", '--port', '4224', '--browser', firefoxPath, '--tests', 'all', '--testOutput', buildDir] +} + +task jsCoverage(type: Exec, dependsOn: 'jstd', description: 'JS code coverage with cobertura') { + commandLine = ['python', "${projectDir}/test/lib/lcov-to-cobertura-xml.py", '-e', 'test.lib', '-o', "${buildDir}/coverage.xml", "${buildDir}/jsTestDriver.conf-coverage.dat"] +} + +task all(dependsOn: ['clean', 'jshintz', 'test', 'jsduck', 'minifyJs', 'gzipJs']) << {} diff --git a/src/bower_components/stacktrace.js/component.json b/src/bower_components/stacktrace.js/component.json new file mode 100644 index 0000000000000..e85c22be21e5a --- /dev/null +++ b/src/bower_components/stacktrace.js/component.json @@ -0,0 +1,10 @@ +{ + "name": "stacktrace.js", + "version": "0.6.0", + "repo": "eriwen/javascript-stacktrace", + "main": "stacktrace.js", + "scripts": [ + "stacktrace.js" + ], + "dependencies": {} +} diff --git a/src/bower_components/stacktrace.js/gradle/wrapper/gradle-wrapper.properties b/src/bower_components/stacktrace.js/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000000000..31dd56ae4a136 --- /dev/null +++ b/src/bower_components/stacktrace.js/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,6 @@ +#Tue Aug 21 18:36:49 MDT 2012 +zipStorePath=wrapper/dists +distributionPath=wrapper/dists +distributionBase=GRADLE_USER_HOME +zipStoreBase=GRADLE_USER_HOME +distributionUrl=http\://services.gradle.org/distributions/gradle-1.1-bin.zip diff --git a/src/bower_components/stacktrace.js/gradlew b/src/bower_components/stacktrace.js/gradlew new file mode 100755 index 0000000000000..e61422d06d5db --- /dev/null +++ b/src/bower_components/stacktrace.js/gradlew @@ -0,0 +1,164 @@ +#!/bin/bash + +############################################################################## +## +## Gradle start up script for UN*X +## +############################################################################## + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS="" + +APP_NAME="Gradle" +APP_BASE_NAME=`basename "$0"` + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD="maximum" + +warn ( ) { + echo "$*" +} + +die ( ) { + echo + echo "$*" + echo + exit 1 +} + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +case "`uname`" in + CYGWIN* ) + cygwin=true + ;; + Darwin* ) + darwin=true + ;; + MINGW* ) + msys=true + ;; +esac + +# For Cygwin, ensure paths are in UNIX format before anything is touched. +if $cygwin ; then + [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` +fi + +# Attempt to set APP_HOME +# Resolve links: $0 may be a link +PRG="$0" +# Need this for relative symlinks. +while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG=`dirname "$PRG"`"/$link" + fi +done +SAVED="`pwd`" +cd "`dirname \"$PRG\"`/" +APP_HOME="`pwd -P`" +cd "$SAVED" + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD="java" + which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." +fi + +# Increase the maximum file descriptors if we can. +if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then + MAX_FD_LIMIT=`ulimit -H -n` + if [ $? -eq 0 ] ; then + if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then + MAX_FD="$MAX_FD_LIMIT" + fi + ulimit -n $MAX_FD + if [ $? -ne 0 ] ; then + warn "Could not set maximum file descriptor limit: $MAX_FD" + fi + else + warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" + fi +fi + +# For Darwin, add options to specify how the application appears in the dock +if $darwin; then + GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" +fi + +# For Cygwin, switch paths to Windows format before running java +if $cygwin ; then + APP_HOME=`cygpath --path --mixed "$APP_HOME"` + CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` + + # We build the pattern for arguments to be converted via cygpath + ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` + SEP="" + for dir in $ROOTDIRSRAW ; do + ROOTDIRS="$ROOTDIRS$SEP$dir" + SEP="|" + done + OURCYGPATTERN="(^($ROOTDIRS))" + # Add a user-defined pattern to the cygpath arguments + if [ "$GRADLE_CYGPATTERN" != "" ] ; then + OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" + fi + # Now convert the arguments - kludge to limit ourselves to /bin/sh + i=0 + for arg in "$@" ; do + CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` + CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option + + if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition + eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` + else + eval `echo args$i`="\"$arg\"" + fi + i=$((i+1)) + done + case $i in + (0) set -- ;; + (1) set -- "$args0" ;; + (2) set -- "$args0" "$args1" ;; + (3) set -- "$args0" "$args1" "$args2" ;; + (4) set -- "$args0" "$args1" "$args2" "$args3" ;; + (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; + (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; + (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; + (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; + (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; + esac +fi + +# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules +function splitJvmOpts() { + JVM_OPTS=("$@") +} +eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS +JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" + +exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" diff --git a/src/bower_components/stacktrace.js/gradlew.bat b/src/bower_components/stacktrace.js/gradlew.bat new file mode 100644 index 0000000000000..8a0b282aa6885 --- /dev/null +++ b/src/bower_components/stacktrace.js/gradlew.bat @@ -0,0 +1,90 @@ +@if "%DEBUG%" == "" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS= + +set DIRNAME=%~dp0 +if "%DIRNAME%" == "" set DIRNAME=. +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if "%ERRORLEVEL%" == "0" goto init + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto init + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:init +@rem Get command-line arguments, handling Windowz variants + +if not "%OS%" == "Windows_NT" goto win9xME_args +if "%@eval[2+2]" == "4" goto 4NT_args + +:win9xME_args +@rem Slurp the command line arguments. +set CMD_LINE_ARGS= +set _SKIP=2 + +:win9xME_args_slurp +if "x%~1" == "x" goto execute + +set CMD_LINE_ARGS=%* +goto execute + +:4NT_args +@rem Get arguments from the 4NT Shell from JP Software +set CMD_LINE_ARGS=%$ + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% + +:end +@rem End local scope for the variables with windows NT shell +if "%ERRORLEVEL%"=="0" goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 +exit /b 1 + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/src/bower_components/stacktrace.js/jshint.json b/src/bower_components/stacktrace.js/jshint.json new file mode 100644 index 0000000000000..3723f84818ce7 --- /dev/null +++ b/src/bower_components/stacktrace.js/jshint.json @@ -0,0 +1,48 @@ +{ + "asi" : false, + "bitwise" : true, + "boss" : true, + "browser" : true, + "couch" : true, + "curly" : true, + "debug" : false, + "eqeqeq" : true, + "eqnull" : false, + "es5" : true, + "evil" : false, + "expr" : true, + "forin" : false, + "globalstrict" : true, + "immed" : false, + "latedef" : true, + "laxbreak" : false, + "loopfunc" : true, + "maxerr" : 50, + "newcap" : true, + "noarg" : false, + "node" : true, + "noempty" : true, + "nonew" : true, + "nomen" : false, + "onevar" : false, + "passfail" : false, + "plusplus" : false, + "prototypejs" : true, + "regexdash" : true, + "regexp" : false, + "rhino" : true, + "undef" : true, + "safe" : false, + "shadow" : true, + "strict" : false, + "sub" : true, + "supernew" : true, + "trailing" : true, + "white" : false, + "wsh" : true, + "indent" : 4, + + "predef" : [ + "ActiveXObject" + ] +} \ No newline at end of file diff --git a/src/bower_components/stacktrace.js/package.json b/src/bower_components/stacktrace.js/package.json new file mode 100644 index 0000000000000..d40a80a028198 --- /dev/null +++ b/src/bower_components/stacktrace.js/package.json @@ -0,0 +1,20 @@ +{ + "name": "stacktrace-js", + "description": "Framework-agnostic, micro-library for getting stack traces in all environments", + "author": "Eric Wendelin (http://eriwen.com)", + "version": "0.6.0", + "keywords": ["stack-trace", "cross-browser", "framework-agnostic", "client", "browser"], + "homepage": "http://stacktracejs.com", + "repository": { + "type": "git", + "url": "git://github.com/eriwen/javascript-stacktrace.git" + }, + "main": "./stacktrace.js", + "engines": { + "node": "*" + }, + "dependencies": {}, + "devDependencies": { + "jshint": "0.9.x" + } +} diff --git a/src/bower_components/stacktrace.js/stacktrace-bookmarklet.js b/src/bower_components/stacktrace.js/stacktrace-bookmarklet.js new file mode 100644 index 0000000000000..f8ea3a20c1c9d --- /dev/null +++ b/src/bower_components/stacktrace.js/stacktrace-bookmarklet.js @@ -0,0 +1 @@ +javascript:(function(window,document){ldJS=function(){s=document.createElement('script');s.type='text/javascript';s.src='https://github.com/eriwen/javascript-stacktrace/raw/master/stacktrace.js';document.getElementsByTagName('head')[0].appendChild(s);};aT=function(){alert(printStackTrace().join('\n'))};aTWE=function(){window.onerror=aT};aTCF=function(fn){eval('_old_'+fn+'='+fn+';function%20'+fn+'(args){aT();_old_'+fn+'.call(this,args);}')};c=document.createElement('div');cs=c.style;cs.position='fixed';cs.top='0';cs.right='0';cs.backgroundColor='#ddd';cs.padding='0.3em';cs.margin='0%20auto';t=document.createElement('span');ts=t.style;ts.fontWeight='bold';t.innerHTML='Javascript%20Stacktrace:%20';c.appendChild(t);b0=document.createElement('input');b0.type='button';b0.value='Load%20stacktrace.js';b0.style.margin='0%201em';b0.onclick=ldJS;c.appendChild(b0);b1=document.createElement('input');b1.type='button';b1.value='Attach%20to%20window.onerror';b1.style.margin='0%201em';b1.onclick=aTWE;c.appendChild(b1);i=document.createElement('input');i.type='text';c.appendChild(i);b2=document.createElement('input');b2.type='button';b2.value='Attach%20to%20custom%20function';b2.style.marginRight='2em';b2.onclick=function(){aTCF(i.value)};c.appendChild(b2);cl=document.createElement('A');cl.href='javascript:void(0);';cl.onclick=function(){c.parentNode.removeChild(c);};cl.innerHTML='close';c.appendChild(cl);document.body.appendChild(c);})(window,document); \ No newline at end of file diff --git a/src/bower_components/stacktrace.js/stacktrace.js b/src/bower_components/stacktrace.js/stacktrace.js new file mode 100644 index 0000000000000..92169fd825fc1 --- /dev/null +++ b/src/bower_components/stacktrace.js/stacktrace.js @@ -0,0 +1,485 @@ +// Domain Public by Eric Wendelin http://eriwen.com/ (2008) +// Luke Smith http://lucassmith.name/ (2008) +// Loic Dachary (2008) +// Johan Euphrosine (2008) +// Oyvind Sean Kinsey http://kinsey.no/blog (2010) +// Victor Homyakov (2010) +/*global module, exports, define, ActiveXObject*/ +(function(global, factory) { + if (typeof exports === 'object') { + // Node + module.exports = factory(); + } else if (typeof define === 'function' && define.amd) { + // AMD + define(factory); + } else { + // Browser globals + global.printStackTrace = factory(); + } +}(this, function() { + /** + * 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(); + 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'; + } + + if (e.stack && e.sourceURL) { + return 'safari'; + } + + if (e.stack && e.number) { + return 'ie'; + } + + if (e.stack && e.fileName) { + return 'firefox'; + } + + if (e.message && e['opera#sourceloc']) { + // e.message.indexOf("Backtrace:") > -1 -> opera9 + // 'opera#sourceloc' in e -> opera9, opera10a + // !e.stacktrace -> opera9 + if (!e.stacktrace) { + return 'opera9'; // use e.message + } + if (e.message.indexOf('\n') > -1 && e.message.split('\n').length > e.stacktrace.split('\n').length) { + // e.message may have more stack entries than e.stacktrace + return 'opera9'; // use e.message + } + return 'opera10a'; // use e.stacktrace + } + + if (e.message && e.stack && 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' + } + + if (e.stack && !e.fileName) { + // Chrome 27 does not have e.arguments as earlier versions, + // but still does not have e.fileName as Firefox + return 'chrome'; + } + + 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} callback 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 of function calls, files and line numbers + */ + chrome: function(e) { + return (e.stack + '\n') + .replace(/^[\s\S]+?\s+at\s+/, ' at ') // remove message + .replace(/^\s+(at eval )?at\s+/gm, '') // remove 'at' and indentation + .replace(/^([^\(]+?)([\n$])/gm, '{anonymous}() ($1)$2') + .replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}() ($1)') + .replace(/^(.+) \((.+)\)$/gm, '$1@$2') + .split('\n') + .slice(0, -1); + }, + + /** + * Given an Error object, return a formatted Array based on Safari's stack string. + * + * @param e - Error object to inspect + * @return Array of function calls, files and line numbers + */ + safari: function(e) { + return e.stack.replace(/\[native code\]\n/m, '') + .replace(/^(?=\w+Error\:).*$\n/m, '') + .replace(/^@/gm, '{anonymous}()@') + .split('\n'); + }, + + /** + * Given an Error object, return a formatted Array based on IE's stack string. + * + * @param e - Error object to inspect + * @return Array of function calls, files and line numbers + */ + ie: function(e) { + return e.stack + .replace(/^\s*at\s+(.*)$/gm, '$1') + .replace(/^Anonymous function\s+/gm, '{anonymous}() ') + .replace(/^(.+)\s+\((.+)\)$/gm, '$1@$2') + .split('\n') + .slice(1); + }, + + /** + * Given an Error object, return a formatted Array based on Firefox's stack string. + * + * @param e - Error object to inspect + * @return Array of function calls, files and line numbers + */ + firefox: function(e) { + return e.stack.replace(/(?:\n@:0)?\s+$/m, '') + .replace(/^(?:\((\S*)\))?@/gm, '{anonymous}($1)@') + .split('\n'); + }, + + opera11: function(e) { + 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(//, "$1").replace(//, ANON); + result.push(fnName + '@' + location + ' -- ' + lines[i + 1].replace(/^\s+/, '')); + } + } + + return result; + }, + + opera10b: function(e) { + // "([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 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 5-, IE 9-, and others + other: function(curr) { + var ANON = '{anonymous}', fnRE = /function\s*([\w\-$]+)?\s*\(/i, stack = [], fn, args, maxStackSize = 10; + var slice = Array.prototype.slice; + while (curr && curr['arguments'] && stack.length < maxStackSize) { + fn = fnRE.test(curr.toString()) ? RegExp.$1 || ANON : ANON; + args = slice.call(curr['arguments'] || []); + stack[stack.length] = fn + '(' + this.stringifyArguments(args) + ')'; + try { + curr = curr.caller; + } catch (e) { + stack[stack.length] = '' + e; + break; + } + } + return stack; + }, + + /** + * Given arguments array as a String, substituting type names for non-string types. + * + * @param {Arguments,Array} args + * @return {String} 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) { + // TODO constructor comparison does not work for iframes + 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; + } else { + result[i] = '?'; + } + } + } + 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 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 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 JS source URL + * @return 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) + // 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 = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\s*[:=]\s*function\b/; + // {name} = eval() + var reFunctionEvaluation = /['"]?([$_A-Za-z][$_A-Za-z0-9]*)['"]?\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 '(?)'; + } + }; + + return printStackTrace; +})); diff --git a/src/bower_components/stacktrace.js/test/CapturedExceptions.js b/src/bower_components/stacktrace.js/test/CapturedExceptions.js new file mode 100644 index 0000000000000..95948e5ad91ef --- /dev/null +++ b/src/bower_components/stacktrace.js/test/CapturedExceptions.js @@ -0,0 +1,352 @@ +var CapturedExceptions = {}; + +CapturedExceptions.opera_854 = { + message: "Statement on line 44: Type mismatch (usually a non-object value used where an object is required)\n" + + "Backtrace:\n" + + " Line 44 of linked script file://localhost/G:/js/stacktrace.js\n" + + " this.undef();\n" + + " Line 31 of linked script file://localhost/G:/js/stacktrace.js\n" + + " ex = ex || this.createException();\n" + + " Line 18 of linked script file://localhost/G:/js/stacktrace.js\n" + + " var p = new printStackTrace.implementation(), result = p.run(ex);\n" + + " Line 4 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " printTrace(printStackTrace());\n" + + " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " bar(n - 1);\n" + + " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " bar(2);\n" + + " Line 15 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " foo();\n" + + "", + 'opera#sourceloc': 44 +}; + +CapturedExceptions.opera_902 = { + message: "Statement on line 44: Type mismatch (usually a non-object value used where an object is required)\n" + + "Backtrace:\n" + + " Line 44 of linked script file://localhost/G:/js/stacktrace.js\n" + + " this.undef();\n" + + " Line 31 of linked script file://localhost/G:/js/stacktrace.js\n" + + " ex = ex || this.createException();\n" + + " Line 18 of linked script file://localhost/G:/js/stacktrace.js\n" + + " var p = new printStackTrace.implementation(), result = p.run(ex);\n" + + " Line 4 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " printTrace(printStackTrace());\n" + + " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " bar(n - 1);\n" + + " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " bar(2);\n" + + " Line 15 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " foo();\n" + + "", + 'opera#sourceloc': 44 +}; + +CapturedExceptions.opera_927 = { + message: "Statement on line 43: Type mismatch (usually a non-object value used where an object is required)\n" + + "Backtrace:\n" + + " Line 43 of linked script file://localhost/G:/js/stacktrace.js\n" + + " this.undef();\n" + + " Line 31 of linked script file://localhost/G:/js/stacktrace.js\n" + + " ex = ex || this.createException();\n" + + " Line 18 of linked script file://localhost/G:/js/stacktrace.js\n" + + " var p = new printStackTrace.implementation(), result = p.run(ex);\n" + + " Line 4 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " printTrace(printStackTrace());\n" + + " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " bar(n - 1);\n" + + " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " bar(2);\n" + + " Line 15 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " foo();\n" + + "", + 'opera#sourceloc': 43 +}; + +CapturedExceptions.opera_964 = { + message: "Statement on line 42: Type mismatch (usually non-object value supplied where object required)\n" + + "Backtrace:\n" + + " Line 42 of linked script file://localhost/G:/js/stacktrace.js\n" + + " this.undef();\n" + + " Line 27 of linked script file://localhost/G:/js/stacktrace.js\n" + + " ex = ex || this.createException();\n" + + " Line 18 of linked script file://localhost/G:/js/stacktrace.js: In function printStackTrace\n" + + " var p = new printStackTrace.implementation(), result = p.run(ex);\n" + + " Line 4 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function bar\n" + + " printTrace(printStackTrace());\n" + + " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function bar\n" + + " bar(n - 1);\n" + + " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n" + + " bar(2);\n" + + " Line 15 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " foo();\n" + + "", + 'opera#sourceloc': 42, + stacktrace: " ... Line 27 of linked script file://localhost/G:/js/stacktrace.js\n" + + " ex = ex || this.createException();\n" + + " Line 18 of linked script file://localhost/G:/js/stacktrace.js: In function printStackTrace\n" + + " var p = new printStackTrace.implementation(), result = p.run(ex);\n" + + " Line 4 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function bar\n" + + " printTrace(printStackTrace());\n" + + " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function bar\n" + + " bar(n - 1);\n" + + " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n" + + " bar(2);\n" + + " Line 15 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " foo();\n" + + "" +}; + +CapturedExceptions.opera_1010 = { + message: "Statement on line 42: Type mismatch (usually non-object value supplied where object required)", + 'opera#sourceloc': 42, + stacktrace: " Line 42 of linked script file://localhost/G:/js/stacktrace.js\n" + + " this.undef();\n" + + " Line 27 of linked script file://localhost/G:/js/stacktrace.js\n" + + " ex = ex || this.createException();\n" + + " Line 18 of linked script file://localhost/G:/js/stacktrace.js: In function printStackTrace\n" + + " var p = new printStackTrace.implementation(), result = p.run(ex);\n" + + " Line 4 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function bar\n" + + " printTrace(printStackTrace());\n" + + " Line 7 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function bar\n" + + " bar(n - 1);\n" + + " Line 11 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html: In function foo\n" + + " bar(2);\n" + + " Line 15 of inline#1 script in file://localhost/G:/js/test/functional/testcase1.html\n" + + " foo();\n" + + "" +}; + +CapturedExceptions.opera_1063 = { + message: "'this.undef' is not a function", + stack: "([arguments not available])@file://localhost/G:/js/stacktrace.js:42\n" + + "([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" + + "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" + + "bar([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:4\n" + + "bar([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:7\n" + + "foo([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:11\n" + + "@file://localhost/G:/js/test/functional/testcase1.html:15", + stacktrace: "([arguments not available])@file://localhost/G:/js/stacktrace.js:42\n" + + "([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" + + "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" + + "bar([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:4\n" + + "bar([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:7\n" + + "foo([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:11\n" + + "@file://localhost/G:/js/test/functional/testcase1.html:15" +}; + +CapturedExceptions.opera_1111 = { + message: "'this.undef' is not a function", + stack: "([arguments not available])@file://localhost/G:/js/stacktrace.js:42\n" + + "([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" + + "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" + + "bar([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:4\n" + + "bar([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:7\n" + + "foo([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:11\n" + + "@file://localhost/G:/js/test/functional/testcase1.html:15", + stacktrace: "Error thrown at line 42, column 12 in () in file://localhost/G:/js/stacktrace.js:\n" + + " this.undef();\n" + + "called from line 27, column 8 in (ex) in file://localhost/G:/js/stacktrace.js:\n" + + " ex = ex || this.createException();\n" + + "called from line 18, column 4 in printStackTrace(options) in file://localhost/G:/js/stacktrace.js:\n" + + " var p = new printStackTrace.implementation(), result = p.run(ex);\n" + + "called from line 4, column 5 in bar(n) in file://localhost/G:/js/test/functional/testcase1.html:\n" + + " printTrace(printStackTrace());\n" + + "called from line 7, column 4 in bar(n) in file://localhost/G:/js/test/functional/testcase1.html:\n" + + " bar(n - 1);\n" + + "called from line 11, column 4 in foo() in file://localhost/G:/js/test/functional/testcase1.html:\n" + + " bar(2);\n" + + "called from line 15, column 3 in file://localhost/G:/js/test/functional/testcase1.html:\n" + + " foo();" +}; + +CapturedExceptions.opera_1151 = { + message: "'this.undef' is not a function", + stack: "([arguments not available])@file://localhost/G:/js/stacktrace.js:42\n" + + "([arguments not available])@file://localhost/G:/js/stacktrace.js:27\n" + + "printStackTrace([arguments not available])@file://localhost/G:/js/stacktrace.js:18\n" + + "bar([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:4\n" + + "bar([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:7\n" + + "foo([arguments not available])@file://localhost/G:/js/test/functional/testcase1.html:11\n" + + "@file://localhost/G:/js/test/functional/testcase1.html:15", + stacktrace: "Error thrown at line 42, column 12 in () in file://localhost/G:/js/stacktrace.js:\n" + + " this.undef();\n" + + "called from line 27, column 8 in (ex) in file://localhost/G:/js/stacktrace.js:\n" + + " ex = ex || this.createException();\n" + + "called from line 18, column 4 in printStackTrace(options) in file://localhost/G:/js/stacktrace.js:\n" + + " var p = new printStackTrace.implementation(), result = p.run(ex);\n" + + "called from line 4, column 5 in bar(n) in file://localhost/G:/js/test/functional/testcase1.html:\n" + + " printTrace(printStackTrace());\n" + + "called from line 7, column 4 in bar(n) in file://localhost/G:/js/test/functional/testcase1.html:\n" + + " bar(n - 1);\n" + + "called from line 11, column 4 in foo() in file://localhost/G:/js/test/functional/testcase1.html:\n" + + " bar(2);\n" + + "called from line 15, column 3 in file://localhost/G:/js/test/functional/testcase1.html:\n" + + " foo();" +}; + +CapturedExceptions.opera_1216 = { + message: "Cannot convert 'x' to object", + name: "TypeError", + stack: "([arguments not available])@http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.js:4\n" + + "createException([arguments not available])@http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.js:2\n" + + "createException4([arguments not available])@http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.html:56\n" + + "dumpException4([arguments not available])@http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.html:60\n" + + "([arguments not available])@http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.html:1", + stacktrace: "Error thrown at line 4, column 6 in (x) in http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.js:\n" + + " x.undef();\n" + + "called from line 2, column 2 in createException() in http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.js:\n" + + " return ((function(x) {\n" + + "called from line 56, column 8 in createException4() in http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.html:\n" + + " return createException();\n" + + "called from line 60, column 8 in dumpException4() in http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.html:\n" + + " dumpException(createException4());\n" + + "called from line 1, column 0 in (event) in http://localhost:63342/javascript-stacktrace/test/functional/ExceptionLab.html:\n" + + " dumpException4();" +}; + +CapturedExceptions.chrome_15 = { + 'arguments': ["undef"], + message: "Object # has no method 'undef'", + stack: "TypeError: Object # has no method 'undef'\n" + + " at Object.createException (http://127.0.0.1:8000/js/stacktrace.js:42:18)\n" + + " at Object.run (http://127.0.0.1:8000/js/stacktrace.js:31:25)\n" + + " at printStackTrace (http://127.0.0.1:8000/js/stacktrace.js:18:62)\n" + + " at bar (http://127.0.0.1:8000/js/test/functional/testcase1.html:13:17)\n" + + " at bar (http://127.0.0.1:8000/js/test/functional/testcase1.html:16:5)\n" + + " at foo (http://127.0.0.1:8000/js/test/functional/testcase1.html:20:5)\n" + + " at http://127.0.0.1:8000/js/test/functional/testcase1.html:24:4" +}; + +CapturedExceptions.chrome_27 = { + message: "Cannot call method 'undef' of null", + name: "TypeError", + stack: "TypeError: Cannot call method 'undef' of null\n" + + " at file:///E:/javascript-stacktrace/test/functional/ExceptionLab.js:4:9\n" + + " at createException (file:///E:/javascript-stacktrace/test/functional/ExceptionLab.js:8:5)\n" + + " at createException4 (file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:56:16)\n" + + " at dumpException4 (file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:60:23)\n" + + " at HTMLButtonElement.onclick (file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:83:126)" +}; + +CapturedExceptions.chrome_31_multiline_message = { + message: "Object function () {\n" + + " return {\n" + + " name: \"provide multi-line source in exception\"\n" + + " };\n" + + " } has no method 'nonExistentMethod'", + name: "TypeError", + stack: "TypeError: Object function () {\n" + + " return {\n" + + " name: \"provide multi-line source in exception\"\n" + + " };\n" + + " } has no method 'nonExistentMethod'\n" + + " at dumpException6 (file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:82:20)\n" + + " at HTMLButtonElement.onclick (file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:101:122)" +}; + +CapturedExceptions.firefox_3_6 = { + fileName: "http://127.0.0.1:8000/js/stacktrace.js", + lineNumber: 44, + message: "this.undef is not a function", + name: "TypeError", + stack: "()@http://127.0.0.1:8000/js/stacktrace.js:44\n" + + "(null)@http://127.0.0.1:8000/js/stacktrace.js:31\n" + + "printStackTrace()@http://127.0.0.1:8000/js/stacktrace.js:18\n" + + "bar(1)@http://127.0.0.1:8000/js/test/functional/testcase1.html:13\n" + + "bar(2)@http://127.0.0.1:8000/js/test/functional/testcase1.html:16\n" + + "foo()@http://127.0.0.1:8000/js/test/functional/testcase1.html:20\n" + + "@http://127.0.0.1:8000/js/test/functional/testcase1.html:24\n" + + "" +}; + +CapturedExceptions.firefox_3_6_file = { + fileName: "file:///home/user/js/stacktrace.js", + lineNumber: 44, + message: "this.undef is not a function", + name: "TypeError", + stack: "()@file:///home/user/js/stacktrace.js:44\n" + + "(null)@file:///home/user/js/stacktrace.js:31\n" + + "printStackTrace()@file:///home/user/js/stacktrace.js:18\n" + + "bar(1)@file:///home/user/js/test/functional/testcase1.html:13\n" + + "bar(2)@file:///home/user/js/test/functional/testcase1.html:16\n" + + "foo()@file:///home/user/js/test/functional/testcase1.html:20\n" + + "@file:///home/user/js/test/functional/testcase1.html:24\n" + + "" +}; + +CapturedExceptions.firefox_7 = { + fileName: "file:///G:/js/stacktrace.js", + lineNumber: 44, + stack: "()@file:///G:/js/stacktrace.js:44\n" + + "(null)@file:///G:/js/stacktrace.js:31\n" + + "printStackTrace()@file:///G:/js/stacktrace.js:18\n" + + "bar(1)@file:///G:/js/test/functional/testcase1.html:13\n" + + "bar(2)@file:///G:/js/test/functional/testcase1.html:16\n" + + "foo()@file:///G:/js/test/functional/testcase1.html:20\n" + + "@file:///G:/js/test/functional/testcase1.html:24\n" + + "" +}; + +CapturedExceptions.firefox_14 = { + message: "x is null", + stack: "@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:48\n" + + "dumpException3@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:52\n" + + "onclick@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:1\n" + + "", + fileName: "file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html", + lineNumber: 48 +}; + +CapturedExceptions.firefox_22 = { + message: "x is null", + name: "TypeError", + stack: "@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.js:4\n" + + "createException@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.js:8\n" + + "createException4@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:56\n" + + "dumpException4@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:60\n" + + "onclick@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:1\n" + + "", + fileName: "file:///E:/javascript-stacktrace/test/functional/ExceptionLab.js", + lineNumber: 4, + columnNumber: 6 +}; + +CapturedExceptions.safari_6 = { + message: "'null' is not an object (evaluating 'x.undef')", + stack: "@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:48\n" + + "dumpException3@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:52\n" + + "onclick@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:82\n" + + "[native code]", + line: 48, + sourceURL: "file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html" +}; + +CapturedExceptions.ie_10 = { + message: "Unable to get property 'undef' of undefined or null reference", + name: "TypeError", + stack: "TypeError: Unable to get property 'undef' of undefined or null reference\n" + + " at Anonymous function (http://jenkins.eriwen.com/job/stacktrace.js/ws/test/functional/ExceptionLab.html:48:13)\n" + + " at dumpException3 (http://jenkins.eriwen.com/job/stacktrace.js/ws/test/functional/ExceptionLab.html:46:9)\n" + + " at onclick (http://jenkins.eriwen.com/job/stacktrace.js/ws/test/functional/ExceptionLab.html:82:1)", + description: "Unable to get property 'undef' of undefined or null reference", + number: -2146823281 +}; + +CapturedExceptions.node_simple = { + message: 'x is not defined', + name: 'ReferenceError', + type: 'not_defined', + stack: 'ReferenceError: x is not defined\n' + + ' at repl:1:5\n' + + ' at REPLServer.self.eval (repl.js:110:21)\n' + + ' at repl.js:249:20\n' + + ' at REPLServer.self.eval (repl.js:122:7)\n' + + ' at Interface. (repl.js:239:12)\n' + + ' at Interface.EventEmitter.emit (events.js:95:17)\n' + + ' at Interface._onLine (readline.js:202:10)\n' + + ' at Interface._line (readline.js:531:8)\n' + + ' at Interface._ttyWrite (readline.js:760:14)\n' + + ' at ReadStream.onkeypress (readline.js:99:10)', + 'arguments': [ 'x' ] +}; diff --git a/src/bower_components/stacktrace.js/test/TestAMDStacktrace.html b/src/bower_components/stacktrace.js/test/TestAMDStacktrace.html new file mode 100644 index 0000000000000..c7d70cd4d6336 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/TestAMDStacktrace.html @@ -0,0 +1,44 @@ + + + + + + stacktrace.js Test Suite + + + + + + + + + + + +

stacktrace.js Test Suite

+

+

+
    + + diff --git a/src/bower_components/stacktrace.js/test/TestAMDStacktrace.js b/src/bower_components/stacktrace.js/test/TestAMDStacktrace.js new file mode 100644 index 0000000000000..a6d77584bfcbc --- /dev/null +++ b/src/bower_components/stacktrace.js/test/TestAMDStacktrace.js @@ -0,0 +1,43 @@ +/*global module, test, equals, expect, ok, printStackTrace, CapturedExceptions */ +// +// Copyright (C) 2008 Loic Dachary +// Copyright (C) 2008 Johan Euphrosine +// Copyright (C) 2010 Eric Wendelin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +// Setup a mapping to stacktrace for the unit tests +require.config({ + paths: { + 'stacktrace': '../stacktrace' + } +}); + +(function(window, document, undefined) { + module("AMD invocation"); + + test("printStackTrace", function() { + expect(1); + stop(); + require(['stacktrace'], function(printStackTrace) { + var r = printStackTrace(); + equals(r.constructor, Array, 'printStackTrace returns an array'); + start(); + }); + }); +})(window, document); + +// Start QUnit since we set autostart to false +QUnit.start(); \ No newline at end of file diff --git a/src/bower_components/stacktrace.js/test/TestStacktrace.html b/src/bower_components/stacktrace.js/test/TestStacktrace.html new file mode 100644 index 0000000000000..27e9ebb9f55ae --- /dev/null +++ b/src/bower_components/stacktrace.js/test/TestStacktrace.html @@ -0,0 +1,47 @@ + + + + + + stacktrace.js Test Suite + + + + + + + + + + +

    stacktrace.js Test Suite

    +

    +

    +
      + + diff --git a/src/bower_components/stacktrace.js/test/TestStacktrace.js b/src/bower_components/stacktrace.js/test/TestStacktrace.js new file mode 100644 index 0000000000000..0acc428c363fa --- /dev/null +++ b/src/bower_components/stacktrace.js/test/TestStacktrace.js @@ -0,0 +1,888 @@ +/*global module, test, equals, expect, ok, printStackTrace, CapturedExceptions */ +/*jshint bitwise:true, curly:true, forin:true, latedef:true, noarg:true, noempty:true, nonew:true, undef:true, trailing:true, indent:4, browser:true */ +// +// Copyright (C) 2008 Loic Dachary +// Copyright (C) 2008 Johan Euphrosine +// Copyright (C) 2010 Eric Wendelin +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . +// + +(function(window, document, undefined) { + //region setup + var pst = printStackTrace.implementation.prototype; + + var impl = function() { + return new printStackTrace.implementation(); + }; + + var ex; + try { + this.undef(); + } catch (exception) { + ex = exception; + } + + // Testing util functions + var UnitTest = function() { + }; + UnitTest.fn = UnitTest.prototype = { + genericError: null, + createGenericError: function() { + if (UnitTest.prototype.genericError != null) { + return UnitTest.prototype.genericError; + } + //return new Error("Generic error"); + return new Error(); + }, + createModeStub: function(mode) { + return function() { + ok(false, 'must not call run() for mode "' + mode + '"'); + }; + }, + createModeStubs: function(p, stub) { + var modes = ['other', 'opera9', 'opera10a', 'opera10b', 'opera11', 'firefox', 'safari', 'ie', 'chrome']; + for (var i = 0, len = modes.length; i < len; i++) { + var mode = modes[i]; + p[mode] = stub || this.createModeStub(mode); + } + } + }; + //endregion + + //region invocation + module("invocation"); + + test("printStackTrace", function() { + expect(1); + var r = printStackTrace(); + equals(r.constructor, Array, 'printStackTrace returns an array'); + }); + + test("printStackTrace options", function() { + expect(1); + var guessAnonymousFunctions = pst.guessAnonymousFunctions; + pst.guessAnonymousFunctions = function() { + pst.guessAnonymousFunctions = guessAnonymousFunctions; + ok(true, 'guessAnonymousFunctions called'); + }; + printStackTrace({ + guess: true + }); + }); + //endregion + + //region mode + module("mode"); + + test("mode", function() { + expect(1); + equals("chrome safari firefox ie other opera9 opera10a opera10b opera11".indexOf(pst.mode(UnitTest.fn.createGenericError())) >= 0, true); + }); + + test("run mode", function() { + expect(1); + var p = impl(); + UnitTest.fn.createModeStubs(p, function() { + ok(true, 'called mode() successfully'); + }); + p.run(); + }); + + test("run chrome", function() { + expect(2); + var p = impl(); + UnitTest.fn.createModeStubs(p); + p.chrome = function() { + ok(true, 'called run() for "chrome"'); + }; + p.run(CapturedExceptions.chrome_15); + p.run(CapturedExceptions.chrome_27); + }); + + test("run safari", function() { + expect(1); + var p = impl(); + UnitTest.fn.createModeStubs(p); + p.safari = function() { + ok(true, 'called run() for "safari"'); + }; + p.run(CapturedExceptions.safari_6); + }); + + test("run ie", function() { + expect(1); + var p = impl(); + UnitTest.fn.createModeStubs(p); + p.ie = function() { + ok(true, 'called run() for "ie"'); + }; + p.run(CapturedExceptions.ie_10); + }); + + test("run firefox", function() { + expect(5); + var p = impl(); + UnitTest.fn.createModeStubs(p); + p.firefox = function() { + ok(true, 'called run() for "firefox"'); + }; + p.run(CapturedExceptions.firefox_3_6); + p.run(CapturedExceptions.firefox_3_6_file); + p.run(CapturedExceptions.firefox_7); + p.run(CapturedExceptions.firefox_14); + p.run(CapturedExceptions.firefox_22); + }); + + test("run opera9", function() { + expect(4); + var p = impl(); + UnitTest.fn.createModeStubs(p); + p.opera9 = function() { + ok(true, 'called run() for "opera9"'); + }; + p.run(CapturedExceptions.opera_854); + p.run(CapturedExceptions.opera_902); + p.run(CapturedExceptions.opera_927); + p.run(CapturedExceptions.opera_964); + }); + + test("run opera10a", function() { + expect(1); + var p = impl(); + UnitTest.fn.createModeStubs(p); + p.opera10a = function() { + ok(true, 'called run() for "opera10a"'); + }; + p.run(CapturedExceptions.opera_1010); + }); + + test("run opera10b", function() { + expect(1); + var p = impl(); + UnitTest.fn.createModeStubs(p); + p.opera10b = function() { + ok(true, 'called run() for "opera10b"'); + }; + p.run(CapturedExceptions.opera_1063); + }); + + test("run opera11", function() { + expect(3); + var p = impl(); + UnitTest.fn.createModeStubs(p); + p.opera11 = function() { + ok(true, 'called run() for "opera11"'); + }; + p.run(CapturedExceptions.opera_1111); + p.run(CapturedExceptions.opera_1151); + p.run(CapturedExceptions.opera_1216); + }); + + test("run other", function() { + expect(1); + var p = impl(); + UnitTest.fn.createModeStubs(p); + p.other = function() { + ok(true, 'called run() for other browser'); + }; + p.run({}); + }); + + test("function instrumentation", function() { + expect(4); + this.toInstrument = function() { + ok(true, 'called instrumented function'); + }; + this.callback = function(stacktrace) { + ok(typeof stacktrace !== 'undefined', 'called callback'); + }; + pst.instrumentFunction(this, 'toInstrument', this.callback); + ok(this.toInstrument._instrumented, 'function instrumented'); + this.toInstrument(); + pst.deinstrumentFunction(this, 'toInstrument'); + ok(!this.toInstrument._instrumented, 'function deinstrumented'); + this.toInstrument = this.callback = null; + }); + + test("firefox", function() { + expect(34); + + var message = pst.firefox(CapturedExceptions.firefox_3_6); + // equals(message.join('\n'), '', 'processed stack trace'); + equals(message.length, 7, 'Firefox 3.6: 7 stack entries'); + equals(message[0], '{anonymous}()@http://127.0.0.1:8000/js/stacktrace.js:44'); + equals(message[1], '{anonymous}(null)@http://127.0.0.1:8000/js/stacktrace.js:31'); + equals(message[2], 'printStackTrace()@http://127.0.0.1:8000/js/stacktrace.js:18'); + equals(message[3], 'bar(1)@http://127.0.0.1:8000/js/test/functional/testcase1.html:13'); + equals(message[4], 'bar(2)@http://127.0.0.1:8000/js/test/functional/testcase1.html:16'); + equals(message[5], 'foo()@http://127.0.0.1:8000/js/test/functional/testcase1.html:20'); + equals(message[6], '{anonymous}()@http://127.0.0.1:8000/js/test/functional/testcase1.html:24'); + + message = pst.firefox(CapturedExceptions.firefox_3_6_file); + equals(message.length, 7, 'Firefox 3.6: 7 stack entries'); + equals(message[0], '{anonymous}()@file:///home/user/js/stacktrace.js:44'); + equals(message[1], '{anonymous}(null)@file:///home/user/js/stacktrace.js:31'); + equals(message[2], 'printStackTrace()@file:///home/user/js/stacktrace.js:18'); + equals(message[3], 'bar(1)@file:///home/user/js/test/functional/testcase1.html:13'); + equals(message[4], 'bar(2)@file:///home/user/js/test/functional/testcase1.html:16'); + equals(message[5], 'foo()@file:///home/user/js/test/functional/testcase1.html:20'); + equals(message[6], '{anonymous}()@file:///home/user/js/test/functional/testcase1.html:24'); + + message = pst.firefox(CapturedExceptions.firefox_7); + equals(message.length, 7, 'Firefox 7: 7 stack entries'); + equals(message[0], '{anonymous}()@file:///G:/js/stacktrace.js:44'); + equals(message[1], '{anonymous}(null)@file:///G:/js/stacktrace.js:31'); + equals(message[2], 'printStackTrace()@file:///G:/js/stacktrace.js:18'); + equals(message[3], 'bar(1)@file:///G:/js/test/functional/testcase1.html:13'); + equals(message[4], 'bar(2)@file:///G:/js/test/functional/testcase1.html:16'); + equals(message[5], 'foo()@file:///G:/js/test/functional/testcase1.html:20'); + equals(message[6], '{anonymous}()@file:///G:/js/test/functional/testcase1.html:24'); + + message = pst.firefox(CapturedExceptions.firefox_14); + equals(message.length, 3, 'Firefox 14: 3 stack entries'); + equals(message[0], '{anonymous}()@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:48'); + equals(message[1], 'dumpException3@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:52'); + equals(message[2], 'onclick@file:///Users/eric/src/javascript-stacktrace/test/functional/ExceptionLab.html:1'); + + message = pst.firefox(CapturedExceptions.firefox_22); + equals(message.length, 5, 'Firefox 22: 7 stack entries'); + equals(message[0], '{anonymous}()@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.js:4'); + equals(message[1], 'createException@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.js:8'); + equals(message[2], 'createException4@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:56'); + equals(message[3], 'dumpException4@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:60'); + equals(message[4], 'onclick@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:1'); + }); + + if (pst.mode(ex) == 'firefox') { + test("firefox live", function() { + function f1(arg1, arg2) { + try { + return this.undef(); + } catch (exception) { + return exception; + } + } + + var f2 = function() { + return f1(1, "abc"); + }; + + var e = (function() { + return f2(); + })(); + + expect(2); + var message = pst.firefox(e); + // equals(message.join('\n'), '', 'processed stack trace'); + equals(message[0].indexOf('f1@'), 0, message[0] + ' should start with f1@'); + equals(message[1].indexOf('f2@'), 0, message[1] + ' should start with f2@'); + //equals(message[2].indexOf('{anonymous}()@'), 0, message[2] + ' should start with {anonymous}()@'); + }); + } + + test("chrome", function() { + expect(17); + + var message = pst.chrome(CapturedExceptions.chrome_15); + // equals(message.join('\n'), '', 'processed stack trace'); + equals(message.length, 7, 'Chrome 15: 7 stack entries'); + equals(message[0], 'Object.createException@http://127.0.0.1:8000/js/stacktrace.js:42:18'); + equals(message[1], 'Object.run@http://127.0.0.1:8000/js/stacktrace.js:31:25'); + equals(message[2], 'printStackTrace@http://127.0.0.1:8000/js/stacktrace.js:18:62'); + equals(message[3], 'bar@http://127.0.0.1:8000/js/test/functional/testcase1.html:13:17'); + equals(message[4], 'bar@http://127.0.0.1:8000/js/test/functional/testcase1.html:16:5'); + equals(message[5], 'foo@http://127.0.0.1:8000/js/test/functional/testcase1.html:20:5'); + equals(message[6], '{anonymous}()@http://127.0.0.1:8000/js/test/functional/testcase1.html:24:4'); + + message = pst.chrome(CapturedExceptions.chrome_27); + equals(message.length, 5, 'Chrome 27: 5 stack entries'); + equals(message[0], '{anonymous}()@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.js:4:9'); + equals(message[1], 'createException@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.js:8:5'); + equals(message[2], 'createException4@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:56:16'); + equals(message[3], 'dumpException4@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:60:23'); + equals(message[4], 'HTMLButtonElement.onclick@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:83:126'); + + message = pst.chrome(CapturedExceptions.chrome_31_multiline_message); + equals(message.length, 2, 'Chrome 31: 2 stack entries'); + equals(message[0], 'dumpException6@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:82:20'); + equals(message[1], 'HTMLButtonElement.onclick@file:///E:/javascript-stacktrace/test/functional/ExceptionLab.html:101:122'); + }); + + if (pst.mode(ex) == 'chrome') { + test("chrome live", function() { + function f1(arg1, arg2) { + try { + return this.undef(); + } catch (exception) { + return exception; + } + } + + var f2 = function() { + return f1(1, "abc"); + }; + + var e = (function() { + return f2(); + })(); + + expect(3); + var message = pst.chrome(e); + //equals(e.stack, '', 'original stack trace'); + //equals(message.join('\n'), '', 'processed stack trace'); + equals(message[0].indexOf('f1@'), 0, message[0] + ' should start with f1@'); + equals(message[1].indexOf('f2@'), 0, message[1] + ' should start with f2@'); + equals(message[2].indexOf('{anonymous}()@'), 0, message[2] + ' should start with {anonymous}()@'); + }); + } + + test("opera9", function() { + var mode = pst.mode(UnitTest.fn.createGenericError()), e = []; + if (mode == 'opera9') { + function discarded() { + try { + this.undef(); + } catch (exception) { + e.push(exception); + } + } + + function f1(arg1, arg2) { + discarded(); + } + + var f2 = function() { + f1(1, "abc"); + }; + f2(); + } + expect(3 * e.length); + for (var i = 0; i < e.length; i++) { + var message = pst.opera9(e[i]); + var message_string = message.join("\n"); + //equals(message.join("\n"), 'debug', 'debug'); + //equals(message[0].indexOf('f1()') >= 0, true, 'f1 function name'); + equals(message[1].indexOf('discarded()') >= 0, true, 'discarded() statement in f1: ' + message[1]); + equals(message[2].indexOf('{anonymous}()@') >= 0, true, 'f2 is anonymous: ' + message[2]); + equals(message[2].indexOf('f1(1, "abc")') >= 0, true, 'f1() statement in f2: ' + message[2]); + } + }); + + test("opera9", function() { + var e = [CapturedExceptions.opera_854, CapturedExceptions.opera_902, CapturedExceptions.opera_927, CapturedExceptions.opera_964]; + expect(12); // 3 * e.length + for (var i = 0; i < e.length; i++) { + var message = pst.opera9(e[i]); + //equals(message.join("\n"), 'debug', 'debug'); + equals(message.length, 7, 'number of stack entries'); + equals(message[0].indexOf('this.undef()') >= 0, true, 'this.undef() is at the top of stack'); + equals(message[message.length - 1].indexOf('foo()') >= 0, true, 'foo() is at the bottom of stack'); + } + }); + + test("opera10a", function() { + var e = [CapturedExceptions.opera_1010]; + expect(5); // 5 * e.length + for (var i = 0; i < e.length; i++) { + var message = pst.opera10a(e[i]); + //equals(message.join("\n"), 'debug', 'debug'); + equals(message.length, 7, 'number of stack entries'); + equals(message[0].indexOf('this.undef()') >= 0, true, 'this.undef() is at the top of stack'); + equals(message[message.length - 3].indexOf('bar(') >= 0, true, 'bar is 3rd from the bottom of stack'); + equals(message[message.length - 2].indexOf('bar(2)') >= 0, true, 'bar is 2nd from the bottom of stack'); + equals(message[message.length - 1].indexOf('foo()') >= 0, true, 'foo() is at the bottom of stack'); + } + }); + + test("opera10b", function() { + var e = [CapturedExceptions.opera_1063]; + expect(3); // 3 * e.length + for (var i = 0; i < e.length; i++) { + var message = pst.opera10b(e[i]); + //equals(message.join("\n"), 'debug', 'debug'); + equals(message.length, 7, 'number of stack entries'); + equals(message[0].indexOf('createException') >= 0, true, 'createException() is at the top of stack'); + equals(message[message.length - 2].indexOf('foo') >= 0, true, 'foo() is 2nd from the bottom of stack'); + } + }); + + test("opera11", function() { + var e = [CapturedExceptions.opera_1111, CapturedExceptions.opera_1151]; + expect(6); // 3 * e.length + for (var i = 0; i < e.length; i++) { + var message = pst.opera11(e[i]); + //equals(message.join("\n"), 'debug', 'debug'); + equals(message.length, 7, 'number of stack entries'); + equals(message[0].indexOf('createException') >= 0, true, 'createException() is at the top of stack'); + equals(message[message.length - 2].indexOf('foo') >= 0, true, 'foo() is 2nd from the bottom of stack'); + } + }); + + test("opera11", function() { + var mode = pst.mode(UnitTest.fn.createGenericError()); + var e = []; + if (mode == 'opera11') { + function discarded() { + try { + this.undef(); + } catch (exception) { + e.push(exception); + } + } + + function f1(arg1, arg2) { + var blah = arg1; + discarded(); + } + + var f2 = function() { + f1(1, "abc"); + }; + f2(); + } + expect(3 * e.length); + for (var i = 0; i < e.length; i++) { + var stack = pst.opera11(e[i]), stack_string = stack.join('\n'); + //equals(stack_string, 'debug', 'debug'); + equals(stack_string.indexOf('ignored'), -1, 'ignored'); + equals(stack[1].indexOf('f1(') >= 0, true, 'f1 function name: ' + stack[1]); + equals(stack[2].indexOf('{anonymous}()') >= 0, true, 'f2 is anonymous: ' + stack[2]); + } + }); + + test("safari", function() { + var e = [], ex; + + function f0() { + try { + this.undef(); + } catch (exception) { + ex = exception; + } + } + + function f1(arg1, arg2) { + f0(); + } + + var f2 = function() { + f1(1, "abc"); + }; + f2(); + if (pst.mode(ex) == 'safari') { + e.push(ex); + } + expect(2 * e.length); + for (var i = 0; i < e.length; i++) { + var stack = pst.safari(e[i]), stack_string = stack.join('\n'); + //equals(stack_string, 'debug', 'debug'); + equals(stack[0].indexOf('f0') >= 0, true, 'matched f0'); + equals(stack[1].indexOf('f1') >= 0, true, 'f1 function name: ' + stack[1]); + } + }); + + if (pst.mode(ex) == 'ie') { + test("ie10 live", function() { + function f1(arg1, arg2) { + try { + return this.undef(); + } catch (exception) { + return exception; + } + } + + var f2 = function() { + return f1(1, "abc"); + }; + + var e = (function() { + return f2(); + })(); + + expect(3); + var message = pst.ie(e); + //equals(e.stack, '', 'original stack trace'); + //equals(message.join('\n'), '', 'processed stack trace'); + equals(message[0].indexOf('f1@'), 0, message[0] + ' should start with f1@'); + equals(message[1].indexOf('f2@'), 0, message[1] + ' should start with f2@'); + equals(message[2].indexOf('{anonymous}()@'), 0, message[2] + ' should start with {anonymous}()@'); + }); + } + + test("ie10", function() { + expect(4); + + var message = pst.ie(CapturedExceptions.ie_10); + equals(message.length, 3, '3 stack entries'); + equals(message[0], '{anonymous}()@http://jenkins.eriwen.com/job/stacktrace.js/ws/test/functional/ExceptionLab.html:48:13'); + equals(message[1], 'dumpException3@http://jenkins.eriwen.com/job/stacktrace.js/ws/test/functional/ExceptionLab.html:46:9'); + equals(message[2], 'onclick@http://jenkins.eriwen.com/job/stacktrace.js/ws/test/functional/ExceptionLab.html:82:1'); + }); + + test("other", function() { + var mode = pst.mode(UnitTest.fn.createGenericError()); + var frame = function(args, fun, caller) { + this['arguments'] = args; + this.caller = caller; + this.fun = fun; + }; + frame.prototype.toString = function() { + return 'function ' + this.fun + '() {}'; + }; + function f10() { + } + + var frame_f2 = new frame([], '', undefined); + var frame_f1 = new frame([1, 'abc', f10, { + 1: { + 2: { + 3: 4 + } + } + }], 'FUNCTION f1 (a,b,c)', frame_f2); + + expect(mode == 'other' ? 4 : 2); + var message = pst.other(frame_f1); + equals(message[0].indexOf('f1(1,"abc",#function,#object)') >= 0, true, 'f1'); + equals(message[1].indexOf('{anonymous}()') >= 0, true, 'f2 anonymous'); + + if (mode == 'other') { + function f1(arg1, arg2) { + var message = pst.other(arguments.callee); + //equals(message.join("\n"), '', 'debug'); + equals(message[0].indexOf('f1(1,"abc",#function,#object)') >= 0, true, 'f1'); + equals(message[1].indexOf('{anonymous}()') >= 0, true, 'f2 anonymous'); + } + + var f2 = function() { + f1(1, 'abc', f10, { + 1: { + 2: { + 3: 4 + } + } + }); + }; + f2(); + } + }); + + test("other in strict mode", function() { + var results = []; + var p = impl(); + + function f1() { + try { + this.undef(); + } catch (e) { + debugger; + results = p.run(e, 'other'); + } + } + + function f2() { + f1(); + } + + function f3() { + "use strict"; + f2(); + } + + f3(); + + ok(results.length >= 3, 'Stack should contain at least 3 frames in non-strict mode'); + //equals(results, '', 'debug'); + equals(results[1], 'f1()'); + equals(results[2], 'f2()'); + }); + + //endregion + + //region util + module("util"); + + test("stringify", function() { + expect(5); + equals(pst.stringifyArguments(["a", 1, {}, function() { + }, undefined]), '"a",1,#object,#function,undefined'); + equals(pst.stringifyArguments([0, 1, 2, 3]), '0,1,2,3'); + equals(pst.stringifyArguments([ + ['a', null] + ]), '["a",null]'); + equals(pst.stringifyArguments([ + [2, 4, 6, 8, 10, 12, 14] + ]), '[2...14]'); + equals(pst.stringifyArguments([]), ''); + }); + + test("isSameDomain", function() { + expect(1); + ok(pst.isSameDomain(location.href)); + }); + + test("findFunctionName", function() { + expect(13); + equals(pst.findFunctionName(['var a = function aa() {', 'var b = 2;', '};'], 2), 'a'); + equals(pst.findFunctionName(['var a = function () {', 'var b = 2;', '};'], 2), 'a'); + equals(pst.findFunctionName(['var a = function() {', 'var b = 2;', '};'], 2), 'a'); + // FIXME: currently failing because we don't have a way to distinguish which fn is being sought + // equals(pst.findFunctionName(['a:function(){},b:function(){', '};'], 1), 'b'); + equals(pst.findFunctionName(['"a": function(){', '};'], 1), 'a'); + + // different formatting + equals(pst.findFunctionName(['function a() {', 'var b = 2;', '}'], 2), 'a'); + equals(pst.findFunctionName(['function a(b,c) {', 'var b = 2;', '}'], 2), 'a'); + equals(pst.findFunctionName(['function a () {', '}'], 2), 'a'); + equals(pst.findFunctionName(['function\ta\t()\t{', '}'], 2), 'a'); + equals(pst.findFunctionName([' function', ' a', ' ()', ' {', ' }'], 3), 'a'); + + equals(pst.findFunctionName(['var data = new Function("return true;");', ''], 1), 'data'); + equals(pst.findFunctionName(['var data = new Function("s,r",', '"return s + r;");'], 1), 'data'); + + // not found + equals(pst.findFunctionName(['var a = 1;', 'var b = 2;', 'var c = 3;'], 2), '(?)'); + + // false positive in comment + equals(pst.findFunctionName(['function a() {', ' // function commented()', ' error here', '}'], 3), 'a'); + }); + + test("getSource cache miss", function() { + expect(3); + var p = impl(), file = 'file:///test', lines; + p.ajax = function(fileArg, callback) { + equals(fileArg, file, 'cache miss'); + return 'line0\nline1\n'; + }; + lines = p.getSource(file); + equals(lines[0], 'line0'); + equals(lines[1], 'line1'); + }); + + test("getSource cache hit", function() { + expect(2); + var p = impl(), file = 'file:///test', lines; + p.ajax = function(fileArg, callback) { + ok(false, 'not called'); + }; + p.sourceCache[file] = ['line0', 'line1']; + lines = p.getSource(file); + equals(lines[0], 'line0'); + equals(lines[1], 'line1'); + }); + + if (window && window.location && window.location.hostname && window.location.hostname !== 'localhost') { + test("sync ajax", function() { + expect(1); + var p = impl(); + var data = p.ajax(document.location.href); + ok(data.indexOf('stacktrace') >= 0, 'synchronous get'); + }); + } + + test("guessAnonymousFunction", function() { + expect(1); + var p = impl(); + var file = 'http://' + window.location.hostname + '/file.js'; + p.sourceCache[file] = ['var a = function() {', 'var b = 2;', '};']; + equals(p.guessAnonymousFunction(file, 2), 'a'); + }); + + test("guessAnonymousFunction exception", function() { + // FIXME: this test seems to affect guessAnonymousFunction opera11 + expect(1); + var p = impl(); + var oldGetSource = p.getSource; + p.getSource = function() { + throw 'permission denied'; + }; + var file = 'file:///test'; + equals(p.guessAnonymousFunction(file, 2), 'getSource failed with url: file:///test, exception: permission denied'); + // Reset mocked function + p.getSource = oldGetSource; + }); + + test("guessAnonymousFunctions firefox", function() { + var results = []; + var p = impl(); + var file = 'http://' + window.location.hostname + '/file.js'; + p.sourceCache[file] = ['var f2 = function () {', 'var b = 2;', '};', 'function run() {', 'return true;', '}']; + results.push(['{anonymous}()@' + file + ':74', '{anonymous}()@' + file + ':5', '{anonymous}()@' + file + ':2']); + + (function f2() { + try { + this.undef(); + } catch (e) { + if (p.mode(e) == 'firefox') { + results.push(p.run()); + } + } + })(); + + expect(results.length); + for (var i = 0; i < results.length; ++i) { + //equals(results[i], '', 'stack trace'); + var functions = p.guessAnonymousFunctions(results[i]); + //equals(functions.join("\n"), '', 'stack trace after guessing'); + equals(functions[2].substring(0, 2), 'f2', 'guessed f2 as 3rd result: ' + functions[2]); + //equals(functions[2].indexOf('f2'), 0, 'guessed f2 as 3rd result'); + } + }); + + test("guessAnonymousFunctions chrome", function() { + var results = []; + var p = impl(); + var file = 'http://' + window.location.hostname + '/file.js'; + p.sourceCache[file] = ['var f2 = function() {', 'var b = 2;', '};']; + results.push(['createException() (' + file + ':1:1)', 'run() (' + file + ':1:1)', 'f2() (' + file + ':1:1)']); + + var f2 = function() { + try { + this.undef(); + } catch (e) { + if (p.mode(e) == 'chrome') { + results.push(p.run()); + } + } + }; + f2(); + + expect(results.length); + for (var i = 0; i < results.length; ++i) { + //equals((results[i]), '', 'debug'); + var functions = p.guessAnonymousFunctions(results[i]); + // equals(functions.join("\n"), '', 'debug contents of stack'); + equals(functions[2].indexOf('f2'), 0, 'guessed f2 in ' + functions[2]); + } + }); + + // Test for issue #34 + test("guessAnonymousFunctions chrome with eval", function() { + var unit = impl(); + var expected = '{anonymous}()@eval at buildTmplFn (http://domain.com/file.js:17:10)'; + var actual = unit.guessAnonymousFunctions([expected]); + expect(1); + // Nothing should change since no anonymous function in stack + equals(expected, actual); + }); + + test("guessAnonymousFunctions opera9", function() { + var results = []; + var p = impl(); + var file = 'http://' + window.location.hostname + '/file.js'; + p.sourceCache[file] = ['var f2 = function() {', 'bar();', '};']; + results.push(['{anonymous}()@' + file + ':2 -- bar();']); + + var f2 = function() { + try { + this.undef(); + } catch (e) { + if (p.mode(e) == 'opera9') { + results.push(p.run(e)); + } + } + }; + f2(); + + expect(results.length * 1); + for (var i = 0; i < results.length; ++i) { + //equals((results[i]), '', 'debug'); + var functions = p.guessAnonymousFunctions(results[i]); + //equals(functions, '', 'debug'); + equals(functions[0].indexOf('f2()'), 0, 'guessed f2 in ' + functions[0]); + } + }); + + test("guessAnonymousFunctions opera10", function() { + // FIXME: currently failing in Opera 10.60 + var results = []; + var p = impl(); + var file = 'http://' + window.location.hostname + '/file.js'; + p.sourceCache[file] = ['var f2 = function() {', 'var b = 2;', '};']; + results.push(["{anonymous}()@" + file + ":1:1", "{anonymous}()@" + file + ":1:1"]); + + var f2 = function() { + try { + this.undef(); + } catch (e) { + if (p.mode(e) == 'opera10') { + //alert("e.message: " + e.message); + results.push(p.run()); + } + } + }; + f2(); + + expect(results.length * 1); + for (var i = 0; i < results.length; ++i) { + //equals((results[i]), '', 'debug'); + var functions = p.guessAnonymousFunctions(results[i]); + //equals(functions.join("\n"), '', 'debug'); + equals(functions[1].indexOf('f2()'), 0, 'guessed f2 in ' + functions[1]); + } + }); + + test("guessAnonymousFunctions opera11", function() { + var results = []; + var p = impl(); + var file = 'http://' + window.location.hostname + '/file.js'; + p.sourceCache[file] = ['var f2 = function() {', 'bar();', '};']; + results.push(["{anonymous}()@" + file + ":2:1 -- bar();"]); + + var f2 = function() { + try { + this.undef(); + } catch (e) { + if (p.mode(e) == 'opera11') { + results.push(p.run(e)); + } + } + }; + f2(); + + expect(results.length * 1); + for (var i = 0; i < results.length; ++i) { + //equals((results[i]), '', 'debug'); + var functions = p.guessAnonymousFunctions(results[i]); + //equals(functions.join("\n"), '', 'debug'); + equals(functions[0].indexOf('f2()'), 0, 'guessed f2 in ' + functions[0]); + } + }); + + test("guessAnonymousFunctions other", function() { + var results = []; + var p = impl(); + var file = 'http://' + window.location.hostname + '/file.js'; + p.sourceCache[file] = ['var f2 = function() {', 'var b = 2;', '};']; + results.push(['{anonymous}()']); + + (function f2() { + try { + this.undef(); + } catch (e) { + if (p.mode(e) == 'other') { + results.push(p.run()); + } + } + })(); + + expect(results.length); + for (var i = 0; i < results.length; ++i) { + //equals((results[i]), '', 'debug'); + equals(p.guessAnonymousFunctions(results[i])[0].indexOf('{anonymous}'), 0, 'no file and line number in "other" mode'); + } + }); + //endregion +})(window, document); diff --git a/src/bower_components/stacktrace.js/test/functional/ExceptionLab.html b/src/bower_components/stacktrace.js/test/functional/ExceptionLab.html new file mode 100644 index 0000000000000..9443abf3d3498 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/functional/ExceptionLab.html @@ -0,0 +1,121 @@ + + + + + Exception Lab + + + + + + +userAgent + + +
      + + + + + +
      + + +
      + + + + + diff --git a/src/bower_components/stacktrace.js/test/functional/ExceptionLab.js b/src/bower_components/stacktrace.js/test/functional/ExceptionLab.js new file mode 100644 index 0000000000000..f2672ca2f84cf --- /dev/null +++ b/src/bower_components/stacktrace.js/test/functional/ExceptionLab.js @@ -0,0 +1,69 @@ +/*global module, exports, define*/ +(function(global) { + function createException() { + return ((function(x) { + try { + x.undef(); + return x; + } catch (ex) { + return ex; + } + })(null)); + } + + function printProp(prop, value) { + if (typeof value === "string") { + value = '"' + value.replace(/"/g, '\\"').replace(/\r/g, "\\r").replace(/\n/g, '\\n" +\n "') + '"'; + } + return prop + ': ' + value; + } + + function getExceptionProps(ex) { + /*jshint forin:false*/ + var prop, props = [], exceptionPropertyNames = { + message: true, + name: true, + stack: true, + stacktrace: true, + 'arguments': true, + type: true + }; + + // find all (including non-enumerable) own properties + if (typeof Object.getOwnPropertyNames === "function") { + var ownPropertyNames = Object.getOwnPropertyNames(ex); + for (var i = 0; i < ownPropertyNames.length; i++) { + exceptionPropertyNames[ownPropertyNames[i]] = true; + } + } + + // find own and inherited enumerable properties + for (prop in ex) { + exceptionPropertyNames[prop] = true; + } + + for (prop in exceptionPropertyNames) { + var value = ex[prop]; + if (typeof value !== "undefined") { + props.push(printProp(prop, value)); + } + } + return props; + } + + var api = { + createException: createException, + getExceptionProps: getExceptionProps + }; + + if (typeof exports === 'object') { + // Node + module.exports = api; + } else if (typeof define === 'function' && define.amd) { + // AMD + define(api); + } else { + // Browser globals + global.ExceptionLab = api; + } +}(this)); diff --git a/src/bower_components/stacktrace.js/test/functional/index.html b/src/bower_components/stacktrace.js/test/functional/index.html new file mode 100644 index 0000000000000..de0634e685693 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/functional/index.html @@ -0,0 +1,72 @@ + + + + + stacktrace.js functional tests + + + +

      eriwen / stacktrace.js

      +
        +
      • Just include stacktrace.js file on your page, and call it like so:
      • +
      + +
      <script type="text/javascript" src="path/to/stacktrace.js" />  
      +<script type="text/javascript">      
      +... your code ...      
      +if (errorCondition) {
      +          var trace = printStackTrace();
      +          //Output however you want!           
      +		   alert(trace.join('\n\n'));      
      +}      
      +... more code of yours ...  
      +</script>
      +
      +

      Tested in No-options test

      +
        +
      • You can also pass in your own Error to get a stacktrace:
      • +
      + +
      <script type="text/javascript">
      +      var lastError;
      +      try {
      +          // error producing code
      +      } catch(e) {
      +         lastError = e;
      +         // do something else with error
      +      }
      +        // Returns stacktrace from lastError!
      +      printStackTrace({e: lastError});
      +</script>
      +
      +

      Tested in passing error test

      +
        +
      • Some people recommend just assigning it to window.onerror (Only in IE and FF):
      • +
      + +
      window.onerror = function(msg, file, line) {
      +      alert(printStackTrace().join('\n\n'));
      +	    }
      +
      +

      Tested in window.onerror test

      +
        +
      • You can now have any (public or privileged) function give you a stacktrace when it is called:
      • +
      + +
      var p = new printStackTrace.implementation();
      +  p.instrumentFunction(this, 'bar', logStackTrace);  
      +function logStackTrace(stack) {
      +      console.log(stack.join('\n'));
      +  }  function foo() {
      +      var a = 1;
      +      bar();
      +  }  
      +function bar() {
      +      baz();
      +  }  
      +foo(); //Will log a stacktrace when 'bar()' is called containing 'foo()'!
      +    p.deinstrumentFunction(this, 'bar'); //Remove function instrumentation
      +
      +

      Tested in function instrumentation test

      + + diff --git a/src/bower_components/stacktrace.js/test/functional/testCommon.js b/src/bower_components/stacktrace.js/test/functional/testCommon.js new file mode 100644 index 0000000000000..7fdfc1abcaf9d --- /dev/null +++ b/src/bower_components/stacktrace.js/test/functional/testCommon.js @@ -0,0 +1,18 @@ +function toList(array) { + return "
      1. " + (array.join("
      2. ")) + "
      "; +} + +function printTrace(trace) { + var output = document.getElementById("output"); + if (!output) { + output = document.createElement("div"); + output.id = "output"; + document.body.appendChild(output); + } + + var content = []; + content.push(toList(trace)); + content.push("--------------Expected:-------------------"); + content.push(toList(window.expected || [])); + output.innerHTML = (content.join("
      ")); +} \ No newline at end of file diff --git a/src/bower_components/stacktrace.js/test/functional/testNode.js b/src/bower_components/stacktrace.js/test/functional/testNode.js new file mode 100644 index 0000000000000..b3b6770307731 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/functional/testNode.js @@ -0,0 +1,46 @@ +/*global require, console*/ +var ExceptionLab = require("./ExceptionLab"); +var printStackTrace = require("../../stacktrace"); + +var lastException; + +function info(text) { + console.log(text); +} + +function dumpStacktrace(guess) { + var trace = printStackTrace({ + e: lastException, + guess: guess + }); + info(trace.join("\n")); +} + +function dumpException(ex) { + var text = "{\n " + ExceptionLab.getExceptionProps(ex).join(",\n ") + "\n}"; + info(text); + //info(ex.arguments); + lastException = ex; +} + +function dumpExceptionMultiLine() { + var fn = function() { + return { + name: "provide multi-line message in exception" + }; + }; + try { + fn.nonExistentMethod(); + } catch (ex) { + dumpException(ex); + } +} + +info("Exception properties:"); +dumpExceptionMultiLine(); + +var p = new printStackTrace.implementation(); +info("\nException mode: " + p.mode(lastException)); + +info("\nException stack trace:"); +dumpStacktrace(); diff --git a/src/bower_components/stacktrace.js/test/functional/testcase1.html b/src/bower_components/stacktrace.js/test/functional/testcase1.html new file mode 100644 index 0000000000000..a6b0ab50c220a --- /dev/null +++ b/src/bower_components/stacktrace.js/test/functional/testcase1.html @@ -0,0 +1,27 @@ + + + + No-options test + + + + +
      + + + diff --git a/src/bower_components/stacktrace.js/test/functional/testcase2.html b/src/bower_components/stacktrace.js/test/functional/testcase2.html new file mode 100644 index 0000000000000..8dc73ec28f264 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/functional/testcase2.html @@ -0,0 +1,33 @@ + + + + passing error test + + + + +
      + + + diff --git a/src/bower_components/stacktrace.js/test/functional/testcase3.html b/src/bower_components/stacktrace.js/test/functional/testcase3.html new file mode 100644 index 0000000000000..ce6f28d648c1d --- /dev/null +++ b/src/bower_components/stacktrace.js/test/functional/testcase3.html @@ -0,0 +1,35 @@ + + + + window.onerror test + + + + +
      + + + diff --git a/src/bower_components/stacktrace.js/test/functional/testcase4.html b/src/bower_components/stacktrace.js/test/functional/testcase4.html new file mode 100644 index 0000000000000..ec93855277bf6 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/functional/testcase4.html @@ -0,0 +1,34 @@ + + + + function instrumentation test + + + + +
      + + + diff --git a/src/bower_components/stacktrace.js/test/issues/27-1.html b/src/bower_components/stacktrace.js/test/issues/27-1.html new file mode 100644 index 0000000000000..452c8f21e37b4 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/issues/27-1.html @@ -0,0 +1,29 @@ + + + Issue #27 + + + + + diff --git a/src/bower_components/stacktrace.js/test/issues/27-2.html b/src/bower_components/stacktrace.js/test/issues/27-2.html new file mode 100644 index 0000000000000..eb5fc4a7d12e4 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/issues/27-2.html @@ -0,0 +1,28 @@ + + + Issue #27 + + + + + + + diff --git a/src/bower_components/stacktrace.js/test/issues/32.html b/src/bower_components/stacktrace.js/test/issues/32.html new file mode 100644 index 0000000000000..1f065e7a8d58c --- /dev/null +++ b/src/bower_components/stacktrace.js/test/issues/32.html @@ -0,0 +1,53 @@ + + + + Issue #32 + + + + + To invoke onbeforeunload event: +
        +
      • Close the current window.
      • +
      • Navigate to another location by entering a new address or selecting a Favorite.
      • +
      • Click an anchor that refers to another document.
      • +
      • Invoke the anchor.click method.
      • +
      • Invoke the document.write method.
      • +
      • Invoke the document.close method.
      • +
      • Invoke the window.close method.
      • +
      • Invoke the window.navigate or NavigateAndFind method.
      • +
      • Invoke the location.replace method.
      • +
      • Invoke the location.reload method.
      • +
      • Specify a new value for the location.href property.
      • +
      • Submit a form to the address specified in the action attribute via the input type=submit control, or invoke the form.submit method.
      • +
      • Invoke the window.open method, providing the possible value _self for the window name.
      • +
      • Invoke the document.open method.
      • +
      • Click the Back, Forward, Refresh, or Home button.
      • +
      + + + diff --git a/src/bower_components/stacktrace.js/test/issues/55.html b/src/bower_components/stacktrace.js/test/issues/55.html new file mode 100644 index 0000000000000..c5082a4ebb20c --- /dev/null +++ b/src/bower_components/stacktrace.js/test/issues/55.html @@ -0,0 +1,22 @@ + + + + Issue #55 + + + +
      
      +
      +
      +
      diff --git a/src/bower_components/stacktrace.js/test/jsTestDriver.conf b/src/bower_components/stacktrace.js/test/jsTestDriver.conf
      new file mode 100644
      index 0000000000000..da71508e6750d
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/jsTestDriver.conf
      @@ -0,0 +1,21 @@
      +server: http://localhost:4224
      +
      +load:
      +  - lib/qunit.js
      +  - lib/equiv.js
      +  - lib/sinon-1.2.0.js
      +  - lib/sinon-qunit-1.0.0.js
      +  - lib/QUnitAdapter.js
      +  - ../stacktrace.js
      +  - CapturedExceptions.js
      +
      +test:
      +  - TestStacktrace.js
      +
      +plugin:
      +  - name: "coverage"
      +    jar: "lib/plugins/coverage.jar"
      +    module: "com.google.jstestdriver.coverage.CoverageModule"
      +    args: useCoberturaFormat
      +
      +timeout: 120
      diff --git a/src/bower_components/stacktrace.js/test/lib/QUnitAdapter.js b/src/bower_components/stacktrace.js/test/lib/QUnitAdapter.js
      new file mode 100644
      index 0000000000000..2c8da65168c75
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/lib/QUnitAdapter.js
      @@ -0,0 +1,85 @@
      +/*
      +QUnitAdapter
      +Version: 1.1.0
      +
      +Run qunit tests using JS Test Driver
      +
      +This provides almost the same api as qunit.
      +
      +Tests must run sychronously, which means no use of stop and start methods.
      +You can use jsUnit Clock object to deal with timeouts and intervals:
      +http://googletesting.blogspot.com/2007/03/javascript-simulating-time-in-jsunit.html
      +
      +The qunit #main DOM element is not included. If you need to do any DOM manipulation
      +you need to set it up and tear it down in each test.
      +
      +*/
      +(function() {
      +
      +	if(!(window.equiv)) {
      +		throw new Error("QUnitAdapter.js - Unable to find equiv function. Ensure you have added equiv.js to the load section of your jsTestDriver.conf");
      +	}
      +
      +	var QUnitTestCase;
      +
      +    window.module = function(name, lifecycle) {
      +        QUnitTestCase = TestCase(name);
      +        QUnitTestCase.prototype.lifecycle = lifecycle || {};
      +    };
      +    
      +    window.test = function(name, expected, test) {
      +    	QUnitTestCase.prototype['test ' + name] = function() {
      +        	if(this.lifecycle.setup) {
      +        		this.lifecycle.setup();
      +        	}
      +       		if(expected.constructor === Number) {
      +       			expectAsserts(expected);	
      +       		} else {
      +       			test = expected;
      +       		}
      +       		test.call(this.lifecycle);
      +       		
      +			if(this.lifecycle.teardown) {
      +				this.lifecycle.teardown();
      +			}
      +		};
      +    };
      +    
      +    window.expect = function(count) {
      +        expectAsserts(count);
      +    };
      +    
      +    window.ok = function(actual, msg) {
      +        assertTrue(msg ? msg : '', !!actual);
      +    };
      +    
      +    window.equals = function(a, b, msg) {
      +        assertEquals(msg ? msg : '', b, a);
      +    };
      +    
      +    window.start = window.stop = function() {
      +        fail('start and stop methods are not available when using JS Test Driver.\n' +
      +            'Use jsUnit Clock object to deal with timeouts and intervals:\n' + 
      +            'http://googletesting.blogspot.com/2007/03/javascript-simulating-time-in-jsunit.html.');
      +    };
      +    
      +    window.same = function(a, b, msg) {
      +        assertTrue(msg ? msg : '', window.equiv(b, a));
      +    };
      +    
      +    window.reset = function() {
      +    	fail('reset method is not available when using JS Test Driver');
      +    };
      +
      +    window.isLocal = function() {
      +    	return false;
      +    };
      +    
      +    window.QUnit = {
      +    	equiv: window.equiv,
      +    	ok: window.ok
      +    };
      +
      +	module('Default Module');
      +
      +})();
      diff --git a/src/bower_components/stacktrace.js/test/lib/equiv.js b/src/bower_components/stacktrace.js/test/lib/equiv.js
      new file mode 100644
      index 0000000000000..a69b97448cb39
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/lib/equiv.js
      @@ -0,0 +1,185 @@
      +
      +// Tests for equality any JavaScript type and structure without unexpected results.
      +// Discussions and reference: http://philrathe.com/articles/equiv
      +// Test suites: http://philrathe.com/tests/equiv
      +// Author: Philippe RathŽ 
      +window.equiv = function () {
      +
      +    var innerEquiv; // the real equiv function
      +    var callers = []; // stack to decide between skip/abort functions
      +
      +    // Determine what is o.
      +    function hoozit(o) {
      +        if (typeof o === "string") {
      +            return "string";
      +
      +        } else if (typeof o === "boolean") {
      +            return "boolean";
      +
      +        } else if (typeof o === "number") {
      +
      +            if (isNaN(o)) {
      +                return "nan";
      +            } else {
      +                return "number";
      +            }
      +
      +        } else if (typeof o === "undefined") {
      +            return "undefined";
      +
      +        // consider: typeof null === object
      +        } else if (o === null) {
      +            return "null";
      +
      +        // consider: typeof [] === object
      +        } else if (o instanceof Array) {
      +            return "array";
      +        
      +        // consider: typeof new Date() === object
      +        } else if (o instanceof Date) {
      +            return "date";
      +
      +        // consider: /./ instanceof Object;
      +        //           /./ instanceof RegExp;
      +        //          typeof /./ === "function"; // => false in IE and Opera,
      +        //                                          true in FF and Safari
      +        } else if (o instanceof RegExp) {
      +            return "regexp";
      +
      +        } else if (typeof o === "object") {
      +            return "object";
      +
      +        } else if (o instanceof Function) {
      +            return "function";
      +        }
      +    }
      +
      +    // Call the o related callback with the given arguments.
      +    function bindCallbacks(o, callbacks, args) {
      +        var prop = hoozit(o);
      +        if (prop) {
      +            if (hoozit(callbacks[prop]) === "function") {
      +                return callbacks[prop].apply(callbacks, args);
      +            } else {
      +                return callbacks[prop]; // or undefined
      +            }
      +        }
      +    }
      +
      +    var callbacks = function () {
      +
      +        // for string, boolean, number and null
      +        function useStrictEquality(b, a) {
      +            return a === b;
      +        }
      +
      +        return {
      +            "string": useStrictEquality,
      +            "boolean": useStrictEquality,
      +            "number": useStrictEquality,
      +            "null": useStrictEquality,
      +            "undefined": useStrictEquality,
      +
      +            "nan": function (b) {
      +                return isNaN(b);
      +            },
      +
      +            "date": function (b, a) {
      +                return hoozit(b) === "date" && a.valueOf() === b.valueOf();
      +            },
      +
      +            "regexp": function (b, a) {
      +                return hoozit(b) === "regexp" &&
      +                    a.source === b.source && // the regex itself
      +                    a.global === b.global && // and its modifers (gmi) ...
      +                    a.ignoreCase === b.ignoreCase &&
      +                    a.multiline === b.multiline;
      +            },
      +
      +            // - skip when the property is a method of an instance (OOP)
      +            // - abort otherwise,
      +            //   initial === would have catch identical references anyway
      +            "function": function () {
      +                var caller = callers[callers.length - 1];
      +                return caller !== Object &&
      +                        typeof caller !== "undefined";
      +            },
      +
      +            "array": function (b, a) {
      +                var i;
      +                var len;
      +
      +                // b could be an object literal here
      +                if ( ! (hoozit(b) === "array")) {
      +                    return false;
      +                }
      +
      +                len = a.length;
      +                if (len !== b.length) { // safe and faster
      +                    return false;
      +                }
      +                for (i = 0; i < len; i++) {
      +                    if( ! innerEquiv(a[i], b[i])) {
      +                        return false;
      +                    }
      +                }
      +                return true;
      +            },
      +
      +            "object": function (b, a) {
      +                var i;
      +                var eq = true; // unless we can proove it
      +                var aProperties = [], bProperties = []; // collection of strings
      +
      +                // comparing constructors is more strict than using instanceof
      +                if ( a.constructor !== b.constructor) {
      +                    return false;
      +                }
      +
      +                // stack constructor before traversing properties
      +                callers.push(a.constructor);
      +
      +                for (i in a) { // be strict: don't ensures hasOwnProperty and go deep
      +
      +                    aProperties.push(i); // collect a's properties
      +
      +                    if ( ! innerEquiv(a[i], b[i])) {
      +                        eq = false;
      +                    }
      +                }
      +
      +                callers.pop(); // unstack, we are done
      +
      +                for (i in b) {
      +                    bProperties.push(i); // collect b's properties
      +                }
      +
      +                // Ensures identical properties name
      +                return eq && innerEquiv(aProperties.sort(), bProperties.sort());
      +            }
      +        };
      +    }();
      +
      +    innerEquiv = function () { // can take multiple arguments
      +        var args = Array.prototype.slice.apply(arguments);
      +        if (args.length < 2) {
      +            return true; // end transition
      +        }
      +
      +        return (function (a, b) {
      +            if (a === b) {
      +                return true; // catch the most you can
      +
      +            } else if (typeof a !== typeof b || a === null || b === null || typeof a === "undefined" || typeof b === "undefined") {
      +                return false; // don't lose time with error prone cases
      +
      +            } else {
      +                return bindCallbacks(a, callbacks, [b, a]);
      +            }
      +
      +        // apply transition with (1..n) arguments
      +        })(args[0], args[1]) && arguments.callee.apply(this, args.splice(1, args.length -1));
      +    };
      +
      +    return innerEquiv;
      +}(); // equiv
      \ No newline at end of file
      diff --git a/src/bower_components/stacktrace.js/test/lib/lcov-to-cobertura-xml.py b/src/bower_components/stacktrace.js/test/lib/lcov-to-cobertura-xml.py
      new file mode 100644
      index 0000000000000..b1669e9b36997
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/lib/lcov-to-cobertura-xml.py
      @@ -0,0 +1,354 @@
      +#!/usr/bin/env python
      +
      +# Copyright 2011-2012 Eric Wendelin
      +#
      +# This is free software, licensed under the Apache License, Version 2.0,
      +# available in the accompanying LICENSE.txt file.
      +
      +"""
      +Converts lcov line coverage output to Cobertura-compatible XML for CI
      +"""
      +
      +import re, sys, os, time
      +from xml.dom import minidom
      +from optparse import OptionParser
      +
      +VERSION = '1.2'
      +__all__ = ['LcovCobertura']
      +
      +class LcovCobertura(object):
      +    """
      +    Converts code coverage report files in lcov format to Cobertura's XML
      +    report format so that CI servers like Jenkins can aggregate results and
      +    determine build stability etc.
      +
      +    >>> from lcov_cobertura import LcovCobertura
      +    >>> LCOV_INPUT = 'your lcov input'
      +    >>> converter = LcovCobertura(LCOV_INPUT)
      +    >>> cobertura_xml = converter.convert()
      +    >>> print cobertura_xml
      +    """
      +
      +    def __init__(self, lcov_data, base_dir='.', excludes=None):
      +        """
      +        Create a new :class:`LcovCobertura` object using the given `lcov_data`
      +        and `options`.
      +
      +        :param lcov_data: Path to LCOV data file
      +        :type lcov_data: string
      +        :param base_dir: Path upon which to base all sources
      +        :type base_dir: string
      +        :param excludes: list of regexes to packages as excluded
      +        :type excludes: [string]
      +        """
      +
      +        if not excludes:
      +            excludes = []
      +        self.lcov_data = lcov_data
      +        self.base_dir = base_dir
      +        self.excludes = excludes
      +
      +    def convert(self):
      +        """
      +        Convert lcov file to cobertura XML using options from this instance.
      +        """
      +        coverage_data = self.parse()
      +        return self.generate_cobertura_xml(coverage_data)
      +
      +    def parse(self):
      +        """
      +        Generate a data structure representing it that can be serialized in any
      +        logical format.
      +        """
      +
      +        coverage_data = {
      +            'packages': {},
      +            'summary': {'lines-total': 0, 'lines-covered': 0,
      +                        'branches-total': 0, 'branches-covered': 0},
      +            'timestamp': str(int(time.time()))
      +        }
      +        package = None
      +        current_file = None
      +        file_lines_total = 0
      +        file_lines_covered = 0
      +        file_lines = {}
      +        file_methods = {}
      +        file_branches_total = 0
      +        file_branches_covered = 0
      +
      +        for line in self.lcov_data.split('\n'):
      +            if line.strip() == 'end_of_record':
      +                if current_file is not None:
      +                    package_dict = coverage_data['packages'][package]
      +                    package_dict['lines-total'] += file_lines_total
      +                    package_dict['lines-covered'] += file_lines_covered
      +                    package_dict['branches-total'] += file_branches_total
      +                    package_dict['branches-covered'] += file_branches_covered
      +                    file_dict = package_dict['classes'][current_file]
      +                    file_dict['lines-total'] = file_lines_total
      +                    file_dict['lines-covered'] = file_lines_covered
      +                    file_dict['lines'] = dict(file_lines)
      +                    file_dict['methods'] = dict(file_methods)
      +                    file_dict['branches-total'] = file_branches_total
      +                    file_dict['branches-covered'] = file_branches_covered
      +                    coverage_data['summary']['lines-total'] += file_lines_total
      +                    coverage_data['summary']['lines-covered'] += file_lines_covered
      +                    coverage_data['summary']['branches-total'] += file_branches_total
      +                    coverage_data['summary']['branches-covered'] += file_branches_covered
      +
      +            line_parts = line.split(':')
      +            input_type = line_parts[0]
      +
      +            if input_type == 'SF':
      +                # Get file name
      +                file_name = line_parts[-1].strip()
      +                relative_file_name = os.path.relpath(file_name, self.base_dir)
      +                package = '.'.join(relative_file_name.split(os.path.sep)[0:-1])
      +                class_name = file_name.split(os.path.sep)[-1]
      +                if package not in coverage_data['packages']:
      +                    coverage_data['packages'][package] = {
      +                        'classes': {}, 'lines-total': 0, 'lines-covered': 0,
      +                        'branches-total': 0, 'branches-covered': 0
      +                    }
      +                coverage_data['packages'][package]['classes'][
      +                relative_file_name] = {
      +                    'name': class_name, 'lines': {}, 'lines-total': 0,
      +                    'lines-covered': 0, 'branches-total': 0,
      +                    'branches-covered': 0
      +                }
      +                package = package
      +                current_file = relative_file_name
      +                file_lines_total = 0
      +                file_lines_covered = 0
      +                file_lines.clear()
      +                file_methods.clear()
      +                file_branches_total = 0
      +                file_branches_covered = 0
      +            elif input_type == 'DA':
      +                # DA:2,0
      +                (line_number, line_hits) = line_parts[-1].strip().split(',')
      +                line_number = int(line_number)
      +                if line_number not in file_lines:
      +                    file_lines[line_number] = {
      +                        'branch': 'false', 'branches-total': 0,
      +                        'branches-covered': 0
      +                    }
      +                file_lines[line_number]['hits'] = line_hits
      +                # Increment lines total/covered for class and package
      +                if int(line_hits) > 0:
      +                    file_lines_covered += 1
      +                file_lines_total += 1
      +            elif input_type == 'BRDA':
      +                # BRDA:1,1,2,0
      +                (line_number, block_number, branch_number, branch_hits) = line_parts[-1].strip().split(',')
      +                line_number = int(line_number)
      +                if line_number not in file_lines:
      +                    file_lines[line_number] = {
      +                        'branch': 'true', 'branches-total': 0,
      +                        'branches-covered': 0, 'hits': 0
      +                    }
      +                file_lines[line_number]['branch'] = 'true'
      +                file_lines[line_number]['branches-total'] += 1
      +                file_branches_total += 1
      +                if branch_hits != '-' and int(branch_hits) > 0:
      +                    file_lines[line_number]['branches-covered'] += 1
      +                    file_branches_covered += 1
      +            elif input_type == 'BRF':
      +                file_branches_total = int(line_parts[1])
      +            elif input_type == 'BRH':
      +                file_branches_covered = int(line_parts[1])
      +            elif input_type == 'FN':
      +                # FN:5,(anonymous_1)
      +                function_name = line_parts[-1].strip().split(',')[1]
      +                file_methods[function_name] = '0'
      +            elif input_type == 'FNDA':
      +                # FNDA:0,(anonymous_1)
      +                (function_hits, function_name) = line_parts[-1].strip().split(',')
      +                file_methods[function_name] = function_hits
      +
      +        # Exclude packages
      +        excluded = [x for x in coverage_data['packages'] for e in self.excludes
      +                    if re.match(e, x)]
      +        for package in excluded:
      +            del coverage_data['packages'][package]
      +
      +        # Compute line coverage rates
      +        for package_data in list(coverage_data['packages'].values()):
      +            package_data['line-rate'] = self._percent(
      +                package_data['lines-total'],
      +                package_data['lines-covered'])
      +            package_data['branch-rate'] = self._percent(
      +                package_data['branches-total'],
      +                package_data['branches-covered'])
      +
      +        return coverage_data
      +
      +    def generate_cobertura_xml(self, coverage_data):
      +        """
      +        Given parsed coverage data, return a String cobertura XML representation.
      +
      +        :param coverage_data: Nested dict representing coverage information.
      +        :type coverage_data: dict
      +        """
      +
      +        dom_impl = minidom.getDOMImplementation()
      +        doctype = dom_impl.createDocumentType("coverage", None,
      +                                              "http://cobertura.sourceforge.net/xml/coverage-03.dtd")
      +        document = dom_impl.createDocument(None, "coverage", doctype)
      +        root = document.documentElement
      +        summary = coverage_data['summary']
      +        self._attrs(root, {
      +            'branch-rate': self._percent(summary['branches-total'],
      +                                         summary['branches-covered']),
      +            'branches-covered': str(summary['branches-covered']),
      +            'branches-valid': str(summary['branches-total']),
      +            'complexity': '0',
      +            'line-rate': self._percent(summary['lines-total'],
      +                                       summary['lines-covered']),
      +            'lines-valid': str(summary['lines-total']),
      +            'timestamp': coverage_data['timestamp'],
      +            'version': '1.9'
      +        })
      +
      +        sources = self._el(document, 'sources', {})
      +        root.appendChild(sources)
      +
      +        packages_el = self._el(document, 'packages', {})
      +
      +        packages = coverage_data['packages']
      +        for package_name, package_data in list(packages.items()):
      +            package_el = self._el(document, 'package', {
      +                'line-rate': package_data['line-rate'],
      +                'branch-rate': package_data['branch-rate'],
      +                'name': package_name
      +            })
      +            classes_el = self._el(document, 'classes', {})
      +            for class_name, class_data in list(package_data['classes'].items()):
      +                class_el = self._el(document, 'class', {
      +                    'branch-rate': self._percent(class_data['branches-total'],
      +                                                 class_data['branches-covered']),
      +                    'complexity': '0',
      +                    'filename': class_name,
      +                    'line-rate': self._percent(class_data['lines-total'],
      +                                               class_data['lines-covered']),
      +                    'name': class_data['name']
      +                })
      +
      +                # Process methods
      +                methods_el = self._el(document, 'methods', {})
      +                for method_name, hits in list(class_data['methods'].items()):
      +                    method_el = self._el(document, 'method', {
      +                        'name': method_name,
      +                        'hits': hits
      +                    })
      +                    methods_el.appendChild(method_el)
      +
      +                # Process lines
      +                lines_el = self._el(document, 'lines', {})
      +                lines = list(class_data['lines'].keys())
      +                lines.sort()
      +                for line_number in lines:
      +                    line_el = self._el(document, 'line', {
      +                        'branch': class_data['lines'][line_number]['branch'],
      +                        'hits': str(class_data['lines'][line_number]['hits']),
      +                        'number': str(line_number)
      +                    })
      +                    if class_data['lines'][line_number]['branch'] == 'true':
      +                        total = int(class_data['lines'][line_number]['branches-total'])
      +                        covered = int(class_data['lines'][line_number]['branches-covered'])
      +                        percentage = int((covered * 100.0) / total)
      +                        line_el.setAttribute('condition-coverage',
      +                                             '{0}% ({1}/{2})'.format(
      +                                                 percentage, covered, total))
      +                    lines_el.appendChild(line_el)
      +
      +                class_el.appendChild(methods_el)
      +                class_el.appendChild(lines_el)
      +                classes_el.appendChild(class_el)
      +            package_el.appendChild(classes_el)
      +            packages_el.appendChild(package_el)
      +        root.appendChild(packages_el)
      +
      +        return document.toprettyxml()
      +
      +    def _el(self, document, name, attrs):
      +        """
      +        Create an element within document with given name and attributes.
      +
      +        :param document: Document element
      +        :type document: Document
      +        :param name: Element name
      +        :type name: string
      +        :param attrs: Attributes for element
      +        :type attrs: dict
      +        """
      +        return self._attrs(document.createElement(name), attrs)
      +
      +    def _attrs(self, element, attrs):
      +        """
      +        Set attributes on given element.
      +
      +        :param element: DOM Element
      +        :type element: Element
      +        :param attrs: Attributes for element
      +        :type attrs: dict
      +        """
      +        for attr, val in list(attrs.items()):
      +            element.setAttribute(attr, val)
      +        return element
      +
      +    def _percent(self, lines_total, lines_covered):
      +        """
      +        Get the percentage of lines covered in the total, with formatting.
      +
      +        :param lines_total: Total number of lines in given module
      +        :type lines_total: number
      +        :param lines_covered: Number of lines covered by tests in module
      +        :type lines_covered: number
      +        """
      +
      +        if lines_total == 0:
      +            return '0.0'
      +        return str(float(float(lines_covered) / float(lines_total)))
      +
      +if __name__ == '__main__':
      +    def main(argv):
      +        """
      +        Converts LCOV coverage data to Cobertura-compatible XML for reporting.
      +
      +        Usage:
      +            lcov_cobertura.py lcov-file.dat
      +            lcov_cobertura.py lcov-file.dat -b src/dir -e test.lib -o path/out.xml
      +
      +        By default, XML output will be written to ./coverage.xml
      +        """
      +
      +        parser = OptionParser()
      +        parser.usage = 'lcov_cobertura.py lcov-file.dat [-b source/dir] [-e ] [-o output.xml]'
      +        parser.description = 'Converts lcov output to cobertura-compatible XML'
      +        parser.add_option('-b', '--base-dir', action='store',
      +                          help='Directory where source files are located',
      +                          dest='base_dir', default='.')
      +        parser.add_option('-e', '--excludes',
      +                          help='Comma-separated list of regexes of packages to exclude',
      +                          action='append', dest='excludes', default=[])
      +        parser.add_option('-o', '--output',
      +                          help='Path to store cobertura xml file',
      +                          action='store', dest='output', default='coverage.xml')
      +        (options, args) = parser.parse_args(args=argv)
      +
      +        if len(args) != 2:
      +            print((main.__doc__))
      +            sys.exit(1)
      +
      +        try:
      +            with open(args[1], 'r') as lcov_file:
      +                lcov_data = lcov_file.read()
      +                lcov_cobertura = LcovCobertura(lcov_data, options.base_dir, options.excludes)
      +                cobertura_xml = lcov_cobertura.convert()
      +            with open(options.output, mode='wt') as output_file:
      +                output_file.write(cobertura_xml)
      +        except IOError:
      +            sys.stderr.write("Unable to convert %s to Cobertura XML" % args[1])
      +
      +    main(sys.argv)
      diff --git a/src/bower_components/stacktrace.js/test/lib/phantomjs-qunit-runner.js b/src/bower_components/stacktrace.js/test/lib/phantomjs-qunit-runner.js
      new file mode 100644
      index 0000000000000..3e3e73bd0526b
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/lib/phantomjs-qunit-runner.js
      @@ -0,0 +1,84 @@
      +/**
      + * Wait until the test condition is true or a timeout occurs. Useful for waiting
      + * on a server response or for a ui change (fadeIn, etc.) to occur.
      + *
      + * @param testFx javascript condition that evaluates to a boolean,
      + * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
      + * as a callback function.
      + * @param onReady what to do when testFx condition is fulfilled,
      + * it can be passed in as a string (e.g.: "1 == 1" or "$('#bar').is(':visible')" or
      + * as a callback function.
      + * @param timeOutMillis the max amount of time to wait. If not specified, 3 sec is used.
      + */
      +function waitFor(testFx, onReady, timeOutMillis) {
      +	var maxtimeOutMillis = timeOutMillis ? timeOutMillis : 3001, //< Default Max Timout is 3s
      +		start = new Date().getTime(),
      +		condition = false,
      +		interval = setInterval(function() {
      +			if ((new Date().getTime() - start < maxtimeOutMillis) && !condition) {
      +				// If not time-out yet and condition not yet fulfilled
      +				condition = (typeof(testFx) === "string" ? eval(testFx) : testFx()); //< defensive code
      +			} else {
      +				if(!condition) {
      +					// If condition still not fulfilled (timeout but condition is 'false')
      +					console.log("'waitFor()' timeout");
      +					phantom.exit(1);
      +				} else {
      +					// Condition fulfilled (timeout and/or condition is 'true')
      +					console.debug("'waitFor()' finished in " + (new Date().getTime() - start) + "ms.");
      +					typeof(onReady) === "string" ? eval(onReady) : onReady(); //< Do what it's supposed to do once the condition is fulfilled
      +					clearInterval(interval); //< Stop this interval
      +				}
      +			}
      +		}, 100);
      +};
      +
      +if (phantom.args.length < 1) {
      +	console.log('Usage: phantomjs-test-runner.js [-j|--junit] ');
      +	phantom.exit();
      +} else {
      +	var args = phantom.args.slice(),
      +		url = args.pop();
      +	window.console.debug = function() {};
      +	phantom.outputFormat = 'console';
      +	if (args.length) {
      +		var arg = args.pop().toLowerCase();
      +		switch (arg) {
      +		case "-j":
      +		case "--junit":
      +			phantom.outputFormat = 'junit';
      +			break;
      +		default:
      +		}
      +	}
      +}
      +
      +var page = new WebPage();
      +page.onConsoleMessage = function(msg) {
      +	console.log(msg);
      +};
      +page.open(url, function(status) {
      +	if (status !== "success") {
      +		console.log('Unable to access network');
      +		phantom.exit();
      +	} else {
      +		waitFor(function() {
      +			return page.evaluate(function() {
      +				var el = document.getElementById('qunit-testresult');
      +				if (el && el.innerText.match('completed')) {
      +					return true;
      +				}
      +				return false;
      +			});
      +		}, function() {
      +			var failedNum = page.evaluate(function() {
      +				var el = document.getElementById('qunit-testresult');
      +				try {
      +					return el.getElementsByClassName('failed')[0].innerHTML;
      +				} catch (e) { }
      +				return 10000;
      +			});
      +			phantom.exit((parseInt(failedNum, 10) > 0) ? 1 : 0);
      +		});
      +	}
      +});
      \ No newline at end of file
      diff --git a/src/bower_components/stacktrace.js/test/lib/qunit-browserscope.js b/src/bower_components/stacktrace.js/test/lib/qunit-browserscope.js
      new file mode 100644
      index 0000000000000..26f041da14606
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/lib/qunit-browserscope.js
      @@ -0,0 +1,38 @@
      +/*global QUnit */
      +// global variable - test results for BrowserScope
      +var _bTestResults = {};
      +(function() {
      +    var testKey = 'agt1YS1wcm9maWxlcnINCxIEVGVzdBjr68MRDA';
      +    var callbackName = "showBrowserScopeResults";
      +
      +    // Add URL option in QUnit to toggle publishing results to BrowserScope.org
      +    QUnit.config.urlConfig.push("publish");
      +    QUnit.config.testTimeout = 1000; // Timeout for async tests
      +
      +    // Build-up the test results beacon for BrowserScope.org
      +    QUnit.testDone(function(test) {
      +        // make sure all assertions passed successfully
      +        if (!test.failed && test.total === test.passed) {
      +            _bTestResults[test.name] = 1;
      +        } else {
      +            _bTestResults[test.name] = 0;
      +        }
      +    });
      +
      +    // If the user agreed to publish results to BrowserScope.org, go for it!
      +    QUnit.done(function(result) {
      +        if (QUnit.config.publish) {
      +            var newScript = document.createElement('script');
      +            newScript.src = 'http://www.browserscope.org/user/beacon/' + testKey + "?callback=" + callbackName;
      +            var firstScript = document.getElementsByTagName('script')[0];
      +            firstScript.parentNode.insertBefore(newScript, firstScript);
      +        }
      +    });
      +
      +    // Load the results widget from browserscope.org
      +    window[callbackName] = function() {
      +        var script = document.createElement('script');
      +        script.src = "http://www.browserscope.org/user/tests/table/" + testKey + "?o=js";
      +        document.body.appendChild(script);
      +    };
      +}());
      diff --git a/src/bower_components/stacktrace.js/test/lib/qunit-console-outputter.js b/src/bower_components/stacktrace.js/test/lib/qunit-console-outputter.js
      new file mode 100644
      index 0000000000000..3fa0501de6912
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/lib/qunit-console-outputter.js
      @@ -0,0 +1,35 @@
      +var module;
      +QUnit.moduleStart = function(context) {
      +	module = context.name;
      +}
      +var current_test_assertions = [];
      +QUnit.testDone = function(result) {
      +	var name = module + ": " + result.name;
      +	if (result.failed) {
      +		console.log("\u001B[31m✖ " + name);
      +		for (var i = 0; i < current_test_assertions.length; i++) {
      +			console.log("	" + current_test_assertions[i]);
      +		}
      +		console.log("\u001B[39m");
      +	}
      +	current_test_assertions = [];
      +};
      +
      +QUnit.log = function(details) {
      +	if (details.result) {
      +		return;
      +	}
      +	var response = details.message || "";
      +	if (details.expected) {
      +		if (response) {
      +			response += ", ";
      +		}
      +		response = "expected: " + details.expected + ", but was: " + details.actual;
      +	}
      +	current_test_assertions.push("Failed assertion: " + response);
      +};
      +
      +QUnit.done = function(result) {
      +	console.log("Took " + result.runtime + "ms to run " + result.total + " tests. \u001B[32m✔ " + result.passed + "\u001B[39m \u001B[31m✖ " + result.failed + "\u001B[39m ");
      +	return result.failed > 0 ? 1 : 0;
      +};
      \ No newline at end of file
      diff --git a/src/bower_components/stacktrace.js/test/lib/qunit-junit-outputter.js b/src/bower_components/stacktrace.js/test/lib/qunit-junit-outputter.js
      new file mode 100644
      index 0000000000000..7c83bef3b4874
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/lib/qunit-junit-outputter.js
      @@ -0,0 +1,76 @@
      +/*global QUnit, console */
      +(function() {
      +    var module, moduleStart, testStart, testCases = [], current_test_assertions = [];
      +    console.log('');
      +    console.log('');
      +
      +    QUnit.begin(function() {
      +        // That does not work when invoked in PhantomJS
      +    });
      +
      +    QUnit.moduleStart(function(context) {
      +        // context = { name }
      +        moduleStart = new Date();
      +        module = context.name;
      +        testCases = [];
      +    });
      +
      +    QUnit.moduleDone(function(context) {
      +        // context = { name, failed, passed, total }
      +        var xml = '\t\n';
      +
      +        current_test_assertions.push(xml);
      +    });
      +
      +    QUnit.done(function(result) {
      +        // result = { failed, passed, total, runtime }
      +        console.log('');
      +        return result.failed > 0 ? 1 : 0;
      +    });
      +}());
      diff --git a/src/bower_components/stacktrace.js/test/lib/qunit.css b/src/bower_components/stacktrace.js/test/lib/qunit.css
      new file mode 100644
      index 0000000000000..e114ea062bcd7
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/lib/qunit.css
      @@ -0,0 +1,226 @@
      +/**
      + * QUnit 1.2.0pre - A JavaScript Unit Testing Framework
      + *
      + * http://docs.jquery.com/QUnit
      + *
      + * Copyright (c) 2011 John Resig, Jörn Zaefferer
      + * Dual licensed under the MIT (MIT-LICENSE.txt)
      + * or GPL (GPL-LICENSE.txt) licenses.
      + */
      +
      +/** Font Family and Sizes */
      +
      +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult {
      +	font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif;
      +}
      +
      +#qunit-testrunner-toolbar, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; }
      +#qunit-tests { font-size: smaller; }
      +
      +
      +/** Resets */
      +
      +#qunit-tests, #qunit-tests ol, #qunit-header, #qunit-banner, #qunit-userAgent, #qunit-testresult {
      +	margin: 0;
      +	padding: 0;
      +}
      +
      +
      +/** Header */
      +
      +#qunit-header {
      +	padding: 0.5em 0 0.5em 1em;
      +
      +	color: #8699a4;
      +	background-color: #0d3349;
      +
      +	font-size: 1.5em;
      +	line-height: 1em;
      +	font-weight: normal;
      +
      +	border-radius: 15px 15px 0 0;
      +	-moz-border-radius: 15px 15px 0 0;
      +	-webkit-border-top-right-radius: 15px;
      +	-webkit-border-top-left-radius: 15px;
      +}
      +
      +#qunit-header a {
      +	text-decoration: none;
      +	color: #c2ccd1;
      +}
      +
      +#qunit-header a:hover,
      +#qunit-header a:focus {
      +	color: #fff;
      +}
      +
      +#qunit-banner {
      +	height: 5px;
      +}
      +
      +#qunit-testrunner-toolbar {
      +	padding: 0.5em 0 0.5em 2em;
      +	color: #5E740B;
      +	background-color: #eee;
      +}
      +
      +#qunit-userAgent {
      +	padding: 0.5em 0 0.5em 2.5em;
      +	background-color: #2b81af;
      +	color: #fff;
      +	text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
      +}
      +
      +
      +/** Tests: Pass/Fail */
      +
      +#qunit-tests {
      +	list-style-position: inside;
      +}
      +
      +#qunit-tests li {
      +	padding: 0.4em 0.5em 0.4em 2.5em;
      +	border-bottom: 1px solid #fff;
      +	list-style-position: inside;
      +}
      +
      +#qunit-tests.hidepass li.pass, #qunit-tests.hidepass li.running  {
      +	display: none;
      +}
      +
      +#qunit-tests li strong {
      +	cursor: pointer;
      +}
      +
      +#qunit-tests li a {
      +	padding: 0.5em;
      +	color: #c2ccd1;
      +	text-decoration: none;
      +}
      +#qunit-tests li a:hover,
      +#qunit-tests li a:focus {
      +	color: #000;
      +}
      +
      +#qunit-tests ol {
      +	margin-top: 0.5em;
      +	padding: 0.5em;
      +
      +	background-color: #fff;
      +
      +	border-radius: 15px;
      +	-moz-border-radius: 15px;
      +	-webkit-border-radius: 15px;
      +
      +	box-shadow: inset 0px 2px 13px #999;
      +	-moz-box-shadow: inset 0px 2px 13px #999;
      +	-webkit-box-shadow: inset 0px 2px 13px #999;
      +}
      +
      +#qunit-tests table {
      +	border-collapse: collapse;
      +	margin-top: .2em;
      +}
      +
      +#qunit-tests th {
      +	text-align: right;
      +	vertical-align: top;
      +	padding: 0 .5em 0 0;
      +}
      +
      +#qunit-tests td {
      +	vertical-align: top;
      +}
      +
      +#qunit-tests pre {
      +	margin: 0;
      +	white-space: pre-wrap;
      +	word-wrap: break-word;
      +}
      +
      +#qunit-tests del {
      +	background-color: #e0f2be;
      +	color: #374e0c;
      +	text-decoration: none;
      +}
      +
      +#qunit-tests ins {
      +	background-color: #ffcaca;
      +	color: #500;
      +	text-decoration: none;
      +}
      +
      +/*** Test Counts */
      +
      +#qunit-tests b.counts                       { color: black; }
      +#qunit-tests b.passed                       { color: #5E740B; }
      +#qunit-tests b.failed                       { color: #710909; }
      +
      +#qunit-tests li li {
      +	margin: 0.5em;
      +	padding: 0.4em 0.5em 0.4em 0.5em;
      +	background-color: #fff;
      +	border-bottom: none;
      +	list-style-position: inside;
      +}
      +
      +/*** Passing Styles */
      +
      +#qunit-tests li li.pass {
      +	color: #5E740B;
      +	background-color: #fff;
      +	border-left: 26px solid #C6E746;
      +}
      +
      +#qunit-tests .pass                          { color: #528CE0; background-color: #D2E0E6; }
      +#qunit-tests .pass .test-name               { color: #366097; }
      +
      +#qunit-tests .pass .test-actual,
      +#qunit-tests .pass .test-expected           { color: #999999; }
      +
      +#qunit-banner.qunit-pass                    { background-color: #C6E746; }
      +
      +/*** Failing Styles */
      +
      +#qunit-tests li li.fail {
      +	color: #710909;
      +	background-color: #fff;
      +	border-left: 26px solid #EE5757;
      +	white-space: pre;
      +}
      +
      +#qunit-tests > li:last-child {
      +	border-radius: 0 0 15px 15px;
      +	-moz-border-radius: 0 0 15px 15px;
      +	-webkit-border-bottom-right-radius: 15px;
      +	-webkit-border-bottom-left-radius: 15px;
      +}
      +
      +#qunit-tests .fail                          { color: #000000; background-color: #EE5757; }
      +#qunit-tests .fail .test-name,
      +#qunit-tests .fail .module-name             { color: #000000; }
      +
      +#qunit-tests .fail .test-actual             { color: #EE5757; }
      +#qunit-tests .fail .test-expected           { color: green;   }
      +
      +#qunit-banner.qunit-fail                    { background-color: #EE5757; }
      +
      +
      +/** Result */
      +
      +#qunit-testresult {
      +	padding: 0.5em 0.5em 0.5em 2.5em;
      +
      +	color: #2b81af;
      +	background-color: #D2E0E6;
      +
      +	border-bottom: 1px solid white;
      +}
      +
      +/** Fixture */
      +
      +#qunit-fixture {
      +	position: absolute;
      +	top: -10000px;
      +	left: -10000px;
      +}
      diff --git a/src/bower_components/stacktrace.js/test/lib/qunit.js b/src/bower_components/stacktrace.js/test/lib/qunit.js
      new file mode 100644
      index 0000000000000..9aeaf991844bb
      --- /dev/null
      +++ b/src/bower_components/stacktrace.js/test/lib/qunit.js
      @@ -0,0 +1,1597 @@
      +/**
      + * QUnit 1.2.0pre - A JavaScript Unit Testing Framework
      + *
      + * http://docs.jquery.com/QUnit
      + *
      + * Copyright (c) 2011 John Resig, Jörn Zaefferer
      + * Dual licensed under the MIT (MIT-LICENSE.txt)
      + * or GPL (GPL-LICENSE.txt) licenses.
      + */
      +
      +(function(window) {
      +
      +var defined = {
      +	setTimeout: typeof window.setTimeout !== "undefined",
      +	sessionStorage: (function() {
      +		try {
      +			return !!sessionStorage.getItem;
      +		} catch(e) {
      +			return false;
      +		}
      +	})()
      +};
      +
      +var	testId = 0,
      +	toString = Object.prototype.toString,
      +	hasOwn = Object.prototype.hasOwnProperty;
      +
      +var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
      +	this.name = name;
      +	this.testName = testName;
      +	this.expected = expected;
      +	this.testEnvironmentArg = testEnvironmentArg;
      +	this.async = async;
      +	this.callback = callback;
      +	this.assertions = [];
      +};
      +Test.prototype = {
      +	init: function() {
      +		var tests = id("qunit-tests");
      +		if (tests) {
      +			var b = document.createElement("strong");
      +				b.innerHTML = "Running " + this.name;
      +			var li = document.createElement("li");
      +				li.appendChild( b );
      +				li.className = "running";
      +				li.id = this.id = "test-output" + testId++;
      +			tests.appendChild( li );
      +		}
      +	},
      +	setup: function() {
      +		if (this.module != config.previousModule) {
      +			if ( config.previousModule ) {
      +				runLoggingCallbacks('moduleDone', QUnit, {
      +					name: config.previousModule,
      +					failed: config.moduleStats.bad,
      +					passed: config.moduleStats.all - config.moduleStats.bad,
      +					total: config.moduleStats.all
      +				} );
      +			}
      +			config.previousModule = this.module;
      +			config.moduleStats = { all: 0, bad: 0 };
      +			runLoggingCallbacks( 'moduleStart', QUnit, {
      +				name: this.module
      +			} );
      +		}
      +
      +		config.current = this;
      +		this.testEnvironment = extend({
      +			setup: function() {},
      +			teardown: function() {}
      +		}, this.moduleTestEnvironment);
      +		if (this.testEnvironmentArg) {
      +			extend(this.testEnvironment, this.testEnvironmentArg);
      +		}
      +
      +		runLoggingCallbacks( 'testStart', QUnit, {
      +			name: this.testName,
      +			module: this.module
      +		});
      +
      +		// allow utility functions to access the current test environment
      +		// TODO why??
      +		QUnit.current_testEnvironment = this.testEnvironment;
      +
      +		try {
      +			if ( !config.pollution ) {
      +				saveGlobal();
      +			}
      +
      +			this.testEnvironment.setup.call(this.testEnvironment);
      +		} catch(e) {
      +			QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
      +		}
      +	},
      +	run: function() {
      +		config.current = this;
      +		if ( this.async ) {
      +			QUnit.stop();
      +		}
      +
      +		if ( config.notrycatch ) {
      +			this.callback.call(this.testEnvironment);
      +			return;
      +		}
      +		try {
      +			this.callback.call(this.testEnvironment);
      +		} catch(e) {
      +			fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
      +			QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
      +			// else next test will carry the responsibility
      +			saveGlobal();
      +
      +			// Restart the tests if they're blocking
      +			if ( config.blocking ) {
      +				QUnit.start();
      +			}
      +		}
      +	},
      +	teardown: function() {
      +		config.current = this;
      +		try {
      +			this.testEnvironment.teardown.call(this.testEnvironment);
      +			checkPollution();
      +		} catch(e) {
      +			QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
      +		}
      +	},
      +	finish: function() {
      +		config.current = this;
      +		if ( this.expected != null && this.expected != this.assertions.length ) {
      +			QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
      +		}
      +
      +		var good = 0, bad = 0,
      +			tests = id("qunit-tests");
      +
      +		config.stats.all += this.assertions.length;
      +		config.moduleStats.all += this.assertions.length;
      +
      +		if ( tests ) {
      +			var ol = document.createElement("ol");
      +
      +			for ( var i = 0; i < this.assertions.length; i++ ) {
      +				var assertion = this.assertions[i];
      +
      +				var li = document.createElement("li");
      +				li.className = assertion.result ? "pass" : "fail";
      +				li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
      +				ol.appendChild( li );
      +
      +				if ( assertion.result ) {
      +					good++;
      +				} else {
      +					bad++;
      +					config.stats.bad++;
      +					config.moduleStats.bad++;
      +				}
      +			}
      +
      +			// store result when possible
      +			if ( QUnit.config.reorder && defined.sessionStorage ) {
      +				if (bad) {
      +					sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
      +				} else {
      +					sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
      +				}
      +			}
      +
      +			if (bad == 0) {
      +				ol.style.display = "none";
      +			}
      +
      +			var b = document.createElement("strong");
      +			b.innerHTML = this.name + " (" + bad + ", " + good + ", " + this.assertions.length + ")";
      +
      +			var a = document.createElement("a");
      +			a.innerHTML = "Rerun";
      +			a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
      +
      +			addEvent(b, "click", function() {
      +				var next = b.nextSibling.nextSibling,
      +					display = next.style.display;
      +				next.style.display = display === "none" ? "block" : "none";
      +			});
      +
      +			addEvent(b, "dblclick", function(e) {
      +				var target = e && e.target ? e.target : window.event.srcElement;
      +				if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
      +					target = target.parentNode;
      +				}
      +				if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
      +					window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
      +				}
      +			});
      +
      +			var li = id(this.id);
      +			li.className = bad ? "fail" : "pass";
      +			li.removeChild( li.firstChild );
      +			li.appendChild( b );
      +			li.appendChild( a );
      +			li.appendChild( ol );
      +
      +		} else {
      +			for ( var i = 0; i < this.assertions.length; i++ ) {
      +				if ( !this.assertions[i].result ) {
      +					bad++;
      +					config.stats.bad++;
      +					config.moduleStats.bad++;
      +				}
      +			}
      +		}
      +
      +		try {
      +			QUnit.reset();
      +		} catch(e) {
      +			fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
      +		}
      +
      +		runLoggingCallbacks( 'testDone', QUnit, {
      +			name: this.testName,
      +			module: this.module,
      +			failed: bad,
      +			passed: this.assertions.length - bad,
      +			total: this.assertions.length
      +		} );
      +	},
      +
      +	queue: function() {
      +		var test = this;
      +		synchronize(function() {
      +			test.init();
      +		});
      +		function run() {
      +			// each of these can by async
      +			synchronize(function() {
      +				test.setup();
      +			});
      +			synchronize(function() {
      +				test.run();
      +			});
      +			synchronize(function() {
      +				test.teardown();
      +			});
      +			synchronize(function() {
      +				test.finish();
      +			});
      +		}
      +		// defer when previous test run passed, if storage is available
      +		var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
      +		if (bad) {
      +			run();
      +		} else {
      +			synchronize(run, true);
      +		};
      +	}
      +
      +};
      +
      +var QUnit = {
      +
      +	// call on start of module test to prepend name to all tests
      +	module: function(name, testEnvironment) {
      +		config.currentModule = name;
      +		config.currentModuleTestEnviroment = testEnvironment;
      +	},
      +
      +	asyncTest: function(testName, expected, callback) {
      +		if ( arguments.length === 2 ) {
      +			callback = expected;
      +			expected = null;
      +		}
      +
      +		QUnit.test(testName, expected, callback, true);
      +	},
      +
      +	test: function(testName, expected, callback, async) {
      +		var name = '' + testName + '', testEnvironmentArg;
      +
      +		if ( arguments.length === 2 ) {
      +			callback = expected;
      +			expected = null;
      +		}
      +		// is 2nd argument a testEnvironment?
      +		if ( expected && typeof expected === 'object') {
      +			testEnvironmentArg = expected;
      +			expected = null;
      +		}
      +
      +		if ( config.currentModule ) {
      +			name = '' + config.currentModule + ": " + name;
      +		}
      +
      +		if ( !validTest(config.currentModule + ": " + testName) ) {
      +			return;
      +		}
      +
      +		var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
      +		test.module = config.currentModule;
      +		test.moduleTestEnvironment = config.currentModuleTestEnviroment;
      +		test.queue();
      +	},
      +
      +	/**
      +	 * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
      +	 */
      +	expect: function(asserts) {
      +		config.current.expected = asserts;
      +	},
      +
      +	/**
      +	 * Asserts true.
      +	 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
      +	 */
      +	ok: function(a, msg) {
      +		a = !!a;
      +		var details = {
      +			result: a,
      +			message: msg
      +		};
      +		msg = escapeInnerText(msg);
      +		runLoggingCallbacks( 'log', QUnit, details );
      +		config.current.assertions.push({
      +			result: a,
      +			message: msg
      +		});
      +	},
      +
      +	/**
      +	 * Checks that the first two arguments are equal, with an optional message.
      +	 * Prints out both actual and expected values.
      +	 *
      +	 * Prefered to ok( actual == expected, message )
      +	 *
      +	 * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
      +	 *
      +	 * @param Object actual
      +	 * @param Object expected
      +	 * @param String message (optional)
      +	 */
      +	equal: function(actual, expected, message) {
      +		QUnit.push(expected == actual, actual, expected, message);
      +	},
      +
      +	notEqual: function(actual, expected, message) {
      +		QUnit.push(expected != actual, actual, expected, message);
      +	},
      +
      +	deepEqual: function(actual, expected, message) {
      +		QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
      +	},
      +
      +	notDeepEqual: function(actual, expected, message) {
      +		QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
      +	},
      +
      +	strictEqual: function(actual, expected, message) {
      +		QUnit.push(expected === actual, actual, expected, message);
      +	},
      +
      +	notStrictEqual: function(actual, expected, message) {
      +		QUnit.push(expected !== actual, actual, expected, message);
      +	},
      +
      +	raises: function(block, expected, message) {
      +		var actual, ok = false;
      +
      +		if (typeof expected === 'string') {
      +			message = expected;
      +			expected = null;
      +		}
      +
      +		try {
      +			block();
      +		} catch (e) {
      +			actual = e;
      +		}
      +
      +		if (actual) {
      +			// we don't want to validate thrown error
      +			if (!expected) {
      +				ok = true;
      +			// expected is a regexp
      +			} else if (QUnit.objectType(expected) === "regexp") {
      +				ok = expected.test(actual);
      +			// expected is a constructor
      +			} else if (actual instanceof expected) {
      +				ok = true;
      +			// expected is a validation function which returns true is validation passed
      +			} else if (expected.call({}, actual) === true) {
      +				ok = true;
      +			}
      +		}
      +
      +		QUnit.ok(ok, message);
      +	},
      +
      +	start: function(count) {
      +		config.semaphore -= count || 1;
      +		if (config.semaphore > 0) {
      +			// don't start until equal number of stop-calls
      +			return;
      +		}
      +		if (config.semaphore < 0) {
      +			// ignore if start is called more often then stop
      +			config.semaphore = 0;
      +		}
      +		// A slight delay, to avoid any current callbacks
      +		if ( defined.setTimeout ) {
      +			window.setTimeout(function() {
      +				if (config.semaphore > 0) {
      +					return;
      +				}
      +				if ( config.timeout ) {
      +					clearTimeout(config.timeout);
      +				}
      +
      +				config.blocking = false;
      +				process(true);
      +			}, 13);
      +		} else {
      +			config.blocking = false;
      +			process(true);
      +		}
      +	},
      +
      +	stop: function(count) {
      +		config.semaphore += count || 1;
      +		config.blocking = true;
      +
      +		if ( config.testTimeout && defined.setTimeout ) {
      +			clearTimeout(config.timeout);
      +			config.timeout = window.setTimeout(function() {
      +				QUnit.ok( false, "Test timed out" );
      +				config.semaphore = 1;
      +				QUnit.start();
      +			}, config.testTimeout);
      +		}
      +	}
      +};
      +
      +//We want access to the constructor's prototype
      +(function() {
      +	function F(){};
      +	F.prototype = QUnit;
      +	QUnit = new F();
      +	//Make F QUnit's constructor so that we can add to the prototype later
      +	QUnit.constructor = F;
      +})();
      +
      +// Backwards compatibility, deprecated
      +QUnit.equals = QUnit.equal;
      +QUnit.same = QUnit.deepEqual;
      +
      +// Maintain internal state
      +var config = {
      +	// The queue of tests to run
      +	queue: [],
      +
      +	// block until document ready
      +	blocking: true,
      +
      +	// when enabled, show only failing tests
      +	// gets persisted through sessionStorage and can be changed in UI via checkbox
      +	hidepassed: false,
      +
      +	// by default, run previously failed tests first
      +	// very useful in combination with "Hide passed tests" checked
      +	reorder: true,
      +
      +	// by default, modify document.title when suite is done
      +	altertitle: true,
      +
      +	urlConfig: ['noglobals', 'notrycatch'],
      +
      +	//logging callback queues
      +	begin: [],
      +	done: [],
      +	log: [],
      +	testStart: [],
      +	testDone: [],
      +	moduleStart: [],
      +	moduleDone: []
      +};
      +
      +// Load paramaters
      +(function() {
      +	var location = window.location || { search: "", protocol: "file:" },
      +		params = location.search.slice( 1 ).split( "&" ),
      +		length = params.length,
      +		urlParams = {},
      +		current;
      +
      +	if ( params[ 0 ] ) {
      +		for ( var i = 0; i < length; i++ ) {
      +			current = params[ i ].split( "=" );
      +			current[ 0 ] = decodeURIComponent( current[ 0 ] );
      +			// allow just a key to turn on a flag, e.g., test.html?noglobals
      +			current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
      +			urlParams[ current[ 0 ] ] = current[ 1 ];
      +		}
      +	}
      +
      +	QUnit.urlParams = urlParams;
      +	config.filter = urlParams.filter;
      +
      +	// Figure out if we're running the tests from a server or not
      +	QUnit.isLocal = !!(location.protocol === 'file:');
      +})();
      +
      +// Expose the API as global variables, unless an 'exports'
      +// object exists, in that case we assume we're in CommonJS
      +if ( typeof exports === "undefined" || typeof require === "undefined" ) {
      +	extend(window, QUnit);
      +	window.QUnit = QUnit;
      +} else {
      +	extend(exports, QUnit);
      +	exports.QUnit = QUnit;
      +}
      +
      +// define these after exposing globals to keep them in these QUnit namespace only
      +extend(QUnit, {
      +	config: config,
      +
      +	// Initialize the configuration options
      +	init: function() {
      +		extend(config, {
      +			stats: { all: 0, bad: 0 },
      +			moduleStats: { all: 0, bad: 0 },
      +			started: +new Date,
      +			updateRate: 1000,
      +			blocking: false,
      +			autostart: true,
      +			autorun: false,
      +			filter: "",
      +			queue: [],
      +			semaphore: 0
      +		});
      +
      +		var tests = id( "qunit-tests" ),
      +			banner = id( "qunit-banner" ),
      +			result = id( "qunit-testresult" );
      +
      +		if ( tests ) {
      +			tests.innerHTML = "";
      +		}
      +
      +		if ( banner ) {
      +			banner.className = "";
      +		}
      +
      +		if ( result ) {
      +			result.parentNode.removeChild( result );
      +		}
      +
      +		if ( tests ) {
      +			result = document.createElement( "p" );
      +			result.id = "qunit-testresult";
      +			result.className = "result";
      +			tests.parentNode.insertBefore( result, tests );
      +			result.innerHTML = 'Running...
       '; + } + }, + + /** + * Resets the test setup. Useful for tests that modify the DOM. + * + * If jQuery is available, uses jQuery's html(), otherwise just innerHTML. + */ + reset: function() { + if ( window.jQuery ) { + jQuery( "#qunit-fixture" ).html( config.fixture ); + } else { + var main = id( 'qunit-fixture' ); + if ( main ) { + main.innerHTML = config.fixture; + } + } + }, + + /** + * Trigger an event on an element. + * + * @example triggerEvent( document.body, "click" ); + * + * @param DOMElement elem + * @param String type + */ + triggerEvent: function( elem, type, event ) { + if ( document.createEvent ) { + event = document.createEvent("MouseEvents"); + event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView, + 0, 0, 0, 0, 0, false, false, false, false, 0, null); + elem.dispatchEvent( event ); + + } else if ( elem.fireEvent ) { + elem.fireEvent("on"+type); + } + }, + + // Safe object type checking + is: function( type, obj ) { + return QUnit.objectType( obj ) == type; + }, + + objectType: function( obj ) { + if (typeof obj === "undefined") { + return "undefined"; + + // consider: typeof null === object + } + if (obj === null) { + return "null"; + } + + var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || ''; + + switch (type) { + case 'Number': + if (isNaN(obj)) { + return "nan"; + } else { + return "number"; + } + case 'String': + case 'Boolean': + case 'Array': + case 'Date': + case 'RegExp': + case 'Function': + return type.toLowerCase(); + } + if (typeof obj === "object") { + return "object"; + } + return undefined; + }, + + push: function(result, actual, expected, message) { + var details = { + result: result, + message: message, + actual: actual, + expected: expected + }; + + message = escapeInnerText(message) || (result ? "okay" : "failed"); + message = '' + message + ""; + expected = escapeInnerText(QUnit.jsDump.parse(expected)); + actual = escapeInnerText(QUnit.jsDump.parse(actual)); + var output = message + ''; + if (actual != expected) { + output += ''; + output += ''; + } + if (!result) { + var source = sourceFromStacktrace(); + if (source) { + details.source = source; + output += ''; + } + } + output += "
      Expected:
      ' + expected + '
      Result:
      ' + actual + '
      Diff:
      ' + QUnit.diff(expected, actual) +'
      Source:
      ' + escapeInnerText(source) + '
      "; + + runLoggingCallbacks( 'log', QUnit, details ); + + config.current.assertions.push({ + result: !!result, + message: output + }); + }, + + url: function( params ) { + params = extend( extend( {}, QUnit.urlParams ), params ); + var querystring = "?", + key; + for ( key in params ) { + if ( !hasOwn.call( params, key ) ) { + continue; + } + querystring += encodeURIComponent( key ) + "=" + + encodeURIComponent( params[ key ] ) + "&"; + } + return window.location.pathname + querystring.slice( 0, -1 ); + }, + + extend: extend, + id: id, + addEvent: addEvent +}); + +//QUnit.constructor is set to the empty F() above so that we can add to it's prototype later +//Doing this allows us to tell if the following methods have been overwritten on the actual +//QUnit object, which is a deprecated way of using the callbacks. +extend(QUnit.constructor.prototype, { + // Logging callbacks; all receive a single argument with the listed properties + // run test/logs.html for any related changes + begin: registerLoggingCallback('begin'), + // done: { failed, passed, total, runtime } + done: registerLoggingCallback('done'), + // log: { result, actual, expected, message } + log: registerLoggingCallback('log'), + // testStart: { name } + testStart: registerLoggingCallback('testStart'), + // testDone: { name, failed, passed, total } + testDone: registerLoggingCallback('testDone'), + // moduleStart: { name } + moduleStart: registerLoggingCallback('moduleStart'), + // moduleDone: { name, failed, passed, total } + moduleDone: registerLoggingCallback('moduleDone') +}); + +if ( typeof document === "undefined" || document.readyState === "complete" ) { + config.autorun = true; +} + +QUnit.load = function() { + runLoggingCallbacks( 'begin', QUnit, {} ); + + // Initialize the config, saving the execution queue + var oldconfig = extend({}, config); + QUnit.init(); + extend(config, oldconfig); + + config.blocking = false; + + var urlConfigHtml = '', len = config.urlConfig.length; + for ( var i = 0, val; i < len, val = config.urlConfig[i]; i++ ) { + config[val] = QUnit.urlParams[val]; + urlConfigHtml += ''; + } + + var userAgent = id("qunit-userAgent"); + if ( userAgent ) { + userAgent.innerHTML = navigator.userAgent; + } + var banner = id("qunit-header"); + if ( banner ) { + banner.innerHTML = ' ' + banner.innerHTML + ' ' + urlConfigHtml; + addEvent( banner, "change", function( event ) { + var params = {}; + params[ event.target.name ] = event.target.checked ? true : undefined; + window.location = QUnit.url( params ); + }); + } + + var toolbar = id("qunit-testrunner-toolbar"); + if ( toolbar ) { + var filter = document.createElement("input"); + filter.type = "checkbox"; + filter.id = "qunit-filter-pass"; + addEvent( filter, "click", function() { + var ol = document.getElementById("qunit-tests"); + if ( filter.checked ) { + ol.className = ol.className + " hidepass"; + } else { + var tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " "; + ol.className = tmp.replace(/ hidepass /, " "); + } + if ( defined.sessionStorage ) { + if (filter.checked) { + sessionStorage.setItem("qunit-filter-passed-tests", "true"); + } else { + sessionStorage.removeItem("qunit-filter-passed-tests"); + } + } + }); + if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem("qunit-filter-passed-tests") ) { + filter.checked = true; + var ol = document.getElementById("qunit-tests"); + ol.className = ol.className + " hidepass"; + } + toolbar.appendChild( filter ); + + var label = document.createElement("label"); + label.setAttribute("for", "qunit-filter-pass"); + label.innerHTML = "Hide passed tests"; + toolbar.appendChild( label ); + } + + var main = id('qunit-fixture'); + if ( main ) { + config.fixture = main.innerHTML; + } + + if (config.autostart) { + QUnit.start(); + } +}; + +addEvent(window, "load", QUnit.load); + +// addEvent(window, "error") gives us a useless event object +window.onerror = function( message, file, line ) { + if ( QUnit.config.current ) { + ok( false, message + ", " + file + ":" + line ); + } else { + test( "global failure", function() { + ok( false, message + ", " + file + ":" + line ); + }); + } +}; + +function done() { + config.autorun = true; + + // Log the last module results + if ( config.currentModule ) { + runLoggingCallbacks( 'moduleDone', QUnit, { + name: config.currentModule, + failed: config.moduleStats.bad, + passed: config.moduleStats.all - config.moduleStats.bad, + total: config.moduleStats.all + } ); + } + + var banner = id("qunit-banner"), + tests = id("qunit-tests"), + runtime = +new Date - config.started, + passed = config.stats.all - config.stats.bad, + html = [ + 'Tests completed in ', + runtime, + ' milliseconds.
      ', + '', + passed, + ' tests of ', + config.stats.all, + ' passed, ', + config.stats.bad, + ' failed.' + ].join(''); + + if ( banner ) { + banner.className = (config.stats.bad ? "qunit-fail" : "qunit-pass"); + } + + if ( tests ) { + id( "qunit-testresult" ).innerHTML = html; + } + + if ( config.altertitle && typeof document !== "undefined" && document.title ) { + // show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8-charset + document.title = [ + (config.stats.bad ? "\u2716" : "\u2714"), + document.title.replace(/^[\u2714\u2716] /i, "") + ].join(" "); + } + + runLoggingCallbacks( 'done', QUnit, { + failed: config.stats.bad, + passed: passed, + total: config.stats.all, + runtime: runtime + } ); +} + +function validTest( name ) { + var filter = config.filter, + run = false; + + if ( !filter ) { + return true; + } + + var not = filter.charAt( 0 ) === "!"; + if ( not ) { + filter = filter.slice( 1 ); + } + + if ( name.indexOf( filter ) !== -1 ) { + return !not; + } + + if ( not ) { + run = true; + } + + return run; +} + +// so far supports only Firefox, Chrome and Opera (buggy) +// could be extended in the future to use something like https://github.com/csnover/TraceKit +function sourceFromStacktrace() { + try { + throw new Error(); + } catch ( e ) { + if (e.stacktrace) { + // Opera + return e.stacktrace.split("\n")[6]; + } else if (e.stack) { + // Firefox, Chrome + return e.stack.split("\n")[4]; + } else if (e.sourceURL) { + // Safari, PhantomJS + // TODO sourceURL points at the 'throw new Error' line above, useless + //return e.sourceURL + ":" + e.line; + } + } +} + +function escapeInnerText(s) { + if (!s) { + return ""; + } + s = s + ""; + return s.replace(/[\&<>]/g, function(s) { + switch(s) { + case "&": return "&"; + case "<": return "<"; + case ">": return ">"; + default: return s; + } + }); +} + +function synchronize( callback, last ) { + config.queue.push( callback ); + + if ( config.autorun && !config.blocking ) { + process(last); + } +} + +function process( last ) { + var start = new Date().getTime(); + config.depth = config.depth ? config.depth + 1 : 1; + + while ( config.queue.length && !config.blocking ) { + if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) { + config.queue.shift()(); + } else { + window.setTimeout( function(){ + process( last ); + }, 13 ); + break; + } + } + config.depth--; + if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) { + done(); + } +} + +function saveGlobal() { + config.pollution = []; + + if ( config.noglobals ) { + for ( var key in window ) { + if ( !hasOwn.call( window, key ) ) { + continue; + } + config.pollution.push( key ); + } + } +} + +function checkPollution( name ) { + var old = config.pollution; + saveGlobal(); + + var newGlobals = diff( config.pollution, old ); + if ( newGlobals.length > 0 ) { + ok( false, "Introduced global variable(s): " + newGlobals.join(", ") ); + } + + var deletedGlobals = diff( old, config.pollution ); + if ( deletedGlobals.length > 0 ) { + ok( false, "Deleted global variable(s): " + deletedGlobals.join(", ") ); + } +} + +// returns a new Array with the elements that are in a but not in b +function diff( a, b ) { + var result = a.slice(); + for ( var i = 0; i < result.length; i++ ) { + for ( var j = 0; j < b.length; j++ ) { + if ( result[i] === b[j] ) { + result.splice(i, 1); + i--; + break; + } + } + } + return result; +} + +function fail(message, exception, callback) { + if ( typeof console !== "undefined" && console.error && console.warn ) { + console.error(message); + console.error(exception); + console.warn(callback.toString()); + + } else if ( window.opera && opera.postError ) { + opera.postError(message, exception, callback.toString); + } +} + +function extend(a, b) { + for ( var prop in b ) { + if ( b[prop] === undefined ) { + delete a[prop]; + + // Avoid "Member not found" error in IE8 caused by setting window.constructor + } else if ( prop !== "constructor" || a !== window ) { + a[prop] = b[prop]; + } + } + + return a; +} + +function addEvent(elem, type, fn) { + if ( elem.addEventListener ) { + elem.addEventListener( type, fn, false ); + } else if ( elem.attachEvent ) { + elem.attachEvent( "on" + type, fn ); + } else { + fn(); + } +} + +function id(name) { + return !!(typeof document !== "undefined" && document && document.getElementById) && + document.getElementById( name ); +} + +function registerLoggingCallback(key){ + return function(callback){ + config[key].push( callback ); + }; +} + +// Supports deprecated method of completely overwriting logging callbacks +function runLoggingCallbacks(key, scope, args) { + //debugger; + var callbacks; + if ( QUnit.hasOwnProperty(key) ) { + QUnit[key].call(scope, args); + } else { + callbacks = config[key]; + for( var i = 0; i < callbacks.length; i++ ) { + callbacks[i].call( scope, args ); + } + } +} + +// Test for equality any JavaScript type. +// Author: Philippe Rathé +QUnit.equiv = function () { + + var innerEquiv; // the real equiv function + var callers = []; // stack to decide between skip/abort functions + var parents = []; // stack to avoiding loops from circular referencing + + // Call the o related callback with the given arguments. + function bindCallbacks(o, callbacks, args) { + var prop = QUnit.objectType(o); + if (prop) { + if (QUnit.objectType(callbacks[prop]) === "function") { + return callbacks[prop].apply(callbacks, args); + } else { + return callbacks[prop]; // or undefined + } + } + } + + var getProto = Object.getPrototypeOf || function (obj) { + return obj.__proto__; + }; + + var callbacks = function () { + + // for string, boolean, number and null + function useStrictEquality(b, a) { + if (b instanceof a.constructor || a instanceof b.constructor) { + // to catch short annotaion VS 'new' annotation of a + // declaration + // e.g. var i = 1; + // var j = new Number(1); + return a == b; + } else { + return a === b; + } + } + + return { + "string" : useStrictEquality, + "boolean" : useStrictEquality, + "number" : useStrictEquality, + "null" : useStrictEquality, + "undefined" : useStrictEquality, + + "nan" : function(b) { + return isNaN(b); + }, + + "date" : function(b, a) { + return QUnit.objectType(b) === "date" + && a.valueOf() === b.valueOf(); + }, + + "regexp" : function(b, a) { + return QUnit.objectType(b) === "regexp" + && a.source === b.source && // the regex itself + a.global === b.global && // and its modifers + // (gmi) ... + a.ignoreCase === b.ignoreCase + && a.multiline === b.multiline; + }, + + // - skip when the property is a method of an instance (OOP) + // - abort otherwise, + // initial === would have catch identical references anyway + "function" : function() { + var caller = callers[callers.length - 1]; + return caller !== Object && typeof caller !== "undefined"; + }, + + "array" : function(b, a) { + var i, j, loop; + var len; + + // b could be an object literal here + if (!(QUnit.objectType(b) === "array")) { + return false; + } + + len = a.length; + if (len !== b.length) { // safe and faster + return false; + } + + // track reference to avoid circular references + parents.push(a); + for (i = 0; i < len; i++) { + loop = false; + for (j = 0; j < parents.length; j++) { + if (parents[j] === a[i]) { + loop = true;// dont rewalk array + } + } + if (!loop && !innerEquiv(a[i], b[i])) { + parents.pop(); + return false; + } + } + parents.pop(); + return true; + }, + + "object" : function(b, a) { + var i, j, loop; + var eq = true; // unless we can proove it + var aProperties = [], bProperties = []; // collection of + // strings + + // comparing constructors is more strict than using + // instanceof + if (a.constructor !== b.constructor) { + // Allow objects with no prototype to be equivalent to + // objects with Object as their constructor. + if (!((getProto(a) === null && getProto(b) === Object.prototype) || + (getProto(b) === null && getProto(a) === Object.prototype))) + { + return false; + } + } + + // stack constructor before traversing properties + callers.push(a.constructor); + // track reference to avoid circular references + parents.push(a); + + for (i in a) { // be strict: don't ensures hasOwnProperty + // and go deep + loop = false; + for (j = 0; j < parents.length; j++) { + if (parents[j] === a[i]) + loop = true; // don't go down the same path + // twice + } + aProperties.push(i); // collect a's properties + + if (!loop && !innerEquiv(a[i], b[i])) { + eq = false; + break; + } + } + + callers.pop(); // unstack, we are done + parents.pop(); + + for (i in b) { + bProperties.push(i); // collect b's properties + } + + // Ensures identical properties name + return eq + && innerEquiv(aProperties.sort(), bProperties + .sort()); + } + }; + }(); + + innerEquiv = function() { // can take multiple arguments + var args = Array.prototype.slice.apply(arguments); + if (args.length < 2) { + return true; // end transition + } + + return (function(a, b) { + if (a === b) { + return true; // catch the most you can + } else if (a === null || b === null || typeof a === "undefined" + || typeof b === "undefined" + || QUnit.objectType(a) !== QUnit.objectType(b)) { + return false; // don't lose time with error prone cases + } else { + return bindCallbacks(a, callbacks, [ b, a ]); + } + + // apply transition with (1..n) arguments + })(args[0], args[1]) + && arguments.callee.apply(this, args.splice(1, + args.length - 1)); + }; + + return innerEquiv; + +}(); + +/** + * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | + * http://flesler.blogspot.com Licensed under BSD + * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008 + * + * @projectDescription Advanced and extensible data dumping for Javascript. + * @version 1.0.0 + * @author Ariel Flesler + * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html} + */ +QUnit.jsDump = (function() { + function quote( str ) { + return '"' + str.toString().replace(/"/g, '\\"') + '"'; + }; + function literal( o ) { + return o + ''; + }; + function join( pre, arr, post ) { + var s = jsDump.separator(), + base = jsDump.indent(), + inner = jsDump.indent(1); + if ( arr.join ) + arr = arr.join( ',' + s + inner ); + if ( !arr ) + return pre + post; + return [ pre, inner + arr, base + post ].join(s); + }; + function array( arr, stack ) { + var i = arr.length, ret = Array(i); + this.up(); + while ( i-- ) + ret[i] = this.parse( arr[i] , undefined , stack); + this.down(); + return join( '[', ret, ']' ); + }; + + var reName = /^function (\w+)/; + + var jsDump = { + parse:function( obj, type, stack ) { //type is used mostly internally, you can fix a (custom)type in advance + stack = stack || [ ]; + var parser = this.parsers[ type || this.typeOf(obj) ]; + type = typeof parser; + var inStack = inArray(obj, stack); + if (inStack != -1) { + return 'recursion('+(inStack - stack.length)+')'; + } + //else + if (type == 'function') { + stack.push(obj); + var res = parser.call( this, obj, stack ); + stack.pop(); + return res; + } + // else + return (type == 'string') ? parser : this.parsers.error; + }, + typeOf:function( obj ) { + var type; + if ( obj === null ) { + type = "null"; + } else if (typeof obj === "undefined") { + type = "undefined"; + } else if (QUnit.is("RegExp", obj)) { + type = "regexp"; + } else if (QUnit.is("Date", obj)) { + type = "date"; + } else if (QUnit.is("Function", obj)) { + type = "function"; + } else if (typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined") { + type = "window"; + } else if (obj.nodeType === 9) { + type = "document"; + } else if (obj.nodeType) { + type = "node"; + } else if ( + // native arrays + toString.call( obj ) === "[object Array]" || + // NodeList objects + ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) ) + ) { + type = "array"; + } else { + type = typeof obj; + } + return type; + }, + separator:function() { + return this.multiline ? this.HTML ? '
      ' : '\n' : this.HTML ? ' ' : ' '; + }, + indent:function( extra ) {// extra can be a number, shortcut for increasing-calling-decreasing + if ( !this.multiline ) + return ''; + var chr = this.indentChar; + if ( this.HTML ) + chr = chr.replace(/\t/g,' ').replace(/ /g,' '); + return Array( this._depth_ + (extra||0) ).join(chr); + }, + up:function( a ) { + this._depth_ += a || 1; + }, + down:function( a ) { + this._depth_ -= a || 1; + }, + setParser:function( name, parser ) { + this.parsers[name] = parser; + }, + // The next 3 are exposed so you can use them + quote:quote, + literal:literal, + join:join, + // + _depth_: 1, + // This is the list of parsers, to modify them, use jsDump.setParser + parsers:{ + window: '[Window]', + document: '[Document]', + error:'[ERROR]', //when no parser is found, shouldn't happen + unknown: '[Unknown]', + 'null':'null', + 'undefined':'undefined', + 'function':function( fn ) { + var ret = 'function', + name = 'name' in fn ? fn.name : (reName.exec(fn)||[])[1];//functions never have name in IE + if ( name ) + ret += ' ' + name; + ret += '('; + + ret = [ ret, QUnit.jsDump.parse( fn, 'functionArgs' ), '){'].join(''); + return join( ret, QUnit.jsDump.parse(fn,'functionCode'), '}' ); + }, + array: array, + nodelist: array, + arguments: array, + object:function( map, stack ) { + var ret = [ ]; + QUnit.jsDump.up(); + for ( var key in map ) { + var val = map[key]; + ret.push( QUnit.jsDump.parse(key,'key') + ': ' + QUnit.jsDump.parse(val, undefined, stack)); + } + QUnit.jsDump.down(); + return join( '{', ret, '}' ); + }, + node:function( node ) { + var open = QUnit.jsDump.HTML ? '<' : '<', + close = QUnit.jsDump.HTML ? '>' : '>'; + + var tag = node.nodeName.toLowerCase(), + ret = open + tag; + + for ( var a in QUnit.jsDump.DOMAttrs ) { + var val = node[QUnit.jsDump.DOMAttrs[a]]; + if ( val ) + ret += ' ' + a + '=' + QUnit.jsDump.parse( val, 'attribute' ); + } + return ret + close + open + '/' + tag + close; + }, + functionArgs:function( fn ) {//function calls it internally, it's the arguments part of the function + var l = fn.length; + if ( !l ) return ''; + + var args = Array(l); + while ( l-- ) + args[l] = String.fromCharCode(97+l);//97 is 'a' + return ' ' + args.join(', ') + ' '; + }, + key:quote, //object calls it internally, the key part of an item in a map + functionCode:'[code]', //function calls it internally, it's the content of the function + attribute:quote, //node calls it internally, it's an html attribute value + string:quote, + date:quote, + regexp:literal, //regex + number:literal, + 'boolean':literal + }, + DOMAttrs:{//attributes to dump from nodes, name=>realName + id:'id', + name:'name', + 'class':'className' + }, + HTML:false,//if true, entities are escaped ( <, >, \t, space and \n ) + indentChar:' ',//indentation unit + multiline:true //if true, items in a collection, are separated by a \n, else just a space. + }; + + return jsDump; +})(); + +// from Sizzle.js +function getText( elems ) { + var ret = "", elem; + + for ( var i = 0; elems[i]; i++ ) { + elem = elems[i]; + + // Get the text from text nodes and CDATA nodes + if ( elem.nodeType === 3 || elem.nodeType === 4 ) { + ret += elem.nodeValue; + + // Traverse everything else, except comment nodes + } else if ( elem.nodeType !== 8 ) { + ret += getText( elem.childNodes ); + } + } + + return ret; +}; + +//from jquery.js +function inArray( elem, array ) { + if ( array.indexOf ) { + return array.indexOf( elem ); + } + + for ( var i = 0, length = array.length; i < length; i++ ) { + if ( array[ i ] === elem ) { + return i; + } + } + + return -1; +} + +/* + * Javascript Diff Algorithm + * By John Resig (http://ejohn.org/) + * Modified by Chu Alan "sprite" + * + * Released under the MIT license. + * + * More Info: + * http://ejohn.org/projects/javascript-diff-algorithm/ + * + * Usage: QUnit.diff(expected, actual) + * + * QUnit.diff("the quick brown fox jumped over", "the quick fox jumps over") == "the quick brown fox jumped jumps over" + */ +QUnit.diff = (function() { + function diff(o, n) { + var ns = {}; + var os = {}; + + for (var i = 0; i < n.length; i++) { + if (ns[n[i]] == null) + ns[n[i]] = { + rows: [], + o: null + }; + ns[n[i]].rows.push(i); + } + + for (var i = 0; i < o.length; i++) { + if (os[o[i]] == null) + os[o[i]] = { + rows: [], + n: null + }; + os[o[i]].rows.push(i); + } + + for (var i in ns) { + if ( !hasOwn.call( ns, i ) ) { + continue; + } + if (ns[i].rows.length == 1 && typeof(os[i]) != "undefined" && os[i].rows.length == 1) { + n[ns[i].rows[0]] = { + text: n[ns[i].rows[0]], + row: os[i].rows[0] + }; + o[os[i].rows[0]] = { + text: o[os[i].rows[0]], + row: ns[i].rows[0] + }; + } + } + + for (var i = 0; i < n.length - 1; i++) { + if (n[i].text != null && n[i + 1].text == null && n[i].row + 1 < o.length && o[n[i].row + 1].text == null && + n[i + 1] == o[n[i].row + 1]) { + n[i + 1] = { + text: n[i + 1], + row: n[i].row + 1 + }; + o[n[i].row + 1] = { + text: o[n[i].row + 1], + row: i + 1 + }; + } + } + + for (var i = n.length - 1; i > 0; i--) { + if (n[i].text != null && n[i - 1].text == null && n[i].row > 0 && o[n[i].row - 1].text == null && + n[i - 1] == o[n[i].row - 1]) { + n[i - 1] = { + text: n[i - 1], + row: n[i].row - 1 + }; + o[n[i].row - 1] = { + text: o[n[i].row - 1], + row: i - 1 + }; + } + } + + return { + o: o, + n: n + }; + } + + return function(o, n) { + o = o.replace(/\s+$/, ''); + n = n.replace(/\s+$/, ''); + var out = diff(o == "" ? [] : o.split(/\s+/), n == "" ? [] : n.split(/\s+/)); + + var str = ""; + + var oSpace = o.match(/\s+/g); + if (oSpace == null) { + oSpace = [" "]; + } + else { + oSpace.push(" "); + } + var nSpace = n.match(/\s+/g); + if (nSpace == null) { + nSpace = [" "]; + } + else { + nSpace.push(" "); + } + + if (out.n.length == 0) { + for (var i = 0; i < out.o.length; i++) { + str += '' + out.o[i] + oSpace[i] + ""; + } + } + else { + if (out.n[0].text == null) { + for (n = 0; n < out.o.length && out.o[n].text == null; n++) { + str += '' + out.o[n] + oSpace[n] + ""; + } + } + + for (var i = 0; i < out.n.length; i++) { + if (out.n[i].text == null) { + str += '' + out.n[i] + nSpace[i] + ""; + } + else { + var pre = ""; + + for (n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++) { + pre += '' + out.o[n] + oSpace[n] + ""; + } + str += " " + out.n[i].text + nSpace[i] + pre; + } + } + } + + return str; + }; +})(); + +})(this); diff --git a/src/bower_components/stacktrace.js/test/lib/sinon-1.2.0.js b/src/bower_components/stacktrace.js/test/lib/sinon-1.2.0.js new file mode 100644 index 0000000000000..7eeb77edac4a6 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/lib/sinon-1.2.0.js @@ -0,0 +1,2916 @@ +/** + * Sinon.JS 1.2.0, 2011/09/27 + * + * @author Christian Johansen (christian@cjohansen.no) + * + * (The BSD License) + * + * Copyright (c) 2010-2011, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +"use strict"; +/*jslint eqeqeq: false, onevar: false, forin: true, nomen: false, regexp: false, plusplus: false*/ +/*global module, require, __dirname, document*/ +/** + * Sinon core utilities. For internal use only. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +var sinon = (function () { + var div = typeof document != "undefined" && document.createElement("div"); + + function isNode(obj) { + var success = false; + + try { + obj.appendChild(div); + success = div.parentNode == obj; + } catch (e) { + return false; + } finally { + try { + obj.removeChild(div); + } catch (e) {} + } + + return success; + } + + function isElement(obj) { + return div && obj && obj.nodeType === 1 && isNode(obj); + } + + return { + wrapMethod: function wrapMethod(object, property, method) { + if (!object) { + throw new TypeError("Should wrap property of object"); + } + + if (typeof method != "function") { + throw new TypeError("Method wrapper should be function"); + } + + var wrappedMethod = object[property]; + var type = typeof wrappedMethod; + + if (type != "function") { + throw new TypeError("Attempted to wrap " + type + " property " + property + + " as function"); + } + + if (wrappedMethod.restore && wrappedMethod.restore.sinon) { + throw new TypeError("Attempted to wrap " + property + " which is already wrapped"); + } + + if (wrappedMethod.calledBefore) { + var verb = !!wrappedMethod.returns ? "stubbed" : "spied on"; + throw new TypeError("Attempted to wrap " + property + " which is already " + verb); + } + + var owned = object.hasOwnProperty(property); + object[property] = method; + method.displayName = property; + + method.restore = function () { + if(owned) { + object[property] = wrappedMethod; + } else { + delete object[property]; + } + }; + + method.restore.sinon = true; + + return method; + }, + + extend: function extend(target) { + for (var i = 1, l = arguments.length; i < l; i += 1) { + for (var prop in arguments[i]) { + if (arguments[i].hasOwnProperty(prop)) { + target[prop] = arguments[i][prop]; + } + + // DONT ENUM bug, only care about toString + if (arguments[i].hasOwnProperty("toString") && + arguments[i].toString != target.toString) { + target.toString = arguments[i].toString; + } + } + } + + return target; + }, + + create: function create(proto) { + var F = function () {}; + F.prototype = proto; + return new F(); + }, + + deepEqual: function deepEqual(a, b) { + if (typeof a != "object" || typeof b != "object") { + return a === b; + } + + if (isElement(a) || isElement(b)) { + return a === b; + } + + if (a === b) { + return true; + } + + if (Object.prototype.toString.call(a) == "[object Array]") { + if (a.length !== b.length) { + return false; + } + + for (var i = 0, l = a.length; i < l; i += 1) { + if (!deepEqual(a[i], b[i])) { + return false; + } + } + + return true; + } + + var prop, aLength = 0, bLength = 0; + + for (prop in a) { + aLength += 1; + + if (!deepEqual(a[prop], b[prop])) { + return false; + } + } + + for (prop in b) { + bLength += 1; + } + + if (aLength != bLength) { + return false; + } + + return true; + }, + + functionName: function functionName(func) { + var name = func.displayName || func.name; + + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + if (!name) { + var matches = func.toString().match(/function ([^\s\(]+)/); + name = matches && matches[1]; + } + + return name; + }, + + functionToString: function toString() { + if (this.getCall && this.callCount) { + var thisValue, prop, i = this.callCount; + + while (i--) { + thisValue = this.getCall(i).thisValue; + + for (prop in thisValue) { + if (thisValue[prop] === this) { + return prop; + } + } + } + } + + return this.displayName || "sinon fake"; + }, + + getConfig: function (custom) { + var config = {}; + custom = custom || {}; + var defaults = sinon.defaultConfig; + + for (var prop in defaults) { + if (defaults.hasOwnProperty(prop)) { + config[prop] = custom.hasOwnProperty(prop) ? custom[prop] : defaults[prop]; + } + } + + return config; + }, + + format: function (val) { + return "" + val; + }, + + defaultConfig: { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }, + + timesInWords: function timesInWords(count) { + return count == 1 && "once" || + count == 2 && "twice" || + count == 3 && "thrice" || + (count || 0) + " times"; + }, + + calledInOrder: function (spies) { + for (var i = 1, l = spies.length; i < l; i++) { + if (!spies[i - 1].calledBefore(spies[i])) { + return false; + } + } + + return true; + }, + + orderByFirstCall: function (spies) { + return spies.sort(function (a, b) { + // uuid, won't ever be equal + return a.getCall(0).callId < b.getCall(0).callId ? -1 : 1; + }); + } + }; +}()); + +if (typeof module == "object" && typeof require == "function") { + module.exports = sinon; + module.exports.spy = require("./sinon/spy"); + module.exports.stub = require("./sinon/stub"); + module.exports.mock = require("./sinon/mock"); + module.exports.collection = require("./sinon/collection"); + module.exports.assert = require("./sinon/assert"); + module.exports.sandbox = require("./sinon/sandbox"); + module.exports.test = require("./sinon/test"); + module.exports.testCase = require("./sinon/test_case"); + module.exports.assert = require("./sinon/assert"); +} + +/* @depend ../sinon.js */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Spy functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + var spyCall; + var callId = 0; + var push = [].push; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function spy(object, property) { + if (!property && typeof object == "function") { + return spy.create(object); + } + + if (!object || !property) { + return spy.create(function () {}); + } + + var method = object[property]; + return sinon.wrapMethod(object, property, spy.create(method)); + } + + sinon.extend(spy, (function () { + var slice = Array.prototype.slice; + + function delegateToCalls(api, method, matchAny, actual, notCalled) { + api[method] = function () { + if (!this.called) { + return !!notCalled; + } + + var currentCall; + var matches = 0; + + for (var i = 0, l = this.callCount; i < l; i += 1) { + currentCall = this.getCall(i); + + if (currentCall[actual || method].apply(currentCall, arguments)) { + matches += 1; + + if (matchAny) { + return true; + } + } + } + + return matches === this.callCount; + }; + } + + function matchingFake(fakes, args, strict) { + if (!fakes) { + return; + } + + var alen = args.length; + + for (var i = 0, l = fakes.length; i < l; i++) { + if (fakes[i].matches(args, strict)) { + return fakes[i]; + } + } + } + + var uuid = 0; + + // Public API + var spyApi = { + reset: function () { + this.called = false; + this.calledOnce = false; + this.calledTwice = false; + this.calledThrice = false; + this.callCount = 0; + this.args = []; + this.returnValues = []; + this.thisValues = []; + this.exceptions = []; + this.callIds = []; + }, + + create: function create(func) { + var name; + + if (typeof func != "function") { + func = function () {}; + } else { + name = sinon.functionName(func); + } + + function proxy() { + return proxy.invoke(func, this, slice.call(arguments)); + } + + sinon.extend(proxy, spy); + delete proxy.create; + sinon.extend(proxy, func); + + proxy.reset(); + proxy.prototype = func.prototype; + proxy.displayName = name || "spy"; + proxy.toString = sinon.functionToString; + proxy._create = sinon.spy.create; + proxy.id = "spy#" + uuid++; + + return proxy; + }, + + invoke: function invoke(func, thisValue, args) { + var matching = matchingFake(this.fakes, args); + var exception, returnValue; + this.called = true; + this.callCount += 1; + this.calledOnce = this.callCount == 1; + this.calledTwice = this.callCount == 2; + this.calledThrice = this.callCount == 3; + push.call(this.thisValues, thisValue); + push.call(this.args, args); + push.call(this.callIds, callId++); + + try { + if (matching) { + returnValue = matching.invoke(func, thisValue, args); + } else { + returnValue = (this.func || func).apply(thisValue, args); + } + } catch (e) { + push.call(this.returnValues, undefined); + exception = e; + throw e; + } finally { + push.call(this.exceptions, exception); + } + + push.call(this.returnValues, returnValue); + + return returnValue; + }, + + getCall: function getCall(i) { + if (i < 0 || i >= this.callCount) { + return null; + } + + return spyCall.create(this, this.thisValues[i], this.args[i], + this.returnValues[i], this.exceptions[i], + this.callIds[i]); + }, + + calledBefore: function calledBefore(spyFn) { + if (!this.called) { + return false; + } + + if (!spyFn.called) { + return true; + } + + return this.callIds[0] < spyFn.callIds[0]; + }, + + calledAfter: function calledAfter(spyFn) { + if (!this.called || !spyFn.called) { + return false; + } + + return this.callIds[this.callCount - 1] > spyFn.callIds[spyFn.callCount - 1]; + }, + + withArgs: function () { + var args = slice.call(arguments); + + if (this.fakes) { + var match = matchingFake(this.fakes, args, true); + + if (match) { + return match; + } + } else { + this.fakes = []; + } + + var original = this; + var fake = this._create(); + fake.matchingAguments = args; + push.call(this.fakes, fake); + + fake.withArgs = function () { + return original.withArgs.apply(original, arguments); + }; + + return fake; + }, + + matches: function (args, strict) { + var margs = this.matchingAguments; + + if (margs.length <= args.length && + sinon.deepEqual(margs, args.slice(0, margs.length))) { + return !strict || margs.length == args.length; + } + }, + + printf: function (format) { + var spy = this; + var args = [].slice.call(arguments, 1); + var formatter; + + return (format || "").replace(/%(.)/g, function (match, specifyer) { + formatter = spyApi.formatters[specifyer]; + + if (typeof formatter == "function") { + return formatter.call(null, spy, args); + } else if (!isNaN(parseInt(specifyer), 10)) { + return sinon.format(args[specifyer - 1]); + } + + return "%" + specifyer; + }); + } + }; + + delegateToCalls(spyApi, "calledOn", true); + delegateToCalls(spyApi, "alwaysCalledOn", false, "calledOn"); + delegateToCalls(spyApi, "calledWith", true); + delegateToCalls(spyApi, "alwaysCalledWith", false, "calledWith"); + delegateToCalls(spyApi, "calledWithExactly", true); + delegateToCalls(spyApi, "alwaysCalledWithExactly", false, "calledWithExactly"); + delegateToCalls(spyApi, "neverCalledWith", false, "notCalledWith", true); + delegateToCalls(spyApi, "threw", true); + delegateToCalls(spyApi, "alwaysThrew", false, "threw"); + delegateToCalls(spyApi, "returned", true); + delegateToCalls(spyApi, "alwaysReturned", false, "returned"); + delegateToCalls(spyApi, "calledWithNew", true); + delegateToCalls(spyApi, "alwaysCalledWithNew", false, "calledWithNew"); + + spyApi.formatters = { + "c": function (spy) { + return sinon.timesInWords(spy.callCount); + }, + + "n": function (spy) { + return spy.toString(); + }, + + "C": function (spy) { + var calls = []; + + for (var i = 0, l = spy.callCount; i < l; ++i) { + push.call(calls, " " + spy.getCall(i).toString()); + } + + return calls.length > 0 ? "\n" + calls.join("\n") : ""; + }, + + "t": function (spy) { + var objects = []; + + for (var i = 0, l = spy.callCount; i < l; ++i) { + push.call(objects, sinon.format(spy.thisValues[i])); + } + + return objects.join(", "); + }, + + "*": function (spy, args) { + return args.join(", "); + } + }; + + return spyApi; + }())); + + spyCall = (function () { + return { + create: function create(spy, thisValue, args, returnValue, exception, id) { + var proxyCall = sinon.create(spyCall); + delete proxyCall.create; + proxyCall.proxy = spy; + proxyCall.thisValue = thisValue; + proxyCall.args = args; + proxyCall.returnValue = returnValue; + proxyCall.exception = exception; + proxyCall.callId = typeof id == "number" && id || callId++; + + return proxyCall; + }, + + calledOn: function calledOn(thisValue) { + return this.thisValue === thisValue; + }, + + calledWith: function calledWith() { + for (var i = 0, l = arguments.length; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return false; + } + } + + return true; + }, + + calledWithExactly: function calledWithExactly() { + return arguments.length == this.args.length && + this.calledWith.apply(this, arguments); + }, + + notCalledWith: function notCalledWith() { + for (var i = 0, l = arguments.length; i < l; i += 1) { + if (!sinon.deepEqual(arguments[i], this.args[i])) { + return true; + } + } + return false; + }, + + returned: function returned(value) { + return this.returnValue === value; + }, + + threw: function threw(error) { + if (typeof error == "undefined" || !this.exception) { + return !!this.exception; + } + + if (typeof error == "string") { + return this.exception.name == error; + } + + return this.exception === error; + }, + + calledWithNew: function calledWithNew(thisValue) { + return this.thisValue instanceof this.proxy; + }, + + calledBefore: function (other) { + return this.callId < other.callId; + }, + + calledAfter: function (other) { + return this.callId > other.callId; + }, + + toString: function () { + var callStr = this.proxy.toString() + "("; + var args = []; + + for (var i = 0, l = this.args.length; i < l; ++i) { + push.call(args, sinon.format(this.args[i])); + } + + callStr = callStr + args.join(", ") + ")"; + + if (typeof this.returnValue != "undefined") { + callStr += " => " + sinon.format(this.returnValue); + } + + if (this.exception) { + callStr += " !" + this.exception.name; + + if (this.exception.message) { + callStr += "(" + this.exception.message + ")"; + } + } + + return callStr; + } + }; + }()); + + spy.spyCall = spyCall; + + // This steps outside the module sandbox and will be removed + sinon.spyCall = spyCall; + + if (commonJSModule) { + module.exports = spy; + } else { + sinon.spy = spy; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend spy.js + */ +/*jslint eqeqeq: false, onevar: false*/ +/*global module, require, sinon*/ +/** + * Stub functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function stub(object, property, func) { + if (!!func && typeof func != "function") { + throw new TypeError("Custom stub should be function"); + } + + var wrapper; + + if (func) { + wrapper = sinon.spy && sinon.spy.create ? sinon.spy.create(func) : func; + } else { + wrapper = stub.create(); + } + + if (!object && !property) { + return sinon.stub.create(); + } + + if (!property && !!object && typeof object == "object") { + for (var prop in object) { + if (object.hasOwnProperty(prop) && typeof object[prop] == "function") { + stub(object, prop); + } + } + + return object; + } + + return sinon.wrapMethod(object, property, wrapper); + } + + function getCallback(stub, args) { + if (stub.callArgAt < 0) { + for (var i = 0, l = args.length; i < l; ++i) { + if (!stub.callArgProp && typeof args[i] == "function") { + return args[i]; + } + + if (stub.callArgProp && args[i] && + typeof args[i][stub.callArgProp] == "function") { + return args[i][stub.callArgProp]; + } + } + + return null; + } + + return args[stub.callArgAt]; + } + + var join = Array.prototype.join; + + function getCallbackError(stub, func, args) { + if (stub.callArgAt < 0) { + var msg; + + if (stub.callArgProp) { + msg = sinon.functionName(stub) + + " expected to yield to '" + stub.callArgProp + + "', but no object with such a property was passed." + } else { + msg = sinon.functionName(stub) + + " expected to yield, but no callback was passed." + } + + if (args.length > 0) { + msg += " Received [" + join.call(args, ", ") + "]"; + } + + return msg; + } + + return "argument at index " + stub.callArgAt + " is not a function: " + func; + } + + function callCallback(stub, args) { + if (typeof stub.callArgAt == "number") { + var func = getCallback(stub, args); + + if (typeof func != "function") { + throw new TypeError(getCallbackError(stub, func, args)); + } + + func.apply(null, stub.callbackArguments); + } + } + + var uuid = 0; + + sinon.extend(stub, (function () { + var slice = Array.prototype.slice; + + function throwsException(error, message) { + if (typeof error == "string") { + this.exception = new Error(message || ""); + this.exception.name = error; + } else if (!error) { + this.exception = new Error("Error"); + } else { + this.exception = error; + } + + return this; + } + + return { + create: function create() { + var functionStub = function () { + if (functionStub.exception) { + throw functionStub.exception; + } + + callCallback(functionStub, arguments); + + return functionStub.returnValue; + }; + + functionStub.id = "stub#" + uuid++; + var orig = functionStub; + functionStub = sinon.spy.create(functionStub); + functionStub.func = orig; + + sinon.extend(functionStub, stub); + functionStub._create = sinon.stub.create; + functionStub.displayName = "stub"; + functionStub.toString = sinon.functionToString; + + return functionStub; + }, + + returns: function returns(value) { + this.returnValue = value; + + return this; + }, + + "throws": throwsException, + throwsException: throwsException, + + callsArg: function callsArg(pos) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = []; + + return this; + }, + + callsArgWith: function callsArgWith(pos) { + if (typeof pos != "number") { + throw new TypeError("argument index is not number"); + } + + this.callArgAt = pos; + this.callbackArguments = slice.call(arguments, 1); + + return this; + }, + + yields: function () { + this.callArgAt = -1; + this.callbackArguments = slice.call(arguments, 0); + + return this; + }, + + yieldsTo: function (prop) { + this.callArgAt = -1; + this.callArgProp = prop; + this.callbackArguments = slice.call(arguments, 1); + + return this; + } + }; + }())); + + if (commonJSModule) { + module.exports = stub; + } else { + sinon.stub = stub; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend stub.js + */ +/*jslint eqeqeq: false, onevar: false, nomen: false*/ +/*global module, require, sinon*/ +/** + * Mock functions. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + var push = [].push; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function mock(object) { + if (!object) { + return sinon.expectation.create("Anonymous mock"); + } + + return mock.create(object); + } + + sinon.mock = mock; + + sinon.extend(mock, (function () { + function each(collection, callback) { + if (!collection) { + return; + } + + for (var i = 0, l = collection.length; i < l; i += 1) { + callback(collection[i]); + } + } + + return { + create: function create(object) { + if (!object) { + throw new TypeError("object is null"); + } + + var mockObject = sinon.extend({}, mock); + mockObject.object = object; + delete mockObject.create; + + return mockObject; + }, + + expects: function expects(method) { + if (!method) { + throw new TypeError("method is falsy"); + } + + if (!this.expectations) { + this.expectations = {}; + this.proxies = []; + } + + if (!this.expectations[method]) { + this.expectations[method] = []; + var mockObject = this; + + sinon.wrapMethod(this.object, method, function () { + return mockObject.invokeMethod(method, this, arguments); + }); + + push.call(this.proxies, method); + } + + var expectation = sinon.expectation.create(method); + push.call(this.expectations[method], expectation); + + return expectation; + }, + + restore: function restore() { + var object = this.object; + + each(this.proxies, function (proxy) { + if (typeof object[proxy].restore == "function") { + object[proxy].restore(); + } + }); + }, + + verify: function verify() { + var expectations = this.expectations || {}; + var messages = [], met = []; + + each(this.proxies, function (proxy) { + each(expectations[proxy], function (expectation) { + if (!expectation.met()) { + push.call(messages, expectation.toString()); + } else { + push.call(met, expectation.toString()); + } + }); + }); + + this.restore(); + + if (messages.length > 0) { + sinon.expectation.fail(messages.concat(met).join("\n")); + } + + return true; + }, + + invokeMethod: function invokeMethod(method, thisValue, args) { + var expectations = this.expectations && this.expectations[method]; + var length = expectations && expectations.length || 0; + + for (var i = 0; i < length; i += 1) { + if (!expectations[i].met() && + expectations[i].allowsCall(thisValue, args)) { + return expectations[i].apply(thisValue, args); + } + } + + var messages = []; + + for (i = 0; i < length; i += 1) { + push.call(messages, " " + expectations[i].toString()); + } + + messages.unshift("Unexpected call: " + sinon.spyCall.toString.call({ + proxy: method, + args: args + })); + + sinon.expectation.fail(messages.join("\n")); + } + }; + }())); + + var times = sinon.timesInWords; + + sinon.expectation = (function () { + var slice = Array.prototype.slice; + var _invoke = sinon.spy.invoke; + + function callCountInWords(callCount) { + if (callCount == 0) { + return "never called"; + } else { + return "called " + times(callCount); + } + } + + function expectedCallCountInWords(expectation) { + var min = expectation.minCalls; + var max = expectation.maxCalls; + + if (typeof min == "number" && typeof max == "number") { + var str = times(min); + + if (min != max) { + str = "at least " + str + " and at most " + times(max); + } + + return str; + } + + if (typeof min == "number") { + return "at least " + times(min); + } + + return "at most " + times(max); + } + + function receivedMinCalls(expectation) { + var hasMinLimit = typeof expectation.minCalls == "number"; + return !hasMinLimit || expectation.callCount >= expectation.minCalls; + } + + function receivedMaxCalls(expectation) { + if (typeof expectation.maxCalls != "number") { + return false; + } + + return expectation.callCount == expectation.maxCalls; + } + + return { + minCalls: 1, + maxCalls: 1, + + create: function create(methodName) { + var expectation = sinon.extend(sinon.stub.create(), sinon.expectation); + delete expectation.create; + expectation.method = methodName; + + return expectation; + }, + + invoke: function invoke(func, thisValue, args) { + this.verifyCallAllowed(thisValue, args); + + return _invoke.apply(this, arguments); + }, + + atLeast: function atLeast(num) { + if (typeof num != "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.maxCalls = null; + this.limitsSet = true; + } + + this.minCalls = num; + + return this; + }, + + atMost: function atMost(num) { + if (typeof num != "number") { + throw new TypeError("'" + num + "' is not number"); + } + + if (!this.limitsSet) { + this.minCalls = null; + this.limitsSet = true; + } + + this.maxCalls = num; + + return this; + }, + + never: function never() { + return this.exactly(0); + }, + + once: function once() { + return this.exactly(1); + }, + + twice: function twice() { + return this.exactly(2); + }, + + thrice: function thrice() { + return this.exactly(3); + }, + + exactly: function exactly(num) { + if (typeof num != "number") { + throw new TypeError("'" + num + "' is not a number"); + } + + this.atLeast(num); + return this.atMost(num); + }, + + met: function met() { + return !this.failed && receivedMinCalls(this); + }, + + verifyCallAllowed: function verifyCallAllowed(thisValue, args) { + if (receivedMaxCalls(this)) { + this.failed = true; + sinon.expectation.fail(this.method + " already called " + times(this.maxCalls)); + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + sinon.expectation.fail(this.method + " called with " + thisValue + " as thisValue, expected " + + this.expectedThis); + } + + if (!("expectedArguments" in this)) { + return; + } + + if (!args || args.length === 0) { + sinon.expectation.fail(this.method + " received no arguments, expected " + + this.expectedArguments.join()); + } + + if (args.length < this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too few arguments (" + args.join() + + "), expected " + this.expectedArguments.join()); + } + + if (this.expectsExactArgCount && + args.length != this.expectedArguments.length) { + sinon.expectation.fail(this.method + " received too many arguments (" + args.join() + + "), expected " + this.expectedArguments.join()); + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + sinon.expectation.fail(this.method + " received wrong arguments (" + args.join() + + "), expected " + this.expectedArguments.join()); + } + } + }, + + allowsCall: function allowsCall(thisValue, args) { + if (this.met()) { + return false; + } + + if ("expectedThis" in this && this.expectedThis !== thisValue) { + return false; + } + + if (!("expectedArguments" in this)) { + return true; + } + + args = args || []; + + if (args.length < this.expectedArguments.length) { + return false; + } + + if (this.expectsExactArgCount && + args.length != this.expectedArguments.length) { + return false; + } + + for (var i = 0, l = this.expectedArguments.length; i < l; i += 1) { + if (!sinon.deepEqual(this.expectedArguments[i], args[i])) { + return false; + } + } + + return true; + }, + + withArgs: function withArgs() { + this.expectedArguments = slice.call(arguments); + return this; + }, + + withExactArgs: function withExactArgs() { + this.withArgs.apply(this, arguments); + this.expectsExactArgCount = true; + return this; + }, + + on: function on(thisValue) { + this.expectedThis = thisValue; + return this; + }, + + toString: function () { + var args = (this.expectedArguments || []).slice(); + + if (!this.expectsExactArgCount) { + push.call(args, "[...]"); + } + + var callStr = sinon.spyCall.toString.call({ + proxy: this.method, args: args + }); + + var message = callStr.replace(", [...", "[, ...") + " " + + expectedCallCountInWords(this); + + if (this.met()) { + return "Expectation met: " + message; + } + + return "Expected " + message + " (" + + callCountInWords(this.callCount) + ")"; + }, + + verify: function verify() { + if (!this.met()) { + sinon.expectation.fail(this.toString()); + } + + return true; + }, + + fail: function (message) { + var exception = new Error(message); + exception.name = "ExpectationError"; + + throw exception; + } + }; + }()); + + if (commonJSModule) { + module.exports = mock; + } else { + sinon.mock = mock; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend stub.js + * @depend mock.js + */ +/*jslint eqeqeq: false, onevar: false, forin: true*/ +/*global module, require, sinon*/ +/** + * Collections of stubs, spies and mocks. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + var push = [].push; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function getFakes(fakeCollection) { + if (!fakeCollection.fakes) { + fakeCollection.fakes = []; + } + + return fakeCollection.fakes; + } + + function each(fakeCollection, method) { + var fakes = getFakes(fakeCollection); + + for (var i = 0, l = fakes.length; i < l; i += 1) { + if (typeof fakes[i][method] == "function") { + fakes[i][method](); + } + } + } + + function compact(fakeCollection) { + var fakes = getFakes(fakeCollection); + var i = 0; + while (i < fakes.length) { + fakes.splice(i, 1); + } + } + + var collection = { + verify: function resolve() { + each(this, "verify"); + }, + + restore: function restore() { + each(this, "restore"); + compact(this); + }, + + verifyAndRestore: function verifyAndRestore() { + var exception; + + try { + this.verify(); + } catch (e) { + exception = e; + } + + this.restore(); + + if (exception) { + throw exception; + } + }, + + add: function add(fake) { + push.call(getFakes(this), fake); + return fake; + }, + + spy: function spy() { + return this.add(sinon.spy.apply(sinon, arguments)); + }, + + stub: function stub(object, property, value) { + if (property) { + var original = object[property]; + + if (typeof original != "function") { + if (!object.hasOwnProperty(property)) { + throw new TypeError("Cannot stub non-existent own property " + property); + } + + object[property] = value; + + return this.add({ + restore: function () { + object[property] = original; + } + }); + } + } + + return this.add(sinon.stub.apply(sinon, arguments)); + }, + + mock: function mock() { + return this.add(sinon.mock.apply(sinon, arguments)); + }, + + inject: function inject(obj) { + var col = this; + + obj.spy = function () { + return col.spy.apply(col, arguments); + }; + + obj.stub = function () { + return col.stub.apply(col, arguments); + }; + + obj.mock = function () { + return col.mock.apply(col, arguments); + }; + + return obj; + } + }; + + if (commonJSModule) { + module.exports = collection; + } else { + sinon.collection = collection; + } +}(typeof sinon == "object" && sinon || null)); + +/*jslint eqeqeq: false, plusplus: false, evil: true, onevar: false, browser: true, forin: false*/ +/*global module, require, window*/ +/** + * Fake timer API + * setTimeout + * setInterval + * clearTimeout + * clearInterval + * tick + * reset + * Date + * + * Inspired by jsUnitMockTimeOut from JsUnit + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +if (typeof sinon == "undefined") { + var sinon = {}; +} + +sinon.clock = (function () { + var id = 0; + + function addTimer(args, recurring) { + if (args.length === 0) { + throw new Error("Function requires at least 1 parameter"); + } + + var toId = id++; + var delay = args[1] || 0; + + if (!this.timeouts) { + this.timeouts = {}; + } + + this.timeouts[toId] = { + id: toId, + func: args[0], + callAt: this.now + delay + }; + + if (recurring === true) { + this.timeouts[toId].interval = delay; + } + + return toId; + } + + function parseTime(str) { + if (!str) { + return 0; + } + + var strings = str.split(":"); + var l = strings.length, i = l; + var ms = 0, parsed; + + if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error("tick only understands numbers and 'h:m:s'"); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error("Invalid time " + str); + } + + ms += parsed * Math.pow(60, (l - i - 1)); + } + + return ms * 1000; + } + + function createObject(object) { + var newObject; + + if (Object.create) { + newObject = Object.create(object); + } else { + var F = function () {}; + F.prototype = object; + newObject = new F(); + } + + newObject.Date.clock = newObject; + return newObject; + } + + return { + now: 0, + + create: function create(now) { + var clock = createObject(this); + + if (typeof now == "number") { + this.now = now; + } + + return clock; + }, + + setTimeout: function setTimeout(callback, timeout) { + return addTimer.call(this, arguments, false); + }, + + clearTimeout: function clearTimeout(timerId) { + if (!this.timeouts) { + this.timeouts = []; + } + + delete this.timeouts[timerId]; + }, + + setInterval: function setInterval(callback, timeout) { + return addTimer.call(this, arguments, true); + }, + + clearInterval: function clearInterval(timerId) { + this.clearTimeout(timerId); + }, + + tick: function tick(ms) { + ms = typeof ms == "number" ? ms : parseTime(ms); + var tickFrom = this.now, tickTo = this.now + ms, previous = this.now; + var timer = this.firstTimerInRange(tickFrom, tickTo); + + while (timer && tickFrom <= tickTo) { + if (this.timeouts[timer.id]) { + tickFrom = this.now = timer.callAt; + this.callTimer(timer); + } + + timer = this.firstTimerInRange(previous, tickTo); + previous = tickFrom; + } + + this.now = tickTo; + }, + + firstTimerInRange: function (from, to) { + var timer, smallest, originalTimer; + + for (var id in this.timeouts) { + if (this.timeouts.hasOwnProperty(id)) { + if (this.timeouts[id].callAt < from || this.timeouts[id].callAt > to) { + continue; + } + + if (!smallest || this.timeouts[id].callAt < smallest) { + originalTimer = this.timeouts[id]; + smallest = this.timeouts[id].callAt; + + timer = { + func: this.timeouts[id].func, + callAt: this.timeouts[id].callAt, + interval: this.timeouts[id].interval, + id: this.timeouts[id].id + }; + } + } + } + + return timer || null; + }, + + callTimer: function (timer) { + try { + if (typeof timer.func == "function") { + timer.func.call(null); + } else { + eval(timer.func); + } + } catch (e) {} + + if (!this.timeouts[timer.id]) { + return; + } + + if (typeof timer.interval == "number") { + this.timeouts[timer.id].callAt += timer.interval; + } else { + delete this.timeouts[timer.id]; + } + }, + + reset: function reset() { + this.timeouts = {}; + }, + + Date: (function () { + var NativeDate = Date; + + function ClockDate(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + switch (arguments.length) { + case 0: + return new NativeDate(ClockDate.clock.now); + case 1: + return new NativeDate(year); + case 2: + return new NativeDate(year, month); + case 3: + return new NativeDate(year, month, date); + case 4: + return new NativeDate(year, month, date, hour); + case 5: + return new NativeDate(year, month, date, hour, minute); + case 6: + return new NativeDate(year, month, date, hour, minute, second); + default: + return new NativeDate(year, month, date, hour, minute, second, ms); + } + } + + if (NativeDate.now) { + ClockDate.now = function now() { + return ClockDate.clock.now; + }; + } + + if (NativeDate.toSource) { + ClockDate.toSource = function toSource() { + return NativeDate.toSource(); + }; + } + + ClockDate.toString = function toString() { + return NativeDate.toString(); + }; + + ClockDate.prototype = NativeDate.prototype; + ClockDate.parse = NativeDate.parse; + ClockDate.UTC = NativeDate.UTC; + + return ClockDate; + }()) + }; +}()); + +sinon.timers = { + setTimeout: setTimeout, + clearTimeout: clearTimeout, + setInterval: setInterval, + clearInterval: clearInterval, + Date: Date +}; + +sinon.useFakeTimers = (function (global) { + var methods = ["Date", "setTimeout", "setInterval", "clearTimeout", "clearInterval"]; + + function restore() { + var method; + + for (var i = 0, l = this.methods.length; i < l; i++) { + method = this.methods[i]; + global[method] = this["_" + method]; + } + } + + function stubGlobal(method, clock) { + clock["_" + method] = global[method]; + + global[method] = function () { + return clock[method].apply(clock, arguments); + }; + + for (var prop in clock[method]) { + if (clock[method].hasOwnProperty(prop)) { + global[method][prop] = clock[method][prop]; + } + } + + global[method].clock = clock; + } + + return function useFakeTimers(now) { + var clock = sinon.clock.create(now); + clock.restore = restore; + clock.methods = Array.prototype.slice.call(arguments, + typeof now == "number" ? 1 : 0); + + if (clock.methods.length === 0) { + clock.methods = methods; + } + + for (var i = 0, l = clock.methods.length; i < l; i++) { + stubGlobal(clock.methods[i], clock); + } + + return clock; + }; +}(typeof global != "undefined" ? global : this)); + +if (typeof module == "object" && typeof require == "function") { + module.exports = sinon; +} + +/*jslint eqeqeq: false, onevar: false*/ +/*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/ +/** + * Minimal Event interface implementation + * + * Original implementation by Sven Fuchs: https://gist.github.com/995028 + * Modifications and tests by Christian Johansen. + * + * @author Sven Fuchs (svenfuchs@artweb-design.de) + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2011 Sven Fuchs, Christian Johansen + */ + +if (typeof sinon == "undefined") { + this.sinon = {}; +} + +(function () { + var push = [].push; + + sinon.Event = function Event(type, bubbles, cancelable) { + this.initEvent(type, bubbles, cancelable); + }; + + sinon.Event.prototype = { + initEvent: function(type, bubbles, cancelable) { + this.type = type; + this.bubbles = bubbles; + this.cancelable = cancelable; + }, + + stopPropagation: function () {}, + + preventDefault: function () { + this.defaultPrevented = true; + } + }; + + sinon.EventTarget = { + addEventListener: function addEventListener(event, listener, useCapture) { + this.eventListeners = this.eventListeners || {}; + this.eventListeners[event] = this.eventListeners[event] || []; + push.call(this.eventListeners[event], listener); + }, + + removeEventListener: function removeEventListener(event, listener, useCapture) { + var listeners = this.eventListeners && this.eventListeners[event] || []; + + for (var i = 0, l = listeners.length; i < l; ++i) { + if (listeners[i] == listener) { + return listeners.splice(i, 1); + } + } + }, + + dispatchEvent: function dispatchEvent(event) { + var type = event.type; + var listeners = this.eventListeners && this.eventListeners[type] || []; + + for (var i = 0; i < listeners.length; i++) { + if (typeof listeners[i] == "function") { + listeners[i].call(this, event); + } else { + listeners[i].handleEvent(event); + } + } + + return !!event.defaultPrevented; + } + }; +}()); + +/** + * @depend event.js + */ +/*jslint eqeqeq: false, onevar: false*/ +/*global sinon, module, require, ActiveXObject, XMLHttpRequest, DOMParser*/ +/** + * Fake XMLHttpRequest object + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +if (typeof sinon == "undefined") { + this.sinon = {}; +} + +sinon.xhr = { XMLHttpRequest: this.XMLHttpRequest }; + +sinon.FakeXMLHttpRequest = (function () { + /*jsl:ignore*/ + var unsafeHeaders = { + "Accept-Charset": true, + "Accept-Encoding": true, + "Connection": true, + "Content-Length": true, + "Cookie": true, + "Cookie2": true, + "Content-Transfer-Encoding": true, + "Date": true, + "Expect": true, + "Host": true, + "Keep-Alive": true, + "Referer": true, + "TE": true, + "Trailer": true, + "Transfer-Encoding": true, + "Upgrade": true, + "User-Agent": true, + "Via": true + }; + /*jsl:end*/ + + function FakeXMLHttpRequest() { + this.readyState = FakeXMLHttpRequest.UNSENT; + this.requestHeaders = {}; + this.requestBody = null; + this.status = 0; + this.statusText = ""; + + if (typeof FakeXMLHttpRequest.onCreate == "function") { + FakeXMLHttpRequest.onCreate(this); + } + } + + function verifyState(xhr) { + if (xhr.readyState !== FakeXMLHttpRequest.OPENED) { + throw new Error("INVALID_STATE_ERR"); + } + + if (xhr.sendFlag) { + throw new Error("INVALID_STATE_ERR"); + } + } + + sinon.extend(FakeXMLHttpRequest.prototype, sinon.EventTarget, { + async: true, + + open: function open(method, url, async, username, password) { + this.method = method; + this.url = url; + this.async = typeof async == "boolean" ? async : true; + this.username = username; + this.password = password; + this.responseText = null; + this.responseXML = null; + this.requestHeaders = {}; + this.sendFlag = false; + this.readyStateChange(FakeXMLHttpRequest.OPENED); + }, + + readyStateChange: function readyStateChange(state) { + this.readyState = state; + + if (typeof this.onreadystatechange == "function") { + this.onreadystatechange(); + } + + this.dispatchEvent(new sinon.Event("readystatechange")); + }, + + setRequestHeader: function setRequestHeader(header, value) { + verifyState(this); + + if (unsafeHeaders[header] || /^(Sec-|Proxy-)/.test(header)) { + throw new Error("Refused to set unsafe header \"" + header + "\""); + } + + if (this.requestHeaders[header]) { + this.requestHeaders[header] += "," + value; + } else { + this.requestHeaders[header] = value; + } + }, + + // Helps testing + setResponseHeaders: function setResponseHeaders(headers) { + this.responseHeaders = {}; + + for (var header in headers) { + if (headers.hasOwnProperty(header)) { + this.responseHeaders[header] = headers[header]; + } + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.HEADERS_RECEIVED); + } + }, + + // Currently treats ALL data as a DOMString (i.e. no Document) + send: function send(data) { + verifyState(this); + + if (!/^(get|head)$/i.test(this.method)) { + if (this.requestHeaders["Content-Type"]) { + var value = this.requestHeaders["Content-Type"].split(";"); + this.requestHeaders["Content-Type"] = value[0] + ";charset=utf-8"; + } else { + this.requestHeaders["Content-Type"] = "text/plain;charset=utf-8"; + } + + this.requestBody = data; + } + + this.errorFlag = false; + this.sendFlag = this.async; + this.readyStateChange(FakeXMLHttpRequest.OPENED); + + if (typeof this.onSend == "function") { + this.onSend(this); + } + }, + + abort: function abort() { + this.aborted = true; + this.responseText = null; + this.errorFlag = true; + this.requestHeaders = {}; + + if (this.readyState > sinon.FakeXMLHttpRequest.UNSENT && this.sendFlag) { + this.readyStateChange(sinon.FakeXMLHttpRequest.DONE); + this.sendFlag = false; + } + + this.readyState = sinon.FakeXMLHttpRequest.UNSENT; + }, + + getResponseHeader: function getResponseHeader(header) { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return null; + } + + if (/^Set-Cookie2?$/i.test(header)) { + return null; + } + + header = header.toLowerCase(); + + for (var h in this.responseHeaders) { + if (h.toLowerCase() == header) { + return this.responseHeaders[h]; + } + } + + return null; + }, + + getAllResponseHeaders: function getAllResponseHeaders() { + if (this.readyState < FakeXMLHttpRequest.HEADERS_RECEIVED) { + return ""; + } + + var headers = ""; + + for (var header in this.responseHeaders) { + if (this.responseHeaders.hasOwnProperty(header) && + !/^Set-Cookie2?$/i.test(header)) { + headers += header + ": " + this.responseHeaders[header] + "\r\n"; + } + } + + return headers; + }, + + setResponseBody: function setResponseBody(body) { + if (this.readyState == FakeXMLHttpRequest.DONE) { + throw new Error("Request done"); + } + + if (this.async && this.readyState != FakeXMLHttpRequest.HEADERS_RECEIVED) { + throw new Error("No headers received"); + } + + var chunkSize = this.chunkSize || 10; + var index = 0; + this.responseText = ""; + + do { + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.LOADING); + } + + this.responseText += body.substring(index, index + chunkSize); + index += chunkSize; + } while (index < body.length); + + var type = this.getResponseHeader("Content-Type"); + + if (this.responseText && + (!type || /(text\/xml)|(application\/xml)|(\+xml)/.test(type))) { + try { + this.responseXML = FakeXMLHttpRequest.parseXML(this.responseText); + } catch (e) {} + } + + if (this.async) { + this.readyStateChange(FakeXMLHttpRequest.DONE); + } else { + this.readyState = FakeXMLHttpRequest.DONE; + } + }, + + respond: function respond(status, headers, body) { + this.setResponseHeaders(headers || {}); + this.status = typeof status == "number" ? status : 200; + this.statusText = FakeXMLHttpRequest.statusCodes[this.status]; + this.setResponseBody(body || ""); + } + }); + + sinon.extend(FakeXMLHttpRequest, { + UNSENT: 0, + OPENED: 1, + HEADERS_RECEIVED: 2, + LOADING: 3, + DONE: 4 + }); + + // Borrowed from JSpec + FakeXMLHttpRequest.parseXML = function parseXML(text) { + var xmlDoc; + + if (typeof DOMParser != "undefined") { + var parser = new DOMParser(); + xmlDoc = parser.parseFromString(text, "text/xml"); + } else { + xmlDoc = new ActiveXObject("Microsoft.XMLDOM"); + xmlDoc.async = "false"; + xmlDoc.loadXML(text); + } + + return xmlDoc; + }; + + FakeXMLHttpRequest.statusCodes = { + 100: "Continue", + 101: "Switching Protocols", + 200: "OK", + 201: "Created", + 202: "Accepted", + 203: "Non-Authoritative Information", + 204: "No Content", + 205: "Reset Content", + 206: "Partial Content", + 300: "Multiple Choice", + 301: "Moved Permanently", + 302: "Found", + 303: "See Other", + 304: "Not Modified", + 305: "Use Proxy", + 307: "Temporary Redirect", + 400: "Bad Request", + 401: "Unauthorized", + 402: "Payment Required", + 403: "Forbidden", + 404: "Not Found", + 405: "Method Not Allowed", + 406: "Not Acceptable", + 407: "Proxy Authentication Required", + 408: "Request Timeout", + 409: "Conflict", + 410: "Gone", + 411: "Length Required", + 412: "Precondition Failed", + 413: "Request Entity Too Large", + 414: "Request-URI Too Long", + 415: "Unsupported Media Type", + 416: "Requested Range Not Satisfiable", + 417: "Expectation Failed", + 422: "Unprocessable Entity", + 500: "Internal Server Error", + 501: "Not Implemented", + 502: "Bad Gateway", + 503: "Service Unavailable", + 504: "Gateway Timeout", + 505: "HTTP Version Not Supported" + }; + + return FakeXMLHttpRequest; +}()); + +(function (global) { + var GlobalXMLHttpRequest = global.XMLHttpRequest; + var GlobalActiveXObject = global.ActiveXObject; + var supportsActiveX = typeof ActiveXObject != "undefined"; + var supportsXHR = typeof XMLHttpRequest != "undefined"; + + sinon.useFakeXMLHttpRequest = function () { + sinon.FakeXMLHttpRequest.restore = function restore(keepOnCreate) { + if (supportsXHR) { + global.XMLHttpRequest = GlobalXMLHttpRequest; + } + + if (supportsActiveX) { + global.ActiveXObject = GlobalActiveXObject; + } + + delete sinon.FakeXMLHttpRequest.restore; + + if (keepOnCreate !== true) { + delete sinon.FakeXMLHttpRequest.onCreate; + } + }; + + if (supportsXHR) { + global.XMLHttpRequest = sinon.FakeXMLHttpRequest; + } + + if (supportsActiveX) { + global.ActiveXObject = function ActiveXObject(objId) { + if (objId == "Microsoft.XMLHTTP" || /^Msxml2\.XMLHTTP/i.test(objId)) { + return new sinon.FakeXMLHttpRequest(); + } + + return new GlobalActiveXObject(objId); + }; + } + + return sinon.FakeXMLHttpRequest; + }; +}(this)); + +if (typeof module == "object" && typeof require == "function") { + module.exports = sinon; +} + +/** + * @depend fake_xml_http_request.js + */ +/*jslint eqeqeq: false, onevar: false, regexp: false, plusplus: false*/ +/*global module, require, window*/ +/** + * The Sinon "server" mimics a web server that receives requests from + * sinon.FakeXMLHttpRequest and provides an API to respond to those requests, + * both synchronously and asynchronously. To respond synchronuously, canned + * answers have to be provided upfront. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +if (typeof sinon == "undefined") { + var sinon = {}; +} + +sinon.fakeServer = (function () { + var push = [].push; + function F() {} + + function create(proto) { + F.prototype = proto; + return new F(); + } + + function responseArray(handler) { + var response = handler; + + if (Object.prototype.toString.call(handler) != "[object Array]") { + response = [200, {}, handler]; + } + + if (typeof response[2] != "string") { + throw new TypeError("Fake server response body should be string, but was " + + typeof response[2]); + } + + return response; + } + + var wloc = window.location; + var rCurrLoc = new RegExp("^" + wloc.protocol + "//" + wloc.host); + + function matchOne(response, reqMethod, reqUrl) { + var rmeth = response.method; + var matchMethod = !rmeth || rmeth.toLowerCase() == reqMethod.toLowerCase(); + var url = response.url; + var matchUrl = !url || url == reqUrl || (typeof url.test == "function" && url.test(reqUrl)); + + return matchMethod && matchUrl; + } + + function match(response, request) { + var requestMethod = this.getHTTPMethod(request); + var requestUrl = request.url; + + if (!/^https?:\/\//.test(requestUrl) || rCurrLoc.test(requestUrl)) { + requestUrl = requestUrl.replace(rCurrLoc, ""); + } + + if (matchOne(response, this.getHTTPMethod(request), requestUrl)) { + if (typeof response.response == "function") { + var args = [request].concat(requestUrl.match(response.url).slice(1)); + return response.response.apply(response, args); + } + + return true; + } + + return false; + } + + return { + create: function () { + var server = create(this); + this.xhr = sinon.useFakeXMLHttpRequest(); + server.requests = []; + + this.xhr.onCreate = function (xhrObj) { + server.addRequest(xhrObj); + }; + + return server; + }, + + addRequest: function addRequest(xhrObj) { + var server = this; + push.call(this.requests, xhrObj); + + xhrObj.onSend = function () { + server.handleRequest(this); + }; + + if (this.autoRespond && !this.responding) { + setTimeout(function () { + server.responding = false; + server.respond(); + }, this.autoRespondAfter || 10); + + this.responding = true; + } + }, + + getHTTPMethod: function getHTTPMethod(request) { + if (this.fakeHTTPMethods && /post/i.test(request.method)) { + var matches = (request.requestBody || "").match(/_method=([^\b;]+)/); + return !!matches ? matches[1] : request.method; + } + + return request.method; + }, + + handleRequest: function handleRequest(xhr) { + if (xhr.async) { + if (!this.queue) { + this.queue = []; + } + + push.call(this.queue, xhr); + } else { + this.processRequest(xhr); + } + }, + + respondWith: function respondWith(method, url, body) { + if (arguments.length == 1) { + this.response = responseArray(method); + } else { + if (!this.responses) { + this.responses = []; + } + + if (arguments.length == 2) { + body = url; + url = method; + method = null; + } + + push.call(this.responses, { + method: method, + url: url, + response: typeof body == "function" ? body : responseArray(body) + }); + } + }, + + respond: function respond() { + var queue = this.queue || []; + var request; + + while(request = queue.shift()) { + this.processRequest(request); + } + }, + + processRequest: function processRequest(request) { + try { + if (request.aborted) { + return; + } + + var response = this.response || [404, {}, ""]; + + if (this.responses) { + for (var i = 0, l = this.responses.length; i < l; i++) { + if (match.call(this, this.responses[i], request)) { + response = this.responses[i].response; + break; + } + } + } + + if (request.readyState != 4) { + request.respond(response[0], response[1], response[2]); + } + } catch (e) {} + }, + + restore: function restore() { + return this.xhr.restore && this.xhr.restore.apply(this.xhr, arguments); + } + }; +}()); + +if (typeof module == "object" && typeof require == "function") { + module.exports = sinon; +} + +/** + * @depend fake_server.js + * @depend fake_timers.js + */ +/*jslint browser: true, eqeqeq: false, onevar: false*/ +/*global sinon*/ +/** + * Add-on for sinon.fakeServer that automatically handles a fake timer along with + * the FakeXMLHttpRequest. The direct inspiration for this add-on is jQuery + * 1.3.x, which does not use xhr object's onreadystatehandler at all - instead, + * it polls the object for completion with setInterval. Dispite the direct + * motivation, there is nothing jQuery-specific in this file, so it can be used + * in any environment where the ajax implementation depends on setInterval or + * setTimeout. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +(function () { + function Server() {} + Server.prototype = sinon.fakeServer; + + sinon.fakeServerWithClock = new Server(); + + sinon.fakeServerWithClock.addRequest = function addRequest(xhr) { + if (xhr.async) { + if (typeof setTimeout.clock == "object") { + this.clock = setTimeout.clock; + } else { + this.clock = sinon.useFakeTimers(); + this.resetClock = true; + } + + if (!this.longestTimeout) { + var clockSetTimeout = this.clock.setTimeout; + var clockSetInterval = this.clock.setInterval; + var server = this; + + this.clock.setTimeout = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetTimeout.apply(this, arguments); + }; + + this.clock.setInterval = function (fn, timeout) { + server.longestTimeout = Math.max(timeout, server.longestTimeout || 0); + + return clockSetInterval.apply(this, arguments); + }; + } + } + + return sinon.fakeServer.addRequest.call(this, xhr); + }; + + sinon.fakeServerWithClock.respond = function respond() { + var returnVal = sinon.fakeServer.respond.apply(this, arguments); + + if (this.clock) { + this.clock.tick(this.longestTimeout || 0); + this.longestTimeout = 0; + + if (this.resetClock) { + this.clock.restore(); + this.resetClock = false; + } + } + + return returnVal; + }; + + sinon.fakeServerWithClock.restore = function restore() { + if (this.clock) { + this.clock.restore(); + } + + return sinon.fakeServer.restore.apply(this, arguments); + }; +}()); + +/** + * @depend ../sinon.js + * @depend collection.js + * @depend util/fake_timers.js + * @depend util/fake_server_with_clock.js + */ +/*jslint eqeqeq: false, onevar: false, plusplus: false*/ +/*global require, module*/ +/** + * Manages fake collections as well as fake utilities such as Sinon's + * timers and fake XHR implementation in one convenient object. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +if (typeof module == "object" && typeof require == "function") { + var sinon = require("../sinon"); + sinon.extend(sinon, require("./util/fake_timers")); +} + +(function () { + var push = [].push; + + function exposeValue(sandbox, config, key, value) { + if (!value) { + return; + } + + if (config.injectInto) { + config.injectInto[key] = value; + } else { + push.call(sandbox.args, value); + } + } + + function prepareSandboxFromConfig(config) { + var sandbox = sinon.create(sinon.sandbox); + + if (config.useFakeServer) { + if (typeof config.useFakeServer == "object") { + sandbox.serverPrototype = config.useFakeServer; + } + + sandbox.useFakeServer(); + } + + if (config.useFakeTimers) { + if (typeof config.useFakeTimers == "object") { + sandbox.useFakeTimers.apply(sandbox, config.useFakeTimers); + } else { + sandbox.useFakeTimers(); + } + } + + return sandbox; + } + + sinon.sandbox = sinon.extend(sinon.create(sinon.collection), { + useFakeTimers: function useFakeTimers() { + this.clock = sinon.useFakeTimers.apply(sinon, arguments); + + return this.add(this.clock); + }, + + serverPrototype: sinon.fakeServer, + + useFakeServer: function useFakeServer() { + var proto = this.serverPrototype || sinon.fakeServer; + + if (!proto || !proto.create) { + return null; + } + + this.server = proto.create(); + return this.add(this.server); + }, + + inject: function (obj) { + sinon.collection.inject.call(this, obj); + + if (this.clock) { + obj.clock = this.clock; + } + + if (this.server) { + obj.server = this.server; + obj.requests = this.server.requests; + } + + return obj; + }, + + create: function (config) { + if (!config) { + return sinon.create(sinon.sandbox); + } + + var sandbox = prepareSandboxFromConfig(config); + sandbox.args = sandbox.args || []; + var prop, value, exposed = sandbox.inject({}); + + if (config.properties) { + for (var i = 0, l = config.properties.length; i < l; i++) { + prop = config.properties[i]; + value = exposed[prop] || prop == "sandbox" && sandbox; + exposeValue(sandbox, config, prop, value); + } + } else { + exposeValue(sandbox, config, "sandbox", value); + } + + return sandbox; + } + }); + + sinon.sandbox.useFakeXMLHttpRequest = sinon.sandbox.useFakeServer; + + if (typeof module != "undefined") { + module.exports = sinon.sandbox; + } +}()); + +/** + * @depend ../sinon.js + * @depend stub.js + * @depend mock.js + * @depend sandbox.js + */ +/*jslint eqeqeq: false, onevar: false, forin: true, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Test function, sandboxes fakes + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function test(callback) { + var type = typeof callback; + + if (type != "function") { + throw new TypeError("sinon.test needs to wrap a test function, got " + type); + } + + return function () { + var config = sinon.getConfig(sinon.config); + config.injectInto = config.injectIntoThis && this || config.injectInto; + var sandbox = sinon.sandbox.create(config); + var exception, result; + var args = Array.prototype.slice.call(arguments).concat(sandbox.args); + + try { + result = callback.apply(this, args); + } catch (e) { + exception = e; + } + + sandbox.verifyAndRestore(); + + if (exception) { + throw exception; + } + + return result; + }; + } + + test.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "server", "requests"], + useFakeTimers: true, + useFakeServer: true + }; + + if (commonJSModule) { + module.exports = test; + } else { + sinon.test = test; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend test.js + */ +/*jslint eqeqeq: false, onevar: false, eqeqeq: false*/ +/*global module, require, sinon*/ +/** + * Test case, sandboxes all test functions + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon || !Object.prototype.hasOwnProperty) { + return; + } + + function createTest(property, setUp, tearDown) { + return function () { + if (setUp) { + setUp.apply(this, arguments); + } + + var exception, result; + + try { + result = property.apply(this, arguments); + } catch (e) { + exception = e; + } + + if (tearDown) { + tearDown.apply(this, arguments); + } + + if (exception) { + throw exception; + } + + return result; + }; + } + + function testCase(tests, prefix) { + /*jsl:ignore*/ + if (!tests || typeof tests != "object") { + throw new TypeError("sinon.testCase needs an object with test functions"); + } + /*jsl:end*/ + + prefix = prefix || "test"; + var rPrefix = new RegExp("^" + prefix); + var methods = {}, testName, property, method; + var setUp = tests.setUp; + var tearDown = tests.tearDown; + + for (testName in tests) { + if (tests.hasOwnProperty(testName)) { + property = tests[testName]; + + if (/^(setUp|tearDown)$/.test(testName)) { + continue; + } + + if (typeof property == "function" && rPrefix.test(testName)) { + method = property; + + if (setUp || tearDown) { + method = createTest(property, setUp, tearDown); + } + + methods[testName] = sinon.test(method); + } else { + methods[testName] = tests[testName]; + } + } + } + + return methods; + } + + if (commonJSModule) { + module.exports = testCase; + } else { + sinon.testCase = testCase; + } +}(typeof sinon == "object" && sinon || null)); + +/** + * @depend ../sinon.js + * @depend stub.js + */ +/*jslint eqeqeq: false, onevar: false, nomen: false, plusplus: false*/ +/*global module, require, sinon*/ +/** + * Assertions matching the test spy retrieval interface. + * + * @author Christian Johansen (christian@cjohansen.no) + * @license BSD + * + * Copyright (c) 2010-2011 Christian Johansen + */ + +(function (sinon) { + var commonJSModule = typeof module == "object" && typeof require == "function"; + var slice = Array.prototype.slice; + var assert; + + if (!sinon && commonJSModule) { + sinon = require("../sinon"); + } + + if (!sinon) { + return; + } + + function verifyIsStub() { + var method; + + for (var i = 0, l = arguments.length; i < l; ++i) { + method = arguments[i]; + + if (!method) { + assert.fail("fake is not a spy"); + } + + if (typeof method != "function") { + assert.fail(method + " is not a function"); + } + + if (typeof method.getCall != "function") { + assert.fail(method + " is not stubbed"); + } + } + } + + function failAssertion(object, msg) { + var failMethod = object.fail || assert.fail; + failMethod.call(object, msg); + } + + function mirrorPropAsAssertion(name, method, message) { + if (arguments.length == 2) { + message = method; + method = name; + } + + assert[name] = function (fake) { + verifyIsStub(fake); + + var args = slice.call(arguments, 1); + var failed = false; + + if (typeof method == "function") { + failed = !method(fake); + } else { + failed = typeof fake[method] == "function" ? + !fake[method].apply(fake, args) : !fake[method]; + } + + if (failed) { + failAssertion(this, fake.printf.apply(fake, [message].concat(args))); + } else { + assert.pass(name); + } + }; + } + + function exposedName(prefix, prop) { + return !prefix || /^fail/.test(prop) ? prop : + prefix + prop.slice(0, 1).toUpperCase() + prop.slice(1); + }; + + assert = { + failException: "AssertError", + + fail: function fail(message) { + var error = new Error(message); + error.name = this.failException || assert.failException; + + throw error; + }, + + pass: function pass(assertion) {}, + + callOrder: function assertCallOrder() { + verifyIsStub.apply(null, arguments); + var expected = "", actual = ""; + + if (!sinon.calledInOrder(arguments)) { + try { + expected = [].join.call(arguments, ", "); + actual = sinon.orderByFirstCall(slice.call(arguments)).join(", "); + } catch (e) {} + + failAssertion(this, "expected " + expected + " to be " + + "called in order but were called as " + actual); + } else { + assert.pass("callOrder"); + } + }, + + callCount: function assertCallCount(method, count) { + verifyIsStub(method); + + if (method.callCount != count) { + var msg = "expected %n to be called " + sinon.timesInWords(count) + + " but was called %c%C"; + failAssertion(this, method.printf(msg)); + } else { + assert.pass("callCount"); + } + }, + + expose: function expose(target, options) { + if (!target) { + throw new TypeError("target is null or undefined"); + } + + var o = options || {}; + var prefix = typeof o.prefix == "undefined" && "assert" || o.prefix; + var includeFail = typeof o.includeFail == "undefined" || !!o.includeFail; + + for (var method in this) { + if (method != "export" && (includeFail || !/^(fail)/.test(method))) { + target[exposedName(prefix, method)] = this[method]; + } + } + + return target; + } + }; + + mirrorPropAsAssertion("called", "expected %n to have been called at least once but was never called"); + mirrorPropAsAssertion("notCalled", function (spy) { return !spy.called; }, + "expected %n to not have been called but was called %c%C"); + mirrorPropAsAssertion("calledOnce", "expected %n to be called once but was called %c%C"); + mirrorPropAsAssertion("calledTwice", "expected %n to be called twice but was called %c%C"); + mirrorPropAsAssertion("calledThrice", "expected %n to be called thrice but was called %c%C"); + mirrorPropAsAssertion("calledOn", "expected %n to be called with %1 as this but was called with %t"); + mirrorPropAsAssertion("alwaysCalledOn", "expected %n to always be called with %1 as this but was called with %t"); + mirrorPropAsAssertion("calledWith", "expected %n to be called with arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWith", "expected %n to always be called with arguments %*%C"); + mirrorPropAsAssertion("calledWithExactly", "expected %n to be called with exact arguments %*%C"); + mirrorPropAsAssertion("alwaysCalledWithExactly", "expected %n to always be called with exact arguments %*%C"); + mirrorPropAsAssertion("neverCalledWith", "expected %n to never be called with arguments %*%C"); + mirrorPropAsAssertion("threw", "%n did not throw exception%C"); + mirrorPropAsAssertion("alwaysThrew", "%n did not always throw exception%C"); + + if (commonJSModule) { + module.exports = assert; + } else { + sinon.assert = assert; + } +}(typeof sinon == "object" && sinon || null)); + diff --git a/src/bower_components/stacktrace.js/test/lib/sinon-qunit-1.0.0.js b/src/bower_components/stacktrace.js/test/lib/sinon-qunit-1.0.0.js new file mode 100644 index 0000000000000..cebc0f14ddca9 --- /dev/null +++ b/src/bower_components/stacktrace.js/test/lib/sinon-qunit-1.0.0.js @@ -0,0 +1,62 @@ +/** + * sinon-qunit 1.0.0, 2010/12/08 + * + * @author Christian Johansen (christian@cjohansen.no) + * + * (The BSD License) + * + * Copyright (c) 2010-2011, Christian Johansen, christian@cjohansen.no + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, + * are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * * Neither the name of Christian Johansen nor the names of his contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, + * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +/*global sinon, QUnit, test*/ +sinon.assert.fail = function (msg) { + QUnit.ok(false, msg); +}; + +sinon.assert.pass = function (assertion) { + QUnit.ok(true, assertion); +}; + +sinon.config = { + injectIntoThis: true, + injectInto: null, + properties: ["spy", "stub", "mock", "clock", "sandbox"], + useFakeTimers: true, + useFakeServer: false +}; + +(function (global) { + var qTest = QUnit.test; + + QUnit.test = global.test = function (testName, expected, callback, async) { + if (arguments.length === 2) { + callback = expected; + expected = null; + } + + return qTest(testName, expected, sinon.test(callback), async); + }; +}(this)); diff --git a/src/kibana/controllers/kibana.js b/src/kibana/controllers/kibana.js index 0b9e906f08489..cc20a98050216 100644 --- a/src/kibana/controllers/kibana.js +++ b/src/kibana/controllers/kibana.js @@ -18,7 +18,10 @@ define(function (require) { appendToBody: false }); }) - .controller('kibana', function ($scope, courier, config, configFile, notify, $timeout) { + .controller('kibana', function ($scope, courier, config, configFile, createNotifier, $timeout) { + var notify = createNotifier({ + location: 'Kibana Controller' + }); $scope.apps = configFile.apps; $scope.$on('$locationChangeSuccess', function (event, uri) { @@ -123,4 +126,4 @@ define(function (require) { courier.start(); }); }); -}); \ No newline at end of file +}); diff --git a/src/kibana/index.js b/src/kibana/index.js index f154fef269dae..b946e1223c70b 100644 --- a/src/kibana/index.js +++ b/src/kibana/index.js @@ -2,7 +2,6 @@ * main app level module */ define(function (require) { - var angular = require('angular'); var $ = require('jquery'); var _ = require('lodash'); @@ -50,7 +49,7 @@ define(function (require) { notify.lifecycle('bootstrap'); angular .bootstrap(document, ['kibana']) - .invoke(function (notify) { + .invoke(function () { notify.lifecycle('bootstrap', true); $(document.body).children().show(); }); @@ -61,4 +60,4 @@ define(function (require) { }); return kibana; -}); \ No newline at end of file +}); diff --git a/src/kibana/notify/manager.js b/src/kibana/notify/manager.js index be0916ff0100d..c385cb9d05a6c 100644 --- a/src/kibana/notify/manager.js +++ b/src/kibana/notify/manager.js @@ -1,6 +1,12 @@ define(function (require) { var _ = require('lodash'); var $ = require('jquery'); + var createStackTrace = require('stacktrace'); + + var notifs = []; + var setTO = setTimeout; + var clearTO = clearTimeout; + var log = (typeof KIBANA_DIST === 'undefined') ? _.bindKey(console, 'log') : _.noop; var fatalToastTemplate = (function lazyTemplate(tmpl) { var compiled; @@ -10,154 +16,185 @@ define(function (require) { }; }(require('text!./partials/fatal.html'))); - /** - * Functionality to check that - */ - function NotifyManager() { + function now() { + if (window.performance && window.performance.now) { + return window.performance.now(); + } + return Date.now(); + } - var applicationBooted; - var notifs = this._notifs = []; - var setTO = setTimeout; - var clearTO = clearTimeout; + function closeNotif(cb, key) { + return function () { + // this === notif + var i = notifs.indexOf(this); + if (i !== -1) notifs.splice(i, 1); + if (this.timerId) this.timerId = clearTO(this.timerId); + if (typeof cb === 'function') cb(key); + }; + } - function now() { - if (window.performance && window.performance.now) { - return window.performance.now(); - } - return Date.now(); + function add(notif, cb) { + if (notif.lifetime !== Infinity) { + notif.timerId = setTO(function () { + closeNotif(cb, 'ignore').call(notif); + }, notif.lifetime); } - var log = (typeof KIBANA_DIST === 'undefined') ? _.bindKey(console, 'log') : _.noop; - - function closeNotif(cb, key) { - return function () { - // this === notif - var i = notifs.indexOf(this); - if (i !== -1) notifs.splice(i, 1); - if (this.timerId) this.timerId = clearTO(this.timerId); - if (typeof cb === 'function') cb(key); - }; + if (notif.actions) { + notif.actions.forEach(function (action) { + notif[action] = closeNotif(cb, action); + }); } - function add(notif, cb) { - if (notif.lifetime !== Infinity) { - notif.timerId = setTO(function () { - closeNotif(cb, 'ignore').call(notif); - }, notif.lifetime); - } + notifs.push(notif); + } - if (notif.actions) { - notif.actions.forEach(function (action) { - notif[action] = closeNotif(cb, action); - }); - } + function formatMsg(msg, from) { + var rtn = ''; + if (from) { + rtn += from + ': '; + } - notifs.push(notif); + if (typeof msg === 'string') { + rtn += msg; + } else if (msg instanceof Error) { + rtn += msg.message; } - this._setTimerFns = function (set, clear) { - setTO = set; - clearTO = clear; - }; + return rtn; + } - /** - * Notify the serivce of app lifecycle events - * @type {[type]} - */ - var lifecycleEvents = window.kibanaLifecycleEvents = {}; - this.lifecycle = function (name, success) { - var status; - if (name === 'bootstrap' && success === true) applicationBooted = true; - - if (success === void 0) { - // start - lifecycleEvents[name] = now(); + function formatStack(err) { + if (!err) return null; + + var isError = (err instanceof Error); + var stack = createStackTrace({ e: isError ? err : void 0 }); + var msg = isError ? err.message : err; + + return msg + '\n' + stack.map(function (line) { return ' ' + line; }).join('\n'); + } + + /** + * Track application lifecycle events + * @type {[type]} + */ + var lifecycleEvents = window.kibanaLifecycleEvents = {}; + + var applicationBooted; + + /** + * Functionality to check that + */ + function NotifyManager(opts) { + opts = opts || {}; + + // label type thing to say where notifications came from + this.from = opts.location; + + // attach the global notification list + this._notifs = notifs; + } + + NotifyManager.prototype.lifecycle = function (name, success) { + var status; + if (name === 'bootstrap' && success === true) applicationBooted = true; + + if (success === void 0) { + // start + lifecycleEvents[name] = now(); + } else { + // end + if (success) { + lifecycleEvents[name] = now() - (lifecycleEvents[name] || 0); + status = lifecycleEvents[name].toFixed(2) + ' ms'; } else { - // end - if (success) { - lifecycleEvents[name] = now() - (lifecycleEvents[name] || 0); - status = lifecycleEvents[name].toFixed(2) + ' ms'; - } else { - lifecycleEvents[name] = false; - status = 'failure'; - } + lifecycleEvents[name] = false; + status = 'failure'; } + } - log('KBN: ' + name + (status ? ' - ' + status : '')); - }; + log('KBN: ' + name + (status ? ' - ' + status : '')); + }; - /** - * Kill the page, and display an error - * @param {Error} err - The fatal error that occured - */ - this.fatal = function (err) { - var html = fatalToastTemplate({ - msg: err instanceof Error ? err.message : err, - stack: err.stack - }); + /** + * Kill the page, and display an error + * @param {Error} err - The fatal error that occured + */ + NotifyManager.prototype.fatal = function (err) { + var html = fatalToastTemplate({ + msg: formatMsg(err, this.from), + stack: formatStack(err) + }); + + var $container = $('#fatal-splash-screen'); + if ($container.size()) { + $container.append(html); + return; + } - var $container = $('#fatal-splash-screen'); - if ($container.size()) { - $container.append(html); - return; - } + $container = $(); - $container = $(); + // in case the app has not completed boot + $(document.body) + .removeAttr('ng-cloak') + .html('
      ' + html + '
      '); - // in case the app has not completed boot - $(document.body) - .removeAttr('ng-cloak') - .html('
      ' + html + '
      '); - }; + console.error(err.stack); + }; - /** - * Alert the user of an error that occured - * @param {Error|String} err - */ - this.error = function (err, cb) { - add({ - type: 'danger', - content: err instanceof Error ? err.message : err, - icon: 'warning', - title: 'Error', - lifetime: Infinity, - actions: ['report', 'accept'] - }, cb); - }; + /** + * Alert the user of an error that occured + * @param {Error|String} err + */ + NotifyManager.prototype.error = function (err, cb) { + add({ + type: 'danger', + content: formatMsg(err, this.from), + icon: 'warning', + title: 'Error', + lifetime: Infinity, + actions: ['report', 'accept'], + stack: formatStack(err) + }, cb); + }; - /** - * Warn the user abort something - * @param {[type]} msg [description] - * @return {[type]} [description] - */ - this.warning = function (msg, cb) { - add({ - type: 'warning', - content: msg, - icon: 'warning', - title: 'Warning', - lifetime: 7000, - actions: ['accept'] - }, cb); - }; + /** + * Warn the user abort something + * @param {[type]} msg [description] + * @return {[type]} [description] + */ + NotifyManager.prototype.warning = function (msg, cb) { + add({ + type: 'warning', + content: formatMsg(msg, this.from), + icon: 'warning', + title: 'Warning', + lifetime: 7000, + actions: ['accept'] + }, cb); + }; - /** - * Display a debug message - * @param {String} msg [description] - * @return {[type]} [description] - */ - this.info = function (msg, cb) { - add({ - type: 'info', - content: msg, - icon: 'info-circle', - title: 'Debug', - lifetime: 7000, - actions: ['accept'] - }, cb); - }; - } + /** + * Display a debug message + * @param {String} msg [description] + * @return {[type]} [description] + */ + NotifyManager.prototype.info = function (msg, cb) { + add({ + type: 'info', + content: formatMsg(msg), + icon: 'info-circle', + title: 'Debug', + lifetime: 7000, + actions: ['accept'] + }, cb); + }; + + // set the timer functions that all notification managers will use + NotifyManager.prototype._setTimerFns = function (set, clear) { + setTO = set; + clearTO = clear; + }; return NotifyManager; - -}); \ No newline at end of file +}); diff --git a/src/kibana/notify/notify.js b/src/kibana/notify/notify.js index ec246d14dacc5..5c0a5286ea921 100644 --- a/src/kibana/notify/notify.js +++ b/src/kibana/notify/notify.js @@ -10,12 +10,10 @@ define(function (require) { require('./directives'); - module.service('notify', function () { - var service = this; - // modify the service to have bound proxies to the manager - _.forOwn(manager, function (val, key) { - service[key] = typeof val === 'function' ? _.bindKey(manager, key) : val; - }); + module.factory('createNotifier', function () { + return function (opts) { + return new NotifyManager(opts); + }; }); /** @@ -90,4 +88,4 @@ define(function (require) { return manager; -}); \ No newline at end of file +}); diff --git a/src/kibana/notify/partials/toaster.html b/src/kibana/notify/partials/toaster.html index 24ecf0c94e807..281a0ed2b0c9c 100644 --- a/src/kibana/notify/partials/toaster.html +++ b/src/kibana/notify/partials/toaster.html @@ -6,13 +6,18 @@ {{ notif.content }} + +
      
           
         
       
      \ No newline at end of file
      diff --git a/src/kibana/require.config.js b/src/kibana/require.config.js
      index 9d2620139546e..d3680609a9196 100644
      --- a/src/kibana/require.config.js
      +++ b/src/kibana/require.config.js
      @@ -16,6 +16,7 @@ require.config({
           lodash: '../bower_components/lodash/dist/lodash',
           moment: '../bower_components/moment/moment',
           gridster: '../bower_components/gridster/dist/jquery.gridster',
      +    stacktrace: '../bower_components/stacktrace.js/stacktrace',
           modules: 'utils/modules',
           bower_components: '../bower_components'
         },
      diff --git a/src/kibana/setup.js b/src/kibana/setup.js
      index c013522e4bb35..31da81741c5ad 100644
      --- a/src/kibana/setup.js
      +++ b/src/kibana/setup.js
      @@ -37,7 +37,7 @@ define(function (require) {
       
               angular
                 .bootstrap(appEl, ['setup'])
      -          .invoke(function (es, config, notify) {
      +          .invoke(function (es, config) {
                   // init the setup module
                   async.series([
                     async.apply(checkForES, es),
      @@ -136,4 +136,4 @@ define(function (require) {
             });
           });
         };
      -});
      \ No newline at end of file
      +});
      diff --git a/src/kibana/styles/_notify.less b/src/kibana/styles/_notify.less
      index f7cb2dd871983..fc5ff99fc7cee 100644
      --- a/src/kibana/styles/_notify.less
      +++ b/src/kibana/styles/_notify.less
      @@ -20,7 +20,7 @@
           -webkit-box-shadow: 3px 0px 19px 0px rgba(50, 50, 50, 0.67);
           -moz-box-shadow:    3px 0px 19px 0px rgba(50, 50, 50, 0.67);
           box-shadow:         3px 0px 19px 0px rgba(50, 50, 50, 0.67);
      -    padding: 0px 15px;
      +    padding: 0.00001px 15px;
           margin: 0 0 10px 0;
           border: none;
       
      @@ -36,15 +36,23 @@
             td {
               vertical-align: middle;
       
      +        // :not(:first-child)
      +        text-align: right;
      +
      +
               &:first-child {
                 text-align: left;
      -          width: 80%;
               }
      -        // :not(:first-child)
      -        text-align: right;
      -        width: 20%;
             }
       
           }
      +
      +    pre {
      +      display: inline-block;
      +      max-width: 100%;
      +      margin: 10px 0;
      +      word-break: normal;
      +      word-wrap: normal;
      +    }
         }
       }
      \ No newline at end of file
      diff --git a/src/kibana/styles/main.css b/src/kibana/styles/main.css
      index 7645ff7271609..313d2f3721a5c 100644
      --- a/src/kibana/styles/main.css
      +++ b/src/kibana/styles/main.css
      @@ -7033,7 +7033,7 @@ kbn-table tr.even td {
         -webkit-box-shadow: 3px 0px 19px 0px rgba(50, 50, 50, 0.67);
         -moz-box-shadow: 3px 0px 19px 0px rgba(50, 50, 50, 0.67);
         box-shadow: 3px 0px 19px 0px rgba(50, 50, 50, 0.67);
      -  padding: 0px 15px;
      +  padding: 0.00001px 15px;
         margin: 0 0 10px 0;
         border: none;
       }
      @@ -7049,11 +7049,16 @@ kbn-table tr.even td {
       .toaster-container .alert table td {
         vertical-align: middle;
         text-align: right;
      -  width: 20%;
       }
       .toaster-container .alert table td:first-child {
         text-align: left;
      -  width: 80%;
      +}
      +.toaster-container .alert pre {
      +  display: inline-block;
      +  max-width: 100%;
      +  margin: 10px 0;
      +  word-break: normal;
      +  word-wrap: normal;
       }
       disc-field-chooser ul {
         margin: 0;