There are <%= tasks.length %> tasks.
2 |
3 | <% for (var i in tasks) { %>
4 | <% var task = tasks[i]; %>
5 | ', e: 'this & that' });
59 | assert.equal(rendered, 'a <div> b this & that c');
60 | }
61 | };
62 |
--------------------------------------------------------------------------------
/tests/utilities.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 | var utilities = require('josi/utilities');
3 |
4 | this.name = 'Utilities Tests';
5 |
6 | this.tests = {
7 | 'Extension is stripped': function() {
8 | var result = utilities.stripExtension('index.html');
9 | assert.equal(result, 'index');
10 | },
11 | 'Extensionless filename is unchanged': function() {
12 | var result = utilities.stripExtension('index');
13 | assert.equal(result, 'index');
14 | },
15 | 'Trailing dot is stripped': function() {
16 | var result = utilities.stripExtension('index.');
17 | assert.equal(result, 'index');
18 | },
19 | 'Other dots in file ignored': function() {
20 | var result = utilities.stripExtension('index.2.html');
21 | assert.equal(result, 'index.2');
22 | },
23 | 'First argument is task': function() {
24 | var processed = utilities.processARGV(['', '', 'atask']);
25 | assert.equal(processed.task, 'atask');
26 | },
27 | 'Dashes are ignored': function() {
28 | var processed = utilities.processARGV(['', '', 'atask', '-a=b']);
29 | assert.equal(processed.opts.a, 'b');
30 | },
31 | 'Double dashes are ignored': function() {
32 | var processed = utilities.processARGV(['', '', 'atask', '--a=b']);
33 | assert.equal(processed.opts.a, 'b');
34 | },
35 | 'Dashes not required': function() {
36 | var processed = utilities.processARGV(['', '', 'atask', 'a=b']);
37 | assert.equal(processed.opts.a, 'b');
38 | },
39 | 'If not key value pair then it is an arg': function() {
40 | var processed = utilities.processARGV(['', '', 'atask', 'arg']);
41 | assert.equal(processed.args[0], 'arg');
42 | },
43 | 'Opt keys are case insensitive': function() {
44 | var processed = utilities.processARGV(['', '', 'atask', 'CaMel=b', 'c=HumPs', 'third=alongerthing']);
45 | assert.equal(processed.opts.camel, 'b');
46 | assert.equal(processed.opts.c, 'HumPs');
47 | assert.equal(processed.opts.third, 'alongerthing');
48 | },
49 | 'Opt values are case sensitive': function() {
50 | var processed = utilities.processARGV(['', '', 'atask', 'CaMel=b']);
51 | assert.equal(processed.opts.camel, 'b');
52 | },
53 | 'Args are case sensitive': function() {
54 | var processed = utilities.processARGV(['', '', 'atask', 'HumPs']);
55 | assert.equal(processed.args[0], 'HumPs');
56 | },
57 | 'Multiple opts work': function() {
58 | var processed = utilities.processARGV(['', '', 'atask', 'a=b', 'c=100', 'third=alongerthing']);
59 | assert.equal(processed.opts.a, 'b');
60 | assert.equal(processed.opts.c, '100');
61 | assert.equal(processed.opts.third, 'alongerthing');
62 | },
63 | 'Multiple args work': function() {
64 | var processed = utilities.processARGV(['', '', 'atask', 'arg1', 'arg2', 'arg3']);
65 | assert.equal(processed.args[0], 'arg1');
66 | assert.equal(processed.args[1], 'arg2');
67 | assert.equal(processed.args[2], 'arg3');
68 | },
69 | 'Merge doesn\'t touch arguments': function() {
70 | var a = { a: 1 };
71 | var b = { a: 2 };
72 | var result = utilities.merge(a, b);
73 | assert.equal(a.a, 1);
74 | assert.equal(Object.keys(a).length, 1);
75 | assert.equal(b.a, 2);
76 | assert.equal(Object.keys(b).length, 1);
77 | },
78 | 'Merge obeys ordering or arguments': function() {
79 | var a = { a: 1 };
80 | var b = { a: 2 };
81 | var result = utilities.merge(a, b);
82 | assert.equal(result.a, 2);
83 | }
84 | };
85 |
--------------------------------------------------------------------------------
/tests/controllerrouting.js:
--------------------------------------------------------------------------------
1 | var assert = require('assert');
2 |
3 | var test = require('josi/test');
4 | var routeresults = require('josi/routeresults');
5 | var ControllerRouter = require('josi/routing').ControllerRouter;
6 |
7 | this.name = 'Controller Router Tests';
8 |
9 | var createRouter = function(setRouterDefaults) {
10 | var factory = {
11 | getController: function(controllerName) {
12 | if (controllerName == 'missing') {
13 | throw new Error('Missing Controller');
14 | } else {
15 | return {
16 | index: test.createMockFunction(controllerName + ' controller, index action')
17 | };
18 | }
19 | }
20 | };
21 | var router = new ControllerRouter(factory);
22 | router.add(
23 | /^\/(?:(\w+)\/?)?(?:(\w+)\/?)?(?:(\w+)\/?)?$/,
24 | setRouterDefaults ?
25 | { controller: 'home', action: 'index' } :
26 | {}
27 | );
28 | return router;
29 | };
30 |
31 | this.tests = {
32 | 'Result is a RouteResult': function() {
33 | var router = createRouter();
34 | var result = router.route('/');
35 | assert.ok(result instanceof routeresults.RouteResult);
36 | },
37 | 'If no controller matched and no default then missing route': function() {
38 | var router = createRouter();
39 | var result = router.route('/');
40 | assert.ok(result instanceof routeresults.MissingRouteResult);
41 | },
42 | 'If no controller matched and no default then correct error message': function() {
43 | var router = createRouter();
44 | var result = router.route('/');
45 | assert.equal(result.message, 'No controller was matched in the url and there was no default controller specified.');
46 | },
47 | 'If no action matched and no default then missing route': function() {
48 | var router = createRouter();
49 | var result = router.route('/');
50 | assert.ok(result instanceof routeresults.MissingRouteResult);
51 | },
52 | 'If no controller matched fallback to default': function() {
53 | var router = createRouter(true);
54 | var result = router.route('/');
55 | assert.equal(result.action.getName(), 'home controller, index action');
56 | },
57 | 'If no action matched fallback to default': function() {
58 | var router = createRouter(true);
59 | var result = router.route('/product');
60 | assert.equal(result.action.getName(), 'product controller, index action');
61 | },
62 | 'If factory doesn\'t return controller then missing route': function() {
63 | var router = createRouter();
64 | var result = router.route('/missing');
65 | assert.ok(result instanceof routeresults.MissingRouteResult);
66 | },
67 | 'If factory doesn\'t return controller then correct error message': function() {
68 | var router = createRouter();
69 | var result = router.route('/missing');
70 | assert.equal(result.message, 'The "missing" controller is missing.');
71 | },
72 | 'If action missing from controller then missing route': function() {
73 | var router = createRouter();
74 | var result = router.route('/product/missing');
75 | assert.ok(result instanceof routeresults.MissingRouteResult);
76 | },
77 | 'If action missing from controller then correct error message': function() {
78 | var router = createRouter();
79 | var result = router.route('/product/missing');
80 | assert.equal(result.message, 'The "product" controller doesn\'t have an action called "missing"');
81 | },
82 | 'Result\'s context has controller set': function() {
83 | var router = createRouter(true);
84 | var result = router.route('/');
85 | assert.equal(result.route.controller, 'home');
86 | },
87 | 'Result\'s context has action set': function() {
88 | var router = createRouter(true);
89 | var result = router.route('/');
90 | assert.equal(result.route.action, 'index');
91 | },
92 | };
93 |
--------------------------------------------------------------------------------
/lib/josi/routing.js:
--------------------------------------------------------------------------------
1 | require('josi/class');
2 | var routeresults = require('josi/routeresults');
3 | var actionresults = require('josi/actionresults');
4 | var utilities = require('josi/utilities');
5 |
6 | this.FunctionRouter = FunctionRouter = Class.extend({
7 | init: function() {
8 | this.routes = [];
9 | },
10 | add: function(pattern, action, remap) {
11 | this.routes.push({ pattern: pattern, action: action, remap: remap || false });
12 | },
13 | addStatic: function(dir) {
14 | this.routes.push({
15 | pattern: new RegExp("/" + dir + "/([^?]*)(.*)?"),
16 | action: function() {
17 | return actionresults.content(dir + "/" + this.route[0]);
18 | },
19 | remap: false
20 | });
21 | },
22 | route: function(url) {
23 | for (var i in this.routes) {
24 | var route = this.routes[i];
25 | if (route.pattern instanceof RegExp) {
26 | var match = route.pattern.exec(url);
27 | if (match) {
28 | var context = match.slice(1);
29 | if (route.remap) {
30 | var mappingResult = route.action(context);
31 | if (mappingResult instanceof routeresults.RouteResult) {
32 | return mappingResult;
33 | } else {
34 | throw new Error('Remapped route from router.route must return an instance of RouteResult');
35 | }
36 | } else {
37 | return new routeresults.RouteResult(route.action, context);
38 | }
39 | }
40 | }
41 | }
42 | return routeresults.missingRoute();
43 | }
44 | });
45 |
46 | this.ControllerRouter = ControllerRouter = FunctionRouter.extend({
47 | init: function(factory) {
48 | this.factory = factory;
49 | this._super();
50 | },
51 | add: function(pattern, defaults) {
52 | var factory = this.factory;
53 | this._super(
54 | pattern,
55 | function(context) {
56 | var controller = context[0] || defaults.controller;
57 | var action = context[1] || defaults.action;
58 | if (!controller) {
59 | return routeresults.missingRoute('No controller was matched in the url and there was no default controller specified.');
60 | }
61 | try {
62 | var controllerObj = factory.getController(controller);
63 | } catch (e) {
64 | if (e.message && e.message === 'Missing Controller') {
65 | return routeresults.missingRoute('The "' + controller + '" controller is missing.');
66 | } else {
67 | return new routeresults.ErrorRouteResult(e);
68 | }
69 | }
70 | if (controllerObj[action]) {
71 | var result = controllerObj[action];
72 | context = context.slice(2);
73 | context.controller = controller;
74 | context.action = action;
75 | return new routeresults.RouteResult(result, context);
76 | } else {
77 | return routeresults.missingRoute('The "' + controller + '" controller doesn\'t have an action called "' + action + '"');
78 | }
79 | },
80 | true // remap
81 | );
82 | }
83 | });
84 |
85 | this.ControllerFactory = ControllerFactory = Class.extend();
86 |
87 | this.ModuleControllerFactory = ModuleControllerFactory = ControllerFactory.extend({
88 | init: function(dir) {
89 | this.dir = dir;
90 | },
91 | getController: function(controller) {
92 | var moduleDir = this.dir + '/controllers/' + controller;
93 | try {
94 | return require(moduleDir);
95 | } catch (e) {
96 | if (e.message && e.message === "Cannot find module '" + moduleDir + "'") {
97 | throw new Error('Missing Controller');
98 | } else {
99 | throw e;
100 | }
101 | }
102 | }
103 | });
104 |
--------------------------------------------------------------------------------
/lib/multipart-js/write.js:
--------------------------------------------------------------------------------
1 | // This API should be a symmetrical mirror of the writer API in writer.js
2 | // Instead of having onpartbegin and onpartend events, call the partBegin
3 | // and partEnd methods, and write file contents. Then, the writer emits
4 | // "data" events with chunks of data suitable for sending over the wire.
5 | // That is, it converts the data objects into one big string.
6 |
7 | // var w = writer();
8 | // w.boundary = "foo-bar-bazfj3980haf38h";
9 | // w.type = "form-data";
10 | // w.headers = {...};
11 | // w.partBegin({...}); // send the headers, causes the initial "data" event to emit
12 | // w.write("..."); // encode the data, wrap it, etc., and emit more "data" events
13 | // w.partEnd(); // close off that part, emitting a "data" event with the --boundary
14 | // w.partBegin({...}); // another part...
15 | // w.partBegin({...}); // if the last one was multipart, then do a child, else error.
16 | // w.partEnd(); // close off that child part
17 | // w.partEnd(); // close off the parent
18 | // w.close(); // close off all open parts, and emit "end" event
19 |
20 | var
21 | utils = require("./utils"),
22 | error = utils.error,
23 | emit = utils.emit,
24 | EVENTS = exports.EVENTS = ["onData", "onEnd", "onError"];
25 |
26 | var S = 0;
27 | exports.STATE =
28 | { STARTED : S++ //nothing received
29 | , PART_STARTED : S++ // a part header was written
30 | , WRITING : S++ // client is writing a part
31 | , PART_ENDED : S++ // a end part was written
32 | , CLOSED : S++ // close was called
33 | };
34 | for (S in exports.STATE) exports.STATE[exports.STATE[S]] = S;
35 | S = exports.STATE;
36 |
37 | exports.writer = writer;
38 | exports.Writer = Writer;
39 |
40 | function NYI () { throw new Error("Not yet implemented") }
41 |
42 | // Returns a new writer.
43 | // Attaches event handlers to it, and they'll get notified
44 | // call myWriter.write(chunk) and then myWriter.close() when it's done.
45 | function writer () { return new Writer() }
46 |
47 | function end (writer) {
48 | // close the whole stack of open parts, and emit "end"
49 | throw new Error("TODO");
50 | }
51 | function newPart (writer) {
52 | var p =
53 | { headers:{}
54 | , parent : writer.part
55 | };
56 | parent.parts = parent.parts || [];
57 | parent.parts.push(p);
58 | }
59 |
60 | function Writer () {
61 | this.firstPartReceived = false;
62 | this.state = S.STARTED;
63 | }
64 |
65 |
66 | // Writes a chunk to the multipart stream
67 | // Sets error if not called after a partBegin
68 | Writer.prototype.write = write;
69 | function write (chunk) {
70 | var writer = this;
71 | //ye old state machine
72 | if (chunk === null) return;
73 | if (writer.state !== S.PART_STARTED) {
74 | error(writer, "Illegal state. Must call partBegin before writing.");
75 | return;
76 | }
77 | // TODO - encode the data if base64 content-transfer-encoding
78 | emit(writer, "onData", chunk);
79 | }
80 |
81 | Writer.prototype.close = NYI;
82 |
83 | // Starts a part or nested part.
84 | // Emits data events to listeners with headers for part.
85 | // If first part, will emit http headers plus headers of first part.
86 | // Sets error if part is added to a part of type other than multipart,
87 | // or if new part is started before the old one is written correctly.
88 | //
89 | // Params: object describing header for event
90 | // e.g. { "content-type" : "text/plain", filename : "foo.txt" }
91 | Writer.prototype.partBegin = partBegin;
92 | function partBegin (partDesc) {
93 | var writer = this;
94 | if (!writer.boundary || writer.boundary.length < 1) {
95 | error(writer, "Missing boundary. Set boundary property.");
96 | return;
97 | }
98 | if (writer.state !== S.STARTED && writer.state !== S.PART_ENDED) {
99 | error(writer, "Illegal state. Cannot begin new part right now.");
100 | return;
101 | }
102 | else if (this.currentPart && !isMulti(this.currentPart)) {
103 | error(writer, "Bad format. Cannot add part to non multipart parent.");
104 | return;
105 | } else {
106 | // TODO - check for invalid state before adding the new part
107 | //newPart(writer);
108 | partChunk = "";
109 | if (!writer.firstPartReceived) {
110 | //write the http headers
111 | }
112 | // TODO - encode part headers based on partDesc
113 | emit(writer, "onData", "TODO: encoded chunk");
114 | }
115 | }
116 |
117 | Writer.prototype.partEnd = NYI;
118 |
119 |
--------------------------------------------------------------------------------
/lib/josi/templating.js:
--------------------------------------------------------------------------------
1 | var fs = require("fs");
2 | require('josi/class');
3 |
4 | var TemplatingEngine = Class.extend({
5 | init: function() {
6 | var cache = {};
7 | var self = this;
8 | this.load = function(path, cb) {
9 | if (cache[path]) {
10 | cb(cache[path]);
11 | } else {
12 | fs.readFile(path, 'utf8', function(err, data) {
13 | if (err) {
14 | // todo: deal with error when loading view
15 | throw err;
16 | }
17 | cache[path] = self.compile(data);
18 | cb(cache[path]);
19 | });
20 | }
21 | };
22 | },
23 | compile: function(str) {
24 | throw new Error('Not implemented error. To use create a subclass of TemplatingEngine that implements compile.');
25 | },
26 | render: function(data, callback, view, master) {
27 | var self = this;
28 | data = data || {};
29 | this.load(view, function(template) {
30 | try {
31 | var rendered = template(data);
32 | } catch (e) {
33 | callback(e);
34 | }
35 | if (master) {
36 | data.main = rendered;
37 | self.load(master, function(masterTemplate) {
38 | var rendered = masterTemplate(data);
39 | callback(null, rendered);
40 | });
41 | } else {
42 | callback(null, rendered);
43 | }
44 | });
45 | }
46 | });
47 |
48 | // JavaScript Micro-Templating
49 | // by John Resig - http://ejohn.org/ - MIT Licensed
50 | // http://ejohn.org/blog/javascript-micro-templating/
51 | // single quote fix by Neil Donewar - #comment-321850
52 | this.MicroTemplatingEngine = TemplatingEngine.extend({
53 | compile: function(str) {
54 | var unscopedTemplate = new Function("obj", "helpers",
55 | "var __ = { stack: [], helpers: helpers };" +
56 |
57 | // 'namespace' the view data if the user provided a namespace
58 | (this.namespace ? 'obj.' + this.namespace + ' = obj;' : '') +
59 |
60 | // Introduce the data as local variables using with(){}
61 | "with(obj){__.stack.push('" +
62 |
63 | // Convert the template into pure JavaScript
64 | str
65 | .replace(/[\r\t\n]/g, " ")
66 | .replace(/'(?=[^%]*%>)/g,"\t")
67 | .split("'").join("\\'")
68 | .split("\t").join("'")
69 | .replace(/<%=(.+?)%>/g, "',$1,'")
70 | .replace(/<%:(.+?)%>/g, "',__.helpers.escapeHTML($1),'")
71 | .split("<%").join("');")
72 | .split("%>").join("__.stack.push('") +
73 | "');}return __.stack.join('');"
74 | );
75 | return function(obj) {
76 | return unscopedTemplate(obj, { escapeHTML: escapeHTML });
77 | };
78 | }
79 | });
80 |
81 | var escapeHTML = function (str) {
82 | return str
83 | .replace(/&/g,'&')
84 | .replace(//g,'>');
86 | };
87 |
88 | // Adapted from jQuery Templating Plugin
89 | // Copyright 2010, John Resig
90 | // Dual licensed under the MIT or GPL Version 2 licenses.
91 | // http://github.com/jquery/jquery-tmpl/
92 | this.jQueryTemplatingEngine = TemplatingEngine.extend({
93 | compile: function(str) {
94 | var tmplcmd = {
95 | 'each': {
96 | _default: [null, "$i"],
97 | prefix: "for(var $2 in $1){",
98 | suffix: "}"
99 | },
100 | 'if': {
101 | prefix: "if($1){",
102 | suffix: "}"
103 | },
104 | 'else': {
105 | prefix: "}else{"
106 | },
107 | '=': {
108 | _default: ["this"],
109 | prefix: "_.push(typeof $1==='function'?$1.call(this):$1);"
110 | }
111 | };
112 | var fn = new Function("$data",
113 | "var _=[];_.data=$data;" +
114 |
115 | // Introduce the data as local variables using with(){}
116 | "with($data){_.push('" +
117 |
118 | // Convert the template into pure JavaScript
119 | str
120 | .replace(/[\r\t\n]/g, " ")
121 | .replace(/\${([^}]*)}/g, "{{= $1}}")
122 | .replace(/{{(\/?)(\w+|.)(?:\((.*?)\))?(?: (.*?))?}}/g, function(all, slash, type, fnargs, args) {
123 | var tmpl = tmplcmd[type];
124 |
125 | if (!tmpl) {
126 | throw "Template command not found: " + type;
127 | }
128 |
129 | var def = tmpl._default;
130 |
131 | return "');" + tmpl[slash ? "suffix" : "prefix"]
132 | .split("$1").join(args || (def ? def[0] : ''))
133 | .split("$2").join(fnargs || (def ? def[1] : '')) + "_.push('";
134 | })
135 | + "');}return _.join('');");
136 | return fn;
137 | }
138 | });
139 |
--------------------------------------------------------------------------------
/lib/josi/actionresults.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 |
3 | require('josi/class');
4 | var utilities = require('josi/utilities');
5 |
6 | this.ActionResult = ActionResult = Class.extend({
7 | init: function(body, headers, statusCode) {
8 | this.statusCode = statusCode || 200;
9 | this.headers = headers || [ ['Content-Type', 'text/html'] ];
10 | this.body = body;
11 | },
12 | execute: function(req, res) {
13 | res.writeHead(this.statusCode, this.headers);
14 | if (this.body) {
15 | res.write(this.body);
16 | }
17 | res.end();
18 | }
19 | });
20 |
21 | this.ViewResult = ViewResult = ActionResult.extend({
22 | init: function(data, action, controller) {
23 | this._super();
24 | this.data = data;
25 | this.context = {
26 | controller: controller,
27 | action: action
28 | };
29 | },
30 | execute: function(req, res, app) {
31 | var viewResult = this;
32 | var execute = this._super;
33 | app.templater.render(
34 | this.data,
35 | function(err, rendered) {
36 | if (err) {
37 | error(err).execute(req, res);
38 | return;
39 | }
40 | viewResult.body = rendered;
41 | execute.apply(viewResult, [req, res]);
42 | },
43 | this.view,
44 | this.master
45 | );
46 | }
47 | });
48 |
49 | this.ContentResult = ContentResult = ActionResult.extend({
50 | init: function(filename) {
51 | this._super(null, []);
52 | this.filename = filename;
53 | },
54 | execute: function(req, res) {
55 | var self = this;
56 | var filename = this.filename;
57 | fs.stat(filename, function(err, stat) {
58 | if (err) {
59 | if (err.errno && err.errno === 2) {
60 | notFound('Static content not found.').execute(req, res);
61 | return;
62 | }
63 | }
64 | var etag = Number(stat.mtime);
65 | if (req.headers['if-none-match'] &&
66 | req.headers['if-none-match'] == etag) {
67 | notModified().execute(req, res);
68 | return;
69 | }
70 | self.headers.push(['Content-Length', stat.size]);
71 | self.headers.push(['ETag', etag]);
72 | var first = true;
73 | var stream = fs.createReadStream(filename);
74 | stream
75 | .addListener('error', function(err) {
76 | if (first) {
77 | return error(err).execute(req, res);
78 | }
79 | stream.destroy();
80 | req.end();
81 | })
82 | .addListener('data', function(data){
83 | if (first) {
84 | first = false;
85 | var contentType = utilities.mime.lookup(utilities.extension(filename));
86 | self.headers.push(['Transfer-Encoding', 'chunked']);
87 | self.headers.push(['Content-Type', contentType]);
88 | res.writeHead(200, self.headers);
89 | }
90 | res.write(data, 'binary');
91 | })
92 | .addListener('end', function() {
93 | res.end();
94 | });
95 | });
96 | }
97 | });
98 |
99 | this.AsyncResult = AsyncResult = ActionResult.extend({
100 | init: function(func) {
101 | this._super();
102 | this.func = func;
103 | },
104 | execute: function(req, res) {
105 | res.writeHead(this.statusCode, this.headers);
106 | this.func(req, res);
107 | }
108 | });
109 |
110 | this.async = function(func) {
111 | return new AsyncResult(func);
112 | };
113 |
114 | this.view = function(data, action, controller) {
115 | return new ViewResult(data, action, controller);
116 | };
117 |
118 | this.redirect = function(url) {
119 | return new ActionResult('Redirecting...', [ ['Content-Type', 'text/html'], ['Location', url] ], 301);
120 | };
121 |
122 | this.notFound = notFound = function(msg) {
123 | return new ActionResult(msg || 'Not found.', [ ['Content-Type', 'text/html'] ], 404);
124 | };
125 |
126 | this.notModified = notModified = function() {
127 | return new ActionResult(null, [], 304);
128 | };
129 |
130 | this.error = error = function(err, httpStatusCode) {
131 | var msg = err instanceof Error ? err.message + '\r\n' + err.stack : err;
132 | return new ActionResult(msg, [ ['Content-Type', 'text/plain'] ], httpStatusCode || 500);
133 | };
134 |
135 | this.raw = function(data) {
136 | return new ActionResult(data, [ ['Content-Type', 'text/plain'] ]);
137 | };
138 |
139 | this.json = function(data) {
140 | return new ActionResult(JSON.stringify(data), [ ['Content-Type', 'application/json'] ]);
141 | };
142 |
143 | this.content = function(filename) {
144 | return new ContentResult(filename);
145 | };
146 |
--------------------------------------------------------------------------------
/lib/dirty/dirty.js:
--------------------------------------------------------------------------------
1 | var
2 | dirty = exports,
3 | events = require('events'),
4 | fs = require('fs');
5 |
6 | dirty.uuid = function() {
7 | var uuid = '', i;
8 | for (i = 0; i < 32; i++) {
9 | uuid += Math.floor(Math.random() * 16).toString(16);
10 | }
11 | return uuid;
12 | };
13 |
14 | var Dirty = dirty.Dirty = function(filePath, options) {
15 | process.EventEmitter.call(this);
16 |
17 | options = options || {};
18 | options.flushInterval = options.flushInterval || 10;
19 | options.flushLimit = options.flushLimit || 1000;
20 |
21 | var
22 | self = this,
23 | docs = {},
24 | log = [],
25 | loading = false,
26 | flushing = false,
27 | readStream = fs.createReadStream(filePath),
28 | writeStream = fs.createWriteStream(filePath, {flags: 'a+'}),
29 | readBuffer = '',
30 | timer;
31 |
32 | readStream
33 | .addListener('error', function(err) {
34 | // No such file or directory
35 | if (err.errno == 2) {
36 | self.emit('load');
37 | return;
38 | }
39 | self.emit('error', err);
40 | })
41 | .addListener('data', function(chunk) {
42 | var
43 | offset,
44 | chunk,
45 | doc;
46 |
47 | readBuffer += chunk;
48 |
49 | while ((offset = readBuffer.indexOf("\n")) > -1) {
50 | chunk = readBuffer.substr(0, offset);
51 | readBuffer = readBuffer.substr(offset+1);
52 |
53 | try {
54 | doc = JSON.parse(chunk);
55 | } catch (e) {
56 | continue;
57 | }
58 |
59 | if (doc.deleted) {
60 | delete docs[doc.id];
61 | } else {
62 | docs[doc.id] = doc.value;
63 | }
64 | }
65 | })
66 | .addListener('end', function() {
67 | self.emit('load');
68 | });
69 |
70 | writeStream
71 | .addListener('drain', function() {
72 | if (!log.length) {
73 | self.emit('drain');
74 | }
75 | })
76 | .addListener('error', function(err) {
77 | self.emit('error', err);
78 | });
79 |
80 | this.add = function(doc, cb) {
81 | var id = dirty.uuid();
82 | this.set(id, doc, cb);
83 | return id;
84 | };
85 |
86 | this.set = function(id, doc, cb) {
87 | if (doc !== undefined) {
88 | docs[id] = doc;
89 | } else {
90 | delete docs[id];
91 | }
92 |
93 | log.push(!cb ? id : [id, cb]);
94 |
95 | if (loading === true) {
96 | // Do not flush while loading
97 | } else if (options.flushLimit && !flushing && log.length >= options.flushLimit) {
98 | this.flush();
99 | if (timer) {
100 | clearTimeout(timer);
101 | }
102 | } else if (options.flushInterval && !flushing && !timer) {
103 | timer = setTimeout(function() {
104 | self.flush();
105 | timer = null;
106 | }, options.flushInterval);
107 | }
108 | };
109 |
110 | this.get = function(id) {
111 | return docs[id];
112 | };
113 |
114 | this.remove = function(id, cb) {
115 | this.set(id, undefined, cb);
116 | };
117 |
118 | this.flush = function(flushCb) {
119 | flushing = true;
120 |
121 | var
122 | chunkCallbacks = [],
123 | chunk = '',
124 | id;
125 |
126 | while (true) {
127 | id = log.shift();
128 | if (id === undefined) {
129 | break;
130 | }
131 |
132 | if (id instanceof Array) {
133 | chunkCallbacks.push(id[1]);
134 | id = id[0];
135 | }
136 |
137 | if (id in docs) {
138 | chunk += JSON.stringify({id: id, value: docs[id]})+"\n";
139 | } else {
140 | chunk += JSON.stringify({id: id, deleted: true})+"\n";
141 | }
142 |
143 | if (chunk.length < 16 * 1024 && log.length) {
144 | continue;
145 | }
146 |
147 | writeStream.write(chunk, function(err) {
148 | chunkCallbacks.forEach(function(cb) {
149 | cb(err);
150 | });
151 | callbacks = [];
152 |
153 | if (err) {
154 | if (flushCb) {
155 | flushCb(err);
156 | }
157 | self.emit('error', err);
158 | }
159 |
160 | flushing = false;
161 |
162 | if (log.length) {
163 | self.flush();
164 | } else if (flushCb) {
165 | cb(null);
166 | }
167 | });
168 | }
169 | };
170 |
171 | this.filter = function(fn) {
172 | var ret = [];
173 | for (var key in docs) if (docs.hasOwnProperty(key)) {
174 | if (fn(docs[key])) ret.push(docs[key]);
175 | }
176 | return ret;
177 | }
178 |
179 | };
180 |
181 | // from the old sys module
182 | var inherits = function (ctor, superCtor) {
183 | var tempCtor = function(){};
184 | tempCtor.prototype = superCtor.prototype;
185 | ctor.super_ = superCtor;
186 | ctor.prototype = new tempCtor();
187 | ctor.prototype.constructor = ctor;
188 | }
189 |
190 | inherits(Dirty, events.EventEmitter);
191 |
--------------------------------------------------------------------------------
/lib/josi/tasks/create.js:
--------------------------------------------------------------------------------
1 | var sys = require('sys');
2 | var fs = require('fs');
3 |
4 | var utilities = require('josi/utilities');
5 |
6 | this.task = {
7 | name: 'create',
8 | doc: 'create a new josi app or components of an existing josi app',
9 | execute: function(opts, args) {
10 | if (args.length < 2) {
11 | var appName = opts.name || args[0];
12 | if (!appName) {
13 | sys.puts('ERROR: When creating a josi app you must specify its name.');
14 | return;
15 | }
16 | if (utilities.fileOrDirectoryExists(appName)) {
17 | sys.puts('ERROR: Can\'t create a josi app named "' + appName + '",' +
18 | ' as a file or directory with that name already exists.');
19 | return;
20 | }
21 | sys.puts('Creating app...');
22 | createApp(appName);
23 | sys.puts('App created. Type "cd ' + appName + '; josi run" to start the web server.');
24 | } else {
25 | if (!utilities.cwdContainsApp()) {
26 | sys.puts('ERROR: The creation of josi components can only be done within a josi app.');
27 | return;
28 | }
29 | var component = args[0].toLowerCase();
30 | switch (component) {
31 | case 'controller':
32 | sys.puts('Creating controller...');
33 | createController('.', args[1]);
34 | createView('.', args[1], 'index');
35 | sys.puts('Controller created.');
36 | break;
37 | case 'action':
38 | if (args.length != 3) {
39 | sys.puts('ERROR: The name of the action to be created must be specified.');
40 | return;
41 | }
42 | if (!utilities.fileOrDirectoryExists('controllers/' + args[1] + '.js')) {
43 | sys.puts('ERROR: The "' + args[1] + '" controller doesn\'t exist.');
44 | return;
45 | }
46 | sys.puts('Creating action...');
47 | createView('.', args[1], args[2]);
48 | addActionToController(args[1], args[2]);
49 | sys.puts('Action created.');
50 | break;
51 | // todo: case 'test':
52 | default:
53 | sys.puts('ERROR: "' + args[1] + '" is not a valid josi component to create.');
54 | return;
55 | }
56 | }
57 | }
58 | };
59 |
60 | var createApp = function(appName) {
61 | fs.mkdirSync(appName, 0777);
62 | fs.writeFileSync(appName + '/app.js',
63 | [ 'this.init = function() {',
64 | ' this.router.add(',
65 | ' // this route matches: /
//',
66 | ' /^\\/(?:(\\w+)\\/?)?(?:(\\w+)\\/?)?(?:([0-9]+)\\/?)?$/,',
67 | ' { controller: \'home\', action: \'index\' }',
68 | ' );',
69 | '};', ''
70 | ].join('\r\n')
71 | );
72 | createController(appName, 'home');
73 | fs.mkdirSync(appName + '/views', 0777);
74 | createViewMaster(appName);
75 | createView(appName, 'home', 'index');
76 | };
77 |
78 | var createController = function(appName, controllerName) {
79 | var controllersDir = appName + '/controllers';
80 | if (!utilities.fileOrDirectoryExists(controllersDir)) {
81 | fs.mkdirSync(controllersDir, 0777);
82 | }
83 | var filename = appName + '/controllers/' + controllerName + '.js';
84 | if (utilities.fileOrDirectoryExists(filename)) {
85 | throw new Error('Controller already exists');
86 | }
87 | fs.writeFileSync(filename,
88 | [ 'var view = require(\'josi/actionresults\').view;',
89 | '',
90 | 'this.index = function() {',
91 | ' return view({',
92 | ' title: \'' + (appName == '.' ? 'A josi app' : appName + ' - a josi app') + '\',',
93 | ' controller: \'' + controllerName + '\',',
94 | ' action: \'index\',',
95 | ' description: \'' + (appName == '.' ? 'This' : appName) + ' is a josi app\'',
96 | ' });',
97 | '};', ''
98 | ].join('\r\n')
99 | );
100 | };
101 |
102 | var createView = function(appName, controllerName, viewName) {
103 | var viewsDir = appName + '/views';
104 | if (!utilities.fileOrDirectoryExists(viewsDir)) {
105 | fs.mkdirSync(viewsDir, 0777);
106 | }
107 | viewsDir = viewsDir + '/' + controllerName;
108 | if (!utilities.fileOrDirectoryExists(viewsDir)) {
109 | fs.mkdirSync(viewsDir, 0777);
110 | }
111 | var filename = appName + '/views/' + controllerName + '/' + viewName + '.html';
112 | if (utilities.fileOrDirectoryExists(filename)) {
113 | throw new Error('View already exists');
114 | }
115 | fs.writeFileSync(filename,
116 | [ 'Controller: <%= controller %>
',
117 | 'Action: <%= action %>
',
118 | '<%= description %>
', ''
119 | ].join('\r\n')
120 | );
121 | };
122 |
123 | var createViewMaster = function(appName) {
124 | fs.writeFileSync(appName + '/views/master.html',
125 | [ '',
126 | ' ',
127 | ' <%= title %>',
128 | ' ',
129 | ' ',
130 | ' <%= title %>
',
131 | ' <%= main %>',
132 | ' ',
133 | '', ''
134 | ].join('\r\n')
135 | );
136 | };
137 |
138 | var addActionToController = function(controllerName, actionName) {
139 | var writeStream = fs.createWriteStream('controllers/' + controllerName + '.js', { flags: 'a' });
140 | writeStream.write(
141 | [ '',
142 | 'this.' + actionName + ' = function() {',
143 | ' return view({',
144 | ' title: \'A josi app\',',
145 | ' controller: \'' + controllerName + '\',',
146 | ' action: \'' + actionName + '\',',
147 | ' description: \'This is a josi app\'',
148 | ' });',
149 | '};', ''
150 | ].join('\r\n')
151 | );
152 | writeStream.end();
153 | };
154 |
--------------------------------------------------------------------------------
/lib/josi/server.js:
--------------------------------------------------------------------------------
1 | var http = require('http');
2 | var url = require('url');
3 | var querystring = require('querystring');
4 |
5 | var actionresults = require('josi/actionresults');
6 | var routing = require('josi/routing');
7 | var templating = require('josi/templating');
8 | var utilities = require('josi/utilities');
9 | var multipart = require('multipart-js/multipart');
10 |
11 | this.Server = function(dir) {
12 | var app = require(dir + '/app');
13 | app.settings = utilities.merge(
14 | {
15 | maxContentLength: 100 * 1024 // 100KB
16 | },
17 | app.settings
18 | );
19 | if (!app.router) {
20 | var controllerFactory = new routing.ModuleControllerFactory(dir);
21 | app.router = new routing.ControllerRouter(controllerFactory);
22 | }
23 | if (!app.templater) {
24 | app.templater = new templating.MicroTemplatingEngine();
25 | }
26 | app.init();
27 |
28 | var processActionResult = function(actionResult, context) {
29 | if (actionResult instanceof actionresults.ActionResult) {
30 | if (actionResult instanceof actionresults.ViewResult) {
31 | actionResult.view = 'views/' + (actionResult.context.controller || context.route.controller) +
32 | '/' + (actionResult.context.action || context.route.action) + '.html';
33 | actionResult.master = 'views/master.html';
34 | }
35 | return actionResult;
36 | } else if (actionResult instanceof Object) {
37 | return actionresults.json(actionResult);
38 | } else if (typeof actionResult === 'string') {
39 | return actionresults.raw(actionResult);
40 | } else {
41 | // todo: better error message when action result not instance of ActionResult
42 | return actionresults.error('Bad action result.');
43 | }
44 | };
45 |
46 | var parseCookies = function(header) {
47 | var cookies = {};
48 | if (header) {
49 | header
50 | .split(';')
51 | .forEach(function(cookie) {
52 | var pair = cookie.split('=');
53 | pair = pair.map(function(p) { return p.trim(); });
54 | pair.length === 1 ?
55 | cookies[''] = pair[0] :
56 | cookies[pair[0]] = pair[1];
57 | });
58 | }
59 | return cookies;
60 | };
61 |
62 | var server = http.createServer(function(req, res) {
63 | var parsedUrl = url.parse(req.url);
64 | var cookie = (function() {
65 | var reqCookies = parseCookies(req.headers['cookie']);
66 | var resCookies = {};
67 | return {
68 | get: function(key) {
69 | return reqCookies[key];
70 | },
71 | // todo: enable cookie expiry, domain, path & secure
72 | set: function(key, value) {
73 | resCookies[key] = value;
74 | },
75 | toHeaders: function() {
76 | var cookies = [];
77 | for (var key in resCookies) {
78 | cookies.push(key + '=' + resCookies[key] + '; path=/;');
79 | }
80 | return cookies;
81 | }
82 | // todo: enable cookie removal
83 | };
84 | })();
85 | var routeResult = app.router.route(parsedUrl.pathname);
86 | var actionContext = {
87 | route: routeResult.route || {},
88 | query: querystring.parse(parsedUrl.query),
89 | form: {},
90 | files: {},
91 | cookie: cookie
92 | };
93 | var callback = function(err) {
94 | if (err) {
95 | actionresults.error(err, 413).execute(req, res);
96 | return;
97 | }
98 | actionContext.params = utilities.merge(actionContext.query, actionContext.form);
99 | if (actionContext.route.controller) {
100 | actionContext.params.controller = actionContext.route.controller;
101 | }
102 | if (actionContext.route.action) {
103 | actionContext.params.action = actionContext.route.action;
104 | }
105 | try {
106 | var actionResult;
107 | if (routeResult instanceof RouteResult) {
108 | if (routeResult instanceof MissingRouteResult) {
109 | actionResult = actionresults.notFound(routeResult.message || 'No route matched the url: ' + parsedUrl.pathname);
110 | } else if (routeResult instanceof ErrorRouteResult) {
111 | actionResult = actionresults.error(routeResult.error);
112 | } else {
113 | actionResult = routeResult.action.apply(actionContext);
114 | }
115 | } else {
116 | throw new Error('Result from router not an instance of RouteResult.');
117 | }
118 | actionResult = processActionResult(actionResult, actionContext);
119 | var cookieHeaders = cookie.toHeaders();
120 | if (cookieHeaders) {
121 | cookieHeaders.forEach(function(header) {
122 | actionResult.headers.push(['Set-Cookie', header]);
123 | });
124 | }
125 | actionResult.execute(req, res, app);
126 | } catch (e) {
127 | actionresults.error(e).execute(req, res);
128 | }
129 | };
130 | var contentType = req.headers['content-type'] || req.headers['Content-Type'];
131 | var contentLength = parseInt(req.headers['content-length'] || req.headers['Content-Length'], 10);
132 | if (contentLength > app.settings.maxContentLength) {
133 | callback(new Error('Max content length exceeded'));
134 | return;
135 | }
136 | if (contentType && /multipart\/form-data/.test(contentType)) {
137 | var currentPart;
138 | var parser = multipart.parser();
139 | parser.headers = req.headers;
140 | parser.onPartBegin = function(part) {
141 | currentPart = {
142 | name: part.name,
143 | filename: part.filename,
144 | data: ''
145 | };
146 | };
147 | parser.onPartEnd = function(part) {
148 | if (part) {
149 | if (currentPart.filename) {
150 | actionContext.files[currentPart.name] = currentPart;
151 | } else {
152 | actionContext.form[currentPart.name] = currentPart.data;
153 | }
154 | }
155 | };
156 | parser.onData = function(data) {
157 | // todo: write to tmp file, rather than buffering
158 | currentPart.data += data;
159 | };
160 | req.addListener('data', function(chunk) {
161 | parser.write(chunk);
162 | });
163 | req.addListener('end', function() {
164 | parser.close();
165 | callback();
166 | });
167 | } else {
168 | var postedBody = '';
169 | req.addListener('data', function(chunk) {
170 | postedBody += chunk;
171 | });
172 | req.addListener('end', function() {
173 | actionContext.form = querystring.parse(postedBody);
174 | callback();
175 | });
176 | }
177 | req.addListener('error', function(error) {
178 | // todo: deal with request error
179 | });
180 | });
181 |
182 | this.listen = function(port) {
183 | server.listen(port);
184 | };
185 | };
186 |
--------------------------------------------------------------------------------
/lib/josi/utilities.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var path = require('path');
3 |
4 | this.merge = function() {
5 | var result = {};
6 | for (var a in arguments) {
7 | var arg = arguments[a];
8 | for (var i in arg) {
9 | result[i] = arg[i];
10 | }
11 | }
12 | return result;
13 | };
14 |
15 | this.argumentsToArray = function(args) {
16 | var arr = [];
17 | for (var a in args) {
18 | arr.push(args[a]);
19 | }
20 | return arr;
21 | };
22 |
23 | this.fileOrDirectoryExists = fileOrDirectoryExists = function(fileOrDirectory) {
24 | try {
25 | fs.statSync(fileOrDirectory);
26 | return true;
27 | } catch (e) {
28 | if (e.errno && e.errno == 2) {
29 | return false;
30 | } else {
31 | throw e;
32 | }
33 | }
34 | };
35 |
36 | this.cwdContainsApp = function() {
37 | return fileOrDirectoryExists(path.join(process.cwd(), 'app.js'));
38 | };
39 |
40 | this.processARGV = function(argv) {
41 | var task = (argv[2] && /^\w+$/.test(argv[2])) ? argv[2] : '';
42 | var optsRE = /-{0,2}(\w+)=(\w+)/;
43 | var opts = {};
44 | var args = [];
45 | argv
46 | .slice(3)
47 | .forEach(function(arg) {
48 | var match = optsRE.exec(arg);
49 | if (match) {
50 | opts[match[1].toLowerCase()] = match[2];
51 | } else {
52 | args.push(arg);
53 | }
54 | });
55 | return {
56 | task: task,
57 | opts: opts,
58 | args: args
59 | };
60 | };
61 |
62 | this.stripExtension = function(filename) {
63 | var index = filename.lastIndexOf('.');
64 | return index < 0 ? filename : filename.substring(0, index);
65 | };
66 |
67 | this.extension = function (filename) {
68 | var index = filename.lastIndexOf('.');
69 | return index < 0 ? '' : filename.substring(index);
70 | };
71 |
72 | this.mime = mime = {
73 | lookup: function(ext, fallback) {
74 | return mime.types[ext.toLowerCase()] || fallback || mime.defaultType;
75 | },
76 | defaultType: 'application/octet-stream',
77 | types: {
78 | ".3gp" : "video/3gpp",
79 | ".a" : "application/octet-stream",
80 | ".ai" : "application/postscript",
81 | ".aif" : "audio/x-aiff",
82 | ".aiff" : "audio/x-aiff",
83 | ".asc" : "application/pgp-signature",
84 | ".asf" : "video/x-ms-asf",
85 | ".asm" : "text/x-asm",
86 | ".asx" : "video/x-ms-asf",
87 | ".atom" : "application/atom+xml",
88 | ".au" : "audio/basic",
89 | ".avi" : "video/x-msvideo",
90 | ".bat" : "application/x-msdownload",
91 | ".bin" : "application/octet-stream",
92 | ".bmp" : "image/bmp",
93 | ".bz2" : "application/x-bzip2",
94 | ".c" : "text/x-c",
95 | ".cab" : "application/vnd.ms-cab-compressed",
96 | ".cc" : "text/x-c",
97 | ".chm" : "application/vnd.ms-htmlhelp",
98 | ".class" : "application/octet-stream",
99 | ".com" : "application/x-msdownload",
100 | ".conf" : "text/plain",
101 | ".cpp" : "text/x-c",
102 | ".crt" : "application/x-x509-ca-cert",
103 | ".css" : "text/css",
104 | ".csv" : "text/csv",
105 | ".cxx" : "text/x-c",
106 | ".deb" : "application/x-debian-package",
107 | ".der" : "application/x-x509-ca-cert",
108 | ".diff" : "text/x-diff",
109 | ".djv" : "image/vnd.djvu",
110 | ".djvu" : "image/vnd.djvu",
111 | ".dll" : "application/x-msdownload",
112 | ".dmg" : "application/octet-stream",
113 | ".doc" : "application/msword",
114 | ".dot" : "application/msword",
115 | ".dtd" : "application/xml-dtd",
116 | ".dvi" : "application/x-dvi",
117 | ".ear" : "application/java-archive",
118 | ".eml" : "message/rfc822",
119 | ".eps" : "application/postscript",
120 | ".exe" : "application/x-msdownload",
121 | ".f" : "text/x-fortran",
122 | ".f77" : "text/x-fortran",
123 | ".f90" : "text/x-fortran",
124 | ".flv" : "video/x-flv",
125 | ".for" : "text/x-fortran",
126 | ".gem" : "application/octet-stream",
127 | ".gemspec": "text/x-script.ruby",
128 | ".gif" : "image/gif",
129 | ".gz" : "application/x-gzip",
130 | ".h" : "text/x-c",
131 | ".hh" : "text/x-c",
132 | ".htm" : "text/html",
133 | ".html" : "text/html",
134 | ".ico" : "image/x-icon",
135 | ".ics" : "text/calendar",
136 | ".ifb" : "text/calendar",
137 | ".iso" : "application/octet-stream",
138 | ".jar" : "application/java-archive",
139 | ".java" : "text/x-java-source",
140 | ".jnlp" : "application/x-java-jnlp-file",
141 | ".jpeg" : "image/jpeg",
142 | ".jpg" : "image/jpeg",
143 | ".js" : "application/javascript",
144 | ".json" : "application/json",
145 | ".log" : "text/plain",
146 | ".m3u" : "audio/x-mpegurl",
147 | ".m4v" : "video/mp4",
148 | ".man" : "text/troff",
149 | ".mathml" : "application/mathml+xml",
150 | ".mbox" : "application/mbox",
151 | ".mdoc" : "text/troff",
152 | ".me" : "text/troff",
153 | ".mid" : "audio/midi",
154 | ".midi" : "audio/midi",
155 | ".mime" : "message/rfc822",
156 | ".mml" : "application/mathml+xml",
157 | ".mng" : "video/x-mng",
158 | ".mov" : "video/quicktime",
159 | ".mp3" : "audio/mpeg",
160 | ".mp4" : "video/mp4",
161 | ".mp4v" : "video/mp4",
162 | ".mpeg" : "video/mpeg",
163 | ".mpg" : "video/mpeg",
164 | ".ms" : "text/troff",
165 | ".msi" : "application/x-msdownload",
166 | ".odp" : "application/vnd.oasis.opendocument.presentation",
167 | ".ods" : "application/vnd.oasis.opendocument.spreadsheet",
168 | ".odt" : "application/vnd.oasis.opendocument.text",
169 | ".ogg" : "application/ogg",
170 | ".p" : "text/x-pascal",
171 | ".pas" : "text/x-pascal",
172 | ".pbm" : "image/x-portable-bitmap",
173 | ".pdf" : "application/pdf",
174 | ".pem" : "application/x-x509-ca-cert",
175 | ".pgm" : "image/x-portable-graymap",
176 | ".pgp" : "application/pgp-encrypted",
177 | ".pkg" : "application/octet-stream",
178 | ".pl" : "text/x-script.perl",
179 | ".pm" : "text/x-script.perl-module",
180 | ".png" : "image/png",
181 | ".pnm" : "image/x-portable-anymap",
182 | ".ppm" : "image/x-portable-pixmap",
183 | ".pps" : "application/vnd.ms-powerpoint",
184 | ".ppt" : "application/vnd.ms-powerpoint",
185 | ".ps" : "application/postscript",
186 | ".psd" : "image/vnd.adobe.photoshop",
187 | ".py" : "text/x-script.python",
188 | ".qt" : "video/quicktime",
189 | ".ra" : "audio/x-pn-realaudio",
190 | ".rake" : "text/x-script.ruby",
191 | ".ram" : "audio/x-pn-realaudio",
192 | ".rar" : "application/x-rar-compressed",
193 | ".rb" : "text/x-script.ruby",
194 | ".rdf" : "application/rdf+xml",
195 | ".roff" : "text/troff",
196 | ".rpm" : "application/x-redhat-package-manager",
197 | ".rss" : "application/rss+xml",
198 | ".rtf" : "application/rtf",
199 | ".ru" : "text/x-script.ruby",
200 | ".s" : "text/x-asm",
201 | ".sgm" : "text/sgml",
202 | ".sgml" : "text/sgml",
203 | ".sh" : "application/x-sh",
204 | ".sig" : "application/pgp-signature",
205 | ".snd" : "audio/basic",
206 | ".so" : "application/octet-stream",
207 | ".svg" : "image/svg+xml",
208 | ".svgz" : "image/svg+xml",
209 | ".swf" : "application/x-shockwave-flash",
210 | ".t" : "text/troff",
211 | ".tar" : "application/x-tar",
212 | ".tbz" : "application/x-bzip-compressed-tar",
213 | ".tcl" : "application/x-tcl",
214 | ".tex" : "application/x-tex",
215 | ".texi" : "application/x-texinfo",
216 | ".texinfo" : "application/x-texinfo",
217 | ".text" : "text/plain",
218 | ".tif" : "image/tiff",
219 | ".tiff" : "image/tiff",
220 | ".torrent" : "application/x-bittorrent",
221 | ".tr" : "text/troff",
222 | ".txt" : "text/plain",
223 | ".vcf" : "text/x-vcard",
224 | ".vcs" : "text/x-vcalendar",
225 | ".vrml" : "model/vrml",
226 | ".war" : "application/java-archive",
227 | ".wav" : "audio/x-wav",
228 | ".wma" : "audio/x-ms-wma",
229 | ".wmv" : "video/x-ms-wmv",
230 | ".wmx" : "video/x-ms-wmx",
231 | ".wrl" : "model/vrml",
232 | ".wsdl" : "application/wsdl+xml",
233 | ".xbm" : "image/x-xbitmap",
234 | ".xhtml" : "application/xhtml+xml",
235 | ".xls" : "application/vnd.ms-excel",
236 | ".xml" : "application/xml",
237 | ".xpm" : "image/x-xpixmap",
238 | ".xsl" : "application/xml",
239 | ".xslt" : "application/xslt+xml",
240 | ".yaml" : "text/yaml",
241 | ".yml" : "text/yaml",
242 | ".zip" : "application/zip"
243 | }
244 | };
245 |
--------------------------------------------------------------------------------
/lib/multipart-js/parse.js:
--------------------------------------------------------------------------------
1 |
2 | var utils = require("./utils")
3 | , error = utils.error
4 | , emit = utils.emit
5 | , wrapExpression = /^[ \t]+/
6 | , multipartExpression = new RegExp(
7 | "^multipart\/(" +
8 | "mixed|rfc822|message|digest|alternative|" +
9 | "related|report|signed|encrypted|form-data|" +
10 | "x-mixed-replace|byteranges)", "i")
11 | , boundaryExpression = /boundary=([^;]+)/i
12 | , CR = "\r"
13 | , LF = "\n"
14 | , CRLF = CR+LF
15 | , MAX_BUFFER_LENGTH = 16 * 1024
16 |
17 | // parser states.
18 | var S = 0
19 | exports.STATE =
20 | { NEW_PART : S++
21 | , HEADER : S++
22 | , BODY : S++
23 | };
24 | for (S in exports.STATE) exports.STATE[exports.STATE[S]] = S;
25 | S = exports.STATE;
26 |
27 | // events for discoverability
28 | exports.EVENTS = [ "onPartBegin", "onPartEnd", "onData", "onEnd", "onError" ];
29 |
30 | exports.parser = function parser () { return new Parser() }
31 | exports.Parser = Parser;
32 |
33 | // the parser's "parts" object is a nested collection of the header objects
34 | // check the parser's "part" member to know what it's currently chewin on.
35 | // this.part.parent refers to that part's containing message (which may be
36 | // the parser itself)
37 | // child messages inherit their parent's headers
38 | function Parser () {
39 | this.buffer = "";
40 | this.part = this;
41 | this.state = S.NEW_PART;
42 | // handy for debugging bad input
43 | this.position = this.column = this.line = 0;
44 | this.parent = this;
45 | this.type = this.headers = this.isMultiPart = this.boundary = null;
46 | }
47 |
48 | Parser.prototype.write = function (chunk) {
49 | // sys.debug("write: "+chunk);
50 | var parser = this
51 | , part = parser.part
52 | // write to the buffer, and then process the buffer.
53 | parser.buffer += chunk;
54 |
55 | while (parser.buffer) {
56 | switch (parser.state) {
57 | case S.NEW_PART:
58 | // part is a multipart message.
59 | // we're either going to start reading a new part, or we're going to
60 | // end the current part, depending on whether the boundary has -- at
61 | // the end. either way, we expect --boundary right away.
62 | if (!parser.part.isMultiPart) {
63 | multipartHeaders(parser.part);
64 | }
65 | if (!parser.part.isMultiPart) {
66 | error(parser, "Expected multipart message (did you set the headers?)");
67 | }
68 | var boundary = parser.part.boundary
69 | , len = boundary.length
70 | , offset = parser.buffer.indexOf(boundary)
71 | if (offset === -1) {
72 | if (parser.buffer.length > len) {
73 | error(parser, "Malformed: boundary not found at start of message");
74 | }
75 | // keep waiting for it.
76 | return;
77 | }
78 | if (offset > 0) {
79 | error(parser, "Malformed: data before the boundary");
80 | return;
81 | }
82 | if (parser.buffer.length < (len + 2)) {
83 | // we'll need to see either -- or CRLF after the boundary.
84 | // get it on the next pass.
85 | return;
86 | }
87 | if (parser.buffer.substr(len, 2) === "--") {
88 | // this message is done.
89 | // chomp off the boundary and crlf and move up
90 | if (parser.part !== parser) {
91 | // wait to see the crlf, unless this is the top-level message.
92 | if (parser.buffer.length < (len + 4)) return;
93 | if (parser.buffer.substr(len+2, 2) !== CRLF) {
94 | error(parser, "Malformed: CRLF not found after boundary");
95 | return;
96 | }
97 | }
98 | parser.buffer = parser.buffer.substr(len + 4);
99 | emit(parser, "onPartEnd", parser.part);
100 | parser.part = parser.part.parent;
101 | parser.state = S.NEW_PART;
102 | continue;
103 | }
104 | if (parser.part !== parser) {
105 | // wait to see the crlf, unless this is the top-level message.
106 | if (parser.buffer.length < (len + 2)) return;
107 | if (parser.buffer.substr(len, 2) !== CRLF) {
108 | error(parser, "Malformed: CRLF not found after boundary");
109 | return;
110 | }
111 | }
112 | // walk past the crlf
113 | parser.buffer = parser.buffer.substr(len + 2);
114 | // mint a new child part, and start parsing headers.
115 | parser.part = startPart(parser.part);
116 | parser.state = S.HEADER;
117 | continue;
118 | case S.HEADER:
119 | // just grab everything to the double crlf.
120 | var headerEnd = parser.buffer.indexOf(CRLF+CRLF)
121 | if (headerEnd === -1) {
122 | if (parser.buffer.length > MAX_BUFFER_LENGTH) {
123 | error(parser, "Malformed: header unreasonably long.");
124 | }
125 | return;
126 | }
127 | var headerString = parser.buffer.substr(0, headerEnd)
128 | parseHeaderString(parser.part.headers, headerString, parser);
129 | // chomp off the header and the empty line.
130 | parser.buffer = parser.buffer.substr(headerEnd + 4);
131 | multipartHeaders(parser.part);
132 |
133 | // let the world know
134 | emit(parser, "onPartBegin", parser.part);
135 |
136 | if (parser.part.isMultiPart) {
137 | // it has a boundary and we're ready to grab parts out.
138 | parser.state = S.NEW_PART;
139 | } else {
140 | // it doesn't have a boundary, and is about to
141 | // start spitting out body bits.
142 | parser.state = S.BODY;
143 | }
144 | continue;
145 | case S.BODY:
146 | // look for parser.part.parent.boundary
147 | var boundary = parser.part.parent.boundary
148 | , offset = parser.buffer.indexOf(boundary)
149 | if (offset === -1) {
150 | // emit and wait for more data, but be careful, because
151 | // we might only have half of the boundary so far.
152 | // make sure to leave behind the boundary's length, so that we'll
153 | // definitely get it next time if it's on its way.
154 | var emittable = parser.buffer.length - boundary.length
155 | if (parser.buffer.substr(-1) === CR) emittable -= 1;
156 | if (parser.buffer.substr(-2) === CRLF) emittable -= 2;
157 |
158 | if (emittable > 0) {
159 | emit(parser, "onData", parser.buffer.substr(0, emittable));
160 | parser.buffer = parser.buffer.substr(emittable);
161 | }
162 | // haven't seen the boundary, so wait for more bytes.
163 | return;
164 | }
165 | if (offset > 0) {
166 | var emittable = parser.buffer.substr(0, offset)
167 | if (emittable.substr(-2) === CRLF) {
168 | emittable = emittable.substr(0, emittable.length-2);
169 | }
170 | if (emittable) emit(parser, "onData", emittable);
171 | parser.buffer = parser.buffer.substr(offset);
172 | }
173 |
174 | // let em know we're done.
175 | emit(parser, "onPartEnd", parser.part);
176 |
177 | // now buffer starts with boundary.
178 | if (parser.buffer.substr(boundary.length, 2) === "--") {
179 | // message end.
180 | // parent ends, look for a new part in the grandparent.
181 | parser.part = parser.part.parent;
182 | emit(parser, "onPartEnd", parser.part);
183 | parser.part = parser.part.parent;
184 | parser.state = S.NEW_PART;
185 | parser.buffer = parser.buffer.substr(boundary.length + 4);
186 | } else {
187 | // another part coming for the parent message.
188 | parser.part = parser.part.parent;
189 | parser.state = S.NEW_PART;
190 | }
191 | continue;
192 | }
193 | }
194 | }
195 |
196 | Parser.prototype.close = function () {
197 | emit(this, "onEnd");
198 | Parser.call(this);
199 | }
200 |
201 | function parseHeaderString (headers, string, parser) {
202 | var lines = string.split(CRLF)
203 | , field, value, line
204 | for (var i = 0, l = lines.length; i < l; i ++) {
205 | line = lines[i];
206 | if (line.match(wrapExpression)) {
207 | if (!field) {
208 | error(parser, "Malformed. First header starts with whitespace.");
209 | }
210 | value += line.replace(wrapExpression, " ");
211 | continue;
212 | } else if (field) {
213 | // now that we know it's not wrapping, put it on the headers obj.
214 | affixHeader(headers, field, value);
215 | }
216 | line = line.split(":");
217 | field = line.shift().toLowerCase();
218 | if (!field) {
219 | error(parser, "Malformed: improper field name.");
220 | }
221 | value = line.join(":").replace(/^\s+/, "");
222 | }
223 | // now affix the last field.
224 | affixHeader(headers, field, value);
225 | }
226 |
227 | function affixHeader (headers, field, value) {
228 | if (!headers.hasOwnProperty(field)) {
229 | headers[field] = value;
230 | } else if (Array.isArray(headers[field])) {
231 | headers[field].push(value);
232 | } else {
233 | headers[field] = [headers[field], value];
234 | }
235 | }
236 |
237 | function startPart (parent) {
238 | return { headers : {}
239 | , parent : parent
240 | };
241 | }
242 |
243 | // check the headers of a message. If it wants to be multipart,
244 | // then we'll be returning true, and setting some additional data,
245 | // like type and boundary.
246 | function multipartHeaders (message) {
247 | // if it has a boundary already, then it's most likely the parser object,
248 | // and the user has told us what they expect the boundary to be.
249 | // take their word for it.
250 | if (message.boundary) {
251 | if (message.boundary.substr(0, 2) !== "--") {
252 | message.boundary = "--" + message.boundary;
253 | }
254 | return message.isMultiPart = true;
255 | }
256 |
257 | var field
258 | , val
259 | , contentType
260 | , contentDisposition = ""
261 | for (var h in message.headers) if (message.headers.hasOwnProperty(h)) {
262 | val = message.headers[h];
263 | field = h.toLowerCase();
264 | if (field === "content-type") {
265 | contentType = val;
266 | } else if (field === "content-disposition") {
267 | contentDisposition = val;
268 | }
269 | }
270 |
271 | if (!Array.isArray(contentDisposition)) {
272 | contentDisposition = contentDisposition.split(",");
273 | }
274 | contentDisposition = contentDisposition[contentDisposition.length - 1];
275 |
276 | // Name and filename can come along with either content-disposition
277 | // or content-type. Well-behaved agents use CD rather than CT,
278 | // but sadly not all agents are well-behaved.
279 | [contentDisposition, contentType].forEach(function (h) {
280 | if (!h) return;
281 | var cd = h.split(/; */)
282 | cd.shift();
283 | for (var i = 0, l = cd.length; i < l; i ++) {
284 | var bit = cd[i].split("=")
285 | , name = bit.shift()
286 | , val = stripQuotes(bit.join("="))
287 | if (name === "filename" || name === "name") {
288 | message[name] = val;
289 | }
290 | }
291 | });
292 |
293 | if (!contentType) return false;
294 |
295 | if (!Array.isArray(contentType)) contentType = contentType.split(",");
296 | contentType = contentType[contentType.length-1];
297 |
298 | // make sure it's actually multipart.
299 | var mpType = multipartExpression.exec(contentType)
300 | if (!mpType) return false;
301 |
302 | // make sure we have a boundary.
303 | var boundary = boundaryExpression.exec(contentType)
304 | if (!boundary) return false;
305 |
306 | message.type = mpType[1];
307 | message.boundary = "--" + boundary[1];
308 | message.isMultiPart = true;
309 | return true;
310 | }
311 |
312 | function stripslashes(str) {
313 | // + original by: Kevin van Zonneveld (http://kevin.vanzonneveld.net)
314 | // + improved by: Ates Goral (http://magnetiq.com)
315 | // + fixed by: Mick@el
316 | // + improved by: marrtins
317 | // + bugfixed by: Onno Marsman
318 | // + improved by: rezna
319 | // + input by: Rick Waldron
320 | // + reimplemented by: Brett Zamir (http://brett-zamir.me)
321 | // * example 1: stripslashes("Kevin\'s code");
322 | // * returns 1: "Kevin's code"
323 | // * example 2: stripslashes("Kevin\\\'s code");
324 | // * returns 2: "Kevin\'s code"
325 | return (str+"").replace(/\\(.?)/g, function (s, n1) {
326 | switch(n1) {
327 | case "\\":
328 | return "\\";
329 | case "0":
330 | return "\0";
331 | case "":
332 | return "";
333 | default:
334 | return n1;
335 | }
336 | });
337 | }
338 | function stripQuotes (str) {
339 | str = stripslashes(str);
340 | return str.substr(1, str.length - 2);
341 | }
342 |
--------------------------------------------------------------------------------
/examples/hello/static/jquery-1.4.2.min.js:
--------------------------------------------------------------------------------
1 | /*!
2 | * jQuery JavaScript Library v1.4.2
3 | * http://jquery.com/
4 | *
5 | * Copyright 2010, John Resig
6 | * Dual licensed under the MIT or GPL Version 2 licenses.
7 | * http://jquery.org/license
8 | *
9 | * Includes Sizzle.js
10 | * http://sizzlejs.com/
11 | * Copyright 2010, The Dojo Foundation
12 | * Released under the MIT, BSD, and GPL Licenses.
13 | *
14 | * Date: Sat Feb 13 22:33:48 2010 -0500
15 | */
16 | (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)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
21 | 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))&&
22 | (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,
23 | 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===
24 | "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,
25 | 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(;ba";
34 | 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,
35 | 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=
36 | false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="";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=
37 | 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,
38 | 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];
39 | 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,
40 | 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===
41 | 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,
42 | 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-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=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
47 | 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");
48 | 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,
49 | 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(".");
50 | 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),
51 | 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=0){a.type=
53 | 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()&&
54 | 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;
55 | 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-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",
63 | 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,
64 | "_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,
65 | 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;j0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
71 | 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();
72 | 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||
73 | 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":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m=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()},
80 | 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,
81 | 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)},
82 | 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)}},
83 | 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 hl[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=
84 | h[3];l=0;for(m=h.length;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===
86 | "="?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,
87 | 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";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"&&
90 | 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="";
91 | 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="";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}}();
92 | (function(){var g=s.createElement("div");g.innerHTML="";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)}:
93 | 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=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f0)for(var j=d;j0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
96 | {},i;if(f&&a.length){e=0;for(var o=a.length;e-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===
97 | "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",
98 | 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")?
99 | 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===
100 | 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=/"+d+">"},F={option:[1,""],legend:[1,""],thead:[1,""],tr:[2,""],td:[3,""],col:[2,""],area:[1,""],_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","
"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
102 | 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},
103 | 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)})},
104 | 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,
105 | 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);
106 | 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,
107 | ""):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;b0||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]);
111 | return this}else{e=0;for(var j=d.length;e0?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)||["",
112 | ""])[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]===""&&!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=
113 | 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]?
114 | 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=
115 | 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=
116 | 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,
117 | "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=
118 | 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=
119 | 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=/