diff --git a/.csslint.rc b/.csslint.rc deleted file mode 100644 index 35baf2b2f..000000000 --- a/.csslint.rc +++ /dev/null @@ -1,25 +0,0 @@ -{ - "empty-rules": false, - "empty-rules-reason": "LESS creates some empty rules", - - "bulletproof-font-face": false, - "buttetproof-font-face-reason": "This only seems to apply when you provide multiple formats, and we aren't doing that", - - "qualified-headings": false, - "qualified-headings-reason": "This happens as part of the reset, we reset headings inside .towtruck (though technically we aren't using headings so we could remove that reset)", - - "adjoining-classes": false, - "adjoining-classes-reason": ".class1.class2 isn't supported in some older browsers, but we don't support those", - - "vendor-prefix": false, - "vendor-prefix-reason": "We have some vendor prefixes in vendor-prefix-specific selectors, and CSSLint complains about that.", - "compatible-vendor-prefixes": false, - "compatible-vendor-prefixes-reason": "Same as vendor-prefix", - - "duplicate-background-images": false, - "duplicate-background-images-reason": "LESS explodes out some background images, creating duplicates, but they aren't duplicated in the source.", - - "fallback-colors": false, - "fallback-colors-reason": "Some old browsers don't support RGBA, but we don't care about those browsers" - -} diff --git a/.gitignore b/.gitignore index 9e32ffc2d..6171942c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,6 @@ -node_modules -.wrangler -.DS_Store -.env* -addon/Profile -dump.rdb -app/http/public/recorder.css -build -test-build -togetherjs.mozillalabs.com -addon/togetherjs.xpi -togetherjs/togetherjs.css -togetherjs/togetherjsPackage.js +node_modules/ +dist/ +src/templates/generated.js +test-results/ +playwright-report/ +*.log diff --git a/.jshintrc b/.jshintrc deleted file mode 100644 index ded86ce62..000000000 --- a/.jshintrc +++ /dev/null @@ -1,23 +0,0 @@ -{ - // JSHint configurations for node-jshint. - // install with "npm install -g jshint" - // run with "jshint --config config.json" - - "asi" : false, // Semicolons required! we minify. - "undef" : false, // var early, var often - - // Predefined globals whom JSHint will accept the existence of. - "browser" : true, - - // Require curly braces - "curly": true, - // Require hasOwnProperty check - "forin": true, - "indent": 2, - // No arguments.callee/caller - "noarg": true, - // Warn about unused variables - "unused": true, - // No trailing whitespace - "trailing": true -} \ No newline at end of file diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 407aee6b0..000000000 --- a/.travis.yml +++ /dev/null @@ -1,6 +0,0 @@ -language: node_js -node_js: - - "0.10" -before_install: - - npm install -g npm - - npm install -g grunt-cli diff --git a/Gruntfile.js b/Gruntfile.js deleted file mode 100644 index ef6670166..000000000 --- a/Gruntfile.js +++ /dev/null @@ -1,788 +0,0 @@ -/*jshint forin:false */ - -var fs = require("fs"); -var path = require('path'); -var nunjucks = require("nunjucks"); -var marked = require("marked"); -var docco = require("docco"); - -var vars = { - enableExample: false, - enableHome: false, - GA_ACCOUNT: "UA-35433268-28", - base: "" -}; - -var TESTDIR = "test-build"; - -module.exports = function (grunt) { - - if (! grunt.option("dest")) { - grunt.option("dest", "build"); - } - - var dumpLineNumbers = false; - if (grunt.option("less-line-numbers")) { - grunt.verbose.writeln("Enabling LESS line numbers"); - dumpLineNumbers = true; - } - - function copyLink(src, dest) { - if (grunt.file.isDir(src)) { - grunt.file.mkdir(dest); - return; - } - var destDir = path.dirname(dest); - if (! grunt.file.exists(destDir)) { - grunt.file.mkdir(destDir); - } - if (! grunt.option("no-hardlink")) { - try { - if (grunt.file.exists(dest)) { - grunt.file.delete(dest); - } - fs.linkSync(src, dest); - } catch (e) { - grunt.file.copy(src, dest); - } - } else { - grunt.file.copy(src, dest); - } - } - - function copyMany(src, dest, patterns) { - var paths = grunt.file.expand({cwd: src}, patterns); - paths.forEach(function (p) { - var srcPath = path.join(src, p); - var destPath = path.join(dest, p); - copyLink(srcPath, destPath); - }); - } - - - var libs = []; - grunt.file.expand( - ["togetherjs/*.js", "!togetherjs/randomutil.js", "!togetherjs/recorder.js", "!togetherjs/togetherjs.js"] - ).forEach(function (filename) { - filename = filename.replace(/^togetherjs\//, ""); - filename = filename.replace(/\.js$/, ""); - libs.push(filename); - }); - var langs = []; - grunt.file.expand("togetherjs/locale/*.json").forEach(function (langFilename) { - var lang = path.basename(langFilename).replace(/\.json/, ""); - langs.push(lang); - libs.push("templates-" + lang); - }); - - grunt.initConfig({ - pkg: grunt.file.readJSON('package.json'), - - less: { - development: { - files: { - "<%= grunt.option('dest') || 'build' %>/togetherjs/togetherjs.css": "togetherjs/togetherjs.less", - "<%= grunt.option('dest') || 'build' %>/togetherjs/recorder.css": "togetherjs/recorder.less" - }, - options: { - dumpLineNumbers: dumpLineNumbers - } - } - }, - - requirejs: { - compile: { - options: { - baseUrl: "togetherjs/", - //paths: requirejsPaths, - include: ["libs/almond"].concat(libs), - //Wrap any build bundle in a start and end text specified by wrap. - //Use this to encapsulate the module code so that define/require are - //not globals. The end text can expose some globals from your file, - //making it easy to create stand-alone libraries that do not mandate - //the end user use requirejs. - wrap: { - start: "(function() {", - end: "TogetherJS.require = TogetherJS._requireObject = require;\nTogetherJS._loaded = true;\nrequire([\"session\"]);\n}());" - }, - optimize: "none", - out: function writer(text) { - var dest = path.join(grunt.option("dest"), "togetherjs/togetherjsPackage.js"); - grunt.file.write(dest, text); - } - } - } - }, - - jshint: { - options: { - curly: true, - browser: true, - globals: { - define: true - } - }, - all: [ - "Gruntfile", - "togetherjs/*.js" - ] - }, - - csslint: { - // Check here for options: https://github.com/stubbornella/csslint/wiki/Rules - options: { - csslintrc: ".csslint.rc" - }, - src: [path.join(grunt.option("dest"), "togetherjs/togetherjs.css")] - }, - - watch: { - main: { - files: ["togetherjs/**/*", "Gruntfile.js"], - tasks: ["build"], - options: { - nospawn: true - } - }, - site: { - files: ["togetherjs/**/*", "Gruntfile.js", "site/**/*", "!**/*_flymake*", "!**/*~", "!**/.*"], - tasks: ["build", "buildsite"] - }, - // FIXME: I thought I wouldn't have to watch for - // togetherjs/**/*.js, but because the hard links are regularly - // broken by git, this needs to be run often, and it's easy to - // forget. Then between git action the build will be over-run, - // but that's harmless. - minimal: { - files: ["togetherjs/**/*.less", "togetherjs/togetherjs.js", "togetherjs/templates-localized.js", - "togetherjs/**/*.html", "togetherjs/**/*.js", "!**/*_flymake*", "togetherjs/locales/**/*.json"], - tasks: ["build"] - } - }, - - 'http-server': { - 'test': { - // the server root directory - root: '.', - cache: 30, - //showDir: true, - //autoIndex: true, - // run in parallel with other tasks - runInBackground: true - } - }, - - 'phantom-tests': grunt.file.expand({ - cwd:"togetherjs/tests/" - }, "test_*.js", "func_*.js", "interactive.js", "!test_ot.js"). - reduce(function(o, k) { o[k] = {}; return o; }, {}) - - }); - - grunt.loadNpmTasks("grunt-contrib-less"); - grunt.loadNpmTasks("grunt-contrib-csslint"); - grunt.loadNpmTasks("grunt-contrib-jshint"); - grunt.loadNpmTasks("grunt-contrib-requirejs"); - grunt.loadNpmTasks("grunt-contrib-watch"); - grunt.loadNpmTasks('grunt-contrib-copy'); - - grunt.registerTask("config-requirejs", function() { - // configure the requirejs paths based on the current options - var requirejsPaths = { - jquery: "libs/jquery-1.11.1.min", - walkabout: "libs/walkabout/walkabout", - esprima: "libs/walkabout/lib/esprima", - falafel: "libs/walkabout/lib/falafel", - tinycolor: "libs/tinycolor", - whrandom: "libs/whrandom/random", - jqueryui: "libs/jquery-ui.min", - jquerypunch: "libs/jquery.ui.touch-punch.min", - // Make sure we get the built form of this one: - templates: path.join("..", grunt.option("dest"), "togetherjs/templates") - }; - langs.forEach(function(lang) { - requirejsPaths["templates-" + lang] = - path.join("..", grunt.option("dest"), "togetherjs", "templates-" + lang); - }); - grunt.config.merge({ - requirejs: { - compile: { - options: { - paths: requirejsPaths - } - } - } - }); - grunt.task.run("requirejs"); - }); - - grunt.registerTask("copylib", "copy the library", function () { - var pattern = ["**", "!togetherjs.js", "!templates-localized.js", "!**/*.less", "!#*", "!**/*_flymake*", "!**/*.md", "!**/*.tmp", "!**/#*"]; - grunt.log.writeln("Copying files from " + "togetherjs/".cyan + " to " + path.join(grunt.option("dest"), "togetherjs").cyan); - if (grunt.option("exclude-tests")) { - pattern.push("!tests/"); - pattern.push("!tests/**"); - grunt.log.writeln(" (excluding tests)"); - } - copyMany( - "togetherjs/", path.join(grunt.option("dest"), "togetherjs"), - pattern - ); - }); - - grunt.registerTask("copysite", "copy the site (not library)", function () { - grunt.log.writeln("Copying files from " + "site/".cyan + " to " + grunt.option("dest").cyan); - copyMany( - "site/", grunt.option("dest"), - ["**", "!**/*.tmpl", "!**/*.html", "!public/**", "!**/*_flymake*", "!**/*.md"]); - copyMany( - "site/public/", grunt.option("dest"), - ["**"]); - }); - - grunt.registerTask("build", ["copylib", "maybeless", "substitute", "config-requirejs"]); - grunt.registerTask("buildsite", ["copysite", "render", "rendermd", "docco"]); - grunt.registerTask("devwatch", ["build", "watch:minimal"]); - // For some reason doing ["build", "buildsite", "watch:site"] - // doesn't work, it gets through buildsite and doesn't watch; - // instead just doing watch:site seems okay: - grunt.registerTask("sitewatch", ["buildsite", "watch:site"]); - - function escapeString(s) { - if (typeof s != "string") { - throw new Error("Not a string: " + s); - } - var data = JSON.stringify(s); - return data.substr(1, data.length-2); - } - - grunt.registerTask( - "substitute", - "Substitute templates-localized.js and parameters in togetherjs.js", - function () { - // FIXME: I could use grunt.file.copy(..., {process: function (content, path) {}}) here - var baseUrl = grunt.option("base-url") || ""; // baseURL to be entered by the user - if (! baseUrl) { - grunt.log.writeln("No --base-url, using auto-detect"); - } - var destBase = grunt.option("dest") || "build"; // where to put the built files. If not indicated then into build/ - var hubUrl = grunt.option("hub-url") || process.env.HUB_URL || "https://hub.togetherjs.com"; // URL of the hub server - grunt.log.writeln("Using hub URL " + hubUrl.cyan); - var gitCommit = process.env.GIT_COMMIT || ""; - var subs = { - __interface_html__: grunt.file.read("togetherjs/interface.html"), - __help_txt__: grunt.file.read("togetherjs/help.txt"), - __walkthrough_html__: grunt.file.read("togetherjs/walkthrough.html"), - __baseUrl__: baseUrl, - __hubUrl__: hubUrl, - __gitCommit__: gitCommit - }; - - function substituteContent(content, s) { - for (var v in s) { - var re = new RegExp(v, "g"); - if (typeof s[v] != "string") { - grunt.log.error("Substitution variable " + v.cyan + " is not a string") - } - content = content.replace(re, escapeString(s[v])); - } - return content; - } - - var filenames = { - "togetherjs.js": { - src: "togetherjs/togetherjs.js", - extraVariables: {__min__: "no"} - }, - "togetherjs-min.js": { - src: "togetherjs/togetherjs.js", - extraVariables: {__min__: "yes"} - } - }; - - for (var dest in filenames) { - var info = filenames[dest]; - var src = info.src; - var extraVariables = info.extraVariables; - dest = destBase + "/" + dest; - var content = fs.readFileSync(src, "UTF-8"); - var s = subs; - - if (extraVariables) { - s = Object.create(subs); - for (var a in extraVariables) { - s[a] = extraVariables[a]; - } - } - content = substituteContent(content, s); - grunt.log.writeln("writing " + src.cyan + " to " + dest.cyan); - grunt.file.write(dest, content); - } - - grunt.file.expand("togetherjs/locale/*.json").forEach(function (langFilename) { - var templates = grunt.file.read("togetherjs/templates-localized.js"); - var lang = path.basename(langFilename).replace(/\.json/, ""); - var translation = JSON.parse(grunt.file.read(langFilename)); - var dest = path.join(grunt.option("dest"), "togetherjs/templates-" + lang + ".js"); - - var translatedInterface = translateFile("togetherjs/interface.html", translation); - var translatedHelp = translateFile("togetherjs/help.txt", translation); - var translatedWalkthrough = translateFile("togetherjs/walkthrough.html", translation); - - var vars = subs; - - subs.__interface_html__ = translatedInterface; - subs.__help_txt__ = translatedHelp; - subs.__walkthrough_html__ = translatedWalkthrough; - subs.__names__ = translation.names; - templates = substituteContent(templates, subs); - - grunt.file.write(dest, templates); - grunt.log.writeln("writing " + dest.cyan + " based on " + langFilename.cyan); - }); - - return true; - } - ); - - - function translateFile(source, translation) { - var env = new nunjucks.Environment(new nunjucks.FileSystemLoader("./")); - var tmpl = env.getTemplate(source); - return tmpl.render({ - gettext: function (string) { - return translation[string] || string; - } - }); - } - - grunt.registerTask("maybeless", "Maybe compile togetherjs.less", function () { - var sources = grunt.file.expand(["togetherjs/**/*.less", "site/**/*.less"]); - var found = false; - sources.forEach(function (fn) { - var source = fs.statSync(fn); - var destFn = grunt.option("dest") + "/" + fn.substr(0, fn.length-4) + "css"; - if (! fs.existsSync(destFn)) { - found = true; - return; - } - var dest = fs.statSync(destFn); - if (source.mtime.getTime() > dest.mtime.getTime()) { - grunt.log.writeln("Destination LESS out of date: " + destFn.cyan); - found = true; - } - }); - if (found) { - grunt.task.run("less"); - } else { - grunt.log.writeln("No .less files need regenerating."); - } - }); - - grunt.registerTask("render", "Render the site", function () { - var env = new nunjucks.Environment(new nunjucks.FileSystemLoader("site/")); - var sources = grunt.file.expand({cwd: "site/"}, "**/*.html"); - sources.forEach(function (source) { - var dest = grunt.option("dest") + "/" + source; - grunt.log.writeln("Rendering " + source.cyan + " to " + dest.cyan); - var data = grunt.file.read("site/" + source); - var tmplVars = Object.create(vars); - while (true) { - var match = /\{\#\s+set\s+([^\s]+)\s+([^#]+)\s*\#\}/.exec(data); - if (! match) { - break; - } - tmplVars[match[1]] = JSON.parse(match[2]); - grunt.log.writeln(" Found variable " + match[1] + " = " + match[2]); - data = data.substr(match.index + match[0].length); - } - tmplVars.base = path.relative(path.dirname("site/" + source), "site/"); - if (tmplVars.base && tmplVars.base.search(/\/$/) == -1) { - tmplVars.base += "/"; - } - if (tmplVars.absoluteLinks) { - tmplVars.base = "/"; - } - tmplVars.base = tmplVars.base.replace(/\\/g, '/'); - var tmpl = env.getTemplate(source); - var result = tmpl.render(tmplVars); - grunt.file.write(dest, result); - }); - }); - - function parseMarkdownOutput(doc) { - var title = (/]*>(.*)<\/h1>/i).exec(doc); - title = title[1]; - var body = doc.replace(/]*>.*<\/h1>/i, ""); - return { - title: title, - body: body - }; - } - - function addHeaderIds(doc) { - var result = []; - while (doc) { - var match = (/((.*)(<\/h\d>)/i).exec(doc); - if (! match) { - result.push(doc); - break; - } - var id = match[2]; - id = id.toLowerCase(); - id = id.replace(/ +/g, "-"); - id = id.replace(/[^a-z0-9_\-]/g, ""); - var header = match[1] + ' id="' + id + '">' + match[2] + match[3]; - result.push(doc.substr(0, match.index)); - result.push(header); - doc = doc.substr(match.index + match[0].length); - } - return result.join(""); - } - - function highlight(code, lang) { - var hjs = require("highlight.js"); - var aliases = { - html: "xml", - js: "javascript" - }; - lang = aliases[lang] || lang; - try { - if (lang) { - return hjs.highlight(lang, code).value; - } else { - return hjs.highlightAuto(code).value; - } - } catch (e) { - grunt.fail.fatal("Error highlighting: " + e); - throw e; - } - } - - marked.setOptions({highlight: highlight}); - - grunt.registerTask("rendermd", "Render the site Markdown files", function () { - var env = new nunjucks.Environment(new nunjucks.FileSystemLoader("site/")); - var sources = grunt.file.expand({cwd: "site/"}, "**/*.md", "!**/README.md"); - sources.forEach(function (source) { - var basename = source.replace(/\.md$/, ""); - var dest = grunt.option("dest") + "/" + basename + ".html"; - grunt.log.writeln("Rendering " + source.cyan + " to " + dest.cyan); - var data = grunt.file.read("site/" + source); - var templateName = "generic-markdown.tmpl"; - var match = (/template:\s*([a-zA-Z_\-0-9.]*)/).exec(data); - if (match) { - templateName = match[1]; - } - var html = marked(data, { - smartypants: true - }); - var parsed = parseMarkdownOutput(html); - parsed.body = addHeaderIds(parsed.body); - var tmpl = env.getTemplate(templateName); - var tmplVars = Object.create(vars); - tmplVars.markdownBody = parsed.body; - tmplVars.title = parsed.title; - tmplVars.base = path.relative(path.dirname("site/" + source), "site/"); - if (tmplVars.base && tmplVars.base.search(/\/$/) == -1) { - tmplVars.base += "/"; - } - tmplVars.base = tmplVars.base.replace(/\\/g, '/'); - var result = tmpl.render(tmplVars); - grunt.file.write(dest, result); - }); - }); - - function doccoFormat(source, sections) { - sections.forEach(function (section) { - var code = highlight(section.codeText, "javascript"); - code = code.replace(/\s+$/, ''); - section.codeHtml = "
" + code + "
"; - section.docsHtml = marked(section.docsText); - }); - } - - grunt.registerTask("docco", "Create comment-separating source code", function () { - var env = new nunjucks.Environment(new nunjucks.FileSystemLoader("site/")); - var sources = grunt.file.expand({cwd: "togetherjs/"}, "*.js"); - sources.sort(); - var sourceDescriptions = JSON.parse(grunt.file.read("togetherjs/module-descriptions.json")); - var sourceList = []; - sources.forEach(function (source) { - var name = source.replace(/\.js$/, ""); - sourceList.push({ - name: name, - link: source + ".html", - description: marked(sourceDescriptions[name] || "", {smartypants: true}) - }); - }); - sources.forEach(function (source) { - var sourceName = source.replace(/\.js$/, ""); - var dest = grunt.option("dest") + "/source/" + source + ".html"; - grunt.log.writeln("Rendering " + source.cyan + " to " + dest.cyan); - var code = grunt.file.read("togetherjs/" + source); - var sections = docco.parse(source, code, {languages:{}}); - doccoFormat(source, sections); - sections.forEach(function (section, i) { - section.index = i; - section.empty = section.codeText.replace(/\s/gm, "") === ""; - }); - var first = marked.lexer(sections[0].docsText)[0]; - var hasTitle = first && first.type == 'heading' && first.depth == 1; - var title = hasTitle ? first.text : path.basename(source, ".js"); - var tmpl = env.getTemplate("source-code.tmpl"); - var tmplVars = Object.create(vars); - tmplVars.title = title; - tmplVars.sections = sections; - tmplVars.source = source; - tmplVars.sourceName = sourceName; - tmplVars.sourceDescription = marked(sourceDescriptions[sourceName] || "", {smartypants: true}); - tmplVars.base = "../"; - tmplVars.sourceList = sourceList; - var result = tmpl.render(tmplVars); - grunt.file.write(dest, result); - }); - var tmplVars = Object.create(vars); - tmplVars.title = "TogetherJS Source Code"; - tmplVars.sourceList = sourceList; - tmplVars.base = "../"; - var tmpl = env.getTemplate("source-code-index.tmpl"); - grunt.file.write(grunt.option("dest") + "/source/index.html", tmpl.render(tmplVars)); - }); - - grunt.registerTask("buildaddon", "Build the Firefox addon and move the XPI into the site", function () { - var done = this.async(); - grunt.util.spawn({ - cmd: "cfx", - args: ["xpi"], - opts: { - cwd: "addon/" - } - }, function (error, result, code) { - if (error) { - grunt.log.error("Error running cfx xpi: " + error.toString().cyan); - grunt.fail.fatal("Error creating XPI"); - done(); - return; - } - var dest = path.join(grunt.option("dest"), "togetherjs.xpi"); - grunt.file.copy("addon/togetherjs.xpi", dest); - grunt.log.writeln("Created " + dest.cyan); - done(); - }); - }); - - grunt.registerTask("publish", "Publish to togetherjs.mozillalabs.com/public/", function () { - if (! grunt.file.isDir("togetherjs.mozillalabs.com")) { - grunt.log.writeln("Error: you must check out togetherjs.mozillalabs.com"); - grunt.log.writeln("Use:"); - grunt.log.writeln(" $ git clone -b togetherjs.mozillalabs.com git:git@github.com:mozilla/togetherjs.git togetherjs.mozillalabs.com"); - grunt.log.writeln(" $ cd togetherjs.mozillalabs.com/.git"); - grunt.log.writeln(" $ echo '[remote \"staging\"]\n\turl = git@heroku.com:togetherjs-staging.git\n\tpush = refs/heads/togetherjs.mozillalabs.com:refs/heads/master\n[remote \"production\"]\n\turl = git@heroku.com:togetherjs.git\n\tpush = refs/heads/togetherjs.mozillalabs.com:refs/heads/master\n' >> config"); - grunt.fail.fatal("Must checkout togetherjs.mozillalabs.com"); - return; - } - var versions = "togetherjs.mozillalabs.com/public/versions"; - if (! grunt.file.isDir(versions)) { - grunt.log.writeln("Error: " + versions.cyan + " does not exist"); - grunt.fail.fatal("No versions/ directory"); - return; - } - var tmp = "togetherjs.mozillalabs.com/public_versions_tmp"; - fs.rename(versions, tmp); - grunt.file.delete("togetherjs.mozillalabs.com/public"); - grunt.file.mkdir("togetherjs.mozillalabs.com/public"); - fs.rename(tmp, versions); - if (! grunt.option("base-url")) { - grunt.option("base-url", "https://togetherjs.com"); - } - grunt.option("dest", "togetherjs.mozillalabs.com/public"); - grunt.option("exclude-tests", true); - grunt.option("no-hardlink", true); - grunt.task.run(["build", "buildsite", "buildaddon"]); - grunt.task.run(["movecss"]); - grunt.log.writeln("To actually publish you must do:"); - grunt.log.writeln(" $ cd togetherjs.mozillalabs.com/"); - grunt.log.writeln(" $ git add -A"); - grunt.log.writeln(" $ git commit -a -m 'Publish'"); - grunt.log.writeln(" $ git push && git push staging"); - }); - - grunt.registerTask("publishversion", "Publish to togetherjs.mozillalabs.com/public/versions/", function () { - var version = grunt.option("togetherjs-version"); - if (! version) { - grunt.log.error("You must provide a --togetherjs-version=X.Y argument"); - grunt.fail.fatal("No --togetherjs-version"); - return; - } - if (! grunt.file.isDir("togetherjs.mozillalabs.com/public/versions")) { - grunt.log.error("The directory togetherjs.mozillalabs.com/public/versions does not exist"); - grunt.fail.fatal(); - return; - } - var destDir = "togetherjs.mozillalabs.com/public/versions/" + version; - if (grunt.file.exists(destDir)) { - grunt.log.error("The directory " + destDir + " already exists"); - grunt.log.error(" Delete it first to re-create version"); - grunt.fail.fatal(); - return; - } - grunt.option("base-url", "https://togetherjs.com/versions/" + version); - grunt.option("dest", destDir); - grunt.option("exclude-tests", true); - grunt.option("no-hardlink", true); - grunt.task.run(["build"]); - grunt.task.run(["movecss"]); - var readme = grunt.file.read("togetherjs.mozillalabs.com/public/versions/README.md"); - readme += " * [" + version + "](./" + version + "/togetherjs.js)\n"; - grunt.file.write("togetherjs.mozillalabs.com/public/versions/README.md", readme); - }); - - grunt.registerTask("movecss", "Publish generated css files to dest", function () { - // Can't figure out how to parameterize the less task, hence this lame move - ["togetherjs/togetherjs.css", "togetherjs/recorder.css"].forEach(function (css) { - var src = path.join("build", css); - var dest = path.join(grunt.option("dest"), css); - grunt.file.copy(src, dest); - grunt.log.writeln("Copying " + src.cyan + " to " + dest.cyan); - }); - }); - - grunt.loadNpmTasks('grunt-contrib-watch'); - - grunt.registerTask('dev', function() { - grunt.util.spawn({ - cmd: 'node', - args: ['devserver.js'] - }); - grunt.task.run('watch'); - }); - - grunt.registerTask("test", "Run jshint and test suite", ["jshint", "phantom"]); - grunt.loadNpmTasks('grunt-http-server'); - - grunt.registerTask("phantom", ["phantom-setup", "phantom-tests"]); - - grunt.registerTask("phantom-setup", "Run jdoctest test suite in phantomjs", - function() { - var done = this.async(); - // find unused ports for web server and hub - var freeport = require("freeport"); - freeport(function(err1, hubPort) { - freeport(function(err2, webPort) { - if (err1 || err2) { return done(err1 || err2); } - - // build togetherjs using these default ports - grunt.option("base-url", "http://localhost:"+webPort+"/"+TESTDIR+"/"); - grunt.option("hub-url", "http://localhost:"+hubPort); - grunt.option("no-hardlink", true); - grunt.option("dest", TESTDIR); - // make sure the web server will use the right port - grunt.config.merge({ - 'http-server': { - test: { - port: webPort, - host: "localhost" - } - } - }); - // spawn a hub, using the hub port - var hub = require("./hub/server"); - hub.startServer(hubPort, "localhost"); - // build & start the web server - grunt.task.run("build", "http-server:test"); - // ok, now we can run the tests in phantomjs! - done(); - }); - }); - }); - - // PhantomJS event handlers - var phantomjs = require("grunt-lib-phantomjs").init(grunt); - var phantomStatus; - - phantomjs.on('fail.load', function(url) { - phantomjs.halt(); - grunt.verbose.write('Running PhantomJS...').or.write('...'); - grunt.log.error('PhantomJS unable to load "' + url + '" URI.'); - phantomStatus.failed += 1; - phantomStatus.total += 1; - }); - - phantomjs.on('fail.timeout', function() { - phantomjs.halt(); - grunt.log.writeln(); - grunt.log.error('PhantomJS timed out.'); - phantomStatus.failed += 1; - phantomStatus.total += 1; - }); - - phantomjs.on('doctestjs.pass', function(result) { - phantomStatus.total += 1; - grunt.verbose.ok("Passed: "+result.example.summary); - }); - - phantomjs.on('doctestjs.fail', function(result) { - phantomStatus.failed += 1; - phantomStatus.total += 1; - grunt.log.error("Failed: "+result.example.expr); - grunt.log.subhead("Expected:"); - grunt.log.writeln(result.example.expected); - grunt.log.subhead("Got:"); - grunt.log.writeln(result.got); - }); - - phantomjs.on('doctestjs.end', function() { - phantomjs.halt(); - }); - - // Pass through console.log statements (when verbose) - phantomjs.on('console', grunt.verbose.writeln); - - grunt.registerMultiTask("phantom-tests", function() { - grunt.task.requires('phantom-setup'); - var url = grunt.option('base-url') + - "togetherjs/tests/index.html?name=" + this.target; - grunt.verbose.writeln("Running tests at: "+url); - - // Merge task-specific and/or target-specific options with these defaults. - var options = this.options({ - // PhantomJS timeout, in ms. - timeout: 10000, - // JDoctest-PhantomJS bridge file to be injected. - inject: path.join(__dirname, 'phantomjs', 'bridge.js'), - //screenshot: true, - page: { - // leave room for the togetherjs sidebar - viewportSize: { width: 1024, height: 1024 } - } - }); - - // Reset test status - phantomStatus = {failed: 0, passed: 0, total: 0, start: Date.now()}; - - // Start phantomjs on this URL - var done = this.async(); - phantomjs.spawn(url, { - options: options, - done: function() { - var duration = Date.now() - phantomStatus.start; - // Log results. - if (phantomStatus.failed > 0) { - grunt.warn(phantomStatus.failed + '/' + phantomStatus.total + - ' assertions failed (' + duration + 'ms)'); - } else if (phantomStatus.total === 0) { - grunt.warn('0/0 assertions ran (' + duration + 'ms)'); - } else { - grunt.verbose.writeln(); - grunt.log.ok(phantomStatus.total + ' assertions passed (' + duration + 'ms)'); - } - // All done! - done(); - } - }); - }); - - grunt.registerTask('default', 'start'); - -}; diff --git a/Procfile b/Procfile deleted file mode 100644 index c7450d3ff..000000000 --- a/Procfile +++ /dev/null @@ -1 +0,0 @@ -web: node hub/server.js diff --git a/README.md b/README.md index 237cda154..7a91daa6c 100644 --- a/README.md +++ b/README.md @@ -32,76 +32,70 @@ Setting up a development environment TogetherJS has two main pieces: -* The [server](https://github.com/mozilla/togetherjs/blob/develop/hub/server.js), which echos messages back and forth between users. The server doesn't do much, you may gaze upon its incredibly boring [history](https://github.com/mozilla/togetherjs/commits/develop/hub/server.js). +* The hub in [`hub-worker/`](hub-worker/), which echoes messages back and forth between users. It doesn't do much: it broadcasts whatever it receives to everyone else in the room, and understands none of it. -* The client in [`togetherjs/`](https://github.com/mozilla/togetherjs/tree/develop/togetherjs) which does all the real work. +* The client in [`src/`](src/), which does all the real work. There is no shared hub server anymore, so you'll need to host your own (see "Hosting the Hub Server" in `site/docs/index.md`). The recommended way is [`hub-worker/`](hub-worker/), a Cloudflare Workers + Durable Objects port of the relay logic that runs on Cloudflare's free plan. Note if you include TogetherJS on an https site, you must use an https/wss hub server (Cloudflare Workers handle this automatically). -The files need to be lightly "built": we use [LESS](http://lesscss.org/) for styles, and a couple files are generated. To develop you need to build the library using [Grunt](http://gruntjs.com/). - -To build a copy of the library, check out TogetherJS: - -```sh -$ git clone git://github.com/mozilla/togetherjs.git -$ cd togetherjs -``` - -Then [install npm](http://nodejs.org/download/) and run: +The client is a set of ES modules under `src/`, bundled with +[esbuild](https://esbuild.github.io/). To build it, install +[Node](https://nodejs.org/) 20 or newer and run: ```sh $ npm install -$ npm install -g grunt-cli +$ npm run build ``` -This will install a bunch of stuff, most of which is only used for development. The only "server" dependency is [WebSocket-Node](https://github.com/Worlize/WebSocket-Node) (and if you use our hub then you don't need to worry about the server). By default everything is installed locally, i.e., in `node_modules/`. This works just fine, but it is useful to install the `grunt` command-line program globally, which `npm install -g grunt-cli` does. +That writes `dist/`: -Now you can build TogetherJS, like: +* `togetherjs.js` — the whole client, ready to drop into a page with + `` +* `togetherjs.min.js` — the same thing, minified, with a source map +* `togetherjs.esm.js` — an ES module entry, for `import { TogetherJS } from "togetherjs"` +* `togetherjs.css`, `images/` — the stylesheet and assets +* `recorder.js`, `walkabout.js` — separate bundles, loaded on demand -```sh -$ grunt build buildsite --no-hardlink -``` - -This will create a copy of the entire `togetherjs.com` site in `build/`. You'll need to setup a local web server of your own pointed to the `build/` directory. To start a server on port 8080, run: +To develop, run a watching build with a static server: ```sh -$ node devserver.js +$ npm run dev ``` -If you want to develop with TogetherJS you probably want the files built continually. To do this use: +Then open `examples/index.html`. It expects a hub at `http://localhost:8787` +(run `npx wrangler dev` inside `hub-worker/`), or you can point it elsewhere +with `?hub=http://host:port`. + +The hub URL baked into a build comes from the `HUB_URL` environment variable: ```sh -$ grunt devwatch +$ HUB_URL=https://hub.example.com npm run build ``` -This will rebuild when changes are detected. Note that Grunt is configured to create [hard links](http://en.wikipedia.org/wiki/Hard_link) instead of copying so that most changes you make to files in `togetherjs/` don't need to be rebuilt to show up in `build/togetherjs/`. `--no-hardlink` turns this behavior off. +`BASE_URL` does the same for the URL the client's own assets are served from; +leave it unset and the client works out where it was loaded from. -You may wish to create a static copy of the TogetherJS client to distribute and use on your website. To do this run: +Testing +------- + +Unit tests use [Vitest](https://vitest.dev/) and live in `tests/unit/`: ```sh -$ grunt build --base-url https://myapp.com --no-hardlink --dest static-myapp +$ npm test ``` -Then `static-myapp/togetherjs.js` and `static-myapp/togetherjs-min.js` will be in place, and the rest of the code will be under `static-myapp/togetherjs/`. You would deploy these on your server. +End-to-end tests use [Playwright](https://playwright.dev/) and live in +`tests/e2e/`. They drive two or three real browser contexts through a session +against an in-process stand-in for the hub, covering cursors, chat, form sync +and the audio/video mesh: -Running a local server ----------------------- -You'll need to run your own hub server (see "Hosting the Hub Server" in -`site/docs/index.md` and [`hub-worker/`](hub-worker/)). If you make changes to -the hub and want to point a local build at it, set the HUB_URL environment -variable when building. For example: -``` -$ HUB_URL=http://localhost:8080 grunt devwatch +```sh +$ npm run test:e2e ``` -Testing -------- - -Tests are in `togetherjs/tests/` -- these are [doctest.js](http://doctestjs.org/) tests. To actually run the tests build togetherjs, serve it up, and go to `http://localhost:PORT/togetherjs/tests/` -- from there the tests are linked to from the top of the page. The actual tests are `*.js` files in `togetherjs/tests/`, generally `test_*.js` for unit-style tests, and `func_*.js` for functional tests. - -The "Manual testing" link is something that lets you simulate different conditions in TogetherJS without setting up a second browser/client. +Lint with `npm run lint` and format with `npm run format`. -There is unfortunately no automated runner for these tests. It might be nice if [Karma](http://karma-runner.github.io/) could be setup with doctest.js in general, but so far that isn't done. +`examples/manual/` holds pages for poking at particular behaviours by hand. License ------- diff --git a/addon/.gitignore b/addon/.gitignore deleted file mode 100644 index aaa177fe7..000000000 --- a/addon/.gitignore +++ /dev/null @@ -1 +0,0 @@ -Profile* \ No newline at end of file diff --git a/addon/Procfile b/addon/Procfile deleted file mode 100644 index 87679ba7f..000000000 --- a/addon/Procfile +++ /dev/null @@ -1,2 +0,0 @@ -profilea: cfx run --binary /Applications/FirefoxNightly.app -p ProfileA -profileb: sleep 1; cfx run --binary /Applications/FirefoxNightly.app -p ProfileB diff --git a/addon/README.md b/addon/README.md deleted file mode 100644 index 93db047b0..000000000 --- a/addon/README.md +++ /dev/null @@ -1,3 +0,0 @@ -The Firefox Addon simply adds togetherjs.js to a window - when you turn -it on the tab gets togetherjs for that one tab, and when you turn it off -it stops. diff --git a/addon/data/attachment.js b/addon/data/attachment.js deleted file mode 100644 index 6de4edcd4..000000000 --- a/addon/data/attachment.js +++ /dev/null @@ -1,19 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -self.port.on("Config", function (config) { - var doc = unsafeWindow.document; - unsafeWindow._TogetherJSBookmarklet = true; - unsafeWindow.TogetherJSConfig_hubBase = config.hubBase; - if (config.shareId) { - unsafeWindow._TogetherJSShareId = config.shareId; - } - var script = doc.createElement("script"); - script.src = config.url; - console.log("Attaching:", script.outerHTML, "to:", window.location.href); - doc.head.appendChild(script); -}); - -// FIXME: need to bind to session.on("close") and emit this: -// self.port.emit("Close"); diff --git a/addon/data/button.html b/addon/data/button.html deleted file mode 100644 index 0103f51c9..000000000 --- a/addon/data/button.html +++ /dev/null @@ -1,3 +0,0 @@ -
- togetherjs -
diff --git a/addon/data/button.js b/addon/data/button.js deleted file mode 100644 index 09e73da98..000000000 --- a/addon/data/button.js +++ /dev/null @@ -1,11 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -self.port.on("TogetherJSOn", function () { - document.getElementById("togetherjs-button").innerHTML = "truckin'"; -}); - -self.port.on("TogetherJSOff", function () { - document.getElementById("togetherjs-button").innerHTML = "togetherjs"; -}); diff --git a/addon/data/startup-help.html b/addon/data/startup-help.html deleted file mode 100644 index 63ca8b5db..000000000 --- a/addon/data/startup-help.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - Welcome to TogetherJS - - - -

Thanks for checking out TogetherJS.

- -

You should see a link/button at the bottom right of the -browser: togetherjs — if you don't then you need to show the -Add-on Bar:

- -

Go to View > Toolbars > Add-on bar to -display the bar -(or read -more here).

- -

Once you see the button, click it to start sharing your session. -Once you are sharing you have to tell someone else (more instructions -here)

- - - diff --git a/addon/lib/main.js b/addon/lib/main.js deleted file mode 100644 index 401f72700..000000000 --- a/addon/lib/main.js +++ /dev/null @@ -1,115 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -const widgets = require("widget"); -const data = require("self").data; -const tabs = require("tabs"); -const { StartupPanel } = require("./startup-panel"); -const { Page } = require("page-worker"); -const simplePrefs = require('simple-prefs'); - -var button = widgets.Widget({ - id: "togetherjs-starter", - label: "Start TogetherJS", - contentURL: data.url("button.html"), - contentScriptFile: data.url("button.js"), - onClick: function () { - console.log("Starting TogetherJS because of click"); - startTogetherJS(); - }, - width: 48 -}); - -StartupPanel({ - name: "TogetherJS", - contentURL: data.url("startup-help.html") -}); - -var autoDomains = []; -function updateAutoDomains() { - var domains = simplePrefs.prefs.autoDomains; - domains = domains.split(/,/g); - autoDomains = []; - domains.forEach(function (item) { - if (item.search(/^\s*$/) === 0) { - return; - } - item = item.replace(/^\s+/, "").replace(/\s+$/, ""); - item = item.split(/;/); - var domain = item[0]; - var shareId = item[1] || null; - if (domain.indexOf("//") == -1) { - // Just a plain domain - domain = "^https?:\\/\\/" + domain; - } - domain = new RegExp(domain, "i"); - autoDomains.push({domain: domain, shareId: shareId}); - }); -} -simplePrefs.on("autoDomains", updateAutoDomains); -updateAutoDomains(); - - -function startTogetherJS(shareId) { - var tab = tabs.activeTab; - if (tab.togetherjsCloser) { - tab.togetherjsCloser(); - return; - } - tab.togetherjsCloser = function () { - tab.togetherjsCloser = null; - button.port.emit("TogetherJSOff"); - tab.removeListener("ready", attachWorker); - }; - var worker; - function attachWorker() { - worker = tab.attach({ - contentScriptFile: [ - data.url("attachment.js") - ] - }); - worker.port.on("Close", function () { - tab.togetherjsCloser(); - }); - worker.port.emit("Config", {url: simplePrefs.prefs.togetherjsJs, hubBase: simplePrefs.prefs.hubBase, shareId: shareId || null}); - } - button.port.emit("TogetherJSOn"); - tab.on("ready", attachWorker); - attachWorker(); -} -// Need poll for back button code - -tabs.on("open", watchTab); - -function watchTab(tab) { - tab.on("ready", function () { - if (tabs.activeTab == tab && tab.url.indexOf("#&togetherjs") != -1) { - console.log("Starting TogetherJS on share link", tab.url); - startTogetherJS(); - } - if (tabs.activeTab == tab && ! tab.togetherjsCloser) { - var started = false; - autoDomains.forEach(function (matcher) { - console.log("matcher", matcher, matcher.domain); - if ((! started) && tab.url.search(matcher.domain) != -1) { - console.log("Start TogetherJS autoDomain"); - startTogetherJS(matcher.shareId); - started = true; - } - }); - } - }); -} - -for (var i=0; i build. + contexts.push( + await esbuild.context({ + ...shared, + entryPoints: [path.join(SRC, "index.js")], + outfile: path.join(DIST, "togetherjs.js"), + sourcemap: true, + }), + ); + + // Same thing, minified. Unlike the old togetherjs-min.js, this one really is. + contexts.push( + await esbuild.context({ + ...shared, + entryPoints: [path.join(SRC, "index.js")], + outfile: path.join(DIST, "togetherjs.min.js"), + minify: true, + sourcemap: true, + }), + ); + + // Named ESM entry for npm consumers. + contexts.push( + await esbuild.context({ + ...shared, + format: "esm", + entryPoints: [path.join(SRC, "index.js")], + outfile: path.join(DIST, "togetherjs.esm.js"), + sourcemap: true, + }), + ); + + for (const entry of extraEntries) { + contexts.push( + await esbuild.context({ + ...shared, + entryPoints: [entry.in], + outfile: path.join(DIST, `${entry.out}.js`), + sourcemap: true, + }), + ); + } + + await buildCss(); + await copyAssets(); + + if (watch || serve) { + await Promise.all(contexts.map((c) => c.watch())); + if (serve) { + const { hosts, port } = await contexts[0].serve({ + servedir: ROOT, + port: 8080, + }); + console.log(`serving ${ROOT} at http://${hosts[0]}:${port}/`); + } + // Stylesheets and images are outside esbuild's graph; poll them. + watchAssets(); + } else { + await Promise.all(contexts.map((c) => c.rebuild())); + await Promise.all(contexts.map((c) => c.dispose())); + } +} + +function watchAssets() { + const watchDirs = [path.join(SRC, "styles"), path.join(SRC, "templates")]; + for (const dir of watchDirs) { + fs.watch(dir, { recursive: true }) + .then(async (watcher) => { + for await (const _event of watcher) { + try { + await buildTemplates(); + await buildCss(); + console.log("assets rebuilt"); + } catch (e) { + console.error("asset rebuild failed:", e.message); + } + } + }) + .catch(() => {}); + } +} + +buildAll().catch((e) => { + console.error(e); + process.exit(1); +}); diff --git a/build/templates.mjs b/build/templates.mjs new file mode 100644 index 000000000..3c848ce69 --- /dev/null +++ b/build/templates.mjs @@ -0,0 +1,88 @@ +// Renders the HTML/text templates once per locale and emits a single generated +// ES module that gets bundled into the client. +// +// This replaces the Grunt "substitute" task, which shelled out to nunjucks to +// evaluate a single `gettext()` call and then wrote one `templates-.js` +// file per locale for RequireJS to fetch at runtime. Bundling every locale +// instead costs ~40KB of HTML (before gzip) and removes both the extra request +// and the load-order race that the old togetherjs/templates.js worked around. + +import fs from "node:fs/promises"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +const ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), ".."); +const TEMPLATE_DIR = path.join(ROOT, "src/templates"); +const LOCALE_DIR = path.join(TEMPLATE_DIR, "locale"); + +// The only template syntax the sources use is {{ gettext('...') }} / {{ gettext("...") }}. +// A real template engine was never needed for one tag with one filter. +const GETTEXT = /\{\{\s*gettext\(\s*(['"])([\s\S]*?)\1\s*\)\s*\}\}/g; + +/** Unescape the JS string literal escapes that appear inside gettext() calls. */ +function unescapeLiteral(s) { + return s.replace(/\\(['"\\])/g, "$1"); +} + +/* Every locale carries its own copy of the markup, so anything that survives + into the bundle is paid for five times over. HTML comments are notes to + whoever edits interface.html; they mean nothing at runtime. */ +function stripComments(source) { + return source.replace(/\n?/g, ""); +} + +function translate(source, translation, missing) { + return source.replace(GETTEXT, (_match, _quote, raw) => { + const key = unescapeLiteral(raw); + const translated = translation[key]; + if (translated === undefined) { + missing.add(key); + return key; + } + return translated; + }); +} + +export async function buildTemplates() { + const [interfaceHtml, walkthroughHtml, helpTxt] = await Promise.all([ + fs.readFile(path.join(TEMPLATE_DIR, "interface.html"), "utf8"), + fs.readFile(path.join(TEMPLATE_DIR, "walkthrough.html"), "utf8"), + fs.readFile(path.join(TEMPLATE_DIR, "help.txt"), "utf8"), + ]); + + const localeFiles = (await fs.readdir(LOCALE_DIR)).filter((f) => f.endsWith(".json")); + const locales = {}; + const report = []; + + for (const file of localeFiles) { + const lang = path.basename(file, ".json"); + const translation = JSON.parse(await fs.readFile(path.join(LOCALE_DIR, file), "utf8")); + const missing = new Set(); + + locales[lang] = { + interface: stripComments(translate(interfaceHtml, translation, missing)), + walkthrough: stripComments(translate(walkthroughHtml, translation, missing)), + help: translate(helpTxt, translation, missing), + names: translation.names || "", + }; + + // Locale keys ARE the English source strings, so rewording a string in + // interface.html silently drops its translation everywhere. Say so loudly. + if (lang !== "en-US" && missing.size) { + report.push({ lang, missing: missing.size }); + } + } + + const source = + "// Generated by build/templates.mjs — do not edit.\n" + + `export const locales = ${JSON.stringify(locales, null, 2)};\n` + + `export const availableLocales = ${JSON.stringify(Object.keys(locales))};\n`; + + const out = path.join(TEMPLATE_DIR, "generated.js"); + const previous = await fs.readFile(out, "utf8").catch(() => null); + if (previous !== source) { + await fs.writeFile(out, source); + } + + return { locales: Object.keys(locales), untranslated: report }; +} diff --git a/client b/client deleted file mode 100644 index e69de29bb..000000000 diff --git a/devserver.js b/devserver.js deleted file mode 100644 index e30388034..000000000 --- a/devserver.js +++ /dev/null @@ -1,38 +0,0 @@ -var -http = require("http"), -url = require("url"), -path = require("path"), -fs = require("fs"), -port = process.argv[2] || process.env['PORT'] || 8080; - -http.createServer(function(request, response) { - - var uri = url.parse(request.url).pathname - var filename = path.join(process.cwd(), 'build', uri); - - fs.exists(filename, function(exists) { - if(!exists) { - response.writeHead(404, {"Content-Type": "text/plain"}); - response.write("404 Not Found\n"); - response.end(); - return; - } - - if (fs.statSync(filename).isDirectory()) filename += '/index.html'; - - fs.readFile(filename, "binary", function(err, file) { - if(err) { - response.writeHead(500, {"Content-Type": "text/plain"}); - response.write(err + "\n"); - response.end(); - return; - } - - response.writeHead(200); - response.write(file, "binary"); - response.end(); - }); - }); -}).listen(parseInt(port, 10)); - -console.log("Static file server running at\n => http://localhost:" + port + "/\nCTRL + C to shutdown"); diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 000000000..0ee9a7cff --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,46 @@ +import globals from "globals"; + +export default [ + { + ignores: ["dist/**", "node_modules/**", "src/vendor/**", "src/templates/generated.js", "site/**"], + }, + { + files: ["src/**/*.js"], + languageOptions: { + ecmaVersion: 2022, + sourceType: "module", + globals: { + ...globals.browser, + // Substituted by build/build.mjs. + __HUB_URL__: "readonly", + __GIT_COMMIT__: "readonly", + __BASE_URL__: "readonly", + // Third-party globals the client integrates with when the host page + // happens to provide them. + CKEDITOR: "readonly", + YT: "readonly", + MozActivity: "readonly", + // Published by the client itself, for the test harness / YouTube API. + onYouTubeIframeAPIReady: "writable", + TogetherJSTestSpy: "writable", + }, + }, + rules: { + "no-unused-vars": ["warn", { args: "none", varsIgnorePattern: "^_" }], + "no-undef": "error", + eqeqeq: "off", // the ported code uses == widely; not worth the churn + "no-console": "off", + }, + }, + { + files: ["build/**/*.mjs", "tests/**/*.js", "tests/**/*.mjs", "*.config.js"], + languageOptions: { + ecmaVersion: 2022, + sourceType: "module", + globals: { ...globals.node, ...globals.browser }, + }, + rules: { + "no-unused-vars": ["warn", { args: "none", varsIgnorePattern: "^_" }], + }, + }, +]; diff --git a/examples/index.html b/examples/index.html new file mode 100644 index 000000000..8f4cb4fdd --- /dev/null +++ b/examples/index.html @@ -0,0 +1,43 @@ + + + + + + TogetherJS example + + + +

TogetherJS example

+

+ A minimal host page for developing the client. Start a session, then open + the share link in a second window to exercise cursors, chat, form sync + and the audio/video mesh. +

+ + + + + + + + + + + + + diff --git a/togetherjs/tests/manual/index.html b/examples/manual/index.html similarity index 100% rename from togetherjs/tests/manual/index.html rename to examples/manual/index.html diff --git a/togetherjs/tests/manual/multi-textarea-focus.html b/examples/manual/multi-textarea-focus.html similarity index 100% rename from togetherjs/tests/manual/multi-textarea-focus.html rename to examples/manual/multi-textarea-focus.html diff --git a/togetherjs/tests/manual/youtube-video.html b/examples/manual/youtube-video.html similarity index 100% rename from togetherjs/tests/manual/youtube-video.html rename to examples/manual/youtube-video.html diff --git a/togetherjs/recorder.html b/examples/recorder.html similarity index 100% rename from togetherjs/recorder.html rename to examples/recorder.html diff --git a/togetherjs/walkabout.html b/examples/walkabout.html similarity index 100% rename from togetherjs/walkabout.html rename to examples/walkabout.html diff --git a/hub/server.js b/hub/server.js deleted file mode 100644 index 58f7448f9..000000000 --- a/hub/server.js +++ /dev/null @@ -1,433 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -// New Relic Server monitoring support -if ( process.env.NEW_RELIC_HOME ) { - require("newrelic"); -} - -var SAMPLE_STATS_INTERVAL = 60*1000; // 1 minute -var SAMPLE_LOAD_INTERVAL = 5*60*1000; // 5 minutes -var EMPTY_ROOM_LOG_TIMEOUT = 3*60*1000; // 3 minutes -var WEBSOCKET_COMPAT = true; - -var WebSocketServer = WEBSOCKET_COMPAT ? - require("./websocket-compat").server : - require("websocket").server; -var http = require('http'); -var parseUrl = require('url').parse; -var fs = require('fs'); - -// FIXME: not sure what logger to use -//var logger = require('../../lib/logger'); - -// LOG_LEVEL values: -// 0: show everything (including debug) -// 1: don't show debug, do show logger.log -// 2: don't show logger.log and debug, do show logger.info (and STATS) -// 3: don't show info, do show warn -// 4: don't show warn, do show error -// 5: don't show anything -// Stats are at level 2 - -var thisSource = "// What follows is the source for the server.\n" + - "// Obviously we can't prove this is the actual source, but if it isn't then we're \n" + - "// a bunch of lying liars, so at least you have us on record.\n\n" + - fs.readFileSync(__filename); - -var Logger = function (level, filename, stdout) { - this.level = level; - this.filename = filename; - this.stdout = !!stdout; - this._open(); - process.on("SIGUSR2", (function () { - this._open(); - }).bind(this)); -}; - -Logger.prototype = { - - write: function () { - if (this.stdout) { - console.log.apply(console, arguments); - } - if (this.file) { - var s = []; - for (var i=0; i EMPTY_ROOM_LOG_TIMEOUT) { - logStats(id, connectionStats[id]); - delete connectionStats[id]; - continue; - } - var totalClients = countClients(connectionStats[id].clients); - var connections = 0; - if (allConnections[id]) { - connections = allConnections[id].length; - } - connectionStats[id].sample.push({ - time: Date.now(), - totalClients: totalClients, - connections: connections - }); - } -}, SAMPLE_STATS_INTERVAL); - -setInterval(function () { - var load = getLoad(); - load.time = Date.now(); - logger.info("LOAD", JSON.stringify(load)); -}, SAMPLE_LOAD_INTERVAL); - -function getLoad() { - var sessions = 0; - var connections = 0; - var empty = 0; - var solo = 0; - for (var id in allConnections) { - if (allConnections[id].length) { - sessions++; - connections += allConnections[id].length; - if (allConnections[id].length == 1) { - solo++; - } - } else { - empty++; - } - } - return { - sessions: sessions, - connections: connections, - empty: empty, - solo: solo - }; -} - -function countClients(clients) { - var n = 0; - for (var clientId in clients) { - n++; - } - return n; -} - -function logStats(id, stats) { - logger.info("STATS", JSON.stringify({ - id: id, - created: stats.created, - sample: stats.sample, - totalClients: countClients(stats.clients), - totalMessageChars: stats.totalMessageChars, - totalMessages: stats.totalMessages, - domain: stats.firstDomain || null, - domainCount: countClients(stats.domains), - urls: countClients(stats.urls) - })); -} - -if (require.main == module) { - var ops = require('optimist') - .usage("Usage: $0 [--port 8080] [--host=localhost] [--log=filename] [--log-level=N]") - .describe("port", "The port to server on (default $HUB_SERVER_PORT, $PORT, $VCAP_APP_PORT, or 8080") - .describe("host", "The interface to serve on (default $HUB_SERVER_HOST, $HOST, $VCAP_APP_HOST, 127.0.0.1). Use 0.0.0.0 to make it public") - .describe("log-level", "The level of logging to do, from 0 (very verbose) to 5 (nothing) (default $LOG_LEVEL or 0)") - .describe("log", "A file to log to (default $LOG_FILE or stdout)") - .describe("stdout", "Log to both stdout and the log file"); - var port = ops.argv.port || process.env.HUB_SERVER_PORT || process.env.VCAP_APP_PORT || - process.env.PORT || 8080; - var host = ops.argv.host || process.env.HUB_SERVER_HOST || process.env.VCAP_APP_HOST || - process.env.HOST || '127.0.0.1'; - var logLevel = process.env.LOG_LEVEL || 0; - var logFile = process.env.LOG_FILE || ops.argv.log; - var stdout = ops.argv.stdout || !logFile; - if (ops.argv['log-level']) { - logLevel = parseInt(ops.argv['log-level'], 10); - } - logger = new Logger(logLevel, logFile, stdout); - if (ops.argv.h || ops.argv.help) { - console.log(ops.help()); - process.exit(); - } else { - startServer(port, host); - } -} - -exports.startServer = startServer; diff --git a/hub/websocket-compat.js b/hub/websocket-compat.js deleted file mode 100644 index 6a85209e8..000000000 --- a/hub/websocket-compat.js +++ /dev/null @@ -1,150 +0,0 @@ -/* - * A hacked websocket module which retains compatibility with the old - * Hixie-76 version of the standard, needed for phantom JS (and, - * presumably, very old browsers). - * - * This file released into the public domain - * by C. Scott Ananian 2014-08-26 - * - * Based on https://gist.github.com/toshirot/1428579 - */ -var events = require("events"); -var util = require("util"); - -var WebSocketRequest = require('websocket').request; -var WebSocketServer = require('websocket').server; - -// Copy helpers from WebSocketServer to WebSocketRequest - -WebSocketRequest.prototype.connections = []; -WebSocketRequest.prototype.handleRequestAccepted = - WebSocketServer.prototype.handleRequestAccepted; -WebSocketRequest.prototype.handleConnectionClose = - WebSocketServer.prototype.handleConnectionClose; -WebSocketRequest.prototype.broadcastUTF = - WebSocketServer.prototype.broadcastUTF; - -var miksagoServerFactory = require('websocket-server'); -var miksagoConnection = require('../node_modules/websocket-server/lib/ws/connection'); - -var CompatWebSocketServer = function(options) { - events.EventEmitter.call(this); // superclass constructor - var self = this; - var handleConnection; - - // node-websocket-server (hixie-75 and hixie-76 support) - var miksagoServer = miksagoServerFactory.createServer(); - miksagoServer.server = options.httpServer; - miksagoServer.addListener('connection', function(connection) { - // Add remoteAddress property - connection.remoteAddress = connection._socket.remoteAddress; - - // We want to use "sendUTF" regardless of the server implementation - connection.sendUTF = connection.send; - handleConnection(connection); - }); - - // WebSocket-Node config (modern websocket support) - var wsServerConfig = { - // All options *except* 'httpServer' are required when bypassing - // WebSocketServer. - maxReceivedFrameSize: options.maxReceivedFrameSize || 0x10000, - maxReceivedMessageSize: options.maxReceivedMessageSize || 0x100000, - fragmentOutgoingMessages: true, - fragmentationThreshold: 0x4000, - keepalive: true, - keepaliveInterval: 20000, - assembleFragments: true, - // autoAcceptConnections is not applicable when bypassing WebSocketServer - // autoAcceptConnections: false, - disableNagleAlgorithm: true, - closeTimeout: 5000 - }; - - // Handle the upgrade event ourselves instead of using WebSocketServer - var wsRequest={}; - options.httpServer.on('upgrade', function(req, socket, head) { - if (typeof req.headers['sec-websocket-version'] !== 'undefined') { - - // WebSocket hybi-08/-09/-10 connection (WebSocket-Node) - wsRequest = new WebSocketRequest(socket, req, wsServerConfig); - try { - wsRequest.readHandshake(); - } catch (e) { - wsRequest.reject( - e.httpCode ? e.httpCode : 400, - e.message, - e.headers - ); - return; - } - wsRequest.once('requestAccepted', function(connection) { - wsRequest.handleRequestAccepted(connection); - }); - self.emit('request', wsRequest); - - } else { - - // WebSocket hixie-75/-76/hybi-00 connection (node-websocket-server) - if (req.method === 'GET' && - (req.headers.upgrade && req.headers.connection) && - req.headers.upgrade.toLowerCase() === 'websocket' && - req.headers.connection.toLowerCase() === 'upgrade') { - new miksagoConnection( - miksagoServer.manager, miksagoServer.options, req, socket, head - ); - } - } - }); - - // A connection handler for old-style websockets - handleConnection = function(connection) { - // fake a request - self.emit('request', new CompatRequest(self, connection)); - }; -}; -util.inherits(CompatWebSocketServer, events.EventEmitter); - -var CompatRequest = function(server, connection) { - this._server = server; - this._connection = connection; - this.origin = connection._options.origin || '*'; - this.httpRequest = connection._req; - // create wrapper right away in order to install event handlers promptly - this._connectionWrapper = new CompatConnection(server, connection); -}; -CompatRequest.prototype.reject = function(code, message) { - this._connection.reject(message || "no reason"); -}; -CompatRequest.prototype.accept = function(proto, origin) { - // this is faked: we've already accepted the connection - return this._connectionWrapper; -}; - -var CompatConnection = function(server, connection) { - var self = this; - events.EventEmitter.call(this); // superclass constructor - - this._server = server; - this._connection = connection; - this.remoteAddress = connection.remoteAddress; - - connection.addListener('message', function(wsMessage) { - // make the argument compatible with WebSocket-Node - self.emit('message', { - type: 'utf8', - utf8Data: wsMessage - }); - }); - - connection.addListener('close', function() { - self.emit('close'); - }); -}; -util.inherits(CompatConnection, events.EventEmitter); - -CompatConnection.prototype.sendUTF = function(message) { - return this._connection.sendUTF(message); -}; - -module.exports.server = CompatWebSocketServer; diff --git a/package-lock.json b/package-lock.json index 60c8cdcb4..da1a669e7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,2014 +1,4078 @@ { "name": "togetherjs", - "version": "0.4.0a", - "lockfileVersion": 1, + "version": "0.5.0", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==" - }, - "ajv": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.0.tgz", - "integrity": "sha1-6yhAdG6dxIvV4GOjbj/UAMXqtak=", - "requires": { - "co": "4.6.0", - "fast-deep-equal": "1.0.0", - "fast-json-stable-stringify": "2.0.0", - "json-schema-traverse": "0.3.1" - } - }, - "amdefine": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-0.0.8.tgz", - "integrity": "sha1-NNyMmB5qyzvhhTvvjw7JSjnVW6A=" - }, - "ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "packages": { + "": { + "name": "togetherjs", + "version": "0.5.0", + "license": "MPL-2.0", + "dependencies": { + "tinycolor2": "^1.6.0" + }, + "devDependencies": { + "@playwright/test": "^1.49.0", + "esbuild": "^0.25.0", + "eslint": "^9.17.0", + "globals": "^15.14.0", + "jsdom": "^25.0.1", + "prettier": "^3.4.2", + "vitest": "^3.2.4", + "ws": "^8.21.3" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@asamuzakjp/css-color": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-3.2.0.tgz", + "integrity": "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.3", + "@csstools/css-color-parser": "^3.0.9", + "@csstools/css-parser-algorithms": "^3.0.4", + "@csstools/css-tokenizer": "^3.0.3", + "lru-cache": "^10.4.3" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz", + "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.3.0", + "minimatch": "^3.1.5", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/@eslint/eslintrc/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/@eslint/eslintrc/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@playwright/test": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.62.1.tgz", + "integrity": "sha512-DTcUc8qii+cpHvtOwggMtBRMjKZHXYWdw8syRYu2vtzuq4Wxphqq4NfCs5Zt44L6mA8rfDfj+PHnxFc/FeK6mQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "playwright": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vitest/expect": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/chai": "^5.2.2", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "3.2.7", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.17" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "3.2.7", + "pathe": "^2.0.3", + "strip-literal": "^3.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "magic-string": "^0.30.17", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^4.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "3.2.7", + "loupe": "^3.1.4", + "tinyrainbow": "^2.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.18.0.tgz", + "integrity": "sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", "dev": true }, - "ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=", + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/brace-expansion": { + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cross-spawn/node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssstyle": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-4.6.0.tgz", + "integrity": "sha512-2z+rWdzbbSZv6/rhtvzvqeZQHrBaqgogqt85sqFNbabZOuFbCVFb8kPeEtZjiKkbrm395irpNKiYeFeLiQnFPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^3.2.0", + "rrweb-cssom": "^0.8.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/cssstyle/node_modules/rrweb-cssom": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.8.0.tgz", + "integrity": "sha512-guoltQEx+9aMf2gDZ0s62EcV8lsXR+0w8915TC3ITdn2YueuNjdAYh/levpU9nFaoChh9RUS5ZdQMrKfVEN9tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-5.0.0.tgz", + "integrity": "sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/debug/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/esbuild/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/esbuild/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/eslint": { + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.5", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/eslint/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/eslint/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", "dev": true }, - "argparse": { - "version": "0.1.16", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-0.1.16.tgz", - "integrity": "sha1-z9AeD7uj1srtBJ+9dY1A9lGW9Xw=", - "requires": { - "underscore": "1.7.0", - "underscore.string": "2.4.0" + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" }, - "dependencies": { - "underscore": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.7.0.tgz", - "integrity": "sha1-a7rwh3UA02vjTsqlhODbn+8DUgk=" - }, - "underscore.string": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.4.0.tgz", - "integrity": "sha1-jN2PusTi0uoefi6Al8QvRCKA+Fs=" + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true } } }, - "asn1": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.3.tgz", - "integrity": "sha1-2sh4dxPJlmhJ/IGAd36+nB3fO4Y=" - }, - "assert-plus": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" - }, - "async": { - "version": "0.1.22", - "resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz", - "integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=" + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } }, - "asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "aws-sign2": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } }, - "aws4": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.6.0.tgz", - "integrity": "sha1-g+9cqGCysy5KDe7e6MdxudtXRx4=" + "node_modules/flatted": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", + "dev": true, + "license": "ISC" }, - "bcrypt-pbkdf": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz", - "integrity": "sha1-Y7xdy2EzG5K8Bf1SiVPDNGKgb40=", + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", "optional": true, - "requires": { - "tweetnacl": "0.14.5" + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "bl": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/bl/-/bl-1.0.3.tgz", - "integrity": "sha1-/FQhoo/UImA2w7OJGmaiW8ZNIm4=", + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, - "requires": { - "readable-stream": "2.0.6" + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "boom": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", - "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", - "requires": { - "hoek": "4.2.0" + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "buffer-crc32": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.1.1.tgz", - "integrity": "sha1-fhENyZU5CKt8MqzccMn5RbHLxSY=" - }, - "bunyan": { - "version": "0.14.6", - "resolved": "https://registry.npmjs.org/bunyan/-/bunyan-0.14.6.tgz", - "integrity": "sha1-zNnPCu2Og/hFPW3XLJ69jY3kVQs=" - }, - "bytes": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/bytes/-/bytes-0.1.0.tgz", - "integrity": "sha1-xXSBIigSbWNp0VdpJahXnbP45aI=" + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } }, - "caseless": { - "version": "0.12.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } }, - "chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "node_modules/globals": { + "version": "15.15.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-15.15.0.tgz", + "integrity": "sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==", "dev": true, - "requires": { - "ansi-styles": "2.2.1", - "escape-string-regexp": "1.0.5", - "has-ansi": "2.0.0", - "strip-ansi": "3.0.1", - "supports-color": "2.0.0" + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "clean-css": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-2.0.8.tgz", - "integrity": "sha1-6TfN/cxXgaAIF67EB56Fs+wVeiA=", - "optional": true, - "requires": { - "commander": "2.0.0" + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" }, - "dependencies": { - "commander": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.0.0.tgz", - "integrity": "sha1-0bhvkB+LZL2UG96tr5JFMDk76Sg=", - "optional": true - } + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "cli": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/cli/-/cli-0.4.5.tgz", - "integrity": "sha1-ePlIXNFhtWbppsctcXDEJw6B22E=", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "requires": { - "glob": "3.1.21" + "license": "MIT", + "engines": { + "node": ">=8" } }, - "co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" - }, - "coffee-script": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.3.3.tgz", - "integrity": "sha1-FQ1rTLUiiUNp7+1qIQHCC8f0pPQ=" - }, - "colors": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/colors/-/colors-0.6.2.tgz", - "integrity": "sha1-JCP+ZnisDF2uiFLl0OW+CMmXq8w=" - }, - "combined-stream": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.5.tgz", - "integrity": "sha1-k4NwpXtKUd6ix3wV1cX9+JUWQAk=", - "requires": { - "delayed-stream": "1.0.0" - } - }, - "commander": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-0.6.1.tgz", - "integrity": "sha1-+mihT2qUXVTbvlDYzbMyDp47GgY=" - }, - "concat-stream": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.5.0.tgz", - "integrity": "sha1-U/fUPFHF5D+ByP3QMyHGMb5o1hE=", - "dev": true, - "requires": { - "inherits": "2.0.3", - "readable-stream": "2.0.6", - "typedarray": "0.0.6" - } - }, - "connect": { - "version": "2.7.2", - "resolved": "https://registry.npmjs.org/connect/-/connect-2.7.2.tgz", - "integrity": "sha1-EXmUY72qr5nV+b7xM78kILJuJoA=", - "requires": { - "buffer-crc32": "0.1.1", - "bytes": "0.1.0", - "cookie": "0.0.5", - "cookie-signature": "0.0.1", - "debug": "3.1.0", - "formidable": "1.0.11", - "fresh": "0.1.0", - "pause": "0.0.1", - "qs": "0.5.1", - "send": "0.1.0" - } - }, - "cookie": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.0.5.tgz", - "integrity": "sha1-+az521frdWjJ/MWWJWt7si4wfIE=" - }, - "cookie-signature": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-0.0.1.tgz", - "integrity": "sha1-E9NgO1z2O++/haiAHjeqkA20aYU=" + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "core-util-is": { + "node_modules/has-tostringtag": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", - "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" - }, - "cryptiles": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", - "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", - "requires": { - "boom": "5.2.0" - }, - "dependencies": { - "boom": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", - "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", - "requires": { - "hoek": "4.2.0" - } - } + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "csslint": { - "version": "0.9.10", - "resolved": "https://registry.npmjs.org/csslint/-/csslint-0.9.10.tgz", - "integrity": "sha1-xBuptrn+x3vKhxEuces6Ig71m8Q=", - "dev": true + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, - "cycle": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz", - "integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI=" + "node_modules/html-encoding-sniffer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-4.0.0.tgz", + "integrity": "sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-encoding": "^3.1.1" + }, + "engines": { + "node": ">=18" + } }, - "dashdash": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", - "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", - "requires": { - "assert-plus": "1.0.0" + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" } }, - "dateformat": { - "version": "1.0.2-1.2.3", - "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-1.0.2-1.2.3.tgz", - "integrity": "sha1-sCIMAt6YYXQztyhRz0fePfLNvuk=" + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } }, - "debug": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", - "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", - "requires": { - "ms": "2.0.0" + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" } }, - "delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "docco": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/docco/-/docco-0.6.3.tgz", - "integrity": "sha1-xHtYI9eVY9b8Or1J895ImG5VIu4=", - "dev": true, - "requires": { - "commander": "0.6.1", - "fs-extra": "4.0.2", - "highlight.js": "9.12.0", - "marked": "0.3.6", - "underscore": "1.4.4" - }, - "dependencies": { - "highlight.js": { - "version": "9.12.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-9.12.0.tgz", - "integrity": "sha1-5tnb5Xy+/mB1HwKvM2GVhwyQwB4=", - "dev": true - } + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" } }, - "ecc-jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz", - "integrity": "sha1-D8c6ntXw1Tw4GTOYUj735UN3dQU=", - "optional": true, - "requires": { - "jsbn": "0.1.1" + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "ecstatic": { - "version": "0.4.13", - "resolved": "https://registry.npmjs.org/ecstatic/-/ecstatic-0.4.13.tgz", - "integrity": "sha1-nLbq/+IRuchO+z9VPN4sMAJxeyk=", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { - "ent": "0.0.7", - "mime": "1.2.6", - "optimist": "0.3.7" - }, + "license": "MIT", "dependencies": { - "optimist": { - "version": "0.3.7", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", - "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", - "dev": true, - "requires": { - "wordwrap": "0.0.3" - } - } + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "ejs": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/ejs/-/ejs-2.5.7.tgz", - "integrity": "sha1-zIcsFoiArjxxiXYv1f/ACJbJUYo=" - }, - "ejs-locals": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/ejs-locals/-/ejs-locals-1.0.2.tgz", - "integrity": "sha1-ubMg/2kzFUEF+g7taD6mTWeAiM4=" + "node_modules/is-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "dev": true, + "license": "MIT" }, - "ent": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/ent/-/ent-0.0.7.tgz", - "integrity": "sha1-g11Of556jUkhxpLpAQ7JdtpemUk=", + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "node_modules/js-tokens": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-9.0.1.tgz", + "integrity": "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==", + "dev": true, + "license": "MIT" }, - "esprima": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-1.0.4.tgz", - "integrity": "sha1-n1V+CPw7TSbs6d00+Pv0drYlha0=" - }, - "eventemitter2": { - "version": "0.4.14", - "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-0.4.14.tgz", - "integrity": "sha1-j2G3XN4BKy6esoTUVFWDtWQ7Yas=" - }, - "exit": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", - "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=" - }, - "express": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/express/-/express-3.0.6.tgz", - "integrity": "sha1-0nT8uGi5V4i/SvYhaNddE/132LQ=", - "requires": { - "buffer-crc32": "0.1.1", - "commander": "0.6.1", - "connect": "2.7.2", - "cookie": "0.0.5", - "cookie-signature": "0.0.1", - "debug": "3.1.0", - "fresh": "0.1.0", - "methods": "0.0.1", - "mkdirp": "0.3.3", - "range-parser": "0.0.4", - "send": "0.1.0" - } - }, - "extend": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.1.tgz", - "integrity": "sha1-p1Xqe8Gt/MWjHOfnYtuq3F5jZEQ=" - }, - "extract-zip": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.5.0.tgz", - "integrity": "sha1-ksz22B73Cp+kwXRxFMzvbYaIpsQ=", - "dev": true, - "requires": { - "concat-stream": "1.5.0", - "debug": "0.7.4", - "mkdirp": "0.5.0", - "yauzl": "2.4.1" - }, - "dependencies": { - "debug": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", - "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", - "dev": true - }, - "minimist": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", - "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", - "dev": true - }, - "mkdirp": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.0.tgz", - "integrity": "sha1-HXMHam35hs2TROFecfzAWkyavxI=", - "dev": true, - "requires": { - "minimist": "0.0.8" - } + "node_modules/jsdom": { + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-25.0.1.tgz", + "integrity": "sha512-8i7LzZj7BF8uplX+ZyOlIz86V6TAsSs+np6m1kpW9u0JWi4z/1t+FzcK1aek+ybTnAC4KhBL4uXCNT0wcUIeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssstyle": "^4.1.0", + "data-urls": "^5.0.0", + "decimal.js": "^10.4.3", + "form-data": "^4.0.0", + "html-encoding-sniffer": "^4.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "is-potential-custom-element-name": "^1.0.1", + "nwsapi": "^2.2.12", + "parse5": "^7.1.2", + "rrweb-cssom": "^0.7.1", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^5.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^7.0.0", + "whatwg-encoding": "^3.1.1", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^14.0.0", + "ws": "^8.18.0", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "canvas": "^2.11.2" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true } } }, - "extsprintf": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" - }, - "eyes": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz", - "integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A=" - }, - "fast-deep-equal": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.0.0.tgz", - "integrity": "sha1-liVqO8l1WV6zbYLpkp0GDYk0Of8=" + "node_modules/jsdom/node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } }, - "fast-json-stable-stringify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "node_modules/jsdom/node_modules/tough-cookie": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-5.1.2.tgz", + "integrity": "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^6.1.32" + }, + "engines": { + "node": ">=16" + } }, - "faye-websocket": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.4.4.tgz", - "integrity": "sha1-wUxbO/FNdBf/v9mQwKdJXNnzN7w=", - "dev": true + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" }, - "fd-slicer": { + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", - "integrity": "sha1-i1vL2ewyfFBBv5qwI/1nUPEXfmU=", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, - "requires": { - "pend": "1.2.0" - } + "license": "MIT" }, - "fileset": { - "version": "0.1.8", - "resolved": "https://registry.npmjs.org/fileset/-/fileset-0.1.8.tgz", - "integrity": "sha1-UGuRqTluqn4y+0KoQHfHoMc2t0E=", + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, - "requires": { - "glob": "3.1.21", - "minimatch": "0.2.14" + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" } }, - "findup-sync": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-0.1.3.tgz", - "integrity": "sha1-fz56l7gjksZTvwZYm9hRkOk8NoM=", - "requires": { - "glob": "3.2.11", - "lodash": "2.4.2" - }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", "dependencies": { - "glob": { - "version": "3.2.11", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.2.11.tgz", - "integrity": "sha1-Spc/Y1uRkPcV0QmH1cAP0oFevj0=", - "requires": { - "inherits": "2.0.3", - "minimatch": "0.3.0" - } - }, - "lodash": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" - }, - "minimatch": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.3.0.tgz", - "integrity": "sha1-J12O2qxPG7MyZHIInnlJyDlGmd0=", - "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" - } - } + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "forever-agent": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "form-data": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.1.tgz", - "integrity": "sha1-b7lPvXGIUwbXPRXMSX/kzE7NRL8=", - "requires": { - "asynckit": "0.4.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "formidable": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/formidable/-/formidable-1.0.11.tgz", - "integrity": "sha1-aPYzJaA15kS297s9ESQ7l2HeGzA=" - }, - "freeport": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/freeport/-/freeport-1.0.5.tgz", - "integrity": "sha1-JV6KuEFwwzuoXZkOghrl9KGpvF0=", - "dev": true + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" }, - "fresh": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.1.0.tgz", - "integrity": "sha1-A+SwF4Qk5MLV0ZpU2IFM3JeTSFA=" + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" }, - "fs-extra": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.2.tgz", - "integrity": "sha1-+RcExT0bRh+JNFKwwwfZmXZHq2s=", + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "4.0.0", - "universalify": "0.1.1" - }, + "license": "MIT", "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - } + "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "gaze": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/gaze/-/gaze-0.3.4.tgz", - "integrity": "sha1-X5S92gr+U7xxCWm81vKCVI1gwnk=", + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "dev": true, - "requires": { - "fileset": "0.1.8", - "minimatch": "0.2.14" + "license": "MIT", + "engines": { + "node": ">= 0.4" } }, - "generate-function": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/generate-function/-/generate-function-2.0.0.tgz", - "integrity": "sha1-aFj+fAlpt9TpCTM3ZHrHn2DfvnQ=", - "dev": true + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } }, - "generate-object-property": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/generate-object-property/-/generate-object-property-1.2.0.tgz", - "integrity": "sha1-nA4cQDCM6AT0eDYYuTf6iPmdUNA=", + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, - "requires": { - "is-property": "1.0.2" + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, - "getobject": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/getobject/-/getobject-0.1.0.tgz", - "integrity": "sha1-BHpEl4n6Fg0Bj1SG7ZEyC27HiFw=" - }, - "getpass": { - "version": "0.1.7", - "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", - "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", - "requires": { - "assert-plus": "1.0.0" - } - }, - "glob": { - "version": "3.1.21", - "resolved": "https://registry.npmjs.org/glob/-/glob-3.1.21.tgz", - "integrity": "sha1-0p4KBV3qUTj00H7UDomC6DwgZs0=", - "requires": { - "graceful-fs": "1.2.3", - "inherits": "1.0.2", - "minimatch": "0.2.14" - }, - "dependencies": { - "inherits": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-1.0.2.tgz", - "integrity": "sha1-ykMJ2t7mtUzAuNJH6NfHoJdb3Js=" + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "graceful-fs": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-1.2.3.tgz", - "integrity": "sha1-FaSAaldUfLLS2/J/QuiajDRRs2Q=" - }, - "grunt": { - "version": "0.4.5", - "resolved": "https://registry.npmjs.org/grunt/-/grunt-0.4.5.tgz", - "integrity": "sha1-VpN81RlDJK3/bSB2MYMqnWuk5/A=", - "requires": { - "async": "0.1.22", - "coffee-script": "1.3.3", - "colors": "0.6.2", - "dateformat": "1.0.2-1.2.3", - "eventemitter2": "0.4.14", - "exit": "0.1.2", - "findup-sync": "0.1.3", - "getobject": "0.1.0", - "glob": "3.1.21", - "grunt-legacy-log": "0.1.3", - "grunt-legacy-util": "0.2.0", - "hooker": "0.2.3", - "iconv-lite": "0.2.11", - "js-yaml": "2.0.5", - "lodash": "0.9.2", - "minimatch": "0.2.14", - "nopt": "1.0.10", - "rimraf": "2.2.8", - "underscore.string": "2.2.1", - "which": "1.0.9" - } - }, - "grunt-amd-check": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/grunt-amd-check/-/grunt-amd-check-0.5.2.tgz", - "integrity": "sha1-UjwyoeZaUWI4sLeOj+0txUypJj0=", - "requires": { - "grunt": "0.4.5", - "grunt-lib-amd": "0.1.3", - "underscore": "1.4.4" - } - }, - "grunt-contrib-copy": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/grunt-contrib-copy/-/grunt-contrib-copy-0.4.1.tgz", - "integrity": "sha1-8HU7QK4hu3BtrvsLKZ4DzfX6nW4=", - "dev": true - }, - "grunt-contrib-csslint": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/grunt-contrib-csslint/-/grunt-contrib-csslint-0.1.2.tgz", - "integrity": "sha1-UFo/YW1MV5AUrkmgiHIU1dCZmrY=", + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, - "requires": { - "csslint": "0.9.10" - } + "license": "MIT" }, - "grunt-contrib-jshint": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/grunt-contrib-jshint/-/grunt-contrib-jshint-0.4.3.tgz", - "integrity": "sha1-79bO/oT8rBQ+QlE82uc2eMUiB5M=", + "node_modules/nwsapi": { + "version": "2.2.24", + "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.24.tgz", + "integrity": "sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A==", "dev": true, - "requires": { - "jshint": "1.1.0" - } + "license": "MIT" }, - "grunt-contrib-less": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/grunt-contrib-less/-/grunt-contrib-less-0.5.2.tgz", - "integrity": "sha1-XoFCpB97nj8OCpd0P9WwkYm7oW8=", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "requires": { - "less": "1.3.3" + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" } }, - "grunt-contrib-requirejs": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/grunt-contrib-requirejs/-/grunt-contrib-requirejs-0.4.4.tgz", - "integrity": "sha1-h/IWWpgeSKRdIvjMUpnQk0AxuXI=", + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { - "requirejs": "2.1.22" + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "grunt-contrib-watch": { - "version": "0.4.4", - "resolved": "https://registry.npmjs.org/grunt-contrib-watch/-/grunt-contrib-watch-0.4.4.tgz", - "integrity": "sha1-Mg/HfFzTO3PlRGzZ7ZGWEhFqpjw=", + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { - "gaze": "0.3.4", - "tiny-lr": "0.0.4" + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "grunt-http-server": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/grunt-http-server/-/grunt-http-server-0.0.5.tgz", - "integrity": "sha1-p4kKitivHdnlbLh24egjbyA9/RY=", + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { - "http-server": "0.6.0", - "lodash": "4.17.4" - }, + "license": "MIT", "dependencies": { - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true - } + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "grunt-legacy-log": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/grunt-legacy-log/-/grunt-legacy-log-0.1.3.tgz", - "integrity": "sha1-7ClCboAwIa9ZAp+H0vnNczWgVTE=", - "requires": { - "colors": "0.6.2", - "grunt-legacy-log-utils": "0.1.1", - "hooker": "0.2.3", - "lodash": "2.4.2", - "underscore.string": "2.3.3" - }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", "dependencies": { - "lodash": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" - }, - "underscore.string": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", - "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=" - } + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "grunt-legacy-log-utils": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/grunt-legacy-log-utils/-/grunt-legacy-log-utils-0.1.1.tgz", - "integrity": "sha1-wHBrndkGThFvNvI/5OawSGcsD34=", - "requires": { - "colors": "0.6.2", - "lodash": "2.4.2", - "underscore.string": "2.3.3" - }, - "dependencies": { - "lodash": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", - "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=" - }, - "underscore.string": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.3.3.tgz", - "integrity": "sha1-ccCL9rQosRM/N+ePo6Icgvcymw0=" - } + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, - "grunt-legacy-util": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/grunt-legacy-util/-/grunt-legacy-util-0.2.0.tgz", - "integrity": "sha1-kzJIhNv343qf98Am3/RR2UqeVUs=", - "requires": { - "async": "0.1.22", - "exit": "0.1.2", - "getobject": "0.1.0", - "hooker": "0.2.3", - "lodash": "0.9.2", - "underscore.string": "2.2.1", - "which": "1.0.9" - } - }, - "grunt-lib-amd": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/grunt-lib-amd/-/grunt-lib-amd-0.1.3.tgz", - "integrity": "sha1-78mznvE5lri9Il2BU/cAkzuQNX8=", - "requires": { - "amdefine": "0.0.8", - "grunt": "0.4.5", - "mout": "0.3.0", - "underscore": "1.4.4" - } - }, - "grunt-lib-phantomjs": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/grunt-lib-phantomjs/-/grunt-lib-phantomjs-0.6.0.tgz", - "integrity": "sha1-rR9/IS/EojJfMvzUnGoNo2h8H7Q=", - "dev": true, - "requires": { - "eventemitter2": "0.4.14", - "phantomjs": "1.9.20", - "semver": "1.0.14", - "temporary": "0.0.8" - } - }, - "habitat": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/habitat/-/habitat-0.4.2.tgz", - "integrity": "sha1-0I7NHLF07jMS4Icrs5hSks3VqqM=" + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "har-schema": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" }, - "har-validator": { - "version": "5.0.3", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", - "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", - "requires": { - "ajv": "5.5.0", - "har-schema": "2.0.0" + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" } }, - "has-ansi": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", - "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, - "requires": { - "ansi-regex": "2.1.1" + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "hasha": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/hasha/-/hasha-2.2.0.tgz", - "integrity": "sha1-eNfL/B5tZjA/55g3NlmEUXsvbuE=", + "node_modules/playwright": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.62.1.tgz", + "integrity": "sha512-0M+L3LAD8/nm554LOla9Ayx0j0tmFZ0FBcoQ7F1VuVHpM/XpiC8RcDzBQB8W5+hA8L22THxELzeF+2WcUzvcLg==", "dev": true, - "requires": { - "is-stream": "1.1.0", - "pinkie-promise": "2.0.1" + "license": "Apache-2.0", + "dependencies": { + "playwright-core": "1.62.1" + }, + "bin": { + "playwright": "cli.js" + }, + "engines": { + "node": ">=20" + }, + "optionalDependencies": { + "fsevents": "2.3.2" } }, - "hawk": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", - "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", - "requires": { - "boom": "4.3.1", - "cryptiles": "3.1.2", - "hoek": "4.2.0", - "sntp": "2.1.0" + "node_modules/playwright-core": { + "version": "1.62.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.62.1.tgz", + "integrity": "sha512-wPYSwEBJY9GHraISXqyqtx0na0LpO3XEX7jNDhntbex7tzUS7kLnZsOlFruFJB4Hi/rhDMjXGqHewDZ68nYZVw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "playwright-core": "cli.js" + }, + "engines": { + "node": ">=20" } }, - "highlight.js": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-7.3.0.tgz", - "integrity": "sha1-bF8PZOcHj2ZAK82/yJEQw/0bqZ8=", - "dev": true + "node_modules/playwright/node_modules/fsevents": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", + "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "hoek": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.0.tgz", - "integrity": "sha512-v0XCLxICi9nPfYrS9RL8HbYnXi9obYAeLbSP00BmnZwCK9+Ih9WOjoZ8YoHCoav2csqn4FOz4Orldsy2dmDwmQ==" - }, - "hooker": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/hooker/-/hooker-0.2.3.tgz", - "integrity": "sha1-uDT3I8xKJCqmWWNFnfbZhMXT2Vk=" - }, - "http-server": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/http-server/-/http-server-0.6.0.tgz", - "integrity": "sha1-uze++MEqSYOWwNcaC1rI36l41R4=", - "dev": true, - "requires": { - "colors": "0.6.2", - "ecstatic": "0.4.13", - "opener": "1.3.0", - "optimist": "0.5.2", - "portfinder": "0.2.1", - "union": "0.3.8" - }, - "dependencies": { - "optimist": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.5.2.tgz", - "integrity": "sha1-hcjBRUszFeSniUfoV7HfAzRQv7w=", - "dev": true, - "requires": { - "wordwrap": "0.0.3" - } + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" } }, - "http-signature": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", - "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", - "requires": { - "assert-plus": "1.0.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" } }, - "iconv-lite": { - "version": "0.2.11", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.2.11.tgz", - "integrity": "sha1-HOYKOleGSiktEyH/RgnKS7llrcg=" + "node_modules/prettier": { + "version": "3.9.6", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.9.6.tgz", + "integrity": "sha512-OpN0zzVdiaiAhxpuuj5efpIS4sY9j7bY6uR5mnj5yPzGkdkjNKSJeUThPb60Jw29QuAZgA4o+/iB49kFiaBX6g==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } }, - "inherits": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", - "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/rollup": { + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", + "fsevents": "~2.3.2" + } + }, + "node_modules/rrweb-cssom": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.7.1.tgz", + "integrity": "sha512-TrEMa7JGdVm0UThDJSx7ddw5nVm3UJS9o9CCIZ72B1vSyEZoziDqBYP3XIoi/12lKrJR8rE3jeFHMok2F/Mnsg==", + "dev": true, + "license": "MIT" }, - "is": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/is/-/is-0.3.0.tgz", - "integrity": "sha1-qPcd/IpuKDcWJ/JskpCYxvTV1dc=" + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" }, - "is-my-json-valid": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/is-my-json-valid/-/is-my-json-valid-2.16.1.tgz", - "integrity": "sha512-ochPsqWS1WXj8ZnMIV0vnNXooaMhp7cyL4FMSIPKTtnV0Ha/T19G2b9kkhcNsabV9bxYkze7/aLZJb/bYuFduQ==", + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", "dev": true, - "requires": { - "generate-function": "2.0.0", - "generate-object-property": "1.2.0", - "jsonpointer": "4.0.1", - "xtend": "4.0.1" + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" } }, - "is-property": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-property/-/is-property-1.0.2.tgz", - "integrity": "sha1-V/4cTkhHTt1lsJkR8msc1Ald2oQ=", - "dev": true - }, - "is-stream": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", - "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", - "dev": true - }, - "is-typedarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" - }, - "isarray": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", - "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", - "dev": true - }, - "isexe": { + "node_modules/shebang-command": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", - "dev": true - }, - "isstream": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } }, - "js-yaml": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-2.0.5.tgz", - "integrity": "sha1-olrmUJmZ6X3yeMZxnaEb0Gh3Q6g=", - "requires": { - "argparse": "0.1.16", - "esprima": "1.0.4" + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" } }, - "jsbn": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", - "optional": true + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" }, - "jshint": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jshint/-/jshint-1.1.0.tgz", - "integrity": "sha1-mYe5C4YFVsvH84DVdVoj1QQhNRM=", - "dev": true, - "requires": { - "cli": "0.4.5", - "esprima": "https://github.com/ariya/esprima/tarball/master", - "minimatch": "0.2.14", - "peakle": "0.0.1", - "shelljs": "0.1.4", - "underscore": "1.4.4" - }, - "dependencies": { - "esprima": { - "version": "https://github.com/ariya/esprima/tarball/master", - "integrity": "sha1-C0XMQgDkwwAPPkY1H9aa+FeCIPo=", - "dev": true - } + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" } }, - "json-schema": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" - }, - "json-schema-traverse": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", - "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" }, - "json-stringify-safe": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" }, - "jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, - "requires": { - "graceful-fs": "4.1.11" + "license": "MIT", + "engines": { + "node": ">=8" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-literal": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/strip-literal/-/strip-literal-3.1.0.tgz", + "integrity": "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==", + "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true, - "optional": true - } + "js-tokens": "^9.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/antfu" } }, - "jsonpointer": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jsonpointer/-/jsonpointer-4.0.1.tgz", - "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", - "dev": true + "node_modules/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" }, - "jsprim": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", - "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", - "requires": { - "assert-plus": "1.0.0", - "extsprintf": "1.3.0", - "json-schema": "0.2.3", - "verror": "1.10.0" - } - }, - "kew": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/kew/-/kew-0.7.0.tgz", - "integrity": "sha1-edk9LTM2PW/dKXCzNdkUGtWR15s=", - "dev": true + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" }, - "klaw": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", - "integrity": "sha1-QIhDO0azsbolnXh4XY6W9zugJDk=", + "node_modules/tinycolor2": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/tinycolor2/-/tinycolor2-1.6.0.tgz", + "integrity": "sha512-XPaBkWQJdsf3pLKJV9p4qN/S+fm2Oj8AIPo1BTUhg5oxkvm9+SVEGFdhyOz7tTdUTfvxMiAs4sp6/eZO2Ew+pw==", + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", "dev": true, - "requires": { - "graceful-fs": "4.1.11" - }, + "license": "MIT" + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true, - "optional": true - } + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "less": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/less/-/less-1.3.3.tgz", - "integrity": "sha1-fujzAKQQgPNUTIDHpwzfamEoDPk=", - "requires": { - "ycssmin": "1.0.1" + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" } }, - "less-middleware": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/less-middleware/-/less-middleware-0.1.15.tgz", - "integrity": "sha1-hQoUWaWWmego6zoH4ItKNQ7aC0Y=", - "requires": { - "less": "1.6.3", - "mkdirp": "0.3.5", - "node.extend": "1.0.10" - }, - "dependencies": { - "less": { - "version": "1.6.3", - "resolved": "https://registry.npmjs.org/less/-/less-1.6.3.tgz", - "integrity": "sha1-cc6J7DC3dLNWfyVMZ5WPLywZO94=", - "requires": { - "clean-css": "2.0.8", - "mime": "1.2.6", - "mkdirp": "0.3.5", - "request": "2.83.0", - "source-map": "0.1.43" - } - }, - "mkdirp": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", - "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=" - } + "node_modules/tinyrainbow": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz", + "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" } }, - "lodash": { - "version": "0.9.2", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-0.9.2.tgz", - "integrity": "sha1-jzSZxSRdNG1oLlsNO0B2fgnxqSw=" + "node_modules/tinyspy": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", + "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } }, - "lru-cache": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-2.7.3.tgz", - "integrity": "sha1-bUUk6LlV+V1PW1iFHOId1y+06VI=" + "node_modules/tldts": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz", + "integrity": "sha512-WMi/OQ2axVTf/ykqCQgXiIct+mSQDFdH2fkwhPwgEwvJ1kSzZRiinb0zF2Xb8u4+OqPChmyI6MEu4EezNJz+FQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^6.1.86" + }, + "bin": { + "tldts": "bin/cli.js" + } }, - "marked": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/marked/-/marked-0.3.6.tgz", - "integrity": "sha1-ssbGGPzOzk74bE/Gy4p8v1rtqNc=", - "dev": true + "node_modules/tldts-core": { + "version": "6.1.86", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-6.1.86.tgz", + "integrity": "sha512-Je6p7pkk+KMzMv2XXKmAE3McmolOQFdxkKw0R8EYNr7sELW46JqnNeTX8ybPiQgvg1ymCoF8LXs5fzFaZvJPTA==", + "dev": true, + "license": "MIT" }, - "methods": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/methods/-/methods-0.0.1.tgz", - "integrity": "sha1-J3yQ+L7zlwlkWoNxxRw7bGSOBow=" - }, - "mime": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.6.tgz", - "integrity": "sha1-sfhsdowCX6h7SAdfFwnyiuryA2U=" - }, - "mime-db": { - "version": "1.30.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.30.0.tgz", - "integrity": "sha1-dMZD2i3Z1qRTmZY0ZbJtXKfXHwE=" - }, - "mime-types": { - "version": "2.1.17", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.17.tgz", - "integrity": "sha1-Cdejk/A+mVp5+K+Fe3Cp4KsWVXo=", - "requires": { - "mime-db": "1.30.0" - } - }, - "minimatch": { - "version": "0.2.14", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-0.2.14.tgz", - "integrity": "sha1-x054BXT2PG+aCQ6Q775u9TpqdWo=", - "requires": { - "lru-cache": "2.7.3", - "sigmund": "1.0.1" - } - }, - "minimist": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.10.tgz", - "integrity": "sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8=" - }, - "mkdirp": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.3.tgz", - "integrity": "sha1-WV4lHBNww6aLqyE20ONIuBBa3xM=" - }, - "mout": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/mout/-/mout-0.3.0.tgz", - "integrity": "sha1-C281MgPlopKen1K0oTprwkWyieg=" - }, - "ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" - }, - "nan": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.8.0.tgz", - "integrity": "sha1-7XFfP+neArV6XmJS2QqWZ14fCFo=" - }, - "newrelic": { - "version": "0.9.20", - "resolved": "https://registry.npmjs.org/newrelic/-/newrelic-0.9.20.tgz", - "integrity": "sha1-YTuSzT9zUsUIurZ+GKfHebEMX38=", - "requires": { - "bunyan": "0.14.6" - } - }, - "node-static": { - "version": "0.6.9", - "resolved": "https://registry.npmjs.org/node-static/-/node-static-0.6.9.tgz", - "integrity": "sha1-GZYe3TVwPIzJLCikNtP13Rodkdo=", - "requires": { - "colors": "0.6.2", - "mime": "2.0.3", - "optimist": "0.6.1" - }, - "dependencies": { - "mime": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/mime/-/mime-2.0.3.tgz", - "integrity": "sha512-TrpAd/vX3xaLPDgVRm6JkZwLR0KHfukMdU2wTEbqMDdCnY6Yo3mE+mjs9YE6oMNw2QRfXVeBEYpmpO94BIqiug==" - } + "node_modules/tr46": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-5.1.1.tgz", + "integrity": "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=18" } }, - "node.extend": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/node.extend/-/node.extend-1.0.10.tgz", - "integrity": "sha1-Mmm934HFRTX0CKvHhMMrDSvVX28=", - "requires": { - "is": "0.3.0" + "node_modules/tr46/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, - "nopt": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz", - "integrity": "sha1-bd0hvSoxQXuScn3Vhfim83YI6+4=", - "requires": { - "abbrev": "1.1.1" + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "noptify": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/noptify/-/noptify-0.0.3.tgz", - "integrity": "sha1-WPZUpz2XU98MUdlobckhBKZ/S7s=", + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { - "nopt": "2.0.0" - }, + "license": "BSD-2-Clause", "dependencies": { - "nopt": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-2.0.0.tgz", - "integrity": "sha1-ynQW8gpeP5w7hhgPlilfo9C1Lg0=", - "dev": true, - "requires": { - "abbrev": "1.1.1" - } - } + "punycode": "^2.1.0" } }, - "nunjucks": { - "version": "0.1.10", - "resolved": "https://registry.npmjs.org/nunjucks/-/nunjucks-0.1.10.tgz", - "integrity": "sha1-jZYIBQM+hX0D0BoiuUOpTnl/MOo=", - "dev": true - }, - "oauth-sign": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", - "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" - }, - "opener": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/opener/-/opener-1.3.0.tgz", - "integrity": "sha1-EwumYiE/qELttM0DYdMaFTAaQ+I=", - "dev": true - }, - "optimist": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.6.1.tgz", - "integrity": "sha1-2j6nRob6IaGaERwybpDrFaAZZoY=", - "requires": { - "minimist": "0.0.10", - "wordwrap": "0.0.3" + "node_modules/uri-js/node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" } }, - "package": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/package/-/package-1.0.1.tgz", - "integrity": "sha1-0lofmeJQbcsn1nBLg9yooxLk7cw=", - "dev": true - }, - "path-is-absolute": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", - "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true - }, - "pause": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz", - "integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10=" - }, - "peakle": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/peakle/-/peakle-0.0.1.tgz", - "integrity": "sha1-KGRF1qdzPxfcJzUB4uA5n0G4kOA=", - "dev": true - }, - "pend": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=", - "dev": true - }, - "performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" - }, - "phantomjs": { - "version": "1.9.20", - "resolved": "https://registry.npmjs.org/phantomjs/-/phantomjs-1.9.20.tgz", - "integrity": "sha1-RCSsog4U0lXAsIia9va4lz2hDg0=", - "dev": true, - "requires": { - "extract-zip": "1.5.0", - "fs-extra": "0.26.7", - "hasha": "2.2.0", - "kew": "0.7.0", - "progress": "1.1.8", - "request": "2.67.0", - "request-progress": "2.0.1", - "which": "1.2.14" - }, - "dependencies": { - "assert-plus": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.2.0.tgz", - "integrity": "sha1-104bh+ev/A24qttwIfP+SBAasjQ=", - "dev": true - }, - "async": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/async/-/async-2.6.0.tgz", - "integrity": "sha512-xAfGg1/NTLBBKlHFmnd7PlmUW9KhVQIUuSrYem9xzFUZy13ScvtyGGejaae9iAVRiRq9+Cx7DPFaAAhCpyxyPw==", - "dev": true, - "requires": { - "lodash": "4.17.4" - } - }, - "aws-sign2": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.6.0.tgz", - "integrity": "sha1-FDQt0428yU0OW4fXY81jYSwOeU8=", - "dev": true - }, - "boom": { - "version": "2.10.1", - "resolved": "https://registry.npmjs.org/boom/-/boom-2.10.1.tgz", - "integrity": "sha1-OciRjO/1eZ+D+UkqhI9iWt0Mdm8=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } - }, - "caseless": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.11.0.tgz", - "integrity": "sha1-cVuW6phBWTzDMGeSP17GDr2k99c=", - "dev": true - }, - "commander": { - "version": "2.12.2", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.12.2.tgz", - "integrity": "sha512-BFnaq5ZOGcDN7FlrtBT4xxkgIToalIIxwjxLWVJ8bGTpe1LroqMiqQXdA7ygc7CRvaYS+9zfPGFnJqFSayx+AA==", - "dev": true - }, - "cryptiles": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-2.0.5.tgz", - "integrity": "sha1-O9/s3GCBR8HGcgL6KR59ylnqo7g=", - "dev": true, - "requires": { - "boom": "2.10.1" - } - }, - "form-data": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-1.0.1.tgz", - "integrity": "sha1-rjFduaSQf6BlUCMEpm13M0de43w=", - "dev": true, - "requires": { - "async": "2.6.0", - "combined-stream": "1.0.5", - "mime-types": "2.1.17" - } - }, - "fs-extra": { - "version": "0.26.7", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.26.7.tgz", - "integrity": "sha1-muH92UiXeY7at20JGM9C0MMYT6k=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11", - "jsonfile": "2.4.0", - "klaw": "1.3.1", - "path-is-absolute": "1.0.1", - "rimraf": "2.2.8" - } - }, - "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "dev": true - }, - "har-validator": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-2.0.6.tgz", - "integrity": "sha1-zcvAgYgmWtEZtqWnyKtw7s+10n0=", - "dev": true, - "requires": { - "chalk": "1.1.3", - "commander": "2.12.2", - "is-my-json-valid": "2.16.1", - "pinkie-promise": "2.0.1" - } - }, - "hawk": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/hawk/-/hawk-3.1.3.tgz", - "integrity": "sha1-B4REvXwWQLD+VA0sm3PVlnjo4cQ=", - "dev": true, - "requires": { - "boom": "2.10.1", - "cryptiles": "2.0.5", - "hoek": "2.16.3", - "sntp": "1.0.9" - } - }, - "hoek": { - "version": "2.16.3", - "resolved": "https://registry.npmjs.org/hoek/-/hoek-2.16.3.tgz", - "integrity": "sha1-ILt0A9POo5jpHcRxCo/xuCdKJe0=", - "dev": true + "node_modules/vite": { + "version": "7.3.6", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.6.tgz", + "integrity": "sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0 || ^0.28.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true }, - "http-signature": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.1.1.tgz", - "integrity": "sha1-33LiZwZs0Kxn+3at+OE0qPvPkb8=", - "dev": true, - "requires": { - "assert-plus": "0.2.0", - "jsprim": "1.4.1", - "sshpk": "1.13.1" - } + "jiti": { + "optional": true }, - "jsonfile": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", - "integrity": "sha1-NzaitCi4e72gzIO1P6PWM6NcKug=", - "dev": true, - "requires": { - "graceful-fs": "4.1.11" - } + "less": { + "optional": true }, - "lodash": { - "version": "4.17.4", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.4.tgz", - "integrity": "sha1-eCA6TRwyiuHYbcpkYONptX9AVa4=", - "dev": true + "lightningcss": { + "optional": true }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", - "dev": true + "sass": { + "optional": true }, - "qs": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-5.2.1.tgz", - "integrity": "sha1-gB/uAw4LlFDWOFrcSKTMVbRK7fw=", - "dev": true + "sass-embedded": { + "optional": true }, - "request": { - "version": "2.67.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.67.0.tgz", - "integrity": "sha1-ivdHgOK/EeoK6aqWXBHxGv0nJ0I=", - "dev": true, - "requires": { - "aws-sign2": "0.6.0", - "bl": "1.0.3", - "caseless": "0.11.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "1.0.1", - "har-validator": "2.0.6", - "hawk": "3.1.3", - "http-signature": "1.1.1", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "node-uuid": "1.4.8", - "oauth-sign": "0.8.2", - "qs": "5.2.1", - "stringstream": "0.0.5", - "tough-cookie": "2.2.2", - "tunnel-agent": "0.4.3" - } + "stylus": { + "optional": true }, - "sntp": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-1.0.9.tgz", - "integrity": "sha1-ZUEYTMkK7qbG57NeJlkIJEPGYZg=", - "dev": true, - "requires": { - "hoek": "2.16.3" - } + "sugarss": { + "optional": true }, - "tough-cookie": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.2.2.tgz", - "integrity": "sha1-yDoYMPTl7wuT7yo0iOck+N4Basc=", - "dev": true + "terser": { + "optional": true }, - "tunnel-agent": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", - "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", - "dev": true + "tsx": { + "optional": true }, - "which": { - "version": "1.2.14", - "resolved": "https://registry.npmjs.org/which/-/which-1.2.14.tgz", - "integrity": "sha1-mofEN48D6CfOyvGs31bHNsAcFOU=", - "dev": true, - "requires": { - "isexe": "2.0.0" - } + "yaml": { + "optional": true } } }, - "pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=", - "dev": true - }, - "pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "node_modules/vite-node": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/vite-node/-/vite-node-3.2.4.tgz", + "integrity": "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==", "dev": true, - "requires": { - "pinkie": "2.0.4" + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.4.1", + "es-module-lexer": "^1.7.0", + "pathe": "^2.0.3", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" } }, - "pkginfo": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/pkginfo/-/pkginfo-0.2.3.tgz", - "integrity": "sha1-cjnEKl72wwuPMoQ52bn/cQQkkPg=" + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "portfinder": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/portfinder/-/portfinder-0.2.1.tgz", - "integrity": "sha1-srmwFk+eF/o6nH2yME0KdRQMca0=", + "node_modules/vite/node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, - "requires": { - "mkdirp": "0.0.7" + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/vitest": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", + "dev": true, + "license": "MIT", "dependencies": { - "mkdirp": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.0.7.tgz", - "integrity": "sha1-2JtPDkw+XlylQjWTFnXglP4aUHI=", - "dev": true + "@types/chai": "^5.2.2", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", + "chai": "^5.2.0", + "debug": "^4.4.1", + "expect-type": "^1.2.1", + "magic-string": "^0.30.17", + "pathe": "^2.0.3", + "picomatch": "^4.0.2", + "std-env": "^3.9.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.14", + "tinypool": "^1.1.1", + "tinyrainbow": "^2.0.0", + "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", + "vite-node": "3.2.4", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true } } }, - "process-nextick-args": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", - "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=", - "dev": true - }, - "progress": { - "version": "1.1.8", - "resolved": "https://registry.npmjs.org/progress/-/progress-1.1.8.tgz", - "integrity": "sha1-4mDHj2Fhzdmw5WzD4Khd4Xx6V74=", - "dev": true - }, - "punycode": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" - }, - "qs": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.1.tgz", - "integrity": "sha1-n2v12axsdjhOldNtFbSJgOXkrdA=" - }, - "range-parser": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz", - "integrity": "sha1-wEJ//vUcEKy6B4KkbJYC50T/Ygs=" - }, - "readable-stream": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", - "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", - "dev": true, - "requires": { - "core-util-is": "1.0.2", - "inherits": "2.0.3", - "isarray": "1.0.0", - "process-nextick-args": "1.0.7", - "string_decoder": "0.10.31", - "util-deprecate": "1.0.2" - } - }, - "request": { - "version": "2.83.0", - "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", - "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", - "requires": { - "aws-sign2": "0.7.0", - "aws4": "1.6.0", - "caseless": "0.12.0", - "combined-stream": "1.0.5", - "extend": "3.0.1", - "forever-agent": "0.6.1", - "form-data": "2.3.1", - "har-validator": "5.0.3", - "hawk": "6.0.2", - "http-signature": "1.2.0", - "is-typedarray": "1.0.0", - "isstream": "0.1.2", - "json-stringify-safe": "5.0.1", - "mime-types": "2.1.17", - "oauth-sign": "0.8.2", - "performance-now": "2.1.0", - "qs": "6.5.1", - "safe-buffer": "5.1.1", - "stringstream": "0.0.5", - "tough-cookie": "2.3.3", - "tunnel-agent": "0.6.0", - "uuid": "3.1.0" - }, - "dependencies": { - "qs": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.1.tgz", - "integrity": "sha512-eRzhrN1WSINYCDCbrz796z37LOe3m5tmW7RQf6oBntukAG1nmovJvhnwHHRMAfeoItc1m2Hk02WER2aQ/iqs+A==" - } + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" } }, - "request-progress": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/request-progress/-/request-progress-2.0.1.tgz", - "integrity": "sha1-XTa7V5YcZzqlt4jbyBQf3yO0Tgg=", + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", "dev": true, - "requires": { - "throttleit": "1.0.0" + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" } }, - "requirejs": { - "version": "2.1.22", - "resolved": "https://registry.npmjs.org/requirejs/-/requirejs-2.1.22.tgz", - "integrity": "sha1-3Xj9LTQYDA1ixyS1uK68BmTgNm8=", - "dev": true - }, - "rimraf": { - "version": "2.2.8", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.2.8.tgz", - "integrity": "sha1-5Dm+Kq7jJzIZUnMPmaiSnk/FBYI=" - }, - "safe-buffer": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.1.tgz", - "integrity": "sha512-kKvNJn6Mm93gAczWVJg7wH+wGYWNrDHdWvpUmHyEsgCtIwwo3bqPtV4tR5tuPaUhTOo/kvhVwd8XwwOllGYkbg==" - }, - "semver": { - "version": "1.0.14", - "resolved": "https://registry.npmjs.org/semver/-/semver-1.0.14.tgz", - "integrity": "sha1-ysXi1Vpvv5WMsiCuhEBFBxx49nY=", - "dev": true - }, - "send": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/send/-/send-0.1.0.tgz", - "integrity": "sha1-z7COvTzsm3/Bo32f+eh1qXHPRkA=", - "requires": { - "debug": "3.1.0", - "fresh": "0.1.0", - "mime": "1.2.6", - "range-parser": "0.0.4" + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" } }, - "shelljs": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.1.4.tgz", - "integrity": "sha1-37vnjVbDwBaNL7eeEOzR28sH7A4=", - "dev": true - }, - "sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA=" - }, - "sntp": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", - "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", - "requires": { - "hoek": "4.2.0" - } - }, - "source-map": { - "version": "0.1.43", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", - "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", - "optional": true, - "requires": { - "amdefine": "0.0.8" - } - }, - "sshpk": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.13.1.tgz", - "integrity": "sha1-US322mKHFEMW3EwY/hzx2UBzm+M=", - "requires": { - "asn1": "0.2.3", - "assert-plus": "1.0.0", - "bcrypt-pbkdf": "1.0.1", - "dashdash": "1.14.1", - "ecc-jsbn": "0.1.1", - "getpass": "0.1.7", - "jsbn": "0.1.1", - "tweetnacl": "0.14.5" - } - }, - "stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA=" - }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, - "stringstream": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", - "integrity": "sha1-TkhM1N5aC7vuGORjB3EKioFiGHg=" - }, - "strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "node_modules/whatwg-encoding/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, - "requires": { - "ansi-regex": "2.1.1" + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, - "supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=", - "dev": true - }, - "temporary": { - "version": "0.0.8", - "resolved": "https://registry.npmjs.org/temporary/-/temporary-0.0.8.tgz", - "integrity": "sha1-oYqYHSi6jKNgJ/s8MFOMPst0CsA=", + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, - "requires": { - "package": "1.0.1" + "license": "MIT", + "engines": { + "node": ">=18" } }, - "throttleit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-1.0.0.tgz", - "integrity": "sha1-nnhYNtr0Z0MUWlmEtiaNgoUorGw=", - "dev": true - }, - "tiny-lr": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/tiny-lr/-/tiny-lr-0.0.4.tgz", - "integrity": "sha1-gGGFR/Y/aX0Fy0DEwsSwg1Ia77Y=", + "node_modules/whatwg-url": { + "version": "14.2.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-14.2.0.tgz", + "integrity": "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw==", "dev": true, - "requires": { - "debug": "0.7.4", - "faye-websocket": "0.4.4", - "noptify": "0.0.3", - "qs": "0.5.6" - }, + "license": "MIT", "dependencies": { - "debug": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-0.7.4.tgz", - "integrity": "sha1-BuHqgILCyxTjmAbiLi9vdX+Srzk=", - "dev": true - }, - "qs": { - "version": "0.5.6", - "resolved": "https://registry.npmjs.org/qs/-/qs-0.5.6.tgz", - "integrity": "sha1-MbGtBYVnZRxSaSFQa5qHk5EaA4Q=", - "dev": true - } + "tr46": "^5.1.0", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=18" } }, - "tough-cookie": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.3.tgz", - "integrity": "sha1-C2GKVWW23qkL80JdBNVe3EdadWE=", - "requires": { - "punycode": "1.4.1" - } - }, - "tunnel-agent": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", - "requires": { - "safe-buffer": "5.1.1" - } - }, - "tweetnacl": { - "version": "0.14.5", - "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", - "optional": true - }, - "typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=", - "dev": true - }, - "typedarray-to-buffer": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.2.tgz", - "integrity": "sha1-EBezLZhP9VbroQD1AViauhrOLgQ=", - "requires": { - "is-typedarray": "1.0.0" - } - }, - "underscore": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.4.4.tgz", - "integrity": "sha1-YaajIBBiKvoHljvzJSA88SI51gQ=" - }, - "underscore.string": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/underscore.string/-/underscore.string-2.2.1.tgz", - "integrity": "sha1-18D6KvXVoaZ/QlPa7pgTLnM/Dxk=" - }, - "union": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/union/-/union-0.3.8.tgz", - "integrity": "sha1-JqcCpNNSi0NYyXEcir/2zJHSQlc=", - "dev": true, - "requires": { - "pkginfo": "0.2.3", - "qs": "0.5.1" - } - }, - "universal-analytics": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/universal-analytics/-/universal-analytics-0.1.3.tgz", - "integrity": "sha1-ltPF/v3cMs+6ohB5k7TuGIblruI=", - "requires": { - "async": "0.2.10", - "node-uuid": "1.4.8", - "request": "2.83.0", - "underscore": "1.4.4" - }, - "dependencies": { - "async": { - "version": "0.2.10", - "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", - "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=" - }, - "node-uuid": { - "version": "1.4.8", - "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", - "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=" - } + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" } }, - "universalify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=", - "dev": true - }, - "util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", - "dev": true - }, - "uuid": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.1.0.tgz", - "integrity": "sha512-DIWtzUkw04M4k3bf1IcpS2tngXEL26YUD2M0tMDUpnUrz2hgzUBlD55a4FjdLGPvfHxS6uluGWvaVEqgBcVa+g==" - }, - "verror": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", - "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", - "requires": { - "assert-plus": "1.0.0", - "core-util-is": "1.0.2", - "extsprintf": "1.3.0" - } - }, - "websocket": { - "version": "1.0.25", - "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.25.tgz", - "integrity": "sha512-M58njvi6ZxVb5k7kpnHh2BvNKuBWiwIYvsToErBzWhvBZYwlEiLcyLrG41T1jRcrY9ettqPYEqduLI7ul54CVQ==", - "requires": { - "debug": "2.6.9", - "nan": "2.8.0", - "typedarray-to-buffer": "3.1.2", - "yaeti": "0.0.6" - }, - "dependencies": { - "debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "requires": { - "ms": "2.0.0" - } - } + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" } }, - "websocket-server": { - "version": "github:miksago/node-websocket-server#dae6bed226ccfccf3939973155570b39dc8b3df0" - }, - "which": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", - "integrity": "sha1-RgwdoPgQED0DIam2M6+eV15kSG8=" - }, - "winston": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/winston/-/winston-0.6.2.tgz", - "integrity": "sha1-QUT+JYbNwZphK/jANVkBMskGS9I=", - "requires": { - "async": "0.1.22", - "colors": "0.6.2", - "cycle": "1.0.3", - "eyes": "0.1.8", - "pkginfo": "0.2.3", - "request": "2.9.203", - "stack-trace": "0.0.10" - }, - "dependencies": { - "request": { - "version": "2.9.203", - "resolved": "https://registry.npmjs.org/request/-/request-2.9.203.tgz", - "integrity": "sha1-bBcRpUB/uUoRQhlWPkQUW8v0cjo=" + "node_modules/ws": { + "version": "8.21.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.3.tgz", + "integrity": "sha512-201TZ/kPWxoPr/OKWjquZR1SWKXcvxdH+e1xrx89b3YbmzLMFCLfnaG1HFIgWzJOEWZ7MvpK++odZufgYR50Rw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true } } }, - "wordwrap": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.3.tgz", - "integrity": "sha1-o9XabNXAvAAI03I0u68b7WMFkQc=" - }, - "xtend": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.1.tgz", - "integrity": "sha1-pcbVMr5lbiPbgg77lDofBJmNY68=", - "dev": true + "node_modules/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } }, - "yaeti": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", - "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" }, - "yauzl": { - "version": "2.4.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.4.1.tgz", - "integrity": "sha1-lSj0QtqxsihOWLQ3m7GU4i4MQAU=", + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, - "requires": { - "fd-slicer": "1.0.1" + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } - }, - "ycssmin": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/ycssmin/-/ycssmin-1.0.1.tgz", - "integrity": "sha1-fN3o23jPqwDSkBw7IwHjBPr03xY=", - "optional": true } } } diff --git a/package.json b/package.json index c52f2bebc..4cfe74e39 100644 --- a/package.json +++ b/package.json @@ -1,53 +1,49 @@ { "name": "togetherjs", - "version": "0.4.0a", - "main": "hub/server.js", + "version": "0.5.0", "description": "Collaborative help system", "keywords": [], + "license": "MPL-2.0", "repository": { "type": "git", - "url": "https://github.com/mozilla/togetherjs.git" + "url": "https://github.com/jsfiddle/togetherjs.git" }, + "type": "module", + "main": "./dist/togetherjs.js", + "module": "./dist/togetherjs.esm.js", + "exports": { + ".": { + "import": "./dist/togetherjs.esm.js", + "default": "./dist/togetherjs.js" + }, + "./dist/*": "./dist/*" + }, + "files": [ + "dist" + ], "dependencies": { - "ejs": "~2.5.5", - "ejs-locals": "~1.0.2", - "express": "~3.0.6", - "grunt-amd-check": "~0.5.1", - "habitat": "~0.4.0", - "less": "~1.3.1", - "less-middleware": "~0.1.9", - "newrelic": "0.9.20", - "node-static": "~0.6.5", - "optimist": "~0.6.0", - "universal-analytics": "~0.1.3", - "websocket": "~1.0.7", - "websocket-server": "github:miksago/node-websocket-server#master", - "winston": "~0.6.2" + "tinycolor2": "^1.6.0" }, "devDependencies": { - "grunt-contrib-less": "~0.5.1", - "grunt-contrib-csslint": "~0.1.2", - "grunt-contrib-jshint": "~0.4.3", - "grunt-contrib-requirejs": "~0.4.1", - "grunt-contrib-watch": "~0.4.3", - "grunt": "~0.4.1", - "grunt-contrib-copy": "~0.4.1", - "grunt-http-server": "~0.0.5", - "nunjucks": "~0.1.8a", - "marked": "~0.3.4", - "docco": "~0.6.2", - "highlight.js": "~7.3.0", - "optimist": "~0.6.0", - "freeport": "~1.0.3", - "grunt-lib-phantomjs": "~0.6.0" + "@playwright/test": "^1.49.0", + "esbuild": "^0.25.0", + "eslint": "^9.17.0", + "globals": "^15.14.0", + "jsdom": "^25.0.1", + "prettier": "^3.4.2", + "vitest": "^3.2.4", + "ws": "^8.21.3" }, "engines": { - "node": "~0.12.7", - "npm": "^2.11.3" + "node": ">=20" }, "scripts": { - "start": "node hub/server.js", - "test": "grunt test", - "build": "grunt build" + "build": "node build/build.mjs", + "dev": "node build/build.mjs --watch --serve", + "lint": "eslint .", + "format": "prettier --write \"src/**/*.js\" \"build/**/*.mjs\" \"tests/**/*.js\"", + "test": "vitest run", + "test:watch": "vitest", + "test:e2e": "playwright test" } } diff --git a/phantomjs/bridge.js b/phantomjs/bridge.js deleted file mode 100644 index a4af2a618..000000000 --- a/phantomjs/bridge.js +++ /dev/null @@ -1,82 +0,0 @@ -(function (doctest) { - 'use strict'; - - // Function.bind is not defined in phantomjs (!) so polyfill it - if (!Function.prototype.bind) { - Function.prototype.bind = function (oThis) { - if (typeof this !== "function") { - // closest thing possible to the ECMAScript 5 - // internal IsCallable function - throw new TypeError("can't bind"); - } - - var aArgs = Array.prototype.slice.call(arguments, 1), - fToBind = this, - fNOP = function () {}, - fBound = function () { - return fToBind.apply(this instanceof fNOP && oThis - ? this - : oThis, - aArgs.concat(Array.prototype.slice.call(arguments))); - }; - - fNOP.prototype = this.prototype; - fBound.prototype = new fNOP(); - - return fBound; - }; - } - - // default phantomjs background is transparent - document.body.bgColor = 'white'; - - // Send messages to the parent PhantomJS process via alert! Good times!! - function sendMessage() { - var args = [].slice.call(arguments); - alert(JSON.stringify(args)); - } - - // doctestjs-specific stuff. First, be sure we don't autostart: - document.body.className = document.body.className.replace(/autodoctest/,''); - - // Now define a custom reporter which will pass the results up to grunt - var PhantomReporter = function(runner) { - this.runner = runner; - }; - PhantomReporter.prototype.logSuccess = function(example, got) { - this._send('doctestjs.pass', example, got); - }; - PhantomReporter.prototype.logFailure = function(example, got) { - this._send('doctestjs.fail', example, got); - }; - PhantomReporter.prototype._send = function(msg, example, got) { - sendMessage(msg, { - example: { - expr: example.expr, - summary: example.textSummary(), - expected: example.expected - }, - got: got - }); - }; - - // Start/finish the doctest runner. - window.doctestReporterHook = { - finish: function() { - sendMessage('doctestjs.end'); - } - }; - window.addEventListener('load', function() { - var runner = new doctest.Runner({ - Reporter: PhantomReporter - }); - var parser = new doctest.HTMLParser(runner); - parser.loadRemotes(function() { - runner.init(); - parser.parse(); - sendMessage('doctestjs.start'); - runner.run(); - }); - }); - -})(window.doctest); diff --git a/playwright.config.js b/playwright.config.js new file mode 100644 index 000000000..8d8f677dd --- /dev/null +++ b/playwright.config.js @@ -0,0 +1,44 @@ +import { defineConfig } from "@playwright/test"; +import fs from "node:fs"; + +/* This dev container ships a Chromium that Playwright does not manage, so + point at it when it is there and let Playwright resolve its own browser + otherwise (CI, or a normal checkout after `playwright install`). */ +const PINNED_CHROMIUM = + process.env.CHROMIUM_PATH || "/opt/pw-browsers/chromium-1194/chrome-linux/chrome"; +const executablePath = fs.existsSync(PINNED_CHROMIUM) ? PINNED_CHROMIUM : undefined; + +export default defineConfig({ + testDir: "./tests/e2e", + timeout: 30000, + expect: { timeout: 10000 }, + fullyParallel: false, + workers: 1, + reporter: process.env.CI ? "list" : [["list"]], + use: { + baseURL: "http://localhost:8099", + trace: "retain-on-failure", + }, + projects: [ + { + name: "chromium", + use: { + launchOptions: { + executablePath, + args: [ + // The sandbox may route through an HTTPS proxy that would + // intercept the loopback hub connection. + "--no-proxy-server", + // Fake devices and auto-granted permission, so getUserMedia + // resolves headlessly. Note this container produces no devices + // even with the flag set, so tests/e2e/fixtures.js also installs + // canvas/WebAudio-backed tracks; keep both. + "--use-fake-device-for-media-capture", + "--use-fake-ui-for-media-stream", + "--autoplay-policy=no-user-gesture-required", + ], + }, + }, + }, + ], +}); diff --git a/site/docs/index.md b/site/docs/index.md index 77924c156..23a6abeb4 100644 --- a/site/docs/index.md +++ b/site/docs/index.md @@ -51,7 +51,7 @@ In this section we'll describe the general way that TogetherJS works, without di The core of TogetherJS is the **hub**: this is a server that everyone in a session connects to, and it echos messages to all the participants using Web Sockets. This server does not rewrite the messages or do much of anything besides **pass the messages between the participants**. -[WebRTC](http://www.webrtc.org/) is available for **audio chat**, but is not otherwise used. We are often asked about this, as WebRTC offers data channels that allow browsers to send data directly to other browsers without a server. Unfortunately you still need a server to establish the connection (the connection strings to connect browsers are quite unwieldy), it only supports one-to-one connections, and that support is limited to only some browsers and browser versions. Also establishing the connection is significantly slower than Web Sockets. But maybe someday. +[WebRTC](https://webrtc.org/) is used for **audio and video chat**, but not for anything else. We are often asked about this, as WebRTC offers data channels that let browsers send data directly to each other. You still need a server to introduce the two browsers, though, and the hub is already there and already fast; a data channel would add a second path to maintain without removing the first. So application messages continue to go over the Web Socket. Everything that TogetherJS does is based on these messages being passed between browsers. It doesn't require that everyone be on the same page, all it requires is that everyone in the session know what hub URL to connect to (the URL is essentially the session name). People *can* be on different sites, but the session URL is stored in [sessionStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window.sessionStorage) which is local to one domain (and because we use sessionStorage instead of localStorage, it is local to one tab). We don't have any techniques implemented to share sessions across multiple sites, but the only barrier is this local storage of session information. @@ -129,7 +129,19 @@ The other way to set a variable *after* TogetherJS is loaded is `TogetherJS.conf When true (default false), TogetherJS treats the entire URL, including the hash, as the identifier of the page; i.e., if you one person is on `http://example.com/#view1` and another person is at `http://example.com/#view2` then these two people are considered to be at completely different URLs. You'd want to use this in single-page apps where being at the same base URL doesn't mean the two people are looking at the same thing. `TogetherJSConfig_disableWebRTC`: - Disables/removes the button to do audio chat via WebRTC. + Disables/removes the microphone and camera buttons. + +`TogetherJSConfig_enableVideo`: + When true (default false), a camera button appears next to the microphone button and participants can share video. It is off by default because camera access is a bigger ask than the microphone, and because an existing embed should not sprout a camera button when it upgrades. + +`TogetherJSConfig_iceServers`: + An array of [RTCIceServer](https://developer.mozilla.org/en-US/docs/Web/API/RTCPeerConnection/RTCPeerConnection#iceservers) objects used for audio/video connections. Defaults to a public STUN server. See "About audio and video chat" below for why you may want to supply a TURN server here. + +`TogetherJSConfig_getIceServers`: + An `async () => RTCIceServer[]` function, called once per connection. Prefer this over `iceServers` when your TURN credentials are short-lived: a static array cannot express a credential that expires. + +`TogetherJSConfig_maxRtcPeers`: + How many people may be in a call at once (default 6). Everyone sends their audio and video directly to everyone else, so the bandwidth each participant needs grows with the size of the call. Past this limit new connections are refused rather than degrading the call for everyone. `TogetherJSConfig_youtube`: If true, then YouTube videos will be synchronized (i.e., when one person plays or pauses a video, it will play for all people). This will also load up the YouTube iframe API. @@ -166,17 +178,41 @@ The button you add to your site to start TogetherJS will typically look like thi TogetherJS sessions are connected to the domain you start them on (specifically the [origin](http://tools.ietf.org/html/rfc6454)). So if part of your site is on another domain, people won't be able to talk across those domains. Even a page that is on https when another is on http will cause the session to be lost. We might make this work sometime, but if it's an issue to you please give us [feedback](https://docs.google.com/forms/d/1lVE7JyRo_tjakN0mLG1Cd9X9vseBX9wci153z9JcNEs/viewform). -## About Audio Chat and WebRTC +## About audio and video chat + +Live audio and video chat are based on [WebRTC](https://webrtc.org/), which every current browser supports. -The live audio chat is based on [WebRTC](http://www.webrtc.org/). This is a very new technology, built into some new browsers. +Click the microphone button to join the call, and click it again to mute yourself. If you set `TogetherJSConfig_enableVideo`, a camera button appears alongside it; peers who turn their camera on show up as tiles in a video panel. Audio plays whether or not that panel is open. "Leave call" in the microphone popup hangs up and releases the microphone and camera. -To enable WebRTC both you and your collaborator need a new browser. Right now, [Firefox Nightly](http://nightly.mozilla.org/) is supported, and we believe that the newest release of Chrome should work. +### It needs a secure page -Sometime in 2013 support for this should be available in new (non-experimental) versions of Firefox, Chrome, and both Firefox and Chrome for Android. +Browsers only grant access to the microphone and camera on a secure origin. If your page is served over plain `http`, the buttons will tell you so — no permission prompt will help, the API is simply not there. `localhost` counts as secure, so local development works. -To see a summary of outstanding issues that we know of with audio chat see [this page](https://github.com/mozilla/togetherjs/issues?labels=rtc&milestone=&page=1&state=open). +### It needs TURN on some networks -Note that audio chat will not work between some networks. These networks require a [TURN server](http://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT) which unfortunately we do not have allocated (and full support for TURN has not landed in some browsers). Unfortunately when the network makes chat impossible, chat will simply not work – we don't receive an error, and can't tell you why chat is not working. See [#327](https://github.com/mozilla/togetherjs/issues/327) for progress. +Everyone in a call connects directly to everyone else. Some networks — symmetric NATs, and many corporate firewalls — will not allow that, and the connection needs a [TURN server](https://en.wikipedia.org/wiki/Traversal_Using_Relays_around_NAT) to relay it. TogetherJS does not run one, and the default configuration only includes a STUN server, which is enough for most home networks but not all of them. + +If your users are on networks where calls fail to connect, supply your own: + +```js +TogetherJSConfig_iceServers = [ + {urls: "stun:stun.l.google.com:19302"}, + {urls: "turn:turn.example.com:3478", username: "…", credential: "…"} +]; +``` + +TURN credentials are usually short-lived, so if yours are minted per session use the async hook instead: + +```js +TogetherJSConfig_getIceServers = async () => { + const resp = await fetch("/my-turn-credentials"); + return resp.json(); +}; +``` + +### How many people + +The call is a full mesh: each participant sends their audio and video to every other participant separately, so the bandwidth each person needs grows with the number of people. That is fine for a handful and poor beyond it, so calls are capped at `TogetherJSConfig_maxRtcPeers` (default 6). Supporting larger calls would mean routing media through a server (an SFU), which TogetherJS does not do. ## Extending TogetherJS @@ -432,9 +468,7 @@ The bare minimum that we've identified for TogetherJS is [WebSocket support](htt We recommend the most recent release of [Firefox](http://www.mozilla.org/en-US/firefox/new/) or [Chrome](https://www.google.com/intl/en/chrome/browser/). -If you want to have [WebRTC support](https://github.com/mozilla/togetherjs/wiki/About-Audio-Chat-and-WebRTC) and are using Firefox, as of April 2013 this requires [Firefox Nightly](http://nightly.mozilla.org/) (this support will be moving towards beta and release in the coming months). - -We haven't done much testing on mobile (yet!) and cannot recommend anything there. +Audio and video chat work in any current browser, but need the page to be served over https (see "About audio and video chat" above). #### Internet Explorer diff --git a/src/README.md b/src/README.md new file mode 100644 index 000000000..b6a90eb70 --- /dev/null +++ b/src/README.md @@ -0,0 +1,98 @@ +TogetherJS client +================= + +The client is a set of ES modules bundled by `build/build.mjs` (esbuild). The +entry point is `index.js`; everything else is grouped by what it does. + +Two conventions are worth knowing before reading any of it: + +- **`core/togetherjs.js` is imported, not global.** It defines the `TogetherJS` + object and assigns `window.TogetherJS`, but every module that uses it imports + it explicitly. Under RequireJS the load order made the global safe to read at + module scope; in a single bundle it is not. +- **Configuration is read lazily.** The bundle evaluates when the ` - - - - diff --git a/togetherjs/tests/doctestjs/.resources/boilerplate/css/main.css b/togetherjs/tests/doctestjs/.resources/boilerplate/css/main.css deleted file mode 100755 index 16a90cab7..000000000 --- a/togetherjs/tests/doctestjs/.resources/boilerplate/css/main.css +++ /dev/null @@ -1,397 +0,0 @@ -/* ========================================================================== - HTML5 Boilerplate styles - h5bp.com (generated via initializr.com) - ========================================================================== */ - -html, -button, -input, -select, -textarea { - color: #222; -} - -body { - font-size: 1em; - line-height: 1.4; -} - -::-moz-selection { - background: #b3d4fc; - text-shadow: none; -} - -::selection { - background: #b3d4fc; - text-shadow: none; -} - -hr { - display: block; - height: 1px; - border: 0; - border-top: 1px solid #ccc; - margin: 1em 0; - padding: 0; -} - -img { - vertical-align: middle; -} - -fieldset { - border: 0; - margin: 0; - padding: 0; -} - -textarea { - resize: vertical; -} - -.chromeframe { - margin: 0.2em 0; - background: #ccc; - color: #000; - padding: 0.2em 0; -} - - -/* ===== Initializr Styles ================================================== - Author: Jonathan Verrecchia - verekia.com/initializr/responsive-template - ========================================================================== */ - -body { - font: 16px/26px Helvetica, Helvetica Neue, Arial; -} - -.wrapper { - width: 90%; - margin: 0 5%; -} - -/* =================== - ALL: Orange Theme - =================== */ - -.header-container { - border-bottom: 20px solid #e44d26; -} - -.footer-container, -.main aside { - border-top: 20px solid #e44d26; -} - -.header-container, -.footer-container, -.main aside { - background: #f16529; -} - -.title { - color: white; -} - -/* ============== - MOBILE: Menu - ============== */ - -nav ul { - margin: 0; - padding: 0; -} - -nav a { - display: block; - margin-bottom: 10px; - padding: 15px 0; - - text-align: center; - text-decoration: none; - font-weight: bold; - - color: white; - background: #e44d26; -} - -nav a:hover, -nav a:visited { - color: white; -} - -nav a:hover { - text-decoration: underline; -} - -/* ============== - MOBILE: Main - ============== */ - -.main { - padding: 30px 0; -} - -.main article h1 { - font-size: 2em; -} - -.main aside { - color: white; - padding: 0px 5% 10px; -} - -.footer-container footer { - color: white; - padding: 20px 0; -} - -/* =============== - ALL: IE Fixes - =============== */ - -.ie7 .title { - padding-top: 20px; -} - -/* ========================================================================== - Author's custom styles - ========================================================================== */ - - - - - - - - - - - - - - - -/* ========================================================================== - Media Queries - ========================================================================== */ - -@media only screen and (min-width: 480px) { - -/* ==================== - INTERMEDIATE: Menu - ==================== */ - - nav a { - float: left; - width: 27%; - margin: 0 1.7%; - padding: 25px 2%; - margin-bottom: 0; - } - - nav li:first-child a { - margin-left: 0; - } - - nav li:last-child a { - margin-right: 0; - } - -/* ======================== - INTERMEDIATE: IE Fixes - ======================== */ - - nav ul li { - display: inline; - } - - .oldie nav a { - margin: 0 0.7%; - } -} - -@media only screen and (min-width: 768px) { - -/* ==================== - WIDE: CSS3 Effects - ==================== */ - - .header-container, - .main aside { - -webkit-box-shadow: 0 5px 10px #aaa; - -moz-box-shadow: 0 5px 10px #aaa; - box-shadow: 0 5px 10px #aaa; - } - -/* ============ - WIDE: Menu - ============ */ - - .title { - float: left; - } - - nav { - float: right; - width: 38%; - } - -/* ============ - WIDE: Main - ============ */ - - .main article { - float: left; - width: 57%; - } - - .main aside { - float: right; - width: 28%; - } -} - -@media only screen and (min-width: 1140px) { - -/* =============== - Maximal Width - =============== */ - - .wrapper { - width: 1026px; /* 1140px - 10% for margins */ - margin: 0 auto; - } -} - -/* ========================================================================== - Helper classes - ========================================================================== */ - -.ir { - background-color: transparent; - border: 0; - overflow: hidden; - *text-indent: -9999px; -} - -.ir:before { - content: ""; - display: block; - width: 0; - height: 100%; -} - -.hidden { - display: none !important; - visibility: hidden; -} - -.visuallyhidden { - border: 0; - clip: rect(0 0 0 0); - height: 1px; - margin: -1px; - overflow: hidden; - padding: 0; - position: absolute; - width: 1px; -} - -.visuallyhidden.focusable:active, -.visuallyhidden.focusable:focus { - clip: auto; - height: auto; - margin: 0; - overflow: visible; - position: static; - width: auto; -} - -.invisible { - visibility: hidden; -} - -.clearfix:before, -.clearfix:after { - content: " "; - display: table; -} - -.clearfix:after { - clear: both; -} - -.clearfix { - *zoom: 1; -} - -/* ========================================================================== - Print styles - ========================================================================== */ - -@media print { - * { - background: transparent !important; - color: #000 !important; /* Black prints faster: h5bp.com/s */ - box-shadow:none !important; - text-shadow: none !important; - } - - a, - a:visited { - text-decoration: underline; - } - - a[href]:after { - content: " (" attr(href) ")"; - } - - abbr[title]:after { - content: " (" attr(title) ")"; - } - - /* - * Don't show links for images, or javascript/internal links - */ - - .ir a:after, - a[href^="javascript:"]:after, - a[href^="#"]:after { - content: ""; - } - - pre, - blockquote { - border: 1px solid #999; - page-break-inside: avoid; - } - - thead { - display: table-header-group; /* h5bp.com/t */ - } - - tr, - img { - page-break-inside: avoid; - } - - img { - max-width: 100% !important; - } - - @page { - margin: 0.5cm; - } - - p, - h2, - h3 { - orphans: 3; - widows: 3; - } - - h2, - h3 { - page-break-after: avoid; - } -} \ No newline at end of file diff --git a/togetherjs/tests/doctestjs/.resources/boilerplate/css/normalize.css b/togetherjs/tests/doctestjs/.resources/boilerplate/css/normalize.css deleted file mode 100755 index d4210aac2..000000000 --- a/togetherjs/tests/doctestjs/.resources/boilerplate/css/normalize.css +++ /dev/null @@ -1,504 +0,0 @@ -/*! normalize.css v1.0.1 | MIT License | git.io/normalize */ - -/* ========================================================================== - HTML5 display definitions - ========================================================================== */ - -/* - * Corrects `block` display not defined in IE 6/7/8/9 and Firefox 3. - */ - -article, -aside, -details, -figcaption, -figure, -footer, -header, -hgroup, -nav, -section, -summary { - display: block; -} - -/* - * Corrects `inline-block` display not defined in IE 6/7/8/9 and Firefox 3. - */ - -audio, -canvas, -video { - display: inline-block; - *display: inline; - *zoom: 1; -} - -/* - * Prevents modern browsers from displaying `audio` without controls. - * Remove excess height in iOS 5 devices. - */ - -audio:not([controls]) { - display: none; - height: 0; -} - -/* - * Addresses styling for `hidden` attribute not present in IE 7/8/9, Firefox 3, - * and Safari 4. - * Known issue: no IE 6 support. - */ - -[hidden] { - display: none; -} - -/* ========================================================================== - Base - ========================================================================== */ - -/* - * 1. Corrects text resizing oddly in IE 6/7 when body `font-size` is set using - * `em` units. - * 2. Prevents iOS text size adjust after orientation change, without disabling - * user zoom. - */ - -html { - font-size: 100%; /* 1 */ - -webkit-text-size-adjust: 100%; /* 2 */ - -ms-text-size-adjust: 100%; /* 2 */ -} - -/* - * Addresses `font-family` inconsistency between `textarea` and other form - * elements. - */ - -html, -button, -input, -select, -textarea { - font-family: sans-serif; -} - -/* - * Addresses margins handled incorrectly in IE 6/7. - */ - -body { - margin: 0; -} - -/* ========================================================================== - Links - ========================================================================== */ - -/* - * Addresses `outline` inconsistency between Chrome and other browsers. - */ - -a:focus { - outline: thin dotted; -} - -/* - * Improves readability when focused and also mouse hovered in all browsers. - */ - -a:active, -a:hover { - outline: 0; -} - -/* ========================================================================== - Typography - ========================================================================== */ - -/* - * Addresses font sizes and margins set differently in IE 6/7. - * Addresses font sizes within `section` and `article` in Firefox 4+, Safari 5, - * and Chrome. - */ - -h1 { - font-size: 2em; - margin: 0.67em 0; -} - -h2 { - font-size: 1.5em; - margin: 0.83em 0; -} - -h3 { - font-size: 1.17em; - margin: 1em 0; -} - -h4 { - font-size: 1em; - margin: 1.33em 0; -} - -h5 { - font-size: 0.83em; - margin: 1.67em 0; -} - -h6 { - font-size: 0.75em; - margin: 2.33em 0; -} - -/* - * Addresses styling not present in IE 7/8/9, Safari 5, and Chrome. - */ - -abbr[title] { - border-bottom: 1px dotted; -} - -/* - * Addresses style set to `bolder` in Firefox 3+, Safari 4/5, and Chrome. - */ - -b, -strong { - font-weight: bold; -} - -blockquote { - margin: 1em 40px; -} - -/* - * Addresses styling not present in Safari 5 and Chrome. - */ - -dfn { - font-style: italic; -} - -/* - * Addresses styling not present in IE 6/7/8/9. - */ - -mark { - background: #ff0; - color: #000; -} - -/* - * Addresses margins set differently in IE 6/7. - */ - -p, -pre { - margin: 1em 0; -} - -/* - * Corrects font family set oddly in IE 6, Safari 4/5, and Chrome. - */ - -code, -kbd, -pre, -samp { - font-family: monospace, serif; - _font-family: 'courier new', monospace; - font-size: 1em; -} - -/* - * Improves readability of pre-formatted text in all browsers. - */ - -pre { - white-space: pre; - white-space: pre-wrap; - word-wrap: break-word; -} - -/* - * Addresses CSS quotes not supported in IE 6/7. - */ - -q { - quotes: none; -} - -/* - * Addresses `quotes` property not supported in Safari 4. - */ - -q:before, -q:after { - content: ''; - content: none; -} - -/* - * Addresses inconsistent and variable font size in all browsers. - */ - -small { - font-size: 80%; -} - -/* - * Prevents `sub` and `sup` affecting `line-height` in all browsers. - */ - -sub, -sup { - font-size: 75%; - line-height: 0; - position: relative; - vertical-align: baseline; -} - -sup { - top: -0.5em; -} - -sub { - bottom: -0.25em; -} - -/* ========================================================================== - Lists - ========================================================================== */ - -/* - * Addresses margins set differently in IE 6/7. - */ - -dl, -menu, -ol, -ul { - margin: 1em 0; -} - -dd { - margin: 0 0 0 40px; -} - -/* - * Addresses paddings set differently in IE 6/7. - */ - -menu, -ol, -ul { - padding: 0 0 0 40px; -} - -/* - * Corrects list images handled incorrectly in IE 7. - */ - -nav ul, -nav ol { - list-style: none; - list-style-image: none; -} - -/* ========================================================================== - Embedded content - ========================================================================== */ - -/* - * 1. Removes border when inside `a` element in IE 6/7/8/9 and Firefox 3. - * 2. Improves image quality when scaled in IE 7. - */ - -img { - border: 0; /* 1 */ - -ms-interpolation-mode: bicubic; /* 2 */ -} - -/* - * Corrects overflow displayed oddly in IE 9. - */ - -svg:not(:root) { - overflow: hidden; -} - -/* ========================================================================== - Figures - ========================================================================== */ - -/* - * Addresses margin not present in IE 6/7/8/9, Safari 5, and Opera 11. - */ - -figure { - margin: 0; -} - -/* ========================================================================== - Forms - ========================================================================== */ - -/* - * Corrects margin displayed oddly in IE 6/7. - */ - -form { - margin: 0; -} - -/* - * Define consistent border, margin, and padding. - */ - -fieldset { - border: 1px solid #c0c0c0; - margin: 0 2px; - padding: 0.35em 0.625em 0.75em; -} - -/* - * 1. Corrects color not being inherited in IE 6/7/8/9. - * 2. Corrects text not wrapping in Firefox 3. - * 3. Corrects alignment displayed oddly in IE 6/7. - */ - -legend { - border: 0; /* 1 */ - padding: 0; - white-space: normal; /* 2 */ - *margin-left: -7px; /* 3 */ -} - -/* - * 1. Corrects font size not being inherited in all browsers. - * 2. Addresses margins set differently in IE 6/7, Firefox 3+, Safari 5, - * and Chrome. - * 3. Improves appearance and consistency in all browsers. - */ - -button, -input, -select, -textarea { - font-size: 100%; /* 1 */ - margin: 0; /* 2 */ - vertical-align: baseline; /* 3 */ - *vertical-align: middle; /* 3 */ -} - -/* - * Addresses Firefox 3+ setting `line-height` on `input` using `!important` in - * the UA stylesheet. - */ - -button, -input { - line-height: normal; -} - -/* - * 1. Avoid the WebKit bug in Android 4.0.* where (2) destroys native `audio` - * and `video` controls. - * 2. Corrects inability to style clickable `input` types in iOS. - * 3. Improves usability and consistency of cursor style between image-type - * `input` and others. - * 4. Removes inner spacing in IE 7 without affecting normal text inputs. - * Known issue: inner spacing remains in IE 6. - */ - -button, -html input[type="button"], /* 1 */ -input[type="reset"], -input[type="submit"] { - -webkit-appearance: button; /* 2 */ - cursor: pointer; /* 3 */ - *overflow: visible; /* 4 */ -} - -/* - * Re-set default cursor for disabled elements. - */ - -button[disabled], -input[disabled] { - cursor: default; -} - -/* - * 1. Addresses box sizing set to content-box in IE 8/9. - * 2. Removes excess padding in IE 8/9. - * 3. Removes excess padding in IE 7. - * Known issue: excess padding remains in IE 6. - */ - -input[type="checkbox"], -input[type="radio"] { - box-sizing: border-box; /* 1 */ - padding: 0; /* 2 */ - *height: 13px; /* 3 */ - *width: 13px; /* 3 */ -} - -/* - * 1. Addresses `appearance` set to `searchfield` in Safari 5 and Chrome. - * 2. Addresses `box-sizing` set to `border-box` in Safari 5 and Chrome - * (include `-moz` to future-proof). - */ - -input[type="search"] { - -webkit-appearance: textfield; /* 1 */ - -moz-box-sizing: content-box; - -webkit-box-sizing: content-box; /* 2 */ - box-sizing: content-box; -} - -/* - * Removes inner padding and search cancel button in Safari 5 and Chrome - * on OS X. - */ - -input[type="search"]::-webkit-search-cancel-button, -input[type="search"]::-webkit-search-decoration { - -webkit-appearance: none; -} - -/* - * Removes inner padding and border in Firefox 3+. - */ - -button::-moz-focus-inner, -input::-moz-focus-inner { - border: 0; - padding: 0; -} - -/* - * 1. Removes default vertical scrollbar in IE 6/7/8/9. - * 2. Improves readability and alignment in all browsers. - */ - -textarea { - overflow: auto; /* 1 */ - vertical-align: top; /* 2 */ -} - -/* ========================================================================== - Tables - ========================================================================== */ - -/* - * Remove most spacing between table cells. - */ - -table { - border-collapse: collapse; - border-spacing: 0; -} diff --git a/togetherjs/tests/doctestjs/.resources/boilerplate/css/normalize.min.css b/togetherjs/tests/doctestjs/.resources/boilerplate/css/normalize.min.css deleted file mode 100755 index a783c5307..000000000 --- a/togetherjs/tests/doctestjs/.resources/boilerplate/css/normalize.min.css +++ /dev/null @@ -1,50 +0,0 @@ -/*! normalize.css v1.0.1 | MIT License | git.io/normalize */ -article,aside,details,figcaption,figure,footer,header,hgroup,nav,section,summary{display:block} -audio,canvas,video{display:inline-block;*display:inline;*zoom:1} -audio:not([controls]){display:none;height:0} -[hidden]{display:none} -html{font-size:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%} -html,button,input,select,textarea{font-family:sans-serif} -body{margin:0} -a:focus{outline:thin dotted} -a:active,a:hover{outline:0} -h1{font-size:2em;margin:.67em 0} -h2{font-size:1.5em;margin:.83em 0} -h3{font-size:1.17em;margin:1em 0} -h4{font-size:1em;margin:1.33em 0} -h5{font-size:.83em;margin:1.67em 0} -h6{font-size:.75em;margin:2.33em 0} -abbr[title]{border-bottom:1px dotted} -b,strong{font-weight:bold} -blockquote{margin:1em 40px} -dfn{font-style:italic} -mark{background:#ff0;color:#000} -p,pre{margin:1em 0} -code,kbd,pre,samp{font-family:monospace,serif;_font-family:'courier new',monospace;font-size:1em} -pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word} -q{quotes:none} -q:before,q:after{content:'';content:none} -small{font-size:80%} -sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline} -sup{top:-0.5em} -sub{bottom:-0.25em} -dl,menu,ol,ul{margin:1em 0} -dd{margin:0 0 0 40px} -menu,ol,ul{padding:0 0 0 40px} -nav ul,nav ol{list-style:none;list-style-image:none} -img{border:0;-ms-interpolation-mode:bicubic} -svg:not(:root){overflow:hidden} -figure{margin:0} -form{margin:0} -fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:.35em .625em .75em} -legend{border:0;padding:0;white-space:normal;*margin-left:-7px} -button,input,select,textarea{font-size:100%;margin:0;vertical-align:baseline;*vertical-align:middle} -button,input{line-height:normal} -button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;*overflow:visible} -button[disabled],input[disabled]{cursor:default} -input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;*height:13px;*width:13px} -input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box} -input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none} -button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0} -textarea{overflow:auto;vertical-align:top} -table{border-collapse:collapse;border-spacing:0} \ No newline at end of file diff --git a/togetherjs/tests/doctestjs/.resources/boilerplate/favicon.ico b/togetherjs/tests/doctestjs/.resources/boilerplate/favicon.ico deleted file mode 100755 index 1f2a99867..000000000 Binary files a/togetherjs/tests/doctestjs/.resources/boilerplate/favicon.ico and /dev/null differ diff --git a/togetherjs/tests/doctestjs/.resources/boilerplate/index.html b/togetherjs/tests/doctestjs/.resources/boilerplate/index.html deleted file mode 100755 index 05268f657..000000000 --- a/togetherjs/tests/doctestjs/.resources/boilerplate/index.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - -
-
-

h1.title

- -
-
- -
-
- -
-
-

article header h1

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam sodales urna non odio egestas tempor. Nunc vel vehicula ante. Etiam bibendum iaculis libero, eget molestie nisl pharetra in. In semper consequat est, eu porta velit mollis nec.

-
-
-

article section h2

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam sodales urna non odio egestas tempor. Nunc vel vehicula ante. Etiam bibendum iaculis libero, eget molestie nisl pharetra in. In semper consequat est, eu porta velit mollis nec. Curabitur posuere enim eget turpis feugiat tempor. Etiam ullamcorper lorem dapibus velit suscipit ultrices. Proin in est sed erat facilisis pharetra.

-
-
-

article section h2

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam sodales urna non odio egestas tempor. Nunc vel vehicula ante. Etiam bibendum iaculis libero, eget molestie nisl pharetra in. In semper consequat est, eu porta velit mollis nec. Curabitur posuere enim eget turpis feugiat tempor. Etiam ullamcorper lorem dapibus velit suscipit ultrices. Proin in est sed erat facilisis pharetra.

-
-
-

article footer h3

-

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aliquam sodales urna non odio egestas tempor. Nunc vel vehicula ante. Etiam bibendum iaculis libero, eget molestie nisl pharetra in. In semper consequat est, eu porta velit mollis nec. Curabitur posuere enim eget turpis feugiat tempor.

-
-
- - - -
-
- - - - - - - - - - - diff --git a/togetherjs/tests/doctestjs/.resources/boilerplate/js/main.js b/togetherjs/tests/doctestjs/.resources/boilerplate/js/main.js deleted file mode 100755 index 8b1378917..000000000 --- a/togetherjs/tests/doctestjs/.resources/boilerplate/js/main.js +++ /dev/null @@ -1 +0,0 @@ - diff --git a/togetherjs/tests/doctestjs/.resources/boilerplate/js/vendor/jquery-1.8.1.min.js b/togetherjs/tests/doctestjs/.resources/boilerplate/js/vendor/jquery-1.8.1.min.js deleted file mode 100755 index e7f2a292b..000000000 --- a/togetherjs/tests/doctestjs/.resources/boilerplate/js/vendor/jquery-1.8.1.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v@1.8.1 jquery.com | jquery.org/license */ -(function(a,b){function G(a){var b=F[a]={};return p.each(a.split(s),function(a,c){b[c]=!0}),b}function J(a,c,d){if(d===b&&a.nodeType===1){var e="data-"+c.replace(I,"-$1").toLowerCase();d=a.getAttribute(e);if(typeof d=="string"){try{d=d==="true"?!0:d==="false"?!1:d==="null"?null:+d+""===d?+d:H.test(d)?p.parseJSON(d):d}catch(f){}p.data(a,c,d)}else d=b}return d}function K(a){var b;for(b in a){if(b==="data"&&p.isEmptyObject(a[b]))continue;if(b!=="toJSON")return!1}return!0}function ba(){return!1}function bb(){return!0}function bh(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function bi(a,b){do a=a[b];while(a&&a.nodeType!==1);return a}function bj(a,b,c){b=b||0;if(p.isFunction(b))return p.grep(a,function(a,d){var e=!!b.call(a,d,a);return e===c});if(b.nodeType)return p.grep(a,function(a,d){return a===b===c});if(typeof b=="string"){var d=p.grep(a,function(a){return a.nodeType===1});if(be.test(b))return p.filter(b,d,!c);b=p.filter(b,d)}return p.grep(a,function(a,d){return p.inArray(a,b)>=0===c})}function bk(a){var b=bl.split("|"),c=a.createDocumentFragment();if(c.createElement)while(b.length)c.createElement(b.pop());return c}function bC(a,b){return a.getElementsByTagName(b)[0]||a.appendChild(a.ownerDocument.createElement(b))}function bD(a,b){if(b.nodeType!==1||!p.hasData(a))return;var c,d,e,f=p._data(a),g=p._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;d").appendTo(e.body),c=b.css("display");b.remove();if(c==="none"||c===""){bI=e.body.appendChild(bI||p.extend(e.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!bJ||!bI.createElement)bJ=(bI.contentWindow||bI.contentDocument).document,bJ.write(""),bJ.close();b=bJ.body.appendChild(bJ.createElement(a)),c=bH(b,"display"),e.body.removeChild(bI)}return bS[a]=c,c}function ci(a,b,c,d){var e;if(p.isArray(b))p.each(b,function(b,e){c||ce.test(a)?d(a,e):ci(a+"["+(typeof e=="object"?b:"")+"]",e,c,d)});else if(!c&&p.type(b)==="object")for(e in b)ci(a+"["+e+"]",b[e],c,d);else d(a,b)}function cz(a){return function(b,c){typeof b!="string"&&(c=b,b="*");var d,e,f,g=b.toLowerCase().split(s),h=0,i=g.length;if(p.isFunction(c))for(;h)[^>]*$|#([\w\-]*)$)/,v=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,w=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,y=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,z=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,A=/^-ms-/,B=/-([\da-z])/gi,C=function(a,b){return(b+"").toUpperCase()},D=function(){e.addEventListener?(e.removeEventListener("DOMContentLoaded",D,!1),p.ready()):e.readyState==="complete"&&(e.detachEvent("onreadystatechange",D),p.ready())},E={};p.fn=p.prototype={constructor:p,init:function(a,c,d){var f,g,h,i;if(!a)return this;if(a.nodeType)return this.context=this[0]=a,this.length=1,this;if(typeof a=="string"){a.charAt(0)==="<"&&a.charAt(a.length-1)===">"&&a.length>=3?f=[null,a,null]:f=u.exec(a);if(f&&(f[1]||!c)){if(f[1])return c=c instanceof p?c[0]:c,i=c&&c.nodeType?c.ownerDocument||c:e,a=p.parseHTML(f[1],i,!0),v.test(f[1])&&p.isPlainObject(c)&&this.attr.call(a,c,!0),p.merge(this,a);g=e.getElementById(f[2]);if(g&&g.parentNode){if(g.id!==f[2])return d.find(a);this.length=1,this[0]=g}return this.context=e,this.selector=a,this}return!c||c.jquery?(c||d).find(a):this.constructor(c).find(a)}return p.isFunction(a)?d.ready(a):(a.selector!==b&&(this.selector=a.selector,this.context=a.context),p.makeArray(a,this))},selector:"",jquery:"1.8.1",length:0,size:function(){return this.length},toArray:function(){return k.call(this)},get:function(a){return a==null?this.toArray():a<0?this[this.length+a]:this[a]},pushStack:function(a,b,c){var d=p.merge(this.constructor(),a);return d.prevObject=this,d.context=this.context,b==="find"?d.selector=this.selector+(this.selector?" ":"")+c:b&&(d.selector=this.selector+"."+b+"("+c+")"),d},each:function(a,b){return p.each(this,a,b)},ready:function(a){return p.ready.promise().done(a),this},eq:function(a){return a=+a,a===-1?this.slice(a):this.slice(a,a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(k.apply(this,arguments),"slice",k.call(arguments).join(","))},map:function(a){return this.pushStack(p.map(this,function(b,c){return a.call(b,c,b)}))},end:function(){return this.prevObject||this.constructor(null)},push:j,sort:[].sort,splice:[].splice},p.fn.init.prototype=p.fn,p.extend=p.fn.extend=function(){var a,c,d,e,f,g,h=arguments[0]||{},i=1,j=arguments.length,k=!1;typeof h=="boolean"&&(k=h,h=arguments[1]||{},i=2),typeof h!="object"&&!p.isFunction(h)&&(h={}),j===i&&(h=this,--i);for(;i0)return;d.resolveWith(e,[p]),p.fn.trigger&&p(e).trigger("ready").off("ready")},isFunction:function(a){return p.type(a)==="function"},isArray:Array.isArray||function(a){return p.type(a)==="array"},isWindow:function(a){return a!=null&&a==a.window},isNumeric:function(a){return!isNaN(parseFloat(a))&&isFinite(a)},type:function(a){return a==null?String(a):E[m.call(a)]||"object"},isPlainObject:function(a){if(!a||p.type(a)!=="object"||a.nodeType||p.isWindow(a))return!1;try{if(a.constructor&&!n.call(a,"constructor")&&!n.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}var d;for(d in a);return d===b||n.call(a,d)},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},error:function(a){throw new Error(a)},parseHTML:function(a,b,c){var d;return!a||typeof a!="string"?null:(typeof b=="boolean"&&(c=b,b=0),b=b||e,(d=v.exec(a))?[b.createElement(d[1])]:(d=p.buildFragment([a],b,c?null:[]),p.merge([],(d.cacheable?p.clone(d.fragment):d.fragment).childNodes)))},parseJSON:function(b){if(!b||typeof b!="string")return null;b=p.trim(b);if(a.JSON&&a.JSON.parse)return a.JSON.parse(b);if(w.test(b.replace(y,"@").replace(z,"]").replace(x,"")))return(new Function("return "+b))();p.error("Invalid JSON: "+b)},parseXML:function(c){var d,e;if(!c||typeof c!="string")return null;try{a.DOMParser?(e=new DOMParser,d=e.parseFromString(c,"text/xml")):(d=new ActiveXObject("Microsoft.XMLDOM"),d.async="false",d.loadXML(c))}catch(f){d=b}return(!d||!d.documentElement||d.getElementsByTagName("parsererror").length)&&p.error("Invalid XML: "+c),d},noop:function(){},globalEval:function(b){b&&r.test(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(A,"ms-").replace(B,C)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,c,d){var e,f=0,g=a.length,h=g===b||p.isFunction(a);if(d){if(h){for(e in a)if(c.apply(a[e],d)===!1)break}else for(;f0&&a[0]&&a[i-1]||i===0||p.isArray(a));if(j)for(;h-1)i.splice(c,1),e&&(c<=g&&g--,c<=h&&h--)}),this},has:function(a){return p.inArray(a,i)>-1},empty:function(){return i=[],this},disable:function(){return i=j=c=b,this},disabled:function(){return!i},lock:function(){return j=b,c||l.disable(),this},locked:function(){return!j},fireWith:function(a,b){return b=b||[],b=[a,b.slice?b.slice():b],i&&(!d||j)&&(e?j.push(b):k(b)),this},fire:function(){return l.fireWith(this,arguments),this},fired:function(){return!!d}};return l},p.extend({Deferred:function(a){var b=[["resolve","done",p.Callbacks("once memory"),"resolved"],["reject","fail",p.Callbacks("once memory"),"rejected"],["notify","progress",p.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return p.Deferred(function(c){p.each(b,function(b,d){var f=d[0],g=a[b];e[d[1]](p.isFunction(g)?function(){var a=g.apply(this,arguments);a&&p.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f+"With"](this===e?c:this,[a])}:c[f])}),a=null}).promise()},promise:function(a){return typeof a=="object"?p.extend(a,d):d}},e={};return d.pipe=d.then,p.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[a^1][2].disable,b[2][2].lock),e[f[0]]=g.fire,e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=k.call(arguments),d=c.length,e=d!==1||a&&p.isFunction(a.promise)?d:0,f=e===1?a:p.Deferred(),g=function(a,b,c){return function(d){b[a]=this,c[a]=arguments.length>1?k.call(arguments):d,c===h?f.notifyWith(b,c):--e||f.resolveWith(b,c)}},h,i,j;if(d>1){h=new Array(d),i=new Array(d),j=new Array(d);for(;b
a",c=n.getElementsByTagName("*"),d=n.getElementsByTagName("a")[0],d.style.cssText="top:1px;float:left;opacity:.5";if(!c||!c.length||!d)return{};f=e.createElement("select"),g=f.appendChild(e.createElement("option")),h=n.getElementsByTagName("input")[0],b={leadingWhitespace:n.firstChild.nodeType===3,tbody:!n.getElementsByTagName("tbody").length,htmlSerialize:!!n.getElementsByTagName("link").length,style:/top/.test(d.getAttribute("style")),hrefNormalized:d.getAttribute("href")==="/a",opacity:/^0.5/.test(d.style.opacity),cssFloat:!!d.style.cssFloat,checkOn:h.value==="on",optSelected:g.selected,getSetAttribute:n.className!=="t",enctype:!!e.createElement("form").enctype,html5Clone:e.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:e.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},h.checked=!0,b.noCloneChecked=h.cloneNode(!0).checked,f.disabled=!0,b.optDisabled=!g.disabled;try{delete n.test}catch(o){b.deleteExpando=!1}!n.addEventListener&&n.attachEvent&&n.fireEvent&&(n.attachEvent("onclick",m=function(){b.noCloneEvent=!1}),n.cloneNode(!0).fireEvent("onclick"),n.detachEvent("onclick",m)),h=e.createElement("input"),h.value="t",h.setAttribute("type","radio"),b.radioValue=h.value==="t",h.setAttribute("checked","checked"),h.setAttribute("name","t"),n.appendChild(h),i=e.createDocumentFragment(),i.appendChild(n.lastChild),b.checkClone=i.cloneNode(!0).cloneNode(!0).lastChild.checked,b.appendChecked=h.checked,i.removeChild(h),i.appendChild(n);if(n.attachEvent)for(k in{submit:!0,change:!0,focusin:!0})j="on"+k,l=j in n,l||(n.setAttribute(j,"return;"),l=typeof n[j]=="function"),b[k+"Bubbles"]=l;return p(function(){var c,d,f,g,h="padding:0;margin:0;border:0;display:block;overflow:hidden;",i=e.getElementsByTagName("body")[0];if(!i)return;c=e.createElement("div"),c.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",i.insertBefore(c,i.firstChild),d=e.createElement("div"),c.appendChild(d),d.innerHTML="
t
",f=d.getElementsByTagName("td"),f[0].style.cssText="padding:0;margin:0;border:0;display:none",l=f[0].offsetHeight===0,f[0].style.display="",f[1].style.display="none",b.reliableHiddenOffsets=l&&f[0].offsetHeight===0,d.innerHTML="",d.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",b.boxSizing=d.offsetWidth===4,b.doesNotIncludeMarginInBodyOffset=i.offsetTop!==1,a.getComputedStyle&&(b.pixelPosition=(a.getComputedStyle(d,null)||{}).top!=="1%",b.boxSizingReliable=(a.getComputedStyle(d,null)||{width:"4px"}).width==="4px",g=e.createElement("div"),g.style.cssText=d.style.cssText=h,g.style.marginRight=g.style.width="0",d.style.width="1px",d.appendChild(g),b.reliableMarginRight=!parseFloat((a.getComputedStyle(g,null)||{}).marginRight)),typeof d.style.zoom!="undefined"&&(d.innerHTML="",d.style.cssText=h+"width:1px;padding:1px;display:inline;zoom:1",b.inlineBlockNeedsLayout=d.offsetWidth===3,d.style.display="block",d.style.overflow="visible",d.innerHTML="
",d.firstChild.style.width="5px",b.shrinkWrapBlocks=d.offsetWidth!==3,c.style.zoom=1),i.removeChild(c),c=d=f=g=null}),i.removeChild(n),c=d=f=g=h=i=n=null,b}();var H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,I=/([A-Z])/g;p.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(p.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(a){return a=a.nodeType?p.cache[a[p.expando]]:a[p.expando],!!a&&!K(a)},data:function(a,c,d,e){if(!p.acceptData(a))return;var f,g,h=p.expando,i=typeof c=="string",j=a.nodeType,k=j?p.cache:a,l=j?a[h]:a[h]&&h;if((!l||!k[l]||!e&&!k[l].data)&&i&&d===b)return;l||(j?a[h]=l=p.deletedIds.pop()||++p.uuid:l=h),k[l]||(k[l]={},j||(k[l].toJSON=p.noop));if(typeof c=="object"||typeof c=="function")e?k[l]=p.extend(k[l],c):k[l].data=p.extend(k[l].data,c);return f=k[l],e||(f.data||(f.data={}),f=f.data),d!==b&&(f[p.camelCase(c)]=d),i?(g=f[c],g==null&&(g=f[p.camelCase(c)])):g=f,g},removeData:function(a,b,c){if(!p.acceptData(a))return;var d,e,f,g=a.nodeType,h=g?p.cache:a,i=g?a[p.expando]:p.expando;if(!h[i])return;if(b){d=c?h[i]:h[i].data;if(d){p.isArray(b)||(b in d?b=[b]:(b=p.camelCase(b),b in d?b=[b]:b=b.split(" ")));for(e=0,f=b.length;e1,null,!1))},removeData:function(a){return this.each(function(){p.removeData(this,a)})}}),p.extend({queue:function(a,b,c){var d;if(a)return b=(b||"fx")+"queue",d=p._data(a,b),c&&(!d||p.isArray(c)?d=p._data(a,b,p.makeArray(c)):d.push(c)),d||[]},dequeue:function(a,b){b=b||"fx";var c=p.queue(a,b),d=c.length,e=c.shift(),f=p._queueHooks(a,b),g=function(){p.dequeue(a,b)};e==="inprogress"&&(e=c.shift(),d--),e&&(b==="fx"&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return p._data(a,c)||p._data(a,c,{empty:p.Callbacks("once memory").add(function(){p.removeData(a,b+"queue",!0),p.removeData(a,c,!0)})})}}),p.fn.extend({queue:function(a,c){var d=2;return typeof a!="string"&&(c=a,a="fx",d--),arguments.length1)},removeAttr:function(a){return this.each(function(){p.removeAttr(this,a)})},prop:function(a,b){return p.access(this,p.prop,a,b,arguments.length>1)},removeProp:function(a){return a=p.propFix[a]||a,this.each(function(){try{this[a]=b,delete this[a]}catch(c){}})},addClass:function(a){var b,c,d,e,f,g,h;if(p.isFunction(a))return this.each(function(b){p(this).addClass(a.call(this,b,this.className))});if(a&&typeof a=="string"){b=a.split(s);for(c=0,d=this.length;c-1)d=d.replace(" "+c[f]+" "," ");e.className=a?p.trim(d):""}}}return this},toggleClass:function(a,b){var c=typeof a,d=typeof b=="boolean";return p.isFunction(a)?this.each(function(c){p(this).toggleClass(a.call(this,c,this.className,b),b)}):this.each(function(){if(c==="string"){var e,f=0,g=p(this),h=b,i=a.split(s);while(e=i[f++])h=d?h:!g.hasClass(e),g[h?"addClass":"removeClass"](e)}else if(c==="undefined"||c==="boolean")this.className&&p._data(this,"__className__",this.className),this.className=this.className||a===!1?"":p._data(this,"__className__")||""})},hasClass:function(a){var b=" "+a+" ",c=0,d=this.length;for(;c-1)return!0;return!1},val:function(a){var c,d,e,f=this[0];if(!arguments.length){if(f)return c=p.valHooks[f.type]||p.valHooks[f.nodeName.toLowerCase()],c&&"get"in c&&(d=c.get(f,"value"))!==b?d:(d=f.value,typeof d=="string"?d.replace(P,""):d==null?"":d);return}return e=p.isFunction(a),this.each(function(d){var f,g=p(this);if(this.nodeType!==1)return;e?f=a.call(this,d,g.val()):f=a,f==null?f="":typeof f=="number"?f+="":p.isArray(f)&&(f=p.map(f,function(a){return a==null?"":a+""})),c=p.valHooks[this.type]||p.valHooks[this.nodeName.toLowerCase()];if(!c||!("set"in c)||c.set(this,f,"value")===b)this.value=f})}}),p.extend({valHooks:{option:{get:function(a){var b=a.attributes.value;return!b||b.specified?a.value:a.text}},select:{get:function(a){var b,c,d,e,f=a.selectedIndex,g=[],h=a.options,i=a.type==="select-one";if(f<0)return null;c=i?f:0,d=i?f+1:h.length;for(;c=0}),c.length||(a.selectedIndex=-1),c}}},attrFn:{},attr:function(a,c,d,e){var f,g,h,i=a.nodeType;if(!a||i===3||i===8||i===2)return;if(e&&p.isFunction(p.fn[c]))return p(a)[c](d);if(typeof a.getAttribute=="undefined")return p.prop(a,c,d);h=i!==1||!p.isXMLDoc(a),h&&(c=c.toLowerCase(),g=p.attrHooks[c]||(T.test(c)?M:L));if(d!==b){if(d===null){p.removeAttr(a,c);return}return g&&"set"in g&&h&&(f=g.set(a,d,c))!==b?f:(a.setAttribute(c,""+d),d)}return g&&"get"in g&&h&&(f=g.get(a,c))!==null?f:(f=a.getAttribute(c),f===null?b:f)},removeAttr:function(a,b){var c,d,e,f,g=0;if(b&&a.nodeType===1){d=b.split(s);for(;g=0}})});var V=/^(?:textarea|input|select)$/i,W=/^([^\.]*|)(?:\.(.+)|)$/,X=/(?:^|\s)hover(\.\S+|)\b/,Y=/^key/,Z=/^(?:mouse|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=function(a){return p.event.special.hover?a:a.replace(X,"mouseenter$1 mouseleave$1")};p.event={add:function(a,c,d,e,f){var g,h,i,j,k,l,m,n,o,q,r;if(a.nodeType===3||a.nodeType===8||!c||!d||!(g=p._data(a)))return;d.handler&&(o=d,d=o.handler,f=o.selector),d.guid||(d.guid=p.guid++),i=g.events,i||(g.events=i={}),h=g.handle,h||(g.handle=h=function(a){return typeof p!="undefined"&&(!a||p.event.triggered!==a.type)?p.event.dispatch.apply(h.elem,arguments):b},h.elem=a),c=p.trim(_(c)).split(" ");for(j=0;j=0&&(s=s.slice(0,-1),i=!0),s.indexOf(".")>=0&&(t=s.split("."),s=t.shift(),t.sort());if((!f||p.event.customEvent[s])&&!p.event.global[s])return;c=typeof c=="object"?c[p.expando]?c:new p.Event(s,c):new p.Event(s),c.type=s,c.isTrigger=!0,c.exclusive=i,c.namespace=t.join("."),c.namespace_re=c.namespace?new RegExp("(^|\\.)"+t.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,m=s.indexOf(":")<0?"on"+s:"";if(!f){h=p.cache;for(j in h)h[j].events&&h[j].events[s]&&p.event.trigger(c,d,h[j].handle.elem,!0);return}c.result=b,c.target||(c.target=f),d=d!=null?p.makeArray(d):[],d.unshift(c),n=p.event.special[s]||{};if(n.trigger&&n.trigger.apply(f,d)===!1)return;q=[[f,n.bindType||s]];if(!g&&!n.noBubble&&!p.isWindow(f)){r=n.delegateType||s,k=$.test(r+s)?f:f.parentNode;for(l=f;k;k=k.parentNode)q.push([k,r]),l=k;l===(f.ownerDocument||e)&&q.push([l.defaultView||l.parentWindow||a,r])}for(j=0;j=0),h[l]&&j.push(k);j.length&&t.push({elem:f,matches:j})}n.length>o&&t.push({elem:this,matches:n.slice(o)});for(d=0;d0?this.on(b,null,a,c):this.trigger(b)},Y.test(b)&&(p.event.fixHooks[b]=p.event.keyHooks),Z.test(b)&&(p.event.fixHooks[b]=p.event.mouseHooks)}),function(a,b){function $(a,b,c,d){c=c||[],b=b||q;var e,f,g,j,k=b.nodeType;if(k!==1&&k!==9)return[];if(!a||typeof a!="string")return c;g=h(b);if(!g&&!d)if(e=L.exec(a))if(j=e[1]){if(k===9){f=b.getElementById(j);if(!f||!f.parentNode)return c;if(f.id===j)return c.push(f),c}else if(b.ownerDocument&&(f=b.ownerDocument.getElementById(j))&&i(b,f)&&f.id===j)return c.push(f),c}else{if(e[2])return u.apply(c,t.call(b.getElementsByTagName(a),0)),c;if((j=e[3])&&X&&b.getElementsByClassName)return u.apply(c,t.call(b.getElementsByClassName(j),0)),c}return bk(a,b,c,d,g)}function _(a){return function(b){var c=b.nodeName.toLowerCase();return c==="input"&&b.type===a}}function ba(a){return function(b){var c=b.nodeName.toLowerCase();return(c==="input"||c==="button")&&b.type===a}}function bb(a,b,c){if(a===b)return c;var d=a.nextSibling;while(d){if(d===b)return-1;d=d.nextSibling}return 1}function bc(a,b,c,d){var e,g,h,i,j,k,l,m,n,p,r=!c&&b!==q,s=(r?"":"")+a.replace(H,"$1"),u=y[o][s];if(u)return d?0:t.call(u,0);j=a,k=[],m=0,n=f.preFilter,p=f.filter;while(j){if(!e||(g=I.exec(j)))g&&(j=j.slice(g[0].length),h.selector=l),k.push(h=[]),l="",r&&(j=" "+j);e=!1;if(g=J.exec(j))l+=g[0],j=j.slice(g[0].length),e=h.push({part:g.pop().replace(H," "),string:g[0],captures:g});for(i in p)(g=S[i].exec(j))&&(!n[i]||(g=n[i](g,b,c)))&&(l+=g[0],j=j.slice(g[0].length),e=h.push({part:i,string:g.shift(),captures:g}));if(!e)break}return l&&(h.selector=l),d?j.length:j?$.error(a):t.call(y(s,k),0)}function bd(a,b,e,f){var g=b.dir,h=s++;return a||(a=function(a){return a===e}),b.first?function(b){while(b=b[g])if(b.nodeType===1)return a(b)&&b}:f?function(b){while(b=b[g])if(b.nodeType===1&&a(b))return b}:function(b){var e,f=h+"."+c,i=f+"."+d;while(b=b[g])if(b.nodeType===1){if((e=b[o])===i)return b.sizset;if(typeof e=="string"&&e.indexOf(f)===0){if(b.sizset)return b}else{b[o]=i;if(a(b))return b.sizset=!0,b;b.sizset=!1}}}}function be(a,b){return a?function(c){var d=b(c);return d&&a(d===!0?c:d)}:b}function bf(a,b,c){var d,e,g=0;for(;d=a[g];g++)f.relative[d.part]?e=bd(e,f.relative[d.part],b,c):e=be(e,f.filter[d.part].apply(null,d.captures.concat(b,c)));return e}function bg(a){return function(b){var c,d=0;for(;c=a[d];d++)if(c(b))return!0;return!1}}function bh(a,b,c,d){var e=0,f=b.length;for(;e0?i(h,c,g):[]}function bj(a,c,d,e){var f,g,h,i,j,k,l,m,n,o,p,q,r,s=0,t=a.length,v=S.POS,w=new RegExp("^"+v.source+"(?!"+A+")","i"),x=function(){var a=1,c=arguments.length-2;for(;al){g+=k.slice(l,n.index),l=p,q=[c],J.test(g)&&(m&&(q=m),m=e);if(r=O.test(g))g=g.slice(0,-5).replace(J,"$&*"),l++;n.length>1&&n[0].replace(w,x),m=bi(g,n[1],n[2],q,m,r)}g=""}}o||(g+=k),o=!1}g?J.test(g)?bh(g,m||[c],d,e):$(g,c,d,e?e.concat(m):m):u.apply(d,m)}return t===1?d:$.uniqueSort(d)}function bk(a,b,e,g,h){a=a.replace(H,"$1");var i,k,l,m,n,o,p,q,r,s,v=bc(a,b,h),w=b.nodeType;if(S.POS.test(a))return bj(v,b,e,g);if(g)i=t.call(g,0);else if(v.length===1){if((o=t.call(v[0],0)).length>2&&(p=o[0]).part==="ID"&&w===9&&!h&&f.relative[o[1].part]){b=f.find.ID(p.captures[0].replace(R,""),b,h)[0];if(!b)return e;a=a.slice(o.shift().string.length)}r=(v=N.exec(o[0].string))&&!v.index&&b.parentNode||b,q="";for(n=o.length-1;n>=0;n--){p=o[n],s=p.part,q=p.string+q;if(f.relative[s])break;if(f.order.test(s)){i=f.find[s](p.captures[0].replace(R,""),r,h);if(i==null)continue;a=a.slice(0,a.length-q.length)+q.replace(S[s],""),a||u.apply(e,t.call(i,0));break}}}if(a){k=j(a,b,h),c=k.dirruns++,i==null&&(i=f.find.TAG("*",N.test(a)&&b.parentNode||b));for(n=0;m=i[n];n++)d=k.runs++,k(m)&&e.push(m)}return e}var c,d,e,f,g,h,i,j,k,l,m=!0,n="undefined",o=("sizcache"+Math.random()).replace(".",""),q=a.document,r=q.documentElement,s=0,t=[].slice,u=[].push,v=function(a,b){return a[o]=b||!0,a},w=function(){var a={},b=[];return v(function(c,d){return b.push(c)>f.cacheLength&&delete a[b.shift()],a[c]=d},a)},x=w(),y=w(),z=w(),A="[\\x20\\t\\r\\n\\f]",B="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",C=B.replace("w","w#"),D="([*^$|!~]?=)",E="\\["+A+"*("+B+")"+A+"*(?:"+D+A+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+C+")|)|)"+A+"*\\]",F=":("+B+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+E+")|[^:]|\\\\.)*|.*))\\)|)",G=":(nth|eq|gt|lt|first|last|even|odd)(?:\\(((?:-\\d)?\\d*)\\)|)(?=[^-]|$)",H=new RegExp("^"+A+"+|((?:^|[^\\\\])(?:\\\\.)*)"+A+"+$","g"),I=new RegExp("^"+A+"*,"+A+"*"),J=new RegExp("^"+A+"*([\\x20\\t\\r\\n\\f>+~])"+A+"*"),K=new RegExp(F),L=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,M=/^:not/,N=/[\x20\t\r\n\f]*[+~]/,O=/:not\($/,P=/h\d/i,Q=/input|select|textarea|button/i,R=/\\(?!\\)/g,S={ID:new RegExp("^#("+B+")"),CLASS:new RegExp("^\\.("+B+")"),NAME:new RegExp("^\\[name=['\"]?("+B+")['\"]?\\]"),TAG:new RegExp("^("+B.replace("w","w*")+")"),ATTR:new RegExp("^"+E),PSEUDO:new RegExp("^"+F),CHILD:new RegExp("^:(only|nth|last|first)-child(?:\\("+A+"*(even|odd|(([+-]|)(\\d*)n|)"+A+"*(?:([+-]|)"+A+"*(\\d+)|))"+A+"*\\)|)","i"),POS:new RegExp(G,"ig"),needsContext:new RegExp("^"+A+"*[>+~]|"+G,"i")},T=function(a){var b=q.createElement("div");try{return a(b)}catch(c){return!1}finally{b=null}},U=T(function(a){return a.appendChild(q.createComment("")),!a.getElementsByTagName("*").length}),V=T(function(a){return a.innerHTML="",a.firstChild&&typeof a.firstChild.getAttribute!==n&&a.firstChild.getAttribute("href")==="#"}),W=T(function(a){a.innerHTML="";var b=typeof a.lastChild.getAttribute("multiple");return b!=="boolean"&&b!=="string"}),X=T(function(a){return a.innerHTML="",!a.getElementsByClassName||!a.getElementsByClassName("e").length?!1:(a.lastChild.className="e",a.getElementsByClassName("e").length===2)}),Y=T(function(a){a.id=o+0,a.innerHTML="
",r.insertBefore(a,r.firstChild);var b=q.getElementsByName&&q.getElementsByName(o).length===2+q.getElementsByName(o+0).length;return e=!q.getElementById(o),r.removeChild(a),b});try{t.call(r.childNodes,0)[0].nodeType}catch(Z){t=function(a){var b,c=[];for(;b=this[a];a++)c.push(b);return c}}$.matches=function(a,b){return $(a,null,null,b)},$.matchesSelector=function(a,b){return $(b,null,null,[a]).length>0},g=$.getText=function(a){var b,c="",d=0,e=a.nodeType;if(e){if(e===1||e===9||e===11){if(typeof a.textContent=="string")return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=g(a)}else if(e===3||e===4)return a.nodeValue}else for(;b=a[d];d++)c+=g(b);return c},h=$.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?b.nodeName!=="HTML":!1},i=$.contains=r.contains?function(a,b){var c=a.nodeType===9?a.documentElement:a,d=b&&b.parentNode;return a===d||!!(d&&d.nodeType===1&&c.contains&&c.contains(d))}:r.compareDocumentPosition?function(a,b){return b&&!!(a.compareDocumentPosition(b)&16)}:function(a,b){while(b=b.parentNode)if(b===a)return!0;return!1},$.attr=function(a,b){var c,d=h(a);return d||(b=b.toLowerCase()),f.attrHandle[b]?f.attrHandle[b](a):W||d?a.getAttribute(b):(c=a.getAttributeNode(b),c?typeof a[b]=="boolean"?a[b]?b:null:c.specified?c.value:null:null)},f=$.selectors={cacheLength:50,createPseudo:v,match:S,order:new RegExp("ID|TAG"+(Y?"|NAME":"")+(X?"|CLASS":"")),attrHandle:V?{}:{href:function(a){return a.getAttribute("href",2)},type:function(a){return a.getAttribute("type")}},find:{ID:e?function(a,b,c){if(typeof b.getElementById!==n&&!c){var d=b.getElementById(a);return d&&d.parentNode?[d]:[]}}:function(a,c,d){if(typeof c.getElementById!==n&&!d){var e=c.getElementById(a);return e?e.id===a||typeof e.getAttributeNode!==n&&e.getAttributeNode("id").value===a?[e]:b:[]}},TAG:U?function(a,b){if(typeof b.getElementsByTagName!==n)return b.getElementsByTagName(a)}:function(a,b){var c=b.getElementsByTagName(a);if(a==="*"){var d,e=[],f=0;for(;d=c[f];f++)d.nodeType===1&&e.push(d);return e}return c},NAME:function(a,b){if(typeof b.getElementsByName!==n)return b.getElementsByName(name)},CLASS:function(a,b,c){if(typeof b.getElementsByClassName!==n&&!c)return b.getElementsByClassName(a)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(R,""),a[3]=(a[4]||a[5]||"").replace(R,""),a[2]==="~="&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),a[1]==="nth"?(a[2]||$.error(a[0]),a[3]=+(a[3]?a[4]+(a[5]||1):2*(a[2]==="even"||a[2]==="odd")),a[4]=+(a[6]+a[7]||a[2]==="odd")):a[2]&&$.error(a[0]),a},PSEUDO:function(a,b,c){var d,e;if(S.CHILD.test(a[0]))return null;if(a[3])a[2]=a[3];else if(d=a[4])K.test(d)&&(e=bc(d,b,c,!0))&&(e=d.indexOf(")",d.length-e)-d.length)&&(d=d.slice(0,e),a[0]=a[0].slice(0,e)),a[2]=d;return a.slice(0,3)}},filter:{ID:e?function(a){return a=a.replace(R,""),function(b){return b.getAttribute("id")===a}}:function(a){return a=a.replace(R,""),function(b){var c=typeof b.getAttributeNode!==n&&b.getAttributeNode("id");return c&&c.value===a}},TAG:function(a){return a==="*"?function(){return!0}:(a=a.replace(R,"").toLowerCase(),function(b){return b.nodeName&&b.nodeName.toLowerCase()===a})},CLASS:function(a){var b=x[o][a];return b||(b=x(a,new RegExp("(^|"+A+")"+a+"("+A+"|$)"))),function(a){return b.test(a.className||typeof a.getAttribute!==n&&a.getAttribute("class")||"")}},ATTR:function(a,b,c){return b?function(d){var e=$.attr(d,a),f=e+"";if(e==null)return b==="!=";switch(b){case"=":return f===c;case"!=":return f!==c;case"^=":return c&&f.indexOf(c)===0;case"*=":return c&&f.indexOf(c)>-1;case"$=":return c&&f.substr(f.length-c.length)===c;case"~=":return(" "+f+" ").indexOf(c)>-1;case"|=":return f===c||f.substr(0,c.length+1)===c+"-"}}:function(b){return $.attr(b,a)!=null}},CHILD:function(a,b,c,d){if(a==="nth"){var e=s++;return function(a){var b,f,g=0,h=a;if(c===1&&d===0)return!0;b=a.parentNode;if(b&&(b[o]!==e||!a.sizset)){for(h=b.firstChild;h;h=h.nextSibling)if(h.nodeType===1){h.sizset=++g;if(h===a)break}b[o]=e}return f=a.sizset-d,c===0?f===0:f%c===0&&f/c>=0}}return function(b){var c=b;switch(a){case"only":case"first":while(c=c.previousSibling)if(c.nodeType===1)return!1;if(a==="first")return!0;c=b;case"last":while(c=c.nextSibling)if(c.nodeType===1)return!1;return!0}}},PSEUDO:function(a,b,c,d){var e,g=f.pseudos[a]||f.pseudos[a.toLowerCase()];return g||$.error("unsupported pseudo: "+a),g[o]?g(b,c,d):g.length>1?(e=[a,a,"",b],function(a){return g(a,0,e)}):g}},pseudos:{not:v(function(a,b,c){var d=j(a.replace(H,"$1"),b,c);return function(a){return!d(a)}}),enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&!!a.checked||b==="option"&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},parent:function(a){return!f.pseudos.empty(a)},empty:function(a){var b;a=a.firstChild;while(a){if(a.nodeName>"@"||(b=a.nodeType)===3||b===4)return!1;a=a.nextSibling}return!0},contains:v(function(a){return function(b){return(b.textContent||b.innerText||g(b)).indexOf(a)>-1}}),has:v(function(a){return function(b){return $(a,b).length>0}}),header:function(a){return P.test(a.nodeName)},text:function(a){var b,c;return a.nodeName.toLowerCase()==="input"&&(b=a.type)==="text"&&((c=a.getAttribute("type"))==null||c.toLowerCase()===b)},radio:_("radio"),checkbox:_("checkbox"),file:_("file"),password:_("password"),image:_("image"),submit:ba("submit"),reset:ba("reset"),button:function(a){var b=a.nodeName.toLowerCase();return b==="input"&&a.type==="button"||b==="button"},input:function(a){return Q.test(a.nodeName)},focus:function(a){var b=a.ownerDocument;return a===b.activeElement&&(!b.hasFocus||b.hasFocus())&&(!!a.type||!!a.href)},active:function(a){return a===a.ownerDocument.activeElement}},setFilters:{first:function(a,b,c){return c?a.slice(1):[a[0]]},last:function(a,b,c){var d=a.pop();return c?a:[d]},even:function(a,b,c){var d=[],e=c?1:0,f=a.length;for(;e",a.querySelectorAll("[selected]").length||e.push("\\["+A+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),a.querySelectorAll(":checked").length||e.push(":checked")}),T(function(a){a.innerHTML="

",a.querySelectorAll("[test^='']").length&&e.push("[*^$]="+A+"*(?:\"\"|'')"),a.innerHTML="",a.querySelectorAll(":enabled").length||e.push(":enabled",":disabled")}),e=e.length&&new RegExp(e.join("|")),bk=function(a,d,f,g,h){if(!g&&!h&&(!e||!e.test(a)))if(d.nodeType===9)try{return u.apply(f,t.call(d.querySelectorAll(a),0)),f}catch(i){}else if(d.nodeType===1&&d.nodeName.toLowerCase()!=="object"){var j,k,l,m=d.getAttribute("id"),n=m||o,p=N.test(a)&&d.parentNode||d;m?n=n.replace(c,"\\$&"):d.setAttribute("id",n),j=bc(a,d,h),n="[id='"+n+"']";for(k=0,l=j.length;k0})}(),f.setFilters.nth=f.setFilters.eq,f.filters=f.pseudos,$.attr=p.attr,p.find=$,p.expr=$.selectors,p.expr[":"]=p.expr.pseudos,p.unique=$.uniqueSort,p.text=$.getText,p.isXMLDoc=$.isXML,p.contains=$.contains}(a);var bc=/Until$/,bd=/^(?:parents|prev(?:Until|All))/,be=/^.[^:#\[\.,]*$/,bf=p.expr.match.needsContext,bg={children:!0,contents:!0,next:!0,prev:!0};p.fn.extend({find:function(a){var b,c,d,e,f,g,h=this;if(typeof a!="string")return p(a).filter(function(){for(b=0,c=h.length;b0)for(e=d;e=0:p.filter(a,this).length>0:this.filter(a).length>0)},closest:function(a,b){var c,d=0,e=this.length,f=[],g=bf.test(a)||typeof a!="string"?p(a,b||this.context):0;for(;d-1:p.find.matchesSelector(c,a)){f.push(c);break}c=c.parentNode}}return f=f.length>1?p.unique(f):f,this.pushStack(f,"closest",a)},index:function(a){return a?typeof a=="string"?p.inArray(this[0],p(a)):p.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(a,b){var c=typeof a=="string"?p(a,b):p.makeArray(a&&a.nodeType?[a]:a),d=p.merge(this.get(),c);return this.pushStack(bh(c[0])||bh(d[0])?d:p.unique(d))},addBack:function(a){return this.add(a==null?this.prevObject:this.prevObject.filter(a))}}),p.fn.andSelf=p.fn.addBack,p.each({parent:function(a){var b=a.parentNode;return b&&b.nodeType!==11?b:null},parents:function(a){return p.dir(a,"parentNode")},parentsUntil:function(a,b,c){return p.dir(a,"parentNode",c)},next:function(a){return bi(a,"nextSibling")},prev:function(a){return bi(a,"previousSibling")},nextAll:function(a){return p.dir(a,"nextSibling")},prevAll:function(a){return p.dir(a,"previousSibling")},nextUntil:function(a,b,c){return p.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return p.dir(a,"previousSibling",c)},siblings:function(a){return p.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return p.sibling(a.firstChild)},contents:function(a){return p.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:p.merge([],a.childNodes)}},function(a,b){p.fn[a]=function(c,d){var e=p.map(this,b,c);return bc.test(a)||(d=c),d&&typeof d=="string"&&(e=p.filter(d,e)),e=this.length>1&&!bg[a]?p.unique(e):e,this.length>1&&bd.test(a)&&(e=e.reverse()),this.pushStack(e,a,k.call(arguments).join(","))}}),p.extend({filter:function(a,b,c){return c&&(a=":not("+a+")"),b.length===1?p.find.matchesSelector(b[0],a)?[b[0]]:[]:p.find.matches(a,b)},dir:function(a,c,d){var e=[],f=a[c];while(f&&f.nodeType!==9&&(d===b||f.nodeType!==1||!p(f).is(d)))f.nodeType===1&&e.push(f),f=f[c];return e},sibling:function(a,b){var c=[];for(;a;a=a.nextSibling)a.nodeType===1&&a!==b&&c.push(a);return c}});var bl="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",bm=/ jQuery\d+="(?:null|\d+)"/g,bn=/^\s+/,bo=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,bp=/<([\w:]+)/,bq=/]","i"),bv=/^(?:checkbox|radio)$/,bw=/checked\s*(?:[^=]|=\s*.checked.)/i,bx=/\/(java|ecma)script/i,by=/^\s*\s*$/g,bz={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},bA=bk(e),bB=bA.appendChild(e.createElement("div"));bz.optgroup=bz.option,bz.tbody=bz.tfoot=bz.colgroup=bz.caption=bz.thead,bz.th=bz.td,p.support.htmlSerialize||(bz._default=[1,"X
","
"]),p.fn.extend({text:function(a){return p.access(this,function(a){return a===b?p.text(this):this.empty().append((this[0]&&this[0].ownerDocument||e).createTextNode(a))},null,a,arguments.length)},wrapAll:function(a){if(p.isFunction(a))return this.each(function(b){p(this).wrapAll(a.call(this,b))});if(this[0]){var b=p(a,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&b.insertBefore(this[0]),b.map(function(){var a=this;while(a.firstChild&&a.firstChild.nodeType===1)a=a.firstChild;return a}).append(this)}return this},wrapInner:function(a){return p.isFunction(a)?this.each(function(b){p(this).wrapInner(a.call(this,b))}):this.each(function(){var b=p(this),c=b.contents();c.length?c.wrapAll(a):b.append(a)})},wrap:function(a){var b=p.isFunction(a);return this.each(function(c){p(this).wrapAll(b?a.call(this,c):a)})},unwrap:function(){return this.parent().each(function(){p.nodeName(this,"body")||p(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.appendChild(a)})},prepend:function(){return this.domManip(arguments,!0,function(a){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(a,this.firstChild)})},before:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(a,this),"before",this.selector)}},after:function(){if(!bh(this[0]))return this.domManip(arguments,!1,function(a){this.parentNode.insertBefore(a,this.nextSibling)});if(arguments.length){var a=p.clean(arguments);return this.pushStack(p.merge(this,a),"after",this.selector)}},remove:function(a,b){var c,d=0;for(;(c=this[d])!=null;d++)if(!a||p.filter(a,[c]).length)!b&&c.nodeType===1&&(p.cleanData(c.getElementsByTagName("*")),p.cleanData([c])),c.parentNode&&c.parentNode.removeChild(c);return this},empty:function(){var a,b=0;for(;(a=this[b])!=null;b++){a.nodeType===1&&p.cleanData(a.getElementsByTagName("*"));while(a.firstChild)a.removeChild(a.firstChild)}return this},clone:function(a,b){return a=a==null?!1:a,b=b==null?a:b,this.map(function(){return p.clone(this,a,b)})},html:function(a){return p.access(this,function(a){var c=this[0]||{},d=0,e=this.length;if(a===b)return c.nodeType===1?c.innerHTML.replace(bm,""):b;if(typeof a=="string"&&!bs.test(a)&&(p.support.htmlSerialize||!bu.test(a))&&(p.support.leadingWhitespace||!bn.test(a))&&!bz[(bp.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(bo,"<$1>");try{for(;d1&&typeof j=="string"&&bw.test(j))return this.each(function(){p(this).domManip(a,c,d)});if(p.isFunction(j))return this.each(function(e){var f=p(this);a[0]=j.call(this,e,c?f.html():b),f.domManip(a,c,d)});if(this[0]){e=p.buildFragment(a,this,k),g=e.fragment,f=g.firstChild,g.childNodes.length===1&&(g=f);if(f){c=c&&p.nodeName(f,"tr");for(h=e.cacheable||l-1;i0?this.clone(!0):this).get(),p(g[e])[b](d),f=f.concat(d);return this.pushStack(f,a,g.selector)}}),p.extend({clone:function(a,b,c){var d,e,f,g;p.support.html5Clone||p.isXMLDoc(a)||!bu.test("<"+a.nodeName+">")?g=a.cloneNode(!0):(bB.innerHTML=a.outerHTML,bB.removeChild(g=bB.firstChild));if((!p.support.noCloneEvent||!p.support.noCloneChecked)&&(a.nodeType===1||a.nodeType===11)&&!p.isXMLDoc(a)){bE(a,g),d=bF(a),e=bF(g);for(f=0;d[f];++f)e[f]&&bE(d[f],e[f])}if(b){bD(a,g);if(c){d=bF(a),e=bF(g);for(f=0;d[f];++f)bD(d[f],e[f])}}return d=e=null,g},clean:function(a,b,c,d){var f,g,h,i,j,k,l,m,n,o,q,r,s=b===e&&bA,t=[];if(!b||typeof b.createDocumentFragment=="undefined")b=e;for(f=0;(h=a[f])!=null;f++){typeof h=="number"&&(h+="");if(!h)continue;if(typeof h=="string")if(!br.test(h))h=b.createTextNode(h);else{s=s||bk(b),l=b.createElement("div"),s.appendChild(l),h=h.replace(bo,"<$1>"),i=(bp.exec(h)||["",""])[1].toLowerCase(),j=bz[i]||bz._default,k=j[0],l.innerHTML=j[1]+h+j[2];while(k--)l=l.lastChild;if(!p.support.tbody){m=bq.test(h),n=i==="table"&&!m?l.firstChild&&l.firstChild.childNodes:j[1]===""&&!m?l.childNodes:[];for(g=n.length-1;g>=0;--g)p.nodeName(n[g],"tbody")&&!n[g].childNodes.length&&n[g].parentNode.removeChild(n[g])}!p.support.leadingWhitespace&&bn.test(h)&&l.insertBefore(b.createTextNode(bn.exec(h)[0]),l.firstChild),h=l.childNodes,l.parentNode.removeChild(l)}h.nodeType?t.push(h):p.merge(t,h)}l&&(h=l=s=null);if(!p.support.appendChecked)for(f=0;(h=t[f])!=null;f++)p.nodeName(h,"input")?bG(h):typeof h.getElementsByTagName!="undefined"&&p.grep(h.getElementsByTagName("input"),bG);if(c){q=function(a){if(!a.type||bx.test(a.type))return d?d.push(a.parentNode?a.parentNode.removeChild(a):a):c.appendChild(a)};for(f=0;(h=t[f])!=null;f++)if(!p.nodeName(h,"script")||!q(h))c.appendChild(h),typeof h.getElementsByTagName!="undefined"&&(r=p.grep(p.merge([],h.getElementsByTagName("script")),q),t.splice.apply(t,[f+1,0].concat(r)),f+=r.length)}return t},cleanData:function(a,b){var c,d,e,f,g=0,h=p.expando,i=p.cache,j=p.support.deleteExpando,k=p.event.special;for(;(e=a[g])!=null;g++)if(b||p.acceptData(e)){d=e[h],c=d&&i[d];if(c){if(c.events)for(f in c.events)k[f]?p.event.remove(e,f):p.removeEvent(e,f,c.handle);i[d]&&(delete i[d],j?delete e[h]:e.removeAttribute?e.removeAttribute(h):e[h]=null,p.deletedIds.push(d))}}}}),function(){var a,b;p.uaMatch=function(a){a=a.toLowerCase();var b=/(chrome)[ \/]([\w.]+)/.exec(a)||/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||a.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(a)||[];return{browser:b[1]||"",version:b[2]||"0"}},a=p.uaMatch(g.userAgent),b={},a.browser&&(b[a.browser]=!0,b.version=a.version),b.chrome?b.webkit=!0:b.webkit&&(b.safari=!0),p.browser=b,p.sub=function(){function a(b,c){return new a.fn.init(b,c)}p.extend(!0,a,this),a.superclass=this,a.fn=a.prototype=this(),a.fn.constructor=a,a.sub=this.sub,a.fn.init=function c(c,d){return d&&d instanceof p&&!(d instanceof a)&&(d=a(d)),p.fn.init.call(this,c,d,b)},a.fn.init.prototype=a.fn;var b=a(e);return a}}();var bH,bI,bJ,bK=/alpha\([^)]*\)/i,bL=/opacity=([^)]*)/,bM=/^(top|right|bottom|left)$/,bN=/^(none|table(?!-c[ea]).+)/,bO=/^margin/,bP=new RegExp("^("+q+")(.*)$","i"),bQ=new RegExp("^("+q+")(?!px)[a-z%]+$","i"),bR=new RegExp("^([-+])=("+q+")","i"),bS={},bT={position:"absolute",visibility:"hidden",display:"block"},bU={letterSpacing:0,fontWeight:400},bV=["Top","Right","Bottom","Left"],bW=["Webkit","O","Moz","ms"],bX=p.fn.toggle;p.fn.extend({css:function(a,c){return p.access(this,function(a,c,d){return d!==b?p.style(a,c,d):p.css(a,c)},a,c,arguments.length>1)},show:function(){return b$(this,!0)},hide:function(){return b$(this)},toggle:function(a,b){var c=typeof a=="boolean";return p.isFunction(a)&&p.isFunction(b)?bX.apply(this,arguments):this.each(function(){(c?a:bZ(this))?p(this).show():p(this).hide()})}}),p.extend({cssHooks:{opacity:{get:function(a,b){if(b){var c=bH(a,"opacity");return c===""?"1":c}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":p.support.cssFloat?"cssFloat":"styleFloat"},style:function(a,c,d,e){if(!a||a.nodeType===3||a.nodeType===8||!a.style)return;var f,g,h,i=p.camelCase(c),j=a.style;c=p.cssProps[i]||(p.cssProps[i]=bY(j,i)),h=p.cssHooks[c]||p.cssHooks[i];if(d===b)return h&&"get"in h&&(f=h.get(a,!1,e))!==b?f:j[c];g=typeof d,g==="string"&&(f=bR.exec(d))&&(d=(f[1]+1)*f[2]+parseFloat(p.css(a,c)),g="number");if(d==null||g==="number"&&isNaN(d))return;g==="number"&&!p.cssNumber[i]&&(d+="px");if(!h||!("set"in h)||(d=h.set(a,d,e))!==b)try{j[c]=d}catch(k){}},css:function(a,c,d,e){var f,g,h,i=p.camelCase(c);return c=p.cssProps[i]||(p.cssProps[i]=bY(a.style,i)),h=p.cssHooks[c]||p.cssHooks[i],h&&"get"in h&&(f=h.get(a,!0,e)),f===b&&(f=bH(a,c)),f==="normal"&&c in bU&&(f=bU[c]),d||e!==b?(g=parseFloat(f),d||p.isNumeric(g)?g||0:f):f},swap:function(a,b,c){var d,e,f={};for(e in b)f[e]=a.style[e],a.style[e]=b[e];d=c.call(a);for(e in b)a.style[e]=f[e];return d}}),a.getComputedStyle?bH=function(b,c){var d,e,f,g,h=a.getComputedStyle(b,null),i=b.style;return h&&(d=h[c],d===""&&!p.contains(b.ownerDocument,b)&&(d=p.style(b,c)),bQ.test(d)&&bO.test(c)&&(e=i.width,f=i.minWidth,g=i.maxWidth,i.minWidth=i.maxWidth=i.width=d,d=h.width,i.width=e,i.minWidth=f,i.maxWidth=g)),d}:e.documentElement.currentStyle&&(bH=function(a,b){var c,d,e=a.currentStyle&&a.currentStyle[b],f=a.style;return e==null&&f&&f[b]&&(e=f[b]),bQ.test(e)&&!bM.test(b)&&(c=f.left,d=a.runtimeStyle&&a.runtimeStyle.left,d&&(a.runtimeStyle.left=a.currentStyle.left),f.left=b==="fontSize"?"1em":e,e=f.pixelLeft+"px",f.left=c,d&&(a.runtimeStyle.left=d)),e===""?"auto":e}),p.each(["height","width"],function(a,b){p.cssHooks[b]={get:function(a,c,d){if(c)return a.offsetWidth===0&&bN.test(bH(a,"display"))?p.swap(a,bT,function(){return cb(a,b,d)}):cb(a,b,d)},set:function(a,c,d){return b_(a,c,d?ca(a,b,d,p.support.boxSizing&&p.css(a,"boxSizing")==="border-box"):0)}}}),p.support.opacity||(p.cssHooks.opacity={get:function(a,b){return bL.test((b&&a.currentStyle?a.currentStyle.filter:a.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":b?"1":""},set:function(a,b){var c=a.style,d=a.currentStyle,e=p.isNumeric(b)?"alpha(opacity="+b*100+")":"",f=d&&d.filter||c.filter||"";c.zoom=1;if(b>=1&&p.trim(f.replace(bK,""))===""&&c.removeAttribute){c.removeAttribute("filter");if(d&&!d.filter)return}c.filter=bK.test(f)?f.replace(bK,e):f+" "+e}}),p(function(){p.support.reliableMarginRight||(p.cssHooks.marginRight={get:function(a,b){return p.swap(a,{display:"inline-block"},function(){if(b)return bH(a,"marginRight")})}}),!p.support.pixelPosition&&p.fn.position&&p.each(["top","left"],function(a,b){p.cssHooks[b]={get:function(a,c){if(c){var d=bH(a,b);return bQ.test(d)?p(a).position()[b]+"px":d}}}})}),p.expr&&p.expr.filters&&(p.expr.filters.hidden=function(a){return a.offsetWidth===0&&a.offsetHeight===0||!p.support.reliableHiddenOffsets&&(a.style&&a.style.display||bH(a,"display"))==="none"},p.expr.filters.visible=function(a){return!p.expr.filters.hidden(a)}),p.each({margin:"",padding:"",border:"Width"},function(a,b){p.cssHooks[a+b]={expand:function(c){var d,e=typeof c=="string"?c.split(" "):[c],f={};for(d=0;d<4;d++)f[a+bV[d]+b]=e[d]||e[d-2]||e[0];return f}},bO.test(a)||(p.cssHooks[a+b].set=b_)});var cd=/%20/g,ce=/\[\]$/,cf=/\r?\n/g,cg=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,ch=/^(?:select|textarea)/i;p.fn.extend({serialize:function(){return p.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?p.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ch.test(this.nodeName)||cg.test(this.type))}).map(function(a,b){var c=p(this).val();return c==null?null:p.isArray(c)?p.map(c,function(a,c){return{name:b.name,value:a.replace(cf,"\r\n")}}):{name:b.name,value:c.replace(cf,"\r\n")}}).get()}}),p.param=function(a,c){var d,e=[],f=function(a,b){b=p.isFunction(b)?b():b==null?"":b,e[e.length]=encodeURIComponent(a)+"="+encodeURIComponent(b)};c===b&&(c=p.ajaxSettings&&p.ajaxSettings.traditional);if(p.isArray(a)||a.jquery&&!p.isPlainObject(a))p.each(a,function(){f(this.name,this.value)});else for(d in a)ci(d,a[d],c,f);return e.join("&").replace(cd,"+")};var cj,ck,cl=/#.*$/,cm=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,cn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,co=/^(?:GET|HEAD)$/,cp=/^\/\//,cq=/\?/,cr=/)<[^<]*)*<\/script>/gi,cs=/([?&])_=[^&]*/,ct=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,cu=p.fn.load,cv={},cw={},cx=["*/"]+["*"];try{cj=f.href}catch(cy){cj=e.createElement("a"),cj.href="",cj=cj.href}ck=ct.exec(cj.toLowerCase())||[],p.fn.load=function(a,c,d){if(typeof a!="string"&&cu)return cu.apply(this,arguments);if(!this.length)return this;var e,f,g,h=this,i=a.indexOf(" ");return i>=0&&(e=a.slice(i,a.length),a=a.slice(0,i)),p.isFunction(c)?(d=c,c=b):c&&typeof c=="object"&&(f="POST"),p.ajax({url:a,type:f,dataType:"html",data:c,complete:function(a,b){d&&h.each(d,g||[a.responseText,b,a])}}).done(function(a){g=arguments,h.html(e?p("
").append(a.replace(cr,"")).find(e):a)}),this},p.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(a,b){p.fn[b]=function(a){return this.on(b,a)}}),p.each(["get","post"],function(a,c){p[c]=function(a,d,e,f){return p.isFunction(d)&&(f=f||e,e=d,d=b),p.ajax({type:c,url:a,data:d,success:e,dataType:f})}}),p.extend({getScript:function(a,c){return p.get(a,b,c,"script")},getJSON:function(a,b,c){return p.get(a,b,c,"json")},ajaxSetup:function(a,b){return b?cB(a,p.ajaxSettings):(b=a,a=p.ajaxSettings),cB(a,b),a},ajaxSettings:{url:cj,isLocal:cn.test(ck[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":cx},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":a.String,"text html":!0,"text json":p.parseJSON,"text xml":p.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:cz(cv),ajaxTransport:cz(cw),ajax:function(a,c){function y(a,c,f,i){var k,s,t,u,w,y=c;if(v===2)return;v=2,h&&clearTimeout(h),g=b,e=i||"",x.readyState=a>0?4:0,f&&(u=cC(l,x,f));if(a>=200&&a<300||a===304)l.ifModified&&(w=x.getResponseHeader("Last-Modified"),w&&(p.lastModified[d]=w),w=x.getResponseHeader("Etag"),w&&(p.etag[d]=w)),a===304?(y="notmodified",k=!0):(k=cD(l,u),y=k.state,s=k.data,t=k.error,k=!t);else{t=y;if(!y||a)y="error",a<0&&(a=0)}x.status=a,x.statusText=""+(c||y),k?o.resolveWith(m,[s,y,x]):o.rejectWith(m,[x,y,t]),x.statusCode(r),r=b,j&&n.trigger("ajax"+(k?"Success":"Error"),[x,l,k?s:t]),q.fireWith(m,[x,y]),j&&(n.trigger("ajaxComplete",[x,l]),--p.active||p.event.trigger("ajaxStop"))}typeof a=="object"&&(c=a,a=b),c=c||{};var d,e,f,g,h,i,j,k,l=p.ajaxSetup({},c),m=l.context||l,n=m!==l&&(m.nodeType||m instanceof p)?p(m):p.event,o=p.Deferred(),q=p.Callbacks("once memory"),r=l.statusCode||{},t={},u={},v=0,w="canceled",x={readyState:0,setRequestHeader:function(a,b){if(!v){var c=a.toLowerCase();a=u[c]=u[c]||a,t[a]=b}return this},getAllResponseHeaders:function(){return v===2?e:null},getResponseHeader:function(a){var c;if(v===2){if(!f){f={};while(c=cm.exec(e))f[c[1].toLowerCase()]=c[2]}c=f[a.toLowerCase()]}return c===b?null:c},overrideMimeType:function(a){return v||(l.mimeType=a),this},abort:function(a){return a=a||w,g&&g.abort(a),y(0,a),this}};o.promise(x),x.success=x.done,x.error=x.fail,x.complete=q.add,x.statusCode=function(a){if(a){var b;if(v<2)for(b in a)r[b]=[r[b],a[b]];else b=a[x.status],x.always(b)}return this},l.url=((a||l.url)+"").replace(cl,"").replace(cp,ck[1]+"//"),l.dataTypes=p.trim(l.dataType||"*").toLowerCase().split(s),l.crossDomain==null&&(i=ct.exec(l.url.toLowerCase()),l.crossDomain=!(!i||i[1]==ck[1]&&i[2]==ck[2]&&(i[3]||(i[1]==="http:"?80:443))==(ck[3]||(ck[1]==="http:"?80:443)))),l.data&&l.processData&&typeof l.data!="string"&&(l.data=p.param(l.data,l.traditional)),cA(cv,l,c,x);if(v===2)return x;j=l.global,l.type=l.type.toUpperCase(),l.hasContent=!co.test(l.type),j&&p.active++===0&&p.event.trigger("ajaxStart");if(!l.hasContent){l.data&&(l.url+=(cq.test(l.url)?"&":"?")+l.data,delete l.data),d=l.url;if(l.cache===!1){var z=p.now(),A=l.url.replace(cs,"$1_="+z);l.url=A+(A===l.url?(cq.test(l.url)?"&":"?")+"_="+z:"")}}(l.data&&l.hasContent&&l.contentType!==!1||c.contentType)&&x.setRequestHeader("Content-Type",l.contentType),l.ifModified&&(d=d||l.url,p.lastModified[d]&&x.setRequestHeader("If-Modified-Since",p.lastModified[d]),p.etag[d]&&x.setRequestHeader("If-None-Match",p.etag[d])),x.setRequestHeader("Accept",l.dataTypes[0]&&l.accepts[l.dataTypes[0]]?l.accepts[l.dataTypes[0]]+(l.dataTypes[0]!=="*"?", "+cx+"; q=0.01":""):l.accepts["*"]);for(k in l.headers)x.setRequestHeader(k,l.headers[k]);if(!l.beforeSend||l.beforeSend.call(m,x,l)!==!1&&v!==2){w="abort";for(k in{success:1,error:1,complete:1})x[k](l[k]);g=cA(cw,l,c,x);if(!g)y(-1,"No Transport");else{x.readyState=1,j&&n.trigger("ajaxSend",[x,l]),l.async&&l.timeout>0&&(h=setTimeout(function(){x.abort("timeout")},l.timeout));try{v=1,g.send(t,y)}catch(B){if(v<2)y(-1,B);else throw B}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var cE=[],cF=/\?/,cG=/(=)\?(?=&|$)|\?\?/,cH=p.now();p.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var a=cE.pop()||p.expando+"_"+cH++;return this[a]=!0,a}}),p.ajaxPrefilter("json jsonp",function(c,d,e){var f,g,h,i=c.data,j=c.url,k=c.jsonp!==!1,l=k&&cG.test(j),m=k&&!l&&typeof i=="string"&&!(c.contentType||"").indexOf("application/x-www-form-urlencoded")&&cG.test(i);if(c.dataTypes[0]==="jsonp"||l||m)return f=c.jsonpCallback=p.isFunction(c.jsonpCallback)?c.jsonpCallback():c.jsonpCallback,g=a[f],l?c.url=j.replace(cG,"$1"+f):m?c.data=i.replace(cG,"$1"+f):k&&(c.url+=(cF.test(j)?"&":"?")+c.jsonp+"="+f),c.converters["script json"]=function(){return h||p.error(f+" was not called"),h[0]},c.dataTypes[0]="json",a[f]=function(){h=arguments},e.always(function(){a[f]=g,c[f]&&(c.jsonpCallback=d.jsonpCallback,cE.push(f)),h&&p.isFunction(g)&&g(h[0]),h=g=b}),"script"}),p.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(a){return p.globalEval(a),a}}}),p.ajaxPrefilter("script",function(a){a.cache===b&&(a.cache=!1),a.crossDomain&&(a.type="GET",a.global=!1)}),p.ajaxTransport("script",function(a){if(a.crossDomain){var c,d=e.head||e.getElementsByTagName("head")[0]||e.documentElement;return{send:function(f,g){c=e.createElement("script"),c.async="async",a.scriptCharset&&(c.charset=a.scriptCharset),c.src=a.url,c.onload=c.onreadystatechange=function(a,e){if(e||!c.readyState||/loaded|complete/.test(c.readyState))c.onload=c.onreadystatechange=null,d&&c.parentNode&&d.removeChild(c),c=b,e||g(200,"success")},d.insertBefore(c,d.firstChild)},abort:function(){c&&c.onload(0,1)}}}});var cI,cJ=a.ActiveXObject?function(){for(var a in cI)cI[a](0,1)}:!1,cK=0;p.ajaxSettings.xhr=a.ActiveXObject?function(){return!this.isLocal&&cL()||cM()}:cL,function(a){p.extend(p.support,{ajax:!!a,cors:!!a&&"withCredentials"in a})}(p.ajaxSettings.xhr()),p.support.ajax&&p.ajaxTransport(function(c){if(!c.crossDomain||p.support.cors){var d;return{send:function(e,f){var g,h,i=c.xhr();c.username?i.open(c.type,c.url,c.async,c.username,c.password):i.open(c.type,c.url,c.async);if(c.xhrFields)for(h in c.xhrFields)i[h]=c.xhrFields[h];c.mimeType&&i.overrideMimeType&&i.overrideMimeType(c.mimeType),!c.crossDomain&&!e["X-Requested-With"]&&(e["X-Requested-With"]="XMLHttpRequest");try{for(h in e)i.setRequestHeader(h,e[h])}catch(j){}i.send(c.hasContent&&c.data||null),d=function(a,e){var h,j,k,l,m;try{if(d&&(e||i.readyState===4)){d=b,g&&(i.onreadystatechange=p.noop,cJ&&delete cI[g]);if(e)i.readyState!==4&&i.abort();else{h=i.status,k=i.getAllResponseHeaders(),l={},m=i.responseXML,m&&m.documentElement&&(l.xml=m);try{l.text=i.responseText}catch(a){}try{j=i.statusText}catch(n){j=""}!h&&c.isLocal&&!c.crossDomain?h=l.text?200:404:h===1223&&(h=204)}}}catch(o){e||f(-1,o)}l&&f(h,j,l,k)},c.async?i.readyState===4?setTimeout(d,0):(g=++cK,cJ&&(cI||(cI={},p(a).unload(cJ)),cI[g]=d),i.onreadystatechange=d):d()},abort:function(){d&&d(0,1)}}}});var cN,cO,cP=/^(?:toggle|show|hide)$/,cQ=new RegExp("^(?:([-+])=|)("+q+")([a-z%]*)$","i"),cR=/queueHooks$/,cS=[cY],cT={"*":[function(a,b){var c,d,e,f=this.createTween(a,b),g=cQ.exec(b),h=f.cur(),i=+h||0,j=1;if(g){c=+g[2],d=g[3]||(p.cssNumber[a]?"":"px");if(d!=="px"&&i){i=p.css(f.elem,a,!0)||c||1;do e=j=j||".5",i=i/j,p.style(f.elem,a,i+d),j=f.cur()/h;while(j!==1&&j!==e)}f.unit=d,f.start=i,f.end=g[1]?i+(g[1]+1)*c:c}return f}]};p.Animation=p.extend(cW,{tweener:function(a,b){p.isFunction(a)?(b=a,a=["*"]):a=a.split(" ");var c,d=0,e=a.length;for(;d-1,j={},k={},l,m;i?(k=e.position(),l=k.top,m=k.left):(l=parseFloat(g)||0,m=parseFloat(h)||0),p.isFunction(b)&&(b=b.call(a,c,f)),b.top!=null&&(j.top=b.top-f.top+l),b.left!=null&&(j.left=b.left-f.left+m),"using"in b?b.using.call(a,j):e.css(j)}},p.fn.extend({position:function(){if(!this[0])return;var a=this[0],b=this.offsetParent(),c=this.offset(),d=c_.test(b[0].nodeName)?{top:0,left:0}:b.offset();return c.top-=parseFloat(p.css(a,"marginTop"))||0,c.left-=parseFloat(p.css(a,"marginLeft"))||0,d.top+=parseFloat(p.css(b[0],"borderTopWidth"))||0,d.left+=parseFloat(p.css(b[0],"borderLeftWidth"))||0,{top:c.top-d.top,left:c.left-d.left}},offsetParent:function(){return this.map(function(){var a=this.offsetParent||e.body;while(a&&!c_.test(a.nodeName)&&p.css(a,"position")==="static")a=a.offsetParent;return a||e.body})}}),p.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(a,c){var d=/Y/.test(c);p.fn[a]=function(e){return p.access(this,function(a,e,f){var g=da(a);if(f===b)return g?c in g?g[c]:g.document.documentElement[e]:a[e];g?g.scrollTo(d?p(g).scrollLeft():f,d?f:p(g).scrollTop()):a[e]=f},a,e,arguments.length,null)}}),p.each({Height:"height",Width:"width"},function(a,c){p.each({padding:"inner"+a,content:c,"":"outer"+a},function(d,e){p.fn[e]=function(e,f){var g=arguments.length&&(d||typeof e!="boolean"),h=d||(e===!0||f===!0?"margin":"border");return p.access(this,function(c,d,e){var f;return p.isWindow(c)?c.document.documentElement["client"+a]:c.nodeType===9?(f=c.documentElement,Math.max(c.body["scroll"+a],f["scroll"+a],c.body["offset"+a],f["offset"+a],f["client"+a])):e===b?p.css(c,d,e,h):p.style(c,d,e,h)},c,g?e:b,g,null)}})}),a.jQuery=a.$=p,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return p})})(window); \ No newline at end of file diff --git a/togetherjs/tests/doctestjs/.resources/boilerplate/js/vendor/modernizr-2.6.1.min.js b/togetherjs/tests/doctestjs/.resources/boilerplate/js/vendor/modernizr-2.6.1.min.js deleted file mode 100755 index 52b523fa2..000000000 --- a/togetherjs/tests/doctestjs/.resources/boilerplate/js/vendor/modernizr-2.6.1.min.js +++ /dev/null @@ -1,4 +0,0 @@ -/* Modernizr 2.6.1 (Custom Build) | MIT & BSD - * Build: http://modernizr.com/download/#-fontface-backgroundsize-borderimage-borderradius-boxshadow-flexbox-hsla-multiplebgs-opacity-rgba-textshadow-cssanimations-csscolumns-generatedcontent-cssgradients-cssreflections-csstransforms-csstransforms3d-csstransitions-applicationcache-canvas-canvastext-draganddrop-hashchange-history-audio-video-indexeddb-input-inputtypes-localstorage-postmessage-sessionstorage-websockets-websqldatabase-webworkers-geolocation-inlinesvg-smil-svg-svgclippaths-touch-webgl-shiv-mq-cssclasses-addtest-prefixed-teststyles-testprop-testallprops-hasevent-prefixes-domprefixes-load - */ -;window.Modernizr=function(a,b,c){function D(a){j.cssText=a}function E(a,b){return D(n.join(a+";")+(b||""))}function F(a,b){return typeof a===b}function G(a,b){return!!~(""+a).indexOf(b)}function H(a,b){for(var d in a){var e=a[d];if(!G(e,"-")&&j[e]!==c)return b=="pfx"?e:!0}return!1}function I(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:F(f,"function")?f.bind(d||b):f}return!1}function J(a,b,c){var d=a.charAt(0).toUpperCase()+a.slice(1),e=(a+" "+p.join(d+" ")+d).split(" ");return F(b,"string")||F(b,"undefined")?H(e,b):(e=(a+" "+q.join(d+" ")+d).split(" "),I(e,b,c))}function K(){e.input=function(c){for(var d=0,e=c.length;d',a,""].join(""),k.id=h,(l?k:m).innerHTML+=f,m.appendChild(k),l||(m.style.background="",g.appendChild(m)),i=c(k,a),l?k.parentNode.removeChild(k):m.parentNode.removeChild(m),!!i},z=function(b){var c=a.matchMedia||a.msMatchMedia;if(c)return c(b).matches;var d;return y("@media "+b+" { #"+h+" { position: absolute; } }",function(b){d=(a.getComputedStyle?getComputedStyle(b,null):b.currentStyle)["position"]=="absolute"}),d},A=function(){function d(d,e){e=e||b.createElement(a[d]||"div"),d="on"+d;var f=d in e;return f||(e.setAttribute||(e=b.createElement("div")),e.setAttribute&&e.removeAttribute&&(e.setAttribute(d,""),f=F(e[d],"function"),F(e[d],"undefined")||(e[d]=c),e.removeAttribute(d))),e=null,f}var a={select:"input",change:"input",submit:"form",reset:"form",error:"img",load:"img",abort:"img"};return d}(),B={}.hasOwnProperty,C;!F(B,"undefined")&&!F(B.call,"undefined")?C=function(a,b){return B.call(a,b)}:C=function(a,b){return b in a&&F(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=w.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(w.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(w.call(arguments)))};return e}),s.flexbox=function(){return J("flexWrap")},s.canvas=function(){var a=b.createElement("canvas");return!!a.getContext&&!!a.getContext("2d")},s.canvastext=function(){return!!e.canvas&&!!F(b.createElement("canvas").getContext("2d").fillText,"function")},s.webgl=function(){return!!a.WebGLRenderingContext},s.touch=function(){var c;return"ontouchstart"in a||a.DocumentTouch&&b instanceof DocumentTouch?c=!0:y(["@media (",n.join("touch-enabled),("),h,")","{#modernizr{top:9px;position:absolute}}"].join(""),function(a){c=a.offsetTop===9}),c},s.geolocation=function(){return"geolocation"in navigator},s.postmessage=function(){return!!a.postMessage},s.websqldatabase=function(){return!!a.openDatabase},s.indexedDB=function(){return!!J("indexedDB",a)},s.hashchange=function(){return A("hashchange",a)&&(b.documentMode===c||b.documentMode>7)},s.history=function(){return!!a.history&&!!history.pushState},s.draganddrop=function(){var a=b.createElement("div");return"draggable"in a||"ondragstart"in a&&"ondrop"in a},s.websockets=function(){return"WebSocket"in a||"MozWebSocket"in a},s.rgba=function(){return D("background-color:rgba(150,255,150,.5)"),G(j.backgroundColor,"rgba")},s.hsla=function(){return D("background-color:hsla(120,40%,100%,.5)"),G(j.backgroundColor,"rgba")||G(j.backgroundColor,"hsla")},s.multiplebgs=function(){return D("background:url(https://),url(https://),red url(https://)"),/(url\s*\(.*?){3}/.test(j.background)},s.backgroundsize=function(){return J("backgroundSize")},s.borderimage=function(){return J("borderImage")},s.borderradius=function(){return J("borderRadius")},s.boxshadow=function(){return J("boxShadow")},s.textshadow=function(){return b.createElement("div").style.textShadow===""},s.opacity=function(){return E("opacity:.55"),/^0.55$/.test(j.opacity)},s.cssanimations=function(){return J("animationName")},s.csscolumns=function(){return J("columnCount")},s.cssgradients=function(){var a="background-image:",b="gradient(linear,left top,right bottom,from(#9f9),to(white));",c="linear-gradient(left top,#9f9, white);";return D((a+"-webkit- ".split(" ").join(b+a)+n.join(c+a)).slice(0,-a.length)),G(j.backgroundImage,"gradient")},s.cssreflections=function(){return J("boxReflect")},s.csstransforms=function(){return!!J("transform")},s.csstransforms3d=function(){var a=!!J("perspective");return a&&"webkitPerspective"in g.style&&y("@media (transform-3d),(-webkit-transform-3d){#modernizr{left:9px;position:absolute;height:3px;}}",function(b,c){a=b.offsetLeft===9&&b.offsetHeight===3}),a},s.csstransitions=function(){return J("transition")},s.fontface=function(){var a;return y('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},s.generatedcontent=function(){var a;return y(['#modernizr:after{content:"',l,'";visibility:hidden}'].join(""),function(b){a=b.offsetHeight>=1}),a},s.video=function(){var a=b.createElement("video"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('video/ogg; codecs="theora"').replace(/^no$/,""),c.h264=a.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/,""),c.webm=a.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/,"")}catch(d){}return c},s.audio=function(){var a=b.createElement("audio"),c=!1;try{if(c=!!a.canPlayType)c=new Boolean(c),c.ogg=a.canPlayType('audio/ogg; codecs="vorbis"').replace(/^no$/,""),c.mp3=a.canPlayType("audio/mpeg;").replace(/^no$/,""),c.wav=a.canPlayType('audio/wav; codecs="1"').replace(/^no$/,""),c.m4a=(a.canPlayType("audio/x-m4a;")||a.canPlayType("audio/aac;")).replace(/^no$/,"")}catch(d){}return c},s.localstorage=function(){try{return localStorage.setItem(h,h),localStorage.removeItem(h),!0}catch(a){return!1}},s.sessionstorage=function(){try{return sessionStorage.setItem(h,h),sessionStorage.removeItem(h),!0}catch(a){return!1}},s.webworkers=function(){return!!a.Worker},s.applicationcache=function(){return!!a.applicationCache},s.svg=function(){return!!b.createElementNS&&!!b.createElementNS(r.svg,"svg").createSVGRect},s.inlinesvg=function(){var a=b.createElement("div");return a.innerHTML="",(a.firstChild&&a.firstChild.namespaceURI)==r.svg},s.smil=function(){return!!b.createElementNS&&/SVGAnimate/.test(m.call(b.createElementNS(r.svg,"animate")))},s.svgclippaths=function(){return!!b.createElementNS&&/SVGClipPath/.test(m.call(b.createElementNS(r.svg,"clipPath")))};for(var L in s)C(s,L)&&(x=L.toLowerCase(),e[x]=s[L](),v.push((e[x]?"":"no-")+x));return e.input||K(),e.addTest=function(a,b){if(typeof a=="object")for(var d in a)C(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,f&&(g.className+=" "+(b?"":"no-")+a),e[a]=b}return e},D(""),i=k=null,function(a,b){function k(a,b){var c=a.createElement("p"),d=a.getElementsByTagName("head")[0]||a.documentElement;return c.innerHTML="x",d.insertBefore(c.lastChild,d.firstChild)}function l(){var a=r.elements;return typeof a=="string"?a.split(" "):a}function m(a){var b=i[a[g]];return b||(b={},h++,a[g]=h,i[h]=b),b}function n(a,c,f){c||(c=b);if(j)return c.createElement(a);f||(f=m(c));var g;return f.cache[a]?g=f.cache[a].cloneNode():e.test(a)?g=(f.cache[a]=f.createElem(a)).cloneNode():g=f.createElem(a),g.canHaveChildren&&!d.test(a)?f.frag.appendChild(g):g}function o(a,c){a||(a=b);if(j)return a.createDocumentFragment();c=c||m(a);var d=c.frag.cloneNode(),e=0,f=l(),g=f.length;for(;e",f="hidden"in a,j=a.childNodes.length==1||function(){b.createElement("a");var a=b.createDocumentFragment();return typeof a.cloneNode=="undefined"||typeof a.createDocumentFragment=="undefined"||typeof a.createElement=="undefined"}()}catch(c){f=!0,j=!0}})();var r={elements:c.elements||"abbr article aside audio bdi canvas data datalist details figcaption figure footer header hgroup mark meter nav output progress section summary time video",shivCSS:c.shivCSS!==!1,supportsUnknownElements:j,shivMethods:c.shivMethods!==!1,type:"default",shivDocument:q,createElement:n,createDocumentFragment:o};a.html5=r,q(b)}(this,b),e._version=d,e._prefixes=n,e._domPrefixes=q,e._cssomPrefixes=p,e.mq=z,e.hasEvent=A,e.testProp=function(a){return H([a])},e.testAllProps=J,e.testStyles=y,e.prefixed=function(a,b,c){return b?J(a,b,c):J(a,"pfx")},g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" js "+v.join(" "):""),e}(this,this.document),function(a,b,c){function d(a){return o.call(a)=="[object Function]"}function e(a){return typeof a=="string"}function f(){}function g(a){return!a||a=="loaded"||a=="complete"||a=="uninitialized"}function h(){var a=p.shift();q=1,a?a.t?m(function(){(a.t=="c"?B.injectCss:B.injectJs)(a.s,0,a.a,a.x,a.e,1)},0):(a(),h()):q=0}function i(a,c,d,e,f,i,j){function k(b){if(!o&&g(l.readyState)&&(u.r=o=1,!q&&h(),l.onload=l.onreadystatechange=null,b)){a!="img"&&m(function(){t.removeChild(l)},50);for(var d in y[c])y[c].hasOwnProperty(d)&&y[c][d].onload()}}var j=j||B.errorTimeout,l={},o=0,r=0,u={t:d,s:c,e:f,a:i,x:j};y[c]===1&&(r=1,y[c]=[],l=b.createElement(a)),a=="object"?l.data=c:(l.src=c,l.type=a),l.width=l.height="0",l.onerror=l.onload=l.onreadystatechange=function(){k.call(this,r)},p.splice(e,0,u),a!="img"&&(r||y[c]===2?(t.insertBefore(l,s?null:n),m(k,j)):y[c].push(l))}function j(a,b,c,d,f){return q=0,b=b||"j",e(a)?i(b=="c"?v:u,a,b,this.i++,c,d,f):(p.splice(this.i++,0,a),p.length==1&&h()),this}function k(){var a=B;return a.loader={load:j,i:0},a}var l=b.documentElement,m=a.setTimeout,n=b.getElementsByTagName("script")[0],o={}.toString,p=[],q=0,r="MozAppearance"in l.style,s=r&&!!b.createRange().compareNode,t=s?l:n.parentNode,l=a.opera&&o.call(a.opera)=="[object Opera]",l=!!b.attachEvent&&!l,u=r?"object":l?"script":"img",v=l?"script":u,w=Array.isArray||function(a){return o.call(a)=="[object Array]"},x=[],y={},z={timeout:function(a,b){return b.length&&(a.timeout=b[0]),a}},A,B;B=function(a){function b(a){var a=a.split("!"),b=x.length,c=a.pop(),d=a.length,c={url:c,origUrl:c,prefixes:a},e,f,g;for(f=0;f ul { - padding-left: 0; -} - -#contents li { - list-style: none; -} - -#contents { - border-bottom: 1px solid #999; - padding-left: 1em; -} - -.header-container header { - width: 98%; -} - -code { - color: #288; -} - -aside code { - color: #9ff; -} - -section:target h1, section:target h2, section:target h3, section:target h4, -section:target h5, section:target h6, h3:target, h4:target { - border-bottom: 3px solid #f90; -} - -footer a:link, footer a:visited, -aside a:link, aside a:visited { - text-decoration: none; - color: #fd1; -} - -section header h1, section header h2, section header h3, section header h4, -section header h5, section header h6 { - margin-left: -1em; - margin-top: 1.75em; - border-bottom: 1px solid #000; -} - -pre { - font-size: 90%; - line-height: 1.3; - border: 1px solid #999; - border-radius: 4px; -} - -@media only screen and (min-width: 768px) { - - .main aside { - position: absolute; - right: 1em; - } - -} - -.header-container header { - margin-left: 1em; -} - -h1.title a:link, h1.title a:visited { - text-decoration: none; - color: #fff; -} - -h1.title a:hover { - text-decoration: underline; -} - - -dd p { - margin-top: 0; -} - -dd p:last-child { - margin-bottom: 0; -} \ No newline at end of file diff --git a/togetherjs/tests/doctestjs/.resources/example.xml b/togetherjs/tests/doctestjs/.resources/example.xml deleted file mode 100644 index 5c9a987b2..000000000 --- a/togetherjs/tests/doctestjs/.resources/example.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - Example Feed - A subtitle. - - - urn:uuid:60a76c80-d399-11d9-b91C-0003939e0af6 - 2003-12-13T18:30:02Z - - John Doe - johndoe@example.com - - - - Atom-Powered Robots Run Amok - - - - urn:uuid:1225c695-cfb8-4ebb-aaaa-80da344efa6a - 2003-12-13T18:30:02Z - Some text. - - - diff --git a/togetherjs/tests/doctestjs/.resources/footer.html b/togetherjs/tests/doctestjs/.resources/footer.html deleted file mode 100644 index acd0e2785..000000000 --- a/togetherjs/tests/doctestjs/.resources/footer.html +++ /dev/null @@ -1,30 +0,0 @@ -
- -

Download

-

- You can download this project in either - zip or - tar formats. -

- -

You can also clone the project with Git - by running: -

$ git clone git://github.com/ianb/doctestjs
-

- - - - - - - diff --git a/togetherjs/tests/doctestjs/.resources/header.html b/togetherjs/tests/doctestjs/.resources/header.html deleted file mode 100644 index 149857f9c..000000000 --- a/togetherjs/tests/doctestjs/.resources/header.html +++ /dev/null @@ -1,83 +0,0 @@ - - - - - - - __TITLE__ - - - - - - - - - - - - -
-
-

Doctest.js:

- - - -
-
- -
-
- - - - - __BODY__ - - - -
- -
- - - - - - - - - - - - diff --git a/togetherjs/tests/doctestjs/.resources/include-scripts.sh b/togetherjs/tests/doctestjs/.resources/include-scripts.sh deleted file mode 100755 index 44cd544c8..000000000 --- a/togetherjs/tests/doctestjs/.resources/include-scripts.sh +++ /dev/null @@ -1,50 +0,0 @@ -#!/usr/bin/env bash - -set -e - -base="$(python -c 'import sys, os; print os.path.dirname(os.path.dirname(os.path.abspath(sys.argv[1])))' "$BASH_SOURCE")" - -if [ ! -f $base/doctest.js ] ; then - echo "Could not find $base/doctest.js" - exit 1 -fi - -if [ ! -d $base/.resources/jshint ] ; then - echo "Could not find $base/.resources/jshint" - echo "Try:" - echo " git clone https://github.com/jshint/jshint.git .resources/jshint" - exit 2 -fi - -if [ ! -d $base/.resources/esprima ] ; then - echo "Could not find $base/.resources/esprima" - echo "Try:" - echo " git clone https://github.com/ariya/esprima.git .resources/esprima" - exit 3 -fi - -echo "Substituting $base/doctest.js" - -python -c ' -import os, sys, re, subprocess -os.chdir(sys.argv[1]) -with open("doctest.js", "rb") as fp: - content = fp.read() -names = {} -for arg in sys.argv[2:]: - name, rest = arg.split("=", 1) - names[name] = rest -regex = re.compile(r"\/\*\s+INSERT\s+(.*?)\s+\*\/\s*\n(.*?)\/*\s+END\s+INSERT\s+\*\/", re.S) -def repl(match): - print "Replacing %s" % match.group(1) - filename = names.get(match.group(1)) - # I do not understand why --ascii needs an option - output = subprocess.check_output( - ["uglifyjs", "--no-copyright", "--max-line-len", "200", "-b", "max-line-len=200,ascii-only=true,beautify=false", filename]) - return "/* INSERT %s */\n%s\n/* END INSERT */" % ( - match.group(1), output) -new_content = regex.sub(repl, content) -with open("doctest.js", "wb") as fp: - fp.write(new_content) -print "wrote doctest.js" -' "$base" esprima.js=$base/.resources/esprima/esprima.js jshint.js=$base/.resources/jshint/jshint.js diff --git a/togetherjs/tests/doctestjs/.resources/retemplate.py b/togetherjs/tests/doctestjs/.resources/retemplate.py deleted file mode 100755 index f4a3f58ce..000000000 --- a/togetherjs/tests/doctestjs/.resources/retemplate.py +++ /dev/null @@ -1,104 +0,0 @@ -#!/usr/bin/env python -import re - -section_re = re.compile(r''' -(?P) - (?P[^\000]*) -(?P) -''', re.VERBOSE | re.MULTILINE) - -value_sub_re = re.compile(r'(?P<[^>]*")(?:__)(?P[A-Z_]+)(?:__)(?P"[^>]*>)') - -title_sub_re = re.compile(r'(.*?)', re.I) - - -def get_variables(content, value_matches): - vars = {} - for match in section_re.finditer(content): - vars[match.group('name')] = match.group('value') - for name, value_match_start, value_match_end in value_matches: - regex = re.escape(value_match_start) + '([^"]*)' + re.escape(value_match_end) - regex = re.compile(regex) - for match in regex.finditer(content): - vars[name] = match.group(1) - match = title_sub_re.search(content) - if match: - vars['PAGE_TITLE'] = match.group(1) - else: - print 'No found' - return vars - - -def get_value_matches(template): - matches = [] - for match in value_sub_re.finditer(template): - matches.append(( - match.group('name'), - match.group('front'), - match.group('back'))) - return matches - - -def sub_template(template, content): - matches = get_value_matches(template) - content_vars = get_variables(content, matches) - - def sub_section(match): - if match.group('name') not in content_vars: - # Failure, needs to be fixed - raise Exception('Must have section <!-- %s -->' % match.group('name')) - return ( - match.group('front1') + match.group('name') + match.group('front2') - + content_vars.get(match.group('name'), '') - + match.group('back1') + '/' + match.group('name') + match.group('back2') - ) - - new_content = section_re.sub(sub_section, template) - - def sub_variable(match): - if match.group('name') not in content_vars: - print 'Missing tag: __%s__' % match.group('name') - return '<!-- ' + match.group(0) + ' -->' - return ( - match.group('front') - + content_vars[match.group('name')] - + match.group('back')) - - new_content = value_sub_re.sub(sub_variable, new_content) - - def sub_title(match): - if 'PAGE_TITLE' in content_vars: - return '<title>' + content_vars['PAGE_TITLE'] + '' - else: - return match.group(0) - - new_content = title_sub_re.sub(sub_title, new_content) - - return new_content - - -def rewrite_page(page_name, template_name): - with open(template_name) as fp: - template = fp.read() - with open(page_name) as fp: - content = fp.read() - try: - new_content = sub_template(template, content) - except: - print 'Error in page:', page_name - raise - with open(page_name, 'w') as fp: - fp.write(new_content) - -if __name__ == '__main__': - import sys - if len(sys.argv) < 3: - print 'Usage: retemplate.py TEMPLATE_FILE CONTENT_FILE [...CONTENT_FILE2...]' - sys.exit(2) - template_name = sys.argv[1] - for filename in sys.argv[2:]: - rewrite_page(filename, template_name) diff --git a/togetherjs/tests/doctestjs/.resources/template.html b/togetherjs/tests/doctestjs/.resources/template.html deleted file mode 100644 index 954bbf7dc..000000000 --- a/togetherjs/tests/doctestjs/.resources/template.html +++ /dev/null @@ -1,78 +0,0 @@ - - - - - - - - - - - - - - - - - - - -
-
-

Doctest.js:

- - - -
-
- -
-
- - - - - __BODY__ - - - -
- -
- - - - - - - - - - - - diff --git a/togetherjs/tests/doctestjs/.resources/toc.js b/togetherjs/tests/doctestjs/.resources/toc.js deleted file mode 100644 index a58aeec03..000000000 --- a/togetherjs/tests/doctestjs/.resources/toc.js +++ /dev/null @@ -1,52 +0,0 @@ -function contentsOnLoad() { - if (contentsOnLoad.hasRun) { - return; - } - contentsOnLoad.hasRun = true; - var dest = document.getElementById('contents'); - var toc = [document.createElement('ul')]; - var generatedIds = []; - dest.appendChild(toc[0]); - var els = document.querySelectorAll('h3, h4, h5, h6'); - for (var i=0; i= toc.length) { - var ul = document.createElement('ul'); - var container = document.createElement('li'); - container.appendChild(ul); - toc[toc.length-1].appendChild(container); - toc.push(ul); - } - var name = el.getAttribute('id'); - if (! name) { - name = 'header-'+(i+1); - generatedIds.push(name); - el.setAttribute('id', name); - } - var li = document.createElement('li'); - var anchor = document.createElement('a'); - if (el.getAttribute('href')) { - anchor.setAttribute('href', el.getAttribute('href')); - el.style.display = 'none'; - } else { - anchor.setAttribute('href', '#'+name); - } - li.appendChild(anchor); - anchor.innerHTML = el.innerHTML; - toc[toc.length-1].appendChild(li); - } - // Re-scroll: - if (location.hash && generatedIds.indexOf(location.hash.substr(1)) != -1) { - location.hash = location.hash; - } -} - -document.addEventListener("DOMContentLoaded", contentsOnLoad, false); -window.addEventListener("load", contentsOnLoad, false); diff --git a/togetherjs/tests/doctestjs/.resources/try.js b/togetherjs/tests/doctestjs/.resources/try.js deleted file mode 100644 index 8592ba94a..000000000 --- a/togetherjs/tests/doctestjs/.resources/try.js +++ /dev/null @@ -1,25 +0,0 @@ -window.addEventListener('load', function () { - var innerHTML = $('#display').html(); - if (localStorage.editText) { - $('#editor').val(localStorage.editText); - } - $('#editor').change(function () { - localStorage.editText = $('#editor').val(); - }); - $('#testit').click(function () { - $('#display').html(innerHTML); - $('#editit').click(function () { - $('#edit').show(); - $('#display').hide(); - $('#editor').focus(); - $('#doctest-output').hide(); - }); - $('#test-location').addClass('test').text($('#editor').val()); - console.log($('#test-location').text()); - var runner = new doctest.Runner(); - var parser = new doctest.HTMLParser(runner, $('#display')[0], 'pre#test-location'); - runner.init(); - parser.parse(); - runner.run(); - }); -}, false); diff --git a/togetherjs/tests/doctestjs/.syncignore b/togetherjs/tests/doctestjs/.syncignore deleted file mode 100644 index 8b1378917..000000000 --- a/togetherjs/tests/doctestjs/.syncignore +++ /dev/null @@ -1 +0,0 @@ - diff --git a/togetherjs/tests/doctestjs/README.md b/togetherjs/tests/doctestjs/README.md deleted file mode 100644 index a6b824180..000000000 --- a/togetherjs/tests/doctestjs/README.md +++ /dev/null @@ -1,39 +0,0 @@ -## doctest.js - -For a more complete description please [read the main -page](http://doctestjs.org). - -`doctest.js` is a test runner for Javascript, organized around *examples* and *expected result*. Tests look like this: - -```javascript -// Simple stuff: -print(3 * 4); -// => 12 - -// Or complicated stuff: -var complete = false; -var savedResult = null; -$.ajax({ - url: "/test", - dataType: "json", - success: function (result) { - complete = true; - savedResult = result; - } -}); -wait(function () {return complete;}); -print(savedResult); -// => {value1: "something", value2: true} -``` - -And a bunch more features: check out the [tutorial](http://doctestjs.org/tutorial.html) to get started, or read the [reference](http://doctestjs.org/reference.html) for more detail. - -## License - -Doctest.js is released under an MIT-style license. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/togetherjs/tests/doctestjs/bin/doctest b/togetherjs/tests/doctestjs/bin/doctest deleted file mode 100755 index ac6d474c1..000000000 --- a/togetherjs/tests/doctestjs/bin/doctest +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env node - -var path = require('path'); -var fs = require('fs'); -var root = path.join(path.dirname(fs.realpathSync(__filename)), '..'); - -// FIXME: Not sure this is the best way to go about this, or if we should -// just require("doctest")? -var doctest = require(root + '/doctest'); - -function main(filename) { - var runner = new doctest.Runner({Reporter: doctest.ConsoleReporter}); - runner.globs = runner.evalInit(); - var parser = new doctest.TextParser.fromFile(runner, process.argv[2]); - parser.parse(); - runner.run(); - - var reporter = runner.reporter; - return reporter; -} - -function showReport(reporter) { - console.log('Successes:', reporter.successes); - console.log('Failures:', reporter.failures); - if ((! reporter.successes) || reporter.failures) { - // Set exit code to number of failures (if any). It gives a nice failures - // summary when running under make. E.g.: `make: *** [doctest] Error 2` - process.exit(reporter.failures || 1); - } -} - -var filename = process.argv[2]; -if (! filename) { - console.log("Error: you did not give a filename"); - console.log("Usage:", process.argv[1], "FILENAME"); - process.exit(500); -} -showReport(main(filename)); diff --git a/togetherjs/tests/doctestjs/doctest.css b/togetherjs/tests/doctestjs/doctest.css deleted file mode 100644 index c5e4e6a0a..000000000 --- a/togetherjs/tests/doctestjs/doctest.css +++ /dev/null @@ -1,140 +0,0 @@ -/* Basic styling: */ - -body { - font-family: sans-serif; -} - -pre { - padding: 0.3em; -} - -/* Test block formatting: */ - -pre.doctest, pre.commenttest, pre.test { - border: 1px solid #999; - border-radius: 4px; -} - -pre.doctest.doctest-some-failure, pre.commenttest.doctest-some-failure, pre.test.doctest-some-failure { - border: 1px solid #f00; -} - -/* FIXME: it would be nice if this was more obviously styled to show that there - was more content */ -pre.doctest.expand-on-failure, pre.commenttest.expand-on-failure, pre.test.expand-on-failure { - max-height: 3em; - overflow-y: auto; -} - -pre.doctest.expand-on-failure.doctest-some-failure, pre.commenttest.expand-on-failure.doctest-some-failure, pre.test.expand-on-failure.doctest-some-failure { - max-height: none; -} - -/* Individual example formatting: */ - -.doctest-example:target { - border-left: 4px solid #f00; - padding-left: 4px; -} - -.doctest-example { -} - -.doctest-example.doctest-success { - color: #060; -} - -.doctest-example.doctest-failure { - color: #900; -} - -.doctest-example .doctest-actual-output { - color: #066; - /*padding-left: 1em;*/ -} - -.doctest-example.doctest-failure .doctest-output { - padding-left: 1em; -} - -.doctest-example .doctest-output { - font-weight: bold; -} - -.doctest-example .doctest-description { - color: #000; - font-weight: bold; -} - -.doctest-example .doctest-console { - color: #009; - padding-left: 1em; -} - -/* Reporter formatting: */ - -#doctest-success-count.doctest-nonzero { - color: #0f0; -} - -#doctest-failure-count.doctest-nonzero { - color: #f00; -} - -#doctest-aborted { - background-color: #900; - color: #fff; -} - -.doctest-report-table th { - font-weight: normal; - text-align: left; -} - -.doctest-report-table td { - padding: 0 1em; -} - -a.doctest-failure-link { - color: #00f; - text-decoration: none; - padding: 0 1em 0 0; -} - -a.doctest-failure-link:visited { - color: #00f; -} - -a.doctest-failure-link:hover { - text-decoration: underline; -} - -/* Comparison table */ - -.doctest-comparison-table { - border: 1px solid #000; - /* FIXME: not sure why this doesn't keep the table limited to 100% */ - width: 100%; -} - -.doctest-comparison-table td { - overflow: auto; -} - -.doctest-comparison-header th { - background-color: #000; - color: #fff; -} - -.doctest-comparison-error td { - background-color: #fdd; -} - -td.doctest-comparison-got { - padding-right: 1em; - color: #060; -} - -td.doctest-comparison-expected { - color: #900; -} diff --git a/togetherjs/tests/doctestjs/doctest.js b/togetherjs/tests/doctestjs/doctest.js deleted file mode 100644 index cc42a68ca..000000000 --- a/togetherjs/tests/doctestjs/doctest.js +++ /dev/null @@ -1,2410 +0,0 @@ -(function (exports) { - -// Some Node.js globals: -/*global global, require, exports */ -// Some browser globals: -/*global console */ -// Some doctest.js globals: -/*global writeln, wait, doctest:true, doctestReporterHook, esprima:true, JSHINT:true */ - -var globalObject; -if (typeof window == 'undefined') { - if (typeof global == 'undefined') { - globalObject = (function () {return this;})(); - } else { - globalObject = global; - } -} else { - globalObject = window; -} - -var doc; -if (typeof document != 'undefined') { - doc = document; -} else { - doc = null; -} - -exports.setDocument = function (newDocument) { - doc = newDocument; -}; - -var Example = exports.Example = function (runner, expr, expected, attrs) { - this.runner = runner; - this.expr = expr; - if (typeof expected != "string") { - throw "Bad value for expected: " + expected; - } - this.expected = expected; - if (attrs) { - for (var i in attrs) { - if (attrs.hasOwnProperty(i)) { - this[i] = attrs[i]; - } - } - } -}; - -Example.prototype = { - run: function () { - this.output = []; - this.consoleOutput = []; - var globs = this.runner.evalInit(); - try { - this.result = this.runner.evaller(this.expr, globs, this.filename); - } catch (e) { - if (e && e['doctest.abort']) { - return; - } - this.write('Error: ' + e + '\n'); - // FIXME: doesn't format nicely: - if (e && e.stack) { - console.log('Exception Stack:'); - console.log(e.stack); - } - } - }, - check: function () { - var output = this.output.join(''); - // FIXME: consider using this.result - this.runner.matcher.match(this, output, this.expected); - }, - write: function (text) { - this.output.push(text); - }, - writeConsole: function (message) { - this.consoleOutput.push(message); - }, - clearConsole: function () { - this.consoleOutput = []; - }, - timeout: function (passed) { - this.runner.reporter.logFailure(this, "Error: wait timed out after " + passed + " milliseconds"); - }, - textSummary: function () { - return strip(strip(this.expr).substr(0, 20)) + '...'; - } -}; - -var Matcher = exports.Matcher = function (runner) { - this.runner = runner; -}; - -Matcher.prototype = { - match: function (example, got, expected) { - var cleanGot = this.clean(got); - var cleanExpected = this.clean(expected); - var regexp = this.makeRegex(cleanExpected); - if (cleanGot.search(regexp) != -1) { - this.runner.reporter.logSuccess(example, got); - return; - } - var comparisonTable = this.makeComparisonTable(cleanGot, cleanExpected); - this.runner.reporter.logFailure(example, got, comparisonTable); - }, - - makeComparisonTable: function (cleanGot, cleanExpected) { - var gotLines = this.splitLines(cleanGot); - var expectedLines = this.splitLines(cleanExpected); - if (gotLines.length <= 1 || expectedLines.length <= 1) { - return null; - } - var comparisonTable = []; - comparisonTable.push({header: 'Details of mismatch:'}); - var shownTrailing = false; - var matching = 0; - for (var i=0; i= expectedLines.length) { - if (! shownTrailing) { - comparisonTable.push({header: 'Trailing lines in got:'}); - shownTrailing = true; - } - comparisonTable.push({got: gotLines[i], error: true}); - } else { - var regexp = this.makeRegex(expectedLines[i]); - var error = gotLines[i].search(regexp) == -1; - comparisonTable.push({got: gotLines[i], expected: expectedLines[i], error: error}); - if (! error) { - matching++; - } - } - } - if (matching <= 1) { - return null; - } - if (expectedLines.length > gotLines.length) { - comparisonTable.push({header: 'Trailing expected line(s):'}); - for (i=gotLines.length; i' + - '
' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '' + - '
Passed:0
Failures:0
' - ); - this.successEl = doc.getElementById('doctest-success-count'); - this.failureEl = doc.getElementById('doctest-failure-count'); - this.failureLinksEl = doc.getElementById('doctest-failure-links'); - var button = doc.getElementById('doctest-reload'); - // Sometimes this is sticky: - button.disabled = false; - button.addEventListener('click', function (ev) { - button.innerHTML = 'reloading...'; - button.disabled = true; - location.reload(); - }, false); -}; - -HTMLReporter.prototype = { - - logSuccess: function (example, got) { - var num = parseInt(this.successEl.innerHTML.split('/')[0], 10); - num++; - this.successEl.innerHTML = num+' / '+this.runner.examples.length; - addClass(this.successEl, 'doctest-nonzero'); - if (example.htmlSpan) { - addClass(example.htmlSpan, 'doctest-success'); - if (example.expected.indexOf('...') != -1 || - example.expected.indexOf('?') != -1) { - this.addExampleNote(example, 'Output:', 'doctest-actual-output', got || '(none)'); - } - } - this.showConsoleOutput(example, false); - this.runner._hook('reportSuccess', example, got); - }, - - logFailure: function (example, got, comparisonTable) { - this.addFailure(); - if (example.htmlSpan) { - addClass(example.htmlSpan, 'doctest-failure'); - var showGot = got || '(nothing output)'; - var expectedSpan = makeElement('span', {className: 'doctest-description'}, ['Expected:\n']); - example.htmlSpan.insertBefore(expectedSpan, example.htmlSpan.querySelector('.doctest-output')); - if (! example.expected) { - example.htmlSpan.querySelector('.doctest-output').innerHTML = '(nothing expected)\n'; - } - this.addExampleNote(example, 'Got:', 'doctest-actual-output', showGot); - } - if (comparisonTable) { - this.addComparisonTable(example, comparisonTable); - } - if (example.blockEl) { - addClass(example.blockEl, 'doctest-some-failure'); - } - if (example.htmlID) { - var anchor = makeElement('a', {href: '#' + example.htmlID, className: 'doctest-failure-link'}, [example.textSummary()]); - this.failureLinksEl.appendChild(anchor); - if (example.htmlID == positionOnFailure) { - location.hash = '#' + example.htmlID; - } - } - this.showConsoleOutput(example, true); - this.runner._hook('reportFailure', example, got); - }, - - logAbort: function (example, abortMessage) { - this.addFailure(); - this.addAborted(abortMessage); - if (example.htmlSpan) { - addClass(example.htmlSpan, 'doctest-failure'); - } - if (example.blockEl) { - addClass(example.blockEl, 'doctest-some-failure'); - } - this.addExampleNote(example, 'Aborted:', 'doctest-actual-output', abortMessage); - this.runner._hook('reportAbort', example, abortMessage); - }, - - addFailure: function () { - var num = parseInt(this.failureEl.innerHTML, 10); - num++; - this.failureEl.innerHTML = num+''; - addClass(this.failureEl, 'doctest-nonzero'); - }, - - addAborted: function (message) { - doc.getElementById('doctest-abort-row').style.display = ''; - var td = doc.getElementById('doctest-aborted'); - td.appendChild(doc.createTextNode(message)); - }, - - showConsoleOutput: function (example, error) { - if (! example.consoleOutput.length) { - return; - } - if (! example.htmlSpan) { - return; - } - var text = example.consoleOutput.join('\n'); - this.addExampleNote(example, 'Console:', 'doctest-console', text); - }, - - addExampleNote: function (example, description, className, text) { - if (! example.htmlSpan) { - return; - } - example.htmlSpan.appendChild(makeElement('span', {className: 'doctest-description'}, [description + '\n'])); - example.htmlSpan.appendChild(makeElement('span', {className: className}, [text + '\n'])); - }, - - addComparisonTable: function (example, comparisonTable) { - if (! example.htmlSpan) { - // FIXME; should display text table - return; - } - var table = makeElement('table', {className: 'doctest-comparison-table'}); - for (var i=0; i (this.maxLen - indentString.length)) { - this.popSeen(seenPosition); - ostring = this.multilineObjRepr(obj, indentString); - } - this.popSeen(seenPosition); - return ostring; - }, - - multilineObjRepr: function (obj, indentString) { - var keys = sortedKeys(obj); - var ostring = '{\n'; - for (var i=0; i (this.maxLen + indentString.length)) { - this.popSeen(seenPosition); - s = this.multilineArrayRepr(obj, indentString); - } - this.popSeen(seenPosition); - return s; - }, - - multilineArrayRepr: function (obj, indentString) { - var s = "[\n"; - for (var i=0; i"; - } - if (el.nodeType == el.DOCUMENT_TYPE_NODE) { - return ""; - } - var tagName = el.tagName || "(no tag)"; - var s = '<' + tagName.toLowerCase(); - var attrs = []; - if (el.attributes && el.attributes.length) { - for (i=0; i'; - return s; - }, - - xhrRepr: function (req, indentString) { - var s = '[XMLHttpRequest '; - var states = { - 0: 'UNSENT', - 1: 'OPENED', - 2: 'HEADERS_RECEIVED', - 3: 'LOADING', - 4: 'DONE' - }; - s += states[req.readyState]; - if (req.readyState == 4) { - s += ' ' + req.status + ' ' + req.statusText; - } - return s + ']'; - }, - - registry: [ - [function (o) { - return typeof o == 'string'; - }, - function (o) { - o = '"' + o.replace(/([\"\\])/g, '\\$1') + '"'; - o = o.replace(/[\f]/g, "\\f") - .replace(/[\b]/g, "\\b") - .replace(/[\n]/g, "\\n") - .replace(/[\t]/g, "\\t") - .replace(/[\r]/g, "\\r"); - return o; - } - ], - [function (o) { - return typeof o == 'number'; - }, - function (o) { - return o + ""; - } - ], - [function (o) { - return typeof o == 'object' && o.nodeType; - }, - "xmlRepr" - ], - [function (o) { - var typ = typeof o; - if ((typ != 'object' && ! (typ == 'function' && typeof o.item == 'function')) || - o === null || - typeof o.length != 'number' || - o.nodeType === 3) { - return false; - } - return true; - }, - "arrayRepr" - ], - [function (o) { - return typeof XMLHttpRequest !== 'undefined' && o instanceof XMLHttpRequest; - - }, - 'xhrRepr' - ] - ] - -}; - -repr.register = function (condition, reprFunc) { - repr.ReprClass.prototype.registry.push([condition, reprFunc]); -}; - -var Runner = exports.Runner = function (options) { - this.examples = []; - options = options || {}; - for (var i in options) { - if (options.hasOwnProperty(i)) { - if (this[i] === undefined) { - throw 'Unexpected option: ' + i; - } - this[i] = options[i]; - } - } -}; - -Runner.prototype = { - - init: function () { - if (this.matcher === null) { - this.matcher = this.makeMatcher(); - } - if (this.reporter === null) { - this.reporter = this.makeReporter(); - } - if (this.repr === null) { - this.repr = this.makeRepr(); - } - this._hook('init', this); - }, - - run: function () { - this.init(); - if (! this.examples.length) { - throw 'No examples have been added'; - } - this._exampleIndex = 0; - this._runExample(); - }, - - evalInit: function () { - if (typeof this.globs != "undefined") { - return this.globs; - } - this.logGrouped = false; - this._abortCalled = false; - var globs = { - write: this.write.bind(this), - writeln: this.writeln.bind(this), - printResolved: this.printResolved.bind(this), - wait: this.wait.bind(this), - Abort: this.Abort.bind(this), - repr: repr, - Spy: Spy, - jshint: jshint - }; - globs.print = globs.writeln; - var consoleOverwrites = { - log: this.logFactory(null, console.log), - warn: this.logFactory(null, console.warn), - error: this.logFactory(null, console.error), - info: this.logFactory(null, console.info), - clear: this.clearLogs.bind(this) - }; - if (typeof window == 'undefined') { - // Can't just overwrite the console object - globs.console = consoleOverwrites; - for (var i in console) { - if (console.hasOwnProperty(i) && (! globs.console.hasOwnProperty(i))) { - if (console[i].bind) { - globs.console[i] = console[i].bind(console); - } else { - globs.console[i] = console[i]; - } - } - } - var context = require('vm').Script.createContext(); - extend(context, globs); - return context; - } else { - extend(console, consoleOverwrites); - window.onerror = this.windowOnerror; - extend(window, globs); - return null; - } - }, - - write: function (text) { - this._currentExample.write(text); - }, - - writeln: function () { - for (var i=0; i= this.examples.length) { - this._finish(); - break; - } - this._currentExample = this.examples[this._exampleIndex]; - this._exampleIndex++; - this._currentExample.run(); - if (this._exampleWait && ! this._abortCalled) { - this._runWait(); - break; - } - this.evalUninit(); - this._currentExample.check(); - if (this._abortCalled) { - // FIXME: this should show that while finished, and maybe successful, - // the tests were aborted - this.reporter.logAbort(this._currentExample, this._abortCalled); - this._finish(); - break; - } - this._currentExample = null; - } - }, - - _runWait: function () { - var start = Date.now(); - var waitTimeout = this._waitTimeout || this._defaultWaitTimeout; - this._waitTimeout = null; - var self = this; - function poll() { - var now = Date.now(); - var cond = self._waitCondition; - if (typeof cond == "number") { - if (now - start >= cond) { - self._exampleWait = false; - } - } else if (cond) { - if (cond()) { - self._exampleWait = false; - } - } - if (self._exampleWait) { - if (now - start > waitTimeout) { - self._currentExample.timeout(now - start); - } else { - setTimeout(poll, self._waitPollTime); - return; - } - } - self.evalUninit(); - self._currentExample.check(); - self._currentExample = null; - self._runExample(); - } - // FIXME: instead of the poll time, cond could be used if it is a number - setTimeout(poll, this._waitPollTime); - }, - - _hook: function (method) { - if (typeof doctestReporterHook == "undefined") { - return null; - } else if (method && arguments.length > 1 && doctestReporterHook[method]) { - var args = argsToArray(arguments).slice(1); - return doctestReporterHook[method].apply(doctestReporterHook, args); - } else if (method) { - return doctestReporterHook[method]; - } else { - return doctestReporterHook; - } - }, - - _finish: function () { - if (attemptedHash && location.hash == attemptedHash) { - // This fixes up the anchor position after tests have run. - // FIXME: would be nice to detect if the user has scrolled between - // page load and the current moment - location.hash = ''; - location.hash = attemptedHash; - } - this._hook('finish', this); - }, - - _waitPollTime: 100, - _waitTimeout: null, - _waitCondition: null, - _defaultWaitTimeout: 5000, - - /* Dependency Injection, yay! */ - examples: null, - Example: Example, - exampleOptions: null, - makeExample: function (text, expected, filename) { - var options = {filename: filename}; - extend(options, this.exampleOptions); - return new this.Example(this, text, expected, options); - }, - matcher: null, - Matcher: Matcher, - matcherOptions: null, - makeMatcher: function () { - return new this.Matcher(this, this.matcherOptions); - }, - reporter: null, - Reporter: HTMLReporter, - reporterOptions: null, - makeReporter: function () { - return new this.Reporter(this, this.reporterOptions); - }, - repr: repr -}; - -var HTMLParser = exports.HTMLParser = function (runner, containerEl, selector) { - this.runner = runner; - containerEl = containerEl || doc.body; - if (typeof containerEl == 'string') { - containerEl = doc.getElementById(containerEl); - } - if (! containerEl) { - throw 'Bad/null/missing containerEl'; - } - this.containerEl = containerEl; - this.selector = selector || 'pre.doctest, pre.commenttest, pre.test'; -}; - -HTMLParser.prototype = { - parse: function () { - var els = this.findEls(); - for (var i=0; i/.test(line)) { - if (! exampleLines.length) { - throw ('Bad example: ' + this.runner.repr(line) + '\n' + - '> line not preceded by $'); - } - rawExample.push(line); - line = line.replace(/^ *> ?/, ''); - exampleLines.push(line); - } else { - rawOutput.push(line); - outputLines.push(line); - } - } - return result; - }, - - parseCommentEl: function (el) { - if (typeof esprima == "undefined") { - if (typeof require != "undefined") { - esprima = require("./esprima/esprima.js"); - } else { - throw 'You must install or include esprima.js'; - } - } - var contents = getElementText(el); - var ast = esprima.parse(contents, { - range: true, - comment: true - }); - var pos = 0; - var result = []; - for (var i=0; i/) == -1) { - // Not a comment we care about - continue; - } - var start = comment.range[0]; - var end = comment.range[1]; - var example = contents.substr(pos, start-pos); - var output = comment.value.replace(/^\s*=> ?/, ''); - var orig = comment.type == 'Block' ? '/*' + comment.value + '*/' : '//' + comment.value; - if (example === '') { - result[result.length-1][1] += '\n'+output; - result[result.length-1][3] += '\n'+orig; - } - else { - result.push([example, output, example, orig]); - } - pos = end; - } - var last = contents.substr(pos, contents.length-pos); - if (strip(last)) { - result.push([last, '', last, '']); - } - return result; - }, - - loadRemotes: function (callback, selector) { - var els; - if (! selector) { - els = this.findEls(); - } else { - els = document.querySelectorAll(selector); - } - var pending = 0; - argsToArray(els).forEach(function (el) { - var href = el.getAttribute('data-href-pattern'); - if (href) { - try { - href = this.fillPattern(href); - } catch (e) { - var text = '// Error resolving data-href-pattern "' + href + '":\n'; - text += '// ' + e; - el.innerHTML = ''; - el.appendChild(document.createTextNode(text)); - return; - } - } - if (! href) { - href = el.getAttribute('href'); - } - if (! href) { - href = el.getAttribute('src'); - } - if (! href) { - return; - } - pending++; - var req = new XMLHttpRequest(); - if (href.indexOf('?') == -1) { - // Try to stop some caching: - href += '?nocache=' + Date.now(); - } - req.open('GET', href); - req.setRequestHeader('Cache-Control', 'no-cache, max-age=0'); - req.onreadystatechange = (function () { - if (req.readyState != 4) { - return; - } - if (req.status != 200 && !(req.status === 0 && document.location.protocol == "file:")) { - el.appendChild(doc.createTextNode('\n// Error fetching ' + href + ' status: ' + req.status)); - } else { - this.fillElement(el, req.responseText); - } - pending--; - if (! pending) { - callback(); - } - }).bind(this); - req.send(); - }, this); - if (! pending) { - callback(); - } - }, - - fillPattern: function (pattern) { - var regex = /\{([^\}]+)\}/; - var result = ''; - while (true) { - var match = regex.exec(pattern); - if (! match) { - result += pattern; - break; - } - result += pattern.substr(0, match.index); - pattern = pattern.substr(match.index + match[0].length); - var name = match[1]; - var restriction = "^[\\w_\\-\\.]+$"; - var defaultValue = ''; - if (name.lastIndexOf('|') != -1) { - defaultValue = name.substr(name.lastIndexOf('|')+1); - name = name.substr(0, name.lastIndexOf('|')); - } - if (name.indexOf(':') != -1) { - restriction = name.substr(name.indexOf(':')+1); - name = name.substr(0, name.indexOf(':')); - } - var value = params[name]; - if (! value) { - value = defaultValue; - } - if (restriction && value.search(new RegExp(restriction)) == -1) { - throw 'Bad substitution for {' + name + ':' + restriction + '}: "' + value + '"'; - } - result += value; - } - return result; - }, - - fillElement: function (el, text) { - el.innerHTML = ''; - if (hasClass(el, 'commenttest') || hasClass(el, 'test')) { - var texts = this.splitText(text); - console.log("filling in tests", texts, el); - if (texts && texts.length == 1 && ! texts[0].header) { - el.appendChild(document.createTextNode(texts[0].body)); - } else if (texts && texts.length) { - for (var i=0; i/) == -1) { - // Not a comment we care about - continue; - } - var start = comment.range[0]; - var end = comment.range[1]; - var example = this.text.substr(pos, start-pos); - var output = comment.value.replace(/^\s*=>\s*/, ''); - var ex = this.runner.makeExample(example, output, this.filename); - this.runner.examples.push(ex); - pos = end; - } - var last = this.text.substr(pos, this.text.length-pos); - if (strip(last)) { - this.runner.examples.push(this.runner.makeExample(last, '', this.filename)); - } - } -}; - -var strip = exports.strip = function (str) { - str = str + ""; - return str.replace(/\s+$/, "").replace(/^\s+/, ""); -}; - -var rstrip = exports.rstrip = function (str) { - str = str + ""; - return str.replace(/\s+$/, ""); -}; - -var argsToArray = exports.argToArray = function (args) { - var array = []; - for (var i=0; i ' + repr(current, indentString)); - return; - } - indentString = indentString || ''; - var diff = objDiff(orig, current); - var i, keys; - var any = false; - keys = sortedKeys(diff.added); - for (i=0; i ' - + repr(diff.changed[keys[i]][1], indentString)); - } - if (! any) { - print(indentString + '(no changes)'); - } -}; - -var sortedKeys = exports.sortedKeys = function (obj) { - var keys = []; - for (var i in obj) { - if (obj.hasOwnProperty(i)) { - keys.push(i); - } - } - keys.sort(); - return keys; -}; - -var Spy = exports.Spy = function (name, options, extraOptions) { - var self; - name = name || 'spy'; - if (Spy.spies[name]) { - self = Spy.spies[name]; - if ((! options) && ! extraOptions) { - return self; - } - } else { - self = function () { - return self.func.apply(this, arguments); - }; - } - options = options || {}; - if (typeof options == 'function') { - options = {applies: options}; - } - if (extraOptions) { - extendDefault(options, extraOptions); - } - extendDefault(options, Spy.defaultOptions); - self._name = name; - self.options = options; - self.called = false; - self.calledWait = false; - self.args = null; - self.self = null; - self.argList = []; - self.selfList = []; - self.writes = options.writes || false; - self.returns = options.returns || undefined; - self.applies = options.applies || null; - self.throwError = options.throwError || null; - self.ignoreThis = options.ignoreThis || false; - self.wrapArgs = options.wrapArgs || false; - self.func = function () { - self.called = true; - self.calledWait = true; - self.args = argsToArray(arguments); - self.self = this; - self.argList.push(self.args); - self.selfList.push(this); - // It might be possible to get the caller? - if (self.writes) { - if (typeof writeln == "undefined") { - console.warn("Spy writing outside of test:", self.formatCall()); - } else { - writeln(self.formatCall()); - } - } - if (self.throwError) { - var throwError = self.throwError; - if (typeof throwError == "function") { - throwError = self.throwError.apply(this, arguments); - } - throw throwError; - } - if (self.applies) { - try { - return self.applies.apply(this, arguments); - } catch (e) { - console.error('Error in ' + self.repr() + '.applies:', e); - throw e; - } - } - return self.returns; - }; - self.func.toString = function () { - return "Spy('" + self._name + "').func"; - }; - - // Method definitions: - self.formatCall = function () { - var s = ''; - if ((! self.ignoreThis) && self.self !== globalObject && self.self !== self) { - s += repr(self.self) + '.'; - } - s += self._name; - if (self.args === null) { - return s + ':never called'; - } - s += '('; - // This eliminates trailing undefined arguments: - var length = self.args.length; - while (length && self.args[length-1] === undefined) { - length--; - } - for (var i=0; i(<(div|p)[^>]*>)?/g, '\n'); - report = report.replace(/<(div|p)[^>]*>/g, '\n'); - report = report.replace(/<[^>]*>/g, ' '); - report = report.replace(/ +/g, ' '); - console.log('Report:', report); - */ - } - done = true; - }; - req.send(); - wait(function () {return done;}); -} - -function _removeJshintSections(text) { - /* Removes anything surrounded with a comment like: - // jshint-ignore - ... - // jshint-endignore - - It replaces these with whitespace so character and line counts still work. - */ - var result = ''; - var start = /(\/\/|\/\*)\s*jshint-ignore/i; - var end = /jshint-endignore\s*(\*\/)?/i; - while (true) { - var match = text.search(start); - if (match == -1) { - result += text; - break; - } - result += text.substr(0, match); - text = text.substr(match); - match = end.exec(text); - if (! match) { - // throw everything left away. Warn? - break; - } - var endPos = match.index + match[0].length; - var skipped = text.substr(0, endPos); - text = text.substr(endPos); - // Maintain line numbers: - skipped = skipped.replace(/[^\n]/g, ' '); - result += skipped; - } - return result; -} - - -exports.jshint = jshint; - -function NosyXMLHttpRequest(name, req) { - if (this === globalObject) { - throw 'You forgot *new* NosyXMLHttpRequest(' + repr(name) + ')'; - } - if (! name) { - throw 'The name argument is required'; - } - if (typeof name != "string") { - throw 'Wrong type of argument for name: ' + name; - } - if (! req) { - req = new NosyXMLHttpRequest.realXMLHttpRequest(); - } - this._name = name; - this._req = req; - this._method = null; - this._data = null; - this._url = null; - this._headers = {}; - this.abort = printWrap(this._req, 'abort', this._name); - this.getAllResponseHeaders = this._req.getAllResponseHeaders.bind(this._req); - this.getResponseHeader = this._req.getResponseHeader.bind(this._req); - this.open = printWrap(this._req, 'open', this._name, (function (method, url) { - this._method = method; - this._url = url; - }).bind(this)); - this.overrideMimeType = printWrap(this._req, 'overrideMimeType', this._name); - this.send = printWrap(this._req, 'send', this._name, (function (data) { - if (this.timeout !== undefined) { - this._req.timeout = this.timeout; - } - if (this.withCredentials !== undefined) { - this._req.withCredentials = this.withCredentials; - } - this._data = data; - }).bind(this)); - this.setRequestHeader = printWrap(this._req, 'setRequestHeader', this._name, (function (name, value) { - this._headers[name] = value; - }).bind(this)); - this.onreadystatechange = null; - this._req.onreadystatechange = (function () { - this.readyState = this._req.readyState; - if (this.readyState >= this.HEADERS_RECEIVED) { - var props = ['response', 'responseText', 'responseType', - 'responseXML', 'status', 'statusText', 'upload']; - - for (var i=0; i";TokenName[Token.Identifier]="Identifier";TokenName[Token.Keyword]="Keyword";TokenName[Token.NullLiteral]="Null";TokenName[Token.NumericLiteral]="Numeric"; -TokenName[Token.Punctuator]="Punctuator";TokenName[Token.StringLiteral]="String";TokenName[Token.RegularExpression]="RegularExpression";FnExprTokens=["(","{","[","in","typeof","instanceof","new","return","case","delete","throw","void","=","+=","-=","*=","/=","%=","<<=",">>=",">>>=","&=","|=","^=",",","+","-","*","/","%","++","--","<<",">>",">>>","&","|","^","!","~","&&","||","?",":","===","==",">=","<=","<",">","!=","!=="]; -Syntax={AssignmentExpression:"AssignmentExpression",ArrayExpression:"ArrayExpression",BlockStatement:"BlockStatement",BinaryExpression:"BinaryExpression",BreakStatement:"BreakStatement",CallExpression:"CallExpression",CatchClause:"CatchClause",ConditionalExpression:"ConditionalExpression",ContinueStatement:"ContinueStatement",DoWhileStatement:"DoWhileStatement",DebuggerStatement:"DebuggerStatement",EmptyStatement:"EmptyStatement",ExpressionStatement:"ExpressionStatement",ForStatement:"ForStatement",ForInStatement:"ForInStatement",FunctionDeclaration:"FunctionDeclaration",FunctionExpression:"FunctionExpression",Identifier:"Identifier",IfStatement:"IfStatement",Literal:"Literal",LabeledStatement:"LabeledStatement",LogicalExpression:"LogicalExpression",MemberExpression:"MemberExpression",NewExpression:"NewExpression",ObjectExpression:"ObjectExpression",Program:"Program",Property:"Property",ReturnStatement:"ReturnStatement",SequenceExpression:"SequenceExpression",SwitchStatement:"SwitchStatement",SwitchCase:"SwitchCase",ThisExpression:"ThisExpression",ThrowStatement:"ThrowStatement",TryStatement:"TryStatement",UnaryExpression:"UnaryExpression",UpdateExpression:"UpdateExpression",VariableDeclaration:"VariableDeclaration",VariableDeclarator:"VariableDeclarator",WhileStatement:"WhileStatement",WithStatement:"WithStatement"}; -PropertyKind={Data:1,Get:2,Set:4};Messages={UnexpectedToken:"Unexpected token %0",UnexpectedNumber:"Unexpected number",UnexpectedString:"Unexpected string",UnexpectedIdentifier:"Unexpected identifier",UnexpectedReserved:"Unexpected reserved word",UnexpectedEOS:"Unexpected end of input",NewlineAfterThrow:"Illegal newline after throw",InvalidRegExp:"Invalid regular expression",UnterminatedRegExp:"Invalid regular expression: missing /",InvalidLHSInAssignment:"Invalid left-hand side in assignment",InvalidLHSInForIn:"Invalid left-hand side in for-in",MultipleDefaultsInSwitch:"More than one default clause in switch statement",NoCatchOrFinally:"Missing catch or finally after try",UnknownLabel:"Undefined label '%0'",Redeclaration:"%0 '%1' has already been declared",IllegalContinue:"Illegal continue statement",IllegalBreak:"Illegal break statement",IllegalReturn:"Illegal return statement",StrictModeWith:"Strict mode code may not include a with statement",StrictCatchVariable:"Catch variable may not be eval or arguments in strict mode",StrictVarName:"Variable name may not be eval or arguments in strict mode",StrictParamName:"Parameter name eval or arguments is not allowed in strict mode",StrictParamDupe:"Strict mode function may not have duplicate parameter names",StrictFunctionName:"Function name may not be eval or arguments in strict mode",StrictOctalLiteral:"Octal literals are not allowed in strict mode.",StrictDelete:"Delete of an unqualified identifier in strict mode.",StrictDuplicateProperty:"Duplicate data property in object literal not allowed in strict mode",AccessorDataProperty:"Object literal may not have data and accessor property with the same name",AccessorGetSet:"Object literal may not have multiple get/set accessors with the same name",StrictLHSAssignment:"Assignment to eval or arguments is not allowed in strict mode",StrictLHSPostfix:"Postfix increment/decrement may not have eval or arguments operand in strict mode",StrictLHSPrefix:"Prefix increment/decrement may not have eval or arguments operand in strict mode",StrictReservedWord:"Use of future reserved word in strict mode"}; -Regex={NonAsciiIdentifierStart:new RegExp("[\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0620-\u064a\u066e\u066f\u0671-\u06d3\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07ca-\u07ea\u07f4\u07f5\u07fa\u0800-\u0815\u081a\u0824\u0828\u0840-\u0858\u08a0\u08a2-\u08ac\u0904-\u0939\u093d\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097f\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc\u09dd\u09df-\u09e1\u09f0\u09f1\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a59-\u0a5c\u0a5e\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0\u0ae1\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b5c\u0b5d\u0b5f-\u0b61\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58\u0c59\u0c60\u0c61\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0\u0ce1\u0cf1\u0cf2\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d\u0d4e\u0d60\u0d61\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0edc-\u0edf\u0f00\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8c\u1000-\u102a\u103f\u1050-\u1055\u105a-\u105d\u1061\u1065\u1066\u106e-\u1070\u1075-\u1081\u108e\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191c\u1950-\u196d\u1970-\u1974\u1980-\u19ab\u19c1-\u19c7\u1a00-\u1a16\u1a20-\u1a54\u1aa7\u1b05-\u1b33\u1b45-\u1b4b\u1b83-\u1ba0\u1bae\u1baf\u1bba-\u1be5\u1c00-\u1c23\u1c4d-\u1c4f\u1c5a-\u1c7d\u1ce9-\u1cec\u1cee-\u1cf1\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3007\u3021-\u3029\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua61f\ua62a\ua62b\ua640-\ua66e\ua67f-\ua697\ua6a0-\ua6ef\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8f2-\ua8f7\ua8fb\ua90a-\ua925\ua930-\ua946\ua960-\ua97c\ua984-\ua9b2\ua9cf\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa60-\uaa76\uaa7a\uaa80-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaadd\uaae0-\uaaea\uaaf2-\uaaf4\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabe2\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe70-\ufe74\ufe76-\ufefc\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]"),NonAsciiIdentifierPart:new RegExp("[\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0300-\u0374\u0376\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u0483-\u0487\u048a-\u0527\u0531-\u0556\u0559\u0561-\u0587\u0591-\u05bd\u05bf\u05c1\u05c2\u05c4\u05c5\u05c7\u05d0-\u05ea\u05f0-\u05f2\u0610-\u061a\u0620-\u0669\u066e-\u06d3\u06d5-\u06dc\u06df-\u06e8\u06ea-\u06fc\u06ff\u0710-\u074a\u074d-\u07b1\u07c0-\u07f5\u07fa\u0800-\u082d\u0840-\u085b\u08a0\u08a2-\u08ac\u08e4-\u08fe\u0900-\u0963\u0966-\u096f\u0971-\u0977\u0979-\u097f\u0981-\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bc-\u09c4\u09c7\u09c8\u09cb-\u09ce\u09d7\u09dc\u09dd\u09df-\u09e3\u09e6-\u09f1\u0a01-\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3c\u0a3e-\u0a42\u0a47\u0a48\u0a4b-\u0a4d\u0a51\u0a59-\u0a5c\u0a5e\u0a66-\u0a75\u0a81-\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abc-\u0ac5\u0ac7-\u0ac9\u0acb-\u0acd\u0ad0\u0ae0-\u0ae3\u0ae6-\u0aef\u0b01-\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3c-\u0b44\u0b47\u0b48\u0b4b-\u0b4d\u0b56\u0b57\u0b5c\u0b5d\u0b5f-\u0b63\u0b66-\u0b6f\u0b71\u0b82\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe-\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcd\u0bd0\u0bd7\u0be6-\u0bef\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d-\u0c44\u0c46-\u0c48\u0c4a-\u0c4d\u0c55\u0c56\u0c58\u0c59\u0c60-\u0c63\u0c66-\u0c6f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbc-\u0cc4\u0cc6-\u0cc8\u0cca-\u0ccd\u0cd5\u0cd6\u0cde\u0ce0-\u0ce3\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d44\u0d46-\u0d48\u0d4a-\u0d4e\u0d57\u0d60-\u0d63\u0d66-\u0d6f\u0d7a-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dca\u0dcf-\u0dd4\u0dd6\u0dd8-\u0ddf\u0df2\u0df3\u0e01-\u0e3a\u0e40-\u0e4e\u0e50-\u0e59\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb9\u0ebb-\u0ebd\u0ec0-\u0ec4\u0ec6\u0ec8-\u0ecd\u0ed0-\u0ed9\u0edc-\u0edf\u0f00\u0f18\u0f19\u0f20-\u0f29\u0f35\u0f37\u0f39\u0f3e-\u0f47\u0f49-\u0f6c\u0f71-\u0f84\u0f86-\u0f97\u0f99-\u0fbc\u0fc6\u1000-\u1049\u1050-\u109d\u10a0-\u10c5\u10c7\u10cd\u10d0-\u10fa\u10fc-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u135d-\u135f\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u167f\u1681-\u169a\u16a0-\u16ea\u16ee-\u16f0\u1700-\u170c\u170e-\u1714\u1720-\u1734\u1740-\u1753\u1760-\u176c\u176e-\u1770\u1772\u1773\u1780-\u17d3\u17d7\u17dc\u17dd\u17e0-\u17e9\u180b-\u180d\u1810-\u1819\u1820-\u1877\u1880-\u18aa\u18b0-\u18f5\u1900-\u191c\u1920-\u192b\u1930-\u193b\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19d9\u1a00-\u1a1b\u1a20-\u1a5e\u1a60-\u1a7c\u1a7f-\u1a89\u1a90-\u1a99\u1aa7\u1b00-\u1b4b\u1b50-\u1b59\u1b6b-\u1b73\u1b80-\u1bf3\u1c00-\u1c37\u1c40-\u1c49\u1c4d-\u1c7d\u1cd0-\u1cd2\u1cd4-\u1cf6\u1d00-\u1de6\u1dfc-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200c\u200d\u203f\u2040\u2054\u2071\u207f\u2090-\u209c\u20d0-\u20dc\u20e1\u20e5-\u20f0\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2160-\u2188\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d7f-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2de0-\u2dff\u2e2f\u3005-\u3007\u3021-\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u3099\u309a\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31ba\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fcc\ua000-\ua48c\ua4d0-\ua4fd\ua500-\ua60c\ua610-\ua62b\ua640-\ua66f\ua674-\ua67d\ua67f-\ua697\ua69f-\ua6f1\ua717-\ua71f\ua722-\ua788\ua78b-\ua78e\ua790-\ua793\ua7a0-\ua7aa\ua7f8-\ua827\ua840-\ua873\ua880-\ua8c4\ua8d0-\ua8d9\ua8e0-\ua8f7\ua8fb\ua900-\ua92d\ua930-\ua953\ua960-\ua97c\ua980-\ua9c0\ua9cf-\ua9d9\uaa00-\uaa36\uaa40-\uaa4d\uaa50-\uaa59\uaa60-\uaa76\uaa7a\uaa7b\uaa80-\uaac2\uaadb-\uaadd\uaae0-\uaaef\uaaf2-\uaaf6\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uabc0-\uabea\uabec\uabed\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\uf900-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40\ufb41\ufb43\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe00-\ufe0f\ufe20-\ufe26\ufe33\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]")}; -function assert(condition,message){if(!condition){throw new Error("ASSERT: "+message)}}function isDecimalDigit(ch){return ch>=48&&ch<=57}function isHexDigit(ch){return"0123456789abcdefABCDEF".indexOf(ch)>=0 -}function isOctalDigit(ch){return"01234567".indexOf(ch)>=0}function isWhiteSpace(ch){return ch===32||ch===9||ch===11||ch===12||ch===160||ch>=5760&&"\u1680\u180e\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\ufeff".indexOf(String.fromCharCode(ch))>0 -}function isLineTerminator(ch){return ch===10||ch===13||ch===8232||ch===8233}function isIdentifierStart(ch){return ch===36||ch===95||ch>=65&&ch<=90||ch>=97&&ch<=122||ch===92||ch>=128&&Regex.NonAsciiIdentifierStart.test(String.fromCharCode(ch)) -}function isIdentifierPart(ch){return ch===36||ch===95||ch>=65&&ch<=90||ch>=97&&ch<=122||ch>=48&&ch<=57||ch===92||ch>=128&&Regex.NonAsciiIdentifierPart.test(String.fromCharCode(ch))}function isFutureReservedWord(id){switch(id){case"class":case"enum":case"export":case"extends":case"import":case"super":return true; -default:return false}}function isStrictModeReservedWord(id){switch(id){case"implements":case"interface":case"package":case"private":case"protected":case"public":case"static":case"yield":case"let":return true; -default:return false}}function isRestrictedWord(id){return id==="eval"||id==="arguments"}function isKeyword(id){if(strict&&isStrictModeReservedWord(id)){return true}switch(id.length){case 2:return id==="if"||id==="in"||id==="do"; -case 3:return id==="var"||id==="for"||id==="new"||id==="try"||id==="let";case 4:return id==="this"||id==="else"||id==="case"||id==="void"||id==="with"||id==="enum";case 5:return id==="while"||id==="break"||id==="catch"||id==="throw"||id==="const"||id==="yield"||id==="class"||id==="super"; -case 6:return id==="return"||id==="typeof"||id==="delete"||id==="switch"||id==="export"||id==="import";case 7:return id==="default"||id==="finally"||id==="extends";case 8:return id==="function"||id==="continue"||id==="debugger"; -case 10:return id==="instanceof";default:return false}}function skipComment(){var ch,blockComment,lineComment;blockComment=false;lineComment=false;while(index=length){throwError({},Messages.UnexpectedToken,"ILLEGAL")}}else{ch=source.charCodeAt(index++);if(index>=length){throwError({},Messages.UnexpectedToken,"ILLEGAL") -}if(ch===42){ch=source.charCodeAt(index);if(ch===47){++index;blockComment=false}}}}else if(ch===47){ch=source.charCodeAt(index+1);if(ch===47){index+=2;lineComment=true}else if(ch===42){index+=2;blockComment=true; -if(index>=length){throwError({},Messages.UnexpectedToken,"ILLEGAL")}}else{break}}else if(isWhiteSpace(ch)){++index}else if(isLineTerminator(ch)){++index;if(ch===13&&source.charCodeAt(index)===10){++index -}++lineNumber;lineStart=index}else{break}}}function scanHexEscape(prefix){var i,len,ch,code=0;len=prefix==="u"?4:2;for(i=0;i"&&ch2===">"&&ch3===">"){if(ch4==="="){index+=4;return{type:Token.Punctuator,value:">>>=",lineNumber:lineNumber,lineStart:lineStart,range:[start,index]} -}}if(ch1===">"&&ch2===">"&&ch3===">"){index+=3;return{type:Token.Punctuator,value:">>>",lineNumber:lineNumber,lineStart:lineStart,range:[start,index]}}if(ch1==="<"&&ch2==="<"&&ch3==="="){index+=3;return{type:Token.Punctuator,value:"<<=",lineNumber:lineNumber,lineStart:lineStart,range:[start,index]} -}if(ch1===">"&&ch2===">"&&ch3==="="){index+=3;return{type:Token.Punctuator,value:">>=",lineNumber:lineNumber,lineStart:lineStart,range:[start,index]}}if(ch1===ch2&&"+-<>&|".indexOf(ch1)>=0){index+=2;return{type:Token.Punctuator,value:ch1+ch2,lineNumber:lineNumber,lineStart:lineStart,range:[start,index]} -}if("<>=!+-*%&|^/".indexOf(ch1)>=0){++index;return{type:Token.Punctuator,value:ch1,lineNumber:lineNumber,lineStart:lineStart,range:[start,index]}}throwError({},Messages.UnexpectedToken,"ILLEGAL")}function scanHexLiteral(start){var number=""; -while(index=0&&index=0){return scanPunctuator()}return scanRegExp()}return scanRegExp()}if(prevToken.type==="Keyword"){return scanRegExp()}return scanPunctuator() -}function advance(){var ch;skipComment();if(index>=length){return{type:Token.EOF,lineNumber:lineNumber,lineStart:lineStart,range:[index,index]}}ch=source.charCodeAt(index);if(ch===40||ch===41||ch===58){return scanPunctuator() -}if(ch===39||ch===34){return scanStringLiteral()}if(isIdentifierStart(ch)){return scanIdentifier()}if(ch===46){if(isDecimalDigit(source.charCodeAt(index+1))){return scanNumericLiteral()}return scanPunctuator() -}if(isDecimalDigit(ch)){return scanNumericLiteral()}if(extra.tokenize&&ch===47){return advanceSlash()}return scanPunctuator()}function lex(){var token;token=lookahead;index=token.range[1];lineNumber=token.lineNumber; -lineStart=token.lineStart;lookahead=advance();index=token.range[1];lineNumber=token.lineNumber;lineStart=token.lineStart;return token}function peek(){var pos,line,start;pos=index;line=lineNumber;start=lineStart; -lookahead=advance();index=pos;lineNumber=line;lineStart=start}SyntaxTreeDelegate={name:"SyntaxTree",markStart:function(){},markEnd:function(node){return node},markGroupEnd:function(node){return node},postProcess:function(node){return node -},createArrayExpression:function(elements){return{type:Syntax.ArrayExpression,elements:elements}},createAssignmentExpression:function(operator,left,right){return{type:Syntax.AssignmentExpression,operator:operator,left:left,right:right} -},createBinaryExpression:function(operator,left,right){var type=operator==="||"||operator==="&&"?Syntax.LogicalExpression:Syntax.BinaryExpression;return{type:type,operator:operator,left:left,right:right} -},createBlockStatement:function(body){return{type:Syntax.BlockStatement,body:body}},createBreakStatement:function(label){return{type:Syntax.BreakStatement,label:label}},createCallExpression:function(callee,args){return{type:Syntax.CallExpression,callee:callee,arguments:args} -},createCatchClause:function(param,body){return{type:Syntax.CatchClause,param:param,body:body}},createConditionalExpression:function(test,consequent,alternate){return{type:Syntax.ConditionalExpression,test:test,consequent:consequent,alternate:alternate} -},createContinueStatement:function(label){return{type:Syntax.ContinueStatement,label:label}},createDebuggerStatement:function(){return{type:Syntax.DebuggerStatement}},createDoWhileStatement:function(body,test){return{type:Syntax.DoWhileStatement,body:body,test:test} -},createEmptyStatement:function(){return{type:Syntax.EmptyStatement}},createExpressionStatement:function(expression){return{type:Syntax.ExpressionStatement,expression:expression}},createForStatement:function(init,test,update,body){return{type:Syntax.ForStatement,init:init,test:test,update:update,body:body} -},createForInStatement:function(left,right,body){return{type:Syntax.ForInStatement,left:left,right:right,body:body,each:false}},createFunctionDeclaration:function(id,params,defaults,body){return{type:Syntax.FunctionDeclaration,id:id,params:params,defaults:defaults,body:body,rest:null,generator:false,expression:false} -},createFunctionExpression:function(id,params,defaults,body){return{type:Syntax.FunctionExpression,id:id,params:params,defaults:defaults,body:body,rest:null,generator:false,expression:false}},createIdentifier:function(name){return{type:Syntax.Identifier,name:name} -},createIfStatement:function(test,consequent,alternate){return{type:Syntax.IfStatement,test:test,consequent:consequent,alternate:alternate}},createLabeledStatement:function(label,body){return{type:Syntax.LabeledStatement,label:label,body:body} -},createLiteral:function(token){return{type:Syntax.Literal,value:token.value,raw:source.slice(token.range[0],token.range[1])}},createMemberExpression:function(accessor,object,property){return{type:Syntax.MemberExpression,computed:accessor==="[",object:object,property:property} -},createNewExpression:function(callee,args){return{type:Syntax.NewExpression,callee:callee,arguments:args}},createObjectExpression:function(properties){return{type:Syntax.ObjectExpression,properties:properties} -},createPostfixExpression:function(operator,argument){return{type:Syntax.UpdateExpression,operator:operator,argument:argument,prefix:false}},createProgram:function(body){return{type:Syntax.Program,body:body} -},createProperty:function(kind,key,value){return{type:Syntax.Property,key:key,value:value,kind:kind}},createReturnStatement:function(argument){return{type:Syntax.ReturnStatement,argument:argument}},createSequenceExpression:function(expressions){return{type:Syntax.SequenceExpression,expressions:expressions} -},createSwitchCase:function(test,consequent){return{type:Syntax.SwitchCase,test:test,consequent:consequent}},createSwitchStatement:function(discriminant,cases){return{type:Syntax.SwitchStatement,discriminant:discriminant,cases:cases} -},createThisExpression:function(){return{type:Syntax.ThisExpression}},createThrowStatement:function(argument){return{type:Syntax.ThrowStatement,argument:argument}},createTryStatement:function(block,guardedHandlers,handlers,finalizer){return{type:Syntax.TryStatement,block:block,guardedHandlers:guardedHandlers,handlers:handlers,finalizer:finalizer} -},createUnaryExpression:function(operator,argument){if(operator==="++"||operator==="--"){return{type:Syntax.UpdateExpression,operator:operator,argument:argument,prefix:true}}return{type:Syntax.UnaryExpression,operator:operator,argument:argument} -},createVariableDeclaration:function(declarations,kind){return{type:Syntax.VariableDeclaration,declarations:declarations,kind:kind}},createVariableDeclarator:function(id,init){return{type:Syntax.VariableDeclarator,id:id,init:init} -},createWhileStatement:function(test,body){return{type:Syntax.WhileStatement,test:test,body:body}},createWithStatement:function(object,body){return{type:Syntax.WithStatement,object:object,body:body}}}; -function peekLineTerminator(){var pos,line,start,found;pos=index;line=lineNumber;start=lineStart;skipComment();found=lineNumber!==line;index=pos;lineNumber=line;lineStart=start;return found}function throwError(token,messageFormat){var error,args=Array.prototype.slice.call(arguments,2),msg=messageFormat.replace(/%(\d)/g,function(whole,index){assert(index>="||op===">>>="||op==="&="||op==="^="||op==="|=" -}function consumeSemicolon(){var line;if(source.charCodeAt(index)===59){lex();return}line=lineNumber;skipComment();if(lineNumber!==line){return}if(match(";")){lex();return}if(lookahead.type!==Token.EOF&&!match("}")){throwUnexpected(lookahead) -}}function isLeftHandSide(expr){return expr.type===Syntax.Identifier||expr.type===Syntax.MemberExpression}function parseArrayInitialiser(){var elements=[];expect("[");while(!match("]")){if(match(",")){lex(); -elements.push(null)}else{elements.push(parseAssignmentExpression());if(!match("]")){expect(",")}}}expect("]");return delegate.createArrayExpression(elements)}function parsePropertyFunction(param,first){var previousStrict,body; -previousStrict=strict;delegate.markStart();body=parseFunctionSourceElements();if(first&&strict&&isRestrictedWord(param[0].name)){throwErrorTolerant(first,Messages.StrictParamName)}strict=previousStrict; -return delegate.markEnd(delegate.createFunctionExpression(null,param,[],body))}function parseObjectPropertyKey(){var token;delegate.markStart();token=lex();if(token.type===Token.StringLiteral||token.type===Token.NumericLiteral){if(strict&&token.octal){throwErrorTolerant(token,Messages.StrictOctalLiteral) -}return delegate.markEnd(delegate.createLiteral(token))}return delegate.markEnd(delegate.createIdentifier(token.value))}function parseObjectProperty(){var token,key,id,value,param;token=lookahead;delegate.markStart(); -if(token.type===Token.Identifier){id=parseObjectPropertyKey();if(token.value==="get"&&!match(":")){key=parseObjectPropertyKey();expect("(");expect(")");value=parsePropertyFunction([]);return delegate.markEnd(delegate.createProperty("get",key,value)) -}if(token.value==="set"&&!match(":")){key=parseObjectPropertyKey();expect("(");token=lookahead;if(token.type!==Token.Identifier){throwUnexpected(lex())}param=[parseVariableIdentifier()];expect(")");value=parsePropertyFunction(param,token); -return delegate.markEnd(delegate.createProperty("set",key,value))}expect(":");value=parseAssignmentExpression();return delegate.markEnd(delegate.createProperty("init",id,value))}if(token.type===Token.EOF||token.type===Token.Punctuator){throwUnexpected(token) -}else{key=parseObjectPropertyKey();expect(":");value=parseAssignmentExpression();return delegate.markEnd(delegate.createProperty("init",key,value))}}function parseObjectInitialiser(){var properties=[],property,name,key,kind,map={},toString=String; -expect("{");while(!match("}")){property=parseObjectProperty();if(property.key.type===Syntax.Identifier){name=property.key.name}else{name=toString(property.key.value)}kind=property.kind==="init"?PropertyKind.Data:property.kind==="get"?PropertyKind.Get:PropertyKind.Set; -key="$"+name;if(Object.prototype.hasOwnProperty.call(map,key)){if(map[key]===PropertyKind.Data){if(strict&&kind===PropertyKind.Data){throwErrorTolerant({},Messages.StrictDuplicateProperty)}else if(kind!==PropertyKind.Data){throwErrorTolerant({},Messages.AccessorDataProperty) -}}else{if(kind===PropertyKind.Data){throwErrorTolerant({},Messages.AccessorDataProperty)}else if(map[key]&kind){throwErrorTolerant({},Messages.AccessorGetSet)}}map[key]|=kind}else{map[key]=kind}properties.push(property); -if(!match("}")){expect(",")}}expect("}");return delegate.createObjectExpression(properties)}function parseGroupExpression(){var expr;delegate.markStart();expect("(");expr=parseExpression();expect(")"); -return delegate.markGroupEnd(expr)}function parsePrimaryExpression(){var type,token,expr;if(match("(")){return parseGroupExpression()}type=lookahead.type;delegate.markStart();if(type===Token.Identifier){expr=delegate.createIdentifier(lex().value) -}else if(type===Token.StringLiteral||type===Token.NumericLiteral){if(strict&&lookahead.octal){throwErrorTolerant(lookahead,Messages.StrictOctalLiteral)}expr=delegate.createLiteral(lex())}else if(type===Token.Keyword){if(matchKeyword("this")){lex(); -expr=delegate.createThisExpression()}else if(matchKeyword("function")){expr=parseFunctionExpression()}}else if(type===Token.BooleanLiteral){token=lex();token.value=token.value==="true";expr=delegate.createLiteral(token) -}else if(type===Token.NullLiteral){token=lex();token.value=null;expr=delegate.createLiteral(token)}else if(match("[")){expr=parseArrayInitialiser()}else if(match("{")){expr=parseObjectInitialiser()}else if(match("/")||match("/=")){expr=delegate.createLiteral(scanRegExp()) -}if(expr){return delegate.markEnd(expr)}throwUnexpected(lex())}function parseArguments(){var args=[];expect("(");if(!match(")")){while(index":case"<=":case">=":case"instanceof":prec=7; -break;case"in":prec=allowIn?7:0;break;case"<<":case">>":case">>>":prec=8;break;case"+":case"-":prec=9;break;case"*":case"/":case"%":prec=11;break;default:break}return prec}function parseBinaryExpression(){var expr,token,prec,previousAllowIn,stack,right,operator,left,i; -previousAllowIn=state.allowIn;state.allowIn=true;expr=parseUnaryExpression();token=lookahead;prec=binaryPrecedence(token,previousAllowIn);if(prec===0){return expr}token.prec=prec;lex();stack=[expr,token,parseUnaryExpression()]; -while((prec=binaryPrecedence(lookahead,previousAllowIn))>0){while(stack.length>2&&prec<=stack[stack.length-2].prec){right=stack.pop();operator=stack.pop().value;left=stack.pop();stack.push(delegate.createBinaryExpression(operator,left,right)) -}token=lex();token.prec=prec;stack.push(token);stack.push(parseUnaryExpression())}state.allowIn=previousAllowIn;i=stack.length-1;expr=stack[i];while(i>1){expr=delegate.createBinaryExpression(stack[i-1].value,stack[i-2],expr); -i-=2}return expr}function parseConditionalExpression(){var expr,previousAllowIn,consequent,alternate;delegate.markStart();expr=parseBinaryExpression();if(match("?")){lex();previousAllowIn=state.allowIn; -state.allowIn=true;consequent=parseAssignmentExpression();state.allowIn=previousAllowIn;expect(":");alternate=parseAssignmentExpression();expr=delegate.markEnd(delegate.createConditionalExpression(expr,consequent,alternate)) -}else{delegate.markEnd({})}return expr}function parseAssignmentExpression(){var token,marker,left,right,node;token=lookahead;marker=createLocationMarker();node=left=parseConditionalExpression();if(matchAssign()){if(!isLeftHandSide(left)){throwError({},Messages.InvalidLHSInAssignment) -}if(strict&&left.type===Syntax.Identifier&&isRestrictedWord(left.name)){throwErrorTolerant(token,Messages.StrictLHSAssignment)}token=lex();right=parseAssignmentExpression();node=delegate.createAssignmentExpression(token.value,left,right) -}if(marker){marker.end();return marker.applyIf(node)}return node}function parseExpression(){var marker,expr;marker=createLocationMarker();expr=parseAssignmentExpression();if(match(",")){expr=delegate.createSequenceExpression([expr]); -while(index0){if(extra.comments[extra.comments.length-1].range[1]>start){return -}}extra.comments.push({type:type,value:value,range:[start,end],loc:loc})}function scanComment(){var comment,ch,loc,start,blockComment,lineComment;comment="";blockComment=false;lineComment=false;while(index=length){lineComment=false;comment+=ch;loc.end={line:lineNumber,column:length-lineStart};addComment("Line",comment,start,length,loc)}else{comment+=ch -}}else if(blockComment){if(isLineTerminator(ch.charCodeAt(0))){if(ch==="\r"&&source[index+1]==="\n"){++index;comment+="\r\n"}else{comment+=ch}++lineNumber;++index;lineStart=index;if(index>=length){throwError({},Messages.UnexpectedToken,"ILLEGAL") -}}else{ch=source[index++];if(index>=length){throwError({},Messages.UnexpectedToken,"ILLEGAL")}comment+=ch;if(ch==="*"){ch=source[index];if(ch==="/"){comment=comment.substr(0,comment.length-1);blockComment=false; -++index;loc.end={line:lineNumber,column:index-lineStart};addComment("Block",comment,start,index,loc);comment=""}}}}else if(ch==="/"){ch=source[index+1];if(ch==="/"){loc={start:{line:lineNumber,column:index-lineStart}}; -start=index;index+=2;lineComment=true;if(index>=length){loc.end={line:lineNumber,column:index-lineStart};lineComment=false;addComment("Line",comment,start,index,loc)}}else if(ch==="*"){start=index;index+=2; -blockComment=true;loc={start:{line:lineNumber,column:index-lineStart-2}};if(index>=length){throwError({},Messages.UnexpectedToken,"ILLEGAL")}}else{break}}else if(isWhiteSpace(ch.charCodeAt(0))){++index -}else if(isLineTerminator(ch.charCodeAt(0))){++index;if(ch==="\r"&&source[index]==="\n"){++index}++lineNumber;lineStart=index}else{break}}}function filterCommentLocation(){var i,entry,comment,comments=[]; -for(i=0;i0){token=extra.tokens[extra.tokens.length-1]; -if(token.range[0]===pos&&token.type==="Punctuator"){if(token.value==="/"||token.value==="/="){extra.tokens.pop()}}}extra.tokens.push({type:"RegularExpression",value:regex.literal,range:[pos,index],loc:loc}) -}return regex}function filterTokenLocation(){var i,entry,token,tokens=[];for(i=0;i0?1:0;lineStart=0;length=source.length;lookahead=null;state={allowIn:true,labelSet:{},inFunctionBody:false,inIteration:false,inSwitch:false}; -extra={};options=options||{};options.tokens=true;extra.tokens=[];extra.tokenize=true;extra.openParenToken=-1;extra.openCurlyToken=-1;extra.range=typeof options.range==="boolean"&&options.range;extra.loc=typeof options.loc==="boolean"&&options.loc; -if(typeof options.comment==="boolean"&&options.comment){extra.comments=[]}if(typeof options.tolerant==="boolean"&&options.tolerant){extra.errors=[]}if(length>0){if(typeof source[0]==="undefined"){if(code instanceof String){source=code.valueOf() -}}}patch();try{peek();if(lookahead.type===Token.EOF){return extra.tokens}token=lex();while(lookahead.type!==Token.EOF){try{token=lex()}catch(lexError){token=lookahead;if(extra.errors){extra.errors.push(lexError); -break}else{throw lexError}}}filterTokenLocation();tokens=extra.tokens;if(typeof extra.comments!=="undefined"){filterCommentLocation();tokens.comments=extra.comments}if(typeof extra.errors!=="undefined"){tokens.errors=extra.errors -}}catch(e){throw e}finally{unpatch();extra={}}return tokens}function parse(code,options){var program,toString;toString=String;if(typeof code!=="string"&&!(code instanceof String)){code=toString(code)}delegate=SyntaxTreeDelegate; -source=code;index=0;lineNumber=source.length>0?1:0;lineStart=0;length=source.length;lookahead=null;state={allowIn:true,labelSet:{},inFunctionBody:false,inIteration:false,inSwitch:false};extra={};if(typeof options!=="undefined"){extra.range=typeof options.range==="boolean"&&options.range; -extra.loc=typeof options.loc==="boolean"&&options.loc;if(typeof options.range==="boolean"&&options.range){state.rangeStack=[];delegate=extend(delegate,{markStart:function(){skipComment();state.rangeStack.push(index) -}});delegate=extend(delegate,{markEnd:function(node){node.range=[state.rangeStack.pop(),index];return node}})}if(typeof options.loc==="boolean"&&options.loc){state.locStack=[];delegate=extend(delegate,{markStart:function(){skipComment(); -state.locStack.push({line:lineNumber,column:index-lineStart});if(state.rangeStack){state.rangeStack.push(index)}}});delegate=extend(delegate,{markEnd:function(node){if(state.rangeStack){node.range=[state.rangeStack.pop(),index] -}node.loc={};node.loc.start=state.locStack.pop();node.loc.end={line:lineNumber,column:index-lineStart};if(options.source!==null&&options.source!==undefined){node.loc.source=toString(options.source)}return node -}});delegate=extend(delegate,{markGroupEnd:function(node){if(state.rangeStack){node.groupRange=[state.rangeStack.pop(),index]}node.groupLoc={};node.groupLoc.start=state.locStack.pop();node.groupLoc.end={line:lineNumber,column:index-lineStart}; -if(options.source!==null&&options.source!==undefined){node.groupLoc.source=toString(options.source)}return node}})}if(extra.loc&&options.source!==null&&options.source!==undefined){delegate=extend(delegate,{postProcess:function(node){node.loc.source=toString(options.source); -return node}})}if(typeof options.tokens==="boolean"&&options.tokens){extra.tokens=[]}if(typeof options.comment==="boolean"&&options.comment){extra.comments=[]}if(typeof options.tolerant==="boolean"&&options.tolerant){extra.errors=[] -}}if(length>0){if(typeof source[0]==="undefined"){if(code instanceof String){source=code.valueOf()}}}patch();try{program=parseProgram();if(typeof extra.comments!=="undefined"){filterCommentLocation();program.comments=extra.comments -}if(typeof extra.tokens!=="undefined"){filterTokenLocation();program.tokens=extra.tokens}if(typeof extra.errors!=="undefined"){program.errors=extra.errors}if(extra.range||extra.loc){filterGroup(program.body) -}}catch(e){throw e}finally{unpatch();extra={}}return program}exports.version="1.1.0-dev";exports.tokenize=tokenize;exports.parse=parse;exports.Syntax=function(){var name,types={};if(typeof Object.create==="function"){types=Object.create(null) -}for(name in Syntax){if(Syntax.hasOwnProperty(name)){types[name]=Syntax[name]}}if(typeof Object.freeze==="function"){Object.freeze(types)}return types}()}); -/* END INSERT */ - -realExports.esprima = exports; -var esprima = exports; -/* Includes a minified jshint: http://www.jshint.com/ */ -// Avoid clobber: -exports = {}; - -/* INSERT jshint.js */ -var JSHINT;(function(){var require=function(file,cwd){var resolved=require.resolve(file,cwd||"/");var mod=require.modules[resolved];if(!mod)throw new Error("Failed to resolve module "+file+", tried "+resolved); -var cached=require.cache[resolved];var res=cached?cached.exports:mod();return res};require.paths=[];require.modules={};require.cache={};require.extensions=[".js",".coffee",".json"];require._core={assert:true,events:true,fs:true,path:true,vm:true}; -require.resolve=function(){return function(x,cwd){if(!cwd)cwd="/";if(require._core[x])return x;var path=require.modules.path();cwd=path.resolve("/",cwd);var y=cwd||"/";if(x.match(/^(?:\.\.?\/|\/)/)){var m=loadAsFileSync(path.resolve(y,x))||loadAsDirectorySync(path.resolve(y,x)); -if(m)return m}var n=loadNodeModulesSync(x,y);if(n)return n;throw new Error("Cannot find module '"+x+"'");function loadAsFileSync(x){x=path.normalize(x);if(require.modules[x]){return x}for(var i=0;i=0;i--){if(parts[i]==="node_modules")continue;var dir=parts.slice(0,i+1).join("/")+"/node_modules";dirs.push(dir)}return dirs}}}();require.alias=function(from,to){var path=require.modules.path(); -var res=null;try{res=require.resolve(from+"/package.json","/")}catch(err){res=require.resolve(from,"/")}var basedir=path.dirname(res);var keys=(Object.keys||function(obj){var res=[];for(var key in obj)res.push(key); -return res})(require.modules);for(var i=0;i 0\n var up = 0;\n for (var i = parts.length; i >= 0; i--) {\n var last = parts[i];\n if (last == '.') {\n parts.splice(i, 1);\n } else if (last === '..') {\n parts.splice(i, 1);\n up++;\n } else if (up) {\n parts.splice(i, 1);\n up--;\n }\n }\n\n // if the path is allowed to go above the root, restore leading ..s\n if (allowAboveRoot) {\n for (; up--; up) {\n parts.unshift('..');\n }\n }\n\n return parts;\n}\n\n// Regex to split a filename into [*, dir, basename, ext]\n// posix version\nvar splitPathRe = /^(.+\\/(?!$)|\\/)?((?:.+?)?(\\.[^.]*)?)$/;\n\n// path.resolve([from ...], to)\n// posix version\nexports.resolve = function() {\nvar resolvedPath = '',\n resolvedAbsolute = false;\n\nfor (var i = arguments.length; i >= -1 && !resolvedAbsolute; i--) {\n var path = (i >= 0)\n ? arguments[i]\n : process.cwd();\n\n // Skip empty and invalid entries\n if (typeof path !== 'string' || !path) {\n continue;\n }\n\n resolvedPath = path + '/' + resolvedPath;\n resolvedAbsolute = path.charAt(0) === '/';\n}\n\n// At this point the path should be resolved to a full absolute path, but\n// handle relative paths to be safe (might happen when process.cwd() fails)\n\n// Normalize the path\nresolvedPath = normalizeArray(filter(resolvedPath.split('/'), function(p) {\n return !!p;\n }), !resolvedAbsolute).join('/');\n\n return ((resolvedAbsolute ? '/' : '') + resolvedPath) || '.';\n};\n\n// path.normalize(path)\n// posix version\nexports.normalize = function(path) {\nvar isAbsolute = path.charAt(0) === '/',\n trailingSlash = path.slice(-1) === '/';\n\n// Normalize the path\npath = normalizeArray(filter(path.split('/'), function(p) {\n return !!p;\n }), !isAbsolute).join('/');\n\n if (!path && !isAbsolute) {\n path = '.';\n }\n if (path && trailingSlash) {\n path += '/';\n }\n \n return (isAbsolute ? '/' : '') + path;\n};\n\n\n// posix version\nexports.join = function() {\n var paths = Array.prototype.slice.call(arguments, 0);\n return exports.normalize(filter(paths, function(p, index) {\n return p && typeof p === 'string';\n }).join('/'));\n};\n\n\nexports.dirname = function(path) {\n var dir = splitPathRe.exec(path)[1] || '';\n var isWindows = false;\n if (!dir) {\n // No dirname\n return '.';\n } else if (dir.length === 1 ||\n (isWindows && dir.length <= 3 && dir.charAt(1) === ':')) {\n // It is just a slash or a drive letter with a slash\n return dir;\n } else {\n // It is a full dirname, strip trailing slash\n return dir.substring(0, dir.length - 1);\n }\n};\n\n\nexports.basename = function(path, ext) {\n var f = splitPathRe.exec(path)[2] || '';\n // TODO: make this comparison case-insensitive on windows?\n if (ext && f.substr(-1 * ext.length) === ext) {\n f = f.substr(0, f.length - ext.length);\n }\n return f;\n};\n\n\nexports.extname = function(path) {\n return splitPathRe.exec(path)[3] || '';\n};\n\n//@ sourceURL=path")); -require.define("__browserify_process",Function(["require","module","exports","__dirname","__filename","process","global"],"var process = module.exports = {};\n\nprocess.nextTick = (function () {\n var canSetImmediate = typeof window !== 'undefined'\n && window.setImmediate;\n var canPost = typeof window !== 'undefined'\n && window.postMessage && window.addEventListener\n ;\n\n if (canSetImmediate) {\n return window.setImmediate;\n }\n\n if (canPost) {\n var queue = [];\n window.addEventListener('message', function (ev) {\n if (ev.source === window && ev.data === 'browserify-tick') {\n ev.stopPropagation();\n if (queue.length > 0) {\n var fn = queue.shift();\n fn();\n }\n }\n }, true);\n\n return function nextTick(fn) {\n queue.push(fn);\n window.postMessage('browserify-tick', '*');\n };\n }\n\n return function nextTick(fn) {\n setTimeout(fn, 0);\n };\n})();\n\nprocess.title = 'browser';\nprocess.browser = true;\nprocess.env = {};\nprocess.argv = [];\n\nprocess.binding = function (name) {\n if (name === 'evals') return (require)('vm')\n else throw new Error('No such module. (Possibly not yet loaded)')\n};\n\n(function () {\n var cwd = '/';\n var path;\n process.cwd = function () { return cwd };\n process.chdir = function (dir) {\n if (!path) path = require('path');\n cwd = path.resolve(dir, cwd);\n };\n})();\n\n//@ sourceURL=__browserify_process")); -require.define("/node_modules/underscore/package.json",Function(["require","module","exports","__dirname","__filename","process","global"],'module.exports = {"main":"underscore.js"}\n//@ sourceURL=/node_modules/underscore/package.json')); -require.define("/node_modules/underscore/underscore.js",Function(["require","module","exports","__dirname","__filename","process","global"],"// Underscore.js 1.4.4\n// http://underscorejs.org\n// (c) 2009-2013 Jeremy Ashkenas, DocumentCloud Inc.\n// Underscore may be freely distributed under the MIT license.\n\n(function() {\n\n // Baseline setup\n // --------------\n\n // Establish the root object, `window` in the browser, or `global` on the server.\n var root = this;\n\n // Save the previous value of the `_` variable.\n var previousUnderscore = root._;\n\n // Establish the object that gets returned to break out of a loop iteration.\n var breaker = {};\n\n // Save bytes in the minified (but not gzipped) version:\n var ArrayProto = Array.prototype, ObjProto = Object.prototype, FuncProto = Function.prototype;\n\n // Create quick reference variables for speed access to core prototypes.\n var push = ArrayProto.push,\n slice = ArrayProto.slice,\n concat = ArrayProto.concat,\n toString = ObjProto.toString,\n hasOwnProperty = ObjProto.hasOwnProperty;\n\n // All **ECMAScript 5** native function implementations that we hope to use\n // are declared here.\n var\n nativeForEach = ArrayProto.forEach,\n nativeMap = ArrayProto.map,\n nativeReduce = ArrayProto.reduce,\n nativeReduceRight = ArrayProto.reduceRight,\n nativeFilter = ArrayProto.filter,\n nativeEvery = ArrayProto.every,\n nativeSome = ArrayProto.some,\n nativeIndexOf = ArrayProto.indexOf,\n nativeLastIndexOf = ArrayProto.lastIndexOf,\n nativeIsArray = Array.isArray,\n nativeKeys = Object.keys,\n nativeBind = FuncProto.bind;\n\n // Create a safe reference to the Underscore object for use below.\n var _ = function(obj) {\n if (obj instanceof _) return obj;\n if (!(this instanceof _)) return new _(obj);\n this._wrapped = obj;\n };\n\n // Export the Underscore object for **Node.js**, with\n // backwards-compatibility for the old `require()` API. If we're in\n // the browser, add `_` as a global object via a string identifier,\n // for Closure Compiler \"advanced\" mode.\n if (typeof exports !== 'undefined') {\n if (typeof module !== 'undefined' && module.exports) {\n exports = module.exports = _;\n }\n exports._ = _;\n } else {\n root._ = _;\n }\n\n // Current version.\n _.VERSION = '1.4.4';\n\n // Collection Functions\n // --------------------\n\n // The cornerstone, an `each` implementation, aka `forEach`.\n // Handles objects with the built-in `forEach`, arrays, and raw objects.\n // Delegates to **ECMAScript 5**'s native `forEach` if available.\n var each = _.each = _.forEach = function(obj, iterator, context) {\n if (obj == null) return;\n if (nativeForEach && obj.forEach === nativeForEach) {\n obj.forEach(iterator, context);\n } else if (obj.length === +obj.length) {\n for (var i = 0, l = obj.length; i < l; i++) {\n if (iterator.call(context, obj[i], i, obj) === breaker) return;\n }\n } else {\n for (var key in obj) {\n if (_.has(obj, key)) {\n if (iterator.call(context, obj[key], key, obj) === breaker) return;\n }\n }\n }\n };\n\n // Return the results of applying the iterator to each element.\n // Delegates to **ECMAScript 5**'s native `map` if available.\n _.map = _.collect = function(obj, iterator, context) {\n var results = [];\n if (obj == null) return results;\n if (nativeMap && obj.map === nativeMap) return obj.map(iterator, context);\n each(obj, function(value, index, list) {\n results[results.length] = iterator.call(context, value, index, list);\n });\n return results;\n };\n\n var reduceError = 'Reduce of empty array with no initial value';\n\n // **Reduce** builds up a single result from a list of values, aka `inject`,\n // or `foldl`. Delegates to **ECMAScript 5**'s native `reduce` if available.\n _.reduce = _.foldl = _.inject = function(obj, iterator, memo, context) {\n var initial = arguments.length > 2;\n if (obj == null) obj = [];\n if (nativeReduce && obj.reduce === nativeReduce) {\n if (context) iterator = _.bind(iterator, context);\n return initial ? obj.reduce(iterator, memo) : obj.reduce(iterator);\n }\n each(obj, function(value, index, list) {\n if (!initial) {\n memo = value;\n initial = true;\n } else {\n memo = iterator.call(context, memo, value, index, list);\n }\n });\n if (!initial) throw new TypeError(reduceError);\n return memo;\n };\n\n // The right-associative version of reduce, also known as `foldr`.\n // Delegates to **ECMAScript 5**'s native `reduceRight` if available.\n _.reduceRight = _.foldr = function(obj, iterator, memo, context) {\n var initial = arguments.length > 2;\n if (obj == null) obj = [];\n if (nativeReduceRight && obj.reduceRight === nativeReduceRight) {\n if (context) iterator = _.bind(iterator, context);\n return initial ? obj.reduceRight(iterator, memo) : obj.reduceRight(iterator);\n }\n var length = obj.length;\n if (length !== +length) {\n var keys = _.keys(obj);\n length = keys.length;\n }\n each(obj, function(value, index, list) {\n index = keys ? keys[--length] : --length;\n if (!initial) {\n memo = obj[index];\n initial = true;\n } else {\n memo = iterator.call(context, memo, obj[index], index, list);\n }\n });\n if (!initial) throw new TypeError(reduceError);\n return memo;\n };\n\n // Return the first value which passes a truth test. Aliased as `detect`.\n _.find = _.detect = function(obj, iterator, context) {\n var result;\n any(obj, function(value, index, list) {\n if (iterator.call(context, value, index, list)) {\n result = value;\n return true;\n }\n });\n return result;\n };\n\n // Return all the elements that pass a truth test.\n // Delegates to **ECMAScript 5**'s native `filter` if available.\n // Aliased as `select`.\n _.filter = _.select = function(obj, iterator, context) {\n var results = [];\n if (obj == null) return results;\n if (nativeFilter && obj.filter === nativeFilter) return obj.filter(iterator, context);\n each(obj, function(value, index, list) {\n if (iterator.call(context, value, index, list)) results[results.length] = value;\n });\n return results;\n };\n\n // Return all the elements for which a truth test fails.\n _.reject = function(obj, iterator, context) {\n return _.filter(obj, function(value, index, list) {\n return !iterator.call(context, value, index, list);\n }, context);\n };\n\n // Determine whether all of the elements match a truth test.\n // Delegates to **ECMAScript 5**'s native `every` if available.\n // Aliased as `all`.\n _.every = _.all = function(obj, iterator, context) {\n iterator || (iterator = _.identity);\n var result = true;\n if (obj == null) return result;\n if (nativeEvery && obj.every === nativeEvery) return obj.every(iterator, context);\n each(obj, function(value, index, list) {\n if (!(result = result && iterator.call(context, value, index, list))) return breaker;\n });\n return !!result;\n };\n\n // Determine if at least one element in the object matches a truth test.\n // Delegates to **ECMAScript 5**'s native `some` if available.\n // Aliased as `any`.\n var any = _.some = _.any = function(obj, iterator, context) {\n iterator || (iterator = _.identity);\n var result = false;\n if (obj == null) return result;\n if (nativeSome && obj.some === nativeSome) return obj.some(iterator, context);\n each(obj, function(value, index, list) {\n if (result || (result = iterator.call(context, value, index, list))) return breaker;\n });\n return !!result;\n };\n\n // Determine if the array or object contains a given value (using `===`).\n // Aliased as `include`.\n _.contains = _.include = function(obj, target) {\n if (obj == null) return false;\n if (nativeIndexOf && obj.indexOf === nativeIndexOf) return obj.indexOf(target) != -1;\n return any(obj, function(value) {\n return value === target;\n });\n };\n\n // Invoke a method (with arguments) on every item in a collection.\n _.invoke = function(obj, method) {\n var args = slice.call(arguments, 2);\n var isFunc = _.isFunction(method);\n return _.map(obj, function(value) {\n return (isFunc ? method : value[method]).apply(value, args);\n });\n };\n\n // Convenience version of a common use case of `map`: fetching a property.\n _.pluck = function(obj, key) {\n return _.map(obj, function(value){ return value[key]; });\n };\n\n // Convenience version of a common use case of `filter`: selecting only objects\n // containing specific `key:value` pairs.\n _.where = function(obj, attrs, first) {\n if (_.isEmpty(attrs)) return first ? null : [];\n return _[first ? 'find' : 'filter'](obj, function(value) {\n for (var key in attrs) {\n if (attrs[key] !== value[key]) return false;\n }\n return true;\n });\n };\n\n // Convenience version of a common use case of `find`: getting the first object\n // containing specific `key:value` pairs.\n _.findWhere = function(obj, attrs) {\n return _.where(obj, attrs, true);\n };\n\n // Return the maximum element or (element-based computation).\n // Can't optimize arrays of integers longer than 65,535 elements.\n // See: https://bugs.webkit.org/show_bug.cgi?id=80797\n _.max = function(obj, iterator, context) {\n if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {\n return Math.max.apply(Math, obj);\n }\n if (!iterator && _.isEmpty(obj)) return -Infinity;\n var result = {computed : -Infinity, value: -Infinity};\n each(obj, function(value, index, list) {\n var computed = iterator ? iterator.call(context, value, index, list) : value;\n computed >= result.computed && (result = {value : value, computed : computed});\n });\n return result.value;\n };\n\n // Return the minimum element (or element-based computation).\n _.min = function(obj, iterator, context) {\n if (!iterator && _.isArray(obj) && obj[0] === +obj[0] && obj.length < 65535) {\n return Math.min.apply(Math, obj);\n }\n if (!iterator && _.isEmpty(obj)) return Infinity;\n var result = {computed : Infinity, value: Infinity};\n each(obj, function(value, index, list) {\n var computed = iterator ? iterator.call(context, value, index, list) : value;\n computed < result.computed && (result = {value : value, computed : computed});\n });\n return result.value;\n };\n\n // Shuffle an array.\n _.shuffle = function(obj) {\n var rand;\n var index = 0;\n var shuffled = [];\n each(obj, function(value) {\n rand = _.random(index++);\n shuffled[index - 1] = shuffled[rand];\n shuffled[rand] = value;\n });\n return shuffled;\n };\n\n // An internal function to generate lookup iterators.\n var lookupIterator = function(value) {\n return _.isFunction(value) ? value : function(obj){ return obj[value]; };\n };\n\n // Sort the object's values by a criterion produced by an iterator.\n _.sortBy = function(obj, value, context) {\n var iterator = lookupIterator(value);\n return _.pluck(_.map(obj, function(value, index, list) {\n return {\n value : value,\n index : index,\n criteria : iterator.call(context, value, index, list)\n };\n }).sort(function(left, right) {\n var a = left.criteria;\n var b = right.criteria;\n if (a !== b) {\n if (a > b || a === void 0) return 1;\n if (a < b || b === void 0) return -1;\n }\n return left.index < right.index ? -1 : 1;\n }), 'value');\n };\n\n // An internal function used for aggregate \"group by\" operations.\n var group = function(obj, value, context, behavior) {\n var result = {};\n var iterator = lookupIterator(value || _.identity);\n each(obj, function(value, index) {\n var key = iterator.call(context, value, index, obj);\n behavior(result, key, value);\n });\n return result;\n };\n\n // Groups the object's values by a criterion. Pass either a string attribute\n // to group by, or a function that returns the criterion.\n _.groupBy = function(obj, value, context) {\n return group(obj, value, context, function(result, key, value) {\n (_.has(result, key) ? result[key] : (result[key] = [])).push(value);\n });\n };\n\n // Counts instances of an object that group by a certain criterion. Pass\n // either a string attribute to count by, or a function that returns the\n // criterion.\n _.countBy = function(obj, value, context) {\n return group(obj, value, context, function(result, key) {\n if (!_.has(result, key)) result[key] = 0;\n result[key]++;\n });\n };\n\n // Use a comparator function to figure out the smallest index at which\n // an object should be inserted so as to maintain order. Uses binary search.\n _.sortedIndex = function(array, obj, iterator, context) {\n iterator = iterator == null ? _.identity : lookupIterator(iterator);\n var value = iterator.call(context, obj);\n var low = 0, high = array.length;\n while (low < high) {\n var mid = (low + high) >>> 1;\n iterator.call(context, array[mid]) < value ? low = mid + 1 : high = mid;\n }\n return low;\n };\n\n // Safely convert anything iterable into a real, live array.\n _.toArray = function(obj) {\n if (!obj) return [];\n if (_.isArray(obj)) return slice.call(obj);\n if (obj.length === +obj.length) return _.map(obj, _.identity);\n return _.values(obj);\n };\n\n // Return the number of elements in an object.\n _.size = function(obj) {\n if (obj == null) return 0;\n return (obj.length === +obj.length) ? obj.length : _.keys(obj).length;\n };\n\n // Array Functions\n // ---------------\n\n // Get the first element of an array. Passing **n** will return the first N\n // values in the array. Aliased as `head` and `take`. The **guard** check\n // allows it to work with `_.map`.\n _.first = _.head = _.take = function(array, n, guard) {\n if (array == null) return void 0;\n return (n != null) && !guard ? slice.call(array, 0, n) : array[0];\n };\n\n // Returns everything but the last entry of the array. Especially useful on\n // the arguments object. Passing **n** will return all the values in\n // the array, excluding the last N. The **guard** check allows it to work with\n // `_.map`.\n _.initial = function(array, n, guard) {\n return slice.call(array, 0, array.length - ((n == null) || guard ? 1 : n));\n };\n\n // Get the last element of an array. Passing **n** will return the last N\n // values in the array. The **guard** check allows it to work with `_.map`.\n _.last = function(array, n, guard) {\n if (array == null) return void 0;\n if ((n != null) && !guard) {\n return slice.call(array, Math.max(array.length - n, 0));\n } else {\n return array[array.length - 1];\n }\n };\n\n // Returns everything but the first entry of the array. Aliased as `tail` and `drop`.\n // Especially useful on the arguments object. Passing an **n** will return\n // the rest N values in the array. The **guard**\n // check allows it to work with `_.map`.\n _.rest = _.tail = _.drop = function(array, n, guard) {\n return slice.call(array, (n == null) || guard ? 1 : n);\n };\n\n // Trim out all falsy values from an array.\n _.compact = function(array) {\n return _.filter(array, _.identity);\n };\n\n // Internal implementation of a recursive `flatten` function.\n var flatten = function(input, shallow, output) {\n each(input, function(value) {\n if (_.isArray(value)) {\n shallow ? push.apply(output, value) : flatten(value, shallow, output);\n } else {\n output.push(value);\n }\n });\n return output;\n };\n\n // Return a completely flattened version of an array.\n _.flatten = function(array, shallow) {\n return flatten(array, shallow, []);\n };\n\n // Return a version of the array that does not contain the specified value(s).\n _.without = function(array) {\n return _.difference(array, slice.call(arguments, 1));\n };\n\n // Produce a duplicate-free version of the array. If the array has already\n // been sorted, you have the option of using a faster algorithm.\n // Aliased as `unique`.\n _.uniq = _.unique = function(array, isSorted, iterator, context) {\n if (_.isFunction(isSorted)) {\n context = iterator;\n iterator = isSorted;\n isSorted = false;\n }\n var initial = iterator ? _.map(array, iterator, context) : array;\n var results = [];\n var seen = [];\n each(initial, function(value, index) {\n if (isSorted ? (!index || seen[seen.length - 1] !== value) : !_.contains(seen, value)) {\n seen.push(value);\n results.push(array[index]);\n }\n });\n return results;\n };\n\n // Produce an array that contains the union: each distinct element from all of\n // the passed-in arrays.\n _.union = function() {\n return _.uniq(concat.apply(ArrayProto, arguments));\n };\n\n // Produce an array that contains every item shared between all the\n // passed-in arrays.\n _.intersection = function(array) {\n var rest = slice.call(arguments, 1);\n return _.filter(_.uniq(array), function(item) {\n return _.every(rest, function(other) {\n return _.indexOf(other, item) >= 0;\n });\n });\n };\n\n // Take the difference between one array and a number of other arrays.\n // Only the elements present in just the first array will remain.\n _.difference = function(array) {\n var rest = concat.apply(ArrayProto, slice.call(arguments, 1));\n return _.filter(array, function(value){ return !_.contains(rest, value); });\n };\n\n // Zip together multiple lists into a single array -- elements that share\n // an index go together.\n _.zip = function() {\n var args = slice.call(arguments);\n var length = _.max(_.pluck(args, 'length'));\n var results = new Array(length);\n for (var i = 0; i < length; i++) {\n results[i] = _.pluck(args, \"\" + i);\n }\n return results;\n };\n\n // Converts lists into objects. Pass either a single array of `[key, value]`\n // pairs, or two parallel arrays of the same length -- one of keys, and one of\n // the corresponding values.\n _.object = function(list, values) {\n if (list == null) return {};\n var result = {};\n for (var i = 0, l = list.length; i < l; i++) {\n if (values) {\n result[list[i]] = values[i];\n } else {\n result[list[i][0]] = list[i][1];\n }\n }\n return result;\n };\n\n // If the browser doesn't supply us with indexOf (I'm looking at you, **MSIE**),\n // we need this function. Return the position of the first occurrence of an\n // item in an array, or -1 if the item is not included in the array.\n // Delegates to **ECMAScript 5**'s native `indexOf` if available.\n // If the array is large and already in sort order, pass `true`\n // for **isSorted** to use binary search.\n _.indexOf = function(array, item, isSorted) {\n if (array == null) return -1;\n var i = 0, l = array.length;\n if (isSorted) {\n if (typeof isSorted == 'number') {\n i = (isSorted < 0 ? Math.max(0, l + isSorted) : isSorted);\n } else {\n i = _.sortedIndex(array, item);\n return array[i] === item ? i : -1;\n }\n }\n if (nativeIndexOf && array.indexOf === nativeIndexOf) return array.indexOf(item, isSorted);\n for (; i < l; i++) if (array[i] === item) return i;\n return -1;\n };\n\n // Delegates to **ECMAScript 5**'s native `lastIndexOf` if available.\n _.lastIndexOf = function(array, item, from) {\n if (array == null) return -1;\n var hasIndex = from != null;\n if (nativeLastIndexOf && array.lastIndexOf === nativeLastIndexOf) {\n return hasIndex ? array.lastIndexOf(item, from) : array.lastIndexOf(item);\n }\n var i = (hasIndex ? from : array.length);\n while (i--) if (array[i] === item) return i;\n return -1;\n };\n\n // Generate an integer Array containing an arithmetic progression. A port of\n // the native Python `range()` function. See\n // [the Python documentation](http://docs.python.org/library/functions.html#range).\n _.range = function(start, stop, step) {\n if (arguments.length <= 1) {\n stop = start || 0;\n start = 0;\n }\n step = arguments[2] || 1;\n\n var len = Math.max(Math.ceil((stop - start) / step), 0);\n var idx = 0;\n var range = new Array(len);\n\n while(idx < len) {\n range[idx++] = start;\n start += step;\n }\n\n return range;\n };\n\n // Function (ahem) Functions\n // ------------------\n\n // Create a function bound to a given object (assigning `this`, and arguments,\n // optionally). Delegates to **ECMAScript 5**'s native `Function.bind` if\n // available.\n _.bind = function(func, context) {\n if (func.bind === nativeBind && nativeBind) return nativeBind.apply(func, slice.call(arguments, 1));\n var args = slice.call(arguments, 2);\n return function() {\n return func.apply(context, args.concat(slice.call(arguments)));\n };\n };\n\n // Partially apply a function by creating a version that has had some of its\n // arguments pre-filled, without changing its dynamic `this` context.\n _.partial = function(func) {\n var args = slice.call(arguments, 1);\n return function() {\n return func.apply(this, args.concat(slice.call(arguments)));\n };\n };\n\n // Bind all of an object's methods to that object. Useful for ensuring that\n // all callbacks defined on an object belong to it.\n _.bindAll = function(obj) {\n var funcs = slice.call(arguments, 1);\n if (funcs.length === 0) funcs = _.functions(obj);\n each(funcs, function(f) { obj[f] = _.bind(obj[f], obj); });\n return obj;\n };\n\n // Memoize an expensive function by storing its results.\n _.memoize = function(func, hasher) {\n var memo = {};\n hasher || (hasher = _.identity);\n return function() {\n var key = hasher.apply(this, arguments);\n return _.has(memo, key) ? memo[key] : (memo[key] = func.apply(this, arguments));\n };\n };\n\n // Delays a function for the given number of milliseconds, and then calls\n // it with the arguments supplied.\n _.delay = function(func, wait) {\n var args = slice.call(arguments, 2);\n return setTimeout(function(){ return func.apply(null, args); }, wait);\n };\n\n // Defers a function, scheduling it to run after the current call stack has\n // cleared.\n _.defer = function(func) {\n return _.delay.apply(_, [func, 1].concat(slice.call(arguments, 1)));\n };\n\n // Returns a function, that, when invoked, will only be triggered at most once\n // during a given window of time.\n _.throttle = function(func, wait) {\n var context, args, timeout, result;\n var previous = 0;\n var later = function() {\n previous = new Date;\n timeout = null;\n result = func.apply(context, args);\n };\n return function() {\n var now = new Date;\n var remaining = wait - (now - previous);\n context = this;\n args = arguments;\n if (remaining <= 0) {\n clearTimeout(timeout);\n timeout = null;\n previous = now;\n result = func.apply(context, args);\n } else if (!timeout) {\n timeout = setTimeout(later, remaining);\n }\n return result;\n };\n };\n\n // Returns a function, that, as long as it continues to be invoked, will not\n // be triggered. The function will be called after it stops being called for\n // N milliseconds. If `immediate` is passed, trigger the function on the\n // leading edge, instead of the trailing.\n _.debounce = function(func, wait, immediate) {\n var timeout, result;\n return function() {\n var context = this, args = arguments;\n var later = function() {\n timeout = null;\n if (!immediate) result = func.apply(context, args);\n };\n var callNow = immediate && !timeout;\n clearTimeout(timeout);\n timeout = setTimeout(later, wait);\n if (callNow) result = func.apply(context, args);\n return result;\n };\n };\n\n // Returns a function that will be executed at most one time, no matter how\n // often you call it. Useful for lazy initialization.\n _.once = function(func) {\n var ran = false, memo;\n return function() {\n if (ran) return memo;\n ran = true;\n memo = func.apply(this, arguments);\n func = null;\n return memo;\n };\n };\n\n // Returns the first function passed as an argument to the second,\n // allowing you to adjust arguments, run code before and after, and\n // conditionally execute the original function.\n _.wrap = function(func, wrapper) {\n return function() {\n var args = [func];\n push.apply(args, arguments);\n return wrapper.apply(this, args);\n };\n };\n\n // Returns a function that is the composition of a list of functions, each\n // consuming the return value of the function that follows.\n _.compose = function() {\n var funcs = arguments;\n return function() {\n var args = arguments;\n for (var i = funcs.length - 1; i >= 0; i--) {\n args = [funcs[i].apply(this, args)];\n }\n return args[0];\n };\n };\n\n // Returns a function that will only be executed after being called N times.\n _.after = function(times, func) {\n if (times <= 0) return func();\n return function() {\n if (--times < 1) {\n return func.apply(this, arguments);\n }\n };\n };\n\n // Object Functions\n // ----------------\n\n // Retrieve the names of an object's properties.\n // Delegates to **ECMAScript 5**'s native `Object.keys`\n _.keys = nativeKeys || function(obj) {\n if (obj !== Object(obj)) throw new TypeError('Invalid object');\n var keys = [];\n for (var key in obj) if (_.has(obj, key)) keys[keys.length] = key;\n return keys;\n };\n\n // Retrieve the values of an object's properties.\n _.values = function(obj) {\n var values = [];\n for (var key in obj) if (_.has(obj, key)) values.push(obj[key]);\n return values;\n };\n\n // Convert an object into a list of `[key, value]` pairs.\n _.pairs = function(obj) {\n var pairs = [];\n for (var key in obj) if (_.has(obj, key)) pairs.push([key, obj[key]]);\n return pairs;\n };\n\n // Invert the keys and values of an object. The values must be serializable.\n _.invert = function(obj) {\n var result = {};\n for (var key in obj) if (_.has(obj, key)) result[obj[key]] = key;\n return result;\n };\n\n // Return a sorted list of the function names available on the object.\n // Aliased as `methods`\n _.functions = _.methods = function(obj) {\n var names = [];\n for (var key in obj) {\n if (_.isFunction(obj[key])) names.push(key);\n }\n return names.sort();\n };\n\n // Extend a given object with all the properties in passed-in object(s).\n _.extend = function(obj) {\n each(slice.call(arguments, 1), function(source) {\n if (source) {\n for (var prop in source) {\n obj[prop] = source[prop];\n }\n }\n });\n return obj;\n };\n\n // Return a copy of the object only containing the whitelisted properties.\n _.pick = function(obj) {\n var copy = {};\n var keys = concat.apply(ArrayProto, slice.call(arguments, 1));\n each(keys, function(key) {\n if (key in obj) copy[key] = obj[key];\n });\n return copy;\n };\n\n // Return a copy of the object without the blacklisted properties.\n _.omit = function(obj) {\n var copy = {};\n var keys = concat.apply(ArrayProto, slice.call(arguments, 1));\n for (var key in obj) {\n if (!_.contains(keys, key)) copy[key] = obj[key];\n }\n return copy;\n };\n\n // Fill in a given object with default properties.\n _.defaults = function(obj) {\n each(slice.call(arguments, 1), function(source) {\n if (source) {\n for (var prop in source) {\n if (obj[prop] == null) obj[prop] = source[prop];\n }\n }\n });\n return obj;\n };\n\n // Create a (shallow-cloned) duplicate of an object.\n _.clone = function(obj) {\n if (!_.isObject(obj)) return obj;\n return _.isArray(obj) ? obj.slice() : _.extend({}, obj);\n };\n\n // Invokes interceptor with the obj, and then returns obj.\n // The primary purpose of this method is to \"tap into\" a method chain, in\n // order to perform operations on intermediate results within the chain.\n _.tap = function(obj, interceptor) {\n interceptor(obj);\n return obj;\n };\n\n // Internal recursive comparison function for `isEqual`.\n var eq = function(a, b, aStack, bStack) {\n // Identical objects are equal. `0 === -0`, but they aren't identical.\n // See the Harmony `egal` proposal: http://wiki.ecmascript.org/doku.php?id=harmony:egal.\n if (a === b) return a !== 0 || 1 / a == 1 / b;\n // A strict comparison is necessary because `null == undefined`.\n if (a == null || b == null) return a === b;\n // Unwrap any wrapped objects.\n if (a instanceof _) a = a._wrapped;\n if (b instanceof _) b = b._wrapped;\n // Compare `[[Class]]` names.\n var className = toString.call(a);\n if (className != toString.call(b)) return false;\n switch (className) {\n // Strings, numbers, dates, and booleans are compared by value.\n case '[object String]':\n // Primitives and their corresponding object wrappers are equivalent; thus, `\"5\"` is\n // equivalent to `new String(\"5\")`.\n return a == String(b);\n case '[object Number]':\n // `NaN`s are equivalent, but non-reflexive. An `egal` comparison is performed for\n // other numeric values.\n return a != +a ? b != +b : (a == 0 ? 1 / a == 1 / b : a == +b);\n case '[object Date]':\n case '[object Boolean]':\n // Coerce dates and booleans to numeric primitive values. Dates are compared by their\n // millisecond representations. Note that invalid dates with millisecond representations\n // of `NaN` are not equivalent.\n return +a == +b;\n // RegExps are compared by their source patterns and flags.\n case '[object RegExp]':\n return a.source == b.source &&\n a.global == b.global &&\n a.multiline == b.multiline &&\n a.ignoreCase == b.ignoreCase;\n }\n if (typeof a != 'object' || typeof b != 'object') return false;\n // Assume equality for cyclic structures. The algorithm for detecting cyclic\n // structures is adapted from ES 5.1 section 15.12.3, abstract operation `JO`.\n var length = aStack.length;\n while (length--) {\n // Linear search. Performance is inversely proportional to the number of\n // unique nested structures.\n if (aStack[length] == a) return bStack[length] == b;\n }\n // Add the first object to the stack of traversed objects.\n aStack.push(a);\n bStack.push(b);\n var size = 0, result = true;\n // Recursively compare objects and arrays.\n if (className == '[object Array]') {\n // Compare array lengths to determine if a deep comparison is necessary.\n size = a.length;\n result = size == b.length;\n if (result) {\n // Deep compare the contents, ignoring non-numeric properties.\n while (size--) {\n if (!(result = eq(a[size], b[size], aStack, bStack))) break;\n }\n }\n } else {\n // Objects with different constructors are not equivalent, but `Object`s\n // from different frames are.\n var aCtor = a.constructor, bCtor = b.constructor;\n if (aCtor !== bCtor && !(_.isFunction(aCtor) && (aCtor instanceof aCtor) &&\n _.isFunction(bCtor) && (bCtor instanceof bCtor))) {\n return false;\n }\n // Deep compare objects.\n for (var key in a) {\n if (_.has(a, key)) {\n // Count the expected number of properties.\n size++;\n // Deep compare each member.\n if (!(result = _.has(b, key) && eq(a[key], b[key], aStack, bStack))) break;\n }\n }\n // Ensure that both objects contain the same number of properties.\n if (result) {\n for (key in b) {\n if (_.has(b, key) && !(size--)) break;\n }\n result = !size;\n }\n }\n // Remove the first object from the stack of traversed objects.\n aStack.pop();\n bStack.pop();\n return result;\n };\n\n // Perform a deep comparison to check if two objects are equal.\n _.isEqual = function(a, b) {\n return eq(a, b, [], []);\n };\n\n // Is a given array, string, or object empty?\n // An \"empty\" object has no enumerable own-properties.\n _.isEmpty = function(obj) {\n if (obj == null) return true;\n if (_.isArray(obj) || _.isString(obj)) return obj.length === 0;\n for (var key in obj) if (_.has(obj, key)) return false;\n return true;\n };\n\n // Is a given value a DOM element?\n _.isElement = function(obj) {\n return !!(obj && obj.nodeType === 1);\n };\n\n // Is a given value an array?\n // Delegates to ECMA5's native Array.isArray\n _.isArray = nativeIsArray || function(obj) {\n return toString.call(obj) == '[object Array]';\n };\n\n // Is a given variable an object?\n _.isObject = function(obj) {\n return obj === Object(obj);\n };\n\n // Add some isType methods: isArguments, isFunction, isString, isNumber, isDate, isRegExp.\n each(['Arguments', 'Function', 'String', 'Number', 'Date', 'RegExp'], function(name) {\n _['is' + name] = function(obj) {\n return toString.call(obj) == '[object ' + name + ']';\n };\n });\n\n // Define a fallback version of the method in browsers (ahem, IE), where\n // there isn't any inspectable \"Arguments\" type.\n if (!_.isArguments(arguments)) {\n _.isArguments = function(obj) {\n return !!(obj && _.has(obj, 'callee'));\n };\n }\n\n // Optimize `isFunction` if appropriate.\n if (typeof (/./) !== 'function') {\n _.isFunction = function(obj) {\n return typeof obj === 'function';\n };\n }\n\n // Is a given object a finite number?\n _.isFinite = function(obj) {\n return isFinite(obj) && !isNaN(parseFloat(obj));\n };\n\n // Is the given value `NaN`? (NaN is the only number which does not equal itself).\n _.isNaN = function(obj) {\n return _.isNumber(obj) && obj != +obj;\n };\n\n // Is a given value a boolean?\n _.isBoolean = function(obj) {\n return obj === true || obj === false || toString.call(obj) == '[object Boolean]';\n };\n\n // Is a given value equal to null?\n _.isNull = function(obj) {\n return obj === null;\n };\n\n // Is a given variable undefined?\n _.isUndefined = function(obj) {\n return obj === void 0;\n };\n\n // Shortcut function for checking if an object has a given property directly\n // on itself (in other words, not on a prototype).\n _.has = function(obj, key) {\n return hasOwnProperty.call(obj, key);\n };\n\n // Utility Functions\n // -----------------\n\n // Run Underscore.js in *noConflict* mode, returning the `_` variable to its\n // previous owner. Returns a reference to the Underscore object.\n _.noConflict = function() {\n root._ = previousUnderscore;\n return this;\n };\n\n // Keep the identity function around for default iterators.\n _.identity = function(value) {\n return value;\n };\n\n // Run a function **n** times.\n _.times = function(n, iterator, context) {\n var accum = Array(n);\n for (var i = 0; i < n; i++) accum[i] = iterator.call(context, i);\n return accum;\n };\n\n // Return a random integer between min and max (inclusive).\n _.random = function(min, max) {\n if (max == null) {\n max = min;\n min = 0;\n }\n return min + Math.floor(Math.random() * (max - min + 1));\n };\n\n // List of HTML entities for escaping.\n var entityMap = {\n escape: {\n '&': '&',\n '<': '<',\n '>': '>',\n '\"': '"',\n \"'\": ''',\n '/': '/'\n }\n };\n entityMap.unescape = _.invert(entityMap.escape);\n\n // Regexes containing the keys and values listed immediately above.\n var entityRegexes = {\n escape: new RegExp('[' + _.keys(entityMap.escape).join('') + ']', 'g'),\n unescape: new RegExp('(' + _.keys(entityMap.unescape).join('|') + ')', 'g')\n };\n\n // Functions for escaping and unescaping strings to/from HTML interpolation.\n _.each(['escape', 'unescape'], function(method) {\n _[method] = function(string) {\n if (string == null) return '';\n return ('' + string).replace(entityRegexes[method], function(match) {\n return entityMap[method][match];\n });\n };\n });\n\n // If the value of the named property is a function then invoke it;\n // otherwise, return it.\n _.result = function(object, property) {\n if (object == null) return null;\n var value = object[property];\n return _.isFunction(value) ? value.call(object) : value;\n };\n\n // Add your own custom functions to the Underscore object.\n _.mixin = function(obj) {\n each(_.functions(obj), function(name){\n var func = _[name] = obj[name];\n _.prototype[name] = function() {\n var args = [this._wrapped];\n push.apply(args, arguments);\n return result.call(this, func.apply(_, args));\n };\n });\n };\n\n // Generate a unique integer id (unique within the entire client session).\n // Useful for temporary DOM ids.\n var idCounter = 0;\n _.uniqueId = function(prefix) {\n var id = ++idCounter + '';\n return prefix ? prefix + id : id;\n };\n\n // By default, Underscore uses ERB-style template delimiters, change the\n // following template settings to use alternative delimiters.\n _.templateSettings = {\n evaluate : /<%([\\s\\S]+?)%>/g,\n interpolate : /<%=([\\s\\S]+?)%>/g,\n escape : /<%-([\\s\\S]+?)%>/g\n };\n\n // When customizing `templateSettings`, if you don't want to define an\n // interpolation, evaluation or escaping regex, we need one that is\n // guaranteed not to match.\n var noMatch = /(.)^/;\n\n // Certain characters need to be escaped so that they can be put into a\n // string literal.\n var escapes = {\n \"'\": \"'\",\n '\\\\': '\\\\',\n '\\r': 'r',\n '\\n': 'n',\n '\\t': 't',\n '\\u2028': 'u2028',\n '\\u2029': 'u2029'\n };\n\n var escaper = /\\\\|'|\\r|\\n|\\t|\\u2028|\\u2029/g;\n\n // JavaScript micro-templating, similar to John Resig's implementation.\n // Underscore templating handles arbitrary delimiters, preserves whitespace,\n // and correctly escapes quotes within interpolated code.\n _.template = function(text, data, settings) {\n var render;\n settings = _.defaults({}, settings, _.templateSettings);\n\n // Combine delimiters into one regular expression via alternation.\n var matcher = new RegExp([\n (settings.escape || noMatch).source,\n (settings.interpolate || noMatch).source,\n (settings.evaluate || noMatch).source\n ].join('|') + '|$', 'g');\n\n // Compile the template source, escaping string literals appropriately.\n var index = 0;\n var source = \"__p+='\";\n text.replace(matcher, function(match, escape, interpolate, evaluate, offset) {\n source += text.slice(index, offset)\n .replace(escaper, function(match) { return '\\\\' + escapes[match]; });\n\n if (escape) {\n source += \"'+\\n((__t=(\" + escape + \"))==null?'':_.escape(__t))+\\n'\";\n }\n if (interpolate) {\n source += \"'+\\n((__t=(\" + interpolate + \"))==null?'':__t)+\\n'\";\n }\n if (evaluate) {\n source += \"';\\n\" + evaluate + \"\\n__p+='\";\n }\n index = offset + match.length;\n return match;\n });\n source += \"';\\n\";\n\n // If a variable is not specified, place data values in local scope.\n if (!settings.variable) source = 'with(obj||{}){\\n' + source + '}\\n';\n\n source = \"var __t,__p='',__j=Array.prototype.join,\" +\n \"print=function(){__p+=__j.call(arguments,'');};\\n\" +\n source + \"return __p;\\n\";\n\n try {\n render = new Function(settings.variable || 'obj', '_', source);\n } catch (e) {\n e.source = source;\n throw e;\n }\n\n if (data) return render(data, _);\n var template = function(data) {\n return render.call(this, data, _);\n };\n\n // Provide the compiled function source as a convenience for precompilation.\n template.source = 'function(' + (settings.variable || 'obj') + '){\\n' + source + '}';\n\n return template;\n };\n\n // Add a \"chain\" function, which will delegate to the wrapper.\n _.chain = function(obj) {\n return _(obj).chain();\n };\n\n // OOP\n // ---------------\n // If Underscore is called as a function, it returns a wrapped object that\n // can be used OO-style. This wrapper holds altered versions of all the\n // underscore functions. Wrapped objects may be chained.\n\n // Helper function to continue chaining intermediate results.\n var result = function(obj) {\n return this._chain ? _(obj).chain() : obj;\n };\n\n // Add all of the Underscore functions to the wrapper object.\n _.mixin(_);\n\n // Add all mutator Array functions to the wrapper.\n each(['pop', 'push', 'reverse', 'shift', 'sort', 'splice', 'unshift'], function(name) {\n var method = ArrayProto[name];\n _.prototype[name] = function() {\n var obj = this._wrapped;\n method.apply(obj, arguments);\n if ((name == 'shift' || name == 'splice') && obj.length === 0) delete obj[0];\n return result.call(this, obj);\n };\n });\n\n // Add all accessor Array functions to the wrapper.\n each(['concat', 'join', 'slice'], function(name) {\n var method = ArrayProto[name];\n _.prototype[name] = function() {\n return result.call(this, method.apply(this._wrapped, arguments));\n };\n });\n\n _.extend(_.prototype, {\n\n // Start chaining a wrapped Underscore object.\n chain: function() {\n this._chain = true;\n return this;\n },\n\n // Extracts the result from a wrapped and chained object.\n value: function() {\n return this._wrapped;\n }\n\n });\n\n}).call(this);\n\n//@ sourceURL=/node_modules/underscore/underscore.js")); -require.define("events",Function(["require","module","exports","__dirname","__filename","process","global"],"if (!process.EventEmitter) process.EventEmitter = function () {};\n\nvar EventEmitter = exports.EventEmitter = process.EventEmitter;\nvar isArray = typeof Array.isArray === 'function'\n ? Array.isArray\n : function (xs) {\n return Object.prototype.toString.call(xs) === '[object Array]'\n }\n;\n\n// By default EventEmitters will print a warning if more than\n// 10 listeners are added to it. This is a useful default which\n// helps finding memory leaks.\n//\n// Obviously not all Emitters should be limited to 10. This function allows\n// that to be increased. Set to zero for unlimited.\nvar defaultMaxListeners = 10;\nEventEmitter.prototype.setMaxListeners = function(n) {\n if (!this._events) this._events = {};\n this._events.maxListeners = n;\n};\n\n\nEventEmitter.prototype.emit = function(type) {\n // If there is no 'error' event listener then throw.\n if (type === 'error') {\n if (!this._events || !this._events.error ||\n (isArray(this._events.error) && !this._events.error.length))\n {\n if (arguments[1] instanceof Error) {\n throw arguments[1]; // Unhandled 'error' event\n } else {\n throw new Error(\"Uncaught, unspecified 'error' event.\");\n }\n return false;\n }\n }\n\n if (!this._events) return false;\n var handler = this._events[type];\n if (!handler) return false;\n\n if (typeof handler == 'function') {\n switch (arguments.length) {\n // fast cases\n case 1:\n handler.call(this);\n break;\n case 2:\n handler.call(this, arguments[1]);\n break;\n case 3:\n handler.call(this, arguments[1], arguments[2]);\n break;\n // slower\n default:\n var args = Array.prototype.slice.call(arguments, 1);\n handler.apply(this, args);\n }\n return true;\n\n } else if (isArray(handler)) {\n var args = Array.prototype.slice.call(arguments, 1);\n\n var listeners = handler.slice();\n for (var i = 0, l = listeners.length; i < l; i++) {\n listeners[i].apply(this, args);\n }\n return true;\n\n } else {\n return false;\n }\n};\n\n// EventEmitter is defined in src/node_events.cc\n// EventEmitter.prototype.emit() is also defined there.\nEventEmitter.prototype.addListener = function(type, listener) {\n if ('function' !== typeof listener) {\n throw new Error('addListener only takes instances of Function');\n }\n\n if (!this._events) this._events = {};\n\n // To avoid recursion in the case that type == \"newListeners\"! Before\n // adding it to the listeners, first emit \"newListeners\".\n this.emit('newListener', type, listener);\n\n if (!this._events[type]) {\n // Optimize the case of one listener. Don't need the extra array object.\n this._events[type] = listener;\n } else if (isArray(this._events[type])) {\n\n // Check for listener leak\n if (!this._events[type].warned) {\n var m;\n if (this._events.maxListeners !== undefined) {\n m = this._events.maxListeners;\n } else {\n m = defaultMaxListeners;\n }\n\n if (m && m > 0 && this._events[type].length > m) {\n this._events[type].warned = true;\n console.error('(node) warning: possible EventEmitter memory ' +\n 'leak detected. %d listeners added. ' +\n 'Use emitter.setMaxListeners() to increase limit.',\n this._events[type].length);\n console.trace();\n }\n }\n\n // If we've already got an array, just append.\n this._events[type].push(listener);\n } else {\n // Adding the second element, need to change to array.\n this._events[type] = [this._events[type], listener];\n }\n\n return this;\n};\n\nEventEmitter.prototype.on = EventEmitter.prototype.addListener;\n\nEventEmitter.prototype.once = function(type, listener) {\n var self = this;\n self.on(type, function g() {\n self.removeListener(type, g);\n listener.apply(this, arguments);\n });\n\n return this;\n};\n\nEventEmitter.prototype.removeListener = function(type, listener) {\n if ('function' !== typeof listener) {\n throw new Error('removeListener only takes instances of Function');\n }\n\n // does not use listeners(), so no side effect of creating _events[type]\n if (!this._events || !this._events[type]) return this;\n\n var list = this._events[type];\n\n if (isArray(list)) {\n var i = list.indexOf(listener);\n if (i < 0) return this;\n list.splice(i, 1);\n if (list.length == 0)\n delete this._events[type];\n } else if (this._events[type] === listener) {\n delete this._events[type];\n }\n\n return this;\n};\n\nEventEmitter.prototype.removeAllListeners = function(type) {\n // does not use listeners(), so no side effect of creating _events[type]\n if (type && this._events && this._events[type]) this._events[type] = null;\n return this;\n};\n\nEventEmitter.prototype.listeners = function(type) {\n if (!this._events) this._events = {};\n if (!this._events[type]) this._events[type] = [];\n if (!isArray(this._events[type])) {\n this._events[type] = [this._events[type]];\n }\n return this._events[type];\n};\n\n//@ sourceURL=events")); -require.define("/src/shared/vars.js",Function(["require","module","exports","__dirname","__filename","process","global"],'// jshint -W001\n\n"use strict";\n\n// Identifiers provided by the ECMAScript standard.\n\nexports.reservedVars = {\n arguments : false,\n NaN : false\n};\n\nexports.ecmaIdentifiers = {\n Array : false,\n Boolean : false,\n Date : false,\n decodeURI : false,\n decodeURIComponent : false,\n encodeURI : false,\n encodeURIComponent : false,\n Error : false,\n "eval" : false,\n EvalError : false,\n Function : false,\n hasOwnProperty : false,\n isFinite : false,\n isNaN : false,\n JSON : false,\n Math : false,\n Map : false,\n Number : false,\n Object : false,\n parseInt : false,\n parseFloat : false,\n RangeError : false,\n ReferenceError : false,\n RegExp : false,\n Set : false,\n String : false,\n SyntaxError : false,\n TypeError : false,\n URIError : false,\n WeakMap : false\n};\n\n// Global variables commonly provided by a web browser environment.\n\nexports.browser = {\n ArrayBuffer : false,\n ArrayBufferView : false,\n Audio : false,\n Blob : false,\n addEventListener : false,\n applicationCache : false,\n atob : false,\n blur : false,\n btoa : false,\n clearInterval : false,\n clearTimeout : false,\n close : false,\n closed : false,\n DataView : false,\n DOMParser : false,\n defaultStatus : false,\n document : false,\n Element : false,\n event : false,\n FileReader : false,\n Float32Array : false,\n Float64Array : false,\n FormData : false,\n focus : false,\n frames : false,\n getComputedStyle : false,\n HTMLElement : false,\n HTMLAnchorElement : false,\n HTMLBaseElement : false,\n HTMLBlockquoteElement: false,\n HTMLBodyElement : false,\n HTMLBRElement : false,\n HTMLButtonElement : false,\n HTMLCanvasElement : false,\n HTMLDirectoryElement : false,\n HTMLDivElement : false,\n HTMLDListElement : false,\n HTMLFieldSetElement : false,\n HTMLFontElement : false,\n HTMLFormElement : false,\n HTMLFrameElement : false,\n HTMLFrameSetElement : false,\n HTMLHeadElement : false,\n HTMLHeadingElement : false,\n HTMLHRElement : false,\n HTMLHtmlElement : false,\n HTMLIFrameElement : false,\n HTMLImageElement : false,\n HTMLInputElement : false,\n HTMLIsIndexElement : false,\n HTMLLabelElement : false,\n HTMLLayerElement : false,\n HTMLLegendElement : false,\n HTMLLIElement : false,\n HTMLLinkElement : false,\n HTMLMapElement : false,\n HTMLMenuElement : false,\n HTMLMetaElement : false,\n HTMLModElement : false,\n HTMLObjectElement : false,\n HTMLOListElement : false,\n HTMLOptGroupElement : false,\n HTMLOptionElement : false,\n HTMLParagraphElement : false,\n HTMLParamElement : false,\n HTMLPreElement : false,\n HTMLQuoteElement : false,\n HTMLScriptElement : false,\n HTMLSelectElement : false,\n HTMLStyleElement : false,\n HTMLTableCaptionElement: false,\n HTMLTableCellElement : false,\n HTMLTableColElement : false,\n HTMLTableElement : false,\n HTMLTableRowElement : false,\n HTMLTableSectionElement: false,\n HTMLTextAreaElement : false,\n HTMLTitleElement : false,\n HTMLUListElement : false,\n HTMLVideoElement : false,\n history : false,\n Int16Array : false,\n Int32Array : false,\n Int8Array : false,\n Image : false,\n length : false,\n localStorage : false,\n location : false,\n MessageChannel : false,\n MessageEvent : false,\n MessagePort : false,\n moveBy : false,\n moveTo : false,\n MutationObserver : false,\n name : false,\n Node : false,\n NodeFilter : false,\n navigator : false,\n onbeforeunload : true,\n onblur : true,\n onerror : true,\n onfocus : true,\n onload : true,\n onresize : true,\n onunload : true,\n open : false,\n openDatabase : false,\n opener : false,\n Option : false,\n parent : false,\n print : false,\n removeEventListener : false,\n resizeBy : false,\n resizeTo : false,\n screen : false,\n scroll : false,\n scrollBy : false,\n scrollTo : false,\n sessionStorage : false,\n setInterval : false,\n setTimeout : false,\n SharedWorker : false,\n status : false,\n top : false,\n Uint16Array : false,\n Uint32Array : false,\n Uint8Array : false,\n Uint8ClampedArray : false,\n WebSocket : false,\n window : false,\n Worker : false,\n XMLHttpRequest : false,\n XMLSerializer : false,\n XPathEvaluator : false,\n XPathException : false,\n XPathExpression : false,\n XPathNamespace : false,\n XPathNSResolver : false,\n XPathResult : false\n};\n\nexports.devel = {\n alert : false,\n confirm: false,\n console: false,\n Debug : false,\n opera : false,\n prompt : false\n};\n\nexports.worker = {\n importScripts: true,\n postMessage : true,\n self : true\n};\n\n// Widely adopted global names that are not part of ECMAScript standard\nexports.nonstandard = {\n escape : false,\n unescape: false\n};\n\n// Globals provided by popular JavaScript environments.\n\nexports.couch = {\n "require" : false,\n respond : false,\n getRow : false,\n emit : false,\n send : false,\n start : false,\n sum : false,\n log : false,\n exports : false,\n module : false,\n provides : false\n};\n\nexports.node = {\n __filename : false,\n __dirname : false,\n Buffer : false,\n DataView : false,\n console : false,\n exports : true, // In Node it is ok to exports = module.exports = foo();\n GLOBAL : false,\n global : false,\n module : false,\n process : false,\n require : false,\n setTimeout : false,\n clearTimeout : false,\n setInterval : false,\n clearInterval: false\n};\n\nexports.phantom = {\n phantom : true,\n require : true,\n WebPage : true\n};\n\nexports.rhino = {\n defineClass : false,\n deserialize : false,\n gc : false,\n help : false,\n importPackage: false,\n "java" : false,\n load : false,\n loadClass : false,\n print : false,\n quit : false,\n readFile : false,\n readUrl : false,\n runCommand : false,\n seal : false,\n serialize : false,\n spawn : false,\n sync : false,\n toint32 : false,\n version : false\n};\n\nexports.wsh = {\n ActiveXObject : true,\n Enumerator : true,\n GetObject : true,\n ScriptEngine : true,\n ScriptEngineBuildVersion : true,\n ScriptEngineMajorVersion : true,\n ScriptEngineMinorVersion : true,\n VBArray : true,\n WSH : true,\n WScript : true,\n XDomainRequest : true\n};\n\n// Globals provided by popular JavaScript libraries.\n\nexports.dojo = {\n dojo : false,\n dijit : false,\n dojox : false,\n define : false,\n "require": false\n};\n\nexports.jquery = {\n "$" : false,\n jQuery : false\n};\n\nexports.mootools = {\n "$" : false,\n "$$" : false,\n Asset : false,\n Browser : false,\n Chain : false,\n Class : false,\n Color : false,\n Cookie : false,\n Core : false,\n Document : false,\n DomReady : false,\n DOMEvent : false,\n DOMReady : false,\n Drag : false,\n Element : false,\n Elements : false,\n Event : false,\n Events : false,\n Fx : false,\n Group : false,\n Hash : false,\n HtmlTable : false,\n Iframe : false,\n IframeShim : false,\n InputValidator: false,\n instanceOf : false,\n Keyboard : false,\n Locale : false,\n Mask : false,\n MooTools : false,\n Native : false,\n Options : false,\n OverText : false,\n Request : false,\n Scroller : false,\n Slick : false,\n Slider : false,\n Sortables : false,\n Spinner : false,\n Swiff : false,\n Tips : false,\n Type : false,\n typeOf : false,\n URI : false,\n Window : false\n};\n\nexports.prototypejs = {\n "$" : false,\n "$$" : false,\n "$A" : false,\n "$F" : false,\n "$H" : false,\n "$R" : false,\n "$break" : false,\n "$continue" : false,\n "$w" : false,\n Abstract : false,\n Ajax : false,\n Class : false,\n Enumerable : false,\n Element : false,\n Event : false,\n Field : false,\n Form : false,\n Hash : false,\n Insertion : false,\n ObjectRange : false,\n PeriodicalExecuter: false,\n Position : false,\n Prototype : false,\n Selector : false,\n Template : false,\n Toggle : false,\n Try : false,\n Autocompleter : false,\n Builder : false,\n Control : false,\n Draggable : false,\n Draggables : false,\n Droppables : false,\n Effect : false,\n Sortable : false,\n SortableObserver : false,\n Sound : false,\n Scriptaculous : false\n};\n\nexports.yui = {\n YUI : false,\n Y : false,\n YUI_config: false\n};\n\n\n//@ sourceURL=/src/shared/vars.js')); -require.define("/src/shared/messages.js",Function(["require","module","exports","__dirname","__filename","process","global"],'"use strict";\n\nvar _ = require("underscore");\n\nvar errors = {\n // JSHint options\n E001: "Bad option: \'{a}\'.",\n E002: "Bad option value.",\n\n // JSHint input\n E003: "Expected a JSON value.",\n E004: "Input is neither a string nor an array of strings.",\n E005: "Input is empty.",\n E006: "Unexpected early end of program.",\n\n // Strict mode\n E007: "Missing \\"use strict\\" statement.",\n E008: "Strict violation.",\n E009: "Option \'validthis\' can\'t be used in a global scope.",\n E010: "\'with\' is not allowed in strict mode.",\n\n // Constants\n E011: "const \'{a}\' has already been declared.",\n E012: "const \'{a}\' is initialized to \'undefined\'.",\n E013: "Attempting to override \'{a}\' which is a constant.",\n\n // Regular expressions\n E014: "A regular expression literal can be confused with \'/=\'.",\n E015: "Unclosed regular expression.",\n E016: "Invalid regular expression.",\n\n // Tokens\n E017: "Unclosed comment.",\n E018: "Unbegun comment.",\n E019: "Unmatched \'{a}\'.",\n E020: "Expected \'{a}\' to match \'{b}\' from line {c} and instead saw \'{d}\'.",\n E021: "Expected \'{a}\' and instead saw \'{b}\'.",\n E022: "Line breaking error \'{a}\'.",\n E023: "Missing \'{a}\'.",\n E024: "Unexpected \'{a}\'.",\n E025: "Missing \':\' on a case clause.",\n E026: "Missing \'}\' to match \'{\' from line {a}.",\n E027: "Missing \']\' to match \'[\' form line {a}.",\n E028: "Illegal comma.",\n E029: "Unclosed string.",\n\n // Everything else\n E030: "Expected an identifier and instead saw \'{a}\'.",\n E031: "Bad assignment.", // FIXME: Rephrase\n E032: "Expected a small integer and instead saw \'{a}\'.",\n E033: "Expected an operator and instead saw \'{a}\'.",\n E034: "get/set are ES5 features.",\n E035: "Missing property name.",\n E036: "Expected to see a statement and instead saw a block.",\n E037: "Constant {a} was not declared correctly.",\n E038: "Variable {a} was not declared correctly.",\n E039: "Function declarations are not invocable. Wrap the whole function invocation in parens.",\n E040: "Each value should have its own case label.",\n E041: "Unrecoverable syntax error.",\n E042: "Stopping.",\n E043: "Too many errors."\n};\n\nvar warnings = {\n W001: "\'hasOwnProperty\' is a really bad name.",\n W002: "Value of \'{a}\' may be overwritten in IE.",\n W003: "\'{a}\' was used before it was defined.",\n W004: "\'{a}\' is already defined.",\n W005: "A dot following a number can be confused with a decimal point.",\n W006: "Confusing minuses.",\n W007: "Confusing pluses.",\n W008: "A leading decimal point can be confused with a dot: \'{a}\'.",\n W009: "The array literal notation [] is preferrable.",\n W010: "The object literal notation {} is preferrable.",\n W011: "Unexpected space after \'{a}\'.",\n W012: "Unexpected space before \'{a}\'.",\n W013: "Missing space after \'{a}\'.",\n W014: "Bad line breaking before \'{a}\'.",\n W015: "Expected \'{a}\' to have an indentation at {b} instead at {c}.",\n W016: "Unexpected use of \'{a}\'.",\n W017: "Bad operand.",\n W018: "Confusing use of \'{a}\'.",\n W019: "Use the isNaN function to compare with NaN.",\n W020: "Read only.",\n W021: "\'{a}\' is a function.",\n W022: "Do not assign to the exception parameter.",\n W023: "Expected an identifier in an assignment and instead saw a function invocation.",\n W024: "Expected an identifier and instead saw \'{a}\' (a reserved word).",\n W025: "Missing name in function declaration.",\n W026: "Inner functions should be listed at the top of the outer function.",\n W027: "Unreachable \'{a}\' after \'{b}\'.",\n W028: "Label \'{a}\' on {b} statement.",\n W029: "Label \'{a}\' looks like a javascript url.",\n W030: "Expected an assignment or function call and instead saw an expression.",\n W031: "Do not use \'new\' for side effects.",\n W032: "Unnecessary semicolon.",\n W033: "Missing semicolon.",\n W034: "Unnecessary directive \\"{a}\\".",\n W035: "Empty block.",\n W036: "Unexpected /*member \'{a}\'.",\n W037: "\'{a}\' is a statement label.",\n W038: "\'{a}\' used out of scope.",\n W039: "\'{a}\' is not allowed.",\n W040: "Possible strict violation.",\n W041: "Use \'{a}\' to compare with \'{b}\'.",\n W042: "Avoid EOL escaping.",\n W043: "Bad escaping of EOL. Use option multistr if needed.",\n W044: "Bad escaping.",\n W045: "Bad number \'{a}\'.",\n W046: "Don\'t use extra leading zeros \'{a}\'.",\n W047: "A trailing decimal point can be confused with a dot: \'{a}\'.",\n W048: "Unexpected control character in regular expression.",\n W049: "Unexpected escaped character \'{a}\' in regular expression.",\n W050: "JavaScript URL.",\n W051: "Variables should not be deleted.",\n W052: "Unexpected \'{a}\'.",\n W053: "Do not use {a} as a constructor.",\n W054: "The Function constructor is a form of eval.",\n W055: "A constructor name should start with an uppercase letter.",\n W056: "Bad constructor.",\n W057: "Weird construction. Is \'new\' unnecessary?",\n W058: "Missing \'()\' invoking a constructor.",\n W059: "Avoid arguments.{a}.",\n W060: "document.write can be a form of eval.",\n W061: "eval can be harmful.",\n W062: "Wrap an immediate function invocation in parens " +\n "to assist the reader in understanding that the expression " +\n "is the result of a function, and not the function itself.",\n W063: "Math is not a function.",\n W064: "Missing \'new\' prefix when invoking a constructor.",\n W065: "Missing radix parameter.",\n W066: "Implied eval. Consider passing a function instead of a string.",\n W067: "Bad invocation.",\n W068: "Wrapping non-IIFE function literals in parens is unnecessary.",\n W069: "[\'{a}\'] is better written in dot notation.",\n W070: "Extra comma. (it breaks older versions of IE)",\n W071: "This function has too many statements. ({a})",\n W072: "This function has too many parameters. ({a})",\n W073: "Blocks are nested too deeply. ({a})",\n W074: "This function\'s cyclomatic complexity is too high. ({a})",\n W075: "Duplicate key \'{a}\'.",\n W076: "Unexpected parameter \'{a}\' in get {b} function.",\n W077: "Expected a single parameter in set {a} function.",\n W078: "Setter is defined without getter.",\n W079: "Redefinition of \'{a}\'.",\n W080: "It\'s not necessary to initialize \'{a}\' to \'undefined\'.",\n W081: "Too many var statements.",\n W082: "Function declarations should not be placed in blocks. " +\n "Use a function expression or move the statement to the top of " +\n "the outer function.",\n W083: "Don\'t make functions within a loop.",\n W084: "Expected a conditional expression and instead saw an assignment.",\n W085: "Don\'t use \'with\'.",\n W086: "Expected a \'break\' statement before \'{a}\'.",\n W087: "Forgotten \'debugger\' statement?",\n W088: "Creating global \'for\' variable. Should be \'for (var {a} ...\'.",\n W089: "The body of a for in should be wrapped in an if statement to filter " +\n "unwanted properties from the prototype.",\n W090: "\'{a}\' is not a statement label.",\n W091: "\'{a}\' is out of scope.",\n W092: "Wrap the /regexp/ literal in parens to disambiguate the slash operator.",\n W093: "Did you mean to return a conditional instead of an assignment?",\n W094: "Unexpected comma.",\n W095: "Expected a string and instead saw {a}.",\n W096: "The \'{a}\' key may produce unexpected results.",\n W097: "Use the function form of \\"use strict\\".",\n W098: "\'{a}\' is defined but never used.",\n W099: "Mixed spaces and tabs.",\n W100: "This character may get silently deleted by one or more browsers.",\n W101: "Line is too long.",\n W102: "Trailing whitespace.",\n W103: "The \'{a}\' property is deprecated.",\n W104: "\'{a}\' is only available in JavaScript 1.7.",\n W105: "Unexpected {a} in \'{b}\'.",\n W106: "Identifier \'{a}\' is not in camel case.",\n W107: "Script URL.",\n W108: "Strings must use doublequote.",\n W109: "Strings must use singlequote.",\n W110: "Mixed double and single quotes.",\n W112: "Unclosed string.",\n W113: "Control character in string: {a}.",\n W114: "Avoid {a}.",\n W115: "Octal literals are not allowed in strict mode.",\n W116: "Expected \'{a}\' and instead saw \'{b}\'.",\n W117: "\'{a}\' is not defined.",\n};\n\nvar info = {\n I001: "Comma warnings can be turned off with \'laxcomma\'."\n};\n\nexports.errors = {};\nexports.warnings = {};\nexports.info = {};\n\n_.each(errors, function (desc, code) {\n exports.errors[code] = { code: code, desc: desc };\n});\n\n_.each(warnings, function (desc, code) {\n exports.warnings[code] = { code: code, desc: desc };\n});\n\n_.each(info, function (desc, code) {\n exports.info[code] = { code: code, desc: desc };\n});\n\n//@ sourceURL=/src/shared/messages.js')); -require.define("/src/stable/lex.js",Function(["require","module","exports","__dirname","__filename","process","global"],'/*\n * Lexical analysis and token construction.\n */\n\n"use strict";\n\nvar _ = require("underscore");\nvar events = require("events");\nvar reg = require("./reg.js");\nvar state = require("./state.js").state;\n\n// Some of these token types are from JavaScript Parser API\n// while others are specific to JSHint parser.\n// JS Parser API: https://developer.mozilla.org/en-US/docs/SpiderMonkey/Parser_API\n\nvar Token = {\n Identifier: 1,\n Punctuator: 2,\n NumericLiteral: 3,\n StringLiteral: 4,\n Comment: 5,\n Keyword: 6,\n NullLiteral: 7,\n BooleanLiteral: 8,\n RegExp: 9\n};\n\n// This is auto generated from the unicode tables.\n// The tables are at:\n// http://www.fileformat.info/info/unicode/category/Lu/list.htm\n// http://www.fileformat.info/info/unicode/category/Ll/list.htm\n// http://www.fileformat.info/info/unicode/category/Lt/list.htm\n// http://www.fileformat.info/info/unicode/category/Lm/list.htm\n// http://www.fileformat.info/info/unicode/category/Lo/list.htm\n// http://www.fileformat.info/info/unicode/category/Nl/list.htm\n\nvar unicodeLetterTable = [\n 170, 170, 181, 181, 186, 186, 192, 214,\n 216, 246, 248, 705, 710, 721, 736, 740, 748, 748, 750, 750,\n 880, 884, 886, 887, 890, 893, 902, 902, 904, 906, 908, 908,\n 910, 929, 931, 1013, 1015, 1153, 1162, 1319, 1329, 1366,\n 1369, 1369, 1377, 1415, 1488, 1514, 1520, 1522, 1568, 1610,\n 1646, 1647, 1649, 1747, 1749, 1749, 1765, 1766, 1774, 1775,\n 1786, 1788, 1791, 1791, 1808, 1808, 1810, 1839, 1869, 1957,\n 1969, 1969, 1994, 2026, 2036, 2037, 2042, 2042, 2048, 2069,\n 2074, 2074, 2084, 2084, 2088, 2088, 2112, 2136, 2308, 2361,\n 2365, 2365, 2384, 2384, 2392, 2401, 2417, 2423, 2425, 2431,\n 2437, 2444, 2447, 2448, 2451, 2472, 2474, 2480, 2482, 2482,\n 2486, 2489, 2493, 2493, 2510, 2510, 2524, 2525, 2527, 2529,\n 2544, 2545, 2565, 2570, 2575, 2576, 2579, 2600, 2602, 2608,\n 2610, 2611, 2613, 2614, 2616, 2617, 2649, 2652, 2654, 2654,\n 2674, 2676, 2693, 2701, 2703, 2705, 2707, 2728, 2730, 2736,\n 2738, 2739, 2741, 2745, 2749, 2749, 2768, 2768, 2784, 2785,\n 2821, 2828, 2831, 2832, 2835, 2856, 2858, 2864, 2866, 2867,\n 2869, 2873, 2877, 2877, 2908, 2909, 2911, 2913, 2929, 2929,\n 2947, 2947, 2949, 2954, 2958, 2960, 2962, 2965, 2969, 2970,\n 2972, 2972, 2974, 2975, 2979, 2980, 2984, 2986, 2990, 3001,\n 3024, 3024, 3077, 3084, 3086, 3088, 3090, 3112, 3114, 3123,\n 3125, 3129, 3133, 3133, 3160, 3161, 3168, 3169, 3205, 3212,\n 3214, 3216, 3218, 3240, 3242, 3251, 3253, 3257, 3261, 3261,\n 3294, 3294, 3296, 3297, 3313, 3314, 3333, 3340, 3342, 3344,\n 3346, 3386, 3389, 3389, 3406, 3406, 3424, 3425, 3450, 3455,\n 3461, 3478, 3482, 3505, 3507, 3515, 3517, 3517, 3520, 3526,\n 3585, 3632, 3634, 3635, 3648, 3654, 3713, 3714, 3716, 3716,\n 3719, 3720, 3722, 3722, 3725, 3725, 3732, 3735, 3737, 3743,\n 3745, 3747, 3749, 3749, 3751, 3751, 3754, 3755, 3757, 3760,\n 3762, 3763, 3773, 3773, 3776, 3780, 3782, 3782, 3804, 3805,\n 3840, 3840, 3904, 3911, 3913, 3948, 3976, 3980, 4096, 4138,\n 4159, 4159, 4176, 4181, 4186, 4189, 4193, 4193, 4197, 4198,\n 4206, 4208, 4213, 4225, 4238, 4238, 4256, 4293, 4304, 4346,\n 4348, 4348, 4352, 4680, 4682, 4685, 4688, 4694, 4696, 4696,\n 4698, 4701, 4704, 4744, 4746, 4749, 4752, 4784, 4786, 4789,\n 4792, 4798, 4800, 4800, 4802, 4805, 4808, 4822, 4824, 4880,\n 4882, 4885, 4888, 4954, 4992, 5007, 5024, 5108, 5121, 5740,\n 5743, 5759, 5761, 5786, 5792, 5866, 5870, 5872, 5888, 5900,\n 5902, 5905, 5920, 5937, 5952, 5969, 5984, 5996, 5998, 6000,\n 6016, 6067, 6103, 6103, 6108, 6108, 6176, 6263, 6272, 6312,\n 6314, 6314, 6320, 6389, 6400, 6428, 6480, 6509, 6512, 6516,\n 6528, 6571, 6593, 6599, 6656, 6678, 6688, 6740, 6823, 6823,\n 6917, 6963, 6981, 6987, 7043, 7072, 7086, 7087, 7104, 7141,\n 7168, 7203, 7245, 7247, 7258, 7293, 7401, 7404, 7406, 7409,\n 7424, 7615, 7680, 7957, 7960, 7965, 7968, 8005, 8008, 8013,\n 8016, 8023, 8025, 8025, 8027, 8027, 8029, 8029, 8031, 8061,\n 8064, 8116, 8118, 8124, 8126, 8126, 8130, 8132, 8134, 8140,\n 8144, 8147, 8150, 8155, 8160, 8172, 8178, 8180, 8182, 8188,\n 8305, 8305, 8319, 8319, 8336, 8348, 8450, 8450, 8455, 8455,\n 8458, 8467, 8469, 8469, 8473, 8477, 8484, 8484, 8486, 8486,\n 8488, 8488, 8490, 8493, 8495, 8505, 8508, 8511, 8517, 8521,\n 8526, 8526, 8544, 8584, 11264, 11310, 11312, 11358,\n 11360, 11492, 11499, 11502, 11520, 11557, 11568, 11621,\n 11631, 11631, 11648, 11670, 11680, 11686, 11688, 11694,\n 11696, 11702, 11704, 11710, 11712, 11718, 11720, 11726,\n 11728, 11734, 11736, 11742, 11823, 11823, 12293, 12295,\n 12321, 12329, 12337, 12341, 12344, 12348, 12353, 12438,\n 12445, 12447, 12449, 12538, 12540, 12543, 12549, 12589,\n 12593, 12686, 12704, 12730, 12784, 12799, 13312, 13312,\n 19893, 19893, 19968, 19968, 40907, 40907, 40960, 42124,\n 42192, 42237, 42240, 42508, 42512, 42527, 42538, 42539,\n 42560, 42606, 42623, 42647, 42656, 42735, 42775, 42783,\n 42786, 42888, 42891, 42894, 42896, 42897, 42912, 42921,\n 43002, 43009, 43011, 43013, 43015, 43018, 43020, 43042,\n 43072, 43123, 43138, 43187, 43250, 43255, 43259, 43259,\n 43274, 43301, 43312, 43334, 43360, 43388, 43396, 43442,\n 43471, 43471, 43520, 43560, 43584, 43586, 43588, 43595,\n 43616, 43638, 43642, 43642, 43648, 43695, 43697, 43697,\n 43701, 43702, 43705, 43709, 43712, 43712, 43714, 43714,\n 43739, 43741, 43777, 43782, 43785, 43790, 43793, 43798,\n 43808, 43814, 43816, 43822, 43968, 44002, 44032, 44032,\n 55203, 55203, 55216, 55238, 55243, 55291, 63744, 64045,\n 64048, 64109, 64112, 64217, 64256, 64262, 64275, 64279,\n 64285, 64285, 64287, 64296, 64298, 64310, 64312, 64316,\n 64318, 64318, 64320, 64321, 64323, 64324, 64326, 64433,\n 64467, 64829, 64848, 64911, 64914, 64967, 65008, 65019,\n 65136, 65140, 65142, 65276, 65313, 65338, 65345, 65370,\n 65382, 65470, 65474, 65479, 65482, 65487, 65490, 65495,\n 65498, 65500, 65536, 65547, 65549, 65574, 65576, 65594,\n 65596, 65597, 65599, 65613, 65616, 65629, 65664, 65786,\n 65856, 65908, 66176, 66204, 66208, 66256, 66304, 66334,\n 66352, 66378, 66432, 66461, 66464, 66499, 66504, 66511,\n 66513, 66517, 66560, 66717, 67584, 67589, 67592, 67592,\n 67594, 67637, 67639, 67640, 67644, 67644, 67647, 67669,\n 67840, 67861, 67872, 67897, 68096, 68096, 68112, 68115,\n 68117, 68119, 68121, 68147, 68192, 68220, 68352, 68405,\n 68416, 68437, 68448, 68466, 68608, 68680, 69635, 69687,\n 69763, 69807, 73728, 74606, 74752, 74850, 77824, 78894,\n 92160, 92728, 110592, 110593, 119808, 119892, 119894, 119964,\n 119966, 119967, 119970, 119970, 119973, 119974, 119977, 119980,\n 119982, 119993, 119995, 119995, 119997, 120003, 120005, 120069,\n 120071, 120074, 120077, 120084, 120086, 120092, 120094, 120121,\n 120123, 120126, 120128, 120132, 120134, 120134, 120138, 120144,\n 120146, 120485, 120488, 120512, 120514, 120538, 120540, 120570,\n 120572, 120596, 120598, 120628, 120630, 120654, 120656, 120686,\n 120688, 120712, 120714, 120744, 120746, 120770, 120772, 120779,\n 131072, 131072, 173782, 173782, 173824, 173824, 177972, 177972,\n 177984, 177984, 178205, 178205, 194560, 195101\n];\n\nvar identifierStartTable = [];\n\nfor (var i = 0; i < 128; i++) {\n identifierStartTable[i] =\n i === 36 || // $\n i >= 65 && i <= 90 || // A-Z\n i === 95 || // _\n i >= 97 && i <= 122; // a-z\n}\n\nvar identifierPartTable = [];\n\nfor (var i = 0; i < 128; i++) {\n identifierPartTable[i] =\n identifierStartTable[i] || // $, _, A-Z, a-z\n i >= 48 && i <= 57; // 0-9\n}\n\n/*\n * Lexer for JSHint.\n *\n * This object does a char-by-char scan of the provided source code\n * and produces a sequence of tokens.\n *\n * var lex = new Lexer("var i = 0;");\n * lex.start();\n * lex.token(); // returns the next token\n *\n * You have to use the token() method to move the lexer forward\n * but you don\'t have to use its return value to get tokens. In addition\n * to token() method returning the next token, the Lexer object also\n * emits events.\n *\n * lex.on("Identifier", function (data) {\n * if (data.name.indexOf("_") >= 0) {\n * // Produce a warning.\n * }\n * });\n *\n * Note that the token() method returns tokens in a JSLint-compatible\n * format while the event emitter uses a slightly modified version of\n * Mozilla\'s JavaScript Parser API. Eventually, we will move away from\n * JSLint format.\n */\nfunction Lexer(source) {\n var lines = source;\n\n if (typeof lines === "string") {\n lines = lines\n .replace(/\\r\\n/g, "\\n")\n .replace(/\\r/g, "\\n")\n .split("\\n");\n }\n\n // If the first line is a shebang (#!), make it a blank and move on.\n // Shebangs are used by Node scripts.\n\n if (lines[0] && lines[0].substr(0, 2) === "#!") {\n lines[0] = "";\n }\n\n this.emitter = new events.EventEmitter();\n this.source = source;\n this.lines = lines;\n this.prereg = true;\n\n this.line = 0;\n this.char = 1;\n this.from = 1;\n this.input = "";\n\n for (var i = 0; i < state.option.indent; i += 1) {\n state.tab += " ";\n }\n}\n\nLexer.prototype = {\n _lines: [],\n\n get lines() {\n this._lines = state.lines;\n return this._lines;\n },\n\n set lines(val) {\n this._lines = val;\n state.lines = this._lines;\n },\n\n /*\n * Return the next i character without actually moving the\n * char pointer.\n */\n peek: function (i) {\n return this.input.charAt(i || 0);\n },\n\n /*\n * Move the char pointer forward i times.\n */\n skip: function (i) {\n i = i || 1;\n this.char += i;\n this.input = this.input.slice(i);\n },\n\n /*\n * Subscribe to a token event. The API for this method is similar\n * Underscore.js i.e. you can subscribe to multiple events with\n * one call:\n *\n * lex.on("Identifier Number", function (data) {\n * // ...\n * });\n */\n on: function (names, listener) {\n names.split(" ").forEach(function (name) {\n this.emitter.on(name, listener);\n }.bind(this));\n },\n\n /*\n * Trigger a token event. All arguments will be passed to each\n * listener.\n */\n trigger: function () {\n this.emitter.emit.apply(this.emitter, Array.prototype.slice.call(arguments));\n },\n\n /*\n * Extract a punctuator out of the next sequence of characters\n * or return \'null\' if its not possible.\n *\n * This method\'s implementation was heavily influenced by the\n * scanPunctuator function in the Esprima parser\'s source code.\n */\n scanPunctuator: function () {\n var ch1 = this.peek();\n var ch2, ch3, ch4;\n\n switch (ch1) {\n // Most common single-character punctuators\n case ".":\n if ((/^[0-9]$/).test(this.peek(1))) {\n return null;\n }\n\n /* falls through */\n case "(":\n case ")":\n case ";":\n case ",":\n case "{":\n case "}":\n case "[":\n case "]":\n case ":":\n case "~":\n case "?":\n return {\n type: Token.Punctuator,\n value: ch1\n };\n\n // A pound sign (for Node shebangs)\n case "#":\n return {\n type: Token.Punctuator,\n value: ch1\n };\n\n // We\'re at the end of input\n case "":\n return null;\n }\n\n // Peek more characters\n\n ch2 = this.peek(1);\n ch3 = this.peek(2);\n ch4 = this.peek(3);\n\n // 4-character punctuator: >>>=\n\n if (ch1 === ">" && ch2 === ">" && ch3 === ">" && ch4 === "=") {\n return {\n type: Token.Punctuator,\n value: ">>>="\n };\n }\n\n // 3-character punctuators: === !== >>> <<= >>=\n\n if (ch1 === "=" && ch2 === "=" && ch3 === "=") {\n return {\n type: Token.Punctuator,\n value: "==="\n };\n }\n\n if (ch1 === "!" && ch2 === "=" && ch3 === "=") {\n return {\n type: Token.Punctuator,\n value: "!=="\n };\n }\n\n if (ch1 === ">" && ch2 === ">" && ch3 === ">") {\n return {\n type: Token.Punctuator,\n value: ">>>"\n };\n }\n\n if (ch1 === "<" && ch2 === "<" && ch3 === "=") {\n return {\n type: Token.Punctuator,\n value: "<<="\n };\n }\n\n if (ch1 === ">" && ch2 === ">" && ch3 === "=") {\n return {\n type: Token.Punctuator,\n value: "<<="\n };\n }\n\n // 2-character punctuators: <= >= == != ++ -- << >> && ||\n // += -= *= %= &= |= ^= (but not /=, see below)\n if (ch1 === ch2 && ("+-<>&|".indexOf(ch1) >= 0)) {\n return {\n type: Token.Punctuator,\n value: ch1 + ch2\n };\n }\n\n if ("<>=!+-*%&|^".indexOf(ch1) >= 0) {\n if (ch2 === "=") {\n return {\n type: Token.Punctuator,\n value: ch1 + ch2\n };\n }\n\n return {\n type: Token.Punctuator,\n value: ch1\n };\n }\n\n // Special case: /=. We need to make sure that this is an\n // operator and not a regular expression.\n\n if (ch1 === "/") {\n if (ch2 === "=" && /\\/=(?!(\\S*\\/[gim]?))/.test(this.input)) {\n // /= is not a part of a regular expression, return it as a\n // punctuator.\n return {\n type: Token.Punctuator,\n value: "/="\n };\n }\n\n return {\n type: Token.Punctuator,\n value: "/"\n };\n }\n\n return null;\n },\n\n /*\n * Extract a comment out of the next sequence of characters and/or\n * lines or return \'null\' if its not possible. Since comments can\n * span across multiple lines this method has to move the char\n * pointer.\n *\n * In addition to normal JavaScript comments (// and /*) this method\n * also recognizes JSHint- and JSLint-specific comments such as\n * /*jshint, /*jslint, /*globals and so on.\n */\n scanComments: function () {\n var ch1 = this.peek();\n var ch2 = this.peek(1);\n var rest = this.input.substr(2);\n var startLine = this.line;\n var startChar = this.char;\n\n // Create a comment token object and make sure it\n // has all the data JSHint needs to work with special\n // comments.\n\n function commentToken(label, body, opt) {\n var special = ["jshint", "jslint", "members", "member", "globals", "global", "exported"];\n var isSpecial = false;\n var value = label + body;\n var commentType = "plain";\n opt = opt || {};\n\n if (opt.isMultiline) {\n value += "*/";\n }\n\n special.forEach(function (str) {\n if (isSpecial) {\n return;\n }\n\n // Don\'t recognize any special comments other than jshint for single-line\n // comments. This introduced many problems with legit comments.\n if (label === "//" && str !== "jshint") {\n return;\n }\n\n if (body.substr(0, str.length) === str) {\n isSpecial = true;\n label = label + str;\n body = body.substr(str.length);\n }\n\n if (!isSpecial && body.charAt(0) === " " && body.substr(1, str.length) === str) {\n isSpecial = true;\n label = label + " " + str;\n body = body.substr(str.length + 1);\n }\n\n if (!isSpecial) {\n return;\n }\n\n switch (str) {\n case "member":\n commentType = "members";\n break;\n case "global":\n commentType = "globals";\n break;\n default:\n commentType = str;\n }\n });\n\n return {\n type: Token.Comment,\n commentType: commentType,\n value: value,\n body: body,\n isSpecial: isSpecial,\n isMultiline: opt.isMultiline || false,\n isMalformed: opt.isMalformed || false\n };\n }\n\n // End of unbegun comment. Raise an error and skip that input.\n if (ch1 === "*" && ch2 === "/") {\n this.trigger("error", {\n code: "E018",\n line: startLine,\n character: startChar\n });\n\n this.skip(2);\n return null;\n }\n\n // Comments must start either with // or /*\n if (ch1 !== "/" || (ch2 !== "*" && ch2 !== "/")) {\n return null;\n }\n\n // One-line comment\n if (ch2 === "/") {\n this.skip(this.input.length); // Skip to the EOL.\n return commentToken("//", rest);\n }\n\n var body = "";\n\n /* Multi-line comment */\n if (ch2 === "*") {\n this.skip(2);\n\n while (this.peek() !== "*" || this.peek(1) !== "/") {\n if (this.peek() === "") { // End of Line\n body += "\\n";\n\n // If we hit EOF and our comment is still unclosed,\n // trigger an error and end the comment implicitly.\n if (!this.nextLine()) {\n this.trigger("error", {\n code: "E017",\n line: startLine,\n character: startChar\n });\n\n return commentToken("/*", body, {\n isMultiline: true,\n isMalformed: true\n });\n }\n } else {\n body += this.peek();\n this.skip();\n }\n }\n\n this.skip(2);\n return commentToken("/*", body, { isMultiline: true });\n }\n },\n\n /*\n * Extract a keyword out of the next sequence of characters or\n * return \'null\' if its not possible.\n */\n scanKeyword: function () {\n var result = /^[a-zA-Z_$][a-zA-Z0-9_$]*/.exec(this.input);\n var keywords = [\n "if", "in", "do", "var", "for", "new",\n "try", "let", "this", "else", "case",\n "void", "with", "enum", "while", "break",\n "catch", "throw", "const", "yield", "class",\n "super", "return", "typeof", "delete",\n "switch", "export", "import", "default",\n "finally", "extends", "function", "continue",\n "debugger", "instanceof"\n ];\n\n if (result && keywords.indexOf(result[0]) >= 0) {\n return {\n type: Token.Keyword,\n value: result[0]\n };\n }\n\n return null;\n },\n\n /*\n * Extract a JavaScript identifier out of the next sequence of\n * characters or return \'null\' if its not possible. In addition,\n * to Identifier this method can also produce BooleanLiteral\n * (true/false) and NullLiteral (null).\n */\n scanIdentifier: function () {\n var id = "";\n var index = 0;\n var type, char;\n\n // Detects any character in the Unicode categories "Uppercase\n // letter (Lu)", "Lowercase letter (Ll)", "Titlecase letter\n // (Lt)", "Modifier letter (Lm)", "Other letter (Lo)", or\n // "Letter number (Nl)".\n //\n // Both approach and unicodeLetterTable were borrowed from\n // Google\'s Traceur.\n\n function isUnicodeLetter(code) {\n for (var i = 0; i < unicodeLetterTable.length;) {\n if (code < unicodeLetterTable[i++]) {\n return false;\n }\n\n if (code <= unicodeLetterTable[i++]) {\n return true;\n }\n }\n\n return false;\n }\n\n function isHexDigit(str) {\n return (/^[0-9a-fA-F]$/).test(str);\n }\n\n var readUnicodeEscapeSequence = function () {\n /*jshint validthis:true */\n index += 1;\n\n if (this.peek(index) !== "u") {\n return null;\n }\n\n var ch1 = this.peek(index + 1);\n var ch2 = this.peek(index + 2);\n var ch3 = this.peek(index + 3);\n var ch4 = this.peek(index + 4);\n var code;\n\n if (isHexDigit(ch1) && isHexDigit(ch2) && isHexDigit(ch3) && isHexDigit(ch4)) {\n code = parseInt(ch1 + ch2 + ch3 + ch4, 16);\n\n if (isUnicodeLetter(code)) {\n index += 5;\n return "\\\\u" + ch1 + ch2 + ch3 + ch4;\n }\n\n return null;\n }\n\n return null;\n }.bind(this);\n\n var getIdentifierStart = function () {\n /*jshint validthis:true */\n var chr = this.peek(index);\n var code = chr.charCodeAt(0);\n\n if (code === 92) {\n return readUnicodeEscapeSequence();\n }\n\n if (code < 128) {\n if (identifierStartTable[code]) {\n index += 1;\n return chr;\n }\n\n return null;\n }\n\n if (isUnicodeLetter(code)) {\n index += 1;\n return chr;\n }\n\n return null;\n }.bind(this);\n\n var getIdentifierPart = function () {\n /*jshint validthis:true */\n var chr = this.peek(index);\n var code = chr.charCodeAt(0);\n\n if (code === 92) {\n return readUnicodeEscapeSequence();\n }\n\n if (code < 128) {\n if (identifierPartTable[code]) {\n index += 1;\n return chr;\n }\n\n return null;\n }\n\n if (isUnicodeLetter(code)) {\n index += 1;\n return chr;\n }\n\n return null;\n }.bind(this);\n\n char = getIdentifierStart();\n if (char === null) {\n return null;\n }\n\n id = char;\n for (;;) {\n char = getIdentifierPart();\n\n if (char === null) {\n break;\n }\n\n id += char;\n }\n\n switch (id) {\n case "true":\n case "false":\n type = Token.BooleanLiteral;\n break;\n case "null":\n type = Token.NullLiteral;\n break;\n default:\n type = Token.Identifier;\n }\n\n return {\n type: type,\n value: id\n };\n },\n\n /*\n * Extract a numeric literal out of the next sequence of\n * characters or return \'null\' if its not possible. This method\n * supports all numeric literals described in section 7.8.3\n * of the EcmaScript 5 specification.\n *\n * This method\'s implementation was heavily influenced by the\n * scanNumericLiteral function in the Esprima parser\'s source code.\n */\n scanNumericLiteral: function () {\n var index = 0;\n var value = "";\n var length = this.input.length;\n var char = this.peek(index);\n var bad;\n\n function isDecimalDigit(str) {\n return (/^[0-9]$/).test(str);\n }\n\n function isOctalDigit(str) {\n return (/^[0-7]$/).test(str);\n }\n\n function isHexDigit(str) {\n return (/^[0-9a-fA-F]$/).test(str);\n }\n\n function isIdentifierStart(ch) {\n return (ch === "$") || (ch === "_") || (ch === "\\\\") ||\n (ch >= "a" && ch <= "z") || (ch >= "A" && ch <= "Z");\n }\n\n // Numbers must start either with a decimal digit or a point.\n\n if (char !== "." && !isDecimalDigit(char)) {\n return null;\n }\n\n if (char !== ".") {\n value = this.peek(index);\n index += 1;\n char = this.peek(index);\n\n if (value === "0") {\n // Base-16 numbers.\n if (char === "x" || char === "X") {\n index += 1;\n value += char;\n\n while (index < length) {\n char = this.peek(index);\n if (!isHexDigit(char)) {\n break;\n }\n value += char;\n index += 1;\n }\n\n if (value.length <= 2) { // 0x\n return {\n type: Token.NumericLiteral,\n value: value,\n isMalformed: true\n };\n }\n\n if (index < length) {\n char = this.peek(index);\n if (isIdentifierStart(char)) {\n return null;\n }\n }\n\n return {\n type: Token.NumericLiteral,\n value: value,\n base: 16,\n isMalformed: false\n };\n }\n\n // Base-8 numbers.\n if (isOctalDigit(char)) {\n index += 1;\n value += char;\n bad = false;\n\n while (index < length) {\n char = this.peek(index);\n\n // Numbers like \'019\' (note the 9) are not valid octals\n // but we still parse them and mark as malformed.\n\n if (isDecimalDigit(char)) {\n bad = true;\n } else if (!isOctalDigit(char)) {\n break;\n }\n value += char;\n index += 1;\n }\n\n if (index < length) {\n char = this.peek(index);\n if (isIdentifierStart(char)) {\n return null;\n }\n }\n\n return {\n type: Token.NumericLiteral,\n value: value,\n base: 8,\n isMalformed: false\n };\n }\n\n // Decimal numbers that start with \'0\' such as \'09\' are illegal\n // but we still parse them and return as malformed.\n\n if (isDecimalDigit(char)) {\n index += 1;\n value += char;\n }\n }\n\n while (index < length) {\n char = this.peek(index);\n if (!isDecimalDigit(char)) {\n break;\n }\n value += char;\n index += 1;\n }\n }\n\n // Decimal digits.\n\n if (char === ".") {\n value += char;\n index += 1;\n\n while (index < length) {\n char = this.peek(index);\n if (!isDecimalDigit(char)) {\n break;\n }\n value += char;\n index += 1;\n }\n }\n\n // Exponent part.\n\n if (char === "e" || char === "E") {\n value += char;\n index += 1;\n char = this.peek(index);\n\n if (char === "+" || char === "-") {\n value += this.peek(index);\n index += 1;\n }\n\n char = this.peek(index);\n if (isDecimalDigit(char)) {\n value += char;\n index += 1;\n\n while (index < length) {\n char = this.peek(index);\n if (!isDecimalDigit(char)) {\n break;\n }\n value += char;\n index += 1;\n }\n } else {\n return null;\n }\n }\n\n if (index < length) {\n char = this.peek(index);\n if (isIdentifierStart(char)) {\n return null;\n }\n }\n\n return {\n type: Token.NumericLiteral,\n value: value,\n base: 10,\n isMalformed: !isFinite(value)\n };\n },\n\n /*\n * Extract a string out of the next sequence of characters and/or\n * lines or return \'null\' if its not possible. Since strings can\n * span across multiple lines this method has to move the char\n * pointer.\n *\n * This method recognizes pseudo-multiline JavaScript strings:\n *\n * var str = "hello\\\n * world";\n */\n scanStringLiteral: function () {\n var quote = this.peek();\n\n // String must start with a quote.\n if (quote !== "\\"" && quote !== "\'") {\n return null;\n }\n\n // In JSON strings must always use double quotes.\n if (state.jsonMode && quote !== "\\"") {\n this.trigger("warning", {\n code: "W108",\n line: this.line,\n character: this.char // +1?\n });\n }\n\n var value = "";\n var startLine = this.line;\n var startChar = this.char;\n var allowNewLine = false;\n\n this.skip();\n\n while (this.peek() !== quote) {\n while (this.peek() === "") { // End Of Line\n\n // If an EOL is not preceded by a backslash, show a warning\n // and proceed like it was a legit multi-line string where\n // author simply forgot to escape the newline symbol.\n //\n // Another approach is to implicitly close a string on EOL\n // but it generates too many false positives.\n\n if (!allowNewLine) {\n this.trigger("warning", {\n code: "W112",\n line: this.line,\n character: this.char\n });\n } else {\n allowNewLine = false;\n\n // Otherwise show a warning if multistr option was not set.\n // For JSON, show warning no matter what.\n\n if (!state.option.multistr) {\n this.trigger("warning", {\n code: "W043",\n line: this.line,\n character: this.char\n });\n } else if (state.jsonMode) {\n this.trigger("warning", {\n code: "W042",\n line: this.line,\n character: this.char\n });\n }\n }\n\n // If we get an EOF inside of an unclosed string, show an\n // error and implicitly close it at the EOF point.\n\n if (!this.nextLine()) {\n this.trigger("error", {\n code: "E029",\n line: startLine,\n character: startChar\n });\n\n return {\n type: Token.StringLiteral,\n value: value,\n isUnclosed: true,\n quote: quote\n };\n }\n }\n\n allowNewLine = false;\n var char = this.peek();\n var jump = 1; // A length of a jump, after we\'re done\n // parsing this character.\n\n if (char < " ") {\n // Warn about a control character in a string.\n this.trigger("warning", {\n code: "W113",\n line: this.line,\n character: this.char,\n data: [ "" ]\n });\n }\n\n // Special treatment for some escaped characters.\n\n if (char === "\\\\") {\n this.skip();\n char = this.peek();\n\n switch (char) {\n case "\'":\n if (state.jsonMode) {\n this.trigger("warning", {\n code: "W114",\n line: this.line,\n character: this.char,\n data: [ "\\\\\'" ]\n });\n }\n break;\n case "b":\n char = "\\b";\n break;\n case "f":\n char = "\\f";\n break;\n case "n":\n char = "\\n";\n break;\n case "r":\n char = "\\r";\n break;\n case "t":\n char = "\\t";\n break;\n case "0":\n char = "\\0";\n\n // Octal literals fail in strict mode.\n // Check if the number is between 00 and 07.\n var n = parseInt(this.peek(1), 10);\n if (n >= 0 && n <= 7 && state.directive["use strict"]) {\n this.trigger("warning", {\n code: "W115",\n line: this.line,\n character: this.char\n });\n }\n break;\n case "u":\n char = String.fromCharCode(parseInt(this.input.substr(1, 4), 16));\n jump = 5;\n break;\n case "v":\n if (state.jsonMode) {\n this.trigger("warning", {\n code: "W114",\n line: this.line,\n character: this.char,\n data: [ "\\\\v" ]\n });\n }\n\n char = "\\v";\n break;\n case "x":\n var x = parseInt(this.input.substr(1, 2), 16);\n\n if (state.jsonMode) {\n this.trigger("warning", {\n code: "W114",\n line: this.line,\n character: this.char,\n data: [ "\\\\x-" ]\n });\n }\n\n char = String.fromCharCode(x);\n jump = 3;\n break;\n case "\\\\":\n case "\\"":\n case "/":\n break;\n case "":\n allowNewLine = true;\n char = "";\n break;\n case "!":\n if (value.slice(value.length - 2) === "<") {\n break;\n }\n\n /*falls through */\n default:\n // Weird escaping.\n this.trigger("warning", {\n code: "W044",\n line: this.line,\n character: this.char\n });\n }\n }\n\n value += char;\n this.skip(jump);\n }\n\n this.skip();\n return {\n type: Token.StringLiteral,\n value: value,\n isUnclosed: false,\n quote: quote\n };\n },\n\n /*\n * Extract a regular expression out of the next sequence of\n * characters and/or lines or return \'null\' if its not possible.\n *\n * This method is platform dependent: it accepts almost any\n * regular expression values but then tries to compile and run\n * them using system\'s RegExp object. This means that there are\n * rare edge cases where one JavaScript engine complains about\n * your regular expression while others don\'t.\n */\n scanRegExp: function () {\n var index = 0;\n var length = this.input.length;\n var char = this.peek();\n var value = char;\n var body = "";\n var flags = [];\n var malformed = false;\n var isCharSet = false;\n var terminated;\n\n var scanUnexpectedChars = function () {\n // Unexpected control character\n if (char < " ") {\n malformed = true;\n this.trigger("warning", {\n code: "W048",\n line: this.line,\n character: this.char\n });\n }\n\n // Unexpected escaped character\n if (char === "<") {\n malformed = true;\n this.trigger("warning", {\n code: "W049",\n line: this.line,\n character: this.char,\n data: [ char ]\n });\n }\n }.bind(this);\n\n // Regular expressions must start with \'/\'\n if (!this.prereg || char !== "/") {\n return null;\n }\n\n index += 1;\n terminated = false;\n\n // Try to get everything in between slashes. A couple of\n // cases aside (see scanUnexpectedChars) we don\'t really\n // care whether the resulting expression is valid or not.\n // We will check that later using the RegExp object.\n\n while (index < length) {\n char = this.peek(index);\n value += char;\n body += char;\n\n if (isCharSet) {\n if (char === "]") {\n if (this.peek(index - 1) !== "\\\\" || this.peek(index - 2) === "\\\\") {\n isCharSet = false;\n }\n }\n\n if (char === "\\\\") {\n index += 1;\n char = this.peek(index);\n body += char;\n value += char;\n\n scanUnexpectedChars();\n }\n\n index += 1;\n continue;\n }\n\n if (char === "\\\\") {\n index += 1;\n char = this.peek(index);\n body += char;\n value += char;\n\n scanUnexpectedChars();\n\n if (char === "/") {\n index += 1;\n continue;\n }\n\n if (char === "[") {\n index += 1;\n continue;\n }\n }\n\n if (char === "[") {\n isCharSet = true;\n index += 1;\n continue;\n }\n\n if (char === "/") {\n body = body.substr(0, body.length - 1);\n terminated = true;\n index += 1;\n break;\n }\n\n index += 1;\n }\n\n // A regular expression that was never closed is an\n // error from which we cannot recover.\n\n if (!terminated) {\n this.trigger("error", {\n code: "E015",\n line: this.line,\n character: this.from\n });\n\n return void this.trigger("fatal", {\n line: this.line,\n from: this.from\n });\n }\n\n // Parse flags (if any).\n\n while (index < length) {\n char = this.peek(index);\n if (!/[gim]/.test(char)) {\n break;\n }\n flags.push(char);\n value += char;\n index += 1;\n }\n\n // Check regular expression for correctness.\n\n try {\n new RegExp(body, flags.join(""));\n } catch (err) {\n malformed = true;\n this.trigger("error", {\n code: "E016",\n line: this.line,\n character: this.char,\n data: [ err.message ] // Platform dependent!\n });\n }\n\n return {\n type: Token.RegExp,\n value: value,\n flags: flags,\n isMalformed: malformed\n };\n },\n\n /*\n * Scan for any occurence of mixed tabs and spaces. If smarttabs option\n * is on, ignore tabs followed by spaces.\n *\n * Tabs followed by one space followed by a block comment are allowed.\n */\n scanMixedSpacesAndTabs: function () {\n var at, match;\n\n if (state.option.smarttabs) {\n // Negative look-behind for "//"\n match = this.input.match(/(\\/\\/)? \\t/);\n at = match && !match[1] ? 0 : -1;\n } else {\n at = this.input.search(/ \\t|\\t [^\\*]/);\n }\n\n return at;\n },\n\n /*\n * Scan for characters that get silently deleted by one or more browsers.\n */\n scanUnsafeChars: function () {\n return this.input.search(reg.unsafeChars);\n },\n\n /*\n * Produce the next raw token or return \'null\' if no tokens can be matched.\n * This method skips over all space characters.\n */\n next: function () {\n this.from = this.char;\n\n // Move to the next non-space character.\n var start;\n if (/\\s/.test(this.peek())) {\n start = this.char;\n\n while (/\\s/.test(this.peek())) {\n this.from += 1;\n this.skip();\n }\n\n if (this.peek() === "") { // EOL\n if (state.option.trailing) {\n this.trigger("warning", { code: "W102", line: this.line, character: start });\n }\n }\n }\n\n // Methods that work with multi-line structures and move the\n // character pointer.\n\n var match = this.scanComments() ||\n this.scanStringLiteral();\n\n if (match) {\n return match;\n }\n\n // Methods that don\'t move the character pointer.\n\n match =\n this.scanRegExp() ||\n this.scanPunctuator() ||\n this.scanKeyword() ||\n this.scanIdentifier() ||\n this.scanNumericLiteral();\n\n if (match) {\n this.skip(match.value.length);\n return match;\n }\n\n // No token could be matched, give up.\n\n return null;\n },\n\n /*\n * Switch to the next line and reset all char pointers. Once\n * switched, this method also checks for mixed spaces and tabs\n * and other minor warnings.\n */\n nextLine: function () {\n var char;\n\n if (this.line >= this.lines.length) {\n return false;\n }\n\n this.input = this.lines[this.line];\n this.line += 1;\n this.char = 1;\n this.from = 1;\n\n char = this.scanMixedSpacesAndTabs();\n if (char >= 0) {\n this.trigger("warning", { code: "W099", line: this.line, character: char + 1 });\n }\n\n this.input = this.input.replace(/\\t/g, state.tab);\n char = this.scanUnsafeChars();\n\n if (char >= 0) {\n this.trigger("warning", { code: "W100", line: this.line, character: char });\n }\n\n // If there is a limit on line length, warn when lines get too\n // long.\n\n if (state.option.maxlen && state.option.maxlen < this.input.length) {\n this.trigger("warning", { code: "W101", line: this.line, character: this.input.length });\n }\n\n return true;\n },\n\n /*\n * This is simply a synonym for nextLine() method with a friendlier\n * public name.\n */\n start: function () {\n this.nextLine();\n },\n\n /*\n * Produce the next token. This function is called by advance() to get\n * the next token. It retuns a token in a JSLint-compatible format.\n */\n token: function () {\n var token;\n\n function isReserved(token, isProperty) {\n if (!token.reserved) {\n return false;\n }\n\n if (token.meta && token.meta.isFutureReservedWord) {\n // ES3 FutureReservedWord in an ES5 environment.\n if (state.option.es5 && !token.meta.es5) {\n return false;\n }\n\n // Some ES5 FutureReservedWord identifiers are active only\n // within a strict mode environment.\n if (token.meta.strictOnly) {\n if (!state.option.strict && !state.directive["use strict"]) {\n return false;\n }\n }\n\n if (isProperty) {\n return false;\n }\n }\n\n return true;\n }\n\n // Produce a token object.\n var create = function (type, value, isProperty) {\n /*jshint validthis:true */\n var obj;\n\n if (type !== "(endline)" && type !== "(end)") {\n this.prereg = false;\n }\n\n if (type === "(punctuator)") {\n switch (value) {\n case ".":\n case ")":\n case "~":\n case "#":\n case "]":\n this.prereg = false;\n break;\n default:\n this.prereg = true;\n }\n\n obj = Object.create(state.syntax[value] || state.syntax["(error)"]);\n }\n\n if (type === "(identifier)") {\n if (value === "return" || value === "case" || value === "typeof") {\n this.prereg = true;\n }\n\n if (_.has(state.syntax, value)) {\n obj = Object.create(state.syntax[value] || state.syntax["(error)"]);\n\n // If this can\'t be a reserved keyword, reset the object.\n if (!isReserved(obj, isProperty && type === "(identifier)")) {\n obj = null;\n }\n }\n }\n\n if (!obj) {\n obj = Object.create(state.syntax[type]);\n }\n\n obj.identifier = (type === "(identifier)");\n obj.type = obj.type || type;\n obj.value = value;\n obj.line = this.line;\n obj.character = this.char;\n obj.from = this.from;\n\n if (isProperty && obj.identifier) {\n obj.isProperty = isProperty;\n }\n\n return obj;\n }.bind(this);\n\n for (;;) {\n if (!this.input.length) {\n return create(this.nextLine() ? "(endline)" : "(end)", "");\n }\n\n token = this.next();\n\n if (!token) {\n if (this.input.length) {\n // Unexpected character.\n this.trigger("error", {\n code: "E024",\n line: this.line,\n character: this.char,\n data: [ this.peek() ]\n });\n\n this.input = "";\n }\n\n continue;\n }\n\n switch (token.type) {\n case Token.StringLiteral:\n this.trigger("String", {\n line: this.line,\n char: this.char,\n from: this.from,\n value: token.value,\n quote: token.quote\n });\n\n return create("(string)", token.value);\n case Token.Identifier:\n this.trigger("Identifier", {\n line: this.line,\n char: this.char,\n from: this.form,\n name: token.value,\n isProperty: state.tokens.curr.id === "."\n });\n\n /* falls through */\n case Token.Keyword:\n case Token.NullLiteral:\n case Token.BooleanLiteral:\n return create("(identifier)", token.value, state.tokens.curr.id === ".");\n\n case Token.NumericLiteral:\n if (token.isMalformed) {\n this.trigger("warning", {\n code: "W045",\n line: this.line,\n character: this.char,\n data: [ token.value ]\n });\n }\n\n if (state.jsonMode && token.base === 16) {\n this.trigger("warning", {\n code: "W114",\n line: this.line,\n character: this.char,\n data: [ "0x-" ]\n });\n }\n\n if (state.directive["use strict"] && token.base === 8) {\n this.trigger("warning", {\n code: "W115",\n line: this.line,\n character: this.char\n });\n }\n\n this.trigger("Number", {\n line: this.line,\n char: this.char,\n from: this.from,\n value: token.value,\n base: token.base,\n isMalformed: token.malformed\n });\n\n return create("(number)", token.value);\n\n case Token.RegExp:\n return create("(regexp)", token.value);\n\n case Token.Comment:\n state.tokens.curr.comment = true;\n\n if (token.isSpecial) {\n return {\n value: token.value,\n body: token.body,\n type: token.commentType,\n isSpecial: token.isSpecial,\n line: this.line,\n character: this.char,\n from: this.from\n };\n }\n\n break;\n\n case "":\n break;\n\n default:\n return create("(punctuator)", token.value);\n }\n }\n }\n};\n\nexports.Lexer = Lexer;\n//@ sourceURL=/src/stable/lex.js')); -require.define("/src/stable/reg.js",Function(["require","module","exports","__dirname","__filename","process","global"],'/*\n * Regular expressions. Some of these are stupidly long.\n */\n\n/*jshint maxlen:1000 */\n\n"use string";\n\n// Unsafe comment or string (ax)\nexports.unsafeString =\n /@cc|<\\/?|script|\\]\\s*\\]|<\\s*!|</i;\n\n// Unsafe characters that are silently deleted by one or more browsers (cx)\nexports.unsafeChars =\n /[\\u0000-\\u001f\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/;\n\n// Characters in strings that need escaping (nx and nxg)\nexports.needEsc =\n /[\\u0000-\\u001f&<"\\/\\\\\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/;\n\nexports.needEscGlobal =\n /[\\u0000-\\u001f&<"\\/\\\\\\u007f-\\u009f\\u00ad\\u0600-\\u0604\\u070f\\u17b4\\u17b5\\u200c-\\u200f\\u2028-\\u202f\\u2060-\\u206f\\ufeff\\ufff0-\\uffff]/g;\n\n// Star slash (lx)\nexports.starSlash = /\\*\\//;\n\n// Identifier (ix)\nexports.identifier = /^([a-zA-Z_$][a-zA-Z0-9_$]*)$/;\n\n// JavaScript URL (jx)\nexports.javascriptURL = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\\s*:/i;\n\n// Catches /* falls through */ comments (ft)\nexports.fallsThrough = /^\\s*\\/\\*\\s*falls\\sthrough\\s*\\*\\/\\s*$/;\n//@ sourceURL=/src/stable/reg.js')); -require.define("/src/stable/state.js",Function(["require","module","exports","__dirname","__filename","process","global"],'"use strict";\n\nvar state = {\n syntax: {},\n\n reset: function () {\n this.tokens = {\n prev: null,\n next: null,\n curr: null\n },\n\n this.option = {};\n this.directive = {};\n this.jsonMode = false;\n this.lines = [];\n this.tab = "";\n this.cache = {}; // Node.JS doesn\'t have Map. Sniff.\n }\n};\n\nexports.state = state;\n//@ sourceURL=/src/stable/state.js')); -require.define("/src/stable/style.js",Function(["require","module","exports","__dirname","__filename","process","global"],'"use strict";\n\nexports.register = function (linter) {\n // Check for properties named __proto__. This special property was\n // deprecated and then re-introduced for ES6.\n\n linter.on("Identifier", function style_scanProto(data) {\n if (linter.getOption("proto")) {\n return;\n }\n\n if (data.name === "__proto__") {\n linter.warn("W103", {\n line: data.line,\n char: data.char,\n data: [ data.name ]\n });\n }\n });\n\n // Check for properties named __iterator__. This is a special property\n // available only in browsers with JavaScript 1.7 implementation.\n\n linter.on("Identifier", function style_scanIterator(data) {\n if (linter.getOption("iterator")) {\n return;\n }\n\n if (data.name === "__iterator__") {\n linter.warn("W104", {\n line: data.line,\n char: data.char,\n data: [ data.name ]\n });\n }\n });\n\n // Check for dangling underscores.\n\n linter.on("Identifier", function style_scanDangling(data) {\n if (!linter.getOption("nomen")) {\n return;\n }\n\n // Underscore.js\n if (data.name === "_") {\n return;\n }\n\n // In Node, __dirname and __filename should be ignored.\n if (linter.getOption("node")) {\n if (/^(__dirname|__filename)$/.test(data.name) && !data.isProperty) {\n return;\n }\n }\n\n if (/^(_+.*|.*_+)$/.test(data.name)) {\n linter.warn("W105", {\n line: data.line,\n char: data.from,\n data: [ "dangling \'_\'", data.name ]\n });\n }\n });\n\n // Check that all identifiers are using camelCase notation.\n // Exceptions: names like MY_VAR and _myVar.\n\n linter.on("Identifier", function style_scanCamelCase(data) {\n if (!linter.getOption("camelcase")) {\n return;\n }\n\n if (data.name.replace(/^_+/, "").indexOf("_") > -1 && !data.name.match(/^[A-Z0-9_]*$/)) {\n linter.warn("W106", {\n line: data.line,\n char: data.from,\n data: [ data.name ]\n });\n }\n });\n\n // Enforce consistency in style of quoting.\n\n linter.on("String", function style_scanQuotes(data) {\n var quotmark = linter.getOption("quotmark");\n var code;\n\n if (!quotmark) {\n return;\n }\n\n // If quotmark is set to \'single\' warn about all double-quotes.\n\n if (quotmark === "single" && data.quote !== "\'") {\n code = "W109";\n }\n\n // If quotmark is set to \'double\' warn about all single-quotes.\n\n if (quotmark === "double" && data.quote !== "\\"") {\n code = "W108";\n }\n\n // If quotmark is set to true, remember the first quotation style\n // and then warn about all others.\n\n if (quotmark === true) {\n if (!linter.getCache("quotmark")) {\n linter.setCache("quotmark", data.quote);\n }\n\n if (linter.getCache("quotmark") !== data.quote) {\n code = "W110";\n }\n }\n\n if (code) {\n linter.warn(code, {\n line: data.line,\n char: data.char,\n });\n }\n });\n\n linter.on("Number", function style_scanNumbers(data) {\n if (data.value.charAt(0) === ".") {\n // Warn about a leading decimal point.\n linter.warn("W008", {\n line: data.line,\n char: data.char,\n data: [ data.value ]\n });\n }\n\n if (data.value.substr(data.value.length - 1) === ".") {\n // Warn about a trailing decimal point.\n linter.warn("W047", {\n line: data.line,\n char: data.char,\n data: [ data.value ]\n });\n }\n\n if (/^00+/.test(data.value)) {\n // Multiple leading zeroes.\n linter.warn("W046", {\n line: data.line,\n char: data.char,\n data: [ data.value ]\n });\n }\n });\n\n // Warn about script URLs.\n\n linter.on("String", function style_scanJavaScriptURLs(data) {\n var re = /^(?:javascript|jscript|ecmascript|vbscript|mocha|livescript)\\s*:/i;\n\n if (linter.getOption("scripturl")) {\n return;\n }\n\n if (re.test(data.value)) {\n linter.warn("W107", {\n line: data.line,\n char: data.char\n });\n }\n });\n};\n//@ sourceURL=/src/stable/style.js')); -require.define("/src/stable/jshint.js",Function(["require","module","exports","__dirname","__filename","process","global"],'/*!\n * JSHint, by JSHint Community.\n *\n * This file (and this file only) is licensed under the same slightly modified\n * MIT license that JSLint is. It stops evil-doers everywhere:\n *\n * Copyright (c) 2002 Douglas Crockford (www.JSLint.com)\n *\n * Permission is hereby granted, free of charge, to any person obtaining\n * a copy of this software and associated documentation files (the "Software"),\n * to deal in the Software without restriction, including without limitation\n * the rights to use, copy, modify, merge, publish, distribute, sublicense,\n * and/or sell copies of the Software, and to permit persons to whom\n * the Software is furnished to do so, subject to the following conditions:\n *\n * The above copyright notice and this permission notice shall be included\n * in all copies or substantial portions of the Software.\n *\n * The Software shall be used for Good, not Evil.\n *\n * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING\n * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER\n * DEALINGS IN THE SOFTWARE.\n *\n */\n\n/*jshint quotmark:double */\n\nvar _ = require("underscore");\nvar events = require("events");\nvar vars = require("../shared/vars.js");\nvar messages = require("../shared/messages.js");\nvar Lexer = require("./lex.js").Lexer;\nvar reg = require("./reg.js");\nvar state = require("./state.js").state;\nvar style = require("./style.js");\n\n// We build the application inside a function so that we produce only a single\n// global variable. That function will be invoked immediately, and its return\n// value is the JSHINT function itself.\n\nvar JSHINT = (function () {\n "use strict";\n\n var anonname, // The guessed name for anonymous functions.\n\n// These are operators that should not be used with the ! operator.\n\n bang = {\n "<" : true,\n "<=" : true,\n "==" : true,\n "===": true,\n "!==": true,\n "!=" : true,\n ">" : true,\n ">=" : true,\n "+" : true,\n "-" : true,\n "*" : true,\n "/" : true,\n "%" : true\n },\n\n // These are the JSHint boolean options.\n boolOptions = {\n asi : true, // if automatic semicolon insertion should be tolerated\n bitwise : true, // if bitwise operators should not be allowed\n boss : true, // if advanced usage of assignments should be allowed\n browser : true, // if the standard browser globals should be predefined\n camelcase : true, // if identifiers should be required in camel case\n couch : true, // if CouchDB globals should be predefined\n curly : true, // if curly braces around all blocks should be required\n debug : true, // if debugger statements should be allowed\n devel : true, // if logging globals should be predefined (console, alert, etc.)\n dojo : true, // if Dojo Toolkit globals should be predefined\n eqeqeq : true, // if === should be required\n eqnull : true, // if == null comparisons should be tolerated\n es5 : true, // if ES5 syntax should be allowed\n esnext : true, // if es.next specific syntax should be allowed\n evil : true, // if eval should be allowed\n expr : true, // if ExpressionStatement should be allowed as Programs\n forin : true, // if for in statements must filter\n funcscope : true, // if only function scope should be used for scope tests\n gcl : true, // if JSHint should be compatible with Google Closure Linter\n globalstrict: true, // if global "use strict"; should be allowed (also enables \'strict\')\n immed : true, // if immediate invocations must be wrapped in parens\n iterator : true, // if the `__iterator__` property should be allowed\n jquery : true, // if jQuery globals should be predefined\n lastsemic : true, // if semicolons may be ommitted for the trailing\n // statements inside of a one-line blocks.\n latedef : true, // if the use before definition should not be tolerated\n laxbreak : true, // if line breaks should not be checked\n laxcomma : true, // if line breaks should not be checked around commas\n loopfunc : true, // if functions should be allowed to be defined within\n // loops\n mootools : true, // if MooTools globals should be predefined\n multistr : true, // allow multiline strings\n newcap : true, // if constructor names must be capitalized\n noarg : true, // if arguments.caller and arguments.callee should be\n // disallowed\n node : true, // if the Node.js environment globals should be\n // predefined\n noempty : true, // if empty blocks should be disallowed\n nonew : true, // if using `new` for side-effects should be disallowed\n nonstandard : true, // if non-standard (but widely adopted) globals should\n // be predefined\n nomen : true, // if names should be checked\n onevar : true, // if only one var statement per function should be\n // allowed\n passfail : true, // if the scan should stop on first error\n phantom : true, // if PhantomJS symbols should be allowed\n plusplus : true, // if increment/decrement should not be allowed\n proto : true, // if the `__proto__` property should be allowed\n prototypejs : true, // if Prototype and Scriptaculous globals should be\n // predefined\n rhino : true, // if the Rhino environment globals should be predefined\n undef : true, // if variables should be declared before used\n scripturl : true, // if script-targeted URLs should be tolerated\n shadow : true, // if variable shadowing should be tolerated\n smarttabs : true, // if smarttabs should be tolerated\n // (http://www.emacswiki.org/emacs/SmartTabs)\n strict : true, // require the "use strict"; pragma\n sub : true, // if all forms of subscript notation are tolerated\n supernew : true, // if `new function () { ... };` and `new Object;`\n // should be tolerated\n trailing : true, // if trailing whitespace rules apply\n validthis : true, // if \'this\' inside a non-constructor function is valid.\n // This is a function scoped option only.\n withstmt : true, // if with statements should be allowed\n white : true, // if strict whitespace rules apply\n worker : true, // if Web Worker script symbols should be allowed\n wsh : true, // if the Windows Scripting Host environment globals\n // should be predefined\n yui : true, // YUI variables should be predefined\n\n // Obsolete options\n onecase : true, // if one case switch statements should be allowed\n regexp : true, // if the . should not be allowed in regexp literals\n regexdash : true // if unescaped first/last dash (-) inside brackets\n // should be tolerated\n },\n\n // These are the JSHint options that can take any value\n // (we use this object to detect invalid options)\n valOptions = {\n maxlen : false,\n indent : false,\n maxerr : false,\n predef : false,\n quotmark : false, //\'single\'|\'double\'|true\n scope : false,\n maxstatements: false, // {int} max statements per function\n maxdepth : false, // {int} max nested block depth per function\n maxparams : false, // {int} max params per function\n maxcomplexity: false, // {int} max cyclomatic complexity per function\n unused : true // warn if variables are unused. Available options:\n // false - don\'t check for unused variables\n // true - "vars" + check last function param\n // "vars" - skip checking unused function params\n // "strict" - "vars" + check all function params\n },\n\n // These are JSHint boolean options which are shared with JSLint\n // where the definition in JSHint is opposite JSLint\n invertedOptions = {\n bitwise : true,\n forin : true,\n newcap : true,\n nomen : true,\n plusplus: true,\n regexp : true,\n undef : true,\n white : true,\n\n // Inverted and renamed, use JSHint name here\n eqeqeq : true,\n onevar : true\n },\n\n // These are JSHint boolean options which are shared with JSLint\n // where the name has been changed but the effect is unchanged\n renamedOptions = {\n eqeq : "eqeqeq",\n vars : "onevar",\n windows: "wsh"\n },\n\n declared, // Globals that were declared using /*global ... */ syntax.\n exported, // Variables that are used outside of the current file.\n\n functionicity = [\n "closure", "exception", "global", "label",\n "outer", "unused", "var"\n ],\n\n funct, // The current function\n functions, // All of the functions\n\n global, // The global scope\n ignored, // Ignored warnings\n implied, // Implied globals\n inblock,\n indent,\n lookahead,\n lex,\n member,\n membersOnly,\n noreach,\n predefined, // Global variables defined by option\n\n scope, // The current scope\n stack,\n unuseds,\n urls,\n useESNextSyntax,\n warnings,\n\n extraModules = [],\n emitter = new events.EventEmitter();\n\n function checkOption(name, t) {\n name = name.trim();\n\n if (/^-W\\d{3}$/g.test(name)) {\n return true;\n }\n\n if (valOptions[name] === undefined && boolOptions[name] === undefined) {\n if (t.type !== "jslint" || renamedOptions[name] === undefined) {\n error("E001", t, name);\n return false;\n }\n }\n\n return true;\n }\n\n function isString(obj) {\n return Object.prototype.toString.call(obj) === "[object String]";\n }\n\n function isIdentifier(tkn, value) {\n if (!tkn)\n return false;\n\n if (!tkn.identifier || tkn.value !== value)\n return false;\n\n return true;\n }\n\n function isReserved(token) {\n if (!token.reserved) {\n return false;\n }\n\n if (token.meta && token.meta.isFutureReservedWord) {\n // ES3 FutureReservedWord in an ES5 environment.\n if (state.option.es5 && !token.meta.es5) {\n return false;\n }\n\n // Some ES5 FutureReservedWord identifiers are active only\n // within a strict mode environment.\n if (token.meta.strictOnly) {\n if (!state.option.strict && !state.directive["use strict"]) {\n return false;\n }\n }\n\n if (token.isProperty) {\n return false;\n }\n }\n\n return true;\n }\n\n function supplant(str, data) {\n return str.replace(/\\{([^{}]*)\\}/g, function (a, b) {\n var r = data[b];\n return typeof r === "string" || typeof r === "number" ? r : a;\n });\n }\n\n function combine(t, o) {\n var n;\n for (n in o) {\n if (_.has(o, n) && !_.has(JSHINT.blacklist, n)) {\n t[n] = o[n];\n }\n }\n }\n\n function updatePredefined() {\n Object.keys(JSHINT.blacklist).forEach(function (key) {\n delete predefined[key];\n });\n }\n\n function assume() {\n if (state.option.couch) {\n combine(predefined, vars.couch);\n }\n\n if (state.option.rhino) {\n combine(predefined, vars.rhino);\n }\n\n if (state.option.phantom) {\n combine(predefined, vars.phantom);\n }\n\n if (state.option.prototypejs) {\n combine(predefined, vars.prototypejs);\n }\n\n if (state.option.node) {\n combine(predefined, vars.node);\n }\n\n if (state.option.devel) {\n combine(predefined, vars.devel);\n }\n\n if (state.option.dojo) {\n combine(predefined, vars.dojo);\n }\n\n if (state.option.browser) {\n combine(predefined, vars.browser);\n }\n\n if (state.option.nonstandard) {\n combine(predefined, vars.nonstandard);\n }\n\n if (state.option.jquery) {\n combine(predefined, vars.jquery);\n }\n\n if (state.option.mootools) {\n combine(predefined, vars.mootools);\n }\n\n if (state.option.worker) {\n combine(predefined, vars.worker);\n }\n\n if (state.option.wsh) {\n combine(predefined, vars.wsh);\n }\n\n if (state.option.esnext) {\n useESNextSyntax();\n }\n\n if (state.option.globalstrict && state.option.strict !== false) {\n state.option.strict = true;\n }\n\n if (state.option.yui) {\n combine(predefined, vars.yui);\n }\n }\n\n\n // Produce an error warning.\n function quit(code, line, chr) {\n var percentage = Math.floor((line / state.lines.length) * 100);\n var message = messages.errors[code].desc;\n\n throw {\n name: "JSHintError",\n line: line,\n character: chr,\n message: message + " (" + percentage + "% scanned).",\n raw: message\n };\n }\n\n function isundef(scope, code, token, a) {\n return JSHINT.undefs.push([scope, code, token, a]);\n }\n\n function warning(code, t, a, b, c, d) {\n var ch, l, w, msg;\n\n if (/^W\\d{3}$/.test(code)) {\n if (ignored[code]) {\n return;\n }\n\n msg = messages.warnings[code];\n } else if (/E\\d{3}/.test(code)) {\n msg = messages.errors[code];\n } else if (/I\\d{3}/.test(code)) {\n msg = messages.info[code];\n }\n\n t = t || state.tokens.next;\n if (t.id === "(end)") { // `~\n t = state.tokens.curr;\n }\n\n l = t.line || 0;\n ch = t.from || 0;\n\n w = {\n id: "(error)",\n raw: msg.desc,\n code: msg.code,\n evidence: state.lines[l - 1] || "",\n line: l,\n character: ch,\n scope: JSHINT.scope,\n a: a,\n b: b,\n c: c,\n d: d\n };\n\n w.reason = supplant(msg.desc, w);\n JSHINT.errors.push(w);\n\n if (state.option.passfail) {\n quit("E042", l, ch);\n }\n\n warnings += 1;\n if (warnings >= state.option.maxerr) {\n quit("E043", l, ch);\n }\n\n return w;\n }\n\n function warningAt(m, l, ch, a, b, c, d) {\n return warning(m, {\n line: l,\n from: ch\n }, a, b, c, d);\n }\n\n function error(m, t, a, b, c, d) {\n warning(m, t, a, b, c, d);\n }\n\n function errorAt(m, l, ch, a, b, c, d) {\n return error(m, {\n line: l,\n from: ch\n }, a, b, c, d);\n }\n\n // Tracking of "internal" scripts, like eval containing a static string\n function addInternalSrc(elem, src) {\n var i;\n i = {\n id: "(internal)",\n elem: elem,\n value: src\n };\n JSHINT.internals.push(i);\n return i;\n }\n\n function addlabel(t, type, tkn) {\n // Define t in the current function in the current scope.\n if (type === "exception") {\n if (_.has(funct["(context)"], t)) {\n if (funct[t] !== true && !state.option.node) {\n warning("W002", state.tokens.next, t);\n }\n }\n }\n\n if (_.has(funct, t) && !funct["(global)"]) {\n if (funct[t] === true) {\n if (state.option.latedef)\n warning("W003", state.tokens.next, t);\n } else {\n if (!state.option.shadow && type !== "exception") {\n warning("W004", state.tokens.next, t);\n }\n }\n }\n\n funct[t] = type;\n\n if (tkn) {\n funct["(tokens)"][t] = tkn;\n }\n\n if (funct["(global)"]) {\n global[t] = funct;\n if (_.has(implied, t)) {\n if (state.option.latedef) {\n warning("W003", state.tokens.next, t);\n }\n\n delete implied[t];\n }\n } else {\n scope[t] = funct;\n }\n }\n\n function doOption() {\n var nt = state.tokens.next;\n var body = nt.body.split(",").map(function (s) { return s.trim(); });\n var predef = {};\n\n if (nt.type === "globals") {\n body.forEach(function (g) {\n g = g.split(":");\n var key = g[0];\n var val = g[1];\n\n if (key.charAt(0) === "-") {\n key = key.slice(1);\n val = false;\n\n JSHINT.blacklist[key] = key;\n updatePredefined();\n } else {\n predef[key] = (val === "true");\n }\n });\n\n combine(predefined, predef);\n\n for (var key in predef) {\n if (_.has(predef, key)) {\n declared[key] = nt;\n }\n }\n }\n\n if (nt.type === "exported") {\n body.forEach(function (e) {\n exported[e] = true;\n });\n }\n\n if (nt.type === "members") {\n membersOnly = membersOnly || {};\n\n body.forEach(function (m) {\n var ch1 = m.charAt(0);\n var ch2 = m.charAt(m.length - 1);\n\n if (ch1 === ch2 && (ch1 === "\\"" || ch1 === "\'")) {\n m = m\n .substr(1, m.length - 2)\n .replace("\\\\b", "\\b")\n .replace("\\\\t", "\\t")\n .replace("\\\\n", "\\n")\n .replace("\\\\v", "\\v")\n .replace("\\\\f", "\\f")\n .replace("\\\\r", "\\r")\n .replace("\\\\\\\\", "\\\\")\n .replace("\\\\\\"", "\\"");\n }\n\n membersOnly[m] = false;\n });\n }\n\n var numvals = [\n "maxstatements",\n "maxparams",\n "maxdepth",\n "maxcomplexity",\n "maxerr",\n "maxlen",\n "indent"\n ];\n\n if (nt.type === "jshint" || nt.type === "jslint") {\n body.forEach(function (g) {\n g = g.split(":");\n var key = (g[0] || "").trim();\n var val = (g[1] || "").trim();\n\n if (!checkOption(key, nt)) {\n return;\n }\n\n if (numvals.indexOf(key) >= 0) {\n val = +val;\n\n if (typeof val !== "number" || !isFinite(val) || val <= 0 || Math.floor(val) !== val) {\n error("E032", nt, g[1].trim());\n return;\n }\n\n if (key === "indent") {\n state.option["(explicitIndent)"] = true;\n }\n\n state.option[key] = val;\n return;\n }\n\n if (key === "validthis") {\n // `validthis` is valid only within a function scope.\n if (funct["(global)"]) {\n error("E009");\n } else {\n if (val === "true" || val === "false") {\n state.option.validthis = (val === "true");\n } else {\n error("E002", nt);\n }\n }\n return;\n }\n\n if (key === "quotmark") {\n switch (val) {\n case "true":\n case "false":\n state.option.quotmark = (val === "true");\n break;\n case "double":\n case "single":\n state.option.quotmark = val;\n break;\n default:\n error("E002", nt);\n }\n return;\n }\n\n if (key === "unused") {\n switch (val) {\n case "true":\n state.option.unused = true;\n break;\n case "false":\n state.option.unused = false;\n break;\n case "vars":\n case "strict":\n state.option.unused = val;\n break;\n default:\n error("E002", nt);\n }\n return;\n }\n\n if (/^-W\\d{3}$/g.test(key)) {\n ignored[key.slice(1)] = true;\n return;\n }\n\n var tn;\n if (val === "true" || val === "false") {\n if (nt.type === "jslint") {\n tn = renamedOptions[key] || key;\n state.option[tn] = (val === "true");\n\n if (invertedOptions[tn] !== undefined) {\n state.option[tn] = !state.option[tn];\n }\n } else {\n state.option[key] = (val === "true");\n }\n\n if (key === "newcap") {\n state.option["(explicitNewcap)"] = true;\n }\n return;\n }\n\n error("E002", nt);\n });\n\n assume();\n }\n }\n\n // We need a peek function. If it has an argument, it peeks that much farther\n // ahead. It is used to distinguish\n // for ( var i in ...\n // from\n // for ( var i = ...\n\n function peek(p) {\n var i = p || 0, j = 0, t;\n\n while (j <= i) {\n t = lookahead[j];\n if (!t) {\n t = lookahead[j] = lex.token();\n }\n j += 1;\n }\n return t;\n }\n\n // Produce the next token. It looks for programming errors.\n\n function advance(id, t) {\n switch (state.tokens.curr.id) {\n case "(number)":\n if (state.tokens.next.id === ".") {\n warning("W005", state.tokens.curr);\n }\n break;\n case "-":\n if (state.tokens.next.id === "-" || state.tokens.next.id === "--") {\n warning("W006");\n }\n break;\n case "+":\n if (state.tokens.next.id === "+" || state.tokens.next.id === "++") {\n warning("W007");\n }\n break;\n }\n\n if (state.tokens.curr.type === "(string)" || state.tokens.curr.identifier) {\n anonname = state.tokens.curr.value;\n }\n\n if (id && state.tokens.next.id !== id) {\n if (t) {\n if (state.tokens.next.id === "(end)") {\n error("E019", t, t.id);\n } else {\n error("E020", state.tokens.next, id, t.id, t.line, state.tokens.next.value);\n }\n } else if (state.tokens.next.type !== "(identifier)" || state.tokens.next.value !== id) {\n warning("W116", state.tokens.next, id, state.tokens.next.value);\n }\n }\n\n state.tokens.prev = state.tokens.curr;\n state.tokens.curr = state.tokens.next;\n for (;;) {\n state.tokens.next = lookahead.shift() || lex.token();\n\n if (!state.tokens.next) { // No more tokens left, give up\n quit("E041", state.tokens.curr.line);\n }\n\n if (state.tokens.next.id === "(end)" || state.tokens.next.id === "(error)") {\n return;\n }\n\n if (state.tokens.next.isSpecial) {\n doOption();\n } else {\n if (state.tokens.next.id !== "(endline)") {\n break;\n }\n }\n }\n }\n\n\n // This is the heart of JSHINT, the Pratt parser. In addition to parsing, it\n // is looking for ad hoc lint patterns. We add .fud to Pratt\'s model, which is\n // like .nud except that it is only used on the first token of a statement.\n // Having .fud makes it much easier to define statement-oriented languages like\n // JavaScript. I retained Pratt\'s nomenclature.\n\n // .nud Null denotation\n // .fud First null denotation\n // .led Left denotation\n // lbp Left binding power\n // rbp Right binding power\n\n // They are elements of the parsing method called Top Down Operator Precedence.\n\n function expression(rbp, initial) {\n var left, isArray = false, isObject = false;\n\n if (state.tokens.next.id === "(end)")\n error("E006", state.tokens.curr);\n\n advance();\n\n if (initial) {\n anonname = "anonymous";\n funct["(verb)"] = state.tokens.curr.value;\n }\n\n if (initial === true && state.tokens.curr.fud) {\n left = state.tokens.curr.fud();\n } else {\n if (state.tokens.curr.nud) {\n left = state.tokens.curr.nud();\n } else {\n error("E030", state.tokens.curr, state.tokens.curr.id);\n }\n\n while (rbp < state.tokens.next.lbp) {\n isArray = state.tokens.curr.value === "Array";\n isObject = state.tokens.curr.value === "Object";\n\n // #527, new Foo.Array(), Foo.Array(), new Foo.Object(), Foo.Object()\n // Line breaks in IfStatement heads exist to satisfy the checkJSHint\n // "Line too long." error.\n if (left && (left.value || (left.first && left.first.value))) {\n // If the left.value is not "new", or the left.first.value is a "."\n // then safely assume that this is not "new Array()" and possibly\n // not "new Object()"...\n if (left.value !== "new" ||\n (left.first && left.first.value && left.first.value === ".")) {\n isArray = false;\n // ...In the case of Object, if the left.value and state.tokens.curr.value\n // are not equal, then safely assume that this not "new Object()"\n if (left.value !== state.tokens.curr.value) {\n isObject = false;\n }\n }\n }\n\n advance();\n\n if (isArray && state.tokens.curr.id === "(" && state.tokens.next.id === ")") {\n warning("W009", state.tokens.curr);\n }\n\n if (isObject && state.tokens.curr.id === "(" && state.tokens.next.id === ")") {\n warning("W010", state.tokens.curr);\n }\n\n if (state.tokens.curr.led) {\n left = state.tokens.curr.led(left);\n } else {\n error("E033", state.tokens.curr, state.tokens.curr.id);\n }\n }\n }\n return left;\n }\n\n\n// Functions for conformance of style.\n\n function adjacent(left, right) {\n left = left || state.tokens.curr;\n right = right || state.tokens.next;\n if (state.option.white) {\n if (left.character !== right.from && left.line === right.line) {\n left.from += (left.character - left.from);\n warning("W011", left, left.value);\n }\n }\n }\n\n function nobreak(left, right) {\n left = left || state.tokens.curr;\n right = right || state.tokens.next;\n if (state.option.white && (left.character !== right.from || left.line !== right.line)) {\n warning("W012", right, right.value);\n }\n }\n\n function nospace(left, right) {\n left = left || state.tokens.curr;\n right = right || state.tokens.next;\n if (state.option.white && !left.comment) {\n if (left.line === right.line) {\n adjacent(left, right);\n }\n }\n }\n\n function nonadjacent(left, right) {\n if (state.option.white) {\n left = left || state.tokens.curr;\n right = right || state.tokens.next;\n\n if (left.value === ";" && right.value === ";") {\n return;\n }\n\n if (left.line === right.line && left.character === right.from) {\n left.from += (left.character - left.from);\n warning("W013", left, left.value);\n }\n }\n }\n\n function nobreaknonadjacent(left, right) {\n left = left || state.tokens.curr;\n right = right || state.tokens.next;\n if (!state.option.laxbreak && left.line !== right.line) {\n warning("W014", right, right.id);\n } else if (state.option.white) {\n left = left || state.tokens.curr;\n right = right || state.tokens.next;\n if (left.character === right.from) {\n left.from += (left.character - left.from);\n warning("W013", left, left.value);\n }\n }\n }\n\n function indentation(bias) {\n if (!state.option.white && !state.option["(explicitIndent)"]) {\n return;\n }\n\n if (state.tokens.next.id === "(end)") {\n return;\n }\n\n var i = indent + (bias || 0);\n if (state.tokens.next.from !== i) {\n warning("W015", state.tokens.next, state.tokens.next.value, i, state.tokens.next.from);\n }\n }\n\n function nolinebreak(t) {\n t = t || state.tokens.curr;\n if (t.line !== state.tokens.next.line) {\n warning("E022", t, t.value);\n }\n }\n\n\n function comma(opts) {\n opts = opts || {};\n\n if (state.tokens.curr.line !== state.tokens.next.line) {\n if (!state.option.laxcomma) {\n if (comma.first) {\n warning("I001");\n comma.first = false;\n }\n warning("W014", state.tokens.curr, state.tokens.next.id);\n }\n } else if (!state.tokens.curr.comment &&\n state.tokens.curr.character !== state.tokens.next.from && state.option.white) {\n state.tokens.curr.from += (state.tokens.curr.character - state.tokens.curr.from);\n warning("W011", state.tokens.curr, state.tokens.curr.value);\n }\n\n advance(",");\n\n // TODO: This is a temporary solution to fight against false-positives in\n // arrays and objects with trailing commas (see GH-363). The best solution\n // would be to extract all whitespace rules out of parser.\n\n if (state.tokens.next.value !== "]" && state.tokens.next.value !== "}") {\n nonadjacent(state.tokens.curr, state.tokens.next);\n }\n\n if (state.tokens.next.identifier) {\n // Keywords that cannot follow a comma operator.\n switch (state.tokens.next.value) {\n case "break":\n case "case":\n case "catch":\n case "continue":\n case "default":\n case "do":\n case "else":\n case "finally":\n case "for":\n case "if":\n case "in":\n case "instanceof":\n case "return":\n case "switch":\n case "throw":\n case "try":\n case "var":\n case "while":\n case "with":\n error("E024", state.tokens.next, state.tokens.next.value);\n return;\n }\n }\n\n if (state.tokens.next.type === "(punctuator)") {\n switch (state.tokens.next.value) {\n case "}":\n case "]":\n case ",":\n if (opts.allowTrailing) {\n return;\n }\n\n /* falls through */\n case ")":\n error("E024", state.tokens.next, state.tokens.next.value);\n }\n }\n }\n\n // Functional constructors for making the symbols that will be inherited by\n // tokens.\n\n function symbol(s, p) {\n var x = state.syntax[s];\n if (!x || typeof x !== "object") {\n state.syntax[s] = x = {\n id: s,\n lbp: p,\n value: s\n };\n }\n return x;\n }\n\n function delim(s) {\n return symbol(s, 0);\n }\n\n function stmt(s, f) {\n var x = delim(s);\n x.identifier = x.reserved = true;\n x.fud = f;\n return x;\n }\n\n function blockstmt(s, f) {\n var x = stmt(s, f);\n x.block = true;\n return x;\n }\n\n function reserveName(x) {\n var c = x.id.charAt(0);\n if ((c >= "a" && c <= "z") || (c >= "A" && c <= "Z")) {\n x.identifier = x.reserved = true;\n }\n return x;\n }\n\n function prefix(s, f) {\n var x = symbol(s, 150);\n reserveName(x);\n x.nud = (typeof f === "function") ? f : function () {\n this.right = expression(150);\n this.arity = "unary";\n if (this.id === "++" || this.id === "--") {\n if (state.option.plusplus) {\n warning("W016", this, this.id);\n } else if ((!this.right.identifier || isReserved(this.right)) &&\n this.right.id !== "." && this.right.id !== "[") {\n warning("W017", this);\n }\n }\n return this;\n };\n return x;\n }\n\n function type(s, f) {\n var x = delim(s);\n x.type = s;\n x.nud = f;\n return x;\n }\n\n function reserve(name, func) {\n var x = type(name, func);\n x.identifier = true;\n x.reserved = true;\n return x;\n }\n\n function FutureReservedWord(name, meta) {\n var x = type(name, function () {\n return this;\n });\n\n meta = meta || {};\n meta.isFutureReservedWord = true;\n\n x.value = name;\n x.identifier = true;\n x.reserved = true;\n x.meta = meta;\n\n return x;\n }\n\n function reservevar(s, v) {\n return reserve(s, function () {\n if (typeof v === "function") {\n v(this);\n }\n return this;\n });\n }\n\n function infix(s, f, p, w) {\n var x = symbol(s, p);\n reserveName(x);\n x.led = function (left) {\n if (!w) {\n nobreaknonadjacent(state.tokens.prev, state.tokens.curr);\n nonadjacent(state.tokens.curr, state.tokens.next);\n }\n if (s === "in" && left.id === "!") {\n warning("W018", left, "!");\n }\n if (typeof f === "function") {\n return f(left, this);\n } else {\n this.left = left;\n this.right = expression(p);\n return this;\n }\n };\n return x;\n }\n\n function relation(s, f) {\n var x = symbol(s, 100);\n\n x.led = function (left) {\n nobreaknonadjacent(state.tokens.prev, state.tokens.curr);\n nonadjacent(state.tokens.curr, state.tokens.next);\n var right = expression(100);\n\n if (isIdentifier(left, "NaN") || isIdentifier(right, "NaN")) {\n warning("W019", this);\n } else if (f) {\n f.apply(this, [left, right]);\n }\n\n if (!left || !right) {\n quit("E041", state.tokens.curr.line);\n }\n\n if (left.id === "!") {\n warning("W018", left, "!");\n }\n\n if (right.id === "!") {\n warning("W018", right, "!");\n }\n\n this.left = left;\n this.right = right;\n return this;\n };\n return x;\n }\n\n function isPoorRelation(node) {\n return node &&\n ((node.type === "(number)" && +node.value === 0) ||\n (node.type === "(string)" && node.value === "") ||\n (node.type === "null" && !state.option.eqnull) ||\n node.type === "true" ||\n node.type === "false" ||\n node.type === "undefined");\n }\n\n function assignop(s) {\n symbol(s, 20).exps = true;\n\n return infix(s, function (left, that) {\n that.left = left;\n\n if (predefined[left.value] === false &&\n scope[left.value]["(global)"] === true) {\n warning("W020", left);\n } else if (left["function"]) {\n warning("W021", left, left.value);\n }\n\n if (left) {\n if (state.option.esnext && funct[left.value] === "const") {\n error("E013", left, left.value);\n }\n\n if (left.id === "." || left.id === "[") {\n if (!left.left || left.left.value === "arguments") {\n warning("E031", that);\n }\n that.right = expression(19);\n return that;\n } else if (left.identifier && !isReserved(left)) {\n if (funct[left.value] === "exception") {\n warning("W022", left);\n }\n that.right = expression(19);\n return that;\n }\n\n if (left === state.syntax["function"]) {\n warning("W023", state.tokens.curr);\n }\n }\n\n error("E031", that);\n }, 20);\n }\n\n\n function bitwise(s, f, p) {\n var x = symbol(s, p);\n reserveName(x);\n x.led = (typeof f === "function") ? f : function (left) {\n if (state.option.bitwise) {\n warning("W016", this, this.id);\n }\n this.left = left;\n this.right = expression(p);\n return this;\n };\n return x;\n }\n\n\n function bitwiseassignop(s) {\n symbol(s, 20).exps = true;\n return infix(s, function (left, that) {\n if (state.option.bitwise) {\n warning("W016", that, that.id);\n }\n nonadjacent(state.tokens.prev, state.tokens.curr);\n nonadjacent(state.tokens.curr, state.tokens.next);\n if (left) {\n if (left.id === "." || left.id === "[" ||\n (left.identifier && !isReserved(left))) {\n expression(19);\n return that;\n }\n if (left === state.syntax["function"]) {\n warning("W023", state.tokens.curr);\n }\n return that;\n }\n error("E031", that);\n }, 20);\n }\n\n\n function suffix(s) {\n var x = symbol(s, 150);\n\n x.led = function (left) {\n if (state.option.plusplus) {\n warning("W016", this, this.id);\n } else if ((!left.identifier || isReserved(left)) && left.id !== "." && left.id !== "[") {\n warning("W017", this);\n }\n\n this.left = left;\n return this;\n };\n return x;\n }\n\n // fnparam means that this identifier is being defined as a function\n // argument (see identifier())\n // prop means that this identifier is that of an object property\n\n function optionalidentifier(fnparam, prop) {\n if (!state.tokens.next.identifier) {\n return;\n }\n\n advance();\n\n var curr = state.tokens.curr;\n var meta = curr.meta || {};\n var val = state.tokens.curr.value;\n\n if (!isReserved(curr)) {\n return val;\n }\n\n if (prop) {\n if (state.option.es5 || meta.isFutureReservedWord) {\n return val;\n }\n }\n\n if (fnparam && val === "undefined") {\n return val;\n }\n\n warning("W024", state.tokens.curr, state.tokens.curr.id);\n return val;\n }\n\n // fnparam means that this identifier is being defined as a function\n // argument\n // prop means that this identifier is that of an object property\n function identifier(fnparam, prop) {\n var i = optionalidentifier(fnparam, prop);\n if (i) {\n return i;\n }\n if (state.tokens.curr.id === "function" && state.tokens.next.id === "(") {\n warning("W025");\n } else {\n error("E030", state.tokens.next, state.tokens.next.value);\n }\n }\n\n\n function reachable(s) {\n var i = 0, t;\n if (state.tokens.next.id !== ";" || noreach) {\n return;\n }\n for (;;) {\n t = peek(i);\n if (t.reach) {\n return;\n }\n if (t.id !== "(endline)") {\n if (t.id === "function") {\n if (!state.option.latedef) {\n break;\n }\n\n warning("W026", t);\n break;\n }\n\n warning("W027", t, t.value, s);\n break;\n }\n i += 1;\n }\n }\n\n\n function statement(noindent) {\n var i = indent, r, s = scope, t = state.tokens.next;\n\n if (t.id === ";") {\n advance(";");\n return;\n }\n\n // Is this a labelled statement?\n var res = isReserved(t);\n\n // We\'re being more tolerant here: if someone uses\n // a FutureReservedWord as a label, we warn but proceed\n // anyway.\n\n if (res && t.meta && t.meta.isFutureReservedWord) {\n warning("W024", t, t.id);\n res = false;\n }\n\n if (t.identifier && !res && peek().id === ":") {\n advance();\n advance(":");\n scope = Object.create(s);\n addlabel(t.value, "label");\n\n if (!state.tokens.next.labelled && state.tokens.next.value !== "{") {\n warning("W028", state.tokens.next, t.value, state.tokens.next.value);\n }\n\n if (reg.javascriptURL.test(t.value + ":")) {\n warning("W029", t, t.value);\n }\n\n state.tokens.next.label = t.value;\n t = state.tokens.next;\n }\n\n // Is it a lonely block?\n\n if (t.id === "{") {\n block(true, true);\n return;\n }\n\n // Parse the statement.\n\n if (!noindent) {\n indentation();\n }\n r = expression(0, true);\n\n // Look for the final semicolon.\n\n if (!t.block) {\n if (!state.option.expr && (!r || !r.exps)) {\n warning("W030", state.tokens.curr);\n } else if (state.option.nonew && r.id === "(" && r.left.id === "new") {\n warning("W031", t);\n }\n\n if (state.tokens.next.id === ",") {\n return comma();\n }\n\n if (state.tokens.next.id !== ";") {\n if (!state.option.asi) {\n // If this is the last statement in a block that ends on\n // the same line *and* option lastsemic is on, ignore the warning.\n // Otherwise, complain about missing semicolon.\n if (!state.option.lastsemic || state.tokens.next.id !== "}" ||\n state.tokens.next.line !== state.tokens.curr.line) {\n warningAt("W033", state.tokens.curr.line, state.tokens.curr.character);\n }\n }\n } else {\n adjacent(state.tokens.curr, state.tokens.next);\n advance(";");\n nonadjacent(state.tokens.curr, state.tokens.next);\n }\n }\n\n // Restore the indentation.\n\n indent = i;\n scope = s;\n return r;\n }\n\n\n function statements(startLine) {\n var a = [], p;\n\n while (!state.tokens.next.reach && state.tokens.next.id !== "(end)") {\n if (state.tokens.next.id === ";") {\n p = peek();\n\n if (!p || (p.id !== "(" && p.id !== "[")) {\n warning("W032");\n }\n\n advance(";");\n } else {\n a.push(statement(startLine === state.tokens.next.line));\n }\n }\n return a;\n }\n\n\n /*\n * read all directives\n * recognizes a simple form of asi, but always\n * warns, if it is used\n */\n function directives() {\n var i, p, pn;\n\n for (;;) {\n if (state.tokens.next.id === "(string)") {\n p = peek(0);\n if (p.id === "(endline)") {\n i = 1;\n do {\n pn = peek(i);\n i = i + 1;\n } while (pn.id === "(endline)");\n\n if (pn.id !== ";") {\n if (pn.id !== "(string)" && pn.id !== "(number)" &&\n pn.id !== "(regexp)" && pn.identifier !== true &&\n pn.id !== "}") {\n break;\n }\n warning("W033", state.tokens.next);\n } else {\n p = pn;\n }\n } else if (p.id === "}") {\n // Directive with no other statements, warn about missing semicolon\n warning("W033", p);\n } else if (p.id !== ";") {\n break;\n }\n\n indentation();\n advance();\n if (state.directive[state.tokens.curr.value]) {\n warning("W034", state.tokens.curr, state.tokens.curr.value);\n }\n\n if (state.tokens.curr.value === "use strict") {\n if (!state.option["(explicitNewcap)"])\n state.option.newcap = true;\n state.option.undef = true;\n }\n\n // there\'s no directive negation, so always set to true\n state.directive[state.tokens.curr.value] = true;\n\n if (p.id === ";") {\n advance(";");\n }\n continue;\n }\n break;\n }\n }\n\n\n /*\n * Parses a single block. A block is a sequence of statements wrapped in\n * braces.\n *\n * ordinary - true for everything but function bodies and try blocks.\n * stmt - true if block can be a single statement (e.g. in if/for/while).\n * isfunc - true if block is a function body\n */\n function block(ordinary, stmt, isfunc) {\n var a,\n b = inblock,\n old_indent = indent,\n m,\n s = scope,\n t,\n line,\n d;\n\n inblock = ordinary;\n\n if (!ordinary || !state.option.funcscope)\n scope = Object.create(scope);\n\n nonadjacent(state.tokens.curr, state.tokens.next);\n t = state.tokens.next;\n\n var metrics = funct["(metrics)"];\n metrics.nestedBlockDepth += 1;\n metrics.verifyMaxNestedBlockDepthPerFunction();\n\n if (state.tokens.next.id === "{") {\n advance("{");\n line = state.tokens.curr.line;\n if (state.tokens.next.id !== "}") {\n indent += state.option.indent;\n while (!ordinary && state.tokens.next.from > indent) {\n indent += state.option.indent;\n }\n\n if (isfunc) {\n m = {};\n for (d in state.directive) {\n if (_.has(state.directive, d)) {\n m[d] = state.directive[d];\n }\n }\n directives();\n\n if (state.option.strict && funct["(context)"]["(global)"]) {\n if (!m["use strict"] && !state.directive["use strict"]) {\n warning("E007");\n }\n }\n }\n\n a = statements(line);\n\n metrics.statementCount += a.length;\n\n if (isfunc) {\n state.directive = m;\n }\n\n indent -= state.option.indent;\n if (line !== state.tokens.next.line) {\n indentation();\n }\n } else if (line !== state.tokens.next.line) {\n indentation();\n }\n advance("}", t);\n indent = old_indent;\n } else if (!ordinary) {\n error("E021", state.tokens.next, "{", state.tokens.next.value);\n } else {\n if (!stmt || state.option.curly) {\n warning("W116", state.tokens.next, "{", state.tokens.next.value);\n }\n\n noreach = true;\n indent += state.option.indent;\n // test indentation only if statement is in new line\n a = [statement(state.tokens.next.line === state.tokens.curr.line)];\n indent -= state.option.indent;\n noreach = false;\n }\n funct["(verb)"] = null;\n if (!ordinary || !state.option.funcscope) scope = s;\n inblock = b;\n if (ordinary && state.option.noempty && (!a || a.length === 0)) {\n warning("W035");\n }\n metrics.nestedBlockDepth -= 1;\n return a;\n }\n\n\n function countMember(m) {\n if (membersOnly && typeof membersOnly[m] !== "boolean") {\n warning("W036", state.tokens.curr, m);\n }\n if (typeof member[m] === "number") {\n member[m] += 1;\n } else {\n member[m] = 1;\n }\n }\n\n\n function note_implied(tkn) {\n var name = tkn.value, line = tkn.line, a = implied[name];\n if (typeof a === "function") {\n a = false;\n }\n\n if (!a) {\n a = [line];\n implied[name] = a;\n } else if (a[a.length - 1] !== line) {\n a.push(line);\n }\n }\n\n\n // Build the syntax table by declaring the syntactic elements of the language.\n\n type("(number)", function () {\n return this;\n });\n\n type("(string)", function () {\n return this;\n });\n\n state.syntax["(identifier)"] = {\n type: "(identifier)",\n lbp: 0,\n identifier: true,\n nud: function () {\n var v = this.value,\n s = scope[v],\n f;\n\n if (typeof s === "function") {\n // Protection against accidental inheritance.\n s = undefined;\n } else if (typeof s === "boolean") {\n f = funct;\n funct = functions[0];\n addlabel(v, "var");\n s = funct;\n funct = f;\n }\n\n // The name is in scope and defined in the current function.\n if (funct === s) {\n // Change \'unused\' to \'var\', and reject labels.\n switch (funct[v]) {\n case "unused":\n funct[v] = "var";\n break;\n case "unction":\n funct[v] = "function";\n this["function"] = true;\n break;\n case "function":\n this["function"] = true;\n break;\n case "label":\n warning("W037", state.tokens.curr, v);\n break;\n }\n } else if (funct["(global)"]) {\n // The name is not defined in the function. If we are in the global\n // scope, then we have an undefined variable.\n //\n // Operators typeof and delete do not raise runtime errors even if\n // the base object of a reference is null so no need to display warning\n // if we\'re inside of typeof or delete.\n\n if (typeof predefined[v] !== "boolean") {\n // Attempting to subscript a null reference will throw an\n // error, even within the typeof and delete operators\n if (!(anonname === "typeof" || anonname === "delete") ||\n (state.tokens.next && (state.tokens.next.value === "." ||\n state.tokens.next.value === "["))) {\n\n isundef(funct, "W117", state.tokens.curr, v);\n }\n }\n\n note_implied(state.tokens.curr);\n } else {\n // If the name is already defined in the current\n // function, but not as outer, then there is a scope error.\n\n switch (funct[v]) {\n case "closure":\n case "function":\n case "var":\n case "unused":\n warning("W038", state.tokens.curr, v);\n break;\n case "label":\n warning("W037", state.tokens.curr, v);\n break;\n case "outer":\n case "global":\n break;\n default:\n // If the name is defined in an outer function, make an outer entry,\n // and if it was unused, make it var.\n if (s === true) {\n funct[v] = true;\n } else if (s === null) {\n warning("W039", state.tokens.curr, v);\n note_implied(state.tokens.curr);\n } else if (typeof s !== "object") {\n // Operators typeof and delete do not raise runtime errors even\n // if the base object of a reference is null so no need to\n //\n // display warning if we\'re inside of typeof or delete.\n // Attempting to subscript a null reference will throw an\n // error, even within the typeof and delete operators\n if (!(anonname === "typeof" || anonname === "delete") ||\n (state.tokens.next &&\n (state.tokens.next.value === "." || state.tokens.next.value === "["))) {\n\n isundef(funct, "W117", state.tokens.curr, v);\n }\n funct[v] = true;\n note_implied(state.tokens.curr);\n } else {\n switch (s[v]) {\n case "function":\n case "unction":\n this["function"] = true;\n s[v] = "closure";\n funct[v] = s["(global)"] ? "global" : "outer";\n break;\n case "var":\n case "unused":\n s[v] = "closure";\n funct[v] = s["(global)"] ? "global" : "outer";\n break;\n case "closure":\n funct[v] = s["(global)"] ? "global" : "outer";\n break;\n case "label":\n warning("W037", state.tokens.curr, v);\n }\n }\n }\n }\n return this;\n },\n led: function () {\n error("E033", state.tokens.next, state.tokens.next.value);\n }\n };\n\n type("(regexp)", function () {\n return this;\n });\n\n // ECMAScript parser\n\n delim("(endline)");\n delim("(begin)");\n delim("(end)").reach = true;\n delim("(error)").reach = true;\n delim("}").reach = true;\n delim(")");\n delim("]");\n delim("\\"").reach = true;\n delim("\'").reach = true;\n delim(";");\n delim(":").reach = true;\n delim(",");\n delim("#");\n\n reserve("else");\n reserve("case").reach = true;\n reserve("catch");\n reserve("default").reach = true;\n reserve("finally");\n reservevar("arguments", function (x) {\n if (state.directive["use strict"] && funct["(global)"]) {\n warning("E008", x);\n }\n });\n reservevar("eval");\n reservevar("false");\n reservevar("Infinity");\n reservevar("null");\n reservevar("this", function (x) {\n if (state.directive["use strict"] && !state.option.validthis && ((funct["(statement)"] &&\n funct["(name)"].charAt(0) > "Z") || funct["(global)"])) {\n warning("W040", x);\n }\n });\n reservevar("true");\n reservevar("undefined");\n\n assignop("=", "assign", 20);\n assignop("+=", "assignadd", 20);\n assignop("-=", "assignsub", 20);\n assignop("*=", "assignmult", 20);\n assignop("/=", "assigndiv", 20).nud = function () {\n error("E014");\n };\n assignop("%=", "assignmod", 20);\n\n bitwiseassignop("&=", "assignbitand", 20);\n bitwiseassignop("|=", "assignbitor", 20);\n bitwiseassignop("^=", "assignbitxor", 20);\n bitwiseassignop("<<=", "assignshiftleft", 20);\n bitwiseassignop(">>=", "assignshiftright", 20);\n bitwiseassignop(">>>=", "assignshiftrightunsigned", 20);\n infix("?", function (left, that) {\n that.left = left;\n that.right = expression(10);\n advance(":");\n that["else"] = expression(10);\n return that;\n }, 30);\n\n infix("||", "or", 40);\n infix("&&", "and", 50);\n bitwise("|", "bitor", 70);\n bitwise("^", "bitxor", 80);\n bitwise("&", "bitand", 90);\n relation("==", function (left, right) {\n var eqnull = state.option.eqnull && (left.value === "null" || right.value === "null");\n\n if (!eqnull && state.option.eqeqeq)\n warning("W116", this, "===", "==");\n else if (isPoorRelation(left))\n warning("W041", this, "===", left.value);\n else if (isPoorRelation(right))\n warning("W041", this, "===", right.value);\n\n return this;\n });\n relation("===");\n relation("!=", function (left, right) {\n var eqnull = state.option.eqnull &&\n (left.value === "null" || right.value === "null");\n\n if (!eqnull && state.option.eqeqeq) {\n warning("W116", this, "!==", "!=");\n } else if (isPoorRelation(left)) {\n warning("W041", this, "!==", left.value);\n } else if (isPoorRelation(right)) {\n warning("W041", this, "!==", right.value);\n }\n return this;\n });\n relation("!==");\n relation("<");\n relation(">");\n relation("<=");\n relation(">=");\n bitwise("<<", "shiftleft", 120);\n bitwise(">>", "shiftright", 120);\n bitwise(">>>", "shiftrightunsigned", 120);\n infix("in", "in", 120);\n infix("instanceof", "instanceof", 120);\n infix("+", function (left, that) {\n var right = expression(130);\n if (left && right && left.id === "(string)" && right.id === "(string)") {\n left.value += right.value;\n left.character = right.character;\n if (!state.option.scripturl && reg.javascriptURL.test(left.value)) {\n warning("W050", left);\n }\n return left;\n }\n that.left = left;\n that.right = right;\n return that;\n }, 130);\n prefix("+", "num");\n prefix("+++", function () {\n warning("W007");\n this.right = expression(150);\n this.arity = "unary";\n return this;\n });\n infix("+++", function (left) {\n warning("W007");\n this.left = left;\n this.right = expression(130);\n return this;\n }, 130);\n infix("-", "sub", 130);\n prefix("-", "neg");\n prefix("---", function () {\n warning("W006");\n this.right = expression(150);\n this.arity = "unary";\n return this;\n });\n infix("---", function (left) {\n warning("W006");\n this.left = left;\n this.right = expression(130);\n return this;\n }, 130);\n infix("*", "mult", 140);\n infix("/", "div", 140);\n infix("%", "mod", 140);\n\n suffix("++", "postinc");\n prefix("++", "preinc");\n state.syntax["++"].exps = true;\n\n suffix("--", "postdec");\n prefix("--", "predec");\n state.syntax["--"].exps = true;\n prefix("delete", function () {\n var p = expression(0);\n if (!p || (p.id !== "." && p.id !== "[")) {\n warning("W051");\n }\n this.first = p;\n return this;\n }).exps = true;\n\n prefix("~", function () {\n if (state.option.bitwise) {\n warning("W052", this, "~");\n }\n expression(150);\n return this;\n });\n\n prefix("!", function () {\n this.right = expression(150);\n this.arity = "unary";\n\n if (!this.right) { // \'!\' followed by nothing? Give up.\n quit("E041", this.line || 0);\n }\n\n if (bang[this.right.id] === true) {\n warning("W018", this, "!");\n }\n return this;\n });\n\n prefix("typeof", "typeof");\n prefix("new", function () {\n var c = expression(155), i;\n if (c && c.id !== "function") {\n if (c.identifier) {\n c["new"] = true;\n switch (c.value) {\n case "Number":\n case "String":\n case "Boolean":\n case "Math":\n case "JSON":\n warning("W053", state.tokens.prev, c.value);\n break;\n case "Function":\n if (!state.option.evil) {\n warning("W054");\n }\n break;\n case "Date":\n case "RegExp":\n break;\n default:\n if (c.id !== "function") {\n i = c.value.substr(0, 1);\n if (state.option.newcap && (i < "A" || i > "Z") && !_.has(global, c.value)) {\n warning("W055", state.tokens.curr);\n }\n }\n }\n } else {\n if (c.id !== "." && c.id !== "[" && c.id !== "(") {\n warning("W056", state.tokens.curr);\n }\n }\n } else {\n if (!state.option.supernew)\n warning("W057", this);\n }\n adjacent(state.tokens.curr, state.tokens.next);\n if (state.tokens.next.id !== "(" && !state.option.supernew) {\n warning("W058", state.tokens.curr, state.tokens.curr.value);\n }\n this.first = c;\n return this;\n });\n state.syntax["new"].exps = true;\n\n prefix("void").exps = true;\n\n infix(".", function (left, that) {\n adjacent(state.tokens.prev, state.tokens.curr);\n nobreak();\n var m = identifier(false, true);\n\n if (typeof m === "string") {\n countMember(m);\n }\n\n that.left = left;\n that.right = m;\n\n if (m && m === "hasOwnProperty" && state.tokens.next.value === "=") {\n warning("W001");\n }\n\n if (left && left.value === "arguments" && (m === "callee" || m === "caller")) {\n if (state.option.noarg)\n warning("W059", left, m);\n else if (state.directive["use strict"])\n error("E008");\n } else if (!state.option.evil && left && left.value === "document" &&\n (m === "write" || m === "writeln")) {\n warning("W060", left);\n }\n\n if (!state.option.evil && (m === "eval" || m === "execScript")) {\n warning("W061");\n }\n\n return that;\n }, 160, true);\n\n infix("(", function (left, that) {\n if (state.tokens.prev.id !== "}" && state.tokens.prev.id !== ")") {\n nobreak(state.tokens.prev, state.tokens.curr);\n }\n\n nospace();\n if (state.option.immed && !left.immed && left.id === "function") {\n warning("W062");\n }\n\n var n = 0;\n var p = [];\n\n if (left) {\n if (left.type === "(identifier)") {\n if (left.value.match(/^[A-Z]([A-Z0-9_$]*[a-z][A-Za-z0-9_$]*)?$/)) {\n if ("Number String Boolean Date Object".indexOf(left.value) === -1) {\n if (left.value === "Math") {\n warning("W063", left);\n } else if (state.option.newcap) {\n warning("W064", left);\n }\n }\n }\n }\n }\n\n if (state.tokens.next.id !== ")") {\n for (;;) {\n p[p.length] = expression(10);\n n += 1;\n if (state.tokens.next.id !== ",") {\n break;\n }\n comma();\n }\n }\n\n advance(")");\n nospace(state.tokens.prev, state.tokens.curr);\n\n if (typeof left === "object") {\n if (left.value === "parseInt" && n === 1) {\n warning("W065", state.tokens.curr);\n }\n if (!state.option.evil) {\n if (left.value === "eval" || left.value === "Function" ||\n left.value === "execScript") {\n warning("W061", left);\n\n if (p[0] && [0].id === "(string)") {\n addInternalSrc(left, p[0].value);\n }\n } else if (p[0] && p[0].id === "(string)" &&\n (left.value === "setTimeout" ||\n left.value === "setInterval")) {\n warning("W066", left);\n addInternalSrc(left, p[0].value);\n\n // window.setTimeout/setInterval\n } else if (p[0] && p[0].id === "(string)" &&\n left.value === "." &&\n left.left.value === "window" &&\n (left.right === "setTimeout" ||\n left.right === "setInterval")) {\n warning("W066", left);\n addInternalSrc(left, p[0].value);\n }\n }\n if (!left.identifier && left.id !== "." && left.id !== "[" &&\n left.id !== "(" && left.id !== "&&" && left.id !== "||" &&\n left.id !== "?") {\n warning("W067", left);\n }\n }\n\n that.left = left;\n return that;\n }, 155, true).exps = true;\n\n prefix("(", function () {\n nospace();\n\n if (state.tokens.next.id === "function") {\n state.tokens.next.immed = true;\n }\n\n var exprs = [];\n\n if (state.tokens.next.id !== ")") {\n for (;;) {\n exprs.push(expression(0));\n if (state.tokens.next.id !== ",") {\n break;\n }\n comma();\n }\n }\n\n advance(")", this);\n nospace(state.tokens.prev, state.tokens.curr);\n if (state.option.immed && exprs[0].id === "function") {\n if (state.tokens.next.id !== "(" &&\n (state.tokens.next.id !== "." || (peek().value !== "call" && peek().value !== "apply"))) {\n warning("W068", this);\n }\n }\n\n return exprs[0];\n });\n\n infix("[", function (left, that) {\n nobreak(state.tokens.prev, state.tokens.curr);\n nospace();\n var e = expression(0), s;\n if (e && e.type === "(string)") {\n if (!state.option.evil && (e.value === "eval" || e.value === "execScript")) {\n warning("W061", that);\n }\n\n countMember(e.value);\n if (!state.option.sub && reg.identifier.test(e.value)) {\n s = state.syntax[e.value];\n if (!s || !isReserved(s)) {\n warning("W069", state.tokens.prev, e.value);\n }\n }\n }\n advance("]", that);\n\n if (e && e.value === "hasOwnProperty" && state.tokens.next.value === "=") {\n warning("W001");\n }\n\n nospace(state.tokens.prev, state.tokens.curr);\n that.left = left;\n that.right = e;\n return that;\n }, 160, true);\n\n prefix("[", function () {\n var b = state.tokens.curr.line !== state.tokens.next.line;\n this.first = [];\n if (b) {\n indent += state.option.indent;\n if (state.tokens.next.from === indent + state.option.indent) {\n indent += state.option.indent;\n }\n }\n while (state.tokens.next.id !== "(end)") {\n while (state.tokens.next.id === ",") {\n if (!state.option.es5)\n warning("W070");\n advance(",");\n }\n if (state.tokens.next.id === "]") {\n break;\n }\n if (b && state.tokens.curr.line !== state.tokens.next.line) {\n indentation();\n }\n this.first.push(expression(10));\n if (state.tokens.next.id === ",") {\n comma({ allowTrailing: true });\n if (state.tokens.next.id === "]" && !state.option.es5) {\n warning("W070", state.tokens.curr);\n break;\n }\n } else {\n break;\n }\n }\n if (b) {\n indent -= state.option.indent;\n indentation();\n }\n advance("]", this);\n return this;\n }, 160);\n\n\n function property_name() {\n var id = optionalidentifier(false, true);\n\n if (!id) {\n if (state.tokens.next.id === "(string)") {\n id = state.tokens.next.value;\n advance();\n } else if (state.tokens.next.id === "(number)") {\n id = state.tokens.next.value.toString();\n advance();\n }\n }\n\n if (id === "hasOwnProperty") {\n warning("W001");\n }\n\n return id;\n }\n\n\n function functionparams() {\n var next = state.tokens.next;\n var params = [];\n var ident;\n\n advance("(");\n nospace();\n\n if (state.tokens.next.id === ")") {\n advance(")");\n return;\n }\n\n for (;;) {\n ident = identifier(true);\n params.push(ident);\n addlabel(ident, "unused", state.tokens.curr);\n if (state.tokens.next.id === ",") {\n comma();\n } else {\n advance(")", next);\n nospace(state.tokens.prev, state.tokens.curr);\n return params;\n }\n }\n }\n\n\n function doFunction(name, statement) {\n var f;\n var oldOption = state.option;\n var oldScope = scope;\n\n state.option = Object.create(state.option);\n scope = Object.create(scope);\n\n funct = {\n "(name)" : name || "\\"" + anonname + "\\"",\n "(line)" : state.tokens.next.line,\n "(character)": state.tokens.next.character,\n "(context)" : funct,\n "(breakage)" : 0,\n "(loopage)" : 0,\n "(metrics)" : createMetrics(state.tokens.next),\n "(scope)" : scope,\n "(statement)": statement,\n "(tokens)" : {}\n };\n\n f = funct;\n state.tokens.curr.funct = funct;\n\n functions.push(funct);\n\n if (name) {\n addlabel(name, "function");\n }\n\n funct["(params)"] = functionparams();\n funct["(metrics)"].verifyMaxParametersPerFunction(funct["(params)"]);\n\n block(false, false, true);\n\n funct["(metrics)"].verifyMaxStatementsPerFunction();\n funct["(metrics)"].verifyMaxComplexityPerFunction();\n funct["(unusedOption)"] = state.option.unused;\n\n scope = oldScope;\n state.option = oldOption;\n funct["(last)"] = state.tokens.curr.line;\n funct["(lastcharacter)"] = state.tokens.curr.character;\n funct = funct["(context)"];\n\n return f;\n }\n\n function createMetrics(functionStartToken) {\n return {\n statementCount: 0,\n nestedBlockDepth: -1,\n ComplexityCount: 1,\n verifyMaxStatementsPerFunction: function () {\n if (state.option.maxstatements &&\n this.statementCount > state.option.maxstatements) {\n warning("W071", functionStartToken, this.statementCount);\n }\n },\n\n verifyMaxParametersPerFunction: function (params) {\n params = params || [];\n\n if (state.option.maxparams && params.length > state.option.maxparams) {\n warning("W072", functionStartToken, params.length);\n }\n },\n\n verifyMaxNestedBlockDepthPerFunction: function () {\n if (state.option.maxdepth &&\n this.nestedBlockDepth > 0 &&\n this.nestedBlockDepth === state.option.maxdepth + 1) {\n warning("W073", null, this.nestedBlockDepth);\n }\n },\n\n verifyMaxComplexityPerFunction: function () {\n var max = state.option.maxcomplexity;\n var cc = this.ComplexityCount;\n if (max && cc > max) {\n warning("W074", functionStartToken, cc);\n }\n }\n };\n }\n\n function increaseComplexityCount() {\n funct["(metrics)"].ComplexityCount += 1;\n }\n\n // Parse assignments that were found instead of conditionals.\n // For example: if (a = 1) { ... }\n\n function parseCondAssignment() {\n switch (state.tokens.next.id) {\n case "=":\n case "+=":\n case "-=":\n case "*=":\n case "%=":\n case "&=":\n case "|=":\n case "^=":\n case "/=":\n if (!state.option.boss) {\n warning("W084");\n }\n\n advance(state.tokens.next.id);\n expression(20);\n }\n }\n\n\n (function (x) {\n x.nud = function () {\n var b, f, i, p, t;\n var props = {}; // All properties, including accessors\n\n function saveProperty(name, tkn) {\n if (props[name] && _.has(props, name))\n warning("W075", state.tokens.next, i);\n else\n props[name] = {};\n\n props[name].basic = true;\n props[name].basictkn = tkn;\n }\n\n function saveSetter(name, tkn) {\n if (props[name] && _.has(props, name)) {\n if (props[name].basic || props[name].setter)\n warning("W075", state.tokens.next, i);\n } else {\n props[name] = {};\n }\n\n props[name].setter = true;\n props[name].setterToken = tkn;\n }\n\n function saveGetter(name) {\n if (props[name] && _.has(props, name)) {\n if (props[name].basic || props[name].getter)\n warning("W075", state.tokens.next, i);\n } else {\n props[name] = {};\n }\n\n props[name].getter = true;\n props[name].getterToken = state.tokens.curr;\n }\n\n b = state.tokens.curr.line !== state.tokens.next.line;\n if (b) {\n indent += state.option.indent;\n if (state.tokens.next.from === indent + state.option.indent) {\n indent += state.option.indent;\n }\n }\n\n for (;;) {\n if (state.tokens.next.id === "}") {\n break;\n }\n\n if (b) {\n indentation();\n }\n\n if (state.tokens.next.value === "get" && peek().id !== ":") {\n advance("get");\n\n if (!state.option.es5) {\n error("E034");\n }\n\n i = property_name();\n if (!i) {\n error("E035");\n }\n\n saveGetter(i);\n t = state.tokens.next;\n adjacent(state.tokens.curr, state.tokens.next);\n f = doFunction();\n p = f["(params)"];\n\n if (p) {\n warning("W076", t, p[0], i);\n }\n\n adjacent(state.tokens.curr, state.tokens.next);\n } else if (state.tokens.next.value === "set" && peek().id !== ":") {\n advance("set");\n\n if (!state.option.es5) {\n error("E034");\n }\n\n i = property_name();\n if (!i) {\n error("E035");\n }\n\n saveSetter(i, state.tokens.next);\n t = state.tokens.next;\n adjacent(state.tokens.curr, state.tokens.next);\n f = doFunction();\n p = f["(params)"];\n\n if (!p || p.length !== 1) {\n warning("W077", t, i);\n }\n } else {\n i = property_name();\n saveProperty(i, state.tokens.next);\n\n if (typeof i !== "string") {\n break;\n }\n\n advance(":");\n nonadjacent(state.tokens.curr, state.tokens.next);\n expression(10);\n }\n\n countMember(i);\n if (state.tokens.next.id === ",") {\n comma({ allowTrailing: true });\n if (state.tokens.next.id === ",") {\n warning("W070", state.tokens.curr);\n } else if (state.tokens.next.id === "}" && !state.option.es5) {\n warning("W070", state.tokens.curr);\n }\n } else {\n break;\n }\n }\n if (b) {\n indent -= state.option.indent;\n indentation();\n }\n advance("}", this);\n\n // Check for lonely setters if in the ES5 mode.\n if (state.option.es5) {\n for (var name in props) {\n if (_.has(props, name) && props[name].setter && !props[name].getter) {\n warning("W078", props[name].setterToken);\n }\n }\n }\n return this;\n };\n x.fud = function () {\n error("E036", state.tokens.curr);\n };\n }(delim("{")));\n\n // This Function is called when esnext option is set to true\n // it adds the `const` statement to JSHINT\n\n useESNextSyntax = function () {\n var conststatement = stmt("const", function (prefix) {\n var id, name, value;\n\n this.first = [];\n for (;;) {\n nonadjacent(state.tokens.curr, state.tokens.next);\n id = identifier();\n if (funct[id] === "const") {\n warning("E011", null, id);\n }\n if (funct["(global)"] && predefined[id] === false) {\n warning("W079", state.tokens.curr, id);\n }\n addlabel(id, "const");\n if (prefix) {\n break;\n }\n name = state.tokens.curr;\n this.first.push(state.tokens.curr);\n\n if (state.tokens.next.id !== "=") {\n warning("E012", state.tokens.curr, id);\n }\n\n if (state.tokens.next.id === "=") {\n nonadjacent(state.tokens.curr, state.tokens.next);\n advance("=");\n nonadjacent(state.tokens.curr, state.tokens.next);\n if (state.tokens.next.id === "undefined") {\n warning("W080", state.tokens.curr, id);\n }\n if (peek(0).id === "=" && state.tokens.next.identifier) {\n error("E037", state.tokens.next, state.tokens.next.value);\n }\n value = expression(0);\n name.first = value;\n }\n\n if (state.tokens.next.id !== ",") {\n break;\n }\n comma();\n }\n return this;\n });\n conststatement.exps = true;\n };\n\n var varstatement = stmt("var", function (prefix) {\n // JavaScript does not have block scope. It only has function scope. So,\n // declaring a variable in a block can have unexpected consequences.\n var id, name, value;\n\n if (funct["(onevar)"] && state.option.onevar) {\n warning("W081");\n } else if (!funct["(global)"]) {\n funct["(onevar)"] = true;\n }\n\n this.first = [];\n\n for (;;) {\n nonadjacent(state.tokens.curr, state.tokens.next);\n id = identifier();\n\n if (state.option.esnext && funct[id] === "const") {\n warning("E011", null, id);\n }\n\n if (funct["(global)"] && predefined[id] === false) {\n warning("W079", state.tokens.curr, id);\n }\n\n addlabel(id, "unused", state.tokens.curr);\n\n if (prefix) {\n break;\n }\n\n name = state.tokens.curr;\n this.first.push(state.tokens.curr);\n\n if (state.tokens.next.id === "=") {\n nonadjacent(state.tokens.curr, state.tokens.next);\n advance("=");\n nonadjacent(state.tokens.curr, state.tokens.next);\n if (state.tokens.next.id === "undefined") {\n warning("W080", state.tokens.curr, id);\n }\n if (peek(0).id === "=" && state.tokens.next.identifier) {\n error("E038", state.tokens.next, state.tokens.next.value);\n }\n value = expression(0);\n name.first = value;\n }\n if (state.tokens.next.id !== ",") {\n break;\n }\n comma();\n }\n return this;\n });\n varstatement.exps = true;\n\n blockstmt("function", function () {\n if (inblock) {\n warning("W082", state.tokens.curr);\n\n }\n var i = identifier();\n if (state.option.esnext && funct[i] === "const") {\n warning("E011", null, i);\n }\n adjacent(state.tokens.curr, state.tokens.next);\n addlabel(i, "unction", state.tokens.curr);\n\n doFunction(i, { statement: true });\n if (state.tokens.next.id === "(" && state.tokens.next.line === state.tokens.curr.line) {\n error("E039");\n }\n return this;\n });\n\n prefix("function", function () {\n var i = optionalidentifier();\n if (i || state.option.gcl) {\n adjacent(state.tokens.curr, state.tokens.next);\n } else {\n nonadjacent(state.tokens.curr, state.tokens.next);\n }\n doFunction(i);\n if (!state.option.loopfunc && funct["(loopage)"]) {\n warning("W083");\n }\n return this;\n });\n\n blockstmt("if", function () {\n var t = state.tokens.next;\n increaseComplexityCount();\n advance("(");\n nonadjacent(this, t);\n nospace();\n expression(20);\n parseCondAssignment();\n advance(")", t);\n nospace(state.tokens.prev, state.tokens.curr);\n block(true, true);\n if (state.tokens.next.id === "else") {\n nonadjacent(state.tokens.curr, state.tokens.next);\n advance("else");\n if (state.tokens.next.id === "if" || state.tokens.next.id === "switch") {\n statement(true);\n } else {\n block(true, true);\n }\n }\n return this;\n });\n\n blockstmt("try", function () {\n var b;\n\n function doCatch() {\n var oldScope = scope;\n var e;\n\n advance("catch");\n nonadjacent(state.tokens.curr, state.tokens.next);\n advance("(");\n\n scope = Object.create(oldScope);\n\n e = state.tokens.next.value;\n if (state.tokens.next.type !== "(identifier)") {\n e = null;\n warning("E030", state.tokens.next, e);\n }\n\n advance();\n advance(")");\n\n funct = {\n "(name)" : "(catch)",\n "(line)" : state.tokens.next.line,\n "(character)": state.tokens.next.character,\n "(context)" : funct,\n "(breakage)" : funct["(breakage)"],\n "(loopage)" : funct["(loopage)"],\n "(scope)" : scope,\n "(statement)": false,\n "(metrics)" : createMetrics(state.tokens.next),\n "(catch)" : true,\n "(tokens)" : {}\n };\n\n if (e) {\n addlabel(e, "exception");\n }\n\n state.tokens.curr.funct = funct;\n functions.push(funct);\n\n block(false);\n\n scope = oldScope;\n\n funct["(last)"] = state.tokens.curr.line;\n funct["(lastcharacter)"] = state.tokens.curr.character;\n funct = funct["(context)"];\n }\n\n block(false);\n\n if (state.tokens.next.id === "catch") {\n increaseComplexityCount();\n doCatch();\n b = true;\n }\n\n if (state.tokens.next.id === "finally") {\n advance("finally");\n block(false);\n return;\n } else if (!b) {\n error("E021", state.tokens.next, "catch", state.tokens.next.value);\n }\n\n return this;\n });\n\n blockstmt("while", function () {\n var t = state.tokens.next;\n funct["(breakage)"] += 1;\n funct["(loopage)"] += 1;\n increaseComplexityCount();\n advance("(");\n nonadjacent(this, t);\n nospace();\n expression(20);\n parseCondAssignment();\n advance(")", t);\n nospace(state.tokens.prev, state.tokens.curr);\n block(true, true);\n funct["(breakage)"] -= 1;\n funct["(loopage)"] -= 1;\n return this;\n }).labelled = true;\n\n blockstmt("with", function () {\n var t = state.tokens.next;\n if (state.directive["use strict"]) {\n error("E010", state.tokens.curr);\n } else if (!state.option.withstmt) {\n warning("W085", state.tokens.curr);\n }\n\n advance("(");\n nonadjacent(this, t);\n nospace();\n expression(0);\n advance(")", t);\n nospace(state.tokens.prev, state.tokens.curr);\n block(true, true);\n\n return this;\n });\n\n blockstmt("switch", function () {\n var t = state.tokens.next,\n g = false;\n funct["(breakage)"] += 1;\n advance("(");\n nonadjacent(this, t);\n nospace();\n this.condition = expression(20);\n advance(")", t);\n nospace(state.tokens.prev, state.tokens.curr);\n nonadjacent(state.tokens.curr, state.tokens.next);\n t = state.tokens.next;\n advance("{");\n nonadjacent(state.tokens.curr, state.tokens.next);\n indent += state.option.indent;\n this.cases = [];\n\n for (;;) {\n switch (state.tokens.next.id) {\n case "case":\n switch (funct["(verb)"]) {\n case "break":\n case "case":\n case "continue":\n case "return":\n case "switch":\n case "throw":\n break;\n default:\n // You can tell JSHint that you don\'t use break intentionally by\n // adding a comment /* falls through */ on a line just before\n // the next `case`.\n if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) {\n warning("W086", state.tokens.curr, "case");\n }\n }\n indentation(-state.option.indent);\n advance("case");\n this.cases.push(expression(20));\n increaseComplexityCount();\n g = true;\n advance(":");\n funct["(verb)"] = "case";\n break;\n case "default":\n switch (funct["(verb)"]) {\n case "break":\n case "continue":\n case "return":\n case "throw":\n break;\n default:\n // Do not display a warning if \'default\' is the first statement or if\n // there is a special /* falls through */ comment.\n if (this.cases.length) {\n if (!reg.fallsThrough.test(state.lines[state.tokens.next.line - 2])) {\n warning("W086", state.tokens.curr, "default");\n }\n }\n }\n indentation(-state.option.indent);\n advance("default");\n g = true;\n advance(":");\n break;\n case "}":\n indent -= state.option.indent;\n indentation();\n advance("}", t);\n funct["(breakage)"] -= 1;\n funct["(verb)"] = undefined;\n return;\n case "(end)":\n error("E023", state.tokens.next, "}");\n return;\n default:\n if (g) {\n switch (state.tokens.curr.id) {\n case ",":\n error("E040");\n return;\n case ":":\n g = false;\n statements();\n break;\n default:\n error("E025", state.tokens.curr);\n return;\n }\n } else {\n if (state.tokens.curr.id === ":") {\n advance(":");\n error("E024", state.tokens.curr, ":");\n statements();\n } else {\n error("E021", state.tokens.next, "case", state.tokens.next.value);\n return;\n }\n }\n }\n }\n }).labelled = true;\n\n stmt("debugger", function () {\n if (!state.option.debug) {\n warning("W087");\n }\n return this;\n }).exps = true;\n\n (function () {\n var x = stmt("do", function () {\n funct["(breakage)"] += 1;\n funct["(loopage)"] += 1;\n increaseComplexityCount();\n\n this.first = block(true);\n advance("while");\n var t = state.tokens.next;\n nonadjacent(state.tokens.curr, t);\n advance("(");\n nospace();\n expression(20);\n parseCondAssignment();\n advance(")", t);\n nospace(state.tokens.prev, state.tokens.curr);\n funct["(breakage)"] -= 1;\n funct["(loopage)"] -= 1;\n return this;\n });\n x.labelled = true;\n x.exps = true;\n }());\n\n blockstmt("for", function () {\n var s, t = state.tokens.next;\n funct["(breakage)"] += 1;\n funct["(loopage)"] += 1;\n increaseComplexityCount();\n advance("(");\n nonadjacent(this, t);\n nospace();\n if (peek(state.tokens.next.id === "var" ? 1 : 0).id === "in") {\n if (state.tokens.next.id === "var") {\n advance("var");\n varstatement.fud.call(varstatement, true);\n } else {\n switch (funct[state.tokens.next.value]) {\n case "unused":\n funct[state.tokens.next.value] = "var";\n break;\n case "var":\n break;\n default:\n warning("W088", state.tokens.next, state.tokens.next.value);\n }\n advance();\n }\n advance("in");\n expression(20);\n advance(")", t);\n s = block(true, true);\n if (state.option.forin && s && (s.length > 1 || typeof s[0] !== "object" ||\n s[0].value !== "if")) {\n warning("W089", this);\n }\n funct["(breakage)"] -= 1;\n funct["(loopage)"] -= 1;\n return this;\n } else {\n if (state.tokens.next.id !== ";") {\n if (state.tokens.next.id === "var") {\n advance("var");\n varstatement.fud.call(varstatement);\n } else {\n for (;;) {\n expression(0, "for");\n if (state.tokens.next.id !== ",") {\n break;\n }\n comma();\n }\n }\n }\n nolinebreak(state.tokens.curr);\n advance(";");\n if (state.tokens.next.id !== ";") {\n expression(20);\n parseCondAssignment();\n }\n nolinebreak(state.tokens.curr);\n advance(";");\n if (state.tokens.next.id === ";") {\n error("E021", state.tokens.next, ")", ";");\n }\n if (state.tokens.next.id !== ")") {\n for (;;) {\n expression(0, "for");\n if (state.tokens.next.id !== ",") {\n break;\n }\n comma();\n }\n }\n advance(")", t);\n nospace(state.tokens.prev, state.tokens.curr);\n block(true, true);\n funct["(breakage)"] -= 1;\n funct["(loopage)"] -= 1;\n return this;\n }\n }).labelled = true;\n\n\n stmt("break", function () {\n var v = state.tokens.next.value;\n\n if (funct["(breakage)"] === 0)\n warning("W052", state.tokens.next, this.value);\n\n if (!state.option.asi)\n nolinebreak(this);\n\n if (state.tokens.next.id !== ";") {\n if (state.tokens.curr.line === state.tokens.next.line) {\n if (funct[v] !== "label") {\n warning("W090", state.tokens.next, v);\n } else if (scope[v] !== funct) {\n warning("W091", state.tokens.next, v);\n }\n this.first = state.tokens.next;\n advance();\n }\n }\n reachable("break");\n return this;\n }).exps = true;\n\n\n stmt("continue", function () {\n var v = state.tokens.next.value;\n\n if (funct["(breakage)"] === 0)\n warning("W052", state.tokens.next, this.value);\n\n if (!state.option.asi)\n nolinebreak(this);\n\n if (state.tokens.next.id !== ";") {\n if (state.tokens.curr.line === state.tokens.next.line) {\n if (funct[v] !== "label") {\n warning("W090", state.tokens.next, v);\n } else if (scope[v] !== funct) {\n warning("W091", state.tokens.next, v);\n }\n this.first = state.tokens.next;\n advance();\n }\n } else if (!funct["(loopage)"]) {\n warning("W052", state.tokens.next, this.value);\n }\n reachable("continue");\n return this;\n }).exps = true;\n\n\n stmt("return", function () {\n if (this.line === state.tokens.next.line) {\n if (state.tokens.next.id === "(regexp)")\n warning("W092");\n\n if (state.tokens.next.id !== ";" && !state.tokens.next.reach) {\n nonadjacent(state.tokens.curr, state.tokens.next);\n this.first = expression(0);\n\n if (this.first.type === "(punctuator)" && this.first.value === "=" && !state.option.boss) {\n warningAt("W093", this.first.line, this.first.character);\n }\n }\n } else if (!state.option.asi) {\n nolinebreak(this); // always warn (Line breaking error)\n }\n reachable("return");\n return this;\n }).exps = true;\n\n\n stmt("throw", function () {\n nolinebreak(this);\n nonadjacent(state.tokens.curr, state.tokens.next);\n this.first = expression(20);\n reachable("throw");\n return this;\n }).exps = true;\n\n // Future Reserved Words\n\n FutureReservedWord("abstract");\n FutureReservedWord("boolean");\n FutureReservedWord("byte");\n FutureReservedWord("char");\n FutureReservedWord("class", { es5: true });\n FutureReservedWord("double");\n FutureReservedWord("enum", { es5: true });\n FutureReservedWord("export", { es5: true });\n FutureReservedWord("extends", { es5: true });\n FutureReservedWord("final");\n FutureReservedWord("float");\n FutureReservedWord("goto");\n FutureReservedWord("implements", { es5: true, strictOnly: true });\n FutureReservedWord("import", { es5: true });\n FutureReservedWord("int");\n FutureReservedWord("interface");\n FutureReservedWord("let", { es5: true, strictOnly: true });\n FutureReservedWord("long");\n FutureReservedWord("native");\n FutureReservedWord("package", { es5: true, strictOnly: true });\n FutureReservedWord("private", { es5: true, strictOnly: true });\n FutureReservedWord("protected", { es5: true, strictOnly: true });\n FutureReservedWord("public", { es5: true, strictOnly: true });\n FutureReservedWord("short");\n FutureReservedWord("static", { es5: true, strictOnly: true });\n FutureReservedWord("super", { es5: true });\n FutureReservedWord("synchronized");\n FutureReservedWord("throws");\n FutureReservedWord("transient");\n FutureReservedWord("volatile");\n FutureReservedWord("yield", { es5: true, strictOnly: true });\n\n // Parse JSON\n\n function jsonValue() {\n\n function jsonObject() {\n var o = {}, t = state.tokens.next;\n advance("{");\n if (state.tokens.next.id !== "}") {\n for (;;) {\n if (state.tokens.next.id === "(end)") {\n error("E026", state.tokens.next, t.line);\n } else if (state.tokens.next.id === "}") {\n warning("W094", state.tokens.curr);\n break;\n } else if (state.tokens.next.id === ",") {\n error("E028", state.tokens.next);\n } else if (state.tokens.next.id !== "(string)") {\n warning("W095", state.tokens.next, state.tokens.next.value);\n }\n if (o[state.tokens.next.value] === true) {\n warning("W075", state.tokens.next, state.tokens.next.value);\n } else if ((state.tokens.next.value === "__proto__" &&\n !state.option.proto) || (state.tokens.next.value === "__iterator__" &&\n !state.option.iterator)) {\n warning("W096", state.tokens.next, state.tokens.next.value);\n } else {\n o[state.tokens.next.value] = true;\n }\n advance();\n advance(":");\n jsonValue();\n if (state.tokens.next.id !== ",") {\n break;\n }\n advance(",");\n }\n }\n advance("}");\n }\n\n function jsonArray() {\n var t = state.tokens.next;\n advance("[");\n if (state.tokens.next.id !== "]") {\n for (;;) {\n if (state.tokens.next.id === "(end)") {\n error("E027", state.tokens.next, t.line);\n } else if (state.tokens.next.id === "]") {\n warning("W094", state.tokens.curr);\n break;\n } else if (state.tokens.next.id === ",") {\n error("E028", state.tokens.next);\n }\n jsonValue();\n if (state.tokens.next.id !== ",") {\n break;\n }\n advance(",");\n }\n }\n advance("]");\n }\n\n switch (state.tokens.next.id) {\n case "{":\n jsonObject();\n break;\n case "[":\n jsonArray();\n break;\n case "true":\n case "false":\n case "null":\n case "(number)":\n case "(string)":\n advance();\n break;\n case "-":\n advance("-");\n if (state.tokens.curr.character !== state.tokens.next.from) {\n warning("W011", state.tokens.curr);\n }\n adjacent(state.tokens.curr, state.tokens.next);\n advance("(number)");\n break;\n default:\n error("E003", state.tokens.next);\n }\n }\n\n\n // The actual JSHINT function itself.\n var itself = function (s, o, g) {\n var a, i, k, x;\n var optionKeys;\n var newOptionObj = {};\n\n state.reset();\n\n if (o && o.scope) {\n JSHINT.scope = o.scope;\n } else {\n JSHINT.errors = [];\n JSHINT.undefs = [];\n JSHINT.internals = [];\n JSHINT.blacklist = {};\n JSHINT.scope = "(main)";\n }\n\n predefined = Object.create(null);\n combine(predefined, vars.ecmaIdentifiers);\n combine(predefined, vars.reservedVars);\n\n combine(predefined, g || {});\n\n declared = Object.create(null);\n exported = Object.create(null);\n ignored = Object.create(null);\n\n if (o) {\n a = o.predef;\n if (a) {\n if (!Array.isArray(a) && typeof a === "object") {\n a = Object.keys(a);\n }\n\n a.forEach(function (item) {\n var slice, prop;\n\n if (item[0] === "-") {\n slice = item.slice(1);\n JSHINT.blacklist[slice] = slice;\n } else {\n prop = Object.getOwnPropertyDescriptor(o.predef, item);\n predefined[item] = prop ? prop.value : false;\n }\n });\n }\n\n optionKeys = Object.keys(o);\n for (x = 0; x < optionKeys.length; x++) {\n if (/^-W\\d{3}$/g.test(optionKeys[x])) {\n ignored[optionKeys[x].slice(1)] = true;\n } else {\n newOptionObj[optionKeys[x]] = o[optionKeys[x]];\n\n if (optionKeys[x] === "newcap" && o[optionKeys[x]] === false)\n newOptionObj["(explicitNewcap)"] = true;\n\n if (optionKeys[x] === "indent")\n newOptionObj["(explicitIndent)"] = true;\n }\n }\n }\n\n state.option = newOptionObj;\n\n state.option.indent = state.option.indent || 4;\n state.option.maxerr = state.option.maxerr || 50;\n\n indent = 1;\n global = Object.create(predefined);\n scope = global;\n funct = {\n "(global)": true,\n "(name)": "(global)",\n "(scope)": scope,\n "(breakage)": 0,\n "(loopage)": 0,\n "(tokens)": {},\n "(metrics)": createMetrics(state.tokens.next)\n };\n functions = [funct];\n urls = [];\n stack = null;\n member = {};\n membersOnly = null;\n implied = {};\n inblock = false;\n lookahead = [];\n warnings = 0;\n unuseds = [];\n\n if (!isString(s) && !Array.isArray(s)) {\n errorAt("E004", 0);\n return false;\n }\n\n var api = {\n get isJSON() {\n return state.jsonMode;\n },\n\n getOption: function (name) {\n return state.option[name] || null;\n },\n\n getCache: function (name) {\n return state.cache[name];\n },\n\n setCache: function (name, value) {\n state.cache[name] = value;\n },\n\n warn: function (code, data) {\n warningAt.apply(null, [ code, data.line, data.char ].concat(data.data));\n },\n\n on: function (names, listener) {\n names.split(" ").forEach(function (name) {\n emitter.on(name, listener);\n }.bind(this));\n }\n };\n\n emitter.removeAllListeners();\n (extraModules || []).forEach(function (func) {\n func(api);\n });\n\n state.tokens.prev = state.tokens.curr = state.tokens.next = state.syntax["(begin)"];\n\n lex = new Lexer(s);\n\n lex.on("warning", function (ev) {\n warningAt.apply(null, [ ev.code, ev.line, ev.character].concat(ev.data));\n });\n\n lex.on("error", function (ev) {\n errorAt.apply(null, [ ev.code, ev.line, ev.character ].concat(ev.data));\n });\n\n lex.on("fatal", function (ev) {\n quit("E041", ev.line, ev.from);\n });\n\n lex.on("Identifier", function (ev) {\n emitter.emit("Identifier", ev);\n });\n\n lex.on("String", function (ev) {\n emitter.emit("String", ev);\n });\n\n lex.on("Number", function (ev) {\n emitter.emit("Number", ev);\n });\n\n lex.start();\n\n // Check options\n for (var name in o) {\n if (_.has(o, name)) {\n checkOption(name, state.tokens.curr);\n }\n }\n\n assume();\n\n // combine the passed globals after we\'ve assumed all our options\n combine(predefined, g || {});\n\n //reset values\n comma.first = true;\n\n try {\n advance();\n switch (state.tokens.next.id) {\n case "{":\n case "[":\n state.option.laxbreak = true;\n state.jsonMode = true;\n jsonValue();\n break;\n default:\n directives();\n\n if (state.directive["use strict"]) {\n if (!state.option.globalstrict && !state.option.node) {\n warning("W097", state.tokens.prev);\n }\n }\n\n statements();\n }\n advance((state.tokens.next && state.tokens.next.value !== ".") ? "(end)" : undefined);\n\n var markDefined = function (name, context) {\n do {\n if (typeof context[name] === "string") {\n // JSHINT marks unused variables as \'unused\' and\n // unused function declaration as \'unction\'. This\n // code changes such instances back \'var\' and\n // \'closure\' so that the code in JSHINT.data()\n // doesn\'t think they\'re unused.\n\n if (context[name] === "unused")\n context[name] = "var";\n else if (context[name] === "unction")\n context[name] = "closure";\n\n return true;\n }\n\n context = context["(context)"];\n } while (context);\n\n return false;\n };\n\n var clearImplied = function (name, line) {\n if (!implied[name])\n return;\n\n var newImplied = [];\n for (var i = 0; i < implied[name].length; i += 1) {\n if (implied[name][i] !== line)\n newImplied.push(implied[name][i]);\n }\n\n if (newImplied.length === 0)\n delete implied[name];\n else\n implied[name] = newImplied;\n };\n\n var warnUnused = function (name, tkn, type, unused_opt) {\n var line = tkn.line;\n var chr = tkn.character;\n\n if (unused_opt === undefined) {\n unused_opt = state.option.unused;\n }\n\n if (unused_opt === true) {\n unused_opt = "last-param";\n }\n\n var warnable_types = {\n "vars": ["var"],\n "last-param": ["var", "last-param"],\n "strict": ["var", "param", "last-param"]\n };\n\n if (unused_opt) {\n if (warnable_types[unused_opt] && warnable_types[unused_opt].indexOf(type) !== -1) {\n warningAt("W098", line, chr, name);\n }\n }\n\n unuseds.push({\n name: name,\n line: line,\n character: chr\n });\n };\n\n var checkUnused = function (func, key) {\n var type = func[key];\n var tkn = func["(tokens)"][key];\n\n if (key.charAt(0) === "(")\n return;\n\n if (type !== "unused" && type !== "unction")\n return;\n\n // Params are checked separately from other variables.\n if (func["(params)"] && func["(params)"].indexOf(key) !== -1)\n return;\n\n // Variable is in global scope and defined as exported.\n if (func["(global)"] && _.has(exported, key)) {\n return;\n }\n\n warnUnused(key, tkn, "var");\n };\n\n // Check queued \'x is not defined\' instances to see if they\'re still undefined.\n for (i = 0; i < JSHINT.undefs.length; i += 1) {\n k = JSHINT.undefs[i].slice(0);\n\n if (markDefined(k[2].value, k[0])) {\n clearImplied(k[2].value, k[2].line);\n } else if (state.option.undef) {\n warning.apply(warning, k.slice(1));\n }\n }\n\n functions.forEach(function (func) {\n if (func["(unusedOption)"] === false) {\n return;\n }\n\n for (var key in func) {\n if (_.has(func, key)) {\n checkUnused(func, key);\n }\n }\n\n if (!func["(params)"])\n return;\n\n var params = func["(params)"].slice();\n var param = params.pop();\n var type, unused_type;\n\n while (param) {\n type = func[param];\n unused_type = (params.length === func["(params)"].length - 1 ? "last-param" : "param");\n\n // \'undefined\' is a special case for (function (window, undefined) { ... })();\n // patterns.\n\n if (param === "undefined")\n return;\n\n if (type === "unused" || type === "unction") {\n warnUnused(param, func["(tokens)"][param], unused_type, func["(unusedOption)"]);\n }\n\n param = params.pop();\n }\n });\n\n for (var key in declared) {\n if (_.has(declared, key) && !_.has(global, key)) {\n warnUnused(key, declared[key], "var");\n }\n }\n\n } catch (err) {\n if (err && err.name === "JSHintError") {\n var nt = state.tokens.next || {};\n JSHINT.errors.push({\n scope : "(main)",\n raw : err.raw,\n reason : err.message,\n line : err.line || nt.line,\n character : err.character || nt.from\n }, null);\n } else {\n throw err;\n }\n }\n\n // Loop over the listed "internals", and check them as well.\n\n if (JSHINT.scope === "(main)") {\n o = o || {};\n\n for (i = 0; i < JSHINT.internals.length; i += 1) {\n k = JSHINT.internals[i];\n o.scope = k.elem;\n itself(k.value, o, g);\n }\n }\n\n return JSHINT.errors.length === 0;\n };\n\n // Modules.\n itself.addModule = function (func) {\n extraModules.push(func);\n };\n\n itself.addModule(style.register);\n\n // Data summary.\n itself.data = function () {\n var data = {\n functions: [],\n options: state.option\n };\n var implieds = [];\n var members = [];\n var fu, f, i, j, n, globals;\n\n if (itself.errors.length) {\n data.errors = itself.errors;\n }\n\n if (state.jsonMode) {\n data.json = true;\n }\n\n for (n in implied) {\n if (_.has(implied, n)) {\n implieds.push({\n name: n,\n line: implied[n]\n });\n }\n }\n\n if (implieds.length > 0) {\n data.implieds = implieds;\n }\n\n if (urls.length > 0) {\n data.urls = urls;\n }\n\n globals = Object.keys(scope);\n if (globals.length > 0) {\n data.globals = globals;\n }\n\n for (i = 1; i < functions.length; i += 1) {\n f = functions[i];\n fu = {};\n\n for (j = 0; j < functionicity.length; j += 1) {\n fu[functionicity[j]] = [];\n }\n\n for (j = 0; j < functionicity.length; j += 1) {\n if (fu[functionicity[j]].length === 0) {\n delete fu[functionicity[j]];\n }\n }\n\n fu.name = f["(name)"];\n fu.param = f["(params)"];\n fu.line = f["(line)"];\n fu.character = f["(character)"];\n fu.last = f["(last)"];\n fu.lastcharacter = f["(lastcharacter)"];\n data.functions.push(fu);\n }\n\n if (unuseds.length > 0) {\n data.unused = unuseds;\n }\n\n members = [];\n for (n in member) {\n if (typeof member[n] === "number") {\n data.member = member;\n break;\n }\n }\n\n return data;\n };\n\n itself.jshint = itself;\n\n return itself;\n}());\n\n// Make JSHINT a Node module, if possible.\nif (typeof exports === "object" && exports) {\n exports.JSHINT = JSHINT;\n}\n\n//@ sourceURL=/src/stable/jshint.js')); -require("/src/stable/jshint.js");JSHINT=require("/src/stable/jshint.js").JSHINT})(); -/* END INSERT */ - -realExports.JSHINT = JSHINT; -exports = realExports; - -// jshint-endignore - -})(typeof exports == "undefined" ? (typeof doctest == "undefined" ? doctest = {} : doctest) : exports); diff --git a/togetherjs/tests/doctestjs/examples/examples-2.html b/togetherjs/tests/doctestjs/examples/examples-2.html deleted file mode 100644 index 2cd88c4d9..000000000 --- a/togetherjs/tests/doctestjs/examples/examples-2.html +++ /dev/null @@ -1,69 +0,0 @@ - - - - - Example - - - - - - - - - - -
-$ print('hey you')
-hey you
-$ print({'whatever': 'example', something: [1, 2, 3]});
-{something: [1, 2, 3], whatever: "example"}
-$ print(1/0)
-NaN
-$ function foo() {
->   console.log({hey: 'you'});
->   throw 'whatever';
-> }
-> foo();
-Error: whatever
-
- -
-print(1+2)
-/* =>
-   3
-*/
-
-function testme() {
-  setTimeout(function () {
-    print('hey you!');
-  }, 10);
-}
-testme();
-wait(200);
-/* => hey you! */
-
- -

-
-
-function stop() {
-  throw Abort();
-}
-
-print(1+2);
-/* => 3 */
-
-stop();
-/* => something */
-
-print(5+5);
-/* => 12 */
-
- - - - - - - diff --git a/togetherjs/tests/doctestjs/examples/examples-2.js b/togetherjs/tests/doctestjs/examples/examples-2.js deleted file mode 100644 index 6bd8c033c..000000000 --- a/togetherjs/tests/doctestjs/examples/examples-2.js +++ /dev/null @@ -1,13 +0,0 @@ -function factorial(n) { - if (n == 1) { - return n; - } - return n * factorial(n-1); -} - -print(factorial(4)); -/* => 24 */ -print(factorial(3)); -/* => - 20 -*/ \ No newline at end of file diff --git a/togetherjs/tests/doctestjs/examples/examples.html b/togetherjs/tests/doctestjs/examples/examples.html deleted file mode 100644 index f1fb64280..000000000 --- a/togetherjs/tests/doctestjs/examples/examples.html +++ /dev/null @@ -1,198 +0,0 @@ - - - - -Doctest.js: examples - - - - - - - - - - -
- -
- - - - -
- -

doctest.js - by - Ian Bicking -

- -
- - -

Doctest.js: examples

- -
- -
- -

Web Service

- -
- You can use doctest.js to test APIs; in fact, it's reasonable to use - it for acceptance tests of the web APIs themselves, not just to test - the Javascript wrappers around those APIs. -
- -
- In this example we'll access the Geonames - API. First we'll want some routines to help us later on. You - could put these into a separate .js file and include - it, but often (especially in an example ;) it's best to be fully - transparent and list all the routines out in the open... - -
-$ apiLocation = 'http://ws.geonames.org/';
-$ function query(endpoint, q) {
->   var url = apiLocation + endpoint;
->   jQuery.ajax({
->     url: url,
->     data: q,
->     dataType: "json",
->     success: Spy('success', {wait: true, ignoreThis: true}),
->     error: Spy('error')
->   });
-> }
-
- - Some things to notice about this example: - -
    -
  • apiLocation is hard coded, but you could read it - from the query string, allowing something like - test.html?apiLocation=http://localhost:8080. -
  • - -
  • We create a Spy for both success and failure, as we want to - track both of these. We could just use functions, but mostly - there's an advantage to being able to watch .called. - If you used {writes: true} you might not need the - .applies functions. -
  • - -
  • wait can be called from anywhere. That means - when you call this function doctest will wait until something is - called, and will test all the output since that time (either the - success or failure writeln()). Timeout is the other - possibility. -
  • -
- -
- -
- Now we'll use the routine to actually run a test: - -
-$ query('postalCodeSearchJSON', {postalcode: 9011, maxRows: 5});
-success({
-  postalCodes: [
-    {...}
-  ]
-}, ...)
-
- -
- -

Web Service/XML

- -
- What we do for JSON, we can also do for XML; in this case - it's just fetching a static XML Atom document. - -
-$.ajax({
-  url: './.resources/example.xml',
-  dataType: 'xml',
-  success: function (doc) {
-    gdoc = doc;
-    writeln(repr(doc));
-  },
-  error: Spy('error')
-});
-wait(0.5);
-/* =>
-<feed xmlns="http://www.w3.org/2005/Atom">
-  <title>Example Feed</title>
-  ...
-</feed>
-*/
-
-
- -
- -

Deferred/Promise

- -
- - You can also - print jquery.Deferred - (promise) objects once they've resolved, using - the printResolved() function. This also implicitly - calls wait() with the condition that all the promises - be resolved. Both errors and resolved values are printed. - -
-var def1 = $.Deferred();
-var def2 = $.Deferred();
-def1.resolve("Value 1!", "Value2!");
-setTimeout(function () {
-  def2.reject("sucka");
-}, 500);
-printResolved("def1", def1, "def2", def2);
-// => def1 Value 1! Value2! def2 Error: sucka
-  
-
- - - -
- -

Download

-

- You can download this project in either - zip or - tar formats. -

- -

You can also clone the project with Git - by running: -

$ git clone git://github.com/ianb/doctestjs
-

- - - -
- - - - - - diff --git a/togetherjs/tests/doctestjs/examples/long-running-tests.html b/togetherjs/tests/doctestjs/examples/long-running-tests.html deleted file mode 100644 index 0a701ce5d..000000000 --- a/togetherjs/tests/doctestjs/examples/long-running-tests.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Example - - - - - - - This is an example of tests that: -
    -
  • wait on long running processes
  • -
  • use an external javascript file
  • -
  • sections in the tests that will make them run in sequence
  • -
- -

-
-    
-
\ No newline at end of file
diff --git a/togetherjs/tests/doctestjs/examples/long-running-tests.js b/togetherjs/tests/doctestjs/examples/long-running-tests.js
deleted file mode 100644
index 801deef8a..000000000
--- a/togetherjs/tests/doctestjs/examples/long-running-tests.js
+++ /dev/null
@@ -1,39 +0,0 @@
-// = SECTION First
-var done = false;
-function foo() {
-    window.setTimeout(function() {
-        done = true;
-        some_var = true;
-    }, 2000);
-    wait(function(){return done;});
-}
-foo();
-print(done);
-// => false
-
-// = SECTION check output 1
-print(done);
-// => true
-print(some_var);
-// => true
-
-// = SECTION Second
-done = false;
-function foo2() {
-    window.setTimeout(function() {
-        done = true;
-        another_var = true;
-    }, 2000);
-    wait(function(){return done;});
-}
-foo2();
-print(done);
-// => false
-
-// = SECTION check output 2
-print(done);
-// => true
-print(another_var);
-// => true
-print(some_var);
-// => true
diff --git a/togetherjs/tests/doctestjs/index.html b/togetherjs/tests/doctestjs/index.html
deleted file mode 100644
index 0dc795fbd..000000000
--- a/togetherjs/tests/doctestjs/index.html
+++ /dev/null
@@ -1,208 +0,0 @@
-
-
-  
-    
-    
-    
-    Doctest.js: the humane Javascript test framework
-    
-    
-    
-    
-    
-    
-    
-    
-
-
-
-  
-  
-
-    
-
-

Doctest.js: A Humane Javascript Test Framework

- - - -
-
- -
-
- - - - - -

Doctest.js is a test runner and testing framework for Javascript.

- -

Doctest uses a novel approach to testing: example and expected result. Each test is a chunk of code that prints out results and side effects, and then the expected result is matched against that to see if the test passed or failed.

- -

An example (note: these are live examples — you can also try your own via the live demo): - -

-function capitalize(words) {
-  return words.replace(/\b[a-z]/g, function (m) {
-    return m[0].toUpperCase();
-  });
-}
-
-print(capitalize('some words'));
-// => Some Words
-
-print(capitalize('some 4ward words'));
-// => Some 4ward Words
-
-

- -

This is similar to something like assertEqual(capitalize('some words'), 'Some Words') — there's a kind of "equal" check every time you print something. Instead of doing stuff then testing every detail of what happened or what was returned, testing almost happens for you — you print out what you are interested in, and you can even punt: you can start by simply exercising everything that matters, and then inspecting that what happens is what you expect, and copying those results into the test. For instance: - -

-function getProperties(obj) {
-  var result = [];
-  for (i in obj) {
-    result.push(i);
-  }
-  result.sort();
-  return result;
-}
-
-print(getProperties({b: 1, a: 2}));
-// =>
-
-print(getProperties("a"));
-// =>
-
- -

- -

Look: the first example worked great, ["a", "b"]. The second example though wasn't right at all. "0"? Once we have a better idea we can adjust those tests: - -

-function getProperties(obj) {
-  var result = [];
-  for (i in obj) {
-    if (obj.hasOwnProperty(i)) {
-      result.push(i);
-    }
-  }
-  result.sort();
-  return result;
-}
-
-print(getProperties({b: 1, a: 2}));
-// => ["a", "b"]
-
-print(getProperties("a"));
-// => []
-
- -Well, that wasn't quite enough, but this kind of incremental development is exactly why doctest.js is so helpful: it shows you what you've done, it shows you both presence and absence. Most test frameworks only give you tools to test what is there, and not ensure that the things you don't want aren't there.

- -

Async testing made easy!

- -

The other great feature of doctest.js is how it lets you test async code.

- -

Async code is a bit tricky for all test runners. When you run the test, the result of the test won't be known until after you wait some time. Test runners might have ways to pause the test process while the test code completes. They also need ways to test that everything you wanted to happen will happen. Something that is often tricky in test code is to make sure that some callback was called — it's relatively easier to test that the callback was called correctly.

- -

Here doctest's ability to test both what's present and what's missing shines. And the test runner also gives you a great way to serialize your asynchronous tests.

- -

The core feature here is wait() — this lets you register a callback that will tell the test runner when this block of code is fully finished. An example: - -

-var now = Date.now();
-var done = false;
-
-setTimeout(function () {
-  done = true;
-  print('The timeout finished!', Date.now() - now);
-}, 300);
-
-wait(function () {return done;});
-// => The timeout finished! ...
-
- -(Notice we use ... to ignore the specific number that is printed: ellipsis act as a kind of wildcard) - -

- -

With Doctest you don't have to use fake setTimeout or fake async anywhere — you always use the real thing, and it's nearly as easy to test as async code. Frameworks that make this code synchronous are cheating you, because asynchronous code is the hardest of code and deserves to be tested accurately. But when a test framework makes synchronous easy and asynchronous hard, it's all too easy when in the depth of test development to take shortcuts.

- -

Mocking with Spy

- -

In addition there's a simply mocking framework in doctest with Spy. This lets you create a function that records over time it is called — and each time it is called it prints out how it is called. It also makes it easy to wait on the Spy to be called: - -

-var button = $('<button></button>');
-$('body').append(button);
-button.click(Spy('button.click'));
-button.click();
-Spy('button.click').wait();
-/* =>
-<button />.button.click({...})
-*/
-
- -You'll notice here that we also ignored the details of the object passed to the callback. We could use wildcards to match no part or any specific part of the argument called. Also note that the value of this is shown: people usually forget that this is a kind of implicit argument to many functions, but again doctest makes the implicit explicit. Also note that the actual output is always displayed, so you can use this to inspect aspects of the environment even if you don't want to test all fo them. -

- - -

Lineage

- -

Doctest is based on the Python doctest module originally written by Tim Peters. Spy was inspired some by Jasmine's Spy class, and carries over ideas from MiniMock.

- -

If you've used Python's doctest and found it annoying or not widely useful for doctest: doctest.js fixes all those problems: see this post for more.

- - -
-
-
- - - - -
- -
- - - - - - - - - - - - diff --git a/togetherjs/tests/doctestjs/package.json b/togetherjs/tests/doctestjs/package.json deleted file mode 100644 index 02319ced9..000000000 --- a/togetherjs/tests/doctestjs/package.json +++ /dev/null @@ -1,21 +0,0 @@ -{ - "name": "doctestjs", - "version": "0.3.0", - "main": "doctest", - "bin": { - "doctest": "./bin/doctest" - }, - "description": "Example-based testing framework", - "keywords": [], - "author": { - "name": "Ian Bicking", - "email": "ian@ianbicking.org", - "web": "http://ianbicking.org", - "twitter": "ianbicking" - }, - "license": "MIT", - "repository": { - "type": "git", - "url": "https://github.com/ianb/doctestjs.git" - } -} diff --git a/togetherjs/tests/doctestjs/reference.html b/togetherjs/tests/doctestjs/reference.html deleted file mode 100644 index 4b26bf448..000000000 --- a/togetherjs/tests/doctestjs/reference.html +++ /dev/null @@ -1,580 +0,0 @@ - - - - - - - Doctest.js: The Reference - - - - - - - - - - - - - -
-
-

Doctest.js: The Reference

- - - -
-
- -
-
- - - - - -

-Author: Ian Bicking, -ian@ianbicking.org -

- -
- -

HTML page

- -

Your HTML page needs just a couple things to set it up for a test: - -

-<html>
- <head>
-  <script src="doctestjs/doctest.js"></script>
-  <link href="doctestjs/doctest.css" rel="stylesheet">
- </head>
- <body class="autodoctest">
-
-  <pre class="test / doctest">
-    test
-  </pre>
- </body>
-</html>
-
- -

- -

Notably you need <body class="autodoctest"> to get the tests to run automatically on page load. If you were invoking doctest explicitly (like is done on the Try It page) then you might leave this off.

- -

External code

- -

Often you won't want to write your test code inside the test itself. Instead you'll want to put it in its own .js file. Especially with test the code is valid Javascript, and you will probably want syntax highlighting and all that.

- -

To do this, use an href attribtue on your <pre> element, like: - -

-<pre class="test" href="./tests.js"></pre>
-
- -The contents of the element don't matter. The test will be loaded from that location (which could be a full URL but you'll probably have cross-origin errors if you tried that — this doesn't use <script> tags to load those scripts, it uses XMLHttpRequest). Note that the class is still required!

- -

You can also load it from a variable location, using query string parameters to find the file. The most common pattern would be like this: - -

-<pre class="test" data-href-pattern="./{test-name|default.js}"></pre>
-
- -Any URL parameters (like {test-name}) get filled in by the query string (?test-name=example.js). By default these values can only contain letters, numbers, _, - and . — this is to protect against loading scripts from unexpected locations via an implicitly unsafe parameters in the query string.

- -

If you want to use a different restriction on a variable name, use {variable-name:regular_expression} — and use ^ and $ to make sure to match the entire string.

- -

If you want a default (if the parameter isn't set or is empty) use | to separate out the default. The default should come after the regular expression. In the example the default is default.js. Don't use extra spaces around |!

- -

Note this works well with relative addresses like <a href="?test-name=foo.js">foo.js</a>

- -

Format of test

- -

There are two formats that a doctest can take. You've probably seen the test format, there is also the more traditional doctest format.

- -

doctest format

- -

This format is used like: - -

-<pre class="doctest">
-$ first_line();
-> continuation line
-output
-</pre>
-
- -

- -

The first line of course starts wtih $ and a space. Think of it like a Unix command line. And also similar to a command line shell the continuation line is >. Any line without a leading $ or > is considered expected output.

- -

Note you can still have multiple statements using a continuation line. The only difference between two lines with $ and one with a $ followed by > is that in the latter case the two lines are executed together and the output from both is combined into what is expected.

- -

test format

- -

In this format the expected output is in a comment, like: - -

-<pre class="test">
-statement_1();
-statement_2();
-// Some other comment
-// => expected output
-
-More statements
-
-/* =>
-expected output
-*/
-
- -

- -

Basically the test is split up by using the // => comments. Each chunk is executed independently, and the test may be paused at the point where the expected output is found.

- -

Test sections

- -

If you are using external test code (and test) you can include section headers like: - -

-// == SECTION A section header
-
- -

- -

You can have one or more ='s. The text A section header will become a header. Each section header turns into a new <pre> element.

- -

Compact <pre>'s

- -

Sometimes you'll have boilerplate code that sets up the test environment, and you'll be uninterested in that code (unless it fails). This might define helper functions, or run some really routine sanity tests. You can use this to make those test blocks small: - -

-<pre class="test expand-on-failure">
-  ...
-</pre>
-
- -This will make the test 3em tall unless there's a failure, at which point it expands to its full size.

- -

Printing/writing

- -

The print() function is pretty important, of course. (Note it also was called writeln(), a name which is still supported).

- -

print() will print out any strings given to it, and the repr() of any other objects, with a space between each argument.

- -

You can make a custom repr() for any object by adding a .repr() method. It should look like: - -

-MyObject.prototype.repr = function () {
-  return '[MyObject attr: ' + repr(this.attr) + ']';
-};
-
- -

- -

If you can't add a method to your object, you can also add a stringifier using repr.register().

- -

You might do this like: - -

-repr.register(
-  function (o) {
-    return o instanceof MyClass;
-  },
-  function (o, indentString) {
-    return '[MyClass attr: ' + repr(o.attr) + ']';
-  }
-);
-
- -The first function is a test that is applied to objects. If it returns true, then the second function is called to stringify the object. If your representation uses multiple lines, then you should indent subsequent lines with the string, like return '[Beginning\n' + indentString + ' end\n' + indentString + ']';. The beginning is never indented. - -

- -

Some of the objects that have custom representations:

- -
-
XML and DOM objects
-
These are displayed as the normal serialization, e.g., <input type="text" />. XML-style endings for empty tags are always used. Attributes are alphabetized. HTML tags are upppercase and attributes are lowercase, because the DOM seems to like that.
- -
XMLHttpRequest
-
These are displayed like [XMLHttpRequest STATE [STATUS]]. The STATE is one of UNSENT, OPENED, HEADERS_RECEIVED, LOADING and DONE. The [STATUS] is only displayed if the request has finished.
- -
- -

Arrays and objects are displayed like you'd think. This might include objects that you might not think of as "plain" objects. Also the object prototype is not displayed. You'll have to use something like print(o.constructor) if you want to be specific about classes.

- -

console.log()

- -

console.log() works a lot like print(). But unlike print() it doesn't make a test pass or fail, it's purely informative.

- -

All the methods on console should work, like console.warn(). The underlying normal console function is called, but in addition on a test-by-test basis these are collected and displayed.

- -

printResolved() for Deferred and Promises

- -

There's special support for the jQuery Deferred object, and generally Promises/A.

- -

This support is through the printResolved() function, which is basically equivalent to print() except any promise arguments will be waited on to resolve (proper resolution or an error). You can use it like this:

- -
-var def = $.Deferred();
-setTimeout(function () {
-  def.resolve("Resolved!");
-});
-printResolved(def);
-// => Resolved!
-
- -

Errors are printed out with Error: before the value, and multiple arguments are printed out with spaces between them, or a placeholder if there's no arguments. For instance:

- -
-var def = $.Deferred();
-def.reject({code: 1}, "a message");
-printResolved(def);
-// => Error: {code: 1} a message
-// Or you might not have any value at all:
-def = $.Deferred();
-def.resolve();
-printResolved(def);
-// => (resolved)
-
- -

Output matching

- -

The expected output is compared with the output you actually got (received).

- -

First all whitespace is normalized. Empty lines are removed from both sides. Leading spaces are removed (i.e., indentation does not matter). Multiple spaces are normalized to a single space.

- -

There are two wildcard patterns. Ellipsis — ... — means "match anything". This will match zero character, or multiple lines. You should be careful about matching too much.

- -

The shorter wildcard is ?. This matches letters, numbers, underscore, period, and question mark. Note if you want to match a string with such characters you might have to use "?".

- -

Also a special case, " matches ' and vice versa. Since in most contexts these mean the same thing, this lets you be agnostic.

- -

When you have a large expected text and got a lot of text, and that text differs just a bit, you'll see a line-by-line comparison of the two, to help you identify exactly where the problem is. Note if you use wildcards the line-by-line comparison might be very inaccurate.

- -

wait(), async code, and pausing the tests

- -

Often you'll want to let code run for a while on its own before you are done with testing a section of code. I.e., you want to let all the requests complete, DOM elements update, and so forth.

- -

Each section of code can be paused at the end. Code cannot be paused in the middle of a section. So before the output (i.e., before // =>) the test runner can wait and collect output.

- -

Anytime you call wait() inside a section of code it tells the test runner to wait at the end of the test, either until some condition is true or until some time has passed. If the condition doesn't complete an error/timeout message is print()'d.

- -

A half baked version of what happens is this: - -

-var printed = [], waiting = null;
-function print(arg) {
-  printed.push(arg);
-}
-function wait(condition) {
-  waiting = condition;
-}
-function checkOutput() {
-  if (printed.join('\n') != expectedOutput) {
-     fail();
-  }
-}
-eval(exampleCode);
-hardTimeout = 5000; // 5 seconds
-checkTime = 100; // check every 0.1 seconds
-if (waiting === null) {
-  checkOutput();
-} else if (typeof waiting == "number") {
-  setTimeout(checkOutput, waiting);
-} else {
-  var now = Date.now();
-  function checker() {
-    if (waiting()) {
-      checkOutput();
-    } else if (Date.now() - now > hardTimeout) {
-      print("Error: timed out");
-      checkOutput();
-    } else {
-      setTimeout(checker, hardTimeout);
-    }
-  }
-  setTimeout(checker, 0);
-}
-
- -Now you practically know how to write doctest yourself! -

- -

Specifically this is how you can run wait():

- -
-
wait()
-
This makes the test pause just for a moment. It's the same as wait(0).
- -
wait(milliseconds)
-
This forces the test to pause for the given number of milliseconds. Everything is always in milliseconds.
- -
wait(condition)
-
This calls condition() frequently until it returns true.
- -
wait(condition, timeout)
-
This calls condition up until timeout milliseconds. You can use this to extend the timeout. The default timeout is 5 seconds (5000).
-
- -

Spy, mocking, and watching functions

- -

Spy is used to create a mock object/function that can be used to track calls and inspect call order and arguments.

- -

The basic use is like this: - -

-func = Spy('func');
-func(1, 2, 3);
-
-obj = {a: 1, func: func};
-obj.func();
-
-/* =>
-func(1, 2, 3)
-{a: 1, func: Spy('func')}.func()
-*/
-
- -

- -

That is, every time the Spy is called it will print out the call, all its arguments, and if there was a bound this (as in the obj.func() example) then that value will be displayed as well.

- -

Each spy is named, and if you call Spy(name) with a name that has been used before you will get the same Spy object back.

- -

Spy can be invoked in a couple ways:

- -
-
Spy(name)
-
Just creates/gets the Spy with the given name.
- -
Spy(name, {options})
-
Create the Spy with some options (as described below)
- -
Spy(name, function () {...})
-
Creates a Spy that wraps another function that you provide. The Spy will be called first, and will print out the call, and then it will call the sub-function with the same arguments and this.
- -
Spy(name, function () {...}, {options})
-
Create a Spy that wraps a function and has extra options.
- -
- -

Note that your function can raise an exception, and the Spy will pass it through (though also note the exception using console.log()). You can use this to inspect how a library reacts to exceptions in callbacks.

- -

Spy options

- -

The options available:

- -
-
applies: function () {...}
-
This is the function that will be called when the Spy is called. It's the same as passing in the function as the second argument.
- -
writes: false (default true)
-
If this is false (default true) then it will not print out the call.
- -
returns: value (default undefined)
-
This is what the Spy returns when called (assuming you did not use applies). By default it simply returns undefined, which is what a function returns when you have no explicit return statement.
- -
throwError: exceptionObject
-
If given, when the Spy is called it'll do throw exceptionObject
- -
ignoreThis: true (default false)
-
If true, then this won't be printed out regardless of whether it is bound. This is useful when a library binds this carelessly.
- -
wrapArgs: true (default false)
-
If true then wrapping will be forced when the arguments are printed out. Otherwise wrapping is only applied if an argument is longer than 80 columns (the default for printing generally).
- -
wait: true (default false)
-
This is equivalent to calling Spy(name).wait(). You can also pass in a number, which will be the millisecond timeout, e.g., Spy(name, {wait: 10000}) to wait for 10 seconds for the Spy to be called.
- -
methods: {...}
-
Equivalent to calling Spy.methods({...}). See below for details.
- -
- -

You can also change Spy.defaultOptions if you want to override one option by default, for instance to turn off printing or ignore this.

- -

Spy methods

- -

Several methods are available:

- -
-
Spy().wait([timeout])
-
This makes the test pause until the Spy has been called. Sometimes you must use the method instead of Spy(name, {wait: true}). An example: - -
-SomeAPI.onload = Spy('SomeAPI.onload', function (data) {
-  SomeOtherAPI.save(data, Spy('SomeOtherAPI.save'));
-};
-Spy('SomeOtherAPI.save').wait();
-/* =>
-SomeAPI.onlaod({...})
-SomeOtherAPI.save()
-*/
-
- - In this case the Spy is created inside another method, and that method is not called right away. wait: true doesn't work in this case. Instead you should call Spy(name).wait() later. Since names are unique, this will be the same Spy object as referenced earlier. -
- -
Spy.on("obj.attr", [applies/options])
-
This replaces the attribute attr on the object obj with a Spy. The object must be defined at the top level (i.e., eval("obj") must return the object). This is basically the same as: - -
-obj = eval("obj");
-spy = Spy("obj.attr", [applies/options]);
-obj.attr = spy;
-
- -
- -
Spy.on(obj, "obj.attr", [applies/options])
-
This is the same as the previous form, except for use when obj is not a global variable.
- -
aSpy.formatCall()
-
When the Spy is called, generally this does: - -
-print(aSpy.formatCall());
-
- - If you use writes: false then this might be helpful. -
- -
aSpy.method("methodName", [applies/options])
-
This creates an attribute aSpy.methodName and assigns a Spy to that attribute. You may give the normal constructor arguments.
- -
aSpy.methods({methodName: [true or options], ...})
-
This creates multiple attributes at once. You may use {methodName: true} if you have no options to pass in.
- -
- -

Spy attributes

- -

Spies have several attributes to inspect how they have been called:

- -
-
aSpy.self and aSpy.selfList
-
This is the value of this as the spy was called. As the spy is called multiple times each this value is appended to selfList, forming a history.
- -
aSpy.args and aSpy.argList
-
This is the list of arguments that the function was called with. .argList gives the history of past calls.
- -
- - -

Aborting your tests

- -

Tests often has prerequesites. Perhaps some browsers aren't supported. Maybe you need a server setup. Normally doctest will run through all the tests regardless of failures, but when basic prerequesites are missing this creates lots of chatter and failures with no purpose.

- -

To stop the tests from running call Abort(). This will still run the rest of the test block (up until the next // =>).

- -

jshint

- -

A helper is provided to help you run JSHint regularly on your code. Just do this:

- -
-jshint("source-filename.js", [jshint options]);
-
- -

You can give a full URL, but you can also just give the filename. When given a filename then all the <script> tags are searched for that filename. The source is fetched and JSHint is run on that source.

- -

You may give options to suppress or enforce checks. In addition you may list the known issues that you wish to ignore: issues are printed out in order, and are matched like any other text. You might want to use this to simply see the errors without checking them for anything in paticular: - -

-jshint("source-filename");
-// => ...
-
- -

- -

NosyXMLHttpRequest

- -

Sometimes you may want to watch the progress of XMLHttpRequest requests — both how the request is constructed and its result. You can use NosyXMLHttpRequest to wrap request objects.

- -

You probably want to use it like: - -

-XMLHttpRequest = NosyXMLHttpRequest.factory("request");
-
-

- -

The name will be used when showing output (e.g., request.setRequestHeader('X-Something', 'value')).

- - -

Node.js

- -

Doctest.js has some Node support. You must use the comment-based test format in stand-alone Javascript files. Then:

- -
-$ npm install -g doctestjs
-$ doctest test.js
-
- -

This will print a success or failure message, and will exit with a code (the number of failures) if the test does not pass.

- -

If you do not wish to install the package globally, do:

- -
-$ npm install doctestjs
-$ node_modules/.bin/doctest test.js
-
- - - -
- -
-
- - - - - - - - - -
- -
- - - - - - - - - - - - diff --git a/togetherjs/tests/doctestjs/try.html b/togetherjs/tests/doctestjs/try.html deleted file mode 100644 index 18f52091e..000000000 --- a/togetherjs/tests/doctestjs/try.html +++ /dev/null @@ -1,125 +0,0 @@ - - - - - - - Doctest.js: Try It! - - - - - - - - - - - - - - -
-
-

Doctest.js: Try It!

- - - -
-
- -
-
- - - - -
- - - - - - -
- -
- -
-
-

-      
-
- - - -
- -
- - - - - - - - - - - - diff --git a/togetherjs/tests/doctestjs/tutorial.html b/togetherjs/tests/doctestjs/tutorial.html deleted file mode 100644 index 75355490e..000000000 --- a/togetherjs/tests/doctestjs/tutorial.html +++ /dev/null @@ -1,659 +0,0 @@ - - - - - - - A doctest.js tutorial - - - - - - - - - - - - - -
-
-

Doctest.js: A Tutorial

- - - -
-
- -
-
- - - - - - - -
- -
-
-
-

What's it like?

-
- -

- -So you've decided to finally get religion when it comes to testing your Javascript code? Or, you feel like testing just isn't as easy as it could be, and want to find a better way to test your Javascript code? Or even: you've thought about or tried doing Test Driven Development but you've found it hard to get going? Let's do this... - -

- -

- -Doctest.js is basically example code and then expected output. This is really what most tests look like, but instead of lots of assertEqual(example, expected) this example/expected combination is embedded into the structure of the test. - -

- - - -

- -I'm going to get right into how the test code looks, but to actually use doctest.js you have to setup an HTML file in a specific format. That is described later in the HTML section. - -

- -

- -The structure looks like something you've probably seen before. We add one new function, print(), that works a lot like console.log(). Then we have a comment that shows what we expect to be output. A really simple example: - -

-function factorial(n) {
-  if (typeof n != "number") {
-    throw "You must give a number";
-  }
-  if (n <= 0) {
-    return 1;
-  }
-  return n * factorial(n-1);
-}
-
-print(factorial(4))
-// => 25
-    
- -

- -

- -See what I did there? 25 is totally the wrong answer! Also see what happened, the test just ran and told us so! There's also a summary of all the tests; if you do nothing it shows up at the top of the page, but in the interest of introducing the summary, here it is: - -

- -

- -

- -You'll notice it shows a failure (or more than one — it's the summary for all the examples in this tutorial). It also has a link to each failure, so you can jump to the problematic section. - -

- - - -

- -Let's look at what we did there: print(factorial(4)) and // => 25 — the output is just a comment that starts with =>. - -

- -
- -
-
-

Testing for error conditions

-
- -

- -You can also test errors: - -

-print(factorial(null));
-// => Error: You must give a number
-
- -When an exception is thrown it will print out Error: (error text) which you can match against. This way you can test for error conditions just like you test how "correct" invocations work. Note that the print() isn't really necessary here, you could do this just as well: - -
-factorial(null);
-// => Error: You must give a number
-
- -

- -
- -
-
-

print() and output matching

-
- - - -

- -print() pretty-prints things. This is important, because you have to "expect" the same output that print() produces. You can give multiple arguments, like with console.log. - -

-print({someProperty: 123, something: {a: 1, b: 2}, "foo": 123.1032, "another-property": [1,2,3,4]});
-/* =>
-{
-  "another-property": [1, 2, 3, 4],
-  foo: 123.1032,
-  someProperty: 123,
-  something: {a: 1, b: 2}
-}
-*/
-
- -You might notice that the attributes are alphabetized and are quoted only when necessary. If it's a small object it stays on one line: - -
-print({someProperty: 123});
-// => {someProperty: 123}
-
- -

- -

- -But sometimes the output is unpredictable; or rather you can predict it will change. When that's the case you can basically put a wildcard in the expected output: ... — that will match anything, including multiple lines. In addition you can use ? to match one word-like-thing (a number, symbol, etc; not " or whitespace or other symbols). You can use it like this: - -

-print({
-  date: new Date(),
-  timestamp: Date.now()
-});
-
-// => {date: ..., timestamp: ?}
-
- -

- -

- -You might notice that it passes, but you still get to see the actual output. This is a great way to show information that you can review, without actually testing. For instance, you might be testing something that connects to a server, in that case you might want to do this: - -

-var server = {url: "http://localhost:8000"} // or some calculated value
-print(server.url);
-// => ...
-
- -Now if everything seems breaky you can be 100% sure of what server you are connecting to. - -

- -

- -If you have a variable that is dynamic but you still care about the value, you should do something like this: - -

-var date = Date.now();
-print(date == date, date);
-// => true ...
-
- -Think of this pattern of print(x == y) as a kind of assertEqual() equivalent. - -

- -
- -
-
-

Testing async code

-
- - - -

- -This is all well and good, but lots of code in Javascript is asynchronous, meaning that you don't just call a function that returns a value. Doctest.js has an answer to that too: a great answer! - -

- -

- -For our example we'll use XMLHttpRequest, a common source of asynchronosity. We'll test a request (just a loopback request, but if you are testing a foreign service you'd need CORS access). When we instantiate and setup the request we don't have anything really to test — we want to test what happens when the request completes. - -

- -

- -To do this we'll use wait() — when this function is called the test runner will wait at the point where it sees // =>, for a certain amount of time or until a certain condition is met. Only then will it compare all the output that has happened to what was expected, and run the next chunk of test. - -

- -

- -You can use this like: wait(function () {return true when done}) or wait(millisecondsToWait). We'll use the first form, which is almost always better, since it allows the test to continue more quickly. Tests also always time out eventually (by default the timeout is 5000 milliseconds, i.e., 5 seconds — by convention everything in Javascript is in milliseconds). - -

-var endpoint = location.href;
-print(endpoint);
-// => ...
-
-var req = new XMLHttpRequest();
-req.open("GET", endpoint);
-req.onreadystatechange = function () {
-  if (req.readyState != 4) {
-    // hasn't actually finished
-    return;
-  }
-  print("Result:", req.status, req.getResponseHeader('content-type'));
-};
-req.send();
-
-wait(function () {return req.readyState == 4;});
-
-print("Current state:", req.readyState);
-
-/* =>
-Current state: 1
-Result: 200 text/html
-*/
-
- -I put in something tricky there to try to clarify what wait() really does. You'll notice there's a call to wait() that makes sure that req.readyState == 4 (that's the code that means the request is finished). But right after when we do print(req.readyState) it shows a readyState of 1. That's because the entire block is printed (from the previous // => output up until the next one). But the test runner keeps collecting output and doesn't run the next section until that wait() clause returns true. - -

- -

- -Another thing to note is that wait() needs to be called when that block of code is run — it can't be inside a function that isn't called. That said, if you write test helper functions (and you should!), it often works well to put those calls in the helper function. We'll see an example of that next... - -

- -
- -
-
-

The Spy

-
- - - -

- -Note: the next example will use some jQuery, just for the heck of it, though there is no special support for jQuery or other frameworks in Doctest. - -

- -

- -If you use these tools you might end up writing code like this quite a lot: - -

-// We've embedded a button just below this element
-var button = $('#example-button');
-// Just to highlight what we're working with:
-button.css({border: '1px dotted #f00'});
-button.click(function () {
-  print('Button clicked');
-});
-
-// Now we test that our event handler gets called when we do an artificial click of the button:
-button.click();
-// => Button clicked
-
- - - -

- -

- -But maybe we are curious about the arguments passed to that handler — even though we ignored the arguments, there was one passed. And we might want to show what this is bound to; this is kind of like an invisible extra argument passed to every function invocation. We could make a fancier print() statement there. But instead, there's also a handy tool for tracking calls: Spy. - -

- -

- -An example: - -

-var button = $('#example-button2');
-button.css({border: '1px dotted #00f'});
-button.click(Spy('button.click'));
-button.click();
-
-// => ...
-
- - - -

- -

- -That's a lot more information! Let's break it down: - -

-<button id="example-button2" style="border: 1px solid rgb(0, 0, 255); " type="button">Example Button 2</button>.button.click({ ...
-
- -There's two bits of information here. The first is the value (in blue) of this, which is the #example-button2 element. You'll notice it shows the HTML of the element. If you want Spy to ignore this you can use Spy('button.click', {ignoreThis: true}). - -

- -

- -The second value (in green) is the name we gave the Spy when we created it. Note that Spy names are also identifiers, that is, Spy('button.click') === Spy('button.click'). - -

- -

- -Next of course is all the arguments. There's a lot of arguments there. They are... interesting. You'll notice some references to ...recursive... which is what you get when you have self-referencing data structures. But maybe you want to test just a little of that structure without testing all of it. You might do something like this: - -

-// Spy('button.click') fetches the same Spy we were using before, which still has all its call information
-// .formatCall() shows the way the Spy was last called.
-print(Spy('button.click').formatCall());
-
-/* =>
-<button...</button>.button.click({
-  currentTarget: <button...
-  ...
-  timeStamp: ?,
-  type: "click"
-})
-*/
-
-
- -So we've tested that the type is click, that it has a timeStamp (though not the value) and that the currentTarget is a button (presumably the button we bound it to). We still get to see all the other information, we just aren't testing it. This can be helpful in the future when you realize there's more you want to test — you can look at the test output and transcribe more into the test. Or when something fails later you might want to inspect that output to make sure everything is what you expect (and when you see something unexpected that's also a great time to expand your test). - -

- -

- -Spies have a bunch of options, and act as a kind of mock object as well. You can pass in options as the second argument, like Spy('name', {options...}). Some highlights: - - -

-
applies
-
This is a function that the Spy "wraps". So if you do Spy('click', {applies: function (event) {this.remove(); return false;}}) then you'll get the same output printed, but you'll also run this.remove().
- -
writes
-
If you set this to false then it won't automatically print out the calls. The values of the calls will still be recorded, and you can use aSpy.formatCall() to see them.
- -
ignoreThis
-

Lots of code binds this without intending too. It's really easy in Javascript to do this. For instance, if you do handlers[i]() then this will be handlers. (Instead you might do var handler = handlers[i]; handler())

- -

Anyway, sometimes you don't care about this, and using {ignoreThis: true} lets you do that. -

- -
returns
-
If you want the Spy to return a value when its called, give the value here. Normally it returns undefined.
- -
throwError
-
This makes the Spy throw the given error anytime it is called.
- -
wait
-
If you use Spy('name', {wait: true}) then the test will wait until the Spy has been called. This is a pretty common pattern. It's basically the same as Spy('name').wait().
- -
- -

- -

- -You can set values like Spy.defaultOptions.writes = false if you want to set one of these by default. - -

- -

- -If you want to inspect how the Spy has been called, you can check a few attributes: - -

- -
.called
-
True once this Spy has been called.
- -
.self and .selfList
-
This is the value of this, or .selfList contains a history for each call.
- -
.args and .argList
-
The arguments the function was called with, or .argList is a history of arguments.
- -
- -

- -
- -
-
-

console.log

-
- -

- -This isn't a feature you have to do anything about, it's just there for you, so I'm just going to point it out. - -

- -

- -When you use console.log (or any of its friends, like console.warn) those messages will be captured (in addition to going to the log as normal), and the output will be shown in the specific test where they happened. A quick example: - -

-function enumProps(object) {
-  console.log('obj', object);
-  var result = {}
-  for (var attr in object) {
-    if (typeof object[attr] == "number" && attr.toUpperCase() == attr) {
-      result[object[attr]] = attr;
-    }
-  }
-  return result;
-}
-
-print(enumProps($('#example-button')[0]));
-
-// => {...}
-
-
- -You can think of it a little like print() goes to stdout, and console.log() goes to stderr. - -

- -
- -
-
-

Giving Up

-
- -

- -Tests often require some feature or setup to be usable at all. When it's not setup right you'll just get a bunch of meaningless failures. For this reason there's a way to abort all your tests. If you call Abort() then no further tests will be run. If you want to connect to a server, for instance, you might check that the server is really there, and if not then just abort the rest of the tests. For example: - -

-$.ajax({
-  url: '/ping',
-  success: Spy('ping', {wait: true, ignoreThis: true}),
-  error: function () {
-    Abort("Server isn't up");
-  }
-});
-
-// => ping(...)
-
- -

- -
- -
-
-

Setting Up Your HTML

-
- - - -

- -I wanted to show you all the cool features of doctest first, but you can't actually use any of them unless you set up a test runner page. Luckily the page is pretty simple. Let's say you've put doctest.js into doctest/: - -

-<DOCTYPE html>
-<html>
-  <head>
-    <meta charset="UTF-8">
-    <title>My Test</title>
-    <script src="doctest/doctest.js"></script>
-    <link href="doctest/doctest.css" rel="stylesheet">
-    <script src="mylibrary.js"></script>
-  </head>
-  <body class="autodoctest">
-
-  A test:
-
-<pre class="test">
-test goes here
-</pre>
-
-  </body></html>
-
- -

- -

- -Mostly it's just boilerplate: you have to include doctest.js and doctest.css and of course any libraries or dependencies of the thing you are testing. You also must use <body class="autodoctest"> — that's what tells doctest.js you want to find and run tests right away. - -

- -

- -Each test then is in a <pre class="test">. You might not want to actully write your tests inside the HTML, and instead put them in a separate Javascript file. To do that use: - -

-<pre class="test" href="./my_tests.js"></pre>
-
- -This will load the test code from ./my_tests.js and inline it into the element. This is how I personally write most of my tests, though when moving between a narrative and tests (as I am doing in this tutorial) it is nice to keep the tests together with the descriptions. - -

- -

- -Note that specifically when you use href="URL.js" you can split the tests into sections by including the comment // == SECTION Your Section Header Name in the included file, and you'll get multiple elements with headers automatically. - -

- -

- -A pass/fail summary is automatically added to the top of the page, though you can use <div id="doctest-output"></div> to position it someplace specific (as we did in this tutorial). - -

- -
- - -
-
-

Feedback?

-
- -

Was something in this tutorial confusing? Is there a testing problem or pattern you think this tutorial should talk about? Please give feedback in the form of an new issue. Thanks!

- -
- - -

Live Demo...

- -
- - - - - -
- -
- - - - - - - - - - - - diff --git a/togetherjs/tests/func_ace.js b/togetherjs/tests/func_ace.js deleted file mode 100644 index 8b136a240..000000000 --- a/togetherjs/tests/func_ace.js +++ /dev/null @@ -1,127 +0,0 @@ -// =SECTION Setup - -$("#fixture").empty(); -var aceSrc = "./ace.js"; -var script = $(" - - - - - - - - - - - - - - - - -
- Functional tests: -
    -
  1. misc
  2. -
  3. notifications
  4. -
  5. walkthrough
  6. -
  7. peer status
  8. -
  9. forms
  10. -
  11. ace
  12. -
  13. codemirror
  14. -
-
- Unit tests: -
    -
  1. storage
  2. -
  3. resolves
  4. -
  5. elementFinder
  6. - -
  7. ot text
  8. -
  9. linkify
  10. -
  11. console
  12. -
  13. misc (small)
  14. -
-

- Manual testing
- - Manual tests -

-
- -
xxx
-
- -
-
- - diff --git a/togetherjs/tests/interactive.js b/togetherjs/tests/interactive.js deleted file mode 100644 index 145eb86bf..000000000 --- a/togetherjs/tests/interactive.js +++ /dev/null @@ -1,235 +0,0 @@ -// =SECTION Setup - -// Local config overrides -var config = localStorage.getItem("interactiveOverrides"); -if (config) { - config = JSON.parse(config); - window.TogetherJSConfig = config; - for (var a in config) { - TogetherJS.config(a, config[a]); - } -} - -Test.require("ui", "chat", "util", "session", "jquery", "storage", "peers", "cursor", "windowing", "elementFinder", "templates-en-US"); -// => Loaded ... - -printChained( - Test.resetSettings(), - storage.settings.set("seenIntroDialog", true), - storage.settings.set("seenWalkthrough", true), - storage.settings.set("dontShowRtcInfo", true), - Test.startTogetherJS()); - -// => ... - -function addPeer(id) { - var name = "Faker"; - if (id) { - name += " " + id; - } - id = id || 'faker'; - var color = "#" + Math.floor(Math.random() * 0xffffff).toString(16); - Test.newPeer({ - name: name, - color: color, - clientId: id - }); - var len = peers.getAllPeers().length; - var pageHeight = $(document).height(); - var left = (len * 40) % $(window).width(); - var top = len % 2 ? (len * 10) : (pageHeight - 100 - len * 10); - Test.incoming({ - type: "cursor-update", - top: top, - left: left, - clientId: id - }); - Test.incoming({ - type: "scroll-update", - clientId: id, - position: { - location: "body", - offset: 20, - absoluteTop: 20, - documentHeight: $(document).height() - } - }); -} - -function pick(seq) { - if (! seq) { - seq = peers.getAllPeers(true); - } - return seq[Math.floor(Math.random() * seq.length)]; -} - -addPeer(); -// => ... - -// =SECTION Controls - -Test.addControl( - - $("").click(function () { - //$(".togetherjs-cursor").fadeOut(); - }), - - $("").click(function () { - //$(".togetherjs-cursor").fadeOut(); - }), - - $("").click(function () { - addPeer(); - }), - $("").click(function () { - addPeer("faker-" + Math.floor(Math.random() * 1000)); - }), - $('').click(function () { - var peer = pick(); - Test.incoming({ - type: "bye", - clientId: peer.id - }); - - }), - $('').click(function () { - var peer = pick(); - Test.incoming({ - type: "bye", - clientId: peer.id, - reason: "declined-join" - }); - }) -); - -Test.addControl( - $('').keypress(function (ev) { - if (ev.which == 13) { - Test.newPeer({url: ev.target.value}); - ev.target.value = ""; - } - }), - $('').click(function () { - Test.newPeer({url: "http://example.com/?" + Date.now()}); - }), - $('').click(function () { - var url = location.href.replace(/\#.*/, ""); - Test.newPeer({url: url}); - }) -); - -Test.addControl($('').click(function () { - var peer = pick(); - Test.incoming({ - type: "url-change-nudge", - clientId: peer.id, - url: peer.url, - to: peers.Self.id - }); -})); - -var focused = {}; -Test.addControl($('').click(function () { - var peer = pick(); - if (focused[peer.id]) { - Test.incoming({ - type: "form-focus", - element: null, - clientId: peer.id, - url: peer.url - }); - focused[peer.id] = null; - return; - } - var els = $("#controls textarea:visible, #controls input:visible, #controls select:visible"); - - var el = $(els[Math.floor(els.length * Math.random())]); - focused[peer.id] = el; - Test.incoming({ - type: "form-focus", - element: elementFinder.elementLocation(el), - clientId: peer.id, - url: peer.url - }); -})); - -Test.addControl($('').keypress(function (event) { - var el = $(event.target); - if (event.which == 13) { - var peer = pick(); - Test.incoming({ - type: "chat", - text: el.val(), - messageId: 'message-' + Date.now(), - clientId: peer.id - }); - el.val(""); - } -})); - -var el = $('
' + - '' + - '
'); -el.find("#idle-check").change(function (event) { - if (event.target.checked) { - TogetherJSTestSpy.setIdleTime(100); - } else { - TogetherJSTestSpy.setIdleTime(3*60*1000); - } -}); - -el.find("#expire-check").change(function (event) { - if (event.target.checked) { - TogetherJSTestSpy.setByeTime(10*1000); - } else { - TogetherJSTestSpy.setIdleTime(10*60*1000); - } -}); - -el.find("#include-hash").change(function (event) { - var config = localStorage.getItem("interactiveOverrides"); - if (config) { - config = JSON.parse(config); - } else { - config = {}; - } - config.includeHashInUrl = event.target.checked; - localStorage.setItem("interactiveOverrides", JSON.stringify(config)); - alert("Reload required"); -}); -if (TogetherJS.config.get("includeHashInUrl")) { - el.find("#include-hash").prop("checked", true); -} - -Test.addControl(el); - -Test.addControl( - $('').click(function () { - Test.incoming({ - type: "idle-status", - idle: "inactive", - clientId: "faker" - }); - }), - $('').click(function () { - Test.incoming({ - type: "idle-status", - idle: "active", - clientId: "faker" - }); - }), - $('').click(function () { - Test.incoming({ - type: "keydown", - clientId: "faker" - }); - }) -); - -Test.addControl($('').keypress(function (event) { - var el = $(event.target); - if (event.which == 13) { - TogetherJS.config("toolName", el.val() || null); - el.val(""); - } -})); diff --git a/togetherjs/tests/mobiletest.html b/togetherjs/tests/mobiletest.html deleted file mode 100644 index 4ecedd334..000000000 --- a/togetherjs/tests/mobiletest.html +++ /dev/null @@ -1,22 +0,0 @@ - - - - - Mobile test - - - - - - - - - -

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sollicitudin eu metus vel lobortis. Donec ac posuere diam, ut sollicitudin elit. Proin nulla ipsum, dignissim porttitor dui congue, euismod pellentesque quam. Nunc suscipit elit iaculis risus pharetra, vitae faucibus nulla molestie. In et ornare magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. In ultricies, orci eu condimentum commodo, est urna porta metus, sed euismod ante lorem quis sapien. Maecenas semper dui sed est imperdiet blandit.

- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sollicitudin eu metus vel lobortis. Donec ac posuere diam, ut sollicitudin elit. Proin nulla ipsum, dignissim porttitor dui congue, euismod pellentesque quam. Nunc suscipit elit iaculis risus pharetra, vitae faucibus nulla molestie. In et ornare magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. In ultricies, orci eu condimentum commodo, est urna porta metus, sed euismod ante lorem quis sapien. Maecenas semper dui sed est imperdiet blandit.

- -

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sollicitudin eu metus vel lobortis. Donec ac posuere diam, ut sollicitudin elit. Proin nulla ipsum, dignissim porttitor dui congue, euismod pellentesque quam. Nunc suscipit elit iaculis risus pharetra, vitae faucibus nulla molestie. In et ornare magna. Interdum et malesuada fames ac ante ipsum primis in faucibus. In ultricies, orci eu condimentum commodo, est urna porta metus, sed euismod ante lorem quis sapien. Maecenas semper dui sed est imperdiet blandit.

- - - diff --git a/togetherjs/tests/test_console.js b/togetherjs/tests/test_console.js deleted file mode 100644 index f99116c90..000000000 --- a/togetherjs/tests/test_console.js +++ /dev/null @@ -1,22 +0,0 @@ -/*global tjconsole */ -var origConsole = window.console; -Test.require({tjconsole: "console"}); -// => Loaded modules: console - -tjconsole.warn("hey", {a: 1, b: 2}); -tjconsole.log(1, 2, 3, {repr: function () {return (null).foo;}}); -tjconsole.trace(); -print(tjconsole.toString()); - -/* => -TogetherJS base URL: ... -User Agent: ... -Page loaded: 20...Z -Age: ... minutes -URL: ... -------+------+---------------------------------------------- - ... warn hey {"a":1,"b":2} - ... log 1 2 3 {} - ... ... - - */ diff --git a/togetherjs/tests/test_elementFinder.js b/togetherjs/tests/test_elementFinder.js deleted file mode 100644 index 3e01384f2..000000000 --- a/togetherjs/tests/test_elementFinder.js +++ /dev/null @@ -1,25 +0,0 @@ -Test.require("elementFinder"); -// => Loaded modules: ... - -var els = $(document.body).find("*"); -els.each(function (index, el) { - el = $(el); - var loc; - try { - loc = elementFinder.elementLocation(el); - } catch (e) { - console.trace(); - print("Error: cannot get location for", el, ":", e); - return; - } - var result = elementFinder.findElement(loc); - if (result != el[0]) { - print("Bad element:", loc, el); - print("Resolved to:", result); - } else { - console.log("Resolved element", loc, el); - } -}); -print("done."); - -// => done. diff --git a/togetherjs/tests/test_linkify.js b/togetherjs/tests/test_linkify.js deleted file mode 100644 index dea731516..000000000 --- a/togetherjs/tests/test_linkify.js +++ /dev/null @@ -1,19 +0,0 @@ -/*global linkify */ -Test.require("linkify"); -// => Loaded modules: linkify - -print(linkify($("this is a test"))); -// => this is a test -print(linkify($("http://foo.com test"))); -/* => - - http://foo.com -test -*/ - -print(linkify($("yahoo (http://yahoo.com)"))); -/* => -yahoo ( - http://yahoo.com -) -*/ diff --git a/togetherjs/tests/test_misc.js b/togetherjs/tests/test_misc.js deleted file mode 100644 index 40636e3be..000000000 --- a/togetherjs/tests/test_misc.js +++ /dev/null @@ -1,18 +0,0 @@ -/*global util */ -Test.require("util"); -// => Loaded modules: util - -util.assertValidUrl("http://foo.com"); -// => -util.assertValidUrl("//foobar"); -// => -util.assertValidUrl("data:image/png,asdf"); -// => -util.assertValidUrl("javascript:alert()"); -// => Error: AssertionError: ... -util.assertValidUrl("foobar.com"); -// => Error: AssertionError: ... -util.assertValidUrl("http://test.com); something: foo"); -// => Error: AssertionError: ... -util.assertValidUrl("HTTPS://test.com"); -// => diff --git a/togetherjs/tests/test_ot.js b/togetherjs/tests/test_ot.js deleted file mode 100644 index 4485d6941..000000000 --- a/togetherjs/tests/test_ot.js +++ /dev/null @@ -1,161 +0,0 @@ -// =SECTION Setup expand-on-failure - -Test.require("ot", "util", "randomutil"); -// => Loaded... - -var generator = randomutil(1); - -var Client = util.Class({ - - constructor: function (clientId, hub, text) { - this.clientId = clientId; - this.hub = hub; - this.text = text || ""; - this.history = ot.History(clientId, this.text); - this.queuedChanges = []; - this.hub.addClient(this); - }, - - makeChange: function () { - var delta = ot.TextReplace.random(this.text, generator); - var orig = this.text; - this.text = delta.apply(this.text); - var change = this.history.addDelta(delta); - this.text = this.history.getState(); - this.queuedChanges.push(change); - console.log("internal:", change+"", this.clientId); - console.log(" from:", orig); - console.log(" to:", this.text); - }, - - incoming: function (change) { - console.log("incoming change", change); - var delta = this.history.add(change); - console.log("resulting delta", delta+""); - var orig = this.text; - console.log("applying", JSON.stringify(this.text), delta+""); - this.text = delta.apply(this.text); - this.text = this.history.getState(); - console.log(" change:", change+"", this.clientId); - console.log(" from:", orig); - console.log(" to:", this.text); - if (this.text != this.history.getState()) { - console.log("INVALID FORWARD DELTA"); - console.log("Delta applied:", delta+""); - console.log("Produces", JSON.stringify(this.text), "instead of", JSON.stringify(this.history.getState()), "from", JSON.stringify(orig)); - } - }, - - flush: function () { - this.queuedChanges.forEach(function (c) { - this.hub.send(c, this); - }, this); - this.queuedChanges = []; - } -}); - -var hub = { - - clients: [], - - addClient: function (client) { - this.clients.push(client); - }, - - send: function (change, client) { - util.assert(client); - for (var i=0; i - -// =SECTION Test fixture expand-on-failure - -var error; - -function check() { - hub.flushAll(); - var text; - var textId; - var history; - hub.clients.forEach(function (c) { - console.log("----------------------------------------"); - c.history.logHistory(); - }); - for (var i=0; i done. diff --git a/togetherjs/tests/test_ot_text.js b/togetherjs/tests/test_ot_text.js deleted file mode 100644 index 323682c60..000000000 --- a/togetherjs/tests/test_ot_text.js +++ /dev/null @@ -1,316 +0,0 @@ -// =SECTION Setup expand-on-failure - -Test.require("ot", "util", "randomutil"); -// => Loaded... - -var generator = randomutil(1); -generator.defaultChars = "XYZ/_ "; - -function run() { - var base = "abcdefg"; - console.log("Start:", JSON.stringify(base) + "/" + base.length); - var delta1 = ot.TextReplace.random(base, generator); - var delta2 = ot.TextReplace.random(base, generator); - console.log("Delta1:", delta1+""); - console.log("Delta2:", delta2+""); - var sub1 = delta1.transpose(delta2); - console.log("Translated", delta1, "to", sub1[0]); - var text1a = sub1[0].apply(delta2.apply(base)); - console.log(" trans", delta2, "to", sub1[1]); - var text1b = sub1[1].apply(delta1.apply(base)); - console.log("first text:", JSON.stringify(text1a), JSON.stringify(text1b)); - if (text1a != text1b) { - print("Not equal"); - throw new Error("Error; not equal"); - } - var sub2 = delta2.transpose(delta1); - console.log(" Translate", delta2, "to", sub2[0]); - var text2a = sub2[0].apply(delta1.apply(base)); - console.log(" trans", delta1, "to", sub2[1]); - var text2b = sub2[1].apply(delta2.apply(base)); - console.log("second text:", JSON.stringify(text2a), JSON.stringify(text2b)); - if (text2a != text2b) { - print("Not equal"); - throw new Error("Error; not equal"); - } - console.clear(); -} - -// => - -// =SECTION Simple Test Setup expand-on-failure - -function r(start, length, text) { - return ot.TextReplace(start, length, text); -} - -function trans(text, d1, d2) { - var sub = d1.transpose(d2); - console.log(JSON.stringify(text), "+", d1+"", "->", JSON.stringify(d1.apply(text))); - console.log(JSON.stringify(text), "+", d2+"", "->", JSON.stringify(d2.apply(text))); - var d1prime = sub[0]; - var d2prime = sub[1]; - if (d1.equals(d1prime)) { - print(d1, "stays same"); - } else { - print(d1, "becomes", d1prime); - } - if (d2.equals(d2prime)) { - print(d2, "stays same"); - } else { - print(d2, "becomes", d2prime); - } - var text1 = d1prime.apply(d2.apply(text)); - var text2 = d2prime.apply(d1.apply(text)); - print(d2, "+", d1prime, "->", text1); - print(d1, "+", d2prime, "->", text2); - if (text1 != text2) { - print("Error: text mismatch"); - } -} - -// =SECTION Two insertions - -var text = "abcdef"; -var ins1 = r(0, 0, "X"); -var ins2 = r(1, 0, "Y"); - -trans(text, ins1, ins2); -/* => -[insert "X" @0] stays same -[insert "Y" @1] becomes [insert "Y" @2] -[insert "Y" @1] + [insert "X" @0] -> XaYbcdef -[insert "X" @0] + [insert "Y" @2] -> XaYbcdef - */ - -trans(text, ins2, ins1); -/* => -[insert "Y" @1] becomes [insert "Y" @2] -[insert "X" @0] stays same -[insert "X" @0] + [insert "Y" @2] -> XaYbcdef -[insert "Y" @1] + [insert "X" @0] -> XaYbcdef - */ - -ins1 = r(0, 0, "X"); -ins2 = r(0, 0, "Y"); - -trans(text, ins1, ins2); -/* => -[insert "X" @0] becomes [insert "X" @1] -[insert "Y" @0] stays same -[insert "Y" @0] + [insert "X" @1] -> YXabcdef -[insert "X" @0] + [insert "Y" @0] -> YXabcdef - */ - -// As we see in this example, precedence matters (YX vs XY): -trans(text, ins2, ins1); -/* => -[insert "Y" @0] becomes [insert "Y" @1] -[insert "X" @0] stays same -[insert "X" @0] + [insert "Y" @1] -> XYabcdef -[insert "Y" @0] + [insert "X" @0] -> XYabcdef - */ - - -// =SECTION Two Deletions - -text = "abcdef"; -var del1 = r(0, 1, ""); -var del2 = r(1, 1, ""); - -trans(text, del1, del2); -/* => -[delete 1 chars @0] stays same -[delete 1 chars @1] becomes [delete 1 chars @0] -[delete 1 chars @1] + [delete 1 chars @0] -> cdef -[delete 1 chars @0] + [delete 1 chars @0] -> cdef - */ - -trans(text, del2, del1); -/* => -[delete 1 chars @1] becomes [delete 1 chars @0] -[delete 1 chars @0] stays same -[delete 1 chars @0] + [delete 1 chars @0] -> cdef -[delete 1 chars @1] + [delete 1 chars @0] -> cdef - */ - -trans(text, r(0, 2, ""), r(0, 1, "")); -/* => -[delete 2 chars @0] becomes [delete 1 chars @0] -[delete 1 chars @0] becomes [no-op] -[delete 1 chars @0] + [delete 1 chars @0] -> cdef -[delete 2 chars @0] + [no-op] -> cdef - */ - -trans(text, r(0, 1, ""), r(1, 1, "")); -/* => -[delete 1 chars @0] stays same -[delete 1 chars @1] becomes [delete 1 chars @0] -[delete 1 chars @1] + [delete 1 chars @0] -> cdef -[delete 1 chars @0] + [delete 1 chars @0] -> cdef - */ - -trans(text, r(0, 4, ""), r(1, 1, "")); -/* => -[delete 4 chars @0] becomes [delete 3 chars @0] -[delete 1 chars @1] becomes [no-op] -[delete 1 chars @1] + [delete 3 chars @0] -> ef -[delete 4 chars @0] + [no-op] -> ef - */ - -trans(text, r(1, 1, ""), r(0, 4, "")); -/* => -[delete 1 chars @1] becomes [no-op] -[delete 4 chars @0] becomes [delete 3 chars @0] -[delete 4 chars @0] + [no-op] -> ef -[delete 1 chars @1] + [delete 3 chars @0] -> ef - */ - -trans(text, r(0, 3, ""), r(2, 4, "")); -/* => -[delete 3 chars @0] becomes [delete 2 chars @0] -[delete 4 chars @2] becomes [delete 3 chars @0] -[delete 4 chars @2] + [delete 2 chars @0] -> -[delete 3 chars @0] + [delete 3 chars @0] -> - */ - -trans(text, r(2, 4, ""), r(0, 3, "")); -/* => -[delete 4 chars @2] becomes [delete 3 chars @0] -[delete 3 chars @0] becomes [delete 2 chars @0] -[delete 3 chars @0] + [delete 3 chars @0] -> -[delete 4 chars @2] + [delete 2 chars @0] -> - */ - - -// =SECTION Insertion and replacement - -trans(text, r(0, 0, "X"), r(2, 2, "Y")); -/* => -[insert "X" @0] stays same -[replace 2 chars with "Y" @2] becomes [replace 2 chars with "Y" @3] -[replace 2 chars with "Y" @2] + [insert "X" @0] -> XabYef -[insert "X" @0] + [replace 2 chars with "Y" @3] -> XabYef -*/ - -trans(text, r(2, 2, "Y"), r(0, 0, "X")); -/* => -[replace 2 chars with "Y" @2] becomes [replace 2 chars with "Y" @3] -[insert "X" @0] stays same -[insert "X" @0] + [replace 2 chars with "Y" @3] -> XabYef -[replace 2 chars with "Y" @2] + [insert "X" @0] -> XabYef -*/ - -trans(text, r(2, 0, "X"), r(2, 2, "Y")); -/* => -[insert "X" @2] stays same -[replace 2 chars with "Y" @2] becomes [replace 2 chars with "Y" @3] -[replace 2 chars with "Y" @2] + [insert "X" @2] -> abXYef -[insert "X" @2] + [replace 2 chars with "Y" @3] -> abXYef -*/ - -trans(text, r(2, 2, "Y"), r(2, 0, "X")); -/* => -[replace 2 chars with "Y" @2] becomes [replace 2 chars with "Y" @3] -[insert "X" @2] stays same -[insert "X" @2] + [replace 2 chars with "Y" @3] -> abXYef -[replace 2 chars with "Y" @2] + [insert "X" @2] -> abXYef -*/ - -trans(text, r(1, 0, "X"), r(0, 3, "Y")); -/* => -[insert "X" @1] stays same -[replace 3 chars with "Y" @0] becomes [replace 4 chars with "YX" @0] -[replace 3 chars with "Y" @0] + [insert "X" @1] -> YXdef -[insert "X" @1] + [replace 4 chars with "YX" @0] -> YXdef -*/ - -trans(text, r(0, 3, "Y"), r(1, 0, "X")); -/* => -[replace 3 chars with "Y" @0] becomes [replace 4 chars with "XY" @0] -[insert "X" @1] becomes [insert "X" @0] -[insert "X" @1] + [replace 4 chars with "XY" @0] -> XYdef -[replace 3 chars with "Y" @0] + [insert "X" @0] -> XYdef -*/ - -trans(text, r(4, 0, "X"), r(2, 2, "Y")); -/* => -[insert "X" @4] becomes [insert "X" @3] -[replace 2 chars with "Y" @2] stays same -[replace 2 chars with "Y" @2] + [insert "X" @3] -> abYXef -[insert "X" @4] + [replace 2 chars with "Y" @2] -> abYXef -*/ - -trans(text, r(2, 2, "Y"), r(4, 0, "X")); -/* => -[replace 2 chars with "Y" @2] stays same -[insert "X" @4] becomes [insert "X" @3] -[insert "X" @4] + [replace 2 chars with "Y" @2] -> abYXef -[replace 2 chars with "Y" @2] + [insert "X" @3] -> abYXef -*/ - -trans(text, r(0, 0, "X"), r(2, 2, "Y")); -/* => -[insert "X" @0] stays same -[replace 2 chars with "Y" @2] becomes [replace 2 chars with "Y" @3] -[replace 2 chars with "Y" @2] + [insert "X" @0] -> XabYef -[insert "X" @0] + [replace 2 chars with "Y" @3] -> XabYef -*/ - -// =SECTION Test (TP1) - -for (var i=0; i<1000; i++) { - console.log("Run", i); - run(); -} -print("done."); - -// => done. - -// =SECTION Test (TP2 attempt) - - -function runTp2() { - var base = "abcdefg"; - console.log("Start:", JSON.stringify(base) + "/" + base.length); - var deltaFirst = ot.TextReplace.random(base, generator); - var delta1 = ot.TextReplace.random(base, generator); - var delta2 = ot.TextReplace.random(base, generator); - console.log("Delta First:", deltaFirst+""); - console.log("Delta1:", delta1+""); - console.log("Delta2:", delta2+""); - // Now we'll try two orderings: - // first + 1 + 2 - // first + 2 + 1 - var delta1Trans = delta1.transpose(deltaFirst)[0]; - console.log("Translate:", deltaFirst, "+ (", delta1, "becomes", delta1Trans, ")"); - var delta2Trans = delta2.transpose(deltaFirst)[0]; - console.log("Translate:", deltaFirst, "+ (", delta2, "becomes", delta2Trans, ")"); - var delta1Trans_a = delta1Trans.transpose(delta2Trans)[0]; - console.log("Translate:", delta2Trans, "+ (", delta1Trans, "becomes", delta1Trans_a, ")"); - var delta2Trans_a = delta1Trans.transpose(delta2Trans)[1]; - console.log("Translate:", delta1Trans, "+ (", delta2Trans, "becomes", delta2Trans_a, ")"); - var text1 = delta1Trans_a.apply(delta2Trans.apply(deltaFirst.apply(base))); - console.log("text1:", JSON.stringify(base), "->", JSON.stringify(text1)); - console.log(" ", deltaFirst, deltaFirst.apply(base)); - console.log(" ", delta2Trans, delta2Trans.apply(deltaFirst.apply(base))); - console.log(" ", delta1Trans_a, text1); - var text2 = delta2Trans_a.apply(delta1Trans.apply(deltaFirst.apply(base))); - console.log("text2:", JSON.stringify(base), "->", JSON.stringify(text2)); - console.log(" ", deltaFirst, deltaFirst.apply(base)); - console.log(" ", delta1Trans, delta1Trans.apply(deltaFirst.apply(base))); - console.log(" ", delta2Trans_a, text2); - if (text1 != text2) { - print("Not equal"); - throw new Error("Error; not equal"); - } -} - -for (var i=0; i<1000; i++) { - console.clear(); - console.log("Run", i); - runTp2(); -} -print("done."); - -// => done. diff --git a/togetherjs/tests/test_resolves.js b/togetherjs/tests/test_resolves.js deleted file mode 100644 index ef4f933c7..000000000 --- a/togetherjs/tests/test_resolves.js +++ /dev/null @@ -1,36 +0,0 @@ -Test.require("util"); -// => Loaded modules: util - -var def = util.Deferred(); - -setTimeout(util.resolver(def, function () { - return 'ok'; -})); - -printResolved('item', def); - -// => item ok - -def = util.Deferred(); -var chained = util.Deferred(); -setTimeout(util.resolver(def, function () { - return chained; -})); -setTimeout(function () { - chained.resolve("second"); -}); - -printResolved('item2', def); - -// => item2 second - -var defs = [util.Deferred(), util.Deferred(), util.Deferred()]; -var result = util.resolveMany(defs); -setTimeout(function () { - defs[0].resolve('first'); - defs[1].resolve('second'); - defs[2].resolve('last'); -}); - -printResolved(result); -// => ["first", "second", "last"] diff --git a/togetherjs/tests/test_storage.js b/togetherjs/tests/test_storage.js deleted file mode 100644 index 0c2422e71..000000000 --- a/togetherjs/tests/test_storage.js +++ /dev/null @@ -1,18 +0,0 @@ -/*global storage */ -Test.require("storage", "util"); -// => Loaded modules: storage util - -print(storage); -// => [storage for localStorage] - -printResolved(storage.clear(), storage.tab.clear()); -// => (resolved) (resolved) - -printResolved(storage.keys(), storage.tab.keys()); -// => [] [] - -printResolved(storage.tab.set("foo", "bar")); -// => (resolved) - -printResolved(storage.tab.keys()); -// => ["foo"] diff --git a/togetherjs/tests/testutils.js b/togetherjs/tests/testutils.js deleted file mode 100644 index 5dd417a13..000000000 --- a/togetherjs/tests/testutils.js +++ /dev/null @@ -1,248 +0,0 @@ -TogetherJSTestSpy = {}; - -var Test = {}; - -/* Loads the modules that are listed as individual arguments, and adds - them to the global scope. Blocks on the loading. Use like: - - Test.require("foo", "bar"); - // => ... - foo.someFunction()... - - If you want to alias something, do: - - Test.require({myConsole: "console"}) - // => ... - myConsole.log() -*/ -Test.require = function () { - var done = false; - var args = Array.prototype.slice.call(arguments); - var modules = []; - var aliases = {}; - args.forEach(function (m) { - if (typeof m == "object") { - for (var alias in m) { - if (m.hasOwnProperty(alias)) { - modules.push(m[alias]); - aliases[m[alias]] = alias; - } - } - } else { - modules.push(m); - } - }); - - function loadModules() { - if (! modules.length) { - print("Require loaded"); - done = true; - return; - } - TogetherJS.require(modules, function () { - for (var i=0; i= args.length) { - done = true; - return; - } - var f = args[index]; - if (!f.then) { f = f(); } - f.then(function () { - if (! arguments.length) { - print("(done)"); - } else { - print.apply(null, arguments); - } - check(1); - }, function () { - if (! arguments.length) { - print("(error)"); - } else { - print.apply(null, ["Error:"].concat(arguments)); - } - check(1); - }); - function check(increment) { - index += increment; - setTimeout(run); - } - } - wait(function () {return done;}); - run(); -} - -Test.incoming = function (msg) { - TogetherJSTestSpy.getChannel().onmessage(msg); -}; - -Test.addControl = function () { - var div = $("
"); - var el; - for (var i=0; i - - - - TogetherJS interactions - - - - - - - - - - - - - - - -
    -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
  • -
- -
- - -
-
- ↑ -
- Participant name box and cursor -
-
- - -
- Participant entered -
- - -
- Participant left -
- - -
- Click to close chat -
- -
- -
-
-
- -
- -
- -
- - - - - diff --git a/togetherjs/tests/togetherjs-animations/js/animations.js b/togetherjs/tests/togetherjs-animations/js/animations.js deleted file mode 100644 index 6b8ecb62d..000000000 --- a/togetherjs/tests/togetherjs-animations/js/animations.js +++ /dev/null @@ -1,163 +0,0 @@ - // Methods - - function participantScaleUp() { - $('#participant-avatar').transition({ opacity: 1, scale: 2 }); - } - - function participantScaleDown() { - $('#participant-avatar').transition({ opacity: 0, scale: 0 }); - } - - function notificationSlideIn() { - $('#notification').css({ - left: "+=84px", - opacity: 0, - "zIndex": 8888 - }); - $('#notification').animate({ - "left": "-=164px", - opacity: 1, - "zIndex": 9999 - }, "fast"); - }; - - function notificationSlideInParticipantLeave() { - $('#notification2').css({ - left: "+=84px", - opacity: 0, - "zIndex": 8888 - }); - $('#notification2').animate({ - "left": "-=164px", - opacity: 1, - "zIndex": 9999 - }, "fast"); - }; - - function notificationChat() { - $('#notification3').css({ - left: "+=84px", - opacity: 0, - "zIndex": 8888 - }); - $('#notification3').animate({ - "left": "-=164px", - opacity: 1, - "zIndex": 9999 - }, "fast"); - }; - - function closeNotification() { - $('#notification').transition({ - perspective: '300px', - rotateX: '-90deg', - delay: 3000, - opacity: 0 - }); - }; - - function closeNotification2() { - $('#notification2').transition({ - perspective: '300px', - rotateX: '-90deg', - delay: 3000, - opacity: 0 - }); - }; - - function closeNotification3() { - $('#notification3').transition({ - perspective: '300px', - rotateX: '-90deg', - opacity: 0 - }); - }; - - function cursorPopIn() { - scaleUp($("#participant-box"), 10); - } - - function cursorPopOut() { - $("#participant-box") - .transition({ opacity: 0, scale: 0 }) - } - - function scaleUp(el, size) { - var height = el.height(); - var width = el.width(); - var buffer = size / 2; - el.animate({ - opacity: 1, - width: (width + size) + "px", - height: (height + size) + "px", - marginLeft: -buffer + "px", - paddingLeft: buffer + "px" - }).animate({ - width: width + "px", - height: height + "px", - marginLeft: 0, - paddingLeft: 0 - }); - } - - // Objects - - //chat notification - function chatnotification() { - notificationChat(); - closeNotification(); - }; - - // when the participant enters the session - function participantEnter() { - notificationSlideIn(); - participantScaleUp(); - closeNotification(); - cursorPopIn(); - }; - - // when the participant leaves the session - function participantLeave() { - notificationSlideInParticipantLeave(); - participantScaleDown(); - closeNotification2(); - cursorPopOut(); - } - - // when the participant is down the page or up the page - function participantCursorRotateDown() { - $('#participant-cursor').transition({ - rotate: '-180deg' - }); - } - - // when the user is at the same location as the participant - function participantCursorRotateUp() { - $('#participant-cursor').transition({ - rotate: '-30deg' - }); - } - - // NEEDS WORK when user presses a button in the dock to popout a window - function windowPopOut() { - $("#windowpopout") - //.css({ transformOrigin: '100px 50px' }) - .transition({ x: '-110', y: '100', opacity: 1, scale: 2.2 }, 100, 'ease') - .transition({ scale: 2 }, 100, 'ease') - } - - // when the participant is typing - function participantTyping() { - var count = 0; - - setInterval(function(){ - count++; - document.getElementById('participant-typing').innerHTML = new Array(count % 5).join('.'); - }, 150); - - } - - function transformPers() { - alert("transform"); - - } diff --git a/togetherjs/tests/togetherjs-animations/js/jquery-1.8.3.min.js b/togetherjs/tests/togetherjs-animations/js/jquery-1.8.3.min.js deleted file mode 100644 index 83589daa7..000000000 --- a/togetherjs/tests/togetherjs-animations/js/jquery-1.8.3.min.js +++ /dev/null @@ -1,2 +0,0 @@ -/*! jQuery v1.8.3 jquery.com | jquery.org/license */ -(function(e,t){function _(e){var t=M[e]={};return v.each(e.split(y),function(e,n){t[n]=!0}),t}function H(e,n,r){if(r===t&&e.nodeType===1){var i="data-"+n.replace(P,"-$1").toLowerCase();r=e.getAttribute(i);if(typeof r=="string"){try{r=r==="true"?!0:r==="false"?!1:r==="null"?null:+r+""===r?+r:D.test(r)?v.parseJSON(r):r}catch(s){}v.data(e,n,r)}else r=t}return r}function B(e){var t;for(t in e){if(t==="data"&&v.isEmptyObject(e[t]))continue;if(t!=="toJSON")return!1}return!0}function et(){return!1}function tt(){return!0}function ut(e){return!e||!e.parentNode||e.parentNode.nodeType===11}function at(e,t){do e=e[t];while(e&&e.nodeType!==1);return e}function ft(e,t,n){t=t||0;if(v.isFunction(t))return v.grep(e,function(e,r){var i=!!t.call(e,r,e);return i===n});if(t.nodeType)return v.grep(e,function(e,r){return e===t===n});if(typeof t=="string"){var r=v.grep(e,function(e){return e.nodeType===1});if(it.test(t))return v.filter(t,r,!n);t=v.filter(t,r)}return v.grep(e,function(e,r){return v.inArray(e,t)>=0===n})}function lt(e){var t=ct.split("|"),n=e.createDocumentFragment();if(n.createElement)while(t.length)n.createElement(t.pop());return n}function Lt(e,t){return e.getElementsByTagName(t)[0]||e.appendChild(e.ownerDocument.createElement(t))}function At(e,t){if(t.nodeType!==1||!v.hasData(e))return;var n,r,i,s=v._data(e),o=v._data(t,s),u=s.events;if(u){delete o.handle,o.events={};for(n in u)for(r=0,i=u[n].length;r").appendTo(i.body),n=t.css("display");t.remove();if(n==="none"||n===""){Pt=i.body.appendChild(Pt||v.extend(i.createElement("iframe"),{frameBorder:0,width:0,height:0}));if(!Ht||!Pt.createElement)Ht=(Pt.contentWindow||Pt.contentDocument).document,Ht.write(""),Ht.close();t=Ht.body.appendChild(Ht.createElement(e)),n=Dt(t,"display"),i.body.removeChild(Pt)}return Wt[e]=n,n}function fn(e,t,n,r){var i;if(v.isArray(t))v.each(t,function(t,i){n||sn.test(e)?r(e,i):fn(e+"["+(typeof i=="object"?t:"")+"]",i,n,r)});else if(!n&&v.type(t)==="object")for(i in t)fn(e+"["+i+"]",t[i],n,r);else r(e,t)}function Cn(e){return function(t,n){typeof t!="string"&&(n=t,t="*");var r,i,s,o=t.toLowerCase().split(y),u=0,a=o.length;if(v.isFunction(n))for(;u)[^>]*$|#([\w\-]*)$)/,E=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,S=/^[\],:{}\s]*$/,x=/(?:^|:|,)(?:\s*\[)+/g,T=/\\(?:["\\\/bfnrt]|u[\da-fA-F]{4})/g,N=/"[^"\\\r\n]*"|true|false|null|-?(?:\d\d*\.|)\d+(?:[eE][\-+]?\d+|)/g,C=/^-ms-/,k=/-([\da-z])/gi,L=function(e,t){return(t+"").toUpperCase()},A=function(){i.addEventListener?(i.removeEventListener("DOMContentLoaded",A,!1),v.ready()):i.readyState==="complete"&&(i.detachEvent("onreadystatechange",A),v.ready())},O={};v.fn=v.prototype={constructor:v,init:function(e,n,r){var s,o,u,a;if(!e)return this;if(e.nodeType)return this.context=this[0]=e,this.length=1,this;if(typeof e=="string"){e.charAt(0)==="<"&&e.charAt(e.length-1)===">"&&e.length>=3?s=[null,e,null]:s=w.exec(e);if(s&&(s[1]||!n)){if(s[1])return n=n instanceof v?n[0]:n,a=n&&n.nodeType?n.ownerDocument||n:i,e=v.parseHTML(s[1],a,!0),E.test(s[1])&&v.isPlainObject(n)&&this.attr.call(e,n,!0),v.merge(this,e);o=i.getElementById(s[2]);if(o&&o.parentNode){if(o.id!==s[2])return r.find(e);this.length=1,this[0]=o}return this.context=i,this.selector=e,this}return!n||n.jquery?(n||r).find(e):this.constructor(n).find(e)}return v.isFunction(e)?r.ready(e):(e.selector!==t&&(this.selector=e.selector,this.context=e.context),v.makeArray(e,this))},selector:"",jquery:"1.8.3",length:0,size:function(){return this.length},toArray:function(){return l.call(this)},get:function(e){return e==null?this.toArray():e<0?this[this.length+e]:this[e]},pushStack:function(e,t,n){var r=v.merge(this.constructor(),e);return r.prevObject=this,r.context=this.context,t==="find"?r.selector=this.selector+(this.selector?" ":"")+n:t&&(r.selector=this.selector+"."+t+"("+n+")"),r},each:function(e,t){return v.each(this,e,t)},ready:function(e){return v.ready.promise().done(e),this},eq:function(e){return e=+e,e===-1?this.slice(e):this.slice(e,e+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(l.apply(this,arguments),"slice",l.call(arguments).join(","))},map:function(e){return this.pushStack(v.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:[].sort,splice:[].splice},v.fn.init.prototype=v.fn,v.extend=v.fn.extend=function(){var e,n,r,i,s,o,u=arguments[0]||{},a=1,f=arguments.length,l=!1;typeof u=="boolean"&&(l=u,u=arguments[1]||{},a=2),typeof u!="object"&&!v.isFunction(u)&&(u={}),f===a&&(u=this,--a);for(;a0)return;r.resolveWith(i,[v]),v.fn.trigger&&v(i).trigger("ready").off("ready")},isFunction:function(e){return v.type(e)==="function"},isArray:Array.isArray||function(e){return v.type(e)==="array"},isWindow:function(e){return e!=null&&e==e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return e==null?String(e):O[h.call(e)]||"object"},isPlainObject:function(e){if(!e||v.type(e)!=="object"||e.nodeType||v.isWindow(e))return!1;try{if(e.constructor&&!p.call(e,"constructor")&&!p.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(n){return!1}var r;for(r in e);return r===t||p.call(e,r)},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw new Error(e)},parseHTML:function(e,t,n){var r;return!e||typeof e!="string"?null:(typeof t=="boolean"&&(n=t,t=0),t=t||i,(r=E.exec(e))?[t.createElement(r[1])]:(r=v.buildFragment([e],t,n?null:[]),v.merge([],(r.cacheable?v.clone(r.fragment):r.fragment).childNodes)))},parseJSON:function(t){if(!t||typeof t!="string")return null;t=v.trim(t);if(e.JSON&&e.JSON.parse)return e.JSON.parse(t);if(S.test(t.replace(T,"@").replace(N,"]").replace(x,"")))return(new Function("return "+t))();v.error("Invalid JSON: "+t)},parseXML:function(n){var r,i;if(!n||typeof n!="string")return null;try{e.DOMParser?(i=new DOMParser,r=i.parseFromString(n,"text/xml")):(r=new ActiveXObject("Microsoft.XMLDOM"),r.async="false",r.loadXML(n))}catch(s){r=t}return(!r||!r.documentElement||r.getElementsByTagName("parsererror").length)&&v.error("Invalid XML: "+n),r},noop:function(){},globalEval:function(t){t&&g.test(t)&&(e.execScript||function(t){e.eval.call(e,t)})(t)},camelCase:function(e){return e.replace(C,"ms-").replace(k,L)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,n,r){var i,s=0,o=e.length,u=o===t||v.isFunction(e);if(r){if(u){for(i in e)if(n.apply(e[i],r)===!1)break}else for(;s0&&e[0]&&e[a-1]||a===0||v.isArray(e));if(f)for(;u-1)a.splice(n,1),i&&(n<=o&&o--,n<=u&&u--)}),this},has:function(e){return v.inArray(e,a)>-1},empty:function(){return a=[],this},disable:function(){return a=f=n=t,this},disabled:function(){return!a},lock:function(){return f=t,n||c.disable(),this},locked:function(){return!f},fireWith:function(e,t){return t=t||[],t=[e,t.slice?t.slice():t],a&&(!r||f)&&(i?f.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!r}};return c},v.extend({Deferred:function(e){var t=[["resolve","done",v.Callbacks("once memory"),"resolved"],["reject","fail",v.Callbacks("once memory"),"rejected"],["notify","progress",v.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return v.Deferred(function(n){v.each(t,function(t,r){var s=r[0],o=e[t];i[r[1]](v.isFunction(o)?function(){var e=o.apply(this,arguments);e&&v.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===i?n:this,[e])}:n[s])}),e=null}).promise()},promise:function(e){return e!=null?v.extend(e,r):r}},i={};return r.pipe=r.then,v.each(t,function(e,s){var o=s[2],u=s[3];r[s[1]]=o.add,u&&o.add(function(){n=u},t[e^1][2].disable,t[2][2].lock),i[s[0]]=o.fire,i[s[0]+"With"]=o.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=l.call(arguments),r=n.length,i=r!==1||e&&v.isFunction(e.promise)?r:0,s=i===1?e:v.Deferred(),o=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?l.call(arguments):r,n===u?s.notifyWith(t,n):--i||s.resolveWith(t,n)}},u,a,f;if(r>1){u=new Array(r),a=new Array(r),f=new Array(r);for(;t
a",n=p.getElementsByTagName("*"),r=p.getElementsByTagName("a")[0];if(!n||!r||!n.length)return{};s=i.createElement("select"),o=s.appendChild(i.createElement("option")),u=p.getElementsByTagName("input")[0],r.style.cssText="top:1px;float:left;opacity:.5",t={leadingWhitespace:p.firstChild.nodeType===3,tbody:!p.getElementsByTagName("tbody").length,htmlSerialize:!!p.getElementsByTagName("link").length,style:/top/.test(r.getAttribute("style")),hrefNormalized:r.getAttribute("href")==="/a",opacity:/^0.5/.test(r.style.opacity),cssFloat:!!r.style.cssFloat,checkOn:u.value==="on",optSelected:o.selected,getSetAttribute:p.className!=="t",enctype:!!i.createElement("form").enctype,html5Clone:i.createElement("nav").cloneNode(!0).outerHTML!=="<:nav>",boxModel:i.compatMode==="CSS1Compat",submitBubbles:!0,changeBubbles:!0,focusinBubbles:!1,deleteExpando:!0,noCloneEvent:!0,inlineBlockNeedsLayout:!1,shrinkWrapBlocks:!1,reliableMarginRight:!0,boxSizingReliable:!0,pixelPosition:!1},u.checked=!0,t.noCloneChecked=u.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!o.disabled;try{delete p.test}catch(d){t.deleteExpando=!1}!p.addEventListener&&p.attachEvent&&p.fireEvent&&(p.attachEvent("onclick",h=function(){t.noCloneEvent=!1}),p.cloneNode(!0).fireEvent("onclick"),p.detachEvent("onclick",h)),u=i.createElement("input"),u.value="t",u.setAttribute("type","radio"),t.radioValue=u.value==="t",u.setAttribute("checked","checked"),u.setAttribute("name","t"),p.appendChild(u),a=i.createDocumentFragment(),a.appendChild(p.lastChild),t.checkClone=a.cloneNode(!0).cloneNode(!0).lastChild.checked,t.appendChecked=u.checked,a.removeChild(u),a.appendChild(p);if(p.attachEvent)for(l in{submit:!0,change:!0,focusin:!0})f="on"+l,c=f in p,c||(p.setAttribute(f,"return;"),c=typeof p[f]=="function"),t[l+"Bubbles"]=c;return v(function(){var n,r,s,o,u="padding:0;margin:0;border:0;display:block;overflow:hidden;",a=i.getElementsByTagName("body")[0];if(!a)return;n=i.createElement("div"),n.style.cssText="visibility:hidden;border:0;width:0;height:0;position:static;top:0;margin-top:1px",a.insertBefore(n,a.firstChild),r=i.createElement("div"),n.appendChild(r),r.innerHTML="
t
",s=r.getElementsByTagName("td"),s[0].style.cssText="padding:0;margin:0;border:0;display:none",c=s[0].offsetHeight===0,s[0].style.display="",s[1].style.display="none",t.reliableHiddenOffsets=c&&s[0].offsetHeight===0,r.innerHTML="",r.style.cssText="box-sizing:border-box;-moz-box-sizing:border-box;-webkit-box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%;",t.boxSizing=r.offsetWidth===4,t.doesNotIncludeMarginInBodyOffset=a.offsetTop!==1,e.getComputedStyle&&(t.pixelPosition=(e.getComputedStyle(r,null)||{}).top!=="1%",t.boxSizingReliable=(e.getComputedStyle(r,null)||{width:"4px"}).width==="4px",o=i.createElement("div"),o.style.cssText=r.style.cssText=u,o.style.marginRight=o.style.width="0",r.style.width="1px",r.appendChild(o),t.reliableMarginRight=!parseFloat((e.getComputedStyle(o,null)||{}).marginRight)),typeof r.style.zoom!="undefined"&&(r.innerHTML="",r.style.cssText=u+"width:1px;padding:1px;display:inline;zoom:1",t.inlineBlockNeedsLayout=r.offsetWidth===3,r.style.display="block",r.style.overflow="visible",r.innerHTML="
",r.firstChild.style.width="5px",t.shrinkWrapBlocks=r.offsetWidth!==3,n.style.zoom=1),a.removeChild(n),n=r=s=o=null}),a.removeChild(p),n=r=s=o=u=a=p=null,t}();var D=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,P=/([A-Z])/g;v.extend({cache:{},deletedIds:[],uuid:0,expando:"jQuery"+(v.fn.jquery+Math.random()).replace(/\D/g,""),noData:{embed:!0,object:"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000",applet:!0},hasData:function(e){return e=e.nodeType?v.cache[e[v.expando]]:e[v.expando],!!e&&!B(e)},data:function(e,n,r,i){if(!v.acceptData(e))return;var s,o,u=v.expando,a=typeof n=="string",f=e.nodeType,l=f?v.cache:e,c=f?e[u]:e[u]&&u;if((!c||!l[c]||!i&&!l[c].data)&&a&&r===t)return;c||(f?e[u]=c=v.deletedIds.pop()||v.guid++:c=u),l[c]||(l[c]={},f||(l[c].toJSON=v.noop));if(typeof n=="object"||typeof n=="function")i?l[c]=v.extend(l[c],n):l[c].data=v.extend(l[c].data,n);return s=l[c],i||(s.data||(s.data={}),s=s.data),r!==t&&(s[v.camelCase(n)]=r),a?(o=s[n],o==null&&(o=s[v.camelCase(n)])):o=s,o},removeData:function(e,t,n){if(!v.acceptData(e))return;var r,i,s,o=e.nodeType,u=o?v.cache:e,a=o?e[v.expando]:v.expando;if(!u[a])return;if(t){r=n?u[a]:u[a].data;if(r){v.isArray(t)||(t in r?t=[t]:(t=v.camelCase(t),t in r?t=[t]:t=t.split(" ")));for(i=0,s=t.length;i1,null,!1))},removeData:function(e){return this.each(function(){v.removeData(this,e)})}}),v.extend({queue:function(e,t,n){var r;if(e)return t=(t||"fx")+"queue",r=v._data(e,t),n&&(!r||v.isArray(n)?r=v._data(e,t,v.makeArray(n)):r.push(n)),r||[]},dequeue:function(e,t){t=t||"fx";var n=v.queue(e,t),r=n.length,i=n.shift(),s=v._queueHooks(e,t),o=function(){v.dequeue(e,t)};i==="inprogress"&&(i=n.shift(),r--),i&&(t==="fx"&&n.unshift("inprogress"),delete s.stop,i.call(e,o,s)),!r&&s&&s.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return v._data(e,n)||v._data(e,n,{empty:v.Callbacks("once memory").add(function(){v.removeData(e,t+"queue",!0),v.removeData(e,n,!0)})})}}),v.fn.extend({queue:function(e,n){var r=2;return typeof e!="string"&&(n=e,e="fx",r--),arguments.length1)},removeAttr:function(e){return this.each(function(){v.removeAttr(this,e)})},prop:function(e,t){return v.access(this,v.prop,e,t,arguments.length>1)},removeProp:function(e){return e=v.propFix[e]||e,this.each(function(){try{this[e]=t,delete this[e]}catch(n){}})},addClass:function(e){var t,n,r,i,s,o,u;if(v.isFunction(e))return this.each(function(t){v(this).addClass(e.call(this,t,this.className))});if(e&&typeof e=="string"){t=e.split(y);for(n=0,r=this.length;n=0)r=r.replace(" "+n[s]+" "," ");i.className=e?v.trim(r):""}}}return this},toggleClass:function(e,t){var n=typeof e,r=typeof t=="boolean";return v.isFunction(e)?this.each(function(n){v(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if(n==="string"){var i,s=0,o=v(this),u=t,a=e.split(y);while(i=a[s++])u=r?u:!o.hasClass(i),o[u?"addClass":"removeClass"](i)}else if(n==="undefined"||n==="boolean")this.className&&v._data(this,"__className__",this.className),this.className=this.className||e===!1?"":v._data(this,"__className__")||""})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;n=0)return!0;return!1},val:function(e){var n,r,i,s=this[0];if(!arguments.length){if(s)return n=v.valHooks[s.type]||v.valHooks[s.nodeName.toLowerCase()],n&&"get"in n&&(r=n.get(s,"value"))!==t?r:(r=s.value,typeof r=="string"?r.replace(R,""):r==null?"":r);return}return i=v.isFunction(e),this.each(function(r){var s,o=v(this);if(this.nodeType!==1)return;i?s=e.call(this,r,o.val()):s=e,s==null?s="":typeof s=="number"?s+="":v.isArray(s)&&(s=v.map(s,function(e){return e==null?"":e+""})),n=v.valHooks[this.type]||v.valHooks[this.nodeName.toLowerCase()];if(!n||!("set"in n)||n.set(this,s,"value")===t)this.value=s})}}),v.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,s=e.type==="select-one"||i<0,o=s?null:[],u=s?i+1:r.length,a=i<0?u:s?i:0;for(;a=0}),n.length||(e.selectedIndex=-1),n}}},attrFn:{},attr:function(e,n,r,i){var s,o,u,a=e.nodeType;if(!e||a===3||a===8||a===2)return;if(i&&v.isFunction(v.fn[n]))return v(e)[n](r);if(typeof e.getAttribute=="undefined")return v.prop(e,n,r);u=a!==1||!v.isXMLDoc(e),u&&(n=n.toLowerCase(),o=v.attrHooks[n]||(X.test(n)?F:j));if(r!==t){if(r===null){v.removeAttr(e,n);return}return o&&"set"in o&&u&&(s=o.set(e,r,n))!==t?s:(e.setAttribute(n,r+""),r)}return o&&"get"in o&&u&&(s=o.get(e,n))!==null?s:(s=e.getAttribute(n),s===null?t:s)},removeAttr:function(e,t){var n,r,i,s,o=0;if(t&&e.nodeType===1){r=t.split(y);for(;o=0}})});var $=/^(?:textarea|input|select)$/i,J=/^([^\.]*|)(?:\.(.+)|)$/,K=/(?:^|\s)hover(\.\S+|)\b/,Q=/^key/,G=/^(?:mouse|contextmenu)|click/,Y=/^(?:focusinfocus|focusoutblur)$/,Z=function(e){return v.event.special.hover?e:e.replace(K,"mouseenter$1 mouseleave$1")};v.event={add:function(e,n,r,i,s){var o,u,a,f,l,c,h,p,d,m,g;if(e.nodeType===3||e.nodeType===8||!n||!r||!(o=v._data(e)))return;r.handler&&(d=r,r=d.handler,s=d.selector),r.guid||(r.guid=v.guid++),a=o.events,a||(o.events=a={}),u=o.handle,u||(o.handle=u=function(e){return typeof v=="undefined"||!!e&&v.event.triggered===e.type?t:v.event.dispatch.apply(u.elem,arguments)},u.elem=e),n=v.trim(Z(n)).split(" ");for(f=0;f=0&&(y=y.slice(0,-1),a=!0),y.indexOf(".")>=0&&(b=y.split("."),y=b.shift(),b.sort());if((!s||v.event.customEvent[y])&&!v.event.global[y])return;n=typeof n=="object"?n[v.expando]?n:new v.Event(y,n):new v.Event(y),n.type=y,n.isTrigger=!0,n.exclusive=a,n.namespace=b.join("."),n.namespace_re=n.namespace?new RegExp("(^|\\.)"+b.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,h=y.indexOf(":")<0?"on"+y:"";if(!s){u=v.cache;for(f in u)u[f].events&&u[f].events[y]&&v.event.trigger(n,r,u[f].handle.elem,!0);return}n.result=t,n.target||(n.target=s),r=r!=null?v.makeArray(r):[],r.unshift(n),p=v.event.special[y]||{};if(p.trigger&&p.trigger.apply(s,r)===!1)return;m=[[s,p.bindType||y]];if(!o&&!p.noBubble&&!v.isWindow(s)){g=p.delegateType||y,l=Y.test(g+y)?s:s.parentNode;for(c=s;l;l=l.parentNode)m.push([l,g]),c=l;c===(s.ownerDocument||i)&&m.push([c.defaultView||c.parentWindow||e,g])}for(f=0;f=0:v.find(h,this,null,[s]).length),u[h]&&f.push(c);f.length&&w.push({elem:s,matches:f})}d.length>m&&w.push({elem:this,matches:d.slice(m)});for(r=0;r0?this.on(t,null,e,n):this.trigger(t)},Q.test(t)&&(v.event.fixHooks[t]=v.event.keyHooks),G.test(t)&&(v.event.fixHooks[t]=v.event.mouseHooks)}),function(e,t){function nt(e,t,n,r){n=n||[],t=t||g;var i,s,a,f,l=t.nodeType;if(!e||typeof e!="string")return n;if(l!==1&&l!==9)return[];a=o(t);if(!a&&!r)if(i=R.exec(e))if(f=i[1]){if(l===9){s=t.getElementById(f);if(!s||!s.parentNode)return n;if(s.id===f)return n.push(s),n}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(f))&&u(t,s)&&s.id===f)return n.push(s),n}else{if(i[2])return S.apply(n,x.call(t.getElementsByTagName(e),0)),n;if((f=i[3])&&Z&&t.getElementsByClassName)return S.apply(n,x.call(t.getElementsByClassName(f),0)),n}return vt(e.replace(j,"$1"),t,n,r,a)}function rt(e){return function(t){var n=t.nodeName.toLowerCase();return n==="input"&&t.type===e}}function it(e){return function(t){var n=t.nodeName.toLowerCase();return(n==="input"||n==="button")&&t.type===e}}function st(e){return N(function(t){return t=+t,N(function(n,r){var i,s=e([],n.length,t),o=s.length;while(o--)n[i=s[o]]&&(n[i]=!(r[i]=n[i]))})})}function ot(e,t,n){if(e===t)return n;var r=e.nextSibling;while(r){if(r===t)return-1;r=r.nextSibling}return 1}function ut(e,t){var n,r,s,o,u,a,f,l=L[d][e+" "];if(l)return t?0:l.slice(0);u=e,a=[],f=i.preFilter;while(u){if(!n||(r=F.exec(u)))r&&(u=u.slice(r[0].length)||u),a.push(s=[]);n=!1;if(r=I.exec(u))s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=r[0].replace(j," ");for(o in i.filter)(r=J[o].exec(u))&&(!f[o]||(r=f[o](r)))&&(s.push(n=new m(r.shift())),u=u.slice(n.length),n.type=o,n.matches=r);if(!n)break}return t?u.length:u?nt.error(e):L(e,a).slice(0)}function at(e,t,r){var i=t.dir,s=r&&t.dir==="parentNode",o=w++;return t.first?function(t,n,r){while(t=t[i])if(s||t.nodeType===1)return e(t,n,r)}:function(t,r,u){if(!u){var a,f=b+" "+o+" ",l=f+n;while(t=t[i])if(s||t.nodeType===1){if((a=t[d])===l)return t.sizset;if(typeof a=="string"&&a.indexOf(f)===0){if(t.sizset)return t}else{t[d]=l;if(e(t,r,u))return t.sizset=!0,t;t.sizset=!1}}}else while(t=t[i])if(s||t.nodeType===1)if(e(t,r,u))return t}}function ft(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function lt(e,t,n,r,i){var s,o=[],u=0,a=e.length,f=t!=null;for(;u-1&&(s[f]=!(o[f]=c))}}else g=lt(g===o?g.splice(d,g.length):g),i?i(null,o,g,a):S.apply(o,g)})}function ht(e){var t,n,r,s=e.length,o=i.relative[e[0].type],u=o||i.relative[" "],a=o?1:0,f=at(function(e){return e===t},u,!0),l=at(function(e){return T.call(t,e)>-1},u,!0),h=[function(e,n,r){return!o&&(r||n!==c)||((t=n).nodeType?f(e,n,r):l(e,n,r))}];for(;a1&&ft(h),a>1&&e.slice(0,a-1).join("").replace(j,"$1"),n,a0,s=e.length>0,o=function(u,a,f,l,h){var p,d,v,m=[],y=0,w="0",x=u&&[],T=h!=null,N=c,C=u||s&&i.find.TAG("*",h&&a.parentNode||a),k=b+=N==null?1:Math.E;T&&(c=a!==g&&a,n=o.el);for(;(p=C[w])!=null;w++){if(s&&p){for(d=0;v=e[d];d++)if(v(p,a,f)){l.push(p);break}T&&(b=k,n=++o.el)}r&&((p=!v&&p)&&y--,u&&x.push(p))}y+=w;if(r&&w!==y){for(d=0;v=t[d];d++)v(x,m,a,f);if(u){if(y>0)while(w--)!x[w]&&!m[w]&&(m[w]=E.call(l));m=lt(m)}S.apply(l,m),T&&!u&&m.length>0&&y+t.length>1&&nt.uniqueSort(l)}return T&&(b=k,c=N),x};return o.el=0,r?N(o):o}function dt(e,t,n){var r=0,i=t.length;for(;r2&&(f=u[0]).type==="ID"&&t.nodeType===9&&!s&&i.relative[u[1].type]){t=i.find.ID(f.matches[0].replace($,""),t,s)[0];if(!t)return n;e=e.slice(u.shift().length)}for(o=J.POS.test(e)?-1:u.length-1;o>=0;o--){f=u[o];if(i.relative[l=f.type])break;if(c=i.find[l])if(r=c(f.matches[0].replace($,""),z.test(u[0].type)&&t.parentNode||t,s)){u.splice(o,1),e=r.length&&u.join("");if(!e)return S.apply(n,x.call(r,0)),n;break}}}return a(e,h)(r,t,s,n,z.test(e)),n}function mt(){}var n,r,i,s,o,u,a,f,l,c,h=!0,p="undefined",d=("sizcache"+Math.random()).replace(".",""),m=String,g=e.document,y=g.documentElement,b=0,w=0,E=[].pop,S=[].push,x=[].slice,T=[].indexOf||function(e){var t=0,n=this.length;for(;ti.cacheLength&&delete e[t.shift()],e[n+" "]=r},e)},k=C(),L=C(),A=C(),O="[\\x20\\t\\r\\n\\f]",M="(?:\\\\.|[-\\w]|[^\\x00-\\xa0])+",_=M.replace("w","w#"),D="([*^$|!~]?=)",P="\\["+O+"*("+M+")"+O+"*(?:"+D+O+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+_+")|)|)"+O+"*\\]",H=":("+M+")(?:\\((?:(['\"])((?:\\\\.|[^\\\\])*?)\\2|([^()[\\]]*|(?:(?:"+P+")|[^:]|\\\\.)*|.*))\\)|)",B=":(even|odd|eq|gt|lt|nth|first|last)(?:\\("+O+"*((?:-\\d)?\\d*)"+O+"*\\)|)(?=[^-]|$)",j=new RegExp("^"+O+"+|((?:^|[^\\\\])(?:\\\\.)*)"+O+"+$","g"),F=new RegExp("^"+O+"*,"+O+"*"),I=new RegExp("^"+O+"*([\\x20\\t\\r\\n\\f>+~])"+O+"*"),q=new RegExp(H),R=/^(?:#([\w\-]+)|(\w+)|\.([\w\-]+))$/,U=/^:not/,z=/[\x20\t\r\n\f]*[+~]/,W=/:not\($/,X=/h\d/i,V=/input|select|textarea|button/i,$=/\\(?!\\)/g,J={ID:new RegExp("^#("+M+")"),CLASS:new RegExp("^\\.("+M+")"),NAME:new RegExp("^\\[name=['\"]?("+M+")['\"]?\\]"),TAG:new RegExp("^("+M.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+H),POS:new RegExp(B,"i"),CHILD:new RegExp("^:(only|nth|first|last)-child(?:\\("+O+"*(even|odd|(([+-]|)(\\d*)n|)"+O+"*(?:([+-]|)"+O+"*(\\d+)|))"+O+"*\\)|)","i"),needsContext:new RegExp("^"+O+"*[>+~]|"+B,"i")},K=function(e){var t=g.createElement("div");try{return e(t)}catch(n){return!1}finally{t=null}},Q=K(function(e){return e.appendChild(g.createComment("")),!e.getElementsByTagName("*").length}),G=K(function(e){return e.innerHTML="",e.firstChild&&typeof e.firstChild.getAttribute!==p&&e.firstChild.getAttribute("href")==="#"}),Y=K(function(e){e.innerHTML="";var t=typeof e.lastChild.getAttribute("multiple");return t!=="boolean"&&t!=="string"}),Z=K(function(e){return e.innerHTML="",!e.getElementsByClassName||!e.getElementsByClassName("e").length?!1:(e.lastChild.className="e",e.getElementsByClassName("e").length===2)}),et=K(function(e){e.id=d+0,e.innerHTML="
",y.insertBefore(e,y.firstChild);var t=g.getElementsByName&&g.getElementsByName(d).length===2+g.getElementsByName(d+0).length;return r=!g.getElementById(d),y.removeChild(e),t});try{x.call(y.childNodes,0)[0].nodeType}catch(tt){x=function(e){var t,n=[];for(;t=this[e];e++)n.push(t);return n}}nt.matches=function(e,t){return nt(e,null,null,t)},nt.matchesSelector=function(e,t){return nt(t,null,null,[e]).length>0},s=nt.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(i===1||i===9||i===11){if(typeof e.textContent=="string")return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=s(e)}else if(i===3||i===4)return e.nodeValue}else for(;t=e[r];r++)n+=s(t);return n},o=nt.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?t.nodeName!=="HTML":!1},u=nt.contains=y.contains?function(e,t){var n=e.nodeType===9?e.documentElement:e,r=t&&t.parentNode;return e===r||!!(r&&r.nodeType===1&&n.contains&&n.contains(r))}:y.compareDocumentPosition?function(e,t){return t&&!!(e.compareDocumentPosition(t)&16)}:function(e,t){while(t=t.parentNode)if(t===e)return!0;return!1},nt.attr=function(e,t){var n,r=o(e);return r||(t=t.toLowerCase()),(n=i.attrHandle[t])?n(e):r||Y?e.getAttribute(t):(n=e.getAttributeNode(t),n?typeof e[t]=="boolean"?e[t]?t:null:n.specified?n.value:null:null)},i=nt.selectors={cacheLength:50,createPseudo:N,match:J,attrHandle:G?{}:{href:function(e){return e.getAttribute("href",2)},type:function(e){return e.getAttribute("type")}},find:{ID:r?function(e,t,n){if(typeof t.getElementById!==p&&!n){var r=t.getElementById(e);return r&&r.parentNode?[r]:[]}}:function(e,n,r){if(typeof n.getElementById!==p&&!r){var i=n.getElementById(e);return i?i.id===e||typeof i.getAttributeNode!==p&&i.getAttributeNode("id").value===e?[i]:t:[]}},TAG:Q?function(e,t){if(typeof t.getElementsByTagName!==p)return t.getElementsByTagName(e)}:function(e,t){var n=t.getElementsByTagName(e);if(e==="*"){var r,i=[],s=0;for(;r=n[s];s++)r.nodeType===1&&i.push(r);return i}return n},NAME:et&&function(e,t){if(typeof t.getElementsByName!==p)return t.getElementsByName(name)},CLASS:Z&&function(e,t,n){if(typeof t.getElementsByClassName!==p&&!n)return t.getElementsByClassName(e)}},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace($,""),e[3]=(e[4]||e[5]||"").replace($,""),e[2]==="~="&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),e[1]==="nth"?(e[2]||nt.error(e[0]),e[3]=+(e[3]?e[4]+(e[5]||1):2*(e[2]==="even"||e[2]==="odd")),e[4]=+(e[6]+e[7]||e[2]==="odd")):e[2]&&nt.error(e[0]),e},PSEUDO:function(e){var t,n;if(J.CHILD.test(e[0]))return null;if(e[3])e[2]=e[3];else if(t=e[4])q.test(t)&&(n=ut(t,!0))&&(n=t.indexOf(")",t.length-n)-t.length)&&(t=t.slice(0,n),e[0]=e[0].slice(0,n)),e[2]=t;return e.slice(0,3)}},filter:{ID:r?function(e){return e=e.replace($,""),function(t){return t.getAttribute("id")===e}}:function(e){return e=e.replace($,""),function(t){var n=typeof t.getAttributeNode!==p&&t.getAttributeNode("id");return n&&n.value===e}},TAG:function(e){return e==="*"?function(){return!0}:(e=e.replace($,"").toLowerCase(),function(t){return t.nodeName&&t.nodeName.toLowerCase()===e})},CLASS:function(e){var t=k[d][e+" "];return t||(t=new RegExp("(^|"+O+")"+e+"("+O+"|$)"))&&k(e,function(e){return t.test(e.className||typeof e.getAttribute!==p&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r,i){var s=nt.attr(r,e);return s==null?t==="!=":t?(s+="",t==="="?s===n:t==="!="?s!==n:t==="^="?n&&s.indexOf(n)===0:t==="*="?n&&s.indexOf(n)>-1:t==="$="?n&&s.substr(s.length-n.length)===n:t==="~="?(" "+s+" ").indexOf(n)>-1:t==="|="?s===n||s.substr(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r){return e==="nth"?function(e){var t,i,s=e.parentNode;if(n===1&&r===0)return!0;if(s){i=0;for(t=s.firstChild;t;t=t.nextSibling)if(t.nodeType===1){i++;if(e===t)break}}return i-=r,i===n||i%n===0&&i/n>=0}:function(t){var n=t;switch(e){case"only":case"first":while(n=n.previousSibling)if(n.nodeType===1)return!1;if(e==="first")return!0;n=t;case"last":while(n=n.nextSibling)if(n.nodeType===1)return!1;return!0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||nt.error("unsupported pseudo: "+e);return r[d]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?N(function(e,n){var i,s=r(e,t),o=s.length;while(o--)i=T.call(e,s[o]),e[i]=!(n[i]=s[o])}):function(e){return r(e,0,n)}):r}},pseudos:{not:N(function(e){var t=[],n=[],r=a(e.replace(j,"$1"));return r[d]?N(function(e,t,n,i){var s,o=r(e,null,i,[]),u=e.length;while(u--)if(s=o[u])e[u]=!(t[u]=s)}):function(e,i,s){return t[0]=e,r(t,null,s,n),!n.pop()}}),has:N(function(e){return function(t){return nt(e,t).length>0}}),contains:N(function(e){return function(t){return(t.textContent||t.innerText||s(t)).indexOf(e)>-1}}),enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&!!e.checked||t==="option"&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},parent:function(e){return!i.pseudos.empty(e)},empty:function(e){var t;e=e.firstChild;while(e){if(e.nodeName>"@"||(t=e.nodeType)===3||t===4)return!1;e=e.nextSibling}return!0},header:function(e){return X.test(e.nodeName)},text:function(e){var t,n;return e.nodeName.toLowerCase()==="input"&&(t=e.type)==="text"&&((n=e.getAttribute("type"))==null||n.toLowerCase()===t)},radio:rt("radio"),checkbox:rt("checkbox"),file:rt("file"),password:rt("password"),image:rt("image"),submit:it("submit"),reset:it("reset"),button:function(e){var t=e.nodeName.toLowerCase();return t==="input"&&e.type==="button"||t==="button"},input:function(e){return V.test(e.nodeName)},focus:function(e){var t=e.ownerDocument;return e===t.activeElement&&(!t.hasFocus||t.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},active:function(e){return e===e.ownerDocument.activeElement},first:st(function(){return[0]}),last:st(function(e,t){return[t-1]}),eq:st(function(e,t,n){return[n<0?n+t:n]}),even:st(function(e,t){for(var n=0;n=0;)e.push(r);return e}),gt:st(function(e,t,n){for(var r=n<0?n+t:n;++r",e.querySelectorAll("[selected]").length||i.push("\\["+O+"*(?:checked|disabled|ismap|multiple|readonly|selected|value)"),e.querySelectorAll(":checked").length||i.push(":checked")}),K(function(e){e.innerHTML="

",e.querySelectorAll("[test^='']").length&&i.push("[*^$]="+O+"*(?:\"\"|'')"),e.innerHTML="",e.querySelectorAll(":enabled").length||i.push(":enabled",":disabled")}),i=new RegExp(i.join("|")),vt=function(e,r,s,o,u){if(!o&&!u&&!i.test(e)){var a,f,l=!0,c=d,h=r,p=r.nodeType===9&&e;if(r.nodeType===1&&r.nodeName.toLowerCase()!=="object"){a=ut(e),(l=r.getAttribute("id"))?c=l.replace(n,"\\$&"):r.setAttribute("id",c),c="[id='"+c+"'] ",f=a.length;while(f--)a[f]=c+a[f].join("");h=z.test(e)&&r.parentNode||r,p=a.join(",")}if(p)try{return S.apply(s,x.call(h.querySelectorAll(p),0)),s}catch(v){}finally{l||r.removeAttribute("id")}}return t(e,r,s,o,u)},u&&(K(function(t){e=u.call(t,"div");try{u.call(t,"[test!='']:sizzle"),s.push("!=",H)}catch(n){}}),s=new RegExp(s.join("|")),nt.matchesSelector=function(t,n){n=n.replace(r,"='$1']");if(!o(t)&&!s.test(n)&&!i.test(n))try{var a=u.call(t,n);if(a||e||t.document&&t.document.nodeType!==11)return a}catch(f){}return nt(n,null,null,[t]).length>0})}(),i.pseudos.nth=i.pseudos.eq,i.filters=mt.prototype=i.pseudos,i.setFilters=new mt,nt.attr=v.attr,v.find=nt,v.expr=nt.selectors,v.expr[":"]=v.expr.pseudos,v.unique=nt.uniqueSort,v.text=nt.getText,v.isXMLDoc=nt.isXML,v.contains=nt.contains}(e);var nt=/Until$/,rt=/^(?:parents|prev(?:Until|All))/,it=/^.[^:#\[\.,]*$/,st=v.expr.match.needsContext,ot={children:!0,contents:!0,next:!0,prev:!0};v.fn.extend({find:function(e){var t,n,r,i,s,o,u=this;if(typeof e!="string")return v(e).filter(function(){for(t=0,n=u.length;t0)for(i=r;i=0:v.filter(e,this).length>0:this.filter(e).length>0)},closest:function(e,t){var n,r=0,i=this.length,s=[],o=st.test(e)||typeof e!="string"?v(e,t||this.context):0;for(;r-1:v.find.matchesSelector(n,e)){s.push(n);break}n=n.parentNode}}return s=s.length>1?v.unique(s):s,this.pushStack(s,"closest",e)},index:function(e){return e?typeof e=="string"?v.inArray(this[0],v(e)):v.inArray(e.jquery?e[0]:e,this):this[0]&&this[0].parentNode?this.prevAll().length:-1},add:function(e,t){var n=typeof e=="string"?v(e,t):v.makeArray(e&&e.nodeType?[e]:e),r=v.merge(this.get(),n);return this.pushStack(ut(n[0])||ut(r[0])?r:v.unique(r))},addBack:function(e){return this.add(e==null?this.prevObject:this.prevObject.filter(e))}}),v.fn.andSelf=v.fn.addBack,v.each({parent:function(e){var t=e.parentNode;return t&&t.nodeType!==11?t:null},parents:function(e){return v.dir(e,"parentNode")},parentsUntil:function(e,t,n){return v.dir(e,"parentNode",n)},next:function(e){return at(e,"nextSibling")},prev:function(e){return at(e,"previousSibling")},nextAll:function(e){return v.dir(e,"nextSibling")},prevAll:function(e){return v.dir(e,"previousSibling")},nextUntil:function(e,t,n){return v.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return v.dir(e,"previousSibling",n)},siblings:function(e){return v.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return v.sibling(e.firstChild)},contents:function(e){return v.nodeName(e,"iframe")?e.contentDocument||e.contentWindow.document:v.merge([],e.childNodes)}},function(e,t){v.fn[e]=function(n,r){var i=v.map(this,t,n);return nt.test(e)||(r=n),r&&typeof r=="string"&&(i=v.filter(r,i)),i=this.length>1&&!ot[e]?v.unique(i):i,this.length>1&&rt.test(e)&&(i=i.reverse()),this.pushStack(i,e,l.call(arguments).join(","))}}),v.extend({filter:function(e,t,n){return n&&(e=":not("+e+")"),t.length===1?v.find.matchesSelector(t[0],e)?[t[0]]:[]:v.find.matches(e,t)},dir:function(e,n,r){var i=[],s=e[n];while(s&&s.nodeType!==9&&(r===t||s.nodeType!==1||!v(s).is(r)))s.nodeType===1&&i.push(s),s=s[n];return i},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)e.nodeType===1&&e!==t&&n.push(e);return n}});var ct="abbr|article|aside|audio|bdi|canvas|data|datalist|details|figcaption|figure|footer|header|hgroup|mark|meter|nav|output|progress|section|summary|time|video",ht=/ jQuery\d+="(?:null|\d+)"/g,pt=/^\s+/,dt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,vt=/<([\w:]+)/,mt=/]","i"),Et=/^(?:checkbox|radio)$/,St=/checked\s*(?:[^=]|=\s*.checked.)/i,xt=/\/(java|ecma)script/i,Tt=/^\s*\s*$/g,Nt={option:[1,""],legend:[1,"
","
"],thead:[1,"","
"],tr:[2,"","
"],td:[3,"","
"],col:[2,"","
"],area:[1,"",""],_default:[0,"",""]},Ct=lt(i),kt=Ct.appendChild(i.createElement("div"));Nt.optgroup=Nt.option,Nt.tbody=Nt.tfoot=Nt.colgroup=Nt.caption=Nt.thead,Nt.th=Nt.td,v.support.htmlSerialize||(Nt._default=[1,"X
","
"]),v.fn.extend({text:function(e){return v.access(this,function(e){return e===t?v.text(this):this.empty().append((this[0]&&this[0].ownerDocument||i).createTextNode(e))},null,e,arguments.length)},wrapAll:function(e){if(v.isFunction(e))return this.each(function(t){v(this).wrapAll(e.call(this,t))});if(this[0]){var t=v(e,this[0].ownerDocument).eq(0).clone(!0);this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstChild&&e.firstChild.nodeType===1)e=e.firstChild;return e}).append(this)}return this},wrapInner:function(e){return v.isFunction(e)?this.each(function(t){v(this).wrapInner(e.call(this,t))}):this.each(function(){var t=v(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=v.isFunction(e);return this.each(function(n){v(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){v.nodeName(this,"body")||v(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.appendChild(e)})},prepend:function(){return this.domManip(arguments,!0,function(e){(this.nodeType===1||this.nodeType===11)&&this.insertBefore(e,this.firstChild)})},before:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(e,this),"before",this.selector)}},after:function(){if(!ut(this[0]))return this.domManip(arguments,!1,function(e){this.parentNode.insertBefore(e,this.nextSibling)});if(arguments.length){var e=v.clean(arguments);return this.pushStack(v.merge(this,e),"after",this.selector)}},remove:function(e,t){var n,r=0;for(;(n=this[r])!=null;r++)if(!e||v.filter(e,[n]).length)!t&&n.nodeType===1&&(v.cleanData(n.getElementsByTagName("*")),v.cleanData([n])),n.parentNode&&n.parentNode.removeChild(n);return this},empty:function(){var e,t=0;for(;(e=this[t])!=null;t++){e.nodeType===1&&v.cleanData(e.getElementsByTagName("*"));while(e.firstChild)e.removeChild(e.firstChild)}return this},clone:function(e,t){return e=e==null?!1:e,t=t==null?e:t,this.map(function(){return v.clone(this,e,t)})},html:function(e){return v.access(this,function(e){var n=this[0]||{},r=0,i=this.length;if(e===t)return n.nodeType===1?n.innerHTML.replace(ht,""):t;if(typeof e=="string"&&!yt.test(e)&&(v.support.htmlSerialize||!wt.test(e))&&(v.support.leadingWhitespace||!pt.test(e))&&!Nt[(vt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(dt,"<$1>");try{for(;r1&&typeof f=="string"&&St.test(f))return this.each(function(){v(this).domManip(e,n,r)});if(v.isFunction(f))return this.each(function(i){var s=v(this);e[0]=f.call(this,i,n?s.html():t),s.domManip(e,n,r)});if(this[0]){i=v.buildFragment(e,this,l),o=i.fragment,s=o.firstChild,o.childNodes.length===1&&(o=s);if(s){n=n&&v.nodeName(s,"tr");for(u=i.cacheable||c-1;a0?this.clone(!0):this).get(),v(o[i])[t](r),s=s.concat(r);return this.pushStack(s,e,o.selector)}}),v.extend({clone:function(e,t,n){var r,i,s,o;v.support.html5Clone||v.isXMLDoc(e)||!wt.test("<"+e.nodeName+">")?o=e.cloneNode(!0):(kt.innerHTML=e.outerHTML,kt.removeChild(o=kt.firstChild));if((!v.support.noCloneEvent||!v.support.noCloneChecked)&&(e.nodeType===1||e.nodeType===11)&&!v.isXMLDoc(e)){Ot(e,o),r=Mt(e),i=Mt(o);for(s=0;r[s];++s)i[s]&&Ot(r[s],i[s])}if(t){At(e,o);if(n){r=Mt(e),i=Mt(o);for(s=0;r[s];++s)At(r[s],i[s])}}return r=i=null,o},clean:function(e,t,n,r){var s,o,u,a,f,l,c,h,p,d,m,g,y=t===i&&Ct,b=[];if(!t||typeof t.createDocumentFragment=="undefined")t=i;for(s=0;(u=e[s])!=null;s++){typeof u=="number"&&(u+="");if(!u)continue;if(typeof u=="string")if(!gt.test(u))u=t.createTextNode(u);else{y=y||lt(t),c=t.createElement("div"),y.appendChild(c),u=u.replace(dt,"<$1>"),a=(vt.exec(u)||["",""])[1].toLowerCase(),f=Nt[a]||Nt._default,l=f[0],c.innerHTML=f[1]+u+f[2];while(l--)c=c.lastChild;if(!v.support.tbody){h=mt.test(u),p=a==="table"&&!h?c.firstChild&&c.firstChild.childNodes:f[1]===""&&!h?c.childNodes:[];for(o=p.length-1;o>=0;--o)v.nodeName(p[o],"tbody")&&!p[o].childNodes.length&&p[o].parentNode.removeChild(p[o])}!v.support.leadingWhitespace&&pt.test(u)&&c.insertBefore(t.createTextNode(pt.exec(u)[0]),c.firstChild),u=c.childNodes,c.parentNode.removeChild(c)}u.nodeType?b.push(u):v.merge(b,u)}c&&(u=c=y=null);if(!v.support.appendChecked)for(s=0;(u=b[s])!=null;s++)v.nodeName(u,"input")?_t(u):typeof u.getElementsByTagName!="undefined"&&v.grep(u.getElementsByTagName("input"),_t);if(n){m=function(e){if(!e.type||xt.test(e.type))return r?r.push(e.parentNode?e.parentNode.removeChild(e):e):n.appendChild(e)};for(s=0;(u=b[s])!=null;s++)if(!v.nodeName(u,"script")||!m(u))n.appendChild(u),typeof u.getElementsByTagName!="undefined"&&(g=v.grep(v.merge([],u.getElementsByTagName("script")),m),b.splice.apply(b,[s+1,0].concat(g)),s+=g.length)}return b},cleanData:function(e,t){var n,r,i,s,o=0,u=v.expando,a=v.cache,f=v.support.deleteExpando,l=v.event.special;for(;(i=e[o])!=null;o++)if(t||v.acceptData(i)){r=i[u],n=r&&a[r];if(n){if(n.events)for(s in n.events)l[s]?v.event.remove(i,s):v.removeEvent(i,s,n.handle);a[r]&&(delete a[r],f?delete i[u]:i.removeAttribute?i.removeAttribute(u):i[u]=null,v.deletedIds.push(r))}}}}),function(){var e,t;v.uaMatch=function(e){e=e.toLowerCase();var t=/(chrome)[ \/]([\w.]+)/.exec(e)||/(webkit)[ \/]([\w.]+)/.exec(e)||/(opera)(?:.*version|)[ \/]([\w.]+)/.exec(e)||/(msie) ([\w.]+)/.exec(e)||e.indexOf("compatible")<0&&/(mozilla)(?:.*? rv:([\w.]+)|)/.exec(e)||[];return{browser:t[1]||"",version:t[2]||"0"}},e=v.uaMatch(o.userAgent),t={},e.browser&&(t[e.browser]=!0,t.version=e.version),t.chrome?t.webkit=!0:t.webkit&&(t.safari=!0),v.browser=t,v.sub=function(){function e(t,n){return new e.fn.init(t,n)}v.extend(!0,e,this),e.superclass=this,e.fn=e.prototype=this(),e.fn.constructor=e,e.sub=this.sub,e.fn.init=function(r,i){return i&&i instanceof v&&!(i instanceof e)&&(i=e(i)),v.fn.init.call(this,r,i,t)},e.fn.init.prototype=e.fn;var t=e(i);return e}}();var Dt,Pt,Ht,Bt=/alpha\([^)]*\)/i,jt=/opacity=([^)]*)/,Ft=/^(top|right|bottom|left)$/,It=/^(none|table(?!-c[ea]).+)/,qt=/^margin/,Rt=new RegExp("^("+m+")(.*)$","i"),Ut=new RegExp("^("+m+")(?!px)[a-z%]+$","i"),zt=new RegExp("^([-+])=("+m+")","i"),Wt={BODY:"block"},Xt={position:"absolute",visibility:"hidden",display:"block"},Vt={letterSpacing:0,fontWeight:400},$t=["Top","Right","Bottom","Left"],Jt=["Webkit","O","Moz","ms"],Kt=v.fn.toggle;v.fn.extend({css:function(e,n){return v.access(this,function(e,n,r){return r!==t?v.style(e,n,r):v.css(e,n)},e,n,arguments.length>1)},show:function(){return Yt(this,!0)},hide:function(){return Yt(this)},toggle:function(e,t){var n=typeof e=="boolean";return v.isFunction(e)&&v.isFunction(t)?Kt.apply(this,arguments):this.each(function(){(n?e:Gt(this))?v(this).show():v(this).hide()})}}),v.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=Dt(e,"opacity");return n===""?"1":n}}}},cssNumber:{fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":v.support.cssFloat?"cssFloat":"styleFloat"},style:function(e,n,r,i){if(!e||e.nodeType===3||e.nodeType===8||!e.style)return;var s,o,u,a=v.camelCase(n),f=e.style;n=v.cssProps[a]||(v.cssProps[a]=Qt(f,a)),u=v.cssHooks[n]||v.cssHooks[a];if(r===t)return u&&"get"in u&&(s=u.get(e,!1,i))!==t?s:f[n];o=typeof r,o==="string"&&(s=zt.exec(r))&&(r=(s[1]+1)*s[2]+parseFloat(v.css(e,n)),o="number");if(r==null||o==="number"&&isNaN(r))return;o==="number"&&!v.cssNumber[a]&&(r+="px");if(!u||!("set"in u)||(r=u.set(e,r,i))!==t)try{f[n]=r}catch(l){}},css:function(e,n,r,i){var s,o,u,a=v.camelCase(n);return n=v.cssProps[a]||(v.cssProps[a]=Qt(e.style,a)),u=v.cssHooks[n]||v.cssHooks[a],u&&"get"in u&&(s=u.get(e,!0,i)),s===t&&(s=Dt(e,n)),s==="normal"&&n in Vt&&(s=Vt[n]),r||i!==t?(o=parseFloat(s),r||v.isNumeric(o)?o||0:s):s},swap:function(e,t,n){var r,i,s={};for(i in t)s[i]=e.style[i],e.style[i]=t[i];r=n.call(e);for(i in t)e.style[i]=s[i];return r}}),e.getComputedStyle?Dt=function(t,n){var r,i,s,o,u=e.getComputedStyle(t,null),a=t.style;return u&&(r=u.getPropertyValue(n)||u[n],r===""&&!v.contains(t.ownerDocument,t)&&(r=v.style(t,n)),Ut.test(r)&&qt.test(n)&&(i=a.width,s=a.minWidth,o=a.maxWidth,a.minWidth=a.maxWidth=a.width=r,r=u.width,a.width=i,a.minWidth=s,a.maxWidth=o)),r}:i.documentElement.currentStyle&&(Dt=function(e,t){var n,r,i=e.currentStyle&&e.currentStyle[t],s=e.style;return i==null&&s&&s[t]&&(i=s[t]),Ut.test(i)&&!Ft.test(t)&&(n=s.left,r=e.runtimeStyle&&e.runtimeStyle.left,r&&(e.runtimeStyle.left=e.currentStyle.left),s.left=t==="fontSize"?"1em":i,i=s.pixelLeft+"px",s.left=n,r&&(e.runtimeStyle.left=r)),i===""?"auto":i}),v.each(["height","width"],function(e,t){v.cssHooks[t]={get:function(e,n,r){if(n)return e.offsetWidth===0&&It.test(Dt(e,"display"))?v.swap(e,Xt,function(){return tn(e,t,r)}):tn(e,t,r)},set:function(e,n,r){return Zt(e,n,r?en(e,t,r,v.support.boxSizing&&v.css(e,"boxSizing")==="border-box"):0)}}}),v.support.opacity||(v.cssHooks.opacity={get:function(e,t){return jt.test((t&&e.currentStyle?e.currentStyle.filter:e.style.filter)||"")?.01*parseFloat(RegExp.$1)+"":t?"1":""},set:function(e,t){var n=e.style,r=e.currentStyle,i=v.isNumeric(t)?"alpha(opacity="+t*100+")":"",s=r&&r.filter||n.filter||"";n.zoom=1;if(t>=1&&v.trim(s.replace(Bt,""))===""&&n.removeAttribute){n.removeAttribute("filter");if(r&&!r.filter)return}n.filter=Bt.test(s)?s.replace(Bt,i):s+" "+i}}),v(function(){v.support.reliableMarginRight||(v.cssHooks.marginRight={get:function(e,t){return v.swap(e,{display:"inline-block"},function(){if(t)return Dt(e,"marginRight")})}}),!v.support.pixelPosition&&v.fn.position&&v.each(["top","left"],function(e,t){v.cssHooks[t]={get:function(e,n){if(n){var r=Dt(e,t);return Ut.test(r)?v(e).position()[t]+"px":r}}}})}),v.expr&&v.expr.filters&&(v.expr.filters.hidden=function(e){return e.offsetWidth===0&&e.offsetHeight===0||!v.support.reliableHiddenOffsets&&(e.style&&e.style.display||Dt(e,"display"))==="none"},v.expr.filters.visible=function(e){return!v.expr.filters.hidden(e)}),v.each({margin:"",padding:"",border:"Width"},function(e,t){v.cssHooks[e+t]={expand:function(n){var r,i=typeof n=="string"?n.split(" "):[n],s={};for(r=0;r<4;r++)s[e+$t[r]+t]=i[r]||i[r-2]||i[0];return s}},qt.test(e)||(v.cssHooks[e+t].set=Zt)});var rn=/%20/g,sn=/\[\]$/,on=/\r?\n/g,un=/^(?:color|date|datetime|datetime-local|email|hidden|month|number|password|range|search|tel|text|time|url|week)$/i,an=/^(?:select|textarea)/i;v.fn.extend({serialize:function(){return v.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?v.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||an.test(this.nodeName)||un.test(this.type))}).map(function(e,t){var n=v(this).val();return n==null?null:v.isArray(n)?v.map(n,function(e,n){return{name:t.name,value:e.replace(on,"\r\n")}}):{name:t.name,value:n.replace(on,"\r\n")}}).get()}}),v.param=function(e,n){var r,i=[],s=function(e,t){t=v.isFunction(t)?t():t==null?"":t,i[i.length]=encodeURIComponent(e)+"="+encodeURIComponent(t)};n===t&&(n=v.ajaxSettings&&v.ajaxSettings.traditional);if(v.isArray(e)||e.jquery&&!v.isPlainObject(e))v.each(e,function(){s(this.name,this.value)});else for(r in e)fn(r,e[r],n,s);return i.join("&").replace(rn,"+")};var ln,cn,hn=/#.*$/,pn=/^(.*?):[ \t]*([^\r\n]*)\r?$/mg,dn=/^(?:about|app|app\-storage|.+\-extension|file|res|widget):$/,vn=/^(?:GET|HEAD)$/,mn=/^\/\//,gn=/\?/,yn=/)<[^<]*)*<\/script>/gi,bn=/([?&])_=[^&]*/,wn=/^([\w\+\.\-]+:)(?:\/\/([^\/?#:]*)(?::(\d+)|)|)/,En=v.fn.load,Sn={},xn={},Tn=["*/"]+["*"];try{cn=s.href}catch(Nn){cn=i.createElement("a"),cn.href="",cn=cn.href}ln=wn.exec(cn.toLowerCase())||[],v.fn.load=function(e,n,r){if(typeof e!="string"&&En)return En.apply(this,arguments);if(!this.length)return this;var i,s,o,u=this,a=e.indexOf(" ");return a>=0&&(i=e.slice(a,e.length),e=e.slice(0,a)),v.isFunction(n)?(r=n,n=t):n&&typeof n=="object"&&(s="POST"),v.ajax({url:e,type:s,dataType:"html",data:n,complete:function(e,t){r&&u.each(r,o||[e.responseText,t,e])}}).done(function(e){o=arguments,u.html(i?v("
").append(e.replace(yn,"")).find(i):e)}),this},v.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),function(e,t){v.fn[t]=function(e){return this.on(t,e)}}),v.each(["get","post"],function(e,n){v[n]=function(e,r,i,s){return v.isFunction(r)&&(s=s||i,i=r,r=t),v.ajax({type:n,url:e,data:r,success:i,dataType:s})}}),v.extend({getScript:function(e,n){return v.get(e,t,n,"script")},getJSON:function(e,t,n){return v.get(e,t,n,"json")},ajaxSetup:function(e,t){return t?Ln(e,v.ajaxSettings):(t=e,e=v.ajaxSettings),Ln(e,t),e},ajaxSettings:{url:cn,isLocal:dn.test(ln[1]),global:!0,type:"GET",contentType:"application/x-www-form-urlencoded; charset=UTF-8",processData:!0,async:!0,accepts:{xml:"application/xml, text/xml",html:"text/html",text:"text/plain",json:"application/json, text/javascript","*":Tn},contents:{xml:/xml/,html:/html/,json:/json/},responseFields:{xml:"responseXML",text:"responseText"},converters:{"* text":e.String,"text html":!0,"text json":v.parseJSON,"text xml":v.parseXML},flatOptions:{context:!0,url:!0}},ajaxPrefilter:Cn(Sn),ajaxTransport:Cn(xn),ajax:function(e,n){function T(e,n,s,a){var l,y,b,w,S,T=n;if(E===2)return;E=2,u&&clearTimeout(u),o=t,i=a||"",x.readyState=e>0?4:0,s&&(w=An(c,x,s));if(e>=200&&e<300||e===304)c.ifModified&&(S=x.getResponseHeader("Last-Modified"),S&&(v.lastModified[r]=S),S=x.getResponseHeader("Etag"),S&&(v.etag[r]=S)),e===304?(T="notmodified",l=!0):(l=On(c,w),T=l.state,y=l.data,b=l.error,l=!b);else{b=T;if(!T||e)T="error",e<0&&(e=0)}x.status=e,x.statusText=(n||T)+"",l?d.resolveWith(h,[y,T,x]):d.rejectWith(h,[x,T,b]),x.statusCode(g),g=t,f&&p.trigger("ajax"+(l?"Success":"Error"),[x,c,l?y:b]),m.fireWith(h,[x,T]),f&&(p.trigger("ajaxComplete",[x,c]),--v.active||v.event.trigger("ajaxStop"))}typeof e=="object"&&(n=e,e=t),n=n||{};var r,i,s,o,u,a,f,l,c=v.ajaxSetup({},n),h=c.context||c,p=h!==c&&(h.nodeType||h instanceof v)?v(h):v.event,d=v.Deferred(),m=v.Callbacks("once memory"),g=c.statusCode||{},b={},w={},E=0,S="canceled",x={readyState:0,setRequestHeader:function(e,t){if(!E){var n=e.toLowerCase();e=w[n]=w[n]||e,b[e]=t}return this},getAllResponseHeaders:function(){return E===2?i:null},getResponseHeader:function(e){var n;if(E===2){if(!s){s={};while(n=pn.exec(i))s[n[1].toLowerCase()]=n[2]}n=s[e.toLowerCase()]}return n===t?null:n},overrideMimeType:function(e){return E||(c.mimeType=e),this},abort:function(e){return e=e||S,o&&o.abort(e),T(0,e),this}};d.promise(x),x.success=x.done,x.error=x.fail,x.complete=m.add,x.statusCode=function(e){if(e){var t;if(E<2)for(t in e)g[t]=[g[t],e[t]];else t=e[x.status],x.always(t)}return this},c.url=((e||c.url)+"").replace(hn,"").replace(mn,ln[1]+"//"),c.dataTypes=v.trim(c.dataType||"*").toLowerCase().split(y),c.crossDomain==null&&(a=wn.exec(c.url.toLowerCase()),c.crossDomain=!(!a||a[1]===ln[1]&&a[2]===ln[2]&&(a[3]||(a[1]==="http:"?80:443))==(ln[3]||(ln[1]==="http:"?80:443)))),c.data&&c.processData&&typeof c.data!="string"&&(c.data=v.param(c.data,c.traditional)),kn(Sn,c,n,x);if(E===2)return x;f=c.global,c.type=c.type.toUpperCase(),c.hasContent=!vn.test(c.type),f&&v.active++===0&&v.event.trigger("ajaxStart");if(!c.hasContent){c.data&&(c.url+=(gn.test(c.url)?"&":"?")+c.data,delete c.data),r=c.url;if(c.cache===!1){var N=v.now(),C=c.url.replace(bn,"$1_="+N);c.url=C+(C===c.url?(gn.test(c.url)?"&":"?")+"_="+N:"")}}(c.data&&c.hasContent&&c.contentType!==!1||n.contentType)&&x.setRequestHeader("Content-Type",c.contentType),c.ifModified&&(r=r||c.url,v.lastModified[r]&&x.setRequestHeader("If-Modified-Since",v.lastModified[r]),v.etag[r]&&x.setRequestHeader("If-None-Match",v.etag[r])),x.setRequestHeader("Accept",c.dataTypes[0]&&c.accepts[c.dataTypes[0]]?c.accepts[c.dataTypes[0]]+(c.dataTypes[0]!=="*"?", "+Tn+"; q=0.01":""):c.accepts["*"]);for(l in c.headers)x.setRequestHeader(l,c.headers[l]);if(!c.beforeSend||c.beforeSend.call(h,x,c)!==!1&&E!==2){S="abort";for(l in{success:1,error:1,complete:1})x[l](c[l]);o=kn(xn,c,n,x);if(!o)T(-1,"No Transport");else{x.readyState=1,f&&p.trigger("ajaxSend",[x,c]),c.async&&c.timeout>0&&(u=setTimeout(function(){x.abort("timeout")},c.timeout));try{E=1,o.send(b,T)}catch(k){if(!(E<2))throw k;T(-1,k)}}return x}return x.abort()},active:0,lastModified:{},etag:{}});var Mn=[],_n=/\?/,Dn=/(=)\?(?=&|$)|\?\?/,Pn=v.now();v.ajaxSetup({jsonp:"callback",jsonpCallback:function(){var e=Mn.pop()||v.expando+"_"+Pn++;return this[e]=!0,e}}),v.ajaxPrefilter("json jsonp",function(n,r,i){var s,o,u,a=n.data,f=n.url,l=n.jsonp!==!1,c=l&&Dn.test(f),h=l&&!c&&typeof a=="string"&&!(n.contentType||"").indexOf("application/x-www-form-urlencoded")&&Dn.test(a);if(n.dataTypes[0]==="jsonp"||c||h)return s=n.jsonpCallback=v.isFunction(n.jsonpCallback)?n.jsonpCallback():n.jsonpCallback,o=e[s],c?n.url=f.replace(Dn,"$1"+s):h?n.data=a.replace(Dn,"$1"+s):l&&(n.url+=(_n.test(f)?"&":"?")+n.jsonp+"="+s),n.converters["script json"]=function(){return u||v.error(s+" was not called"),u[0]},n.dataTypes[0]="json",e[s]=function(){u=arguments},i.always(function(){e[s]=o,n[s]&&(n.jsonpCallback=r.jsonpCallback,Mn.push(s)),u&&v.isFunction(o)&&o(u[0]),u=o=t}),"script"}),v.ajaxSetup({accepts:{script:"text/javascript, application/javascript, application/ecmascript, application/x-ecmascript"},contents:{script:/javascript|ecmascript/},converters:{"text script":function(e){return v.globalEval(e),e}}}),v.ajaxPrefilter("script",function(e){e.cache===t&&(e.cache=!1),e.crossDomain&&(e.type="GET",e.global=!1)}),v.ajaxTransport("script",function(e){if(e.crossDomain){var n,r=i.head||i.getElementsByTagName("head")[0]||i.documentElement;return{send:function(s,o){n=i.createElement("script"),n.async="async",e.scriptCharset&&(n.charset=e.scriptCharset),n.src=e.url,n.onload=n.onreadystatechange=function(e,i){if(i||!n.readyState||/loaded|complete/.test(n.readyState))n.onload=n.onreadystatechange=null,r&&n.parentNode&&r.removeChild(n),n=t,i||o(200,"success")},r.insertBefore(n,r.firstChild)},abort:function(){n&&n.onload(0,1)}}}});var Hn,Bn=e.ActiveXObject?function(){for(var e in Hn)Hn[e](0,1)}:!1,jn=0;v.ajaxSettings.xhr=e.ActiveXObject?function(){return!this.isLocal&&Fn()||In()}:Fn,function(e){v.extend(v.support,{ajax:!!e,cors:!!e&&"withCredentials"in e})}(v.ajaxSettings.xhr()),v.support.ajax&&v.ajaxTransport(function(n){if(!n.crossDomain||v.support.cors){var r;return{send:function(i,s){var o,u,a=n.xhr();n.username?a.open(n.type,n.url,n.async,n.username,n.password):a.open(n.type,n.url,n.async);if(n.xhrFields)for(u in n.xhrFields)a[u]=n.xhrFields[u];n.mimeType&&a.overrideMimeType&&a.overrideMimeType(n.mimeType),!n.crossDomain&&!i["X-Requested-With"]&&(i["X-Requested-With"]="XMLHttpRequest");try{for(u in i)a.setRequestHeader(u,i[u])}catch(f){}a.send(n.hasContent&&n.data||null),r=function(e,i){var u,f,l,c,h;try{if(r&&(i||a.readyState===4)){r=t,o&&(a.onreadystatechange=v.noop,Bn&&delete Hn[o]);if(i)a.readyState!==4&&a.abort();else{u=a.status,l=a.getAllResponseHeaders(),c={},h=a.responseXML,h&&h.documentElement&&(c.xml=h);try{c.text=a.responseText}catch(p){}try{f=a.statusText}catch(p){f=""}!u&&n.isLocal&&!n.crossDomain?u=c.text?200:404:u===1223&&(u=204)}}}catch(d){i||s(-1,d)}c&&s(u,f,c,l)},n.async?a.readyState===4?setTimeout(r,0):(o=++jn,Bn&&(Hn||(Hn={},v(e).unload(Bn)),Hn[o]=r),a.onreadystatechange=r):r()},abort:function(){r&&r(0,1)}}}});var qn,Rn,Un=/^(?:toggle|show|hide)$/,zn=new RegExp("^(?:([-+])=|)("+m+")([a-z%]*)$","i"),Wn=/queueHooks$/,Xn=[Gn],Vn={"*":[function(e,t){var n,r,i=this.createTween(e,t),s=zn.exec(t),o=i.cur(),u=+o||0,a=1,f=20;if(s){n=+s[2],r=s[3]||(v.cssNumber[e]?"":"px");if(r!=="px"&&u){u=v.css(i.elem,e,!0)||n||1;do a=a||".5",u/=a,v.style(i.elem,e,u+r);while(a!==(a=i.cur()/o)&&a!==1&&--f)}i.unit=r,i.start=u,i.end=s[1]?u+(s[1]+1)*n:n}return i}]};v.Animation=v.extend(Kn,{tweener:function(e,t){v.isFunction(e)?(t=e,e=["*"]):e=e.split(" ");var n,r=0,i=e.length;for(;r-1,f={},l={},c,h;a?(l=i.position(),c=l.top,h=l.left):(c=parseFloat(o)||0,h=parseFloat(u)||0),v.isFunction(t)&&(t=t.call(e,n,s)),t.top!=null&&(f.top=t.top-s.top+c),t.left!=null&&(f.left=t.left-s.left+h),"using"in t?t.using.call(e,f):i.css(f)}},v.fn.extend({position:function(){if(!this[0])return;var e=this[0],t=this.offsetParent(),n=this.offset(),r=er.test(t[0].nodeName)?{top:0,left:0}:t.offset();return n.top-=parseFloat(v.css(e,"marginTop"))||0,n.left-=parseFloat(v.css(e,"marginLeft"))||0,r.top+=parseFloat(v.css(t[0],"borderTopWidth"))||0,r.left+=parseFloat(v.css(t[0],"borderLeftWidth"))||0,{top:n.top-r.top,left:n.left-r.left}},offsetParent:function(){return this.map(function(){var e=this.offsetParent||i.body;while(e&&!er.test(e.nodeName)&&v.css(e,"position")==="static")e=e.offsetParent;return e||i.body})}}),v.each({scrollLeft:"pageXOffset",scrollTop:"pageYOffset"},function(e,n){var r=/Y/.test(n);v.fn[e]=function(i){return v.access(this,function(e,i,s){var o=tr(e);if(s===t)return o?n in o?o[n]:o.document.documentElement[i]:e[i];o?o.scrollTo(r?v(o).scrollLeft():s,r?s:v(o).scrollTop()):e[i]=s},e,i,arguments.length,null)}}),v.each({Height:"height",Width:"width"},function(e,n){v.each({padding:"inner"+e,content:n,"":"outer"+e},function(r,i){v.fn[i]=function(i,s){var o=arguments.length&&(r||typeof i!="boolean"),u=r||(i===!0||s===!0?"margin":"border");return v.access(this,function(n,r,i){var s;return v.isWindow(n)?n.document.documentElement["client"+e]:n.nodeType===9?(s=n.documentElement,Math.max(n.body["scroll"+e],s["scroll"+e],n.body["offset"+e],s["offset"+e],s["client"+e])):i===t?v.css(n,r,i,u):v.style(n,r,i,u)},n,o?i:t,o,null)}})}),e.jQuery=e.$=v,typeof define=="function"&&define.amd&&define.amd.jQuery&&define("jquery",[],function(){return v})})(window); \ No newline at end of file diff --git a/togetherjs/tests/togetherjs-animations/js/jquery.transit.min.js b/togetherjs/tests/togetherjs-animations/js/jquery.transit.min.js deleted file mode 100644 index 71e0b06f6..000000000 --- a/togetherjs/tests/togetherjs-animations/js/jquery.transit.min.js +++ /dev/null @@ -1,9 +0,0 @@ -/*! - * jQuery Transit - CSS3 transitions and transformations - * (c) 2011-2012 Rico Sta. Cruz - * MIT Licensed. - * - * http://ricostacruz.com/jquery.transit - * http://github.com/rstacruz/jquery.transit - */ -(function(k){k.transit={version:"0.9.9",propertyMap:{marginLeft:"margin",marginRight:"margin",marginBottom:"margin",marginTop:"margin",paddingLeft:"padding",paddingRight:"padding",paddingBottom:"padding",paddingTop:"padding"},enabled:true,useTransitionEnd:false};var d=document.createElement("div");var q={};function b(v){if(v in d.style){return v}var u=["Moz","Webkit","O","ms"];var r=v.charAt(0).toUpperCase()+v.substr(1);if(v in d.style){return v}for(var t=0;t-1;q.transition=b("transition");q.transitionDelay=b("transitionDelay");q.transform=b("transform");q.transformOrigin=b("transformOrigin");q.transform3d=e();var i={transition:"transitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd",WebkitTransition:"webkitTransitionEnd",msTransition:"MSTransitionEnd"};var f=q.transitionEnd=i[q.transition]||null;for(var p in q){if(q.hasOwnProperty(p)&&typeof k.support[p]==="undefined"){k.support[p]=q[p]}}d=null;k.cssEase={_default:"ease","in":"ease-in",out:"ease-out","in-out":"ease-in-out",snap:"cubic-bezier(0,1,.5,1)",easeOutCubic:"cubic-bezier(.215,.61,.355,1)",easeInOutCubic:"cubic-bezier(.645,.045,.355,1)",easeInCirc:"cubic-bezier(.6,.04,.98,.335)",easeOutCirc:"cubic-bezier(.075,.82,.165,1)",easeInOutCirc:"cubic-bezier(.785,.135,.15,.86)",easeInExpo:"cubic-bezier(.95,.05,.795,.035)",easeOutExpo:"cubic-bezier(.19,1,.22,1)",easeInOutExpo:"cubic-bezier(1,0,0,1)",easeInQuad:"cubic-bezier(.55,.085,.68,.53)",easeOutQuad:"cubic-bezier(.25,.46,.45,.94)",easeInOutQuad:"cubic-bezier(.455,.03,.515,.955)",easeInQuart:"cubic-bezier(.895,.03,.685,.22)",easeOutQuart:"cubic-bezier(.165,.84,.44,1)",easeInOutQuart:"cubic-bezier(.77,0,.175,1)",easeInQuint:"cubic-bezier(.755,.05,.855,.06)",easeOutQuint:"cubic-bezier(.23,1,.32,1)",easeInOutQuint:"cubic-bezier(.86,0,.07,1)",easeInSine:"cubic-bezier(.47,0,.745,.715)",easeOutSine:"cubic-bezier(.39,.575,.565,1)",easeInOutSine:"cubic-bezier(.445,.05,.55,.95)",easeInBack:"cubic-bezier(.6,-.28,.735,.045)",easeOutBack:"cubic-bezier(.175, .885,.32,1.275)",easeInOutBack:"cubic-bezier(.68,-.55,.265,1.55)"};k.cssHooks["transit:transform"]={get:function(r){return k(r).data("transform")||new j()},set:function(s,r){var t=r;if(!(t instanceof j)){t=new j(t)}if(q.transform==="WebkitTransform"&&!a){s.style[q.transform]=t.toString(true)}else{s.style[q.transform]=t.toString()}k(s).data("transform",t)}};k.cssHooks.transform={set:k.cssHooks["transit:transform"].set};if(k.fn.jquery<"1.8"){k.cssHooks.transformOrigin={get:function(r){return r.style[q.transformOrigin]},set:function(r,s){r.style[q.transformOrigin]=s}};k.cssHooks.transition={get:function(r){return r.style[q.transition]},set:function(r,s){r.style[q.transition]=s}}}n("scale");n("translate");n("rotate");n("rotateX");n("rotateY");n("rotate3d");n("perspective");n("skewX");n("skewY");n("x",true);n("y",true);function j(r){if(typeof r==="string"){this.parse(r)}return this}j.prototype={setFromString:function(t,s){var r=(typeof s==="string")?s.split(","):(s.constructor===Array)?s:[s];r.unshift(t);j.prototype.set.apply(this,r)},set:function(s){var r=Array.prototype.slice.apply(arguments,[1]);if(this.setter[s]){this.setter[s].apply(this,r)}else{this[s]=r.join(",")}},get:function(r){if(this.getter[r]){return this.getter[r].apply(this)}else{return this[r]||0}},setter:{rotate:function(r){this.rotate=o(r,"deg")},rotateX:function(r){this.rotateX=o(r,"deg")},rotateY:function(r){this.rotateY=o(r,"deg")},scale:function(r,s){if(s===undefined){s=r}this.scale=r+","+s},skewX:function(r){this.skewX=o(r,"deg")},skewY:function(r){this.skewY=o(r,"deg")},perspective:function(r){this.perspective=o(r,"px")},x:function(r){this.set("translate",r,null)},y:function(r){this.set("translate",null,r)},translate:function(r,s){if(this._translateX===undefined){this._translateX=0}if(this._translateY===undefined){this._translateY=0}if(r!==null&&r!==undefined){this._translateX=o(r,"px")}if(s!==null&&s!==undefined){this._translateY=o(s,"px")}this.translate=this._translateX+","+this._translateY}},getter:{x:function(){return this._translateX||0},y:function(){return this._translateY||0},scale:function(){var r=(this.scale||"1,1").split(",");if(r[0]){r[0]=parseFloat(r[0])}if(r[1]){r[1]=parseFloat(r[1])}return(r[0]===r[1])?r[0]:r},rotate3d:function(){var t=(this.rotate3d||"0,0,0,0deg").split(",");for(var r=0;r<=3;++r){if(t[r]){t[r]=parseFloat(t[r])}}if(t[3]){t[3]=o(t[3],"deg")}return t}},parse:function(s){var r=this;s.replace(/([a-zA-Z0-9]+)\((.*?)\)/g,function(t,v,u){r.setFromString(v,u)})},toString:function(t){var s=[];for(var r in this){if(this.hasOwnProperty(r)){if((!q.transform3d)&&((r==="rotateX")||(r==="rotateY")||(r==="perspective")||(r==="transformOrigin"))){continue}if(r[0]!=="_"){if(t&&(r==="scale")){s.push(r+"3d("+this[r]+",1)")}else{if(t&&(r==="translate")){s.push(r+"3d("+this[r]+",0)")}else{s.push(r+"("+this[r]+")")}}}}}return s.join(" ")}};function m(s,r,t){if(r===true){s.queue(t)}else{if(r){s.queue(r,t)}else{t()}}}function h(s){var r=[];k.each(s,function(t){t=k.camelCase(t);t=k.transit.propertyMap[t]||k.cssProps[t]||t;t=c(t);if(k.inArray(t,r)===-1){r.push(t)}});return r}function g(s,v,x,r){var t=h(s);if(k.cssEase[x]){x=k.cssEase[x]}var w=""+l(v)+" "+x;if(parseInt(r,10)>0){w+=" "+l(r)}var u=[];k.each(t,function(z,y){u.push(y+" "+w)});return u.join(", ")}k.fn.transition=k.fn.transit=function(z,s,y,C){var D=this;var u=0;var w=true;if(typeof s==="function"){C=s;s=undefined}if(typeof y==="function"){C=y;y=undefined}if(typeof z.easing!=="undefined"){y=z.easing;delete z.easing}if(typeof z.duration!=="undefined"){s=z.duration;delete z.duration}if(typeof z.complete!=="undefined"){C=z.complete;delete z.complete}if(typeof z.queue!=="undefined"){w=z.queue;delete z.queue}if(typeof z.delay!=="undefined"){u=z.delay;delete z.delay}if(typeof s==="undefined"){s=k.fx.speeds._default}if(typeof y==="undefined"){y=k.cssEase._default}s=l(s);var E=g(z,s,y,u);var B=k.transit.enabled&&q.transition;var t=B?(parseInt(s,10)+parseInt(u,10)):0;if(t===0){var A=function(F){D.css(z);if(C){C.apply(D)}if(F){F()}};m(D,w,A);return D}var x={};var r=function(H){var G=false;var F=function(){if(G){D.unbind(f,F)}if(t>0){D.each(function(){this.style[q.transition]=(x[this]||null)})}if(typeof C==="function"){C.apply(D)}if(typeof H==="function"){H()}};if((t>0)&&(f)&&(k.transit.useTransitionEnd)){G=true;D.bind(f,F)}else{window.setTimeout(F,t)}D.each(function(){if(t>0){this.style[q.transition]=E}k(this).css(z)})};var v=function(F){this.offsetWidth;r(F)};m(D,w,v);return this};function n(s,r){if(!r){k.cssNumber[s]=true}k.transit.propertyMap[s]=q.transform;k.cssHooks[s]={get:function(v){var u=k(v).css("transit:transform");return u.get(s)},set:function(v,w){var u=k(v).css("transit:transform");u.setFromString(s,w);k(v).css({"transit:transform":u})}}}function c(r){return r.replace(/([A-Z])/g,function(s){return"-"+s.toLowerCase()})}function o(s,r){if((typeof s==="string")&&(!s.match(/^[\-0-9\.]+$/))){return s}else{return""+s+r}}function l(s){var r=s;if(k.fx.speeds[r]){r=k.fx.speeds[r]}return o(r,"ms")}k.transit.getTransitionValue=g})(jQuery); \ No newline at end of file diff --git a/togetherjs/togetherjs.js b/togetherjs/togetherjs.js deleted file mode 100644 index 91954ee4d..000000000 --- a/togetherjs/togetherjs.js +++ /dev/null @@ -1,932 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/*jshint scripturl:true */ -(function () { - - var defaultConfiguration = { - // Disables clicks for a certain element. - // (e.g., 'canvas' would not show clicks on canvas elements.) - // Setting this to true will disable clicks globally. - dontShowClicks: false, - // Experimental feature to echo clicks to certain elements across clients: - cloneClicks: false, - // Enable Mozilla or Google analytics on the page when TogetherJS is activated: - // FIXME: these don't seem to be working, and probably should be removed in favor - // of the hub analytics - enableAnalytics: false, - // The code to enable (this is defaulting to a Mozilla code): - analyticsCode: "UA-35433268-28", - // The base URL of the hub (gets filled in below): - hubBase: null, - // A function that will return the name of the user: - getUserName: null, - // A function that will return the color of the user: - getUserColor: null, - // A function that will return the avatar of the user: - getUserAvatar: null, - // The siteName is used in the walkthrough (defaults to document.title): - siteName: null, - // Whether to use the minimized version of the code (overriding the built setting) - useMinimizedCode: undefined, - // Append cache-busting queries (useful for development!) - cacheBust: true, - // Any events to bind to - on: {}, - // Hub events to bind to - hub_on: {}, - // Enables the alt-T alt-T TogetherJS shortcut; however, this setting - // must be enabled early as TogetherJSConfig_enableShortcut = true; - enableShortcut: false, - // The name of this tool as provided to users. The UI is updated to use this. - // Because of how it is used in text it should be a proper noun, e.g., - // "MySite's Collaboration Tool" - toolName: null, - // Used to auto-start TogetherJS with a {prefix: pageName, max: participants} - // Also with findRoom: "roomName" it will connect to the given room name - findRoom: null, - // If true, starts TogetherJS automatically (of course!) - autoStart: false, - // If true, then the "Join TogetherJS Session?" confirmation dialog - // won't come up - suppressJoinConfirmation: false, - // If true, then the "Invite a friend" window won't automatically come up - suppressInvite: false, - // A room in which to find people to invite to this session, - inviteFromRoom: null, - // This is used to keep sessions from crossing over on the same - // domain, if for some reason you want sessions that are limited - // to only a portion of the domain: - storagePrefix: "togetherjs", - // When true, we treat the entire URL, including the hash, as the identifier - // of the page; i.e., if you one person is on `http://example.com/#view1` - // and another person is at `http://example.com/#view2` then these two people - // are considered to be at completely different URLs - includeHashInUrl: false, - // When true, the WebRTC-based mic/chat will be disabled - disableWebRTC: false, - // When true, youTube videos will synchronize - youtube: true, - // Ignores the following console messages, disables all messages if set to true - ignoreMessages: ["cursor-update", "keydown", "scroll-update"], - // Ignores the following forms (will ignore all forms if set to true): - ignoreForms: [":password"], - // When undefined, attempts to use the browser's language - lang: undefined, - fallbackLang: "en-US", - // Overrides the UI's font-family; accepts any CSS font-family value, - // including a reference to a CSS custom property already defined on - // the host page (e.g. "var(--font-base)") - baseFont: null - }; - - var styleSheet = "/togetherjs/togetherjs.css"; - - var baseUrl = "__baseUrl__"; - if (baseUrl == "__" + "baseUrl__") { - // Reset the variable if it doesn't get substituted - baseUrl = ""; - } - // Allow override of baseUrl (this is done separately because it needs - // to be done very early) - if (window.TogetherJSConfig && window.TogetherJSConfig.baseUrl) { - baseUrl = window.TogetherJSConfig.baseUrl; - } - if (window.TogetherJSConfig_baseUrl) { - baseUrl = window.TogetherJSConfig_baseUrl; - } - defaultConfiguration.baseUrl = baseUrl; - - // True if this file should use minimized sub-resources: - var min = "__min__" == "__" + "min__" ? false : "__min__" == "yes"; - - var baseUrlOverride = localStorage.getItem("togetherjs.baseUrlOverride"); - if (baseUrlOverride) { - try { - baseUrlOverride = JSON.parse(baseUrlOverride); - } catch (e) { - baseUrlOverride = null; - } - if ((! baseUrlOverride) || baseUrlOverride.expiresAt < Date.now()) { - // Ignore because it has expired - localStorage.removeItem("togetherjs.baseUrlOverride"); - } else { - baseUrl = baseUrlOverride.baseUrl; - var logger = console.warn || console.log; - logger.call(console, "Using TogetherJS baseUrlOverride:", baseUrl); - logger.call(console, "To undo run: localStorage.removeItem('togetherjs.baseUrlOverride')"); - } - } - - var configOverride = localStorage.getItem("togetherjs.configOverride"); - if (configOverride) { - try { - configOverride = JSON.parse(configOverride); - } catch (e) { - configOverride = null; - } - if ((! configOverride) || configOverride.expiresAt < Date.now()) { - localStorage.removeItem("togetherjs.configOverride"); - } else { - var shownAny = false; - for (var attr in configOverride) { - if (! configOverride.hasOwnProperty(attr)) { - continue; - } - if (attr == "expiresAt" || ! configOverride.hasOwnProperty(attr)) { - continue; - } - if (! shownAny) { - console.warn("Using TogetherJS configOverride"); - console.warn("To undo run: localStorage.removeItem('togetherjs.configOverride')"); - } - window["TogetherJSConfig_" + attr] = configOverride[attr]; - console.log("Config override:", attr, "=", configOverride[attr]); - } - } - } - - var version = "unknown"; - // FIXME: we could/should use a version from the checkout, at least - // for production - var cacheBust = "__gitCommit__"; - if ((! cacheBust) || cacheBust == "__gitCommit__") { - cacheBust = Date.now() + ""; - } else { - version = cacheBust; - } - - // Make sure we have all of the console.* methods: - if (typeof console == "undefined") { - console = {}; - } - if (! console.log) { - console.log = function () {}; - } - ["debug", "info", "warn", "error"].forEach(function (method) { - if (! console[method]) { - console[method] = console.log; - } - }); - - if (! baseUrl) { - var scripts = document.getElementsByTagName("script"); - for (var i=0; i with togetherjs.js and togetherjs-min.js)"); - } - - function addStyle() { - var existing = document.getElementById("togetherjs-stylesheet"); - if (! existing) { - var link = document.createElement("link"); - link.id = "togetherjs-stylesheet"; - link.setAttribute("rel", "stylesheet"); - link.href = baseUrl + styleSheet + - (cacheBust ? ("?bust=" + cacheBust) : ''); - document.head.appendChild(link); - } - } - - function addScript(url) { - var script = document.createElement("script"); - script.src = baseUrl + url + - (cacheBust ? ("?bust=" + cacheBust) : ''); - document.head.appendChild(script); - } - - var TogetherJS = window.TogetherJS = function TogetherJS(event) { - var session; - if (TogetherJS.running) { - session = TogetherJS.require("session"); - session.close(); - return; - } - TogetherJS.startup.button = null; - try { - if (event && typeof event == "object") { - if (event.target && typeof event) { - TogetherJS.startup.button = event.target; - } else if (event.nodeType == 1) { - TogetherJS.startup.button = event; - } else if (event[0] && event[0].nodeType == 1) { - // Probably a jQuery element - TogetherJS.startup.button = event[0]; - } - } - } catch (e) { - console.warn("Error determining starting button:", e); - } - if (window.TowTruckConfig) { - console.warn("TowTruckConfig is deprecated; please use TogetherJSConfig"); - if (window.TogetherJSConfig) { - console.warn("Ignoring TowTruckConfig in favor of TogetherJSConfig"); - } else { - window.TogetherJSConfig = TowTruckConfig; - } - } - if (window.TogetherJSConfig && (! window.TogetherJSConfig.loaded)) { - TogetherJS.config(window.TogetherJSConfig); - window.TogetherJSConfig.loaded = true; - } - - // This handles loading configuration from global variables. This - // includes TogetherJSConfig_on_*, which are attributes folded into - // the "on" configuration value. - var attr; - var attrName; - var globalOns = {}; - for (attr in window) { - if (attr.indexOf("TogetherJSConfig_on_") === 0) { - attrName = attr.substr(("TogetherJSConfig_on_").length); - globalOns[attrName] = window[attr]; - } else if (attr.indexOf("TogetherJSConfig_") === 0) { - attrName = attr.substr(("TogetherJSConfig_").length); - TogetherJS.config(attrName, window[attr]); - } else if (attr.indexOf("TowTruckConfig_on_") === 0) { - attrName = attr.substr(("TowTruckConfig_on_").length); - console.warn("TowTruckConfig_* is deprecated, please rename", attr, "to TogetherJSConfig_on_" + attrName); - globalOns[attrName] = window[attr]; - } else if (attr.indexOf("TowTruckConfig_") === 0) { - attrName = attr.substr(("TowTruckConfig_").length); - console.warn("TowTruckConfig_* is deprecated, please rename", attr, "to TogetherJSConfig_" + attrName); - TogetherJS.config(attrName, window[attr]); - } - - - } - // FIXME: copy existing config? - // FIXME: do this directly in TogetherJS.config() ? - // FIXME: close these configs? - var ons = TogetherJS.config.get("on"); - for (attr in globalOns) { - if (globalOns.hasOwnProperty(attr)) { - // FIXME: should we avoid overwriting? Maybe use arrays? - ons[attr] = globalOns[attr]; - } - } - TogetherJS.config("on", ons); - for (attr in ons) { - TogetherJS.on(attr, ons[attr]); - } - var hubOns = TogetherJS.config.get("hub_on"); - if (hubOns) { - for (attr in hubOns) { - if (hubOns.hasOwnProperty(attr)) { - TogetherJS.hub.on(attr, hubOns[attr]); - } - } - } - if (!TogetherJS.config.close('cacheBust')) { - cacheBust = ''; - delete TogetherJS.requireConfig.urlArgs; - } - - if (! TogetherJS.startup.reason) { - // Then a call to TogetherJS() from a button must be started TogetherJS - TogetherJS.startup.reason = "started"; - } - - // FIXME: maybe I should just test for TogetherJS.require: - if (TogetherJS._loaded) { - session = TogetherJS.require("session"); - addStyle(); - session.start(); - return; - } - // A sort of signal to session.js to tell it to actually - // start itself (i.e., put up a UI and try to activate) - TogetherJS.startup._launch = true; - - addStyle(); - var minSetting = TogetherJS.config.get("useMinimizedCode"); - TogetherJS.config.close("useMinimizedCode"); - if (minSetting !== undefined) { - min = !! minSetting; - } - var requireConfig = TogetherJS._extend(TogetherJS.requireConfig); - var deps = ["session", "jquery"]; - var lang = TogetherJS.getConfig("lang"); - // [igoryen]: We should generate this value in Gruntfile.js, based on the available translations - var availableTranslations = { - "en-US": true, - "en": "en-US", - "es": "es-BO", - "es-BO": true, - "ru": true, - "ru-RU": "ru", - "pl": "pl-PL", - "pl-PL": true, - "de-DE": true, - "de": "de-DE" - }; - - if(lang === undefined) { - // BCP 47 mandates hyphens, not underscores, to separate lang parts - lang = navigator.language.replace(/_/g, "-"); - } - if (/-/.test(lang) && !availableTranslations[lang]) { - lang = lang.replace(/-.*$/, ''); - } - if (!availableTranslations[lang]) { - lang = TogetherJS.config.get("fallbackLang"); - } else if (availableTranslations[lang] !== true) { - lang = availableTranslations[lang]; - } - TogetherJS.config("lang", lang); - - var localeTemplates = "templates-" + lang; - deps.splice(0, 0, localeTemplates); - function callback(session, jquery) { - TogetherJS._loaded = true; - if (! min) { - TogetherJS.require = require.config({context: "togetherjs"}); - TogetherJS._requireObject = require; - } - } - if (! min) { - if (typeof require == "function") { - if (! require.config) { - console.warn("The global require (", require, ") is not requirejs; please use togetherjs-min.js"); - throw new Error("Conflict with window.require"); - } - TogetherJS.require = require.config(requireConfig); - } - } - if (typeof TogetherJS.require == "function") { - // This is an already-configured version of require - TogetherJS.require(deps, callback); - } else { - requireConfig.deps = deps; - requireConfig.callback = callback; - if (! min) { - window.require = requireConfig; - } - } - if (min) { - addScript("/togetherjs/togetherjsPackage.js"); - } else { - addScript("/togetherjs/libs/require.js"); - } - }; - - TogetherJS.pageLoaded = Date.now(); - - TogetherJS._extend = function (base, extensions) { - if (! extensions) { - extensions = base; - base = {}; - } - for (var a in extensions) { - if (extensions.hasOwnProperty(a)) { - base[a] = extensions[a]; - } - } - return base; - }; - - TogetherJS._startupInit = { - // What element, if any, was used to start the session: - button: null, - // The startReason is the reason TogetherJS was started. One of: - // null: not started - // started: hit the start button (first page view) - // joined: joined the session (first page view) - reason: null, - // Also, the session may have started on "this" page, or maybe is continued - // from a past page. TogetherJS.continued indicates the difference (false the - // first time TogetherJS is started or joined, true on later page loads). - continued: false, - // This is set to tell the session what shareId to use, if the boot - // code knows (mostly because the URL indicates the id). - _joinShareId: null, - // This tells session to start up immediately (otherwise it would wait - // for session.start() to be run) - _launch: false - }; - TogetherJS.startup = TogetherJS._extend(TogetherJS._startupInit); - TogetherJS.running = false; - - TogetherJS.requireConfig = { - context: "togetherjs", - baseUrl: baseUrl + "/togetherjs", - urlArgs: "bust=" + cacheBust, - paths: { - jquery: "libs/jquery-1.11.1.min", - walkabout: "libs/walkabout/walkabout", - esprima: "libs/walkabout/lib/esprima", - falafel: "libs/walkabout/lib/falafel", - tinycolor: "libs/tinycolor", - whrandom: "libs/whrandom/random" - } - }; - - TogetherJS._mixinEvents = function (proto) { - proto.on = function on(name, callback) { - if (typeof callback != "function") { - console.warn("Bad callback for", this, ".once(", name, ", ", callback, ")"); - throw "Error: .once() called with non-callback"; - } - if (name.search(" ") != -1) { - var names = name.split(/ +/g); - names.forEach(function (n) { - this.on(n, callback); - }, this); - return; - } - if (this._knownEvents && this._knownEvents.indexOf(name) == -1) { - var thisString = "" + this; - if (thisString.length > 20) { - thisString = thisString.substr(0, 20) + "..."; - } - console.warn(thisString + ".on('" + name + "', ...): unknown event"); - if (console.trace) { - console.trace(); - } - } - if (! this._listeners) { - this._listeners = {}; - } - if (! this._listeners[name]) { - this._listeners[name] = []; - } - if (this._listeners[name].indexOf(callback) == -1) { - this._listeners[name].push(callback); - } - }; - proto.once = function once(name, callback) { - if (typeof callback != "function") { - console.warn("Bad callback for", this, ".once(", name, ", ", callback, ")"); - throw "Error: .once() called with non-callback"; - } - var attr = "onceCallback_" + name; - // FIXME: maybe I should add the event name to the .once attribute: - if (! callback[attr]) { - callback[attr] = function onceCallback() { - callback.apply(this, arguments); - this.off(name, onceCallback); - delete callback[attr]; - }; - } - this.on(name, callback[attr]); - }; - proto.off = proto.removeListener = function off(name, callback) { - if (this._listenerOffs) { - // Defer the .off() call until the .emit() is done. - this._listenerOffs.push([name, callback]); - return; - } - if (name.search(" ") != -1) { - var names = name.split(/ +/g); - names.forEach(function (n) { - this.off(n, callback); - }, this); - return; - } - if ((! this._listeners) || ! this._listeners[name]) { - return; - } - var l = this._listeners[name], _len = l.length; - for (var i=0; i<_len; i++) { - if (l[i] == callback) { - l.splice(i, 1); - break; - } - } - }; - proto.emit = function emit(name) { - var offs = this._listenerOffs = []; - if ((! this._listeners) || ! this._listeners[name]) { - return; - } - var args = Array.prototype.slice.call(arguments, 1); - var l = this._listeners[name]; - l.forEach(function (callback) { - - callback.apply(this, args); - }, this); - delete this._listenerOffs; - if (offs.length) { - offs.forEach(function (item) { - this.off(item[0], item[1]); - }, this); - } - - }; - return proto; - }; - - /* This finalizes the unloading of TogetherJS, including unloading modules */ - TogetherJS._teardown = function () { - var requireObject = TogetherJS._requireObject || window.require; - // FIXME: this doesn't clear the context for min-case - if (requireObject.s && requireObject.s.contexts) { - delete requireObject.s.contexts.togetherjs; - } - TogetherJS._loaded = false; - TogetherJS.startup = TogetherJS._extend(TogetherJS._startupInit); - TogetherJS.running = false; - }; - - TogetherJS._mixinEvents(TogetherJS); - TogetherJS._knownEvents = ["ready", "close"]; - TogetherJS.toString = function () { - return "TogetherJS"; - }; - - var defaultHubBase = "__hubUrl__"; - if (defaultHubBase == "__" + "hubUrl"+ "__") { - // Substitution wasn't made - defaultHubBase = "https://hub.togetherjs.mozillalabs.com"; - } - defaultConfiguration.hubBase = defaultHubBase; - - TogetherJS._configuration = {}; - TogetherJS._defaultConfiguration = { - // Disables clicks for a certain element. - // (e.g., 'canvas' would not show clicks on canvas elements.) - // Setting this to true will disable clicks globally. - dontShowClicks: false, - // Experimental feature to echo clicks to certain elements across clients: - cloneClicks: false, - // Enable Mozilla or Google analytics on the page when TogetherJS is activated: - // FIXME: these don't seem to be working, and probably should be removed in favor - // of the hub analytics - enableAnalytics: false, - // The code to enable (this is defaulting to a Mozilla code): - analyticsCode: "UA-35433268-28", - // The base URL of the hub - hubBase: defaultHubBase, - // A function that will return the name of the user: - getUserName: null, - // A function that will return the color of the user: - getUserColor: null, - // A function that will return the avatar of the user: - getUserAvatar: null, - // The siteName is used in the walkthrough (defaults to document.title): - siteName: null, - // Whether to use the minimized version of the code (overriding the built setting) - useMinimizedCode: undefined, - // Any events to bind to - on: {}, - // Hub events to bind to - hub_on: {}, - // Enables the alt-T alt-T TogetherJS shortcut; however, this setting - // must be enabled early as TogetherJSConfig_enableShortcut = true; - enableShortcut: false, - // The name of this tool as provided to users. The UI is updated to use this. - // Because of how it is used in text it should be a proper noun, e.g., - // "MySite's Collaboration Tool" - toolName: null, - // Used to auto-start TogetherJS with a {prefix: pageName, max: participants} - // Also with findRoom: "roomName" it will connect to the given room name - findRoom: null, - // If true, starts TogetherJS automatically (of course!) - autoStart: false, - // If true, then the "Join TogetherJS Session?" confirmation dialog - // won't come up - suppressJoinConfirmation: false, - // If true, then the "Invite a friend" window won't automatically come up - suppressInvite: false, - // A room in which to find people to invite to this session, - inviteFromRoom: null, - // This is used to keep sessions from crossing over on the same - // domain, if for some reason you want sessions that are limited - // to only a portion of the domain: - storagePrefix: "togetherjs", - // When true, we treat the entire URL, including the hash, as the identifier - // of the page; i.e., if you one person is on `http://example.com/#view1` - // and another person is at `http://example.com/#view2` then these two people - // are considered to be at completely different URLs - includeHashInUrl: false, - // The language to present the tool in, such as "en-US" or "ru-RU" - // Note this must be set as TogetherJSConfig_lang, as it effects the loader - // and must be set as soon as this file is included - lang: null - }; - // FIXME: there's a point at which configuration can't be updated - // (e.g., hubBase after the TogetherJS has loaded). We should keep - // track of these and signal an error if someone attempts to - // reconfigure too late - - TogetherJS.getConfig = function (name) { // rename into TogetherJS.config.get()? - var value = TogetherJS._configuration[name]; - if (value === undefined) { - if (! TogetherJS._defaultConfiguration.hasOwnProperty(name)) { - console.error("Tried to load unknown configuration value:", name); - } - value = TogetherJS._defaultConfiguration[name]; - } - return value; - }; - TogetherJS._defaultConfiguration = defaultConfiguration; - TogetherJS._configTrackers = {}; - TogetherJS._configClosed = {}; - - /* TogetherJS.config(configurationObject) - or: TogetherJS.config(configName, value) - - Adds configuration to TogetherJS. You may also set the global variable TogetherJSConfig - and when TogetherJS is started that configuration will be loaded. - - Unknown configuration values will lead to console error messages. - */ - TogetherJS.config = function (name, maybeValue) { - var settings; - if (arguments.length == 1) { - if (typeof name != "object") { - throw new Error('TogetherJS.config(value) must have an object value (not: ' + name + ')'); - } - settings = name; - } else { - settings = {}; - settings[name] = maybeValue; - } - var i; - var tracker; - var attr; - for (attr in settings) { - if (settings.hasOwnProperty(attr)) { - if (TogetherJS._configClosed[attr] && TogetherJS.running) { - throw new Error("The configuration " + attr + " is finalized and cannot be changed"); - } - } - } - for (attr in settings) { - if (! settings.hasOwnProperty(attr)) { - continue; - } - if (attr == "loaded" || attr == "callToStart") { - continue; - } - if (! TogetherJS._defaultConfiguration.hasOwnProperty(attr)) { - console.warn("Unknown configuration value passed to TogetherJS.config():", attr); - } - var previous = TogetherJS._configuration[attr]; - var value = settings[attr]; - TogetherJS._configuration[attr] = value; - var trackers = TogetherJS._configTrackers[name] || []; - var failed = false; - for (i=0; i but not a ')); - }); - - TogetherJS.config.track("disableWebRTC", function (hide, previous) { - if (hide && ! previous) { - ui.container.find("#togetherjs-audio-button").hide(); - adjustDockSize(-1); - } else if ((! hide) && previous) { - ui.container.find("#togetherjs-audio-button").show(); - adjustDockSize(1); - } - }); - - }; - - // After prepareUI, this actually makes the interface live. We have - // to do this later because we call prepareUI when many components - // aren't initialized, so we don't even want the user to be able to - // interact with the interface. But activateUI is called once - // everything is loaded and ready for interaction. - ui.activateUI = function () { - if (deferringPrepareUI) { - console.warn("ui.activateUI called before document is ready; waiting..."); - deferringPrepareUI = "activate"; - return; - } - if (! ui.container) { - ui.prepareUI(); - } - var container = ui.container; - - //create the overlay - if($.browser.mobile) { - // $("body").append( "\x3cdiv class='overlay' style='position: absolute; top: 0; left: 0; background-color: rgba(0,0,0,0); width: 120%; height: 100%; z-index: 1000; margin: -10px'>\x3c/div>" ); - } - - // The share link: - ui.prepareShareLink(container); - container.find("input.togetherjs-share-link").on("keydown", function (event) { - if (event.which == 27) { - windowing.hide("#togetherjs-share"); - return false; - } - return undefined; - }); - session.on("shareId", updateShareLink); - - // The chat input element: - var input = container.find("#togetherjs-chat-input"); - input.bind("keydown", function (event) { - if (event.which == 13 && !event.shiftKey) { // Enter without Shift pressed - submitChat(); - return false; - } - if (event.which == 27) { // Escape - windowing.hide("#togetherjs-chat"); - return false; - } - }); - - function submitChat() { - var val = input.val(); - if ($.trim(val)) { - input.val(""); - // triggering the event manually to avoid the addition of newline character to the textarea: - input.trigger("input").trigger("propertychange"); - chat.submit(val); - } - } - // auto-resize textarea: - input.on("input propertychange", function () { - var $this = $(this); - var actualHeight = $this.height(); - // reset the height of textarea to remove trailing empty space (used for shrinking): - $this.height(TEXTAREA_LINE_HEIGHT); - this.scrollTop = 0; - // scroll to bottom: - this.scrollTop = 9999; - var newHeight = this.scrollTop + $this.height(); - var maxHeight = TEXTAREA_MAX_LINES * TEXTAREA_LINE_HEIGHT; - if (newHeight > maxHeight) { - newHeight = maxHeight; - this.style.overflowY = "scroll"; - } else { - this.style.overflowY = "hidden"; - } - this.style.height = newHeight + "px"; - var diff = newHeight - actualHeight; - $("#togetherjs-chat-input-box").height($("#togetherjs-chat-input-box").height() + diff); - $("#togetherjs-chat-messages").height($("#togetherjs-chat-messages").height() - diff); - return false; - }); - - util.testExpose({submitChat: submitChat}); - - // Moving the window: - // FIXME: this should probably be stickier, and not just move the window around - // so abruptly - var anchor = container.find("#togetherjs-dock-anchor"); - assert(anchor.length); - // FIXME: This is in place to temporarily disable dock dragging: - anchor = container.find("#togetherjs-dock-anchor-disabled"); - anchor.mousedown(function (event) { - var iface = $("#togetherjs-dock"); - // FIXME: switch to .offset() and pageX/Y - var startPos = panelPosition(); - function selectoff() { - return false; - } - function mousemove(event2) { - var fromRight = $window.width() + window.pageXOffset - event2.pageX; - var fromLeft = event2.pageX - window.pageXOffset; - var fromBottom = $window.height() + window.pageYOffset - event2.pageY; - // FIXME: this is to temporarily disable the bottom view: - fromBottom = 10000; - - var pos; - if (fromLeft < fromRight && fromLeft < fromBottom) { - pos = "left"; - } else if (fromRight < fromLeft && fromRight < fromBottom) { - pos = "right"; - } else { - pos = "bottom"; - } - iface.removeClass("togetherjs-dock-left"); - iface.removeClass("togetherjs-dock-right"); - iface.removeClass("togetherjs-dock-bottom"); - iface.addClass("togetherjs-dock-" + pos); - if (startPos && pos != startPos) { - windowing.hide(); - startPos = null; - } - } - $(document).bind("mousemove", mousemove); - // If you don't turn selection off it will still select text, and show a - // text selection cursor: - $(document).bind("selectstart", selectoff); - // FIXME: it seems like sometimes we lose the mouseup event, and it's as though - // the mouse is stuck down: - $(document).one("mouseup", function () { - $(document).unbind("mousemove", mousemove); - $(document).unbind("selectstart", selectoff); - }); - return false; - }); - - function openDock() { - $('.togetherjs-window').animate({ - opacity: 1 - }); - $('#togetherjs-dock-participants').animate({ - opacity: 1 - }); - $('#togetherjs-dock #togetherjs-buttons').animate({ - opacity: 1 - }); - - //for iphone - if($(window).width() < 480) { - $('.togetherjs-dock-right').animate({ - width: "204px" - }, { - duration:60, easing:"linear" - }); - } - - //for ipad - else { - $('.togetherjs-dock-right').animate({ - width: "27%" - }, { - duration:60, easing:"linear" - }); - } - - - // add bg overlay - // $("body").append( "\x3cdiv class='overlay' style='position: absolute; top: 0; left: -2px; background-color: rgba(0,0,0,0.5); width: 200%; height: 400%; z-index: 1000; margin: 0px;'>\x3c/div>" ); - - //disable vertical scrolling - // $("body").css({ - // "position": "fixed", - // top: 0, - // left: 0 - // }); - - //replace the anchor icon - var src = "/togetherjs/images/togetherjs-logo-close.png"; - $("#togetherjs-dock-anchor #togetherjs-dock-anchor-horizontal img").attr("src", src); - } - - function closeDock() { - //enable vertical scrolling - $("body").css({ - "position": "", - top: "", - left: "" - }); - - //replace the anchor icon - var src = "/togetherjs/images/togetherjs-logo-open.png"; - $("#togetherjs-dock-anchor #togetherjs-dock-anchor-horizontal img").attr("src", src); - - $('.togetherjs-window').animate({ - opacity: 0 - }); - $('#togetherjs-dock-participants').animate({ - opacity: 0 - }); - $('#togetherjs-dock #togetherjs-buttons').animate({ - opacity: 0 - }); - $('.togetherjs-dock-right').animate({ - width: "40px" - }, { - duration:60, easing:"linear" - }); - - // remove bg overlay - //$(".overlay").remove(); - } - - // Setting the anchor button + dock mobile actions - if($.browser.mobile) { - - // toggle the audio button - $("#togetherjs-audio-button").click(function () { - windowing.toggle("#togetherjs-rtc-not-supported"); - }); - - // toggle the profile button - $("#togetherjs-profile-button").click(function () { - windowing.toggle("#togetherjs-menu-window"); - }); - - // $("body").append( "\x3cdiv class='overlay' style='position: absolute; top: 0; left: -2px; background-color: rgba(0,0,0,0.5); width: 200%; height: 400%; z-index: 1000; margin: 0px'>\x3c/div>" ); - - //disable vertical scrolling - // $("body").css({ - // "position": "fixed", - // top: 0, - // left: 0 - // }); - - //replace the anchor icon - var src = "/togetherjs/images/togetherjs-logo-close.png"; - $("#togetherjs-dock-anchor #togetherjs-dock-anchor-horizontal img").attr("src", src); - - $("#togetherjs-dock-anchor").toggle(function() { - closeDock(); - },function(){ - openDock(); - }); - } - - $("#togetherjs-share-button").click(function () { - windowing.toggle("#togetherjs-share"); - }); - - $("#togetherjs-profile-button").click(function (event) { - if ($.browser.mobile) { - windowing.show("#togetherjs-menu-window"); - return false; - } - toggleMenu(); - event.stopPropagation(); - return false; - }); - - $("#togetherjs-menu-feedback, #togetherjs-menu-feedback-button").click(function(){ - windowing.hide(); - hideMenu(); - windowing.show("#togetherjs-feedback-form"); - }); - - $("#togetherjs-menu-help, #togetherjs-menu-help-button").click(function () { - windowing.hide(); - hideMenu(); - require(["walkthrough"], function (walkthrough) { - windowing.hide(); - walkthrough.start(false); - }); - }); - - $("#togetherjs-menu-update-name").click(function () { - var input = $("#togetherjs-menu .togetherjs-self-name"); - input.css({ - width: $("#togetherjs-menu").width() - 32 + "px" - }); - ui.displayToggle("#togetherjs-menu .togetherjs-self-name"); - $("#togetherjs-menu .togetherjs-self-name").focus(); - }); - - $("#togetherjs-menu-update-name-button").click(function () { - windowing.show("#togetherjs-edit-name-window"); - $("#togetherjs-edit-name-window input").focus(); - }); - - $("#togetherjs-menu .togetherjs-self-name").bind("keyup change", function (event) { - console.log("alrighty", event); - if (event.which == 13) { - ui.displayToggle("#togetherjs-self-name-display"); - return; - } - var val = $("#togetherjs-menu .togetherjs-self-name").val(); - console.log("values!!", val); - if (val) { - peers.Self.update({name: val}); - } - }); - - $("#togetherjs-menu-update-avatar, #togetherjs-menu-update-avatar-button").click(function () { - hideMenu(); - windowing.show("#togetherjs-avatar-edit"); - }); - - $("#togetherjs-menu-end, #togetherjs-menu-end-button").click(function () { - hideMenu(); - windowing.show("#togetherjs-confirm-end"); - }); - - $("#togetherjs-end-session").click(function () { - session.close(); - //$(".overlay").remove(); - - }); - - $("#togetherjs-menu-update-color").click(function () { - var picker = $("#togetherjs-pick-color"); - if (picker.is(":visible")) { - picker.hide(); - return; - } - picker.show(); - bindPicker(); - picker.find(".togetherjs-swatch-active").removeClass("togetherjs-swatch-active"); - picker.find(".togetherjs-swatch[data-color=\"" + peers.Self.color + "\"]").addClass("togetherjs-swatch-active"); - }); - - $("#togetherjs-pick-color").click(".togetherjs-swatch", function (event) { - var swatch = $(event.target); - var color = swatch.attr("data-color"); - peers.Self.update({ - color: color - }); - event.stopPropagation(); - return false; - }); - - $("#togetherjs-pick-color").click(function (event) { - $("#togetherjs-pick-color").hide(); - event.stopPropagation(); - return false; - }); - - COLORS.forEach(function (color) { - var el = templating.sub("swatch"); - el.attr("data-color", color); - var darkened = tinycolor.darken(color); - el.css({ - backgroundColor: color, - borderColor: darkened - }); - $("#togetherjs-pick-color").append(el); - }); - - $("#togetherjs-chat-button").click(function () { - windowing.toggle("#togetherjs-chat"); - }); - - session.on("display-window", function (id, element) { - if (id == "togetherjs-chat") { - if (! $.browser.mobile) { - $("#togetherjs-chat-input").focus(); - } - } else if (id == "togetherjs-share") { - var link = element.find("input.togetherjs-share-link"); - if (link.is(":visible")) { - link.focus().select(); - } - } - }); - - container.find("#togetherjs-chat-notifier").click(function (event) { - if ($(event.target).is("a") || container.is(".togetherjs-close")) { - return; - } - windowing.show("#togetherjs-chat"); - }); - - // FIXME: Don't think this makes sense - $(".togetherjs header.togetherjs-title").each(function (index, item) { - var button = $(''); - button.click(function (event) { - var window = button.closest(".togetherjs-window"); - windowing.hide(window); - }); - $(item).append(button); - }); - - $("#togetherjs-avatar-done").click(function () { - ui.displayToggle("#togetherjs-no-avatar-edit"); - }); - - $("#togetherjs-self-color").css({backgroundColor: peers.Self.color}); - - var avatar = peers.Self.avatar; - if (avatar) { - $("#togetherjs-self-avatar").attr("src", avatar); - } - - var starterButton = $("#togetherjs-starter button"); - starterButton.click(function () { - windowing.show("#togetherjs-about"); - }).addClass("togetherjs-running"); - if (starterButton.text() == "Start TogetherJS") { - starterButton.attr("data-start-text", starterButton.text()); - starterButton.text("End TogetherJS Session"); - } - - ui.activateAvatarEdit(container, { - onSave: function () { - windowing.hide("#togetherjs-avatar-edit"); - } - }); - - TogetherJS.config.track("inviteFromRoom", function (inviter, previous) { - if (inviter) { - container.find("#togetherjs-invite").show(); - } else { - container.find("#togetherjs-invite").hide(); - } - }); - - container.find("#togetherjs-menu-refresh-invite").click(refreshInvite); - container.find("#togetherjs-menu-invite-anyone").click(function () { - invite(null); - }); - - // The following lines should be at the end of this function - // (new code goes above) - session.emit("new-element", ui.container); - - if (finishedAt && finishedAt > Date.now()) { - setTimeout(function () { - finishedAt = null; - session.emit("ui-ready", ui); - }, finishedAt - Date.now()); - } else { - session.emit("ui-ready", ui); - } - - }; // End ui.activateUI() - - ui.activateAvatarEdit = function (container, options) { - options = options || {}; - var pendingImage = null; - - container.find(".togetherjs-avatar-save").prop("disabled", true); - - container.find(".togetherjs-avatar-save").click(function () { - if (pendingImage) { - peers.Self.update({avatar: pendingImage}); - container.find(".togetherjs-avatar-save").prop("disabled", true); - if (options.onSave) { - options.onSave(); - } - } - }); - - container.find(".togetherjs-upload-avatar").on("change", function () { - util.readFileImage(this).then(function (url) { - sizeDownImage(url).then(function (smallUrl) { - pendingImage = smallUrl; - container.find(".togetherjs-avatar-preview").css({ - backgroundImage: 'url(' + pendingImage + ')' - }); - container.find(".togetherjs-avatar-save").prop("disabled", false); - if (options.onPending) { - options.onPending(); - } - }); - }); - }); - - }; - - function sizeDownImage(imageUrl) { - return util.Deferred(function (def) { - var $canvas = $(""); - $canvas[0].height = session.AVATAR_SIZE; - $canvas[0].width = session.AVATAR_SIZE; - var context = $canvas[0].getContext("2d"); - var img = new Image(); - img.src = imageUrl; - // Sometimes the DOM updates immediately to call - // naturalWidth/etc, and sometimes it doesn't; using setTimeout - // gives it a chance to catch up - setTimeout(function () { - var width = img.naturalWidth || img.width; - var height = img.naturalHeight || img.height; - width = width * (session.AVATAR_SIZE / height); - height = session.AVATAR_SIZE; - context.drawImage(img, 0, 0, width, height); - def.resolve($canvas[0].toDataURL("image/png")); - }); - }); - } - - function fixupAvatars(container) { - /* All
elements need an element inside, - so we add that element here */ - container.find(".togetherjs-person").each(function () { - var $this = $(this); - var inner = $this.find(".togetherjs-person-avatar-swatch"); - if (! inner.length) { - $this.append('
'); - } - }); - } - - ui.prepareShareLink = function (container) { - container.find("input.togetherjs-share-link").click(function () { - $(this).select(); - }).change(function () { - updateShareLink(); - }); - container.find("a.togetherjs-share-link").click(function () { - // FIXME: this is currently opening up Bluetooth, not sharing a link - if (false && window.MozActivity) { - var activity = new MozActivity({ - name: "share", - data: { - type: "url", - url: $(this).attr("href") - } - }); - } - // FIXME: should show some help if you actually try to follow the link - // like this, instead of simply suppressing it - return false; - }); - updateShareLink(); - }; - - // Menu - - function showMenu(event) { - var el = $("#togetherjs-menu"); - assert(el.length); - el.show(); - bindMenu(); - $(document).bind("click", maybeHideMenu); - } - - function bindMenu() { - var el = $("#togetherjs-menu:visible"); - if (el.length) { - var bound = $("#togetherjs-profile-button"); - var boundOffset = bound.offset(); - el.css({ - top: boundOffset.top + bound.height() - $window.scrollTop() + "px", - left: (boundOffset.left + bound.width() - 10 - el.width() - $window.scrollLeft()) + "px" - }); - } - } - - function bindPicker() { - var picker = $("#togetherjs-pick-color:visible"); - if (picker.length) { - var menu = $("#togetherjs-menu-update-color"); - var menuOffset = menu.offset(); - picker.css({ - top: menuOffset.top + menu.height(), - left: menuOffset.left - }); - } - } - - session.on("resize", function () { - bindMenu(); - bindPicker(); - }); - - function toggleMenu() { - if ($("#togetherjs-menu").is(":visible")) { - hideMenu(); - } else { - showMenu(); - } - } - - function hideMenu() { - var el = $("#togetherjs-menu"); - el.hide(); - $(document).unbind("click", maybeHideMenu); - ui.displayToggle("#togetherjs-self-name-display"); - $("#togetherjs-pick-color").hide(); - } - - function maybeHideMenu(event) { - var t = event.target; - while (t) { - if (t.id == "togetherjs-menu") { - // Click inside the menu, ignore this - return; - } - t = t.parentNode; - } - hideMenu(); - } - - function adjustDockSize(buttons) { - /* Add or remove spots from the dock; positive number to - add button(s), negative number to remove button(s) - */ - assert(typeof buttons == "number"); - assert(buttons && Math.floor(buttons) == buttons); - var iface = $("#togetherjs-dock"); - var newHeight = iface.height() + (BUTTON_HEIGHT * buttons); - assert(newHeight >= BUTTON_HEIGHT * 3, "Height went too low (", newHeight, - "), should never be less than 3 buttons high (", BUTTON_HEIGHT * 3, ")"); - iface.css({ - height: newHeight + "px" - }); - } - - // Misc - - function updateShareLink() { - var input = $("input.togetherjs-share-link"); - var link = $("a.togetherjs-share-link"); - var display = $("#togetherjs-session-id"); - if (! session.shareId) { - input.val(""); - link.attr("href", "#"); - display.text("(none)"); - } else { - input.val(session.shareUrl()); - link.attr("href", session.shareUrl()); - display.text(session.shareId); - } - } - - session.on("close", function () { - - if($.browser.mobile) { - // remove bg overlay - //$(".overlay").remove(); - - //after hitting End, reset window draggin - $("body").css({ - "position": "", - top: "", - left: "" - }); - - } - - if (ui.container) { - ui.container.remove(); - ui.container = null; - } - // Clear out any other spurious elements: - $(".togetherjs").remove(); - var starterButton = $("#togetherjs-starter button"); - starterButton.removeClass("togetherjs-running"); - if (starterButton.attr("data-start-text")) { - starterButton.text(starterButton.attr("data-start-text")); - starterButton.attr("data-start-text", ""); - } - if (TogetherJS.startTarget) { - var el = $(TogetherJS.startTarget); - if (el.attr("data-start-togetherjs-html")) { - el.html(el.attr("data-start-togetherjs-html")); - } - el.removeClass("togetherjs-started"); - } - }); - - ui.chat = { - text: function (attrs) { - assert(typeof attrs.text == "string"); - assert(attrs.peer); - assert(attrs.messageId); - var date = attrs.date || Date.now(); - var lastEl = ui.container.find("#togetherjs-chat .togetherjs-chat-message"); - if (lastEl.length) { - lastEl = $(lastEl[lastEl.length-1]); - } - var lastDate = null; - if (lastEl) { - lastDate = parseInt(lastEl.attr("data-date"), 10); - } - if (lastEl && lastEl.attr("data-person") == attrs.peer.id && - lastDate && date < lastDate + COLLAPSE_MESSAGE_LIMIT) { - lastEl.attr("data-date", date); - var content = lastEl.find(".togetherjs-chat-content"); - assert(content.length); - attrs.text = content.text() + "\n" + attrs.text; - attrs.messageId = lastEl.attr("data-message-id"); - lastEl.remove(); - } - var el = templating.sub("chat-message", { - peer: attrs.peer, - content: attrs.text, - date: date - }); - linkify(el.find(".togetherjs-chat-content")); - el.attr("data-person", attrs.peer.id) - .attr("data-date", date) - .attr("data-message-id", attrs.messageId); - ui.chat.add(el, attrs.messageId, attrs.notify); - }, - - joinedSession: function (attrs) { - assert(attrs.peer); - var date = attrs.date || Date.now(); - var el = templating.sub("chat-joined", { - peer: attrs.peer, - date: date - }); - // FIXME: should bind the notification to the dock location - ui.chat.add(el, attrs.peer.className("join-message-"), 4000); - }, - - leftSession: function (attrs) { - assert(attrs.peer); - var date = attrs.date || Date.now(); - var el = templating.sub("chat-left", { - peer: attrs.peer, - date: date, - declinedJoin: attrs.declinedJoin - }); - // FIXME: should bind the notification to the dock location - ui.chat.add(el, attrs.peer.className("join-message-"), 4000); - }, - - system: function (attrs) { - assert(! attrs.peer); - assert(typeof attrs.text == "string"); - var date = attrs.date || Date.now(); - var el = templating.sub("chat-system", { - content: attrs.text, - date: date - }); - ui.chat.add(el, undefined, true); - }, - - clear: deferForContainer(function () { - var container = ui.container.find("#togetherjs-chat-messages"); - container.empty(); - }), - - urlChange: function (attrs) { - assert(attrs.peer); - assert(typeof attrs.url == "string"); - assert(typeof attrs.sameUrl == "boolean"); - var messageId = attrs.peer.className("url-change-"); - // FIXME: duplicating functionality in .add(): - var realId = "togetherjs-chat-" + messageId; - var date = attrs.date || Date.now(); - var title; - // FIXME: strip off common domain from msg.url? E.g., if I'm on - // http://example.com/foobar, and someone goes to http://example.com/baz then - // show only /baz - // FIXME: truncate long titles - if (attrs.title) { - title = attrs.title + " (" + attrs.url + ")"; - } else { - title = attrs.url; - } - var el = templating.sub("url-change", { - peer: attrs.peer, - date: date, - href: attrs.url, - title: title, - sameUrl: attrs.sameUrl - }); - el.find(".togetherjs-nudge").click(function () { - attrs.peer.nudge(); - return false; - }); - el.find(".togetherjs-follow").click(function () { - var url = attrs.peer.url; - if (attrs.peer.urlHash) { - url += attrs.peer.urlHash; - } - location.href = url; - }); - var notify = ! attrs.sameUrl; - if (attrs.sameUrl && ! $("#" + realId).length) { - // Don't bother showing a same-url notification, if no previous notification - // had been shown - return; - } - ui.chat.add(el, messageId, notify); - }, - - invite: function (attrs) { - assert(attrs.peer); - assert(typeof attrs.url == "string"); - var messageId = attrs.peer.className("invite-"); - var date = attrs.date || Date.now(); - var hrefTitle = attrs.url.replace(/\#?&togetherjs=.*/, "").replace(/^\w+:\/\//, ""); - var el = templating.sub("invite", { - peer: attrs.peer, - date: date, - href: attrs.url, - hrefTitle: hrefTitle, - forEveryone: attrs.forEveryone - }); - if (attrs.forEveryone) { - el.find("a").click(function () { - // FIXME: hacky way to do this: - chat.submit("Followed link to " + attrs.url); - }); - } - ui.chat.add(el, messageId, true); - }, - - hideTimeout: null, - - add: deferForContainer(function (el, id, notify) { - if (id) { - el.attr("id", "togetherjs-chat-" + util.safeClassName(id)); - } - var container = ui.container.find("#togetherjs-chat-messages"); - assert(container.length); - var popup = ui.container.find("#togetherjs-chat-notifier"); - container.append(el); - ui.chat.scroll(); - var doNotify = !! notify; - var section = popup.find("#togetherjs-chat-notifier-message"); - if (notify && visibilityApi.hidden()) { - ui.container.find("#togetherjs-notification")[0].play(); - } - if (id && section.data("message-id") == id) { - doNotify = true; - } - if (container.is(":visible")) { - doNotify = false; - } - if (doNotify) { - section.empty(); - section.append(el.clone(true, true)); - if (section.data("message-id") != id) { - section.data("message-id", id || ""); - windowing.show(popup); - } else if (! popup.is(":visible")) { - windowing.show(popup); - } - if (typeof notify == "number") { - // This is the amount of time we're supposed to notify - if (this.hideTimeout) { - clearTimeout(this.hideTimeout); - this.hideTimeout = null; - } - this.hideTimeout = setTimeout((function () { - windowing.hide(popup); - this.hideTimeout = null; - }).bind(this), notify); - } - } - }), - - scroll: deferForContainer(function () { - var container = ui.container.find("#togetherjs-chat-messages")[0]; - container.scrollTop = container.scrollHeight; - }) - - }; - - session.on("display-window", function (id, win) { - if (id == "togetherjs-chat") { - ui.chat.scroll(); - windowing.hide("#togetherjs-chat-notifier"); - } - }); - - /* This class is bound to peers.Peer instances as peer.view. - The .update() method is regularly called by peer objects when info changes. */ - ui.PeerView = util.Class({ - - constructor: function (peer) { - assert(peer.isSelf !== undefined, "PeerView instantiated with non-Peer object"); - this.peer = peer; - this.dockClick = this.dockClick.bind(this); - }, - - /* Takes an element and sets any person-related attributes on the element - Different from updates, which use the class names we set here: */ - setElement: function (el) { - var count = 0; - var classes = ["togetherjs-person", "togetherjs-person-status", - "togetherjs-person-name", "togetherjs-person-name-abbrev", - "togetherjs-person-bgcolor", "togetherjs-person-swatch", - "togetherjs-person-status", "togetherjs-person-role", - "togetherjs-person-url", "togetherjs-person-url-title", - "togetherjs-person-bordercolor"]; - classes.forEach(function (cls) { - var els = el.find("." + cls); - els.addClass(this.peer.className(cls + "-")); - count += els.length; - }, this); - if (! count) { - console.warn("setElement(", el, ") doesn't contain any person items"); - } - this.updateDisplay(el); - }, - - updateDisplay: deferForContainer(function (container) { - container = container || ui.container; - var abbrev = this.peer.name; - if (this.peer.isSelf) { - abbrev = "me"; - } - container.find("." + this.peer.className("togetherjs-person-name-")).text(this.peer.name || ""); - container.find("." + this.peer.className("togetherjs-person-name-abbrev-")).text(abbrev); - var avatarEl = container.find("." + this.peer.className("togetherjs-person-")); - if (this.peer.avatar) { - util.assertValidUrl(this.peer.avatar); - avatarEl.css({ - backgroundImage: "url(" + this.peer.avatar + ")" - }); - } - if (this.peer.idle == "inactive") { - avatarEl.addClass("togetherjs-person-inactive"); - } else { - avatarEl.removeClass("togetherjs-person-inactive"); - } - avatarEl.attr("title", this.peer.name); - if (this.peer.color) { - avatarEl.css({ - borderColor: this.peer.color - }); - avatarEl.find(".togetherjs-person-avatar-swatch").css({ - borderTopColor: this.peer.color, - borderRightColor: this.peer.color - }); - } - if (this.peer.color) { - var colors = container.find("." + this.peer.className("togetherjs-person-bgcolor-")); - colors.css({ - backgroundColor: this.peer.color - }); - colors = container.find("." + this.peer.className("togetherjs-person-bordercolor-")); - colors.css({ - borderColor: this.peer.color - }); - } - container.find("." + this.peer.className("togetherjs-person-role-")) - .text(this.peer.isCreator ? "Creator" : "Participant"); - var urlName = this.peer.title || ""; - if (this.peer.title) { - urlName += " ("; - } - urlName += util.truncateCommonDomain(this.peer.url, location.href); - if (this.peer.title) { - urlName += ")"; - } - container.find("." + this.peer.className("togetherjs-person-url-title-")) - .text(urlName); - var url = this.peer.url; - if (this.peer.urlHash) { - url += this.peer.urlHash; - } - container.find("." + this.peer.className("togetherjs-person-url-")) - .attr("href", url); - // FIXME: should have richer status: - container.find("." + this.peer.className("togetherjs-person-status-")) - .text(this.peer.idle == "active" ? "Active" : "Inactive"); - if (this.peer.isSelf) { - // FIXME: these could also have consistent/reliable class names: - var selfName = $(".togetherjs-self-name"); - selfName.each((function (index, el) { - el = $(el); - if (el.val() != this.peer.name) { - el.val(this.peer.name); - } - }).bind(this)); - $("#togetherjs-menu-avatar").attr("src", this.peer.avatar); - if (! this.peer.name) { - $("#togetherjs-menu .togetherjs-person-name-self").text(this.peer.defaultName); - } - } - if (this.peer.url != session.currentUrl()) { - container.find("." + this.peer.className("togetherjs-person-")) - .addClass("togetherjs-person-other-url"); - } else { - container.find("." + this.peer.className("togetherjs-person-")) - .removeClass("togetherjs-person-other-url"); - } - if (this.peer.following) { - if (this.followCheckbox) { - this.followCheckbox.prop("checked", true); - } - } else { - if (this.followCheckbox) { - this.followCheckbox.prop("checked", false); - } - } - // FIXME: add some style based on following? - updateChatParticipantList(); - this.updateFollow(); - }), - - update: function () { - if (! this.peer.isSelf) { - if (this.peer.status == "live") { - this.dock(); - } else { - this.undock(); - } - } - this.updateDisplay(); - this.updateUrlDisplay(); - }, - - updateUrlDisplay: function (force) { - var url = this.peer.url; - if ((! url) || (url == this._lastUpdateUrlDisplay && ! force)) { - return; - } - this._lastUpdateUrlDisplay = url; - var sameUrl = url == session.currentUrl(); - ui.chat.urlChange({ - peer: this.peer, - url: this.peer.url, - title: this.peer.title, - sameUrl: sameUrl - }); - }, - - urlNudge: function () { - // FIXME: do something more distinct here - this.updateUrlDisplay(true); - }, - - notifyJoined: function () { - ui.chat.joinedSession({ - peer: this.peer - }); - }, - - // when there are too many participants in the dock, consolidate the participants to one avatar, and on mouseOver, the dock expands down to reveal the rest of the participants - // if there are X users in the session - // then hide the users in the dock - // and shrink the size of the dock - // and if you rollover the dock, it expands and reveals the rest of the participants in the dock - - //if users hit X then show the participant button with the consol - - dock: deferForContainer(function () { - - var numberOfUsers = peers.getAllPeers().length; - - // collapse the Dock if too many users - function CollapsedDock() { - // decrease/reset dock height - $("#togetherjs-dock").css("height", 260); - //replace participant button - $("#togetherjs-dock-participants").replaceWith(""); - // new full participant window created on toggle - $("#togetherjs-participantlist-button").click(function () { - windowing.toggle("#togetherjs-participantlist"); - }); - } - - // FIXME: turned off for now - if( numberOfUsers >= 5 && false) { - CollapsedDock(); - } else { - // reset - - } - - - if (this.dockElement) { - return; - } - this.dockElement = templating.sub("dock-person", { - peer: this.peer - }); - this.dockElement.attr("id", this.peer.className("togetherjs-dock-element-")); - ui.container.find("#togetherjs-dock-participants").append(this.dockElement); - this.dockElement.find(".togetherjs-person").animateDockEntry(); - adjustDockSize(1); - this.detailElement = templating.sub("participant-window", { - peer: this.peer - }); - var followId = this.peer.className("togetherjs-person-status-follow-"); - this.detailElement.find('[for="togetherjs-person-status-follow"]').attr("for", followId); - this.detailElement.find('#togetherjs-person-status-follow').attr("id", followId); - this.detailElement.find(".togetherjs-follow").click(function () { - location.href = $(this).attr("href"); - }); - this.detailElement.find(".togetherjs-nudge").click((function () { - this.peer.nudge(); - }).bind(this)); - this.followCheckbox = this.detailElement.find("#" + followId); - this.followCheckbox.change(function () { - if (! this.checked) { - this.peer.unfollow(); - } - // Following doesn't happen until the window is closed - // FIXME: should we tell the user this? - }); - this.maybeHideDetailWindow = this.maybeHideDetailWindow.bind(this); - session.on("hide-window", this.maybeHideDetailWindow); - ui.container.append(this.detailElement); - this.dockElement.click((function () { - if (this.detailElement.is(":visible")) { - windowing.hide(this.detailElement); - } else { - windowing.show(this.detailElement, {bind: this.dockElement}); - this.scrollTo(); - this.cursor().element.animate({ - opacity:0.3 - }).animate({ - opacity:1 - }).animate({ - opacity:0.3 - }).animate({ - opacity:1 - }); - } - }).bind(this)); - this.updateFollow(); - }), - - undock: function () { - if (! this.dockElement) { - return; - } - this.dockElement.animateDockExit().promise().then((function () { - this.dockElement.remove(); - this.dockElement = null; - this.detailElement.remove(); - this.detailElement = null; - adjustDockSize(-1); - }).bind(this)); - }, - - scrollTo: function () { - if (this.peer.url != session.currentUrl()) { - return; - } - var pos = this.peer.scrollPosition; - if (! pos) { - console.warn("Peer has no scroll position:", this.peer); - return; - } - pos = elementFinder.pixelForPosition(pos); - $("html, body").easeTo(pos); - }, - - updateFollow: function () { - if (! this.peer.url) { - return; - } - if (! this.detailElement) { - return; - } - var same = this.detailElement.find(".togetherjs-same-url"); - var different = this.detailElement.find(".togetherjs-different-url"); - if (this.peer.url == session.currentUrl()) { - same.show(); - different.hide(); - } else { - same.hide(); - different.show(); - } - }, - - maybeHideDetailWindow: function (windows) { - if (this.detailElement && windows[0] && windows[0][0] === this.detailElement[0]) { - if (this.followCheckbox[0].checked) { - this.peer.follow(); - } else { - this.peer.unfollow(); - } - } - }, - - dockClick: function () { - // FIXME: scroll to person - }, - - cursor: function () { - return require("cursor").getClient(this.peer.id); - }, - - destroy: function () { - // FIXME: should I get rid of the dockElement? - session.off("hide-window", this.maybeHideDetailWindow); - } - }); - - function updateChatParticipantList() { - var live = peers.getAllPeers(true); - if (live.length) { - ui.displayToggle("#togetherjs-chat-participants"); - $("#togetherjs-chat-participant-list").text( - live.map(function (p) {return p.name;}).join(", ")); - } else { - ui.displayToggle("#togetherjs-chat-no-participants"); - } - } - - function inviteHubUrl() { - var base = TogetherJS.config.get("inviteFromRoom"); - assert(base); - return util.makeUrlAbsolute(base, session.hubUrl()); - } - - var inRefresh = false; - - function refreshInvite() { - if (inRefresh) { - return; - } - inRefresh = true; - require(["who"], function (who) { - var def = who.getList(inviteHubUrl()); - function addUser(user, before) { - var item = templating.sub("invite-user-item", {peer: user}); - item.attr("data-clientid", user.id); - if (before) { - item.insertBefore(before); - } else { - $("#togetherjs-invite-users").append(item); - } - item.click(function() { - invite(user.clientId); - }); - } - function refresh(users, finished) { - var sorted = []; - for (var id in users) { - if (users.hasOwnProperty(id)) { - sorted.push(users[id]); - } - } - sorted.sort(function (a, b) { - return a.name < b.name ? -1 : 1; - }); - var pos = 0; - ui.container.find("#togetherjs-invite-users .togetherjs-menu-item").each(function () { - var $this = $(this); - if (finished && ! users[$this.attr("data-clientid")]) { - $this.remove(); - return; - } - if (pos >= sorted.length) { - return; - } - while (pos < sorted.length && $this.attr("data-clientid") !== sorted[pos].id) { - addUser(sorted[pos], $this); - pos++; - } - while (pos < sorted.length && $this.attr("data-clientid") == sorted[pos].id) { - pos++; - } - }); - for (var i=pos; i TOO_FAR_APART; - } - - session.on("close", unsetListeners); - - function unsetListeners() { - var videos = $('video'); - listeners.forEach(function (event) { - videos.off(event.name, event.listener); - }); - listeners = []; - } - - - session.hub.on('video-timeupdate', function (msg) { - var element = $findElement(msg.location); - var oldTime = element.prop('currentTime'); - var newTime = msg.position; - - //to help throttle uneccesary position changes - if(areTooFarApart(oldTime, newTime)){ - setTime(element, msg.position); - } - }); - - MIRRORED_EVENTS.forEach( function (eventName) { - session.hub.on("video-"+eventName, function (msg) { - var element = $findElement(msg.location); - - setTime(element, msg.position); - - element.trigger(eventName, {silent: true}); - }); - }); - - //Currently does not discriminate between visible and invisible videos - function $findElement(location) { - return $(elementFinder.findElement(location)); - } - - function setTime(video, time) { - video.prop('currentTime', time); - } - -}); diff --git a/togetherjs/visibilityApi.js b/togetherjs/visibilityApi.js deleted file mode 100644 index 6d4b6ce94..000000000 --- a/togetherjs/visibilityApi.js +++ /dev/null @@ -1,46 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -/* Loading this module will cause, when TogetherJS is active, the - session object to emit visibility-change with a `hidden` argument - whenever the visibility changes, on browsers where we can detect - it. - */ - -define(["util", "session"], function (util, session) { - var visibilityApi = util.Module("visibilityApi"); - var hidden; - var visibilityChange; - if (document.hidden !== undefined) { // Opera 12.10 and Firefox 18 and later support - hidden = "hidden"; - visibilityChange = "visibilitychange"; - } else if (document.mozHidden !== undefined) { - hidden = "mozHidden"; - visibilityChange = "mozvisibilitychange"; - } else if (document.msHidden !== undefined) { - hidden = "msHidden"; - visibilityChange = "msvisibilitychange"; - } else if (document.webkitHidden !== undefined) { - hidden = "webkitHidden"; - visibilityChange = "webkitvisibilitychange"; - } - - session.on("start", function () { - document.addEventListener(visibilityChange, change, false); - }); - - session.on("close", function () { - document.removeEventListener(visibilityChange, change, false); - }); - - function change() { - session.emit("visibility-change", document[hidden]); - } - - visibilityApi.hidden = function () { - return document[hidden]; - }; - - return visibilityApi; -}); diff --git a/togetherjs/walkthrough.js b/togetherjs/walkthrough.js deleted file mode 100644 index 9acd32236..000000000 --- a/togetherjs/walkthrough.js +++ /dev/null @@ -1,151 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -define(["util", "ui", "jquery", "windowing", "templates", "templating", "session", "peers"], function (util, ui, $, windowing, templates, templating, session, peers) { - var assert = util.assert; - var walkthrough = util.Module("walkthrough"); - var onHideAll = null; - var container = null; - - var slides = null; - - walkthrough.start = function (firstTime, doneCallback) { - if (! container) { - container = $(templates("walkthrough")); - container.hide(); - ui.container.append(container); - slides = container.find(".togetherjs-walkthrough-slide"); - slides.hide(); - var progress = $("#togetherjs-walkthrough-progress"); - slides.each(function (index) { - var bullet = templating.sub("walkthrough-slide-progress"); - progress.append(bullet); - bullet.click(function () { - show(index); - }); - }); - container.find("#togetherjs-walkthrough-previous").click(previous); - container.find("#togetherjs-walkthrough-next").click(next); - ui.prepareShareLink(container); - container.find(".togetherjs-self-name").bind("keyup", function (event) { - var val = $(event.target).val(); - peers.Self.update({name: val}); - }); - container.find(".togetherjs-swatch").click(function () { - var picker = $("#togetherjs-pick-color"); - if (picker.is(":visible")) { - picker.hide(); - return; - } - picker.show(); - picker.find(".togetherjs-swatch-active").removeClass("togetherjs-swatch-active"); - picker.find(".togetherjs-swatch[data-color=\"" + peers.Self.color + "\"]").addClass("togetherjs-swatch-active"); - var location = container.find(".togetherjs-swatch").offset(); - picker.css({ - top: location.top, - // The -7 comes out of thin air, but puts it in the right place: - left: location.left-7 - }); - }); - if (session.isClient) { - container.find(".togetherjs-if-creator").remove(); - container.find(".togetherjs-ifnot-creator").show(); - } else { - container.find(".togetherjs-if-creator").show(); - container.find(".togetherjs-ifnot-creator").remove(); - } - TogetherJS.config.track("siteName", function (value) { - value = value || document.title; - container.find(".togetherjs-site-name").text(value); - }); - ui.activateAvatarEdit(container, { - onSave: function () { - container.find("#togetherjs-avatar-when-saved").show(); - container.find("#togetherjs-avatar-when-unsaved").hide(); - }, - onPending: function () { - container.find("#togetherjs-avatar-when-saved").hide(); - container.find("#togetherjs-avatar-when-unsaved").show(); - } - }); - // This triggers substititions in the walkthrough: - peers.Self.update({}); - session.emit("new-element", container); - } - assert(typeof firstTime == "boolean", "You must provide a firstTime boolean parameter"); - if (firstTime) { - container.find(".togetherjs-walkthrough-firsttime").show(); - container.find(".togetherjs-walkthrough-not-firsttime").hide(); - } else { - container.find(".togetherjs-walkthrough-firsttime").hide(); - container.find(".togetherjs-walkthrough-not-firsttime").show(); - } - onHideAll = doneCallback; - show(0); - windowing.show(container); - }; - - function show(index) { - slides.hide(); - $(slides[index]).show(); - var bullets = container.find("#togetherjs-walkthrough-progress .togetherjs-walkthrough-slide-progress"); - bullets.removeClass("togetherjs-active"); - $(bullets[index]).addClass("togetherjs-active"); - var $next = $("#togetherjs-walkthrough-next").removeClass("togetherjs-disabled"); - var $previous = $("#togetherjs-walkthrough-previous").removeClass("togetherjs-disabled"); - if (index == slides.length - 1) { - $next.addClass("togetherjs-disabled"); - } else if (index === 0) { - $previous.addClass("togetherjs-disabled"); - } - } - - function previous() { - var index = getIndex(); - index--; - if (index < 0) { - index = 0; - } - show(index); - } - - function next() { - var index = getIndex(); - index++; - if (index >= slides.length) { - index = slides.length-1; - } - show(index); - } - - function getIndex() { - var active = slides.filter(":visible"); - if (! active.length) { - return 0; - } - for (var i=0; i"); - $canvas[0].height = session.AVATAR_SIZE; - $canvas[0].width = session.AVATAR_SIZE; - var context = $canvas[0].getContext("2d"); - context.arc(session.AVATAR_SIZE/2, session.AVATAR_SIZE/2, session.AVATAR_SIZE/2, 0, Math.PI*2); - context.closePath(); - context.clip(); - context.drawImage($video[0], (session.AVATAR_SIZE - width) / 2, 0, width, height); - savePicture($canvas[0].toDataURL("image/png")); - } - - $upload.on("change", function () { - var reader = new FileReader(); - reader.onload = function () { - // FIXME: I don't actually know it's JPEG, but it's probably a - // good enough guess: - var url = "data:image/jpeg;base64," + util.blobToBase64(this.result); - convertImage(url, function (result) { - savePicture(result); - }); - }; - reader.onerror = function () { - console.error("Error reading file:", this.error); - }; - reader.readAsArrayBuffer(this.files[0]); - }); - - function convertImage(imageUrl, callback) { - var $canvas = $(""); - $canvas[0].height = session.AVATAR_SIZE; - $canvas[0].width = session.AVATAR_SIZE; - var context = $canvas[0].getContext("2d"); - var img = new Image(); - img.src = imageUrl; - // Sometimes the DOM updates immediately to call - // naturalWidth/etc, and sometimes it doesn't; using setTimeout - // gives it a chance to catch up - setTimeout(function () { - var width = img.naturalWidth || img.width; - var height = img.naturalHeight || img.height; - width = width * (session.AVATAR_SIZE / height); - height = session.AVATAR_SIZE; - context.drawImage(img, 0, 0, width, height); - callback($canvas[0].toDataURL("image/png")); - }); - } - - }); - - /**************************************** - * RTC support - */ - - function audioButton(selector) { - ui.displayToggle(selector); - if (selector == "#togetherjs-audio-incoming") { - $("#togetherjs-audio-button").addClass("togetherjs-animated").addClass("togetherjs-color-alert"); - } else { - $("#togetherjs-audio-button").removeClass("togetherjs-animated").removeClass("togetherjs-color-alert"); - } - } - - session.on("ui-ready", function () { - $("#togetherjs-audio-button").click(function () { - if ($("#togetherjs-rtc-info").is(":visible")) { - windowing.hide(); - return; - } - if (session.RTCSupported) { - enableAudio(); - } else { - windowing.show("#togetherjs-rtc-not-supported"); - } - }); - - if (! session.RTCSupported) { - audioButton("#togetherjs-audio-unavailable"); - return; - } - audioButton("#togetherjs-audio-ready"); - - var audioStream = null; - var accepted = false; - var connected = false; - var $audio = $("#togetherjs-audio-element"); - var offerSent = null; - var offerReceived = null; - var offerDescription = false; - var answerSent = null; - var answerReceived = null; - var answerDescription = false; - var _connection = null; - var iceCandidate = null; - - function enableAudio() { - accepted = true; - storage.settings.get("dontShowRtcInfo").then(function (dontShow) { - if (! dontShow) { - windowing.show("#togetherjs-rtc-info"); - } - }); - if (! audioStream) { - startStreaming(connect); - return; - } - if (! connected) { - connect(); - } - toggleMute(); - } - - ui.container.find("#togetherjs-rtc-info .togetherjs-dont-show-again").change(function () { - storage.settings.set("dontShowRtcInfo", this.checked); - }); - - function error() { - console.warn.apply(console, arguments); - var s = ""; - for (var i=0; i= expected) { - close(); - } else { - def.notify(users); - } - } - } - console.log("users", users); - }; - channel.send({ - type: "who", - "server-echo": true, - clientId: null - }); - var timeout = setTimeout(function () { - close(); - }, MAX_RESPONSE_TIME); - function close() { - if (timeout) { - clearTimeout(timeout); - } - if (lateResponseTimeout) { - clearTimeout(lateResponseTimeout); - } - channel.close(); - def.resolve(users); - } - }); - }; - - who.invite = function (hubUrl, clientId) { - return util.Deferred(function (def) { - var channel = channels.WebSocketChannel(hubUrl); - var id = util.generateId(); - channel.onmessage = function (msg) { - if (msg.type == "invite" && msg.inviteId == id) { - channel.close(); - def.resolve(); - } - }; - var userInfo = session.makeHelloMessage(false); - delete userInfo.type; - userInfo.clientId = session.clientId; - channel.send({ - type: "invite", - inviteId: id, - url: session.shareUrl(), - userInfo: userInfo, - forClientId: clientId, - clientId: null, - "server-echo": true - }); - }); - }; - - who.ExternalPeer = util.Class({ - isSelf: false, - isExternal: true, - constructor: function (id, attrs) { - attrs = attrs || {}; - assert(id); - this.id = id; - this.identityId = attrs.identityId || null; - this.status = attrs.status || "live"; - this.idle = attrs.status || "active"; - this.name = attrs.name || null; - this.avatar = attrs.avatar || null; - this.color = attrs.color || "#00FF00"; - this.lastMessageDate = 0; - this.view = ui.PeerView(this); - }, - - className: function (prefix) { - prefix = prefix || ""; - return prefix + util.safeClassName(this.id); - } - - }); - - return who; -}); diff --git a/togetherjs/windowing.js b/togetherjs/windowing.js deleted file mode 100644 index ee02fc537..000000000 --- a/togetherjs/windowing.js +++ /dev/null @@ -1,216 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ -define(["jquery", "util", "peers", "session"], function ($, util, peers, session) { - var assert = util.assert; - var windowing = util.Module("windowing"); - var $window = $(window); - // This is also in togetherjs.less, under .togetherjs-animated - var ANIMATION_DURATION = 1000; - - /* Displays one window. A window must already exist. This hides other windows, and - positions the window according to its data-bound-to attributes */ - windowing.show = function (element, options) { - element = $(element); - options = options || {}; - options.bind = options.bind || element.attr("data-bind-to"); - var notification = element.hasClass("togetherjs-notification"); - var modal = element.hasClass("togetherjs-modal"); - if (options.bind) { - options.bind = $(options.bind); - } - windowing.hide(); - element.stop(); - element.show(); - // In addition to being hidden, the window can be faded out, which we want to undo: - element.css({opacity: "1"}); - if (options.bind) { - assert(! modal, "Binding does not currently work with modals"); - bind(element, options.bind); - } - if (notification) { - element.slideIn(); - } else if (! modal) { - element.popinWindow(); - } - if (modal) { - getModalBackground().show(); - modalEscape.bind(); - } - onClose = options.onClose || null; - session.emit("display-window", element.attr("id"), element); - }; - - var onClose = null; - - /* Moves a window to be attached to data-bind-to, e.g., the button - that opened the window. Or you can provide an element that it should bind to. */ - function bind(win, bound) { - if ($.browser.mobile) { - return; - } - win = $(win); - assert(bound.length, "Cannot find binding:", bound.selector, "from:", win.selector); - // FIXME: hardcoding - var ifacePos = "right"; - //var ifacePos = panelPosition(); - var boundPos = bound.offset(); - boundPos.height = bound.height(); - boundPos.width = bound.width(); - var windowHeight = $window.height(); - boundPos.top -= $window.scrollTop(); - boundPos.left -= $window.scrollLeft(); - // FIXME: I appear to have to add the padding to the width to get a "true" - // width. But it's still not entirely consistent. - var height = win.height() + 5; - var width = win.width() + 20; - var left, top; - if (ifacePos == "right") { - left = boundPos.left - 11 - width; - top = boundPos.top + (boundPos.height / 2) - (height / 2); - } else if (ifacePos == "left") { - left = boundPos.left + boundPos.width + 15; - top = boundPos.top + (boundPos.height / 2) - (height / 2); - } else if (ifacePos == "bottom") { - left = (boundPos.left + boundPos.width / 2) - (width / 2); - top = boundPos.top - 10 - height; - } - top = Math.min(windowHeight - 10 - height, Math.max(10, top)); - win.css({ - top: top + "px", - left: left + "px" - }); - if (win.hasClass("togetherjs-window")) { - $("#togetherjs-window-pointer-right, #togetherjs-window-pointer-left").hide(); - var pointer = $("#togetherjs-window-pointer-" + ifacePos); - pointer.show(); - if (ifacePos == "right") { - pointer.css({ - top: boundPos.top + Math.floor(boundPos.height / 2) + "px", - left: left + win.width() + 9 + "px" - }); - } else if (ifacePos == "left") { - pointer.css({ - top: boundPos.top + Math.floor(boundPos.height / 2) + "px", - left: (left - 5) + "px" - }); - } else { - console.warn("don't know how to deal with position:", ifacePos); - } - } - win.data("boundTo", bound.selector || "#" + bound.attr("id")); - bound.addClass("togetherjs-active"); - } - - session.on("resize", function () { - var win = $(".togetherjs-modal:visible, .togetherjs-window:visible"); - if (! win.length) { - return; - } - var boundTo = win.data("boundTo"); - if (! boundTo) { - return; - } - boundTo = $(boundTo); - bind(win, boundTo); - }); - - windowing.hide = function (els) { - // FIXME: also hide modals? - els = els || ".togetherjs-window, .togetherjs-modal, .togetherjs-notification"; - els = $(els); - els = els.filter(":visible"); - els.filter(":not(.togetherjs-notification)").hide(); - getModalBackground().hide(); - var windows = []; - els.each(function (index, element) { - element = $(element); - windows.push(element); - var bound = element.data("boundTo"); - if (! bound) { - return; - } - bound = $(bound); - bound.addClass("togetherjs-animated").addClass("togetherjs-color-pulse"); - setTimeout(function () { - bound.removeClass("togetherjs-color-pulse").removeClass("togetherjs-animated"); - }, ANIMATION_DURATION+10); - element.data("boundTo", null); - bound.removeClass("togetherjs-active"); - if (element.hasClass("togetherjs-notification")) { - element.fadeOut().promise().then(function () { - this.hide(); - }); - } - }); - $("#togetherjs-window-pointer-right, #togetherjs-window-pointer-left").hide(); - if (onClose) { - onClose(); - onClose = null; - } - if (windows.length) { - session.emit("hide-window", windows); - } - }; - - windowing.showNotification = function (element, options) { - element = $(element); - options = options || {}; - assert(false); - }; - - windowing.toggle = function (el) { - el = $(el); - if (el.is(":visible")) { - windowing.hide(el); - } else { - windowing.show(el); - } - }; - - function bindEvents(el) { - el.find(".togetherjs-close, .togetherjs-dismiss").click(function (event) { - var w = $(event.target).closest(".togetherjs-window, .togetherjs-modal, .togetherjs-notification"); - windowing.hide(w); - event.stopPropagation(); - return false; - }); - } - - function getModalBackground() { - if (getModalBackground.element) { - return getModalBackground.element; - } - var background = $("#togetherjs-modal-background"); - assert(background.length); - getModalBackground.element = background; - background.click(function () { - windowing.hide(); - }); - return background; - } - - var modalEscape = { - bind: function () { - $(document).keydown(modalEscape.onKeydown); - }, - unbind: function () { - $(document).unbind("keydown", modalEscape.onKeydown); - }, - onKeydown: function (event) { - if (event.which == 27) { - windowing.hide(); - } - } - }; - - session.on("close", function () { - modalEscape.unbind(); - }); - - session.on("new-element", function (el) { - bindEvents(el); - }); - - return windowing; -}); diff --git a/togetherjs/youtubeVideos.js b/togetherjs/youtubeVideos.js deleted file mode 100644 index 95d6a9ee1..000000000 --- a/togetherjs/youtubeVideos.js +++ /dev/null @@ -1,302 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http:// mozilla.org/MPL/2.0/. */ - -define(["jquery", "util", "session", "elementFinder"], -function ($, util, session, elementFinder) { - - // constant var to indicate whether two players are too far apart in sync - var TOO_FAR_APART = 3000; - // embedded youtube iframes - var youTubeIframes = []; - // youtube API load delay - var API_LOADING_DELAY = 2000; - - session.on("reinitialize", function () { - if (TogetherJS.config.get("youtube")) { - prepareYouTube(); - } - }); - - session.on("close", function () { - $(youTubeIframes).each(function (i, iframe) { - // detach players from iframes - $(iframe).removeData("togetherjs-player"); - $(iframe).removeData("dontPublish"); - $(iframe).removeData("currentVideoId"); - // disable iframeAPI - $(iframe).removeAttr("enablejsapi"); - // remove unique youtube iframe indicators - var id = $(iframe).attr("id") || ""; - if (id.indexOf("youtube-player") === 0) { - // An id we added - $(iframe).removeAttr("id"); - } - youTubeIframes = []; - }); - }); - - $(function() { - TogetherJS.config.track("youtube", function (track, previous) { - if (track && ! previous) { - prepareYouTube(); - // You can enable youtube dynamically, but can't turn it off: - TogetherJS.config.close("youtube"); - } - }); - }); - - var youtubeHooked = false; - - function prepareYouTube() { - // setup iframes first - setupYouTubeIframes(); - - // this function should be global so it can be called when API is loaded - if (!youtubeHooked) { - youtubeHooked = true; - window.onYouTubeIframeAPIReady = (function(oldf) { - return function() { - // YouTube API is ready - $(youTubeIframes).each(function (i, iframe) { - var player = new YT.Player(iframe.id, { // get the reference to the already existing iframe - events: { - 'onReady': insertPlayer, - 'onStateChange': publishPlayerStateChange - } - }); - }); - if (oldf) { - return oldf(); - } - }; - })(window.onYouTubeIframeAPIReady); - } - - if (window.YT === undefined) { - // load necessary API - // it calls onYouTubeIframeAPIReady automatically when the API finishes loading - var tag = document.createElement('script'); - tag.src = "https://www.youtube.com/iframe_api"; - var firstScriptTag = document.getElementsByTagName('script')[0]; - firstScriptTag.parentNode.insertBefore(tag, firstScriptTag); - } else { - // manually invoke APIReady function when the API was already loaded by user - onYouTubeIframeAPIReady(); - } - - // give each youtube iframe a unique id and set its enablejsapi param to true - function setupYouTubeIframes() { - var iframes = $('iframe'); - iframes.each(function (i, iframe) { - // if the iframe's unique id is already set, skip it - // FIXME: what if the user manually sets an iframe's id (i.e. "#my-youtube")? - // maybe we should set iframes everytime togetherjs is reinitialized? - var osrc = $(iframe).attr("src"), src = osrc; - if ((src || "").indexOf("youtube") != -1 && !$(iframe).attr("id")) { - $(iframe).attr("id", "youtube-player"+i); - $(iframe).attr("enablejsapi", 1); - // we also need to add ?enablejsapi to the iframe src. - if (!/[?&]enablejsapi=1(&|$)/.test(src)) { - src += (/[?]/.test(src)) ? '&' : '?'; - src += 'enablejsapi=1'; - } - // the youtube API seems to be unhappy unless the URL starts - // with https - if (!/^https[:]\/\//.test(src)) { - src = 'https://' + src.replace(/^(\w+[:])?\/\//, ''); - } - if (src !== osrc) { - $(iframe).attr("src", src); - } - youTubeIframes[i] = iframe; - } - }); - } // iframes are ready - - function insertPlayer(event) { - // only when it is READY, attach a player to its iframe - var currentPlayer = event.target; - var currentIframe = currentPlayer.getIframe(); - // check if a player is already attached in case of being reinitialized - if (!$(currentIframe).data("togetherjs-player")) { - $(currentIframe).data("togetherjs-player", currentPlayer); - // initialize its dontPublish flag as well - $(currentIframe).data("dontPublish", false); - // store its current video's id - var currentVideoId = getVideoIdFromUrl(currentPlayer.getVideoUrl()); - $(currentIframe).data("currentVideoId", currentVideoId); - } - } - } // end of prepareYouTube - - function publishPlayerStateChange(event) { - var target = event.target; - var currentIframe = target.getIframe(); - //var currentPlayer = $(currentIframe).data("togetherjs-player"); - var currentPlayer = target; - var currentTime = currentPlayer.getCurrentTime(); - //var currentTime = target.k.currentTime; - var iframeLocation = elementFinder.elementLocation(currentIframe); - - if ($(currentPlayer).data("seek")) { - $(currentPlayer).removeData("seek"); - return; - } - - // do not publish if playerState was changed by other users - if ($(currentIframe).data("dontPublish")) { - // make it false again so it can start publishing events of its own state changes - $(currentIframe).data("dontPublish", false); - return; - } - - // notify other people that I changed the player state - if (event.data == YT.PlayerState.PLAYING) { - - var currentVideoId = isDifferentVideoLoaded(currentIframe); - if (currentVideoId) { - // notify that I just loaded another video - publishDifferentVideoLoaded(iframeLocation, currentVideoId); - // update current video id - $(currentIframe).data("currentVideoId", currentVideoId); - } else { - session.send({ - type: "playerStateChange", - element: iframeLocation, - playerState: 1, - playerTime: currentTime - }); - } - } else if (event.data == YT.PlayerState.PAUSED) { - session.send({ - type: "playerStateChange", - element: iframeLocation, - playerState: 2, - playerTime: currentTime - }); - } else { - // do nothing when the state is buffering, cued, or ended - return; - } - } - - function publishDifferentVideoLoaded(iframeLocation, videoId) { - session.send({ - type: "differentVideoLoaded", - videoId: videoId, - element: iframeLocation - }); - } - - session.hub.on('playerStateChange', function (msg) { - var iframe = elementFinder.findElement(msg.element); - var player = $(iframe).data("togetherjs-player"); - var currentTime = player.getCurrentTime(); - var currentState = player.getPlayerState(); - - if (currentState != msg.playerState) { - $(iframe).data("dontPublish", true); - } - - if (msg.playerState == 1) { - player.playVideo(); - // seekTo() updates the video's time and plays it if it was already playing - // and pauses it if it was already paused - if (areTooFarApart(currentTime, msg.playerTime)) { - player.seekTo(msg.playerTime, true); - } - } else if (msg.playerState == 2) { - // When YouTube videos are advanced while playing, - // Chrome: pause -> pause -> play (onStateChange is called even when it is from pause to pause) - // FireFox: buffering -> play -> buffering -> play - // We must prevent advanced videos from going out of sync - player.pauseVideo(); - if (areTooFarApart(currentTime, msg.playerTime)) { - // "seek" flag will help supress publishing unwanted state changes - $(player).data("seek", true); - player.seekTo(msg.playerTime, true); - } - } - }); - - // if a late user joins a channel, synchronize his videos - session.hub.on('hello', function () { - // wait a couple seconds to make sure the late user has finished loading API - setTimeout(synchronizeVideosOfLateGuest, API_LOADING_DELAY); - }); - - session.hub.on('synchronizeVideosOfLateGuest', function (msg) { - // XXX can this message arrive before we're initialized? - var iframe = elementFinder.findElement(msg.element); - var player = $(iframe).data("togetherjs-player"); - // check if another video had been loaded to an existing iframe before I joined - var currentVideoId = getVideoIdFromUrl(player.getVideoUrl()); - if (msg.videoId != currentVideoId) { - $(iframe).data("currentVideoId", msg.videoId); - player.loadVideoById(msg.videoId, msg.playerTime, 'default'); - } else { - // if the video is only cued, I do not have to do anything to sync - if (msg.playerState != 5) { - player.seekTo(msg.playerTime, true).playVideo(); - } - } - }); - - session.hub.on('differentVideoLoaded', function (msg) { - // load a new video if the host has loaded one - var iframe = elementFinder.findElement(msg.element); - var player = $(iframe).data("togetherjs-player"); - player.loadVideoById(msg.videoId, 0, 'default'); - $(iframe).data("currentVideoId", msg.videoId); - - }); - - function synchronizeVideosOfLateGuest() { - youTubeIframes.forEach(function (iframe) { - var currentPlayer = $(iframe).data("togetherjs-player"); - var currentVideoId = getVideoIdFromUrl(currentPlayer.getVideoUrl()); - var currentState = currentPlayer.getPlayerState(); - var currentTime = currentPlayer.getCurrentTime(); - var iframeLocation = elementFinder.elementLocation(iframe); - session.send({ - type: "synchronizeVideosOfLateGuest", - element: iframeLocation, - videoId: currentVideoId, - playerState: currentState, //this might be necessary later - playerTime: currentTime - }); - }); - } - - function isDifferentVideoLoaded(iframe) { - var lastVideoId = $(iframe).data("currentVideoId"); - var currentPlayer = $(iframe).data("togetherjs-player"); - var currentVideoId = getVideoIdFromUrl(currentPlayer.getVideoUrl()); - - // since url forms of iframe src and player's video url are different, - // I have to compare the video ids - if (currentVideoId != lastVideoId) { - return currentVideoId; - } else { - return false; - } - } - - // parses videoId from the url returned by getVideoUrl function - function getVideoIdFromUrl(videoUrl) { - var videoId = videoUrl.split('v=')[1]; - //Chrome and Firefox have different positions for parameters - var ampersandIndex = videoId.indexOf('&'); - if (ampersandIndex != -1) { - videoId = videoId.substring(0, ampersandIndex); - } - return videoId; - } - - function areTooFarApart(myTime, theirTime) { - var secDiff = Math.abs(myTime - theirTime); - var milliDiff = secDiff * 1000; - return milliDiff > TOO_FAR_APART; - } -}); diff --git a/vitest.config.js b/vitest.config.js new file mode 100644 index 000000000..cd84d51ae --- /dev/null +++ b/vitest.config.js @@ -0,0 +1,15 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + // Unit tests import modules directly rather than the built bundle, so the + // same build-time substitutions build/build.mjs makes have to happen here. + define: { + __HUB_URL__: JSON.stringify("http://localhost:8787"), + __GIT_COMMIT__: JSON.stringify("test"), + __BASE_URL__: JSON.stringify("http://localhost:8099/dist"), + }, + test: { + include: ["tests/unit/**/*.test.js"], + environment: "jsdom", + }, +});