Conflicts:
	lib/handlebars.js
	test/index.html
This commit is contained in:
Jason Davies
2011-02-21 01:51:31 +00:00
33 changed files with 2747 additions and 2523 deletions
+5
View File
@@ -0,0 +1,5 @@
dist
lib/handlebars/parser.js
vendor
.rvmrc
.DS_Store
+4
View File
@@ -0,0 +1,4 @@
source "http://rubygems.org"
gem "therubyracer", ">= 0.8.0"
gem "rspec"
+20
View File
@@ -0,0 +1,20 @@
GEM
remote: http://rubygems.org/
specs:
diff-lcs (1.1.2)
rspec (2.1.0)
rspec-core (~> 2.1.0)
rspec-expectations (~> 2.1.0)
rspec-mocks (~> 2.1.0)
rspec-core (2.1.0)
rspec-expectations (2.1.0)
diff-lcs (~> 1.1.2)
rspec-mocks (2.1.0)
therubyracer (0.8.0)
PLATFORMS
ruby
DEPENDENCIES
rspec
therubyracer (>= 0.8.0)
-21
View File
@@ -1,21 +0,0 @@
The MIT License
Copyright (c) 2010 Yehuda Katz
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.
+94 -19
View File
@@ -5,19 +5,19 @@ Handlebars.js is an extension to the [Mustache templating language](http://musta
Installing
----------
Installing Handlebars is easy. Simply [download the package from GitHub](https://github.com/wycats/handlebars.js/zipball/master) and add lib/handlebars.js to your web pages.
Installing Handlebars is easy. Simply [download the package from GitHub](https://github.com/wycats/handlebars.js/archives/master) and add it to your web pages (you should usually use the most recent version).
Usage
-----
In general, the syntax of Handlebars.js templates is identical to Mustache templates. For basic syntax, check out the [Mustache manpage](http://mustache.github.com/mustache.5.html).
In general, the syntax of Handlebars.js templates is a superset of Mustache templates. For basic syntax, check out the [Mustache manpage](http://mustache.github.com/mustache.5.html).
Once you have a template, use the Handlebars.compile method to compile the template into a function. The generated function takes two arguments, a hash of data to apply to the template and an option hash of functions to use as helpers. Here's an example:
Once you have a template, use the Handlebars.compile method to compile the template into a function. The generated function takes a context argument, which will be used to render the template.
var source = "<p>Hello, my name is {{name}}. I am from {{hometown}}. I have " +
"{{kids/length}} kids:</p>" +
"<ul>{{#kids}}<li>{{name}} is {{age}}</li>{{/kids}}</ul>";
var template = Handlebars.compile(source);
var data = { "name": "Alan", "hometown": "Somewhere, TX",
"kids": [{"name": "Jimmy", "age": "12"}, {"name": "Sally", "age": "4"}]};
var result = template(data);
@@ -29,9 +29,45 @@ Once you have a template, use the Handlebars.compile method to compile the templ
// <li>Sally is 4</li>
// </ul>
Registering Helpers
-------------------
You can register helpers that Handlebars will use when evaluating your
template. Here's an example, which assumes that your objects have a URL
embedded in them, as well as the text for a link:
Handlebars.registerHelper('link_to', function(context) {
return "<a href='" + context.url + "'>" + context.body + "</a>";
});
var context = { posts: [{url: "/hello-world", body: "Hello World!"}] };
var source = "<ul>{{#posts}}<li>{{{link_to this}}}</li></ul>"
var template = Handlebars.compile(source);
template(context);
// Would render:
//
// <ul>
// <li><a href='/hello-world'>Hello World!</a></li>
// </ul>
Escaping
--------
By default, the `{{expression}}` syntax will escape its contents. This
helps to protect you against accidental XSS problems caused by malicious
data passed from the server as JSON.
To explicitly *not* escape the contents, use the triple-mustache
(`{{{}}}`). You have seen this used in the above example.
Differences Between Handlebars.js and Mustache
-------------------------
Handlebars.js adds a couple of additional features to make writing templates easier and also changes a tiny detail of how partials work.
----------------------------------------------
Handlebars.js adds a couple of additional features to make writing templates easier and also changes a tiny detail of how partials work.
### Paths
@@ -53,21 +89,46 @@ would render:
Alan - Rad, Inc.
### Strings
When calling a helper, you can pass paths or Strings as parameters. For
instance:
Handlebars.registerHelper('link_to', function(title, context) {
return "<a href='/posts" + context.id + "'>" + title + "</a>"
});
var context = { posts: [{url: "/hello-world", body: "Hello World!"}] };
var source = '<ul>{{#posts}}<li>{{{link_to "Post" this}}}</li></ul>'
var template = Handlebars.compile(source);
template(context);
// Would render:
//
// <ul>
// <li><a href='/hello-world'>Post!</a></li>
// </ul>
When you pass a String as a parameter to a helper, the literal String
gets passed to the helper function.
### Block Helpers
Handlebars.js also adds the ability to define block helpers. Block helpers are functions that can be called from anywhere in the template. Here's an example:
var source = "<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>";
var link = function(context, fn) {
var source = "<ul>{{#people}}<li>{{{#link}}}{{name}}{{/link}}</li>{{/people}}</ul>";
Handlebars.registerHelper('link', function(context, fn) {
return '<a href="/people/' + this.__get__("id") + '">' + fn(this) + '</a>';
};
});
var template = Handlebars.compile(source);
var data = { "people": [
{ "name": "Alan", "id": 1 },
{ "name": "Yehuda", "id": 2 }
]};
template(data, { "link": link });
template(data);
// Should render:
// <ul>
@@ -79,17 +140,22 @@ Whenever the block helper is called it is given two parameters, the argument tha
### Partials
To specify the set of available partials when rendering a template, set them to the partials key of the blocks hash. Partials can be either the string value of the partial source or a precompiled partial function. Here's an example:
You can register additional templates as partials, which will be used by
Handlebars when it encounters a partial (`{{> partialName}}`). Partials
can either be String templates or compiled template functions. Here's an
example:
var source = "<ul>{{#people}}<li>{{> link}}</li>{{/people}}</ul>";
var partials = { "link": '<a href="/people/{{id}}">{{name}}</a>' };
Handlebars.registerPartial('link', '<a href="/people/{{id}}">{{name}}</a>')
var template = Handlebars.compile(source);
var data = { "people": [
{ "name": "Alan", "id": 1 },
{ "name": "Yehuda", "id": 2 }
]};
template(data, { "partials": partials });
template(data);
// Should render:
// <ul>
@@ -100,17 +166,26 @@ To specify the set of available partials when rendering a template, set them to
Precompiling Templates
----------------------
A node.js compatible command-line tool is included in the lib folder. compiler.js takes arguments of the form MethodName=source.hbs and generates a source file with source templates compiled into methods with the given names.
node lib/compiler.js Template=templates/template.hbs Partial=templates/partial.hbs
TODO in the rewrite. This will use RubyRacer and not node.
Performance
-----------
In a rough performance test, precompiled Handlebars.js templates rendered in about half the time of Mustache templates. It would be a shame if it were any other way, since they were precompiled, but the difference in architecture does have some big performance advantages. Justin Marney, a.k.a. [gotascii](http://github.com/gotascii), confirmed that with an [independent test](http://sorescode.com/2010/09/12/benchmarks.html).
In a rough performance test, precompiled Handlebars.js templates (in the original version of Handlebars.js) rendered in about half the time of Mustache templates. It would be a shame if it were any other way, since they were precompiled, but the difference in architecture does have some big performance advantages. Justin Marney, a.k.a. [gotascii](http://github.com/gotascii), confirmed that with an [independent test](http://sorescode.com/2010/09/12/benchmarks.html). The rewritten Handlebars (current version) is faster than the old version, and we will have some benchmarks in the near future.
Building
--------
To build handlebars, just run `rake release`, and you will get two files
in the `dist` directory. The debug version comes with stack trace
annotations for webkit browsers, but will slightly increase startup
time.
Known Issues
------------
* Handlebars.js can be a bit cryptic when there's an error during compilation, and it can be even more cryptic when there's an error while rendering.
* Handlebars.js can be cryptic when there's an error while rendering.
Handlebars Contrib
------------------
+113
View File
@@ -0,0 +1,113 @@
require "rubygems"
require "bundler/setup"
file "lib/handlebars/parser.js" => ["src/handlebars.yy","src/handlebars.l"] do
if ENV['PATH'].split(':').any? {|folder| File.exists?(folder+'/jison')}
system "jison src/handlebars.yy src/handlebars.l"
sh "mv handlebars.js lib/handlebars/parser.js"
else
puts "Jison is not installed. Try running `npm install jison`."
end
end
task :compile => "lib/handlebars/parser.js"
desc "run the spec suite"
task :spec => [:release] do
system "rspec -cfs spec"
end
task :default => [:compile, :spec]
def remove_exports(string)
match = string.match(%r{^// BEGIN\(BROWSER\)\n(.*)\n^// END\(BROWSER\)}m)
match ? match[1] : string
end
minimal_deps = %w(parser base ast visitor utils vm).map do |file|
"lib/handlebars/#{file}.js"
end
base_deps = %w(parser base ast visitor runtime utils vm).map do |file|
"lib/handlebars/#{file}.js"
end
debug_deps = %w(parser base ast visitor printer runtime utils vm debug).map do |file|
"lib/handlebars/#{file}.js"
end
directory "dist"
minimal_deps.unshift "dist"
base_deps.unshift "dist"
debug_deps.unshift "dist"
def build_for_task(task)
FileUtils.rm_rf("dist/*") if File.directory?("dist")
FileUtils.mkdir_p("dist")
contents = []
task.prerequisites.each do |filename|
next if filename == "dist"
contents << "// #{filename}\n" + remove_exports(File.read(filename)) + ";"
end
File.open(task.name, "w") do |file|
file.puts contents.join("\n")
end
end
file "dist/handlebars.js" => minimal_deps do |task|
build_for_task(task)
end
file "dist/handlebars.base.js" => base_deps do |task|
build_for_task(task)
end
file "dist/handlebars.debug.js" => debug_deps do |task|
build_for_task(task)
end
task :build => [:compile, "dist/handlebars.js"]
task :base => [:compile, "dist/handlebars.base.js"]
task :debug => [:compile, "dist/handlebars.debug.js"]
desc "build the build, debug and base versions of handlebars"
task :release => [:build, :debug, :base]
directory "vendor"
desc "benchmark against dust.js and mustache.js"
task :bench => "vendor" do
require "open-uri"
File.open("vendor/mustache.js", "w") do |file|
file.puts open("https://github.com/janl/mustache.js/raw/master/mustache.js").read
file.puts "module.exports = Mustache;"
end
File.open("vendor/benchmark.js", "w") do |file|
file.puts open("https://github.com/mathiasbynens/benchmark.js/raw/master/benchmark.js").read
end
if File.directory?("vendor/dustjs")
system "cd vendor/dustjs && git pull"
else
system "git clone git://github.com/akdubya/dustjs.git vendor/dustjs"
end
if File.directory?("vendor/coffee")
system "cd vendor/coffee && git pull"
else
system "git clone git://github.com/jashkenas/coffee-script.git vendor/coffee"
end
if File.directory?("vendor/eco")
system "cd vendor/eco && git pull"
else
system "git clone git://github.com/sstephenson/eco.git vendor/eco"
end
system "node bench/handlebars.js"
end
+147
View File
@@ -0,0 +1,147 @@
var Benchmark = require("benchmark");
var BenchWarmer = function(names) {
this.benchmarks = [];
this.currentBenches = [];
this.names = [];
this.errors = {};
};
var print = require("sys").print;
BenchWarmer.prototype = {
winners: function(benches) {
var result = Benchmark.filter(benches, function(bench) { return bench.cycles; });
if (result.length > 1) {
result.sort(function(a, b) { return b.compare(a); });
first = result[0];
last = result[result.length - 1];
var winners = [];
Benchmark.each(result, function(bench) {
if (bench.compare(first) === 0) {
winners.push(bench);
}
});
return winners;
} else {
return result;
}
},
suite: function(suite, fn) {
this.suiteName = suite;
this.first = true;
var self = this;
fn(function(name, benchFn) {
self.push(name, benchFn);
});
},
push: function(name, fn) {
if(this.names.indexOf(name) == -1) {
this.names.push(name);
}
var first = this.first, suiteName = this.suiteName, self = this;
this.first = false;
var bench = new Benchmark(function() {
fn();
}, {
name: this.suiteName + ": " + name,
onComplete: function() {
if(first) { self.startLine(suiteName); }
self.writeBench(bench);
self.currentBenches.push(bench);
}, onError: function() {
self.errors[this.name] = this;
}
});
this.benchmarks.push(bench);
},
bench: function() {
var benchSize = 0, names = this.names, self = this, i, l;
for(i=0, l=names.length; i<l; i++) {
var name = names[i];
if(benchSize < name.length) { benchSize = name.length; }
}
this.nameSize = benchSize + 2;
this.benchSize = 20;
var horSize = 0;
this.startLine("ops/msec");
horSize = horSize + "ops/msec ".length;
for(i=0, l=names.length; i<l; i++) {
print(names[i] + new Array(this.benchSize - names[i].length + 1).join(" "));
horSize = horSize + this.benchSize;
}
print("WINNER(S)");
horSize = horSize + "WINNER(S)".length;
print("\n" + new Array(horSize + 1).join("-"));
Benchmark.invoke(this.benchmarks, {
name: "run",
onComplete: function() {
var errors = false, prop, bench;
for(prop in self.errors) { if(self.errors.hasOwnProperty(prop)) { errors = true; break; } }
if(errors) {
print("\n\nErrors:\n");
for(prop in self.errors) {
if(self.errors.hasOwnProperty(prop)) {
bench = self.errors[prop];
print("\n" + bench.name + ":\n");
print(bench.error.message);
if(bench.error.stack) {
print(bench.error.stack.join("\n"));
}
print("\n");
}
}
}
}
});
},
startLine: function(name) {
var winners = Benchmark.map(this.winners(this.currentBenches), function(bench) {
return bench.name.split(": ")[1];
});
this.currentBenches = [];
print(winners.join(", "));
print("\n");
var padding = this.nameSize - name.length + 1;
name = name + new Array(padding).join(" ");
print(name);
},
writeBench: function(bench) {
var out;
if(!bench.error) {
var count = bench.hz,
moe = count * bench.stats.RME / 100;
out = Math.round(count / 1000) + " ±" + Math.round(moe / 1000) + " (" + bench.cycles + ")";
} else {
out = "E";
}
var padding = this.benchSize - out.length + 1;
out = out + new Array(padding).join(" ");
print(out);
}
};
module.exports = BenchWarmer;
+166
View File
@@ -0,0 +1,166 @@
require.paths.push("lib");
require.paths.push("vendor");
require.paths.push("vendor/dustjs/lib");
require.paths.push("vendor/coffee/lib");
require.paths.push("vendor/eco/lib");
var BenchWarmer = require("./benchwarmer");
Handlebars = require("handlebars");
var dust = require("dust");
var Mustache = require("mustache");
var ecoExports = require("eco");
eco = function(str) {
var module = {};
var template = new Function("module", ecoExports.compile(str));
template(module);
return module.exports;
}
var benchDetails = {
string: {
context: {},
handlebars: "Hello world",
dust: "Hello world",
mustache: "Hello world",
eco: "Hello world"
},
variables: {
context: {name: "Mick", count: 30},
handlebars: "Hello {{name}}! You have {{count}} new messages.",
dust: "Hello {name}! You have {count} new messages.",
mustache: "Hello {{name}}! You have {{count}} new messages.",
eco: "Hello <%= @name %>! You have <%= @count %> new messages."
},
object: {
context: { person: { name: "Larry", age: 45 } },
handlebars: "{{#with person}}{{name}}{{age}}{{/with}}",
dust: "{#person}{name}{age}{/person}",
mustache: "{{#person}}{{name}}{{age}}{{/person}}"
},
array: {
context: { names: [{name: "Moe"}, {name: "Larry"}, {name: "Curly"}, {name: "Shemp"}] },
handlebars: "{{#each names}}{{name}}{{/each}}",
dust: "{#names}{name}{/names}",
mustache: "{{#names}}{{name}}{{/names}}",
eco: "<% for item in @names: %><%= item.name %><% end %>"
},
partial: {
context: { peeps: [{name: "Moe", count: 15}, {name: "Larry", count: 5}, {name: "Curly", count: 1}] },
partials: {
mustache: { variables: "Hello {{name}}! You have {{count}} new messages." },
handlebars: { variables: "Hello {{name}}! You have {{count}} new messages." }
},
handlebars: "{{#each peeps}}{{>variables}}{{/each}}",
dust: "{#peeps}{>variables/}{/peeps}",
mustache: "{{#peeps}}{{>variables}}{{/peeps}}"
},
recursion: {
context: { name: '1', kids: [{ name: '1.1', kids: [{name: '1.1.1', kids: []}] }] },
partials: {
mustache: { recursion: "{{name}}{{#kids}}{{>recursion}}{{/kids}}" },
handlebars: { recursion: "{{name}}{{#each kids}}{{>recursion}}{{/each}}" }
},
handlebars: "{{name}}{{#each kids}}{{>recursion}}{{/each}}",
dust: "{name}{#kids}{>recursion:./}{/kids}",
mustache: "{{name}}{{#kids}}{{>recursion}}{{/kids}}"
},
complex: {
handlebars: "<h1>{{header}}</h1>{{#if items}}<ul>{{#each items}}{{#if current}}" +
"<li><strong>{{name}}</strong></li>{{^}}" +
"<li><a href=\"{{url}}\">{{name}}</a></li>{{/if}}" +
"{{/each}}</ul>{{^}}<p>The list is empty.</p>{{/if}}",
dust: "<h1>{header}</h1>\n" +
"{?items}\n" +
" <ul>\n" +
" {#items}\n" +
" {#current}\n" +
" <li><strong>{name}</strong></li>\n" +
" {:else}\n" +
" <li><a href=\"{url}\">{name}</a></li>\n" +
" {/current}\n" +
" {/items}\n" +
" </ul>\n" +
"{:else}\n" +
" <p>The list is empty.</p>\n" +
"{/items}",
context: {
header: function() {
return "Colors";
},
items: [
{name: "red", current: true, url: "#Red"},
{name: "green", current: false, url: "#Green"},
{name: "blue", current: false, url: "#Blue"}
]
}
}
};
handlebarsTemplates = {};
ecoTemplates = {};
var warmer = new BenchWarmer();
var makeSuite = function(name) {
warmer.suite(name, function(bench) {
var templateName = name;
var details = benchDetails[templateName];
var mustachePartials = details.partials && details.partials.mustache;
var mustacheSource = details.mustache;
var context = details.context;
var error = function() { throw new Error("EWOT"); };
//bench("dust", function() {
//dust.render(templateName, context, function(err, out) { });
//});
bench("handlebars", function() {
handlebarsTemplates[templateName](context);
});
//if(ecoTemplates[templateName]) {
//bench("eco", function() {
//ecoTemplates[templateName](context);
//});
//} else {
//bench("eco", error);
//}
//if(mustacheSource) {
//bench("mustache", function() {
//Mustache.to_html(mustacheSource, context, mustachePartials);
//});
//} else {
//bench("mustache", error);
//}
});
}
for(var name in benchDetails) {
if(benchDetails.hasOwnProperty(name)) {
dust.loadSource(dust.compile(benchDetails[name].dust, name));
handlebarsTemplates[name] = Handlebars.compile(benchDetails[name].handlebars);
if(benchDetails[name].eco) { ecoTemplates[name] = eco(benchDetails[name].eco); }
var partials = benchDetails[name].partials;
if(partials) {
for(var partialName in partials.handlebars) {
if(partials.handlebars.hasOwnProperty(partialName)) {
Handlebars.registerPartial(partialName, partials.handlebars[partialName]);
}
}
}
makeSuite(name);
}
}
warmer.bench();
-19
View File
@@ -1,19 +0,0 @@
var handlebars = require('./handlebars');
var fs = require('fs');
if (process.argv.length < 3) {
console.log("handlebars.js compiler");
console.log("Tool for compiling handlebars.js template files into javascript functions.");
console.log("Usage: node compiler.js Function=template.hbs ... > templates.js");
console.log();
return;
}
process.argv.slice(2).forEach(function(val, index, array) {
var parts = val.split("=", 2);
var tmpl = fs.readFileSync(parts[1], 'utf8');
console.log(parts[0] + " = " + handlebars.compileToString(tmpl).toString());
});
+10 -540
View File
@@ -1,546 +1,16 @@
var Handlebars = {
compilerCache: {},
var Handlebars = require("handlebars/base");
module.exports = Handlebars;
compile: function(string) {
if (Handlebars.compilerCache[string] == null) {
var fnBody = Handlebars.compileFunctionBody(string);
var fn = new Function("context", "fallback", "stack", "Handlebars", fnBody);
Handlebars.compilerCache[string] =
function(context, fallback, stack) { return fn(context, fallback, stack, Handlebars); };
}
require("handlebars/utils");
return Handlebars.compilerCache[string];
},
require("handlebars/ast");
require("handlebars/printer");
require("handlebars/visitor");
compileToString: function(string) {
var fnBody = Handlebars.compileFunctionBody(string);
return "function(context, fallback, stack) { " + fnBody + "}";
},
require("handlebars/runtime");
require("handlebars/vm");
compileFunctionBody: function(string) {
var compiler = new Handlebars.Compiler(string);
compiler.compile();
// BEGIN(BROWSER)
return "var stack = stack || [];" + compiler.fn;
},
// END(BROWSER)
isFunction: function(fn) {
return Object.prototype.toString.call(fn) == "[object Function]";
},
trim: function(str) {
return str.replace(/^\s+|\s+$/g, '');
},
escapeText: function(string) {
string = string.replace(/'/g, "\\'");
string = string.replace(/\"/g, "\\\"");
return string;
},
escapeExpression: function(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof Handlebars.SafeString) {
return string.toString();
}
else if (string === null) {
string = "";
}
return string.toString().replace(/&(?!\w+;)|["\\<>]/g, function(str) {
switch(str) {
case "&":
return "&amp;";
case '"':
return "\"";
case "<":
return "&lt;";
case ">":
return "&gt;";
default:
return str;
}
});
},
compilePartial: function(partial) {
if (Handlebars.isFunction(partial)) {
compiled = partial;
} else {
compiled = Handlebars.compile(partial);
}
return compiled;
},
buildContext: function(context, stack) {
var ContextWrapper = function(stack) {
this.__context__ = context;
this.__stack__ = stack.slice(0);
this.__get__ = function(path) {
return this.__context__.evalExpression(path, this.__stack__).data;
};
};
ContextWrapper.prototype = context.data;
return new ContextWrapper(stack);
},
// spot to memoize paths to speed up loops and subsequent parses
pathPatterns: {},
// returns a two element array containing the numbers of contexts to back up the stack and
// the properties to dig into on the current context
//
// for example, if the path is "../../alan/name", the result will be [2, ["alan", "name"]].
parsePath: function(path) {
if (path == null) {
return [0, []];
} else if (Handlebars.pathPatterns["hbs-" + path] != null) {
return Handlebars.pathPatterns["hbs-" + path];
}
var parts = path.split("/");
var readDepth = false;
var depth = 0;
var dig = [];
for (var i = 0, j = parts.length; i < j; i++) {
switch(parts[i]) {
case "..":
if (readDepth) {
throw new Handlebars.Exception("Cannot jump out of context after moving into a context.");
} else {
depth += 1;
}
break;
case ".":
// do nothing - using .'s is pretty dumb, but it's also basically free for us to support
case "this":
// if we do nothing you'll end up sticking in the same context
break;
default:
readDepth = true;
dig.push(parts[i]);
}
}
var ret = [depth, dig];
Handlebars.pathPatterns["hbs" + path] = ret;
return ret;
},
isEmpty: function(value) {
if (typeof value === "undefined") {
return true;
} else if (value === null) {
return true;
} else if (value === false) {
return true;
} else if(Object.prototype.toString.call(value) === "[object Array]" && value.length == 0) {
return true;
} else {
return false;
}
},
// Escapes output and converts empty values to empty strings
filterOutput: function(value, escape) {
if (Handlebars.isEmpty(value)) {
return "";
} else if (escape) {
return Handlebars.escapeExpression(value);
} else {
return value;
}
},
handleBlock: function(lookup, context, arg, fn, notFn) {
var out = "", args;
originalArgs = arg.length ? arg : [null]
if (Handlebars.isFunction(lookup.data)) {
args = originalArgs.concat(fn);
out = out + lookup.data.apply(context, args);
if (notFn != null && Handlebars.isFunction(lookup.data.not)) {
args = originalArgs.concat(notFn);
out = out + lookup.data.not.apply(context, args);
}
}
else {
if (!Handlebars.isEmpty(lookup.data)) {
// TODO: which case is this, and what does it mean for multiple args
out = out + Handlebars.helperMissing.call(arg[0], lookup, fn);
}
if (notFn != null) {
out = out + Handlebars.helperMissing.not.call(arg[0], lookup, notFn);
}
}
return out;
},
// lookup: The evaluated mustache, either function or value. A Handlebars.Context
// context: The wrapped context object
// args: The evaluated arguments to the mustache
// isEscaped: Is it escaped?
handleExpression: function(lookup, context, args, isEscaped) {
var out = "";
if (Handlebars.isFunction(lookup.data)) {
out = out + Handlebars.filterOutput(lookup.data.apply(context, args), isEscaped);
} else if(!Handlebars.isEmpty(lookup.data)) {
out = out + Handlebars.filterOutput(lookup.data, isEscaped);
}
return out;
},
// lookup: The evaluated mustache, either function or value. A Handlebars.Context
// context: The context where this item occurred
// fn: The compiled body of this inverted section
handleInvertedSection: function(lookup, context, fn) {
var out = "";
if(Handlebars.isFunction(lookup.data) && Handlebars.isEmpty(lookup.data())) {
out = out + fn(context);
} else if (Handlebars.isEmpty(lookup.data)) {
out = out + fn(context);
}
return out;
}
}
Handlebars.Context = function(context, fallback, path) {
if (context instanceof Handlebars.Context) {
this.data = context.data;
this.fallback = context.fallback;
this.path = context.path;
} else {
this.data = context;
this.fallback = fallback || {};
this.path = path || "";
}
};
Handlebars.Context.prototype = {
// path: The path to evaluate
// stack: The stack to work with
evalExpression: function(path, stack) {
var newContext = new Handlebars.Context(this);
var parsedPath = Handlebars.parsePath(path);
var depth = parsedPath[0];
var parts = parsedPath[1];
if (depth > stack.length) {
newContext.data = null;
} else if (depth > 0) {
newContext = new Handlebars.Context(stack[stack.length - depth]);
}
for (var i = 0, j = parts.length; i < j && typeof newContext.data !== "undefined" && newContext.data !== null ; i++) {
newContext.data = newContext.data[parts[i]];
}
if (parts.length == 1 && typeof newContext.data === "undefined") {
newContext.data = newContext.fallback[parts[0]];
}
return newContext;
}
};
Handlebars.Compiler = function(string) {
this.string = string;
this.pointer = -1;
this.mustache = false;
this.text = "";
this.fn = "context = new Handlebars.Context(context, fallback); var out = ''; var lookup; ";
this.newlines = "";
this.comment = false;
this.escaped = true;
this.partial = false;
this.inverted = false;
this.endCondition = null;
this.continueInverted = false;
};
Handlebars.Exception = function(message) {
this.message = message;
};
// Build out our basic SafeString type
Handlebars.SafeString = function(string) {
this.string = string;
}
Handlebars.SafeString.prototype.toString = function() {
return this.string.toString();
}
Handlebars.helperMissing = function(object, fn) {
var ret = "";
if(object.data === true) {
return fn(this);
} else if(object.data === false) {
return "";
} else if(Object.prototype.toString.call(object.data) === "[object Array]") {
for(var i=0, j=object.data.length; i<j; i++) {
ret = ret + fn(object.data[i]);
}
return ret;
} else {
return fn(object.data);
}
};
Handlebars.helperMissing.not = function(context, fn) {
return fn(context);
}
Handlebars.Compiler.prototype = {
getChar: function(n) {
var ret = this.peek(n);
this.pointer = this.pointer + (n || 1);
return ret;
},
peek: function(n) {
n = n || 1;
var start = this.pointer + 1;
return this.string.slice(start, start + n);
},
compile: function(endCondition) {
// if we're at the end condition already then we don't have to do any work!
if (!endCondition || !endCondition(this)) {
var chr;
while(chr = this.getChar()) {
if(chr === "{" && this.peek() === "{" && !this.mustache) {
this.getChar();
this.parseMustache();
} else {
if(chr === "\n") {
this.newlines = this.newlines + "\n";
chr = "\\n";
} else if (chr === "\r") {
this.newlines = this.newlines + "\r";
chr = "\\r";
} else if (chr === "\\") {
chr = "\\\\";
}
this.text = this.text + chr;
}
if (endCondition && this.peek(5) == "{{^}}") {
this.continueInverted = true;
this.getChar(5);
break;
}
else if(endCondition && endCondition(this)) { break };
}
}
this.addText();
this.fn += "return out;";
return;
},
addText: function() {
if(this.text) {
this.fn = this.fn + "out = out + \"" + Handlebars.escapeText(this.text) + "\"; ";
this.fn = this.fn + this.newlines;
this.newlines = "";
this.text = "";
}
},
addExpression: function(mustache, params) {
if(!params[0]) params = ["null"]
params = params.join(", ")
var expr = this.lookupFor(mustache);
this.fn += "var proxy = Handlebars.buildContext(context, stack);"
this.fn += "out = out + Handlebars.handleExpression(" + expr + ", proxy, [" + params + "], " + this.escaped + ");";
},
addInvertedSection: function(mustache) {
var compiler = this.compileToEndOfBlock(mustache);
var result = compiler.fn;
// each function made internally needs a unique IDs. These are locals, so they
// don't need to be globally unique, just per compiler
var fnId = "fn" + this.pointer.toString();
this.fn += "var " + fnId + " = function(context) {" + result + "}; ";
this.fn += "lookup = " + this.lookupFor(mustache) + "; ";
this.fn += "out = out + Handlebars.handleInvertedSection(lookup, context, " + fnId + ");"
this.openBlock = false;
this.inverted = false;
},
lookupFor: function(param) {
if (typeof param === "undefined") {
return "context";
}
else {
return "(context.evalExpression('" + param + "', stack))";
}
},
compileToEndOfBlock: function(mustache) {
var compiler = new Handlebars.Compiler(this.string.slice(this.pointer + 1));
// sub-compile with a custom EOF instruction
compiler.compile(function(compiler) {
if (compiler.peek(3) === "{{/") {
if(compiler.peek(mustache.length + 5) === "{{/" + mustache + "}}") {
compiler.getChar(mustache.length + 5);
return true;
} else {
throw new Handlebars.Exception("Mismatched block close: expected " + mustache + ".");
}
}
});
// move the pointer forward the amount of characters handled by the sub-compiler
this.pointer += compiler.pointer + 1;
return compiler;
},
addBlock: function(mustache, params) {
var compiler = this.compileToEndOfBlock(mustache);
var result = compiler.fn;
// each function made internally needs a unique IDs. These are locals, so they
// don't need to be globally unique, just per compiler
var fnId = "fn" + this.pointer.toString();
this.fn += "var wrappedContext = Handlebars.buildContext(context, stack);";
this.fn += "var " + fnId + " = function(context) {" + result + "}; ";
this.fn += "lookup = " + this.lookupFor(mustache) + "; ";
this.fn += "arg = [" + params.join(", ") + "] ;";
this.fn += "stack.push(context);";
if (compiler.continueInverted) {
var invertedCompiler = this.compileToEndOfBlock(mustache);
this.fn += " var " + fnId + "Not = function(context) { " + invertedCompiler.fn + " };";
}
else {
this.fn += " var " + fnId + "Not = null;";
}
this.fn += "out = out + Handlebars.handleBlock(lookup, wrappedContext, arg, " + fnId + ", " + fnId + "Not);"
this.fn += "stack.pop();";
this.openBlock = false;
},
addPartial: function(mustache, param) {
// either used a cached copy of the partial or compile a new one
this.fn += "if (typeof context.fallback['partials'] === 'undefined' || typeof context.fallback['partials']['" + mustache + "'] === 'undefined') throw new Handlebars.Exception('Attempted to render undefined partial: " + mustache + "');";
this.fn += "out = out + Handlebars.compilePartial(context.fallback['partials']['" + mustache + "'])(" + param + ", null, stack);";
},
parseMustache: function() {
var chr, part, mustache, param;
var next = this.peek();
if(next === "!") {
this.comment = true;
this.getChar();
} else if(next === "#") {
this.openBlock = true;
this.getChar();
} else if (next === ">") {
this.partial = true;
this.getChar();
} else if (next === "^") {
this.inverted = true;
this.openBlock = true;
this.getChar();
} else if(next === "{" || next === "&") {
this.escaped = false;
this.getChar();
}
this.addText();
var params = [""], currentParam = 0, literals = [];
while(chr = this.getChar()) {
if(this.stringLiteral) {
params[currentParam] += chr;
if(chr === "\\" && this.peek() === '"') {
params[currentParam] += '"';
this.getChar();
} else if(chr === '"') {
params[++currentParam] = ""
this.stringLiteral = false;
}
} else if(chr === '"') {
if(params[currentParam] !== "") {
throw new Handlebars.Exception("You are already in the middle of" +
"the " + params[currentParam] + " param. " +
"You cannot start a String param")
}
this.stringLiteral = true;
params[currentParam] = chr;
literals[currentParam] = true;
} else if(chr === " ") {
if(params[currentParam] !== "") params[++currentParam] = ""
} else if(chr === "}" && this.peek() === "}") {
mustache = params[0];
arguments = [];
if(!params[1]) params[1] = undefined;
for(var i=1,l=params.length; i<l; i++) {
var argument = params[i];
arguments.push( literals[i] ? argument : "(" + this.lookupFor(argument) + ".data)");
}
this.mustache = false;
// finish reading off the close of the handlebars
this.getChar();
// {{{expression}} is techically valid, but if we started with {{{ we'll try to read
// }}} off of the close of the handlebars
if (!this.escaped && this.peek() === "}") {
this.getChar();
}
if(this.comment) {
this.comment = false;
return;
} else if (this.partial) {
this.addPartial(mustache, "(" + this.lookupFor(argument) + ")");
this.partial = false;
return;
} else if (this.inverted) {
this.addInvertedSection(mustache);
this.inverted = false;
return;
} else if(this.openBlock) {
this.addBlock(mustache, arguments)
return;
} else {
return this.addExpression(mustache, arguments);
}
this.escaped = true;
} else if(this.comment) {
;
} else {
params[currentParam] += chr;
}
}
}
};
// CommonJS Exports
var exports = exports || {};
exports['compile'] = Handlebars.compile;
exports['compileToString'] = Handlebars.compileToString;
+86
View File
@@ -0,0 +1,86 @@
var Handlebars = require("handlebars");
// BEGIN(BROWSER)
(function() {
Handlebars.AST = {};
Handlebars.AST.ProgramNode = function(statements, inverse) {
this.type = "program";
this.statements = statements;
if(inverse) { this.inverse = new Handlebars.AST.ProgramNode(inverse); }
};
Handlebars.AST.MustacheNode = function(params, unescaped) {
this.type = "mustache";
this.id = params[0];
this.params = params.slice(1);
this.escaped = !unescaped;
};
Handlebars.AST.PartialNode = function(id, context) {
this.type = "partial";
// TODO: disallow complex IDs
this.id = id;
this.context = context;
};
var verifyMatch = function(open, close) {
if(open.original !== close.original) {
throw new Handlebars.Exception(open.original + " doesn't match " + close.original);
}
};
Handlebars.AST.BlockNode = function(mustache, program, close) {
verifyMatch(mustache.id, close);
this.type = "block";
this.mustache = mustache;
this.program = program;
};
Handlebars.AST.InverseNode = function(mustache, program, close) {
verifyMatch(mustache.id, close);
this.type = "inverse";
this.mustache = mustache;
this.program = program;
};
Handlebars.AST.ContentNode = function(string) {
this.type = "content";
this.string = string;
};
Handlebars.AST.IdNode = function(parts) {
this.type = "ID";
this.original = parts.join("/");
var dig = [], depth = 0;
for(var i=0,l=parts.length; i<l; i++) {
var part = parts[i];
if(part === "..") { depth++; }
else if(part === "." || part === "this") { continue; }
else { dig.push(part); }
}
this.parts = dig;
this.depth = depth;
this.isSimple = (dig.length === 1) && (depth === 0);
};
Handlebars.AST.StringNode = function(string) {
this.type = "STRING";
this.string = string;
};
Handlebars.AST.CommentNode = function(comment) {
this.type = "comment";
this.comment = comment;
};
})();
// END(BROWSER)
+116
View File
@@ -0,0 +1,116 @@
var handlebars = require("handlebars/parser").parser;
// BEGIN(BROWSER)
var Handlebars = {};
Handlebars.Parser = handlebars;
Handlebars.parse = function(string) {
Handlebars.Parser.yy = Handlebars.AST;
return Handlebars.Parser.parse(string);
};
Handlebars.print = function(ast) {
return new Handlebars.PrintVisitor().accept(ast);
};
Handlebars.Runtime = {};
Handlebars.Runtime.compile = function(string) {
var ast = Handlebars.parse(string);
return function(context, helpers, partials) {
helpers = helpers || Handlebars.helpers;
partials = partials || Handlebars.partials;
var internalContext = new Handlebars.Context(context, helpers, partials);
var runtime = new Handlebars.Runtime(internalContext);
runtime.accept(ast);
return runtime.buffer;
};
};
Handlebars.helpers = {};
Handlebars.partials = {};
Handlebars.registerHelper = function(name, fn, inverse) {
if(inverse) { fn.not = inverse; }
this.helpers[name] = fn;
};
Handlebars.registerPartial = function(name, str) {
this.partials[name] = str;
};
Handlebars.registerHelper('blockHelperMissing', function(context, fn, inverse) {
inverse = inverse || function() {};
var ret = "";
var type = Object.prototype.toString.call(context);
if(type === "[object Function]") {
context = context();
}
if(context === true) {
return fn(this);
} else if(context === false || context == null) {
return inverse(this);
} else if(type === "[object Array]") {
if(context.length > 0) {
for(var i=0, j=context.length; i<j; i++) {
ret = ret + fn(context[i]);
}
} else {
ret = inverse(this);
}
return ret;
} else {
return fn(context);
}
}, function(context, fn) {
return fn(context);
});
Handlebars.registerHelper('each', function(context, fn, inverse) {
var ret = "";
if(context && context.length > 0) {
for(var i=0, j=context.length; i<j; i++) {
ret = ret + fn(context[i]);
}
} else {
ret = inverse(this);
}
return ret;
});
Handlebars.registerHelper('if', function(context, fn, inverse) {
if(!context || context == []) {
return inverse(this);
} else {
return fn(this);
}
});
Handlebars.registerHelper('unless', function(context, fn, inverse) {
Handlebars.helpers['if'].call(this, context, inverse, fn);
});
Handlebars.registerHelper('with', function(context, fn) {
return fn(context);
});
Handlebars.logger = {
DEBUG: 0, INFO: 1, WARN: 2, ERROR: 3, level: 3,
// override in the host environment
log: function(level, str) {}
};
Handlebars.log = function(level, str) { Handlebars.logger.log(level, str); };
// END(BROWSER)
module.exports = Handlebars;
+29
View File
@@ -0,0 +1,29 @@
var Handlebars = require("handlebars");
// BEGIN(BROWSER)
(function() {
var classes = ["Lexer", "PrintVisitor", "Context", "Runtime", "Exception"];
var prop;
for(var i=0, l=classes.length; i<l; i++) {
var className = classes[i], klass = Handlebars[className];
klass.displayName = "new Handlebars." + className;
for(prop in klass) {
if(klass.hasOwnProperty(prop)) {
klass[prop].displayName = "Handlebars." + className + "#" + prop;
}
}
}
for(prop in Handlebars.Utils) {
if(Handlebars.Utils.hasOwnProperty(prop)) {
Handlebars.Utils[prop].displayName = "Handlebars.Utils." + prop;
}
}
Handlebars.parse.displayName = "Handlebars.parse";
Handlebars.print.displayName = "Handlebars.print";
Handlebars.compile.displayName = "Handlebars.compile";
})();
// END(BROWSER)
+114
View File
@@ -0,0 +1,114 @@
var Handlebars = require("handlebars");
require("handlebars/visitor");
// BEGIN(BROWSER)
Handlebars.PrintVisitor = function() { this.padding = 0; };
Handlebars.PrintVisitor.prototype = new Handlebars.Visitor();
Handlebars.PrintVisitor.prototype.pad = function(string, newline) {
var out = "";
for(var i=0,l=this.padding; i<l; i++) {
out = out + " ";
}
out = out + string;
if(newline !== false) { out = out + "\n"; }
return out;
};
Handlebars.PrintVisitor.prototype.program = function(program) {
var out = this.pad("PROGRAM:"),
statements = program.statements,
inverse = program.inverse,
i, l;
this.padding++;
for(i=0, l=statements.length; i<l; i++) {
out = out + this.accept(statements[i]);
}
this.padding--;
if(inverse) {
out = out + this.pad("{{^}}");
this.padding++;
for(i=0, l=inverse.statements.length; i<l; i++) {
out = out + this.accept(inverse.statements[i]);
}
}
this.padding--;
return out;
};
Handlebars.PrintVisitor.prototype.block = function(block) {
var out = "";
out = out + this.pad("BLOCK:");
this.padding++;
out = out + this.accept(block.mustache);
out = out + this.accept(block.program);
this.padding--;
return out;
};
Handlebars.PrintVisitor.prototype.inverse = function(block) {
var out = "";
out = out + this.pad("INVERSE:");
this.padding++;
out = out + this.accept(block.mustache);
out = out + this.accept(block.program);
this.padding--;
return out;
};
Handlebars.PrintVisitor.prototype.mustache = function(mustache) {
var params = mustache.params, paramStrings = [];
for(var i=0, l=params.length; i<l; i++) {
paramStrings.push(this.accept(params[i]));
}
params = "[" + paramStrings.join(", ") + "]";
return this.pad("{{ " + this.accept(mustache.id) + " " + params + " }}");
};
Handlebars.PrintVisitor.prototype.partial = function(partial) {
var content = this.accept(partial.id);
if(partial.context) { content = content + " " + this.accept(partial.context); }
return this.pad("{{> " + content + " }}");
};
Handlebars.PrintVisitor.prototype.STRING = function(string) {
return '"' + string.string + '"';
};
Handlebars.PrintVisitor.prototype.ID = function(id) {
var path = id.parts.join("/");
if(id.parts.length > 1) {
return "PATH:" + path;
} else {
return "ID:" + path;
}
};
Handlebars.PrintVisitor.prototype.content = function(content) {
return this.pad("CONTENT[ '" + content.string + "' ]");
};
Handlebars.PrintVisitor.prototype.comment = function(comment) {
return this.pad("{{! '" + comment.comment + "' }}");
};
// END(BROWSER)
exports.PrintVisitor = Handlebars.PrintVisitor;
+267
View File
@@ -0,0 +1,267 @@
var inspect = function(obj) {
require("sys").print(require("sys").inspect(obj) + "\n");
};
var Handlebars = require("handlebars");
// BEGIN(BROWSER)
// A Context wraps data, and makes it possible to extract a
// new Context given a path. For instance, if the data
// is { person: { name: "Alan" } }, a Context wrapping
// "Alan" can be extracted by searching for "person/name"
Handlebars.Context = function(data, helpers, partials) {
this.data = data;
this.helpers = helpers || {};
this.partials = partials || {};
};
Handlebars.Context.prototype = {
isContext: true,
// Make a shallow copy of the Context
clone: function() {
return new Handlebars.Context(this.data, this.helpers, this.partials);
},
// Search for an object inside the Context's data. The
// path parameter is an object with parts
// ("person/name" represented as ["person", "name"]),
// and depth (the amount of levels to go up the stack,
// originally represented as ..). The stack parameter
// is the objects already searched from the root of
// the original Context in order to get to this point.
//
// Return a new Context wrapping the data found in
// the search.
evaluate: function(path, stack) {
var context = this.clone();
var depth = path.depth, parts = path.parts;
if(depth > stack.length) { context.data = null; }
else if(depth > 0) { context = stack[stack.length - depth].clone(); }
for(var i=0,l=parts.length; i<l && context.data != null; i++) {
context.data = context.data[parts[i]];
}
if(context.data !== undefined) { return context; }
if(parts.length === 1 && context.data === undefined) {
context.data = context.helpers[parts[0]];
}
return context;
}
};
Handlebars.K = function() { return this; };
Handlebars.proxy = function(obj) {
var Proxy = this.K;
Proxy.prototype = obj;
return new Proxy();
};
Handlebars.Runtime = function(context, stack) {
this.stack = stack || [];
this.buffer = "";
this.context = context;
};
Handlebars.Runtime.prototype = {
accept: Handlebars.Visitor.prototype.accept,
ID: function(path) {
return this.context.evaluate(path, this.stack);
},
STRING: function(string) {
return { data: string.string };
},
program: function(program) {
var statements = program.statements;
for(var i=0, l=statements.length; i<l; i++) {
var statement = statements[i];
this[statement.type](statement);
}
return this.buffer;
},
mustache: function(mustache) {
var idObj = this.ID(mustache.id);
var params = mustache.params.slice(0);
var buf;
for(var i=0, l=params.length; i<l; i++) {
var param = params[i];
params[i] = this[param.type](param).data;
}
var data = idObj.data;
var type = Object.prototype.toString.call(data);
var functionType = (type === "[object Function]");
if(!functionType && params.length) {
params = params.slice(0);
params.unshift(data || mustache.id.original);
data = this.context.helpers.helperMissing;
functionType = true;
}
if(functionType) {
buf = data.apply(this.wrapContext(), params);
} else {
buf = data;
}
if(buf && mustache.escaped) { buf = Handlebars.Utils.escapeExpression(buf); }
this.buffer = this.buffer + ((!buf && buf !== 0) ? '' : buf);
},
block: function(block) {
var mustache = block.mustache, data;
var id = mustache.id,
idObj = this.ID(mustache.id),
data = idObj.data;
var result;
if(typeof data === "function") {
params = this.evaluateParams(mustache.params);
} else {
params = [data];
data = this.context.helpers.blockHelperMissing;
}
params.push(this.wrapProgram(block.program));
result = data.apply(this.wrapContext(), params);
this.buffer = this.buffer + ((result === undefined) ? "" : result);
if(block.program.inverse) {
params.pop();
params.push(this.wrapProgram(block.program.inverse));
result = data.not ? data.not.apply(this.wrapContext(), params) : "";
this.buffer = this.buffer + result;
}
},
partial: function(partial) {
var partials = this.context.partials || {};
var id = partial.id.original;
var partialBody = partials[partial.id.original];
var program, context;
if(!partialBody) {
throw new Handlebars.Exception("The partial " + partial.id.original + " does not exist");
}
if(typeof partialBody === "string") {
program = Handlebars.parse(partialBody);
partials[id] = program;
} else {
program = partialBody;
}
if(partial.context) {
context = this.ID(partial.context);
} else {
context = this.context;
}
var runtime = new Handlebars.Runtime(context, this.stack);
this.buffer = this.buffer + runtime.program(program);
},
not: function(context, fn) {
return fn(context);
},
// TODO: Write down the actual spec for inverse sections...
inverse: function(block) {
var mustache = block.mustache,
id = mustache.id,
not;
var idObj = this.ID(id),
data = idObj.data,
isInverse = Handlebars.Utils.isEmpty(data);
var context = this.wrapContext();
if(Object.prototype.toString.call(data) === "[object Function]") {
params = this.evaluateParams(mustache.params);
id = id.parts.join("/");
data = data.apply(context, params);
if(Handlebars.Utils.isEmpty(data)) { isInverse = true; }
if(data.not) { not = data.not; } else { not = this.not; }
} else {
not = this.not;
}
var result = not(context, this.wrapProgram(block.program));
if(result != null) { this.buffer = this.buffer + result; }
return;
},
content: function(content) {
this.buffer += content.string;
},
comment: function() {},
evaluateParams: function(params) {
var ret = [];
for(var i=0, l=params.length; i<l; i++) {
var param = params[i];
ret[i] = this[param.type](param).data;
}
if(ret.length === 0) { ret = [this.wrapContext()]; }
return ret;
},
wrapContext: function() {
var data = this.context.data;
var proxy = Handlebars.proxy(data);
var context = proxy.__context__ = this.context;
var stack = proxy.__stack__ = this.stack.slice(0);
proxy.__get__ = function(path) {
path = new Handlebars.AST.IdNode(path.split("/"));
return context.evaluate(path, stack).data;
};
proxy.isWrappedContext = true;
proxy.__data__ = data;
return proxy;
},
wrapProgram: function(program) {
var currentContext = this.context;
var stack = this.stack.slice(0);
return function(context) {
if(context && context.isWrappedContext) { context = context.__data__; }
stack.push(currentContext);
var newContext = new Handlebars.Context(context, currentContext.helpers, currentContext.partials);
var runtime = new Handlebars.Runtime(newContext, stack);
runtime.program(program);
return runtime.buffer;
};
}
};
// END(BROWSER)
+58
View File
@@ -0,0 +1,58 @@
var Handlebars = require("handlebars");
// BEGIN(BROWSER)
Handlebars.Exception = function(message) {
this.message = message;
};
// Build out our basic SafeString type
Handlebars.SafeString = function(string) {
this.string = string;
};
Handlebars.SafeString.prototype.toString = function() {
return this.string.toString();
};
(function() {
var escape = {
"<": "&lt;",
">": "&gt;"
};
var badChars = /&(?!\w+;)|[<>]/g;
var possible = /[&<>]/
var escapeChar = function(chr) {
return escape[chr] || "&amp;"
};
Handlebars.Utils = {
escapeExpression: function(string) {
// don't escape SafeStrings, since they're already safe
if (string instanceof Handlebars.SafeString) {
return string.toString();
} else if (string == null || string === false) {
return "";
}
if(!possible.test(string)) { return string; }
return string.replace(badChars, escapeChar);
},
isEmpty: function(value) {
if (typeof value === "undefined") {
return true;
} else if (value === null) {
return true;
} else if (value === false) {
return true;
} else if(Object.prototype.toString.call(value) === "[object Array]" && value.length === 0) {
return true;
} else {
return false;
}
}
};
})();
// END(BROWSER)
+13
View File
@@ -0,0 +1,13 @@
var Handlebars = require("handlebars");
// BEGIN(BROWSER)
Handlebars.Visitor = function() {};
Handlebars.Visitor.prototype = {
accept: function(object) {
return this[object.type](object);
}
};
// END(BROWSER)
+659
View File
@@ -0,0 +1,659 @@
var Handlebars = require("handlebars");
// BEGIN(BROWSER)
Handlebars.Compiler = function() {};
Handlebars.JavaScriptCompiler = function() {};
(function(Compiler, JavaScriptCompiler) {
Compiler.OPCODE_MAP = {
appendContent: 1,
getContext: 2,
lookupWithFallback: 3,
lookup: 4,
append: 5,
invokeMustache: 6,
appendEscaped: 7,
pushString: 8,
truthyOrFallback: 9,
functionOrFallback: 10,
invokeProgram: 11,
invokePartial: 12,
push: 13,
invokeInverse: 14
};
Compiler.MULTI_PARAM_OPCODES = {
appendContent: 1,
getContext: 1,
lookupWithFallback: 1,
lookup: 1,
invokeMustache: 2,
pushString: 1,
truthyOrFallback: 1,
functionOrFallback: 1,
invokeProgram: 2,
invokePartial: 1,
push: 1,
invokeInverse: 1
};
Compiler.DISASSEMBLE_MAP = {};
for(var prop in Compiler.OPCODE_MAP) {
var value = Compiler.OPCODE_MAP[prop];
Compiler.DISASSEMBLE_MAP[value] = prop;
}
Compiler.multiParamSize = function(code) {
return Compiler.MULTI_PARAM_OPCODES[Compiler.DISASSEMBLE_MAP[code]];
};
Compiler.prototype = {
disassemble: function() {
var opcodes = this.opcodes, opcode, nextCode;
var out = [], str, name, value;
for(var i=0, l=opcodes.length; i<l; i++) {
opcode = opcodes[i];
if(opcode === 'DECLARE') {
name = opcodes[++i];
value = opcodes[++i];
out.push("DECLARE " + name + " = " + value);
} else {
str = Compiler.DISASSEMBLE_MAP[opcode];
var extraParams = Compiler.multiParamSize(opcode);
var codes = [];
for(var j=0; j<extraParams; j++) {
nextCode = opcodes[++i];
if(typeof nextCode === "string") {
nextCode = "\"" + nextCode.replace("\n", "\\n") + "\"";
}
codes.push(nextCode);
}
str = str + " " + codes.join(" ");
out.push(str);
}
}
return out.join("\n");
},
guid: 0,
compile: function(program) {
this.children = [];
this.depths = {list: []};
return this.program(program);
},
accept: function(node) {
return this[node.type](node);
},
program: function(program) {
var statements = program.statements, statement;
this.opcodes = [];
for(var i=0, l=statements.length; i<l; i++) {
statement = statements[i];
this[statement.type](statement);
}
this.depths.list = this.depths.list.sort(function(a, b) {
return a - b;
});
return this;
},
compileProgram: function(program) {
var result = new Compiler().compile(program);
var guid = this.guid++;
this.usePartial = this.usePartial || result.usePartial;
this.children[guid] = result;
for(var i=0, l=result.depths.list.length; i<l; i++) {
depth = result.depths.list[i];
if(depth < 2) { continue; }
else { this.addDepth(depth - 1); }
}
return guid;
},
block: function(block) {
var mustache = block.mustache;
var params = mustache.params, depth, child, inverse, inverseGuid;
this.pushParams(params);
// ID lookup is now on the stack
this.ID(mustache.id);
var programGuid = this.compileProgram(block.program);
if(block.program.inverse) {
inverseGuid = this.compileProgram(block.program.inverse);
}
if(block.program.inverse) {
this.declare('inverse', inverseGuid);
}
this.opcode('invokeProgram', programGuid, params.length);
this.declare('inverse', null);
this.opcode('append');
},
inverse: function(block) {
this.ID(block.mustache.id);
var programGuid = this.compileProgram(block.program);
this.opcode('invokeInverse', programGuid);
this.opcode('append');
},
partial: function(partial) {
var id = partial.id;
this.usePartial = true;
if(partial.context) {
this.ID(partial.context);
} else {
this.opcode('push', 'context');
}
this.opcode('invokePartial', id.original);
this.opcode('append');
},
content: function(content) {
this.opcode('appendContent', content.string);
},
mustache: function(mustache) {
var params = mustache.params;
this.pushParams(params);
this.ID(mustache.id);
this.opcode('invokeMustache', params.length, mustache.id.original);
if(mustache.escaped) {
this.opcode('appendEscaped');
} else {
this.opcode('append');
}
},
ID: function(id) {
this.addDepth(id.depth);
this.opcode('getContext', id.depth);
this.opcode('lookupWithFallback', id.parts[0] || null);
for(var i=1, l=id.parts.length; i<l; i++) {
this.opcode('lookup', id.parts[i]);
}
},
STRING: function(string) {
this.opcode('pushString', string.string);
},
comment: function() {},
pushParams: function(params) {
var i = params.length, param;
while(i--) {
param = params[i];
this[param.type](param);
}
},
opcode: function(name, val1, val2) {
this.opcodes.push(Compiler.OPCODE_MAP[name]);
if(val1 !== undefined) { this.opcodes.push(val1); }
if(val2 !== undefined) { this.opcodes.push(val2); }
},
declare: function(name, value) {
this.opcodes.push('DECLARE');
this.opcodes.push(name);
this.opcodes.push(value);
},
addDepth: function(depth) {
if(depth === 0) { return; }
if(!this.depths[depth]) {
this.depths[depth] = true;
this.depths.list.push(depth);
}
}
};
JavaScriptCompiler.prototype = {
// PUBLIC API: You can override these methods in a subclass to provide
// alternative compiled forms for name lookup and buffering semantics
nameLookup: function(parent, name, type) {
if(JavaScriptCompiler.RESERVED_WORDS[name]) {
return parent + "['" + name + "']";
} else {
return parent + "." + name;
}
},
appendToBuffer: function(string) {
return "buffer = buffer + " + string + ";";
},
initializeBuffer: function() {
return this.quotedString("");
},
// END PUBLIC API
compile: function(environment, data) {
this.environment = environment;
this.data = data;
this.preamble();
this.stackSlot = 0;
this.stackVars = [];
this.registers = {list: []};
this.compileChildren(environment, data);
Handlebars.log(Handlebars.logger.DEBUG, environment.disassemble() + "\n\n");
var opcodes = environment.opcodes, opcode, name, declareName, declareVal;
this.i = 0;
for(l=opcodes.length; this.i<l; this.i++) {
opcode = this.nextOpcode(0);
if(opcode[0] === 'DECLARE') {
this.i = this.i + 2;
this[opcode[1]] = opcode[2];
} else {
this.i = this.i + opcode[1].length;
this[opcode[0]].apply(this, opcode[1]);
}
}
return this.createFunction();
},
nextOpcode: function(n) {
var opcodes = this.environment.opcodes, opcode = opcodes[this.i + n], name, val;
var extraParams, codes;
if(opcode === 'DECLARE') {
name = opcodes[this.i + 1];
val = opcodes[this.i + 2];
return ['DECLARE', name, val];
} else {
name = Compiler.DISASSEMBLE_MAP[opcode];
extraParams = Compiler.multiParamSize(opcode);
codes = [];
for(var j=0; j<extraParams; j++) {
codes.push(opcodes[this.i + j + 1 + n]);
}
return [name, codes];
}
},
eat: function(opcode) {
this.i = this.i + opcode.length;
},
preamble: function() {
var out = [];
out.push("var buffer = " + this.initializeBuffer() + ", currentContext = context");
var copies = "helpers = helpers || Handlebars.helpers;";
if(this.environment.usePartial) { copies = copies + " partials = partials || Handlebars.partials;"; }
out.push(copies);
// track the last context pushed into place to allow skipping the
// getContext opcode when it would be a noop
this.lastContext = 0;
this.source = out;
},
createFunction: function() {
var container = {
escapeExpression: Handlebars.Utils.escapeExpression,
invokePartial: Handlebars.VM.invokePartial,
programs: [],
program: function(i, helpers, partials, data) {
var programWrapper = this.programs[i];
if(data) {
return Handlebars.VM.program(this.children[i], helpers, partials, data);
} else if(programWrapper) {
return programWrapper;
} else {
programWrapper = this.programs[i] = Handlebars.VM.program(this.children[i], helpers, partials);
return programWrapper;
}
},
programWithDepth: Handlebars.VM.programWithDepth,
noop: Handlebars.VM.noop
};
var locals = this.stackVars.concat(this.registers.list);
if(locals.length > 0) {
this.source[0] = this.source[0] + ", " + locals.join(", ");
}
this.source[0] = this.source[0] + ";";
this.source.push("return buffer;");
var params = ["Handlebars", "context", "helpers", "partials"];
if(this.data) { params.push("data"); }
for(var i=0, l=this.environment.depths.list.length; i<l; i++) {
params.push("depth" + this.environment.depths.list[i]);
}
if(params.length === 4 && !this.environment.usePartial) { params.pop(); }
params.push(this.source.join("\n"));
var fn = Function.apply(this, params);
fn.displayName = "Handlebars.js";
Handlebars.log(Handlebars.logger.DEBUG, fn.toString() + "\n\n");
container.render = fn;
container.children = this.environment.children;
return function(context, helpers, partials, data, $depth) {
try {
var args = Array.prototype.slice.call(arguments);
args.unshift(Handlebars);
return container.render.apply(container, args);
} catch(e) {
throw e;
}
};
},
appendContent: function(content) {
this.source.push(this.appendToBuffer(this.quotedString(content)));
},
append: function() {
var local = this.popStack();
this.source.push("if(" + local + " || " + local + " === 0) { " + this.appendToBuffer(local) + " }");
},
appendEscaped: function() {
var opcode = this.nextOpcode(1), extra = "";
if(opcode[0] === 'appendContent') {
extra = " + " + this.quotedString(opcode[1][0]);
this.eat(opcode);
}
this.source.push(this.appendToBuffer("this.escapeExpression(" + this.popStack() + ")" + extra));
},
getContext: function(depth) {
if(this.lastContext !== depth) {
this.lastContext = depth;
if(depth === 0) {
this.source.push("currentContext = context;");
} else {
this.source.push("currentContext = depth" + depth + ";");
}
}
},
lookupWithFallback: function(name) {
if(name) {
this.pushStack(this.nameLookup('currentContext', name, 'context'));
var topStack = this.topStack();
this.source.push("if(" + topStack + " === undefined) { " + topStack + " = " + this.nameLookup('helpers', name, 'helper') + "; }");
} else {
this.pushStack("currentContext");
}
},
lookup: function(name) {
var topStack = this.topStack();
this.source.push(topStack + " = " + this.nameLookup(topStack, name, 'context') + ";");
},
pushString: function(string) {
this.pushStack(this.quotedString(string));
},
push: function(name) {
this.pushStack(name);
},
invokeMustache: function(paramSize, original) {
var params = ["context"], fn, slot;
fn = this.popStack();
for(var i=0; i<paramSize; i++) {
params.push(this.popStack());
}
if(this.data) { params.push("data"); }
var paramString = params.join(", ");
var helperMissing = ["context"].concat(this.quotedString(original)).concat(params.slice(1));
slot = this.nextStack();
if(paramSize === 0) {
// TODO: This case is not listed in the mustache spec. Is it important?
this.source.push("if(typeof " + fn + " === 'function') { " + slot + " = " + fn + ".call(" + paramString + "); }");
} else {
this.source.push("if(typeof " + fn + " === 'function') { " + slot + " = " + fn + ".call(" + paramString + "); } else { " + slot + " = helpers.helperMissing.call(" + helperMissing + ") }");
}
},
invokeProgram: function(guid, paramSize) {
var inverse = this.programExpression(this.inverse);
var fn = this.popStack();
var id = fn;
var params = ["context"];
var blockMissingParams = ["context", id];
for(var i=0; i<paramSize; i++) {
var param = this.popStack();
params.push(param);
blockMissingParams.push(param);
}
var mainProgram = this.programExpression(guid);
params.push(mainProgram, inverse);
blockMissingParams.push(mainProgram, inverse);
if(this.data) {
params.push("data");
blockMissingParams.push("data");
}
var nextStack = this.nextStack();
this.source.push("if(typeof " + id + " === 'function') { " + nextStack + " = " + id + ".call(" + params.join(", ") + "); }");
this.source.push("else { " + nextStack + " = helpers.blockHelperMissing.call(" + blockMissingParams.join(", ") + "); }");
},
invokeInverse: function(guid) {
var program = this.programExpression(guid);
var blockMissingParams = ["context", this.topStack(), "this.noop", program];
this.pushStack("helpers.blockHelperMissing.call(" + blockMissingParams.join(", ") + ")");
},
invokePartial: function(context) {
this.pushStack("this.invokePartial(" + this.nameLookup('partials', context, 'partial') + ", '" + context + "', " + this.popStack() + ", helpers, partials);");
},
// HELPERS
compiler: JavaScriptCompiler,
compileChildren: function(environment, data) {
var children = environment.children, child, compiler;
var compiled = [];
for(var i=0, l=children.length; i<l; i++) {
child = children[i];
compiler = new this.compiler();
compiled[i] = compiler.compile(child, data);
}
environment.rawChildren = children;
environment.children = compiled;
},
programExpression: function(guid) {
if(guid == null) { return "this.noop"; }
var programParams = [guid, "helpers", "partials"];
var depths = this.environment.rawChildren[guid].depths.list;
if(this.data) { programParams.push("data"); }
for(var i=0, l = depths.length; i<l; i++) {
depth = depths[i];
if(depth === 1) { programParams.push("context"); }
else { programParams.push("depth" + (depth - 1)); }
}
if(!this.environment.usePartial) {
if(programParams[3]) {
programParams[2] = "null";
} else {
programParams.pop();
}
}
if(depths.length === 0) {
return "this.program(" + programParams.join(", ") + ")";
} else {
programParams[0] = "this.children[" + guid + "]";
return "this.programWithDepth(" + programParams.join(", ") + ")";
}
},
register: function(name, val) {
this.useRegister(name);
this.source.push(name + " = " + val + ";");
},
useRegister: function(name) {
if(!this.registers[name]) {
this.registers[name] = true;
this.registers.list.push(name);
}
},
pushStack: function(item) {
this.source.push(this.nextStack() + " = " + item + ";");
return "stack" + this.stackSlot;
},
nextStack: function() {
this.stackSlot++;
if(this.stackSlot > this.stackVars.length) { this.stackVars.push("stack" + this.stackSlot); }
return "stack" + this.stackSlot;
},
popStack: function() {
return "stack" + this.stackSlot--;
},
topStack: function() {
return "stack" + this.stackSlot;
},
quotedString: function(str) {
return '"' + str
.replace(/\\/, '\\\\')
.replace(/"/g, '\\"')
.replace(/\n/g, '\\n')
.replace(/\r/g, '\\r') + '"';
}
};
var reservedWords = ("break case catch continue default delete do else finally " +
"for function if in instanceof new return switch this throw " +
"try typeof var void while with null true false").split(" ");
compilerWords = JavaScriptCompiler.RESERVED_WORDS = {};
for(var i=0, l=reservedWords.length; i<l; i++) {
compilerWords[reservedWords[i]] = true;
}
})(Handlebars.Compiler, Handlebars.JavaScriptCompiler);
Handlebars.VM = {
programWithDepth: function(fn) {
var args = Array.prototype.slice.call(arguments, 1);
return function(context) {
return fn.apply(this, [context].concat(args));
};
},
program: function(fn, helpers, partials, data) {
return function(context) {
return fn(context, helpers, partials, data);
};
},
noop: function() { return ""; },
compile: function(string, data) {
var ast = Handlebars.parse(string);
var environment = new Handlebars.Compiler().compile(ast);
return new Handlebars.JavaScriptCompiler().compile(environment, data);
},
invokePartial: function(partial, name, context, helpers, partials) {
if(partial === undefined) {
throw new Handlebars.Exception("The partial " + name + " could not be found");
} else if(partial instanceof Function) {
return partial(context, helpers, partials);
} else {
partials[name] = Handlebars.VM.compile(partial);
return partials[name](context, helpers, partials);
}
}
};
Handlebars.compile = Handlebars.VM.compile;
// END(BROWSER)
+100
View File
@@ -0,0 +1,100 @@
require "spec_helper"
class TestContext
class TestModule
attr_reader :name, :tests
def initialize(name)
@name = name
@tests = []
end
end
attr_reader :modules
def initialize
@modules = []
end
def module(name)
@modules << TestModule.new(name)
end
def test(name, function)
@modules.last.tests << [name, function]
end
end
test_context = TestContext.new
js_context = Handlebars::Spec::CONTEXT
Module.new do
extend Test::Unit::Assertions
def self.js_backtrace(context)
begin
context.eval("throw")
rescue V8::JSError => e
return e.backtrace(:javascript)
end
end
js_context["p"] = proc do |str|
p str
end
js_context["ok"] = proc do |ok, message|
js_context["$$RSPEC1$$"] = ok
result = js_context.eval("!!$$RSPEC1$$")
message ||= "#{ok} was not truthy"
unless result
backtrace = js_backtrace(js_context)
message << "\n#{backtrace.join("\n")}"
end
assert result, message
end
js_context["equals"] = proc do |first, second, message|
js_context["$$RSPEC1$$"] = first
js_context["$$RSPEC2$$"] = second
result = js_context.eval("$$RSPEC1$$ == $$RSPEC2$$")
message ||= "#{first} did not == #{second}"
unless result
backtrace = js_backtrace(js_context)
message << "\n#{backtrace.join("\n")}"
end
assert result, message
end
js_context["equal"] = js_context["equals"]
js_context["module"] = proc do |name|
test_context.module(name)
end
js_context["test"] = proc do |name, function|
test_context.test(name, function)
end
local = Regexp.escape(File.expand_path(Dir.pwd))
qunit_spec = File.expand_path("../qunit_spec.js", __FILE__)
js_context.load(qunit_spec.sub(/^#{local}\//, ''))
end
test_context.modules.each do |mod|
describe mod.name do
mod.tests.each do |name, function|
it name do
function.call
end
end
end
end
+181
View File
@@ -0,0 +1,181 @@
require "spec_helper"
describe "Parser" do
let(:handlebars) { @context["Handlebars"] }
def program(&block)
ASTBuilder.build do
program do
instance_eval(&block)
end
end
end
def ast_for(string)
ast = handlebars.parse(string)
handlebars.print(ast)
end
class ASTBuilder
def self.build(&block)
ret = new
ret.evaluate(&block)
ret.out
end
attr_reader :out
def initialize
@padding = 0
@out = ""
end
def evaluate(&block)
instance_eval(&block)
end
def pad(string)
@out << (" " * @padding) + string + "\n"
end
def with_padding
@padding += 1
ret = yield
@padding -= 1
ret
end
def program
pad("PROGRAM:")
with_padding { yield }
end
def inverse
pad("{{^}}")
with_padding { yield }
end
def block
pad("BLOCK:")
with_padding { yield }
end
def inverted_block
pad("INVERSE:")
with_padding { yield }
end
def mustache(id, *params)
pad("{{ #{id} [#{params.join(", ")}] }}")
end
def partial(id, context = nil)
content = id.dup
content << " #{context}" if context
pad("{{> #{content} }}")
end
def comment(comment)
pad("{{! '#{comment}' }}")
end
def content(string)
pad("CONTENT[ '#{string}' ]")
end
def string(string)
string.inspect
end
def id(id)
"ID:#{id}"
end
def path(*parts)
"PATH:#{parts.join("/")}"
end
end
it "parses simple mustaches" do
ast_for("{{foo}}").should == program { mustache id("foo") }
end
it "parses mustaches with paths" do
ast_for("{{foo/bar}}").should == program { mustache path("foo", "bar") }
end
it "parses mustaches with this/foo" do
ast_for("{{this/foo}}").should == program { mustache id("foo") }
end
it "parses mustaches with parameters" do
ast_for("{{foo bar}}").should == program { mustache id("foo"), id("bar") }
end
it "parses mustaches with string parameters" do
ast_for("{{foo bar \"baz\" }}").should == program { mustache id("foo"), id("bar"), string("baz")}
end
it "parses contents followed by a mustache" do
ast_for("foo bar {{baz}}").should == program do
content "foo bar "
mustache id("baz")
end
end
it "parses a partial" do
ast_for("{{> foo }}").should == program { partial id("foo") }
end
it "parses a partial with context" do
ast_for("{{> foo bar}}").should == program { partial id("foo"), id("bar") }
end
it "parses a comment" do
ast_for("{{! this is a comment }}").should == program do
comment " this is a comment "
end
end
it "parses an inverse section" do
ast_for("{{#foo}} bar {{^}} baz {{/foo}}").should == program do
block do
mustache id("foo")
program do
content " bar "
end
inverse do
content " baz "
end
end
end
end
it "parses a standalone inverse section" do
ast_for("{{^foo}}bar{{/foo}}").should == program do
inverted_block do
mustache id("foo")
program do
content "bar"
end
end
end
end
it "raises if there's a Parse error" do
lambda { ast_for("{{foo}") }.should raise_error(V8::JSError, /Parse error on line 1/)
lambda { ast_for("{{foo &}}")}.should raise_error(V8::JSError, /Parse error on line 1/)
end
it "knows how to report the correct line number in errors" do
lambda { ast_for("hello\nmy\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 3/m)
lambda { ast_for("hello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 5/m)
end
it "knows how to report the correct line number in errors when the first character is a newline" do
lambda { ast_for("\n\nhello\n\nmy\n\n{{foo}") }.should raise_error(V8::JSError, /Parse error on line 7/m)
end
end
+203 -44
View File
@@ -1,10 +1,26 @@
module("basic context");
var shouldCompileTo = function(string, hash, result, message) {
Handlebars.registerHelper('helperMissing', function(helper, context) {
if(helper === "link_to") {
return new Handlebars.SafeString("<a>" + context + "</a>");
}
});
var shouldCompileTo = function(string, hash, expected, message) {
var template = Handlebars.compile(string);
var params = Object.prototype.toString.call(hash) === "[object Array]" ? hash : [hash, undefined];
equal(template.apply(this, params), result, message);
}
if(Object.prototype.toString.call(hash) === "[object Array]") {
if(hash[1]) {
for(var prop in Handlebars.helpers) {
hash[1][prop] = Handlebars.helpers[prop];
}
}
} else {
hash = [hash];
}
result = template.apply(this, hash)
equal(result, expected, "'" + expected + "' should === '" + result + "': " + message);
};
var shouldThrow = function(fn, exception, message) {
var caught = false;
@@ -20,13 +36,14 @@ var shouldThrow = function(fn, exception, message) {
ok(caught, message || null);
}
test("compiling with a basic context", function() {
shouldCompileTo("Goodbye\n{{cruel}}\n{{world}}!", {cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!",
"It works if all the required keys are provided");
});
test("comments", function() {
shouldCompileTo("{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!",
shouldCompileTo("{{! Goodbye}}Goodbye\n{{cruel}}\n{{world}}!",
{cruel: "cruel", world: "world"}, "Goodbye\ncruel\nworld!",
"comments are ignored");
});
@@ -74,7 +91,7 @@ test("escaping expressions", function() {
test("functions returning safestrings shouldn't be escaped", function() {
var hash = {awesome: function() { return new Handlebars.SafeString("&\"\\<>"); }};
shouldCompileTo("{{awesome}}", hash, '&\"\\<>',
shouldCompileTo("{{awesome}}", hash, '&\"\\<>',
"functions returning safestrings aren't escaped");
});
@@ -84,7 +101,7 @@ test("functions", function() {
});
test("functions with context argument", function() {
shouldCompileTo("{{awesome frank}}",
shouldCompileTo("{{awesome frank}}",
{awesome: function(context) { return context; },
frank: "Frank"},
"Frank", "functions are called with context arguments");
@@ -100,11 +117,12 @@ test("nested paths with empty string value", function() {
"Goodbye world!", "Nested paths access nested objects with empty string");
});
test("bad idea nested paths", function() {
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
test("--- TODO --- bad idea nested paths", function() {
return;
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
shouldThrow(function() {
Handlebars.compile("{{#goodbyes}}{{../name/../name}}{{/goodbyes}}")(hash);
}, Handlebars.Exception,
}, Handlebars.Exception,
"Cannot jump (..) into previous context after moving into a context.");
var string = "{{#goodbyes}}{{.././world}} {{/goodbyes}}";
@@ -123,8 +141,8 @@ test("complex but empty paths", function() {
test("this keyword in paths", function() {
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}";
var hash = {goodbyes: ["goodbye", "Goodbye", "GOODBYE"]};
shouldCompileTo(string, hash, "goodbyeGoodbyeGOODBYE",
"This keyword in paths evaluates to current context");
shouldCompileTo(string, hash, "goodbyeGoodbyeGOODBYE",
"This keyword in paths evaluates to current context");
string = "{{#hellos}}{{this/text}}{{/hellos}}"
hash = {hellos: [{text: "hello"}, {text: "Hello"}, {text: "HELLO"}]};
@@ -144,7 +162,7 @@ test("inverted section with false value", function() {
var hash = {goodbyes: false};
shouldCompileTo(string, hash, "Right On!", "Inverted section rendered when value is false.");
});
test("inverted section with empty set", function() {
var string = "{{#goodbyes}}{{this}}{{/goodbyes}}{{^goodbyes}}Right On!{{/goodbyes}}";
var hash = {goodbyes: []};
@@ -201,10 +219,10 @@ test("block with complex lookup", function() {
});
test("helper with complex lookup", function() {
var string = "{{#goodbyes}}{{{link}}}{{/goodbyes}}"
var string = "{{#goodbyes}}{{{link ../prefix}}}{{/goodbyes}}"
var hash = {prefix: "/root", goodbyes: [{text: "Goodbye", url: "goodbye"}]};
var fallback = {link: function() {
return "<a href='" + this.__get__("../prefix") + "/" + this.url + "'>" + this.text + "</a>"
var fallback = {link: function(prefix) {
return "<a href='" + prefix + "/" + this.url + "'>" + this.text + "</a>"
}};
shouldCompileTo(string, [hash, fallback], "<a href='/root/goodbye'>Goodbye</a>")
});
@@ -212,11 +230,11 @@ test("helper with complex lookup", function() {
test("helper block with complex lookup expression", function() {
var string = "{{#goodbyes}}{{../name}}{{/goodbyes}}"
var hash = {name: "Alan"};
var fallback = {goodbyes: function(context, fn) {
var fallback = {goodbyes: function(fn) {
var out = "";
var byes = ["Goodbye", "goodbye", "GOODBYE"];
for (var i = 0,j = byes.length; i < j; i++) {
out += byes[i] + " " + fn(context) + "! ";
out += byes[i] + " " + fn(this) + "! ";
}
return out;
}};
@@ -224,10 +242,10 @@ test("helper block with complex lookup expression", function() {
});
test("helper with complex lookup and nested template", function() {
var string = "{{#goodbyes}}{{#link}}{{text}}{{/link}}{{/goodbyes}}";
var string = "{{#goodbyes}}{{#link ../prefix}}{{text}}{{/link}}{{/goodbyes}}";
var hash = {prefix: '/root', goodbyes: [{text: "Goodbye", url: "goodbye"}]};
var fallback = {link: function (context, fn) {
return "<a href='" + this.__get__("../prefix") + "/" + this.url + "'>" + fn(this) + "</a>";
var fallback = {link: function (prefix, fn) {
return "<a href='" + prefix + "/" + this.url + "'>" + fn(this) + "</a>";
}};
shouldCompileTo(string, [hash, fallback], "<a href='/root/goodbye'>Goodbye</a>")
});
@@ -243,7 +261,7 @@ test("block helper", function() {
var string = "{{#goodbyes}}{{text}}! {{/goodbyes}}cruel {{world}}!";
var template = Handlebars.compile(string);
result = template({goodbyes: function(context, fn) { return fn({text: "GOODBYE"}); }, world: "world"});
result = template({goodbyes: function(fn) { return fn({text: "GOODBYE"}); }, world: "world"});
equal(result, "GOODBYE! cruel world!");
});
@@ -251,14 +269,14 @@ test("block helper staying in the same context", function() {
var string = "{{#form}}<p>{{name}}</p>{{/form}}"
var template = Handlebars.compile(string);
result = template({form: function(context, fn) { return "<form>" + fn(this) + "</form>" }, name: "Yehuda"});
result = template({form: function(fn) { return "<form>" + fn(this) + "</form>" }, name: "Yehuda"});
equal(result, "<form><p>Yehuda</p></form>");
});
test("block helper should have wrapped context in this", function() {
test("block helper should have context in this", function() {
var source = "<ul>{{#people}}<li>{{#link}}{{name}}{{/link}}</li>{{/people}}</ul>";
var link = function(context, fn) {
return '<a href="/people/' + this.__get__("id") + '">' + fn(this) + '</a>';
var link = function(fn) {
return '<a href="/people/' + this.id + '">' + fn(this) + '</a>';
};
var data = { "people": [
{ "name": "Alan", "id": 1 },
@@ -268,6 +286,10 @@ test("block helper should have wrapped context in this", function() {
shouldCompileTo(source, [data, {link: link}], "<ul><li><a href=\"/people/1\">Alan</a></li><li><a href=\"/people/2\">Yehuda</a></li></ul>");
});
test("block helper for undefined value", function() {
shouldCompileTo("{{#empty}}shouldn't render{{/empty}}", {}, "");
});
test("block helper passing a new context", function() {
var string = "{{#form yehuda}}<p>{{name}}</p>{{/form}}"
var template = Handlebars.compile(string);
@@ -288,39 +310,57 @@ test("nested block helpers", function() {
var string = "{{#form yehuda}}<p>{{name}}</p>{{#link}}Hello{{/link}}{{/form}}"
var template = Handlebars.compile(string);
result = template({form: function(context, fn) { return "<form>" + fn(context) + "</form>" }, yehuda: {name: "Yehuda", link: function(context, fn) { return "<a href='" + context.name + "'>" + fn(context) + "</a>"; }}});
result = template({
form: function(context, fn) { return "<form>" + fn(context) + "</form>" },
yehuda: {name: "Yehuda",
link: function(fn) { return "<a href='" + this.name + "'>" + fn(this) + "</a>"; }
}
});
equal(result, "<form><p>Yehuda</p><a href='Yehuda'>Hello</a></form>");
});
test("block inverted sections", function() {
shouldCompileTo("{{#people}}{{name}}{{^}}{{../none}}{{/people}}", {none: "No people"},
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people"},
"No people");
});
test("block inverted sections with empty arrays", function() {
shouldCompileTo("{{#people}}{{name}}{{^}}{{none}}{{/people}}", {none: "No people", people: []},
"No people");
});
test("block helper inverted sections", function() {
var string = "{{#list people}}{{name}}{{^}}<em>Nobody's here</em>{{/list}}"
var list = function(context, fn) {
console.log(context);
var list = function(context, fn, inverse) {
if (context.length > 0) {
var out = "<ul>";
for(var i = 0,j=context.length; i < j; i++) {
out += "<li>";
out += "<li>";
out += fn(context[i]);
out += "</li>";
}
out += "</ul>";
return out;
} else {
return "<p>" + inverse(this) + "</p>";
}
};
list.not = function(context, fn) {
return "<p>" + fn(context, this) + "</p>";
};
var hash = {list: list, people: [{name: "Alan"}, {name: "Yehuda"}]};
var empty = {list: list, people: []};
var rootMessage = {
list: function(context, fn, inverse) { if(context.length === 0) { return "<p>" + inverse(this) + "</p>"; } },
people: [],
message: "Nobody's here"
}
var messageString = "{{#list people}}Hello{{^}}{{message}}{{/list}}";
// the meaning here may be kind of hard to catch, but list.not is always called,
// so we should see the output of both
shouldCompileTo(string, hash, "<ul><li>Alan</li><li>Yehuda</li></ul><p><em>Nobody's here</em></p>", "Not is called when block inverted section is encountered.");
shouldCompileTo(string, hash, "<ul><li>Alan</li><li>Yehuda</li></ul>", "an inverse wrapper is passed in as a new context");
shouldCompileTo(string, empty, "<p><em>Nobody's here</em></p>", "an inverse wrapper can be optionally called");
shouldCompileTo(messageString, rootMessage, "<p>Nobody's here</p>", "the context of an inverse is the parent of the block");
});
module("fallback hash");
@@ -347,7 +387,7 @@ test("basic partials", function() {
var string = "Dudes: {{#dudes}}{{> dude}}{{/dudes}}";
var partial = "{{name}} ({{url}}) ";
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
shouldCompileTo(string, [hash, {partials: {dude: partial}}], "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
shouldCompileTo(string, [hash, {}, {dude: partial}], "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
"Basic partials output based on current context.");
});
@@ -355,7 +395,7 @@ test("partials with context", function() {
var string = "Dudes: {{>dude dudes}}";
var partial = "{{#this}}{{name}} ({{url}}) {{/this}}";
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
shouldCompileTo(string, [hash, {partials: {dude: partial}}], "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
shouldCompileTo(string, [hash, {}, {dude: partial}], "Dudes: Yehuda (http://yehuda) Alan (http://alan) ",
"Partials can be passed a context");
});
@@ -364,12 +404,12 @@ test("partial in a partial", function() {
var dude = "{{name}} {{> url}} ";
var url = "<a href='{{url}}'>{{url}}</a>";
var hash = {dudes: [{name: "Yehuda", url: "http://yehuda"}, {name: "Alan", url: "http://alan"}]};
shouldCompileTo(string, [hash, {partials: {dude: dude, url: url}}], "Dudes: Yehuda <a href='http://yehuda'>http://yehuda</a> Alan <a href='http://alan'>http://alan</a> ", "Partials are rendered inside of other partials");
shouldCompileTo(string, [hash, {}, {dude: dude, url: url}], "Dudes: Yehuda <a href='http://yehuda'>http://yehuda</a> Alan <a href='http://alan'>http://alan</a> ", "Partials are rendered inside of other partials");
});
test("rendering undefined partial throws an exception", function() {
shouldThrow(function() {
var template = Handlebars.compile("{{> whatever}}");
var template = Handlebars.compile("{{> whatever}}");
template();
}, Handlebars.Exception, "Should throw exception");
});
@@ -378,21 +418,21 @@ test("GH-14: a partial preceding a selector", function() {
var string = "Dudes: {{>dude}} {{another_dude}}";
var dude = "{{name}}";
var hash = {name:"Jeepers", another_dude:"Creepers"};
shouldCompileTo(string, [hash, {partials: {dude:dude}}], "Dudes: Jeepers Creepers", "Regular selectors can follow a partial");
shouldCompileTo(string, [hash, {}, {dude:dude}], "Dudes: Jeepers Creepers", "Regular selectors can follow a partial");
});
test("Partial containing complex expression", function() {
var template = "Dudes: {{#dudes}}{{> dude}} {{/dudes}}";
var dude = "{{../salutation}} {{name}}";
var hash = {salutation: "Mr.", dudes: [{name: "Yehuda"}, {name: "Alan"}]};
shouldCompileTo(template, [hash, {partials: {dude: dude}}], "Dudes: Mr. Yehuda Mr. Alan ");
shouldCompileTo(template, [hash, {}, {dude: dude}], "Dudes: Mr. Yehuda Mr. Alan ");
});
module("String literal parameters");
test("simple literals work", function() {
var string = 'Message: {{hello "world"}}';
var hash = {}
var hash = {};
var fallback = {hello: function(param) { return "Hello " + param; }}
shouldCompileTo(string, [hash, fallback], "Message: Hello world", "template with a simple String literal");
});
@@ -401,7 +441,7 @@ test("using a quote in the middle of a parameter raises an error", function() {
shouldThrow(function() {
var string = 'Message: {{hello wo"rld"}}';
Handlebars.compile(string);
}, Handlebars.Exception, "should throw exception");
}, Error, "should throw exception");
});
test("escaping a String is possible", function(){
@@ -431,7 +471,7 @@ test("block multi-params work", function() {
var string = 'Message: {{#goodbye cruel world}}{{greeting}} {{adj}} {{noun}}{{/goodbye}}';
var hash = {cruel: "cruel", world: "world"}
var fallback = {goodbye: function(cruel, world, fn) {
return fn({greeting: "Goodbye", adj: "cruel", noun: "world"});
return fn({greeting: "Goodbye", adj: cruel, noun: world});
}}
shouldCompileTo(string, [hash, fallback], "Message: Goodbye cruel world", "block helpers with multiple params");
})
@@ -443,3 +483,122 @@ test("constructing a safestring from a string and checking its type", function()
ok(safe instanceof Handlebars.SafeString, "SafeString is an instance of Handlebars.SafeString");
equal(safe, "testing 1, 2, 3", "SafeString is equivalent to its underlying string");
});
module("helperMissing");
test("if a context is not found, helperMissing is used", function() {
var string = "{{hello}} {{link_to world}}"
var context = { hello: "Hello", world: "world" };
shouldCompileTo(string, context, "Hello <a>world</a>")
});
module("built-in helpers");
test("with", function() {
var string = "{{#with person}}{{first}} {{last}}{{/with}}";
shouldCompileTo(string, {person: {first: "Alan", last: "Johnson"}}, "Alan Johnson");
});
test("if", function() {
var string = "{{#if goodbye}}GOODBYE {{/if}}cruel {{world}}!";
shouldCompileTo(string, {goodbye: true, world: "world"}, "GOODBYE cruel world!",
"if with boolean argument shows the contents when true");
shouldCompileTo(string, {goodbye: "dummy", world: "world"}, "GOODBYE cruel world!",
"if with string argument shows the contents");
shouldCompileTo(string, {goodbye: false, world: "world"}, "cruel world!",
"if with boolean argument does not show the contents when false");
shouldCompileTo(string, {world: "world"}, "cruel world!",
"if with undefined does not show the contents");
});
test("each", function() {
var string = "{{#each goodbyes}}{{text}}! {{/each}}cruel {{world}}!"
var hash = {goodbyes: [{text: "goodbye"}, {text: "Goodbye"}, {text: "GOODBYE"}], world: "world"};
shouldCompileTo(string, hash, "goodbye! Goodbye! GOODBYE! cruel world!",
"each with array argument iterates over the contents when not empty");
shouldCompileTo(string, {goodbyes: [], world: "world"}, "cruel world!",
"each with array argument ignores the contents when empty");
});
test("overriding property lookup", function() {
});
test("passing in data to a compiled function that expects data - works with helpers", function() {
var template = Handlebars.compile("{{hello}}", true);
var helpers = {
hello: function(data) {
return data.adjective + " " + this.noun;
}
};
var result = template({noun: "cat"}, helpers, null, {adjective: "happy"});
equals("happy cat", result);
});
test("passing in data to a compiled function that expects data - works with helpers and parameters", function() {
var template = Handlebars.compile("{{hello world}}", true);
var helpers = {
hello: function(noun, data) {
return data.adjective + " " + noun + (this.exclaim ? "!" : "");
}
};
var result = template({exclaim: true, world: "world"}, helpers, null, {adjective: "happy"});
equals("happy world!", result);
});
test("passing in data to a compiled function that expects data - works with block helpers", function() {
var template = Handlebars.compile("{{#hello}}{{world}}{{/hello}}", true);
var helpers = {
hello: function(fn) {
return fn(this);
},
world: function(data) {
return data.adjective + " world" + (this.exclaim ? "!" : "");
}
};
var result = template({exclaim: true}, helpers, null, {adjective: "happy"});
equals("happy world!", result);
});
test("passing in data to a compiled function that expects data - works with block helpers that use ..", function() {
var template = Handlebars.compile("{{#hello}}{{world ../zomg}}{{/hello}}", true);
var helpers = {
hello: function(fn) {
return fn({exclaim: "?"});
},
world: function(thing, data) {
return data.adjective + " " + thing + (this.exclaim || "");
}
};
var result = template({exclaim: true, zomg: "world"}, helpers, null, {adjective: "happy"});
equals("happy world?", result);
});
test("passing in data to a compiled function that expects data - works with block helpers that use ..", function() {
var template = Handlebars.compile("{{#hello}}{{world ../zomg}}{{/hello}}", true);
var helpers = {
hello: function(fn, inverse, data) {
return data.accessData + " " + fn({exclaim: "?"});
},
world: function(thing, data) {
return data.adjective + " " + thing + (this.exclaim || "");
}
};
var result = template({exclaim: true, zomg: "world"}, helpers, null, {adjective: "happy", accessData: "#win"});
equals("#win happy world?", result);
});
+107
View File
@@ -0,0 +1,107 @@
require "v8"
# Monkey patches due to bugs in RubyRacer
class V8::JSError
def initialize(try, to)
@to = to
begin
super(initialize_unsafe(try))
rescue Exception => e
# Original code does not make an Array here
@boundaries = [Boundary.new(:rbframes => e.backtrace)]
@value = e
super("BUG! please report. JSError#initialize failed!: #{e.message}")
end
end
def parse_js_frames(try)
raw = @to.rb(try.StackTrace())
if raw && !raw.empty?
raw.split("\n")[1..-1].tap do |frames|
# Original code uses strip!, and the frames are not guaranteed to be strippable
frames.each {|frame| frame.strip.chomp!(",")}
end
else
[]
end
end
end
module Handlebars
module Spec
def self.js_backtrace(context)
begin
context.eval("throw")
rescue V8::JSError => e
return e.backtrace(:javascript)
end
end
def self.remove_exports(string)
match = string.match(%r{\A(.*?)^// BEGIN\(BROWSER\)\n(.*)\n^// END\(BROWSER\)(.*?)\Z}m)
prelines = match ? match[1].count("\n") + 1 : 0
ret = match ? match[2] : string
("\n" * prelines) + ret
end
def self.js_load(file)
str = File.read(file)
CONTEXT.eval(remove_exports(str), file)
end
CONTEXT = V8::Context.new
CONTEXT.instance_eval do |context|
context["exports"] = nil
context["p"] = proc do |val|
p val if ENV["DEBUG_JS"]
end
context["puts"] = proc do |val|
puts val if ENV["DEBUG_JS"]
end
context["puts_node"] = proc do |val|
puts context["Handlebars"]["PrintVisitor"].new.accept(val)
puts
end
context["puts_caller"] = proc do
puts "BACKTRACE:"
puts Handlebars::Spec.js_backtrace(context)
puts
end
Handlebars::Spec.js_load('lib/handlebars/parser.js')
Handlebars::Spec.js_load('lib/handlebars/base.js');
Handlebars::Spec.js_load('lib/handlebars/ast.js');
Handlebars::Spec.js_load('lib/handlebars/visitor.js');
Handlebars::Spec.js_load('lib/handlebars/printer.js')
Handlebars::Spec.js_load('lib/handlebars/runtime.js')
Handlebars::Spec.js_load('lib/handlebars/utils.js')
Handlebars::Spec.js_load('lib/Handlebars/vm.js')
Handlebars::Spec.js_load('lib/handlebars.js')
context["Handlebars"]["logger"]["level"] = ENV["DEBUG_JS"] ? context["Handlebars"]["logger"][ENV["DEBUG_JS"]] : 4
context["Handlebars"]["logger"]["log"] = proc do |level, str|
logger_level = context["Handlebars"]["logger"]["level"].to_i
if logger_level <= level
puts str
end
end
end
end
end
require "test/unit/assertions"
RSpec.configure do |config|
config.include Test::Unit::Assertions
config.before(:all) do
@context = Handlebars::Spec::CONTEXT
end
end
+148
View File
@@ -0,0 +1,148 @@
require "spec_helper"
require "timeout"
describe "Tokenizer" do
let(:parser) { @context["handlebars"] }
let(:lexer) { @context["handlebars"]["lexer"] }
Token = Struct.new(:name, :text)
def tokenize(string)
lexer.setInput(string)
out = []
while result = parser.terminals_[lexer.lex] and result != "EOF"
out << Token.new(result, lexer.yytext)
end
out
end
RSpec::Matchers.define :match_tokens do |tokens|
match do |result|
result.map(&:name).should == tokens
end
end
RSpec::Matchers.define :be_token do |name, string|
match do |token|
token.name.should == name
token.text.should == string
end
end
it "tokenizes a simple mustache as 'OPEN ID CLOSE'" do
result = tokenize("{{foo}}")
result.should match_tokens(%w(OPEN ID CLOSE))
result[1].should be_token("ID", "foo")
end
it "tokenizes a path as 'OPEN (ID SEP)* ID CLOSE'" do
result = tokenize("{{../foo/bar}}")
result.should match_tokens(%w(OPEN ID SEP ID SEP ID CLOSE))
result[1].should be_token("ID", "..")
end
it "tokenizes a path with this/foo as OPEN ID SEP ID CLOSE" do
result = tokenize("{{this/foo}}")
result.should match_tokens(%w(OPEN ID SEP ID CLOSE))
result[1].should be_token("ID", "this")
result[3].should be_token("ID", "foo")
end
it "tokenizes a simple mustahe with spaces as 'OPEN ID CLOSE'" do
result = tokenize("{{ foo }}")
result.should match_tokens(%w(OPEN ID CLOSE))
result[1].should be_token("ID", "foo")
end
it "tokenizes raw content as 'CONTENT'" do
result = tokenize("foo {{ bar }} baz")
result.should match_tokens(%w(CONTENT OPEN ID CLOSE CONTENT))
result[0].should be_token("CONTENT", "foo ")
result[4].should be_token("CONTENT", " baz")
end
it "tokenizes a partial as 'OPEN_PARTIAL ID CLOSE'" do
result = tokenize("{{> foo}}")
result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
end
it "tokenizes a partial with context as 'OPEN_PARTIAL ID ID CLOSE'" do
result = tokenize("{{> foo bar }}")
result.should match_tokens(%w(OPEN_PARTIAL ID ID CLOSE))
end
it "tokenizes a partial without spaces as 'OPEN_PARTIAL ID CLOSE'" do
result = tokenize("{{>foo}}")
result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
end
it "tokenizes a partial space at the end as 'OPEN_PARTIAL ID CLOSE'" do
result = tokenize("{{>foo }}")
result.should match_tokens(%w(OPEN_PARTIAL ID CLOSE))
end
it "tokenizes a comment as 'COMMENT'" do
result = tokenize("foo {{! this is a comment }} bar {{ baz }}")
result.should match_tokens(%w(CONTENT COMMENT CONTENT OPEN ID CLOSE))
result[1].should be_token("COMMENT", " this is a comment ")
end
it "tokenizes open and closing blocks as 'OPEN_BLOCK ID CLOSE ... OPEN_ENDBLOCK ID CLOSE'" do
result = tokenize("{{#foo}}content{{/foo}}")
result.should match_tokens(%w(OPEN_BLOCK ID CLOSE CONTENT OPEN_ENDBLOCK ID CLOSE))
end
it "tokenizes inverse sections as 'OPEN_INVERSE CLOSE'" do
tokenize("{{^}}").should match_tokens(%w(OPEN_INVERSE CLOSE))
tokenize("{{else}}").should match_tokens(%w(OPEN_INVERSE CLOSE))
tokenize("{{ else }}").should match_tokens(%w(OPEN_INVERSE CLOSE))
end
it "tokenizes inverse sections with ID as 'OPEN_INVERSE ID CLOSE'" do
result = tokenize("{{^foo}}")
result.should match_tokens(%w(OPEN_INVERSE ID CLOSE))
result[1].should be_token("ID", "foo")
end
it "tokenizes inverse sections with ID and spaces as 'OPEN_INVERSE ID CLOSE'" do
result = tokenize("{{^ foo }}")
result.should match_tokens(%w(OPEN_INVERSE ID CLOSE))
result[1].should be_token("ID", "foo")
end
it "tokenizes mustaches with params as 'OPEN ID ID ID CLOSE'" do
result = tokenize("{{ foo bar baz }}")
result.should match_tokens(%w(OPEN ID ID ID CLOSE))
result[1].should be_token("ID", "foo")
result[2].should be_token("ID", "bar")
result[3].should be_token("ID", "baz")
end
it "tokenizes mustaches with String params as 'OPEN ID ID STRING CLOSE'" do
result = tokenize("{{ foo bar \"baz\" }}")
result.should match_tokens(%w(OPEN ID ID STRING CLOSE))
result[3].should be_token("STRING", "baz")
end
it "tokenizes String params with spaces inside as 'STRING'" do
result = tokenize("{{ foo bar \"baz bat\" }}")
result.should match_tokens(%w(OPEN ID ID STRING CLOSE))
result[3].should be_token("STRING", "baz bat")
end
it "tokenizes String params with escapes quotes as 'STRING'" do
result = tokenize(%|{{ foo "bar\\"baz" }}|)
result.should match_tokens(%w(OPEN ID STRING CLOSE))
result[2].should be_token("STRING", %{bar"baz})
end
it "does not time out in a mustache with a single } followed by EOF" do
Timeout.timeout(1) { tokenize("{{foo}").should match_tokens(%w(OPEN ID)) }
end
it "does not time out in a mustache when invalid ID characters are used" do
Timeout.timeout(1) { tokenize("{{foo & }}").should match_tokens(%w(OPEN ID)) }
end
end
+28
View File
@@ -0,0 +1,28 @@
%x mu
%%
[^\x00]*?/("{{") { this.begin("mu"); if (yytext) return 'CONTENT'; }
[^\x00]+ { return 'CONTENT'; }
<mu>"{{>" { return 'OPEN_PARTIAL'; }
<mu>"{{#" { return 'OPEN_BLOCK'; }
<mu>"{{/" { return 'OPEN_ENDBLOCK'; }
<mu>"{{^" { return 'OPEN_INVERSE'; }
<mu>"{{"\s*"else" { return 'OPEN_INVERSE'; }
<mu>"{{{" { return 'OPEN_UNESCAPED'; }
<mu>"{{&" { return 'OPEN_UNESCAPED'; }
<mu>"{{!".*?"}}" { yytext = yytext.substr(3,yyleng-5); this.begin("INITIAL"); return 'COMMENT'; }
<mu>"{{" { return 'OPEN'; }
<mu>"/" { return 'SEP'; }
<mu>\s+ { /*ignore whitespace*/ }
<mu>"}}}" { this.begin("INITIAL"); return 'CLOSE'; }
<mu>"}}" { this.begin("INITIAL"); return 'CLOSE'; }
<mu>'"'("\\"["]|[^"])*'"' { yytext = yytext.substr(1,yyleng-2).replace(/\\"/g,'"'); return 'STRING'; }
<mu>[a-zA-Z0-9_.]+/[} /] { return 'ID'; }
<mu>. { return 'INVALID'; }
<INITIAL,mu><<EOF>> { return 'EOF'; }
+79
View File
@@ -0,0 +1,79 @@
%start root
%%
root
: program EOF { return $1 }
;
program
: statements simpleInverse statements { $$ = new yy.ProgramNode($1, $3) }
| statements { $$ = new yy.ProgramNode($1) }
| "" { $$ = new yy.ProgramNode([]) }
;
statements
: statement { $$ = [$1] }
| statements statement { $1.push($2); $$ = $1 }
;
statement
: openInverse program closeBlock { $$ = new yy.InverseNode($1, $2, $3) }
| openBlock program closeBlock { $$ = new yy.BlockNode($1, $2, $3) }
| mustache { $$ = $1 }
| partial { $$ = $1 }
| CONTENT { $$ = new yy.ContentNode($1) }
| COMMENT { $$ = new yy.CommentNode($1) }
;
openBlock
: OPEN_BLOCK inMustache CLOSE { $$ = new yy.MustacheNode($2) }
;
openInverse
: OPEN_INVERSE inMustache CLOSE { $$ = new yy.MustacheNode($2) }
;
closeBlock
: OPEN_ENDBLOCK path CLOSE { $$ = $2 }
;
mustache
: OPEN inMustache CLOSE { $$ = new yy.MustacheNode($2) }
| OPEN_UNESCAPED inMustache CLOSE { $$ = new yy.MustacheNode($2, true) }
;
partial
: OPEN_PARTIAL path CLOSE { $$ = new yy.PartialNode($2) }
| OPEN_PARTIAL path path CLOSE { $$ = new yy.PartialNode($2, $3) }
;
simpleInverse
: OPEN_INVERSE CLOSE { }
;
inMustache
: path params { $$ = [$1].concat($2) }
| path { $$ = [$1] }
;
params
: params param { $1.push($2); $$ = $1; }
| param { $$ = [$1] }
;
param
: path { $$ = $1 }
| STRING { $$ = new yy.StringNode($1) }
;
path
: pathSegments { $$ = new yy.IdNode($1) }
;
pathSegments
: pathSegments SEP ID { $1.push($3); $$ = $1; }
| ID { $$ = [$1] }
;
-6
View File
@@ -1,6 +0,0 @@
This is the story of guys who work on a project
called {{project}}. Their names were {{#people}}{{firstName}} and {{/people}}
they both enjoyed working on {{project}}.
{{#people}}
{{>personPet}}
{{/people}}
-20
View File
@@ -1,20 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Handlebars.js Test Suite</title>
<link rel="stylesheet" href="vendor/test/qunit.css" type="text/css" media="screen" />
<script type="text/javascript" src="vendor/jquery.js"></script>
<script type="text/javascript" src="vendor/test/qunit.js"></script>
<script type="text/javascript" src="../lib/handlebars.js"></script>
</head>
<body>
<h1 id="qunit-header">Handlebars.js Test Suite</h1>
<h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="main" style="display: none">
</div>
<script type="text/javascript" src="handlebars.js"></script>
</body>
</html>
-324
View File
@@ -1,324 +0,0 @@
/*
mustache.js — Logic-less templates in JavaScript
See http://mustache.github.com/ for more info.
*/
var Mustache = function() {
var Renderer = function() {};
Renderer.prototype = {
otag: "{{",
ctag: "}}",
pragmas: {},
buffer: [],
pragmas_implemented: {
"IMPLICIT-ITERATOR": true
},
context: {},
render: function(template, context, partials, in_recursion) {
// reset buffer & set context
if(!in_recursion) {
this.context = context;
this.buffer = []; // TODO: make this non-lazy
}
// fail fast
if(!this.includes("", template)) {
if(in_recursion) {
return template;
} else {
this.send(template);
return;
}
}
template = this.render_pragmas(template);
var html = this.render_section(template, context, partials);
if(in_recursion) {
return this.render_tags(html, context, partials, in_recursion);
}
this.render_tags(html, context, partials, in_recursion);
},
/*
Sends parsed lines
*/
send: function(line) {
if(line != "") {
this.buffer.push(line);
}
},
/*
Looks for %PRAGMAS
*/
render_pragmas: function(template) {
// no pragmas
if(!this.includes("%", template)) {
return template;
}
var that = this;
var regex = new RegExp(this.otag + "%([\\w-]+) ?([\\w]+=[\\w]+)?" +
this.ctag);
return template.replace(regex, function(match, pragma, options) {
if(!that.pragmas_implemented[pragma]) {
throw({message:
"This implementation of mustache doesn't understand the '" +
pragma + "' pragma"});
}
that.pragmas[pragma] = {};
if(options) {
var opts = options.split("=");
that.pragmas[pragma][opts[0]] = opts[1];
}
return "";
// ignore unknown pragmas silently
});
},
/*
Tries to find a partial in the curent scope and render it
*/
render_partial: function(name, context, partials) {
name = this.trim(name);
if(!partials || partials[name] === undefined) {
throw({message: "unknown_partial '" + name + "'"});
}
if(typeof(context[name]) != "object") {
return this.render(partials[name], context, partials, true);
}
return this.render(partials[name], context[name], partials, true);
},
/*
Renders inverted (^) and normal (#) sections
*/
render_section: function(template, context, partials) {
if(!this.includes("#", template) && !this.includes("^", template)) {
return template;
}
var that = this;
// CSW - Added "+?" so it finds the tighest bound, not the widest
var regex = new RegExp(this.otag + "(\\^|\\#)\\s*(.+)\\s*" + this.ctag +
"\n*([\\s\\S]+?)" + this.otag + "\\/\\s*\\2\\s*" + this.ctag +
"\\s*", "mg");
// for each {{#foo}}{{/foo}} section do...
return template.replace(regex, function(match, type, name, content) {
var value = that.find(name, context);
if(type == "^") { // inverted section
if(!value || that.is_array(value) && value.length === 0) {
// false or empty list, render it
return that.render(content, context, partials, true);
} else {
return "";
}
} else if(type == "#") { // normal section
if(that.is_array(value)) { // Enumerable, Let's loop!
return that.map(value, function(row) {
return that.render(content, that.create_context(row),
partials, true);
}).join("");
} else if(that.is_object(value)) { // Object, Use it as subcontext!
return that.render(content, that.create_context(value),
partials, true);
} else if(typeof value === "function") {
// higher order section
return value.call(context, content, function(text) {
return that.render(text, context, partials, true);
});
} else if(value) { // boolean section
return that.render(content, context, partials, true);
} else {
return "";
}
}
});
},
/*
Replace {{foo}} and friends with values from our view
*/
render_tags: function(template, context, partials, in_recursion) {
// tit for tat
var that = this;
var new_regex = function() {
return new RegExp(that.otag + "(=|!|>|\\{|%)?([^\\/#\\^]+?)\\1?" +
that.ctag + "+", "g");
};
var regex = new_regex();
var tag_replace_callback = function(match, operator, name) {
switch(operator) {
case "!": // ignore comments
return "";
case "=": // set new delimiters, rebuild the replace regexp
that.set_delimiters(name);
regex = new_regex();
return "";
case ">": // render partial
return that.render_partial(name, context, partials);
case "{": // the triple mustache is unescaped
return that.find(name, context);
default: // escape the value
return that.escape(that.find(name, context));
}
};
var lines = template.split("\n");
for(var i = 0; i < lines.length; i++) {
lines[i] = lines[i].replace(regex, tag_replace_callback, this);
if(!in_recursion) {
this.send(lines[i]);
}
}
if(in_recursion) {
return lines.join("\n");
}
},
set_delimiters: function(delimiters) {
var dels = delimiters.split(" ");
this.otag = this.escape_regex(dels[0]);
this.ctag = this.escape_regex(dels[1]);
},
escape_regex: function(text) {
// thank you Simon Willison
if(!arguments.callee.sRE) {
var specials = [
'/', '.', '*', '+', '?', '|',
'(', ')', '[', ']', '{', '}', '\\'
];
arguments.callee.sRE = new RegExp(
'(\\' + specials.join('|\\') + ')', 'g'
);
}
return text.replace(arguments.callee.sRE, '\\$1');
},
/*
find `name` in current `context`. That is find me a value
from the view object
*/
find: function(name, context) {
name = this.trim(name);
// Checks whether a value is thruthy or false or 0
function is_kinda_truthy(bool) {
return bool === false || bool === 0 || bool;
}
var value;
if(is_kinda_truthy(context[name])) {
value = context[name];
} else if(is_kinda_truthy(this.context[name])) {
value = this.context[name];
}
if(typeof value === "function") {
return value.apply(context);
}
if(value !== undefined) {
return value;
}
// silently ignore unkown variables
return "";
},
// Utility methods
/* includes tag */
includes: function(needle, haystack) {
return haystack.indexOf(this.otag + needle) != -1;
},
/*
Does away with nasty characters
*/
escape: function(s) {
s = String(s === null ? "" : s);
return s.replace(/&(?!\w+;)|["<>\\]/g, function(s) {
switch(s) {
case "&": return "&amp;";
case "\\": return "\\\\";
case '"': return '\"';
case "<": return "&lt;";
case ">": return "&gt;";
default: return s;
}
});
},
// by @langalex, support for arrays of strings
create_context: function(_context) {
if(this.is_object(_context)) {
return _context;
} else {
var iterator = ".";
if(this.pragmas["IMPLICIT-ITERATOR"]) {
iterator = this.pragmas["IMPLICIT-ITERATOR"].iterator;
}
var ctx = {};
ctx[iterator] = _context;
return ctx;
}
},
is_object: function(a) {
return a && typeof a == "object";
},
is_array: function(a) {
return Object.prototype.toString.call(a) === '[object Array]';
},
/*
Gets rid of leading and trailing whitespace
*/
trim: function(s) {
return s.replace(/^\s*|\s*$/g, "");
},
/*
Why, why, why? Because IE. Cry, cry cry.
*/
map: function(array, fn) {
if (typeof array.map == "function") {
return array.map(fn);
} else {
var r = [];
var l = array.length;
for(var i = 0; i < l; i++) {
r.push(fn(array[i]));
}
return r;
}
}
};
return({
name: "mustache.js",
version: "0.3.0",
/*
Turns a template and view into HTML
*/
to_html: function(template, view, partials, send_fun) {
var renderer = new Renderer();
if(send_fun) {
renderer.send = send_fun;
}
renderer.render(template, view, partials);
if(!send_fun) {
return renderer.buffer.join("\n");
}
}
});
}();
-20
View File
@@ -1,20 +0,0 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<script src="vendor/jquery.js"></script>
<link rel="stylesheet" href="vendor/test/qunit.css" type="text/css" media="screen" />
<script type="text/javascript" src="../lib/handlebars.js"></script>
<script type="text/javascript" src="mustache-0.3.0.js"></script>
</head>
<body>
<h1 id="qunit-header">Performance Testing</h1>
<h2 id="qunit-banner"></h2>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="main">
</div>
<script type="text/javascript" src="compat.js"></script>
<script type="text/javascript" src="perf.js"></script>
</body>
</html>
-88
View File
@@ -1,88 +0,0 @@
function perfTest(name, test) {
var timings = [];
// run the test 1000 times so we can get a happy average
for (var i = 0; i < 1000; i++) {
var testStart = new Date().getTime();
test();
var testEnd = new Date().getTime();
timings.push((testEnd - testStart)/1000);
}
return analyzeTimes(name, timings);
}
function analyzeTimes(name, timings) {
var stats = {name: name, min: null, max: null, avg: 0, total: 0};
var sum = 0;
for (var i = 0; i < timings.length; i++) {
stats.total += timings[i];
if (i == 0) {
stats.min = timings[i];
stats.max = timings[i];
}
else {
if (timings[i] > stats.max) {
stats.max = timings[i];
}
if (timings[i] < stats.min) {
stats.min = timings[i];
}
}
}
stats.avg = (stats.total / timings.length);
return stats;
}
function perfCompare(name, tests) {
var results = [];
for (var i = 0; j = tests.length, i < j; i++) {
results.push(perfTest(tests[i][0], tests[i][1]));
}
var content = '<h2>' + name + '</h2>';
content += "<table>";
content += "<tr><th>Test</th><th>Total</th><th>Average</th><th>Min</th><th>Max</th></tr>";
for (var i = 0; j = results.length, i < j; i++) {
var result = results[i];
content += "<tr>"
content += "<td>" + result.name + "</td>";
content += "<td>" + result.total + "</td>";
content += "<td>" + result.avg + "</td>";
content += "<td>" + result.min + "</td>";
content += "<td>" + result.max + "</td>";
}
content += "</table>";
$('#main').append(content);
}
// set up the templates
var tmpl = "This is the story of guys who work on a project\n" +
"called {{project}}. Their names were {{#people}}{{firstName}} and {{/people}}\n" +
"they both enjoyed working on {{project}}.\n\n" +
"{{#people}}\n" +
"{{>personPet}}\n" +
"{{/people}}";
var partials = {
personPet: "{{firstName}} {{lastName}} {{#pet}} owned a {{species}}. Its name was {{name}}.{{/pet}}{{^pet}}didn't own a pet.{{/pet}}"
};
var data = {
project: "Handlebars",
people: [
{ firstName: "Yehuda", lastName: "Katz" },
{ firstName: "Alan", lastName: "Johnson", pet: { species: "cat", name: "Luke" } }
]
}
var test1 = function() {
Handlebars.compile(tmpl)(data, {partials: partials});
}
var test2 = function() {
Mustache.to_html(tmpl, data, partials);
}
perfCompare("Mustache Compatibility", [["Handlebars", test1], ["Mustache", test2]]);
-154
View File
@@ -1,154 +0,0 @@
/*!
* jQuery JavaScript Library v1.4.2
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2010, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Sat Feb 13 22:33:48 2010 -0500
*/
(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return 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(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
-140
View File
@@ -1,140 +0,0 @@
ol#qunit-tests {
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
margin:0;
padding:0;
list-style-position:inside;
font-size: smaller;
}
ol#qunit-tests li{
padding:0.4em 0.5em 0.4em 2.5em;
border-bottom:1px solid #fff;
font-size:small;
list-style-position:inside;
}
ol#qunit-tests li ol{
box-shadow: inset 0px 2px 13px #999;
-moz-box-shadow: inset 0px 2px 13px #999;
-webkit-box-shadow: inset 0px 2px 13px #999;
margin-top:0.5em;
margin-left:0;
padding:0.5em;
background-color:#fff;
border-radius:15px;
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
}
ol#qunit-tests li li{
border-bottom:none;
margin:0.5em;
background-color:#fff;
list-style-position: inside;
padding:0.4em 0.5em 0.4em 0.5em;
}
ol#qunit-tests li li.pass{
border-left:26px solid #C6E746;
background-color:#fff;
color:#5E740B;
}
ol#qunit-tests li li.fail{
border-left:26px solid #EE5757;
background-color:#fff;
color:#710909;
}
ol#qunit-tests li.pass{
background-color:#D2E0E6;
color:#528CE0;
}
ol#qunit-tests li.fail{
background-color:#EE5757;
color:#000;
}
ol#qunit-tests li.pass span.test-name{
color: #366097;
}
ol#qunit-tests li.fail span.test-name{
color: #000000;
}
ol#qunit-tests li.pass span.module-name{
}
ol#qunit-tests li.fail span.module-name{
color: #000000;
}
ol#qunit-tests li li.fail span.test-actual {
color: #EE5757;
}
ol#qunit-tests li li.pass span.test-expected {
color: #999999;
}
ol#qunit-tests li li.fail span.test-expected {
color: green;
}
ol#qunit-tests li strong {
cursor:pointer;
}
h1#qunit-header{
background-color:#0d3349;
margin:0;
padding:0.5em 0 0.5em 1em;
color:#fff;
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
border-top-right-radius:15px;
border-top-left-radius:15px;
-moz-border-radius-topright:15px;
-moz-border-radius-topleft:15px;
-webkit-border-top-right-radius:15px;
-webkit-border-top-left-radius:15px;
text-shadow: rgba(0, 0, 0, 0.5) 4px 4px 1px;
}
h2#qunit-banner{
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
height:5px;
margin:0;
padding:0;
}
h2#qunit-banner.qunit-pass{
background-color:#C6E746;
}
h2#qunit-banner.qunit-fail, #qunit-testrunner-toolbar {
background-color:#EE5757;
}
#qunit-testrunner-toolbar {
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
padding:0;
/*width:80%;*/
padding:0em 0 0.5em 2em;
font-size: small;
}
h2#qunit-userAgent {
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
background-color:#2b81af;
margin:0;
padding:0;
color:#fff;
font-size: small;
padding:0.5em 0 0.5em 2.5em;
text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px;
}
p#qunit-testresult{
font-family:"Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial;
margin:0;
font-size: small;
color:#2b81af;
border-bottom-right-radius:15px;
border-bottom-left-radius:15px;
-moz-border-radius-bottomright:15px;
-moz-border-radius-bottomleft:15px;
-webkit-border-bottom-right-radius:15px;
-webkit-border-bottom-left-radius:15px;
background-color:#D2E0E6;
padding:0.5em 0.5em 0.5em 2.5em;
}
strong b.fail{
color:#710909;
}
strong b.pass{
color:#5E740B;
}
-1128
View File
File diff suppressed because it is too large Load Diff