diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d7cb4ab --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +/node_modules/ +/test.js +/.settings +/.tern-project +/.sublime-project +/.project \ No newline at end of file diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 0000000..e07c298 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,125 @@ +{ + // -------------------------------------------------------------------- + // JSHint Nodeclipse Configuration v0.18 + // Strict Edition with some relaxations and switch to Node.js, no `use strict` + // by Ory Band, Michael Haschke, Paul Verest + // https://github.com/Nodeclipse/nodeclipse-1/blob/master/org.nodeclipse.ui/templates/common-templates/.jshintrc + // JSHint Documentation is at http://www.jshint.com/docs/options/ + // JSHint Integration v0.9.10 comes with JSHInt 2.5.6 , see https://github.com/eclipsesource/jshint-eclipse + // -------------------------------------------------------------------- + // from https://gist.github.com/haschek/2595796 + // + // This is a options template for [JSHint][1], using [JSHint example][2] + // and [Ory Band's example][3] as basis and setting config values to + // be most strict: + // + // * set all enforcing options to true + // * set all relaxing options to false + // * set all environment options to false, except the node value + // * set all JSLint legacy options to false + // + // [1]: http://www.jshint.com/ + // [2]: https://github.com/jshint/node-jshint/blob/master/example/config.json //404 + // [3]: https://github.com/oryband/dotfiles/blob/master/jshintrc + // [4]: http://www.jshint.com/options/ + // + // @author http://michael.haschke.biz/ + // @license http://unlicense.org/ + + // == Enforcing Options =============================================== + // + // These options tell JSHint to be more strict towards your code. Use + // them if you want to allow only a safe subset of JavaScript, very + // useful when your codebase is shared with a big number of developers + // with different skill levels. Was all true. + + "bitwise" : false, // Prohibit bitwise operators (&, |, ^, etc.). + "curly" : true, // Require {} for every new block or scope. + "eqeqeq" : true, // Require triple equals i.e. `===`. + "forin" : true, // Tolerate `for in` loops without `hasOwnPrototype`. + "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` + "latedef" : "nofunc", // Prohibit variable use before definition. + "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. + "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. + "noempty" : true, // Prohibit use of empty blocks. + "nonew" : true, // Prohibit use of constructors for side-effects. + "plusplus" : false, // Prohibit use of `++` & `--`. //coding style related only + "regexp" : true, // Prohibit `.` and `[^...]` in regular expressions. + "undef" : true, // Require all non-global variables be declared before they are used. + "strict" : false, // Require `use strict` pragma in every file. + "trailing" : true, // Prohibit trailing whitespaces. + + // == Relaxing Options ================================================ + // + // These options allow you to suppress certain types of warnings. Use + // them only if you are absolutely positive that you know what you are + // doing. Was all false. + "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons). + "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. + "debug" : false, // Allow debugger statements e.g. browser breakpoints. + "eqnull" : false, // Tolerate use of `== null`. + //"es5" : true, // Allow EcmaScript 5 syntax. // es5 is default https://github.com/jshint/jshint/issues/1411 + "esnext" : false, // Allow ES.next (ECMAScript 6) specific features such as `const` and `let`. + "evil" : false, // Tolerate use of `eval`. + "expr" : false, // Tolerate `ExpressionStatement` as Programs. + "funcscope" : false, // Tolerate declarations of variables inside of control structures while accessing them later from the outside. + "globalstrict" : false, // Allow global "use strict" (also enables 'strict'). + "iterator" : false, // Allow usage of __iterator__ property. + "lastsemic" : false, // Tolerat missing semicolons when the it is omitted for the last statement in a one-line block. + "laxbreak" : false, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. + "laxcomma" : true, // Suppress warnings about comma-first coding style. + "loopfunc" : false, // Allow functions to be defined within loops. + "maxerr" : 100, // This options allows you to set the maximum amount of warnings JSHint will produce before giving up. Default is 50. + "multistr" : true, // Tolerate multi-line strings. + "onecase" : false, // Tolerate switches with just one case. + "proto" : false, // Tolerate __proto__ property. This property is deprecated. + "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. + "scripturl" : false, // Tolerate script-targeted URLs. + "smarttabs" : false, // Tolerate mixed tabs and spaces when the latter are used for alignmnent only. + "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. + "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. + "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. + "validthis" : false, // Tolerate strict violations when the code is running in strict mode and you use this in a non-constructor function. + + // == Environments ==================================================== + // + // These options pre-define global variables that are exposed by + // popular JavaScript libraries and runtime environments—such as + // browser or node.js. TODO JSHint Documentation has more, but it is not clear since what JSHint version they appeared + "browser" : false, // Standard browser globals e.g. `window`, `document`. + "couch" : false, // Enable globals exposed by CouchDB. + "devel" : false, // Allow development statements e.g. `console.log();`. + "dojo" : false, // Enable globals exposed by Dojo Toolkit. + "jquery" : false, // Enable globals exposed by jQuery JavaScript library. + "mootools" : false, // Enable globals exposed by MooTools JavaScript framework. + "node" : true, // Enable globals available when code is running inside of the NodeJS runtime environment. + "nonstandard" : false, // Define non-standard but widely adopted globals such as escape and unescape. + "phantom" : false, //?since version? This option defines globals available when your core is running inside of the PhantomJS runtime environment. + "prototypejs" : false, // Enable globals exposed by Prototype JavaScript framework. + "rhino" : false, // Enable globals available when your code is running inside of the Rhino runtime environment. + "worker" : false, //?since version? This option defines globals available when your code is running inside of a Web Worker. + "wsh" : false, // Enable globals available when your code is running as a script for the Windows Script Host. + "yui" : false, //?since version? This option defines globals exposed by the YUI JavaScript framework. + + // == JSLint Legacy =================================================== + // + // These options are legacy from JSLint. Aside from bug fixes they will + // not be improved in any way and might be removed at any point. + "nomen" : false, // Prohibit use of initial or trailing underbars in names. + "onevar" : false, // Allow only one `var` statement per function. + "passfail" : false, // Stop on first error. + "white" : false, // Check against strict whitespace and indentation rules. + + // == Undocumented Options ============================================ + // + // While Michael have found these options in [example1][2] and [example2][3] (already gone 404) + // they are not described in the [JSHint Options documentation][4]. + + "predef" : [ // Extra globals. + //"exampleVar", + //"anotherCoolGlobal", + //"iLoveDouglas" + "Java", "JavaFX", "$ARG" //no effect + ] + //, "indent" : 2 // Specify indentation spacing +} \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js new file mode 100644 index 0000000..6f39c8d --- /dev/null +++ b/Gruntfile.js @@ -0,0 +1,56 @@ +/** + * http://gruntjs.com/configuring-tasks + */ +module.exports = function(grunt) { + var path = require('path'); + var DOC_PATH = 'dist'; + var SOURCE_PATH = 'lib'; + grunt.initConfig({ + pkg : grunt.file.readJSON('package.json'), + + connect : { + options : { + hostname : '*' + }, + docs : { + options : { + port : 8000, + base : DOC_PATH, + middleware : function(connect, options) { + return [ require('connect-livereload')(), connect.static(path.resolve(options.base)) ]; + } + } + } + }, + + clean : { + docs : { + src : DOC_PATH + } + }, + + jsdoc : { + docs : { + src : [ SOURCE_PATH + '/**/*.js', + + // You can add README.md file for index page at documentations. + 'Readme.md' ], + options : { + verbose : true, + destination : DOC_PATH, + configure : 'conf.json', + template : 'node_modules/jaguarjs-jsdoc', + 'private' : false + } + } + }, + + }); + + // Load task libraries + [ 'grunt-contrib-clean', 'grunt-jsdoc', ].forEach(function(taskName) { + grunt.loadNpmTasks(taskName); + }); + + grunt.registerTask('docs', 'Create documentation for mikronode', [ 'clean:docs', 'jsdoc:docs' ]); +}; diff --git a/Readme.md b/Readme.md index 13c721f..e7f9a4f 100644 --- a/Readme.md +++ b/Readme.md @@ -2,28 +2,43 @@ Full-Featured asynchronous Mikrotik API interface for [NodeJS](http://nodejs.org). - var api = require('mikronode'); - - var connection = new api('192.168.0.1','admin','password'); - connection.connect(function(conn) { - - var chan=conn.openChannel(); - - chan.write('/ip/address/print',function() { - chan.on('done',function(data) { - - var parsed = api.parseItems(data); - - parsed.forEach(function(item) { - console.log('Interface/IP: '+item.interface+"/"+item.address); - }); - - chan.close(); - conn.close(); - - }); - }); - }); + var MikroNode = require('mikronode'); + + var connection = MikroNode.getConnection(process.argv[2], process.argv[3], process.argv[4]); + connection.connect(function(conn) { + + var chan = conn.openChannel(); + conn.closeOnDone = true; + chan.write('/ip/address/print', function() { + chan.closeOnDone = true; + chan.on('done', function(data) { + var parsed = MikroNode.parseItems(data); + parsed.forEach(function(item) { + console.log('Interface/IP: ' + item.interface + "/" + item.address); + }); + }); + chan.once('trap', function(trap, chan) { + console.log('Command failed: ' + trap); + }); + chan.once('error', function(err, chan) { + console.log('Oops: ' + err); + }); + }); + }); + + /* Now let's do this with Promises */ + + var connection = MikroNode.getConnection(process.argv[2], process.argv[3], process.argv[4], { + closeOnDone : true + }); + + connection.getConnectPromise().then(function(conn) { + conn.getCommandPromise('/ip/address/print').then(function resolved(values) { + console.log('Addreses: ' + JSON.stringify(values)); + }, function rejected(reason) { + console.log('Oops: ' + JSON.stringify(reason)); + }); + }); ## Installation @@ -35,87 +50,78 @@ ## Features * Channel based communication - * Multiple channels can be used at once. - * Synchronous execution of commands issued on the same channel. - * Asynchrounous execution of commands issued on different channels. + * Multiple channels can be used at once + * Synchronous execution of commands issued on the same channel + * Asynchrounous execution of commands issued on different channels * Focus on high performance + * ES6 Promise support for Connection and Channel + +## Upgrading from versions < 1.0.0 + +There are 2 changes that will need to be made... + + var MikroNode = require('mikronode'); + + // From + var connection = new MikroNode(...) + // To + var connection = MikroNode.getConnection(...) + + // From + connection.closeOnDone(true); + channel.closeOnDone(true); + // To + connection.closeOnDone = true; + channel.closeOnDone - true; + +Everything else should work as expected. + ## TODO - * Cleanup login section in connect method. - * Re-design code to hide internal methods and variables. - * Write tests con make sure everything keeps working while making above changes. + * Write tests con make sure everything keeps working while making changes. ## API +See the [API JSDocs](dist/index.html) in the dist directory. + +## Promises -### Connection - - Calling new api(host,user,pass) returns a connection object. - * RouterBoard need the connection before execute any comm. - * conn.connect(callback) - Connect to the target device. The callback function is called after successful login with the current connection object as its parameter. - * conn.openChannel(id) - Open and return a new channel object. Each channel is a unique command line to the mikrotik, allowing simultaneous execution of commands. The ID parameter is optional. - * conn.connected() - Returns true is currently connected to a mikrotik device. - * conn.closeChannel(id) - Closes an open channel. This will call the close method of the channel object. - * conn closeOnDone(b) - If b == true, when a done event occurs, close the connection after all channels have been closed. - * conn.close(force) - Close the connection. If force is true, force close of any open channels then close this connection. - * conn.getHost() - * conn.getUser() - - -### Channel - - The following methods are available for channels: - - * channel.closeOnDone(b) - If b == true, when a done event occurs, close the channel after all commands queued have been executed. - * channel.saveBuffer(b) - If b is true, then save each line received in a buffer and pass the entire buffer to the done event. Otherwise the done event will not get all the lines, only the last line. - This is handy when following trailing output from a listen command, where the data could be endless. - * channel.getConnection() - * channel.getId() - * channel.write(lines,writeCallback) - Lines can be a string, or an array of strings. If it is a string, then it is split on the EOL character and each resulting line is sent as a separate word (in API speak) - If lines is an array, then each element is sent unaltered. - * channel.close(force) - Close the channel. If there are any commands still waiting to be executed, they will be completed before closing the channel. - If force is TRUE, then the channel is immediately closed. If the channel is running, the cancel command is sent to stop any running listen commands, or potentially long running output. +Promises are now supported for Connection and Channel. nodejs versions > 4.0 include +an ES6 Promise implementation. For earlier versions, installing es6-promise and running +require('es6-promise').polyfull() before requiring mikronode will set up Promise support. +You can also globally export Promise from your favorite ES6 compatable Promise library. ## Examples -### Connect to a Mikrotik, and add an address to ether1 - - var api = require('mikronode'); +[Examples (including Promise examples)](examples/) - var connection = new api('192.168.0.1','admin','password'); - connection.connect(function(conn) { - - var chan=conn.openChannel(); +### Connect to a Mikrotik, and add an address to ether1 - chan.write(['/ip/address/add','=interface=ether1','=address=192.168.1.1'],function() { - chan.on('trap',function(data) { + var MikroNode = require('mikronode'); + + var connection = MikroNode.getConnection('192.168.88.1','admin','password'); + connection.closeOnDone = true; + + connection.connect(function(conn) { + var chan=conn.openChannel(); + chan.closeOnDone = true; + chan.write(['/ip/address/add','=interface=ether1','=address=192.168.1.1'], function(c) { + c.on('trap',function(data) { console.log('Error setting IP: '+data); }); - chan.on('done',function(data) { + c.on('done',function(data) { console.log('IP Set.'); }); - chan.close(); - conn.close(); }); }); ### Writing the program for the example API conversation on the [Mikrotik Wiki](http://wiki.mikrotik.com/wiki/API#.2Fcancel.2C_simultaneous_commands) +DON'T RUN THIS IF YOU'RE CONNECTED VIA ether1! :) - var api = require('mikronode'); - - var connection = new api('192.168.0.1','admin','password'); + var MikroNode = require('mikronode'); + connection.connect(function(conn) { - - conn.closeOnDone(true); + + conn.closeOnDone = true; var chan2=conn.openChannel(2); chan2.write('/interface/listen',function(chan) { chan.on('read',function(data) { @@ -123,18 +129,18 @@ console.log('Interface change: '+JSON.stringify(packet)); }); }); - + var chan3=conn.openChannel(3); - chan3.closeOnDone(true); - + chan3.closeOnDone = true + chan3.write(['/interface/set','=disabled=yes','=.id=ether1'],function(chan) { chan.on('done',function(d,chan) { // We do this here, 'cause we want channel 4 to write after channel 3 is done. var chan4=conn.openChannel(4); // We'll use this later. - chan4.closeOnDone(true); + chan4.closeOnDone = true; chan4.write(['/interface/set','=disabled=no','=.id=ether1'],function() { var chan5=conn.openChannel(5); - chan5.closeOnDone(true); + chan5.closeOnDone = true; chan5.write('/interface/getall',function(chan) { chan.on('done',function(data) { packets=api.parseItems(data); @@ -150,14 +156,15 @@ }); ### Simplifying the above by reducing the number of channels. +DON'T RUN THIS IF YOU'RE CONNECTED VIA ether1! :) Notice how the callback embedding is not needed using the syncronous capability. - var api = require('mikronode'); + var MikroNode = require('mikronode'); - var connection = new api('192.168.0.1','admin','password'); + var connection = MikroNode.getConnecion('192.168.88.1','admin','password'); connection.connect(function(conn) { - conn.closeOnDone(true); // All channels need to complete before the connection will close. + conn.closeOnDone = true; // All channels need to complete before the connection will close. var listenChannel=conn.openChannel(); listenChannel.write('/interface/listen',function(chan) { chan.on('read',function(data) { @@ -182,8 +189,28 @@ actionChannel.close(); // The above commands will complete before this is closed. }); +### A simple Promise scenario - The method *decodeLength* and *encodeString* were written based on code [here on the Mikrotik Wiki](http://wiki.mikrotik.com/wiki/API_PHP_class#Class). + // If your nodejs installation doesn't have Promise support, uncomment + // the following line + //require('es6-promise').polyfill(); + // or globally export Promise from your favorite ES6 compatable Promise library. + var MikroNode = require('mikronode'); + + var connection = MikroNode.getConnection(process.argv[2], process.argv[3], process.argv[4], { + closeOnDone : true + }); + + connection.getConnectPromise().then(function(conn) { + conn.getCommandPromise('/ip/address/print').then(function resolved(values) { + console.log('Addreses: ' + JSON.stringify(values)); + }, function rejected(reason) { + console.log('Oops: ' + JSON.stringify(reason)); + }); + }); + + + The methods *decodeLength* and *encodeString* were written based on code [here on the Mikrotik Wiki](http://wiki.mikrotik.com/wiki/API_PHP_class#Class). ## License diff --git a/conf.json b/conf.json new file mode 100644 index 0000000..7883729 --- /dev/null +++ b/conf.json @@ -0,0 +1,34 @@ +{ + "tags": { + "allowUnknownTags" : true + }, + "plugins": ["plugins/markdown", "plugins/strip-outer-iife", "plugins/jsdoc3-plugin-propertyof"], + "templates": { + "cleverLinks": true, + "monospaceLinks": true, + "default": { + "outputSourceFiles" : true + }, + "applicationName": "MikroNode", + "disqus": "", + "googleAnalytics": "", + "openGraph": { + "title": "", + "type": "website", + "image": "", + "site_name": "", + "url": "" + }, + "meta": { + "title": "", + "description": "", + "keyword": "" + }, + "linenums": false + }, + "markdown": { + "parser": "gfm", + "hardwrap": true, + "tags": ["examples"] + } +} diff --git a/dist/-_usr_src_node_modules_mikronode_lib_index.js.html b/dist/-_usr_src_node_modules_mikronode_lib_index.js.html new file mode 100644 index 0000000..321dcdc --- /dev/null +++ b/dist/-_usr_src_node_modules_mikronode_lib_index.js.html @@ -0,0 +1,1701 @@ + + + + + Source: /usr/src/node/modules/mikronode/lib/index.js + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+

Source: /usr/src/node/modules/mikronode/lib/index.js

+ + + + +
+
+
var net = require('net');
+var events = require('events');
+var crypto = require('crypto');
+var util = require('util');
+var dbg = require('debug');
+/* jshint undef: true, unused: true */
+/* globals Promise */
+
+/**
+ * MikroNode
+ * @module mikronode
+ * @requires net
+ * @requires events
+ * @requires crypto
+ * @requires util
+ * @requires debug
+ */
+module.exports = (function() {
+
+	var debugSocket = dbg('mikronode:socket');
+	var debugSocketData = dbg('mikronode:socket:data');
+	var debugLogin = dbg('mikronode:login');
+	var debugSentence = dbg('mikronode:sentence');
+	var debugConnection = dbg('mikronode:connection');
+	var debugChannel = dbg('mikronode:channel');
+	var debugChannelData = dbg('mikronode:channel:data');
+
+	var _ = require('private-parts').createKey();
+	var emptyString = String.fromCharCode(0);
+
+	/**
+	 * Encodes a string
+	 * @private
+	 * @function
+	 * @param {string} s The string to encode
+	 * @returns {Buffer} Encoded string
+	 */
+	function encodeString(s) {
+		var data = null;
+		var len = Buffer.byteLength(s);
+		var offset = 0;
+
+		if (len < 0x80) {
+			data = new Buffer(len + 1);
+			data[offset++] = len;
+		} else if (len < 0x4000) {
+			data = new Buffer(len + 2);
+			len |= 0x8000;
+			data[offset++] = (len >> 8) & 0xff;
+			data[offset++] = len & 0xff;
+		} else if (len < 0x200000) {
+			data = new Buffer(len + 3);
+			len |= 0xC00000;
+			data[offset++] = (len >> 16) & 0xff;
+			data[offset++] = (len >> 8) & 0xff;
+			data[offset++] = len & 0xff;
+		} else if (len < 0x10000000) {
+			data = new Buffer(len + 4);
+			len |= 0xE0000000;
+			data[offset++] = (len >> 24) & 0xff;
+			data[offset++] = (len >> 16) & 0xff;
+			data[offset++] = (len >> 8) & 0xff;
+			data[offset++] = len & 0xff;
+		} else {
+			data = new Buffer(len + 5);
+			data[offset++] = 0xF0;
+			data[offset++] = (len >> 24) & 0xff;
+			data[offset++] = (len >> 16) & 0xff;
+			data[offset++] = (len >> 8) & 0xff;
+			data[offset++] = len & 0xff;
+		}
+		data.utf8Write(s, offset);
+		return data;
+	}
+
+	/**
+	 * Decodes the length of the data array
+	 * @function
+	 * @private
+	 * @param {array} data - The data to dump
+	 * @returns {array} length
+	 */
+	function decodeLength(data) { // Ported from the PHP API on the
+		// Wiki. Thanks
+		var idx = 0;
+		var b = data[idx++];
+		var len;
+		if (b & 128) {
+			if ((b & 192) === 128) {
+				len = ((b & 63) << 8) + data[idx++];
+			} else {
+				if ((b & 224) === 192) {
+					len = ((b & 31) << 8) + data[idx++];
+					len = (len << 8) + data[idx++];
+				} else {
+					if ((b & 240) === 224) {
+						len = ((b & 15) << 8) + data[idx++];
+						len = (len << 8) + data[idx++];
+						len = (len << 8) + data[idx++];
+					} else {
+						len = data[idx++];
+						len = (len << 8) + data[idx++];
+						len = (len << 8) + data[idx++];
+						len = (len << 8) + data[idx++];
+					}
+				}
+			}
+		} else {
+			len = b;
+		}
+		return [ idx, len ];
+	}
+
+	/**
+	 * Dumps an array to 'debug' in hex format
+	 * @function
+	 * @private
+	 * @param {array} data - The data to dump
+	 */
+	function hexDump(data) {
+		var hex = [];
+		var cref = [];
+		var i = 0;
+		for (var j = 0; j < data.length; j++) {
+			i = j % 8;
+			// m=ctrl.indexOf(data[j]);
+			if ((data[j] < 20) || (data[j] > 126)) {
+				cref[i] = '.';
+			} else {
+				cref[i] = String.fromCharCode(data[j]);
+			}
+			hex[i] = Number(data[j]).toString(16);
+			while (hex[i].length < 2) {
+				hex[i] = "0" + hex[i];
+			}
+			if (hex.length === 8) {
+				debugSocketData("%d: %s    %s", j - 7, hex.join(' '), cref.join(''));
+				hex = [];
+				cref = [];
+			}
+		}
+		if (i !== 8) {
+			debugSocketData(hex.join(' ') + '    ' + cref.join(''));
+			hex = [];
+			cref = [];
+		}
+	}
+
+	/**
+	 * Creates a private property and a getter[,setter]
+	 * @function
+	 * @private
+	 * @param {object} object - The object in which the property should be created
+	 * @param {string} name - The name of the property
+	 * @param {*} initialValue - The property's initial value
+	 * @param {boolean} [allowSet=false] - If true, the property can be set externally.
+	 * @param {boolean} [needPrivate=false] - If true, a private property in the form of
+	 *           _(object)[name] will be created. This can be set only with a reference to
+	 *           'object' AND '_' even if setting by this[name] isn't allowed.
+	 */
+	function createProperty(object, name, initialValue, allowSet, needPrivate) {
+		var props = {
+			enumerable : true
+		};
+		if (needPrivate) {
+			_(object)[name] = initialValue;
+			props.get = function() {
+				return _(object)[name];
+			};
+			if (allowSet) {
+				props.set = function(val) {
+					_(object)[name] = val;
+				};
+			}
+		} else {
+			if (allowSet) {
+				props.writable = true;
+			}
+			props.value = initialValue;
+		}
+		Object.defineProperty(object, name, props);
+	}
+
+	/**
+	 * @exports mikronode.MikroNode
+	 * @class
+	 */
+	function MikroNode() {
+		throw new Error('Not a constructor');
+	}
+
+	/**
+	 * Creates or returns a Connection object.
+	 * @function
+	 * @param {string} host - The host name or ip address
+	 * @param {string} user - The user name
+	 * @param {string} password - The users password
+	 * @param {object} [options]
+	 * @param {number} [options.port=8728] - Sets the port if not the standard 8728.
+	 * @param {boolean} [options.closeOnDone=false] - If set, when the last channel closes,
+	 *           the connection will automatically close.
+	 * @param {number} [options.timeout=0] - Sets the socket inactivity timeout. A timeout
+	 *           does not necessarily mean that an error has occurred, especially if you're
+	 *           only listening for events.
+	 * @param {boolean} [options.closeOnTimeout=false] - If set, when a socket timeout
+	 *           happens the connection will automatically close.
+	 * @returns {mikronode.Connection}
+	 * @example
+	 * 
+	 * <pre>
+	 * var MikroNode = require('mikronode');
+	 * 
+	 * var connection = MikroNode.getConnection('192.168.88.1', 'admin', 'mypassword', {
+	 * 	timeout : 4,
+	 * 	closeOnDone : true,
+	 * 	closeOnTimeout : true,
+	 * });
+	 * </pre>
+	 */
+	MikroNode.getConnection = function getConnection(host, user, password, options) {
+		return new Connection(host, user, password, options);
+	};
+
+	/**
+	 * Parse !re return records into an array of objects
+	 * @function
+	 * @param {string[]} data - The data[] returned from Channel.on('done')
+	 * @returns {object[]}
+	 */
+	MikroNode.parseItems = function parseItems(data) {
+		var db = [];
+		var idx = 0;
+		var record = {};
+		// util.puts('parseItems: '+JSON.stringify(data));
+		data.forEach(function(data) {
+			while (data.length) {
+				var l = data.shift().split(/=/);
+				if (l[0] === '!re') {
+					if (db.length) {
+						record = {};
+					}
+					db.push(record);
+					idx++;
+					continue;
+				}
+				l.shift(); // remove empty first element
+				record[l.shift()] = l.join('='); // next element is key. All the
+				// rest is value.
+			}
+			if (data.length === 1 && (data[0] !== record)) {
+				db.push(record);
+			}
+		});
+		return db;
+	};
+
+	/**
+	 * Trap
+	 * @exports mikronode.Trap
+	 * @class
+	 * @param {string} [message]
+	 */
+	function Trap(message) {
+		/**
+		 * @property {string} message
+		 */
+		this.message = message || '';
+		/**
+		 * @property {string} [category]
+		 */
+		this.category = '';
+		/**
+		 * @property {string} [channelId]
+		 */
+		this.channelId = '';
+		/**
+		 * @property {mikronode.Channel} [channel]
+		 */
+		this.channel = null;
+
+		this.toString = function() {
+			return this.message;
+		};
+	}
+
+	/**
+	 * Emitted when a non-recoverable error has occurred on the socket. No further commands
+	 * can be processed on any channel.
+	 * @event mikronode.Connection#event:error
+	 * @property {error} error - The error object
+	 * @property {mikronode.Connection} connection - The connection originating the event
+	 */
+	/**
+	 * Emitted when a socket has been idle too long.
+	 * @event mikronode.Connection#event:timeout
+	 * @property {string} message - 'Socket Timeout'
+	 * @property {boolean} socketStillOpen - If true, communications can continue
+	 * @property {mikronode.Connection} connection - The connection originating the event
+	 */
+	/**
+	 * Emitted when the connection is closed either by an explicit call to
+	 * {@link mikronode.Connection#close} or when the connection is closed automatically
+	 * via {@link mikronode.Connection#closeOnDone}
+	 * @event mikronode.Connection#event:close
+	 * @property {mikronode.Connection} connection - The connection originating the event
+	 */
+	/**
+	 * Emitted when a login has failed. No further commands can be processed on any
+	 * channel.
+	 * @event mikronode.Connection#event:trap
+	 * @property {mikronode.Trap} trap - The trap object
+	 */
+
+	/**
+	 * Connection
+	 * <p>
+	 * This class shouldn't be instantiated directly.
+	 * @exports mikronode.Connection
+	 * @class
+	 * @implements {EventEmitter}
+	 * @param {string} host - The host name or ip address
+	 * @param {string} user - The user name
+	 * @param {string} password - The users password
+	 * @param {object} [options]
+	 * @param {number} [options.port=8728] - Sets the port if not the standard 8728.
+	 * @param {boolean} [options.closeOnDone=false] - If set, when the last channel closes,
+	 *           the connection will automatically close.
+	 * @param {number} [options.timeout=0] - Sets the socket inactivity timeout. A timeout
+	 *           does not necessarily mean that an error has occurred, especially if you're
+	 *           only listening for events.
+	 * @param {boolean} [options.closeOnTimeout=false] - If set, when a socket timeout
+	 *           happens the connection will automatically close.
+	 * @fires mikronode.Connection#event:trap
+	 * @fires mikronode.Connection#event:error
+	 * @fires mikronode.Connection#event:timeout
+	 * @fires mikronode.Connection#event:close
+	 */
+	function Connection(host, user, password, options) {
+		this.hash = crypto.createHash('md5').update(host + user).digest('hex');
+		// If we already have a connection, return the same one.
+		options = options || {};
+		// if (api._conn[this.hash]) return api._conn[this.hash];
+
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {string} host - Hostname or ip address
+		 * @memberof mikronode.Connection
+		 */
+		createProperty(this, 'host', host);
+
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {string} user - User ID
+		 * @memberof mikronode.Connection
+		 */
+		createProperty(this, 'user', user);
+
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {string} password - Password
+		 * @memberof mikronode.Connection
+		 */
+		createProperty(this, 'password', password);
+
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {number} [port=8728] - Port
+		 * @memberof mikronode.Connection
+		 */
+		createProperty(this, 'port', options.port || 8728);
+
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {number} [timeout=0] - Socket inactivity timeout
+		 * @memberof mikronode.Connection
+		 */
+		createProperty(this, 'timeout', options.timeout);
+
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {string} status - Connection status
+		 * @memberof mikronode.Connection
+		 */
+		createProperty(this, 'status', 'New', false, true);
+
+		/**
+		 * @public
+		 * @instance
+		 * @member {boolean} [closeOnDone=false] - If set, when the last channel closes, the
+		 *         connection will automatically close.
+		 * @memberof mikronode.Connection
+		 */
+		this.closeOnDone = options.closeOnDone;
+
+		/**
+		 * @public
+		 * @instance
+		 * @readonly
+		 * @member {boolean} [options.closeOnTimeout=false] - If set, when a socket timeout
+		 *         happens the connection will automatically close.
+		 * @memberof mikronode.Connection
+		 */
+		this.closeOnTimeout = options.closeOnTimeout;
+
+		/* The following properties are all private */
+		_(this).connected = false;
+		_(this).connecting = false;
+		_(this).socket = null; // socket connection
+		_(this).line = ''; // current line. When the line is built, the sentence event is called.
+		_(this).buffer = []; // buffer holding incoming stream from socket
+		_(this).packet = []; // current packet
+		_(this).channel = {}; // all channels in use
+		_(this).trap = false; // we encountered a trap.
+		_(this).error = {}; // Buffer errors
+		_(this).datalen = 0; // Used to look-ahead to see if more data is available
+		_(this).loginHandler = null;
+
+	}
+	util.inherits(Connection, events.EventEmitter);
+
+	/**
+	 * Triggered by the 'sentence' event
+	 * @private
+	 * @param {string} data - Sentence
+	 * @param {boolean} more - There's data left to read
+	 * @this mikronode.Connection
+	 */
+	Connection.prototype.sentence = function sentence(data, more) {
+		debugSentence('Sentence:(' + more + ') data: ' + data);
+
+		if (_(this).fatal) { // our last message was a fatal error.
+			// debug('Sentence: fatal error: '+data);
+			_(this).packet.push(data);
+			this.emit('fatal', _(this).packet, this);
+			if (!_(this).closing) {
+				this.close();
+			}
+			return;
+		} else if (data === '!fatal') {
+			// we were sent a fatal message... wait
+			// for next sentence to get message.
+			_(this).fatal = true;
+		} else if (data === '!done') {
+			// we got a done signal... but we could
+			// be in a channel.  A .tag may be forthcoming.
+			_(this).packet = _(this).buffer;
+			_(this).buffer = [];
+			debugSentence('Sentence: Done Signal.');
+			if (_(this).trap) {// we previously caught a trap
+				if (!_(this).trap.channelId) {
+					debugSentence('Sentence: No channels.  Sending trap to connection.');
+					this.emit('trap', _(this).trap, this);
+					_(this).trap = false;
+				} else {
+					debugSentence('Sentence: Saving done on trap for channel ' + _(this).trap.channelId);
+					_(this).trap.done = true;
+					_(this).nextTag = 1;
+				}
+			} else {// no trap. Send general packet.
+				if (!more) {
+					debugSentence('Sentence: No more data in packet. Done.');
+					this.emit('done', _(this).packet);
+				} else {
+					debugSentence('Sentence: Could have a tag.');
+					_(this).nextTag = 1;
+				}
+			}
+		} else if (/=ret=/.test(data)) {
+			debugSentence('Sentence: Single return: ' + data);
+			_(this).buffer.push('!re');
+			_(this).buffer.push(data);
+			_(this).packet = _(this).buffer;
+			_(this).buffer = [];
+			_(this).nextTag = 1; // next could be a tag
+		} else if (_(this).nextTag) { // We had a done event, this could be a tag.
+			_(this).nextTag = 0;
+			if (data.match(/\.tag/)) {// Check if we have a tag.
+				var channel = data.substring(5);
+				debugSentence('Sentence: Done channel ' + channel + '.');
+				if (_(this).channel[channel]) {
+					_(this).channel[channel]._done(_(this).packet, _(this).trap);
+					_(this).trap = false;
+				}
+			} else {
+				if (/=ret=/.test(data)) {
+					_(this).nextTag = 1;
+					if (_(this).packet.length) {
+						_(this).packet.push('!re');
+						_(this).packet.push(data);
+					} else {
+						_(this).buffer.push('!re');
+						_(this).buffer.push(data);
+						_(this).packet = _(this).buffer;
+						_(this).buffer = [];
+					}
+					return;
+				}
+				_(this).packet = _(this).buffer;
+				_(this).buffer = [];
+				this.emit('done', _(this).packet, this);
+				_(this).buffer.push(data);
+			}
+		} else if (data.match(/\.tag/)) { // Catch tags where it's not following
+			// !done
+			_(this).packet = _(this).buffer; // backup up the packet
+			_(this).buffer = [];
+			var tagChannelId = data.substring(5);
+			if (_(this).trap) {// we previously caught a trap
+				debugSentence('Sentence: assigned ' + tagChannelId + ' to trap');
+				_(this).trap.channelId = tagChannelId;
+				_(this).trap.channel = _(this).channel[tagChannelId];
+			} else {
+				if (_(this).channel[tagChannelId]) {
+					_(this).channel[tagChannelId]._data(_(this).packet);
+				}
+			}
+		} else if (data.match(/\!trap/)) {
+			_(this).trap = new Trap();
+			debugSentence('Sentence: caught a trap');
+		} else if (_(this).trap) {
+			if (/=message=/.test(data)) {
+				debugSentence('Sentence: caught trap message: ' + data.substr(9));
+				_(this).trap.message = data.substr(9);
+			} else if (/=category=/.test(data)) {
+				debugSentence('Sentence: caught trap category: ' + data.substr(10));
+				_(this).trap.category = data.substr(10);
+			}
+		} else {
+			_(this).buffer[_(this).buffer.length] = data;
+		}
+	};
+
+	/**
+	 * Triggered by a 'data' event on teh socket
+	 * @private
+	 * @param {string} data - Sentence
+	 * @this mikronode.Connection
+	 */
+	Connection.prototype.read = function read(data) {
+		if (debugSocketData.enabled) {
+			hexDump(data);
+		}
+		while (data.length) {
+			debugSocket('read: data-len:' + data.length);
+			if (_(this).len) { // maintain the current data length. What if the data
+				// comes in 2 separate packets?
+				// I am hopping that the API on the other end doesn't send more than
+				// one channel
+				// at a time if more than one packet is required.
+				// if (this.debug>3) debug('read: data:'+data);
+				if (data.length <= _(this).len) {
+					_(this).len -= data.length;
+					_(this).line += data.toString();
+					debugSocketData('read:consume-all: data:' + data);
+					if (_(this).len === 0) {
+						this.emit('sentence', _(this).line, (data.length !== _(this).len));
+						_(this).line = '';
+					}
+					break;
+				} else {
+					debugSocketData('read:consume len:(' + _(this).len + ') data: ' + data);
+					_(this).line += data.toString('utf8', 0, _(this).len);
+					var l = _(this).line;
+					_(this).line = '';
+					data = data.slice(_(this).len);
+					var x = decodeLength(data);
+					_(this).len = x[1];
+					data = data.slice(x[0]); // get rid of excess buffer
+					if (_(this).len === 1 && data[0] === "\x00") {
+						_(this).len = 0;
+						data = data.slice(1); // get rid of excess buffer
+					}
+					this.emit('sentence', l, data.length);
+				}
+			} else {
+				var y = decodeLength(data);
+				_(this).len = y[1];
+				data = data.slice(y[0]);
+				if (_(this).len === 1 && data[0] === "\x00") {
+					_(this).len = 0;
+					data = data.slice(1); // get rid of excess buffer
+				}
+			}
+		}
+	};
+
+	/**
+	 * Send data
+	 * @private
+	 * @param {string} data - Sentence
+	 * @this mikronode.Connection
+	 */
+	Connection.prototype.write = function write(data) {
+		var _this = this;
+		if (!_(this).connected && !_(this).connecting) {
+			debugSocket('write: not connected ');
+			return;
+		}
+		if (typeof (data) === 'string') {
+			data = [ data ];
+		} else if (!Array.isArray(data)) {
+			return;
+		}
+		data.forEach(function(i) {
+			debugSocket('write: sending ' + i);
+			_(_this).socket.write(encodeString(i));
+		});
+		_(this).socket.write(emptyString);
+	};
+
+	/**
+	 * connectCallback
+	 * @callback mikronode.Connection.connectCallback
+	 * @param {mikronode.Connection}
+	 */
+
+	/**
+	 * Opens the socket and performs authentication
+	 * @param {mikronode.Connection.connectCallback} callback - Called when authentication
+	 *           succeeds and the connection is ready for channel activity
+	 * @this mikronode.Connection
+	 */
+	Connection.prototype.connect = function connect(callBack) {
+		if (_(this).connected) {
+			return;
+		}
+		var _this = this;
+		_(this).connectionCallback = callBack;
+		_(this).status = "Connecting";
+		this.addListener('fatal', function(conn) {
+			conn.close();
+		});
+		_(this).socket = new net.Socket({
+			type : 'tcp4'
+		});
+		debugSocket('Created socket to %s:%d', this.host, this.port);
+		_(this).connecting = true;
+		_(this).socket.on('data', function(a) {
+			_this.read(a);
+		});
+		_(this).socket.on('error', function(a) {
+			debugSocket('Connection error: ' + a);
+			_(_this).socket.destroy();
+			_(_this).connected = false;
+			_this.emit('error', a, _this);
+			_this.emit('close', _this);
+			_this.removeAllListeners();
+		});
+		_(this).socket.on('timeout', function(a) {
+			debugSocket('Timeout: ' + a);
+			if (_this.closeOnTimeout) {
+				_this.emit('timeout', 'Socket Timeout', false, _this);
+				_(_this).socket.destroy();
+				_(_this).connected = false;
+				_this.emit('close', _this);
+				_this.removeAllListeners();
+			} else {
+				_this.emit('timeout', 'Socket Timeout', true, _this);
+			}
+		});
+
+		// This will be called if there is no activity to the server.
+		// If this occurs before the login is successful, it could be
+		// that it is a connection timeout.
+		if (this.timeout) {
+			_(this).socket.setTimeout(this.timeout * 1000);
+		}
+		_(this).socket.setKeepAlive(true);
+		this._connector();
+		// While logging in, if an error occurs, we should kill the socket.
+		// This will keep node from not terminating due to lingering
+		// sockets.
+		return this;
+	};
+
+	/**
+	 * Created the loginHandler
+	 * @private
+	 * @this mikronode.Connection
+	 */
+	Connection.prototype._connector = function _connector() {
+		var _this = this;
+		/**
+		 * The loginHandler runs as an event callback from Socket so it's 'this' is Socket
+		 * @private
+		 * @this net.Socket
+		 */
+		_(this).loginHandler = function loginHandler(d) {
+			debugSocket('loginHandler read from %s: %s', _this.host, d);
+			switch (_this.status) {
+			case 'Connecting':
+				_(_this).status = 'Sending Login';
+				debugLogin('Connected to %s: %s', _this.host, _this.status);
+				_this.write('/login');
+				break;
+			case 'Sending Login':
+				if (d.length < 1) {
+					return;
+				}
+				if (d === '!done') {
+					debugLogin('Got !done. Need challenge');
+					return; // waiting for challenge
+				}
+				if (/=ret=/.test(d)) {
+					debugLogin('Got challenge');
+					_(_this).status = 'Sending Credentials';
+					debugLogin(_(_this).status);
+					var challenge = '';
+					var a = d.split('=')[2].split('');
+					while (a.length) {
+						challenge += String.fromCharCode(parseInt("0x" + a.shift() + a.shift()));
+					}
+					if (challenge.length !== 16) {
+						_(_this).status = 'Error';
+						debugLogin(_(_this).status);
+						_(_this).error = 'Bad connection response: ' + d;
+						debugLogin('Challenge length:' + challenge.length);
+						debugLogin(_(_this).error);
+						_this.removeListener('sentence', _this.loginHandler);
+						_this.close();
+					} else {
+						this
+								.write([
+										"/login",
+										"=name=" + _this.user,
+										"=response=00"
+												+ crypto.createHash('md5').update(emptyString + _this.password + challenge).digest(
+														"hex") ]);
+					}
+				}
+				break;
+			case 'Sending Credentials':
+				if (_(_this).trap) {
+					if (d === '!done') {
+						_this.emit('trap', _(_this).trap);
+						_(_this).trap = false;
+						_(_this).status = "Connecting";
+						return;
+					} else {
+						d = d.split(/=/); // Catch multiple trap return keys.
+						if (d.length > 2) {
+							_(_this).trap[d[1]] = d[2];
+						}
+					}
+				} else if (d === '!done') {
+					_(_this).status = 'Connected';
+					_this.removeAllListeners('sentence');
+					_this.removeAllListeners('fatal');
+					_this.removeAllListeners('trap');
+					_this.addListener('sentence', function(data, more) {
+						_this.sentence(data, more);
+					});
+					debugLogin(_(_this).status);
+					_(_this).connected = true;
+					if (_(_this).connectionCallback) {
+						_(_this).connectionCallback(this);
+						_(_this).connectionCallback = null;
+					}
+				} else {
+					if (d === '!trap') {
+						debugLogin('Login Trap ' + d);
+						_this.removeAllListeners('sentence');
+						_this.addListener('sentence', function(data, more) {
+							debugLogin('sentence' + data);
+							_this.sentence(data, more);
+						});
+						_this.sentence(d); // start off trap processing.
+					}
+					debugLogin(_(_this).status);
+				}
+				break;
+			case 'Connected':
+				_this.removeListener('sentence', _this.loginHandler);
+			}
+		};
+		this.addListener('sentence', _(this).loginHandler);
+		debugSocket('Connecting to %s:%d', this.host, this.port);
+		_(this).socket.connect(this.port, this.host, _(this).loginHandler);
+	};
+
+	/**
+	 * Opens a new Channel
+	 * @public
+	 * @param {number} [id=next available]
+	 * @returns {mikronode.Channel}
+	 */
+	Connection.prototype.openChannel = function openChannel(id) {
+		var _this = this;
+		if (!id) {
+			id = Object.keys(_(this).channel).length + 1;
+			while (_(this).channel[id]) {
+				id++;
+			}
+		} else if (_(this).channel[id]) {
+			throw ('Channel already exists for ID ' + id);
+		}
+		debugConnection('Opening channel: ' + id);
+		_(this).channel[id] = new Channel(id, this);
+		_(this).channel[id].addListener('close', function(channel) {
+			_this.closeChannel(channel.id);
+		});
+		return _(this).channel[id];
+	};
+
+	/**
+	 * Returns the channel specified by id.
+	 * @public
+	 * @param {number} id - The id of the channel desired
+	 * @returns {mikronode.Channel}
+	 */
+	Connection.prototype.getChannel = function getChannel(id) {
+		if (!id && id !== 0) {
+			throw ('Missing channel ID parameter' + id);
+		}
+		if (!_(this).channel[id]) {
+			throw ('Channel does not exist for ID ' + id);
+		}
+		debugConnection('Getting channel: ' + id);
+		return _(this).channel[id];
+	};
+
+	/**
+	 * Closes the channel specified by id.
+	 * @public
+	 * @param {number} id - The id of the channel to close
+	 */
+	Connection.prototype.closeChannel = function closeChannel(id) {
+		if (!id) {
+			throw ("Missing ID for stream channel to close.");
+		}
+		if (!_(this).channel[id]) {
+			throw ('Channel does not exist for ID ' + id);
+		}
+		// Make sure that the channel closes itself... so that remaining
+		// commands will execute.
+		if (!_(this).channel[id].closed) {
+			return _(this).channel[id].close();
+		}
+		debugConnection('Closing ' + this.host + ' channel: ' + id);
+		delete _(this).channel[id];
+		if (Object.keys(_(this).channel).length === 0 && (_(this).closing || this.closeOnDone)) {
+			this.close();
+		}
+	};
+
+	Connection.prototype.close = function close(force) {
+		var _this = this;
+		if (!_(this).connected) {
+			debugConnection('Connection disconnected: ' + this.host);
+			_(this).socket.destroy();
+			_(this).connected = false;
+			this.removeAllListeners();
+			this.emit('close', this);
+			this.removeAllListeners();
+			return;
+		}
+		if (!force && (Object.keys(_(this).channel).length > 0)) {
+			_(this).closing = true;
+			debugConnection('deferring closing connection');
+			return;
+		}
+		debugConnection('Connection disconnecting: ' + this.host);
+		this.removeAllListeners('done');
+		this.removeAllListeners('error');
+		this.removeAllListeners('timeout');
+
+		if (force) {
+			Object.keys(_(this).channel).forEach(function(e) {
+				_(_this).channel[e].close(true);
+			});
+		}
+		this.once('fatal', function() { // quit command ends with a fatal.
+			debugConnection('Connection disconnected: ' + this.host);
+			_(_this).socket.destroy();
+			_(_this).connected = false;
+			_this.removeAllListeners();
+			_this.emit('close', _this);
+		});
+		_(this).closing = false;
+		// delete api._conn[_(this).hash];
+		this.write([ '/quit' ]);
+		_(this).closing = true;
+	};
+
+	Connection.prototype.finalize = function finalize() {
+		_(this).close(true);
+	};
+
+	/**
+	 * Returns a Promise for an open connection.
+	 * <p>
+	 * The promise will resolve when the connection is ready for use or reject if there's
+	 * an error or trap. If resolved, the result object will be the
+	 * {@link mikronode.Connection} with authentication completed and ready for channels.
+	 * If rejected, the result object will be an Error if there was a socket error or
+	 * timeout during connection or login or a {@link mikronode.Trap} if there was a
+	 * problem with the login credentials.
+	 * <p>
+	 * @returns {Promise}
+	 */
+	Connection.prototype.getConnectPromise = function connectPromise() {
+		var _this = this;
+		return new Promise(function(resolve, reject) {
+			try {
+				_this.on('error', function(err) {
+					debugConnection('Error: %o', err);
+					reject(err);
+					_this.close();
+				});
+
+				_this.on('trap', function(err) {
+					debugConnection('Trap: %o', err);
+					reject(err);
+					_this.close();
+				});
+
+				_this.connect(function connect(connection) {
+					debugConnection('Resolved');
+					resolve(connection);
+				});
+			} catch (err) {
+				reject(err);
+			}
+		});
+	};
+
+	/**
+	 * Returnes a Promise of a completed command.
+	 * <p>
+	 * The promise will resolve when the command completes or reject if there's an error or
+	 * trap. If resolved, the result will be an array of instances of DestinationClass (or
+	 * Object, if no destination class was specified). If rejected, the result will be an
+	 * Error if there was a socket error or timeout, or a {@link mikronode.Trap} if the
+	 * command failed on the device.
+	 * 
+	 * @param {(string|string[])} data - Can be a single string with the command and
+	 *           optional parameters separated by '\n' or an array of strings with the
+	 *           command in the first position and the parameters in the rest.
+	 * @param {(object|string[])} [parameters] - If the first parameter is a command
+	 *           string, this object will be treated as the parameters for the command.
+	 *           <p>
+	 *           It can be an array or strings...
+	 * 
+	 * <pre>
+	 * ['name=value','name=value'...]
+	 * </pre>
+	 * 
+	 * or an Object...
+	 * 
+	 * <pre>
+	 * {'name': 'value', 'name': 'value'...}
+	 * </pre>
+	 * 
+	 * @param {object} [options] - A set of options that determine what to do with the
+	 *           return data (if any). If neither dataClass nor itemClass are provided, the
+	 *           default behavior will be as though itemClass were set to Object. This will
+	 *           result in Promise.resolve() being called with an array of plain Objects,
+	 *           one for each parsed item.
+	 * @param {boolean} [options.dontParse] - If true, Promise.resolve() will be called
+	 *           with the unaltered data array provided by the channel's 'done' event.
+	 * @param {class} [options.dataClass] - If provided, this class will be instantiated
+	 *           with the data array provided by the channel's 'done' event as the
+	 *           constructor's sole argument. Promise.resolve() will then be called with
+	 *           this object.
+	 * @param {class} [options.itemClass] - If provided, {mikronode.parseItems} will be
+	 *           called on the returned data and this class will be instantiated once for
+	 *           each resulting item. The item object will be passed as the sole argument
+	 *           to the constructor. An array of itemClass objects will be passed to
+	 *           Promise.resolve().
+	 * @param {string} [options.itemKey] - If provided, instead of an array of parsed
+	 *           objects being passed to Promise.resolve(), the parsed objects will be
+	 *           added to a wrapper object using the value of itemKey as the property name.
+	 * 
+	 * @returns {Promise}
+	 */
+	Connection.prototype.getCommandPromise = function commandPromise(data, parameters, options) {
+		var _this = this;
+		debugConnection('getCommandpromise');
+		return new Promise(function(resolve, reject) {
+			try {
+				if (parameters && !Array.isArray(parameters)
+						&& (parameters.dontParse || parameters.dataClass || parameters.itemClass || parameters.itemKey)) {
+					options = parameters;
+					parameters = null;
+				}
+				options = options || {};
+
+				var chan = _this.openChannel();
+				chan.closeOnDone = true;
+				chan.write(data, parameters, function() {
+					chan.on('error', function(err) {
+						debugChannel('Channel %d error: %o', chan.id, err);
+						reject(err);
+						chan.close();
+					});
+					chan.on('trap', function(err) {
+						debugChannel('Channel %d trap: %o', chan.id, err);
+						reject(err);
+					});
+					chan.on('timeout', function(err) {
+						debugChannel('Channel %d timeout', chan.id);
+						reject(err);
+					});
+					chan.on('done', function chanDone(data) {
+						debugChannel('Channel %d done: %o', chan.id, data);
+						if (options.dontParse) {
+							resolve(data);
+							return;
+						}
+						if (typeof options.dataClass === 'function') {
+							resolve(new options.dataClass(data));
+							return;
+						}
+						var items;
+						if (options.itemKey) {
+							items = {};
+						} else {
+							items = [];
+						}
+						var parsed = MikroNode.parseItems(data);
+						parsed.forEach(function(item) {
+							var o;
+							if (typeof options.itemClass === 'function') {
+								o = new options.itemClass(item);
+							} else {
+								o = {};
+								Object.keys(item).forEach(function(k) {
+									o[k] = item[k];
+								});
+							}
+							if (options.itemKey) {
+								items[item[options.itemKey]] = o;
+							} else {
+								items.push(o);
+							}
+						});
+						resolve(items);
+					});
+				});
+			} catch (err) {
+				reject(err);
+			}
+		});
+	};
+
+	/**
+	 * writeCallback
+	 * @callback mikronode.Channel.writeCallback
+	 * @param {Channel}
+	 */
+
+	/**
+	 * Emitted when a command has finished successfully.
+	 * @event mikronode.Channel#event:done
+	 * @property {(string|string[])} data - The data returned by the channel
+	 * @property {Channel} channel - The channel originating the event Fatal event.
+	 */
+	/**
+	 * Emitted when a non-recoverable error has occurred on the socket. No further commands
+	 * can be processed on any channel.
+	 * @event mikronode.Channel#event:error
+	 * @property {error} error - The error object
+	 * @property {Channel} channel - The channel originating the event
+	 */
+	/**
+	 * Emitted when a socket has been idle too long.
+	 * @event mikronode.Channel#event:timeout
+	 * @property {string} message - 'Socket Timeout'
+	 * @property {boolean} socketStillOpen - If true, communications can continue
+	 * @property {Channel} channel - The channel originating the event
+	 */
+	/**
+	 * Emitted when the channel is closed either by an explicit call to
+	 * {@link mikronode.Channel#close} or when the channel is closed automatically via
+	 * {@link mikronode.Channel#closeOnDone}
+	 * @event mikronode.Channel#event:close
+	 * @property {Channel} channel - The channel originating the event
+	 */
+	/**
+	 * Emitted when a command has failed. Subsequent commands may succeed.
+	 * @event mikronode.Channel#event:trap
+	 * @property {mikronode.Trap} trap - The trap object
+	 */
+
+	/**
+	 * Channel (should not be instantiated directly)
+	 * @exports mikronode.Channel
+	 * @implements {EventEmitter}
+	 * @class
+	 * @param {number} id
+	 * @param {mikronode.Connection} conn
+	 * @fires mikronode.Channel#event:done
+	 * @fires mikronode.Channel#event:trap
+	 * @fires mikronode.Channel#event:error
+	 * @fires mikronode.Channel#event:timeout
+	 * @fires {mikronode.Channel#event:close}
+	 */
+	function Channel(id, conn) {
+
+		/**
+		 * Channel ID
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {number} id
+		 * @memberof mikronode.Channel
+		 */
+		createProperty(this, 'id', id);
+		/**
+		 * Connection
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {mikronode.Connection} connection
+		 * @memberof mikronode.Channel
+		 */
+		createProperty(this, 'connection', conn);
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {boolean} running
+		 * @memberof mikronode.Channel
+		 */
+		createProperty(this, 'running', false, false, true);
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {boolean} closing
+		 * @memberof mikronode.Channel
+		 */
+		createProperty(this, 'closing', false, false, true);
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {boolean} closed
+		 * @memberof mikronode.Channel
+		 */
+		createProperty(this, 'closed', false, false, true);
+
+		/**
+		 * Clear event listeners on done
+		 * @public
+		 * @instance
+		 * @member {boolean} clearEvents
+		 * @memberof mikronode.Channel
+		 */
+		this.clearEvents = false;
+
+		/**
+		 * Save each line received in a buffer and pass the entire buffer to the done event.
+		 * Otherwise the done event will not get all the lines, only the last line. This is
+		 * handy when following trailing output from a listen command, where the data could
+		 * be endless.
+		 * @public
+		 * @instance
+		 * @member {boolean} saveBuffer
+		 * @memberof mikronode.Channel
+		 */
+		this.saveBuffer = true;
+
+		/**
+		 * Close channel on done
+		 * @public
+		 * @instance
+		 * @member {boolean} closeOnDone
+		 * @memberof mikronode.Channel
+		 */
+		this.closeOnDone = false;
+
+		/**
+		 * @public
+		 * @readonly
+		 * @instance
+		 * @member {string[]} lastCommand
+		 * @memberof mikronode.Channel
+		 */
+		this.lastCommand = [];
+
+		/**
+		 * @private
+		 * @instance
+		 * @member {mikronode.Channel.writeCallback} writeCallback
+		 * @memberof mikronode.Channel
+		 */
+		_(this).writeCallback = null;
+		/**
+		 * @private
+		 * @instance
+		 * @member {array} packet
+		 * @memberof mikronode.Channel
+		 */
+		_(this).packet = [];
+		/**
+		 * @private
+		 * @instance
+		 * @member {array} commands
+		 * @memberof mikronode.Channel
+		 */
+		_(this).commands = [];
+		/**
+		 * @private
+		 * @instance
+		 * @member {array} buffer
+		 * @memberof mikronode.Channel
+		 */
+		_(this).buffer = [];
+
+		/* We want connection errors to propogate down to
+		 * the channel so they can be caught by a channel promise
+		 */
+		var _this = this;
+
+		/* A 'error' event is thrown by Socket
+		 * and are non-recoverable so we force close the channel. 
+		 */
+		conn.once('error', function(err) {
+			debugChannel('Channel %d caught Connection Error: %o', id, conn);
+			_this.emit('error', err, _this);
+			_this.close(true);
+		});
+
+		/* A 'timeout' event is thrown by Socket
+		 * but they are recoverable. If Connection has closed
+		 * the Socket, we'll close the channel.  Otherwise, just
+		 * notify receivers and let them decide what to do.
+		 */
+		conn.on('timeout', function(message, socketStillOpen) {
+			debugChannel('Channel %d caught Timeout', id);
+			_this.emit('timeout', message, socketStillOpen, _this);
+			if (!socketStillOpen) {
+				_this.close(true);
+			}
+		});
+
+	}
+	util.inherits(Channel, events.EventEmitter);
+
+	/**
+	 * Writes data to the channel
+	 * @param {(string|string[])} data - Can be a single string with the command and
+	 *           optional parameters separated by '\n' or an array of strings with the
+	 *           command in the first position and the parameters in the rest.
+	 * @param {(object|string[])} [parameters] - If the first parameter is a command
+	 *           string, this object will be treated as the parameters for the command.
+	 *           <p>
+	 *           It can be an array or strings...
+	 * 
+	 * <pre>
+	 * ['name=value','name=value'...]
+	 * </pre>
+	 * 
+	 * or an Object...
+	 * 
+	 * <pre>
+	 * {'name': 'value', 'name': 'value'...}
+	 * </pre>
+	 * 
+	 * @param {mikronode.Channel.writeCallback} [writeCallback] - This will be called just
+	 *           before write actually writes the data to the connection.
+	 */
+	Channel.prototype.write = function write(d, parameters, writeCallback) {
+		if (_(this).closing) {
+			return;
+		}
+
+		if (d) {
+			if (typeof (d) === 'string') {
+				d = d.split("\n");
+			}
+			if (typeof parameters !== 'function') {
+				if (Array.isArray(parameters)) {
+					Array.prototype.push.apply(d, parameters);
+				} else if (parameters instanceof Object) {
+					Object.keys(parameters).forEach(function(k) {
+						d.push(k + '=' + parameters[k]);
+					});
+				}
+			} else if (writeCallback === undefined) {
+				writeCallback = parameters;
+			}
+			if (Array.isArray(d) && d.length) {
+				_(this).buffer = _(this).buffer.concat(d);
+			} else {
+				return;
+			}
+		} else {
+			debugChannel('Channel %d write: empty arg.', this.id);
+		}
+
+		if (_(this).running) {
+			this.lastCommand = _(this).buffer;
+			if (debugChannelData.enabled) {
+				debugChannelData('Channel %d running: pushing command %o', this.id, this.lastCommand);
+			} else {
+				debugChannel('Channel %d running: pushing command', this.id);
+			}
+			_(this).commands.push([ _(this).buffer, writeCallback ]);
+			_(this).buffer = [];
+		} else {
+			this.lastCommand = _(this).buffer;
+			var b = _(this).buffer;
+			_(this).running = true;
+			this.saveBuffer = true;
+			_(this).buffer = [];
+			b.push('.tag=' + this.id);
+			if (writeCallback) {
+				writeCallback(this);
+			}
+			if (debugChannelData.enabled) {
+				debugChannelData('Channel %d idle: writing %o', this.id, this.lastCommand);
+			} else {
+				debugChannel('Channel %d idle: writing', this.id);
+			}
+			this.connection.write(b); // Send command.
+		}
+	};
+
+	/**
+	 * Called when connection gets 'done'
+	 * @private
+	 * @param {(string|string[])} data
+	 */
+	Channel.prototype._done = function _done(data, trap) {
+		debugChannel('Channel %d done', this.id);
+
+		if (trap) {
+			debugChannel('Channel %d trap: %o', this.id, trap);
+			this.emit('trap', trap, this);
+		} else {
+			var p = _(this).packet;
+			_(this).packet = [];
+			if (!p.length) {
+				p = [ data ];
+			} else if (p[p.length - 1] !== data) {
+				p.push(data);
+			}
+
+			if (debugChannelData.enabled) {
+				debugChannelData('Channel %d done: %o', this.id, p);
+			} else {
+				debugChannel('Channel %d done', this.id);
+			}
+
+			this.emit('done', p, this);
+		}
+
+		if (this.clearEvents) {
+			this.removeAllListeners('done');
+			this.removeAllListeners('data');
+			this.removeAllListeners('read');
+		}
+		_(this).running = false;
+		if (_(this).commands.length) {
+			var c = _(this).commands.shift();
+			var cl = _(this).closing;
+			_(this).closing = false;
+			debugChannel('Channel %d more commands', this.id);
+			this.write(c[0], {}, c[1]);
+			_(this).closing = cl;
+		} else if (_(this).closing || this.closeOnDone) {
+			this.close();
+		}
+	};
+
+	/**
+	 * Called when connection gets 'data'
+	 * @private
+	 * @param {(string|string[])} data
+	 */
+	Channel.prototype._data = function _data(data) {
+		if (debugChannelData.enabled) {
+			debugChannelData('Channel %d data: %o', this.id, data);
+		} else {
+			debugChannel('Channel %d data', this.id);
+		}
+
+		if (this.saveBuffer) {
+			_(this).packet.push(data);
+		}
+		this.emit('data', [ data ], this);
+		this.emit('read', [ data ], this);
+	};
+
+	/**
+	 * Closes the channel This will close the connection if
+	 * {@link mikronode.Connection#closeOnDone} was set and this was the last channel to
+	 * close.
+	 * @public
+	 * @param {boolean} force - Force close even of there are other commands pending.
+	 *           Otherwise mark the channel as 'closing' which will prevent new commands
+	 *           from being started but will let queued ones finish.
+	 */
+	Channel.prototype.close = function close(force) { // Close _(this) channel.
+		_(this).closing = true;
+		if (_(this).closed || (!force && (_(this).commands.length || _(this).running))) {
+			debugChannel('Channel %d closing deferred', this.id);
+			return;
+		}
+		debugChannel('Channel %d closing.  Forced: %s', this.id, force ? 'true' : 'false');
+		if (_(this).running) {
+			try {
+				debugChannel('Channel %d sending cancel', this.id);
+				this.connection.write([ '/cancel', '=tag=' + this.id ]);
+			} catch (err) {
+				debugChannel('Error sending /cancel', err.stack);
+			}
+		}
+		debugChannel('Channel %d closed', this.id);
+		_(this).closed = true;
+		this.emit('close', this);
+		this.removeAllListeners();
+	};
+
+	/**
+	 * Calls {@link mikronode.Channel#close}(false)
+	 * @public
+	 */
+	Channel.prototype.finalize = function finalize() {
+		debugChannel('Channel %d finalize', this.id);
+		if (!_(this).closing) {
+			this.close();
+		}
+	};
+
+	Object.seal(MikroNode);
+	Object.seal(Connection);
+	Object.seal(Channel);
+
+	return MikroNode;
+})();
+
+
+
+ + + + + + + + + +
+
+ + + + + diff --git a/dist/fonts/glyphicons-halflings-regular.eot b/dist/fonts/glyphicons-halflings-regular.eot new file mode 100644 index 0000000..423bd5d Binary files /dev/null and b/dist/fonts/glyphicons-halflings-regular.eot differ diff --git a/dist/fonts/glyphicons-halflings-regular.svg b/dist/fonts/glyphicons-halflings-regular.svg new file mode 100644 index 0000000..4469488 --- /dev/null +++ b/dist/fonts/glyphicons-halflings-regular.svg @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/dist/fonts/glyphicons-halflings-regular.ttf b/dist/fonts/glyphicons-halflings-regular.ttf new file mode 100644 index 0000000..a498ef4 Binary files /dev/null and b/dist/fonts/glyphicons-halflings-regular.ttf differ diff --git a/dist/fonts/glyphicons-halflings-regular.woff b/dist/fonts/glyphicons-halflings-regular.woff new file mode 100644 index 0000000..d83c539 Binary files /dev/null and b/dist/fonts/glyphicons-halflings-regular.woff differ diff --git a/dist/index.html b/dist/index.html new file mode 100644 index 0000000..faa09b8 --- /dev/null +++ b/dist/index.html @@ -0,0 +1,442 @@ + + + + + Index + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+

Index

+ + + + + + +

+ + + + + + + + + + + + + +
+

Mikronode

Full-Featured asynchronous Mikrotik API interface for NodeJS.

+
    var MikroNode = require('mikronode');
+
+    var connection = MikroNode.getConnection(process.argv[2], process.argv[3], process.argv[4]);
+    connection.connect(function(conn) {
+
+    var chan = conn.openChannel();
+    conn.closeOnDone = true;
+    chan.write('/ip/address/print', function() {
+        chan.closeOnDone = true;
+        chan.on('done', function(data) {
+            var parsed = MikroNode.parseItems(data);
+            parsed.forEach(function(item) {
+                console.log('Interface/IP: ' + item.interface + "/" + item.address);
+            });
+        });
+        chan.once('trap', function(trap, chan) {
+            console.log('Command failed: ' + trap);
+        });
+        chan.once('error', function(err, chan) {
+            console.log('Oops: ' + err);
+        });
+    });
+});
+
+/* Now let's do this with Promises */
+
+var connection = MikroNode.getConnection(process.argv[2], process.argv[3], process.argv[4], {
+    closeOnDone : true
+});
+
+connection.getConnectPromise().then(function(conn) {
+    conn.getCommandPromise('/ip/address/print').then(function resolved(values) {
+        console.log('Addreses: ' + JSON.stringify(values));
+    }, function rejected(reason) {
+        console.log('Oops: ' + JSON.stringify(reason));
+    });
+});

Installation

Clone this repository into your node_modules directory.

+
    +
  • or -
    $ npm install mikronode
  • +
+

Features

    +
  • Channel based communication
  • +
  • Multiple channels can be used at once
  • +
  • Synchronous execution of commands issued on the same channel
  • +
  • Asynchrounous execution of commands issued on different channels
  • +
  • Focus on high performance
  • +
  • ES6 Promise support for Connection and Channel
  • +
+

Upgrading from versions < 1.0.0

There are 2 changes that will need to be made...

+
var MikroNode = require('mikronode');            
+
+// From    
+        var connection = new MikroNode(...)
+// To
+        var connection = MikroNode.getConnection(...)
+
+// From
+        connection.closeOnDone(true);            
+        channel.closeOnDone(true);
+// To
+        connection.closeOnDone = true;            
+        channel.closeOnDone - true;

Everything else should work as expected.

+

TODO

    +
  • Write tests con make sure everything keeps working while making changes.
  • +
+

API

See the API JSDocs in the dist directory.

+

Promises

Promises are now supported for Connection and Channel. nodejs versions > 4.0 include
an ES6 Promise implementation. For earlier versions, installing es6-promise and running
require('es6-promise').polyfull() before requiring mikronode will set up Promise support.
You can also globally export Promise from your favorite ES6 compatable Promise library.

+

Examples

Examples (including Promise examples)

+

Connect to a Mikrotik, and add an address to ether1

var MikroNode = require('mikronode');
+
+var connection = MikroNode.getConnection('192.168.88.1','admin','password');
+connection.closeOnDone = true;
+
+connection.connect(function(conn) {
+    var chan=conn.openChannel();
+    chan.closeOnDone = true;
+    chan.write(['/ip/address/add','=interface=ether1','=address=192.168.1.1'], function(c) {
+       c.on('trap',function(data) {
+          console.log('Error setting IP: '+data);
+       });
+       c.on('done',function(data) {
+          console.log('IP Set.');
+       });
+    });
+ });

Writing the program for the example API conversation on the Mikrotik Wiki

DON'T RUN THIS IF YOU'RE CONNECTED VIA ether1! :)

+
var MikroNode = require('mikronode');
+
+ connection.connect(function(conn) {
+
+    conn.closeOnDone = true;
+    var chan2=conn.openChannel(2);
+    chan2.write('/interface/listen',function(chan) {
+       chan.on('read',function(data) {
+          packet=api.parseItems([data])[0];
+          console.log('Interface change: '+JSON.stringify(packet));
+       });
+    });
+
+    var chan3=conn.openChannel(3);
+    chan3.closeOnDone = true
+
+    chan3.write(['/interface/set','=disabled=yes','=.id=ether1'],function(chan) {
+       chan.on('done',function(d,chan) {
+          // We do this here, 'cause we want channel 4 to write after channel 3 is done.
+          var chan4=conn.openChannel(4); // We'll use this later.
+          chan4.closeOnDone = true;
+          chan4.write(['/interface/set','=disabled=no','=.id=ether1'],function() {
+            var chan5=conn.openChannel(5); 
+            chan5.closeOnDone = true;
+            chan5.write('/interface/getall',function(chan) {
+               chan.on('done',function(data) {
+                  packets=api.parseItems(data);
+                  packets.forEach(function(packet) {
+                      console.log('Interface: '+JSON.stringify(packet));
+                  });
+                  chan2.close(); // This should call the /cancel command to stop the listen.
+               });
+            });
+          })
+       });
+    });
+ });

Simplifying the above by reducing the number of channels.

DON'T RUN THIS IF YOU'RE CONNECTED VIA ether1! :)
Notice how the callback embedding is not needed using the syncronous capability.

+
 var MikroNode = require('mikronode');
+
+ var connection = MikroNode.getConnecion('192.168.88.1','admin','password');
+ connection.connect(function(conn) {
+
+    conn.closeOnDone = true; // All channels need to complete before the connection will close.
+    var listenChannel=conn.openChannel();
+    listenChannel.write('/interface/listen',function(chan) {
+       chan.on('read',function(data) {
+          packet=api.parseItems([data])[0];
+          console.log('Interface change: '+JSON.stringify(packet));
+       });
+    });
+
+    var actionChannel=conn.openChannel();
+    // These will run synchronsously
+    actionChannel.write(['/interface/set','=disabled=yes','=.id=ether1']); // don't care to do anything after it's done.
+    actionChannel.write(['/interface/set','=disabled=no','=.id=ether1']); // don't care to do anything after it's done.
+    actionChannel.write('/interface/getall',function(chan) {
+       chan.on('done',function(data) {
+          packets=api.parseItems(data);
+          packets.forEach(function(packet) {
+              console.log('Interface: '+JSON.stringify(packet));
+          });
+          listenChannel.close(); // This should call the /cancel command to stop the listen.
+       });
+    });
+    actionChannel.close(); // The above commands will complete before this is closed.
+ });

A simple Promise scenario

// If your nodejs installation doesn't have Promise support, uncomment
+// the following line
+//require('es6-promise').polyfill();
+// or globally export Promise from your favorite ES6 compatable Promise library.  
+var MikroNode = require('mikronode');
+
+var connection = MikroNode.getConnection(process.argv[2], process.argv[3], process.argv[4], {
+    closeOnDone : true
+});
+
+connection.getConnectPromise().then(function(conn) {
+    conn.getCommandPromise('/ip/address/print').then(function resolved(values) {
+        console.log('Addreses: ' + JSON.stringify(values));
+    }, function rejected(reason) {
+        console.log('Oops: ' + JSON.stringify(reason));
+    });
+});

The methods decodeLength and encodeString were written based on code here on the Mikrotik Wiki.

+

License

(The MIT License)

+

Copyright (c) 2011 Brandon Myers trakkasure@gmail.com

+

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.

+
+ + + + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/dist/mikronode.Channel.html b/dist/mikronode.Channel.html new file mode 100644 index 0000000..701164d --- /dev/null +++ b/dist/mikronode.Channel.html @@ -0,0 +1,2042 @@ + + + + + Class: Channel + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+

Class: Channel

+ + + + +
+ +
+

+ Channel +

+ +
+ +
+
+ + + + +
+
+

+ + new mikronode.Channel(id, conn) +

+ + + + +
+ + +
+
+ + +
+

Channel (should not be instantiated directly)

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
id + + +number + + + + + +
conn + + +mikronode.Connection + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
Fires:
+ + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + +

Members

+ +
+ +
+
+

clearEventsboolean

+
+ + +
+
+ +
+

Clear event listeners on done

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlyclosedboolean

+
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

closeOnDoneboolean

+
+ + +
+
+ +
+

Close channel on done

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlyclosingboolean

+
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlyconnectionmikronode.Connection

+
+ + +
+
+ +
+

Connection

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlyidnumber

+
+ + +
+
+ +
+

Channel ID

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlylastCommandArray.<string>

+
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlyrunningboolean

+
+ + +
+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

saveBufferboolean

+
+ + +
+
+ +
+

Save each line received in a buffer and pass the entire buffer to the done event.
Otherwise the done event will not get all the lines, only the last line. This is
handy when following trailing output from a listen command, where the data could
be endless.

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+ + + +

Methods

+ +
+ +
+
+

+ + close(force) +

+ + + + +
+ + +
+
+ + +
+

Closes the channel This will close the connection if
mikronode.Connection#closeOnDone was set and this was the last channel to
close.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
force + + +boolean + + + + + +

Force close even of there are other commands pending.
Otherwise mark the channel as 'closing' which will prevent new commands
from being started but will let queued ones finish.

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + finalize() +

+ + + + +
+ + +
+
+ + +
+

Calls mikronode.Channel#close(false)

+
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + write(data, parameters, writeCallback) +

+ + + + +
+ + +
+
+ + +
+

Writes data to the channel

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +string +| + +Array.<string> + + + + + + + + + + +

Can be a single string with the command and
optional parameters separated by '\n' or an array of strings with the
command in the first position and the parameters in the rest.

parameters + + +object +| + +Array.<string> + + + + + + + optional + + + + + +

If the first parameter is a command
string, this object will be treated as the parameters for the command.


It can be an array or strings...

+
+['name=value','name=value'...]
+
+ +

or an Object...

+
+{'name': 'value', 'name': 'value'...}
+
writeCallback + + +mikronode.Channel.writeCallback + + + + + + + optional + + + + + +

This will be called just
before write actually writes the data to the connection.

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ +
+ + + +

Type Definitions

+ +
+ +
+
+

+ + mikronode.Channel.writeCallback() +

+ + + + +
+ + +
+
+ + +
+

writeCallback

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescription
+ + +Channel + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ +
+ + + +

Events

+ +
+ +
+
+

+ + event:close +

+ + + + +
+ + +
+
+ + +
+

Emitted when the channel is closed either by an explicit call to
mikronode.Channel#close or when the channel is closed automatically via
mikronode.Channel#closeOnDone

+
+ + + + + + + + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
channel + + +Channel + + + +

The channel originating the event

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + event:done +

+ + + + +
+ + +
+
+ + +
+

Emitted when a command has finished successfully.

+
+ + + + + + + + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +string +| + +Array.<string> + + + +

The data returned by the channel

channel + + +Channel + + + +

The channel originating the event Fatal event.

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + event:error +

+ + + + +
+ + +
+
+ + +
+

Emitted when a non-recoverable error has occurred on the socket. No further commands
can be processed on any channel.

+
+ + + + + + + + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error + + + +

The error object

channel + + +Channel + + + +

The channel originating the event

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + event:timeout +

+ + + + +
+ + +
+
+ + +
+

Emitted when a socket has been idle too long.

+
+ + + + + + + + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
message + + +string + + + +

'Socket Timeout'

socketStillOpen + + +boolean + + + +

If true, communications can continue

channel + + +Channel + + + +

The channel originating the event

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + event:trap +

+ + + + +
+ + +
+
+ + +
+

Emitted when a command has failed. Subsequent commands may succeed.

+
+ + + + + + + + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
trap + + +mikronode.Trap + + + +

The trap object

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ +
+ +
+ +
+ + + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/dist/mikronode.Connection.html b/dist/mikronode.Connection.html new file mode 100644 index 0000000..7b1c1f0 --- /dev/null +++ b/dist/mikronode.Connection.html @@ -0,0 +1,2589 @@ + + + + + Class: Connection + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+

Class: Connection

+ + + + +
+ +
+

+ Connection +

+ +
+ +
+
+ + + + +
+
+

+ + new mikronode.Connection(host, user, password, options) +

+ + + + +
+ + +
+
+ + +
+

Connection

+


This class shouldn't be instantiated directly.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
host + + +string + + + + + + + + + + +

The host name or ip address

user + + +string + + + + + + + + + + +

The user name

password + + +string + + + + + + + + + + +

The users password

options + + +object + + + + + + + optional + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
port + + +number + + + + + + 8728 + + + + + optional + + + + + +

Sets the port if not the standard 8728.

closeOnDone + + +boolean + + + + + + false + + + + + optional + + + + + +

If set, when the last channel closes,
the connection will automatically close.

timeout + + +number + + + + + + 0 + + + + + optional + + + + + +

Sets the socket inactivity timeout. A timeout
does not necessarily mean that an error has occurred, especially if you're
only listening for events.

closeOnTimeout + + +boolean + + + + + + false + + + + + optional + + + + + +

If set, when a socket timeout
happens the connection will automatically close.

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
Fires:
+ + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + +

Members

+ +
+ +
+
+

closeOnDoneboolean

+
+ + +
+
+ +
+

If set, when the last channel closes, the
connection will automatically close.

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlyhoststring

+
+ + +
+
+ +
+

Hostname or ip address

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlypasswordstring

+
+ + +
+
+ +
+

Password

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlyportnumber

+
+ + +
+
+ +
+

Port

+
+ + + +
+ + + + + + + + + + + + + + + +
Default Value:
+
  • 8728
+ + + + + + + +
+ + + +
+ + + +
+
+

readonlystatusstring

+
+ + +
+
+ +
+

Connection status

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlytimeoutnumber

+
+ + +
+
+ +
+

Socket inactivity timeout

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

readonlyuserstring

+
+ + +
+
+ +
+

User ID

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+ + + +

Methods

+ +
+ +
+
+

+ + closeChannel(id) +

+ + + + +
+ + +
+
+ + +
+

Closes the channel specified by id.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
id + + +number + + + + + +

The id of the channel to close

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + connect(callback) +

+ + + + +
+ + +
+
+ + +
+

Opens the socket and performs authentication

+
+ + + + + +
This:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
callback + + +mikronode.Connection.connectCallback + + + + + +

Called when authentication
succeeds and the connection is ready for channel activity

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+ + + +
+
+ + +
+

Returns the channel specified by id.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
id + + +number + + + + + +

The id of the channel desired

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + +
+
+

+ + getCommandPromise(data, parameters, options){Promise} +

+ + + + +
+ + +
+
+ + +
+

Returnes a Promise of a completed command.

+


The promise will resolve when the command completes or reject if there's an error or
trap. If resolved, the result will be an array of instances of DestinationClass (or
Object, if no destination class was specified). If rejected, the result will be an
Error if there was a socket error or timeout, or a mikronode.Trap if the
command failed on the device.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +string +| + +Array.<string> + + + + + + + + + + +

Can be a single string with the command and
optional parameters separated by '\n' or an array of strings with the
command in the first position and the parameters in the rest.

parameters + + +object +| + +Array.<string> + + + + + + + optional + + + + + +

If the first parameter is a command
string, this object will be treated as the parameters for the command.


It can be an array or strings...

+
+['name=value','name=value'...]
+
+ +

or an Object...

+
+{'name': 'value', 'name': 'value'...}
+
options + + +object + + + + + + + optional + + + + + +

A set of options that determine what to do with the
return data (if any). If neither dataClass nor itemClass are provided, the
default behavior will be as though itemClass were set to Object. This will
result in Promise.resolve() being called with an array of plain Objects,
one for each parsed item.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
dontParse + + +boolean + + + + + + + optional + + + + + +

If true, Promise.resolve() will be called
with the unaltered data array provided by the channel's 'done' event.

dataClass + + +class + + + + + + + optional + + + + + +

If provided, this class will be instantiated
with the data array provided by the channel's 'done' event as the
constructor's sole argument. Promise.resolve() will then be called with
this object.

itemClass + + +class + + + + + + + optional + + + + + +

If provided, {mikronode.parseItems} will be
called on the returned data and this class will be instantiated once for
each resulting item. The item object will be passed as the sole argument
to the constructor. An array of itemClass objects will be passed to
Promise.resolve().

itemKey + + +string + + + + + + + optional + + + + + +

If provided, instead of an array of parsed
objects being passed to Promise.resolve(), the parsed objects will be
added to a wrapper object using the value of itemKey as the property name.

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + +
+
+

+ + getConnectPromise(){Promise} +

+ + + + +
+ + +
+
+ + +
+

Returns a Promise for an open connection.

+


The promise will resolve when the connection is ready for use or reject if there's
an error or trap. If resolved, the result object will be the
mikronode.Connection with authentication completed and ready for channels.
If rejected, the result object will be an Error if there was a socket error or
timeout during connection or login or a mikronode.Trap if there was a
problem with the login credentials.

+

+

+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+ + +
+

Opens a new Channel

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
id + + +number + + + + + + next available + + + + + optional + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + +

Type Definitions

+ +
+ +
+
+

+ + mikronode.Connection.connectCallback() +

+ + + + +
+ + +
+
+ + +
+

connectCallback

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
TypeDescription
+ + +mikronode.Connection + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ +
+ + + +

Events

+ +
+ +
+
+

+ + event:close +

+ + + + +
+ + +
+
+ + +
+

Emitted when the connection is closed either by an explicit call to
mikronode.Connection#close or when the connection is closed automatically
via mikronode.Connection#closeOnDone

+
+ + + + + + + + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
connection + + +mikronode.Connection + + + +

The connection originating the event

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + event:error +

+ + + + +
+ + +
+
+ + +
+

Emitted when a non-recoverable error has occurred on the socket. No further commands
can be processed on any channel.

+
+ + + + + + + + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
error + + +error + + + +

The error object

connection + + +mikronode.Connection + + + +

The connection originating the event

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + event:timeout +

+ + + + +
+ + +
+
+ + +
+

Emitted when a socket has been idle too long.

+
+ + + + + + + + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
message + + +string + + + +

'Socket Timeout'

socketStillOpen + + +boolean + + + +

If true, communications can continue

connection + + +mikronode.Connection + + + +

The connection originating the event

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + + +
+
+

+ + event:trap +

+ + + + +
+ + +
+
+ + +
+

Emitted when a login has failed. No further commands can be processed on any
channel.

+
+ + + + + + + + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
trap + + +mikronode.Trap + + + +

The trap object

+ + + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ +
+ +
+ +
+ + + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/dist/mikronode.MikroNode.html b/dist/mikronode.MikroNode.html new file mode 100644 index 0000000..629eda4 --- /dev/null +++ b/dist/mikronode.MikroNode.html @@ -0,0 +1,874 @@ + + + + + Class: MikroNode + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+

Class: MikroNode

+ + + + +
+ +
+

+ MikroNode +

+ +
+ +
+
+ + + + +
+
+

+ + new mikronode.MikroNode() +

+ + + + +
+ + +
+
+ + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + + + +

Methods

+ +
+ +
+
+

+ + staticmikronode.MikroNode.getConnection(host, user, password, options){mikronode.Connection} +

+ + + + +
+ + +
+
+ + +
+

Creates or returns a Connection object.

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
host + + +string + + + + + + + + + + +

The host name or ip address

user + + +string + + + + + + + + + + +

The user name

password + + +string + + + + + + + + + + +

The users password

options + + +object + + + + + + + optional + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDefaultDescription
port + + +number + + + + + + 8728 + + + + + optional + + + + + +

Sets the port if not the standard 8728.

closeOnDone + + +boolean + + + + + + false + + + + + optional + + + + + +

If set, when the last channel closes,
the connection will automatically close.

timeout + + +number + + + + + + 0 + + + + + optional + + + + + +

Sets the socket inactivity timeout. A timeout
does not necessarily mean that an error has occurred, especially if you're
only listening for events.

closeOnTimeout + + +boolean + + + + + + false + + + + + optional + + + + + +

If set, when a socket timeout
happens the connection will automatically close.

+
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
Example
+ + + +
+var MikroNode = require('mikronode');
+
+var connection = MikroNode.getConnection('192.168.88.1', 'admin', 'mypassword', {
+    timeout : 4,
+    closeOnDone : true,
+    closeOnTimeout : true,
+});
+
+ + + +
+ + + +
+
+

+ + staticmikronode.MikroNode.parseItems(data){Array.<object>} +

+ + + + +
+ + +
+
+ + +
+

Parse !re return records into an array of objects

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
data + + +Array.<string> + + + + + +

The data[] returned from Channel.on('done')

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + + + + + + +
+ +
+ + + + + +
+ +
+ + + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/dist/mikronode.Trap.html b/dist/mikronode.Trap.html new file mode 100644 index 0000000..ffc3d70 --- /dev/null +++ b/dist/mikronode.Trap.html @@ -0,0 +1,811 @@ + + + + + Class: Trap + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+

Class: Trap

+ + + + +
+ +
+

+ Trap +

+ +
+ +
+
+ + + + +
+
+

+ + new mikronode.Trap(message) +

+ + + + +
+ + +
+
+ + +
+

Trap

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
message + + +string + + + + + + + optional + + + + + +
+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + + + + + + + + + + +
+ + +
+ + + + + + + + + + + + +

Members

+ +
+ +
+
+

category

+
+ + +
+
+ + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
category + + +string + + + + + + <optional>
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

channel

+
+ + +
+
+ + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
channel + + +mikronode.Channel + + + + + + <optional>
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

channelId

+
+ + +
+
+ + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeArgumentDescription
channelId + + +string + + + + + + <optional>
+ + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + +
+
+

message

+
+ + +
+
+ + + +
+ + +
Properties:
+ +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
message + + +string + + + +
+ + + + + + + + + + + + + + + + + + + + + + +
+ + + +
+ +
+ + + + + + + +
+ +
+ + + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/dist/module-mikronode.html b/dist/module-mikronode.html new file mode 100644 index 0000000..7af4891 --- /dev/null +++ b/dist/module-mikronode.html @@ -0,0 +1,332 @@ + + + + + Module: mikronode + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ + +
+

Module: mikronode

+ + + + +
+ +
+

+ mikronode +

+ +
+ +
+
+ + + + +

MikroNode

+ + + +
+ + + + + + + + + + + + + + + + + + + + + +
+ + + + +
+ + + + + + +

Requires

+ +
    +
  • module:net
  • + +
  • module:events
  • + +
  • module:crypto
  • + +
  • module:util
  • + +
  • module:debug
  • +
+ + + + + + + + + + + + + +
+ +
+ + + + + + + + +
+
+ + + + + \ No newline at end of file diff --git a/dist/scripts/bootstrap.min.js b/dist/scripts/bootstrap.min.js new file mode 100644 index 0000000..1a6258e --- /dev/null +++ b/dist/scripts/bootstrap.min.js @@ -0,0 +1,7 @@ +/*! + * Bootstrap v3.0.3 (http://getbootstrap.com) + * Copyright 2013 Twitter, Inc. + * Licensed under http://www.apache.org/licenses/LICENSE-2.0 + */ + +if("undefined"==typeof jQuery)throw new Error("Bootstrap requires jQuery");+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]}}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one(a.support.transition.end,function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b()})}(jQuery),+function(a){"use strict";var b='[data-dismiss="alert"]',c=function(c){a(c).on("click",b,this.close)};c.prototype.close=function(b){function c(){f.trigger("closed.bs.alert").remove()}var d=a(this),e=d.attr("data-target");e||(e=d.attr("href"),e=e&&e.replace(/.*(?=#[^\s]*$)/,""));var f=a(e);b&&b.preventDefault(),f.length||(f=d.hasClass("alert")?d:d.parent()),f.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(f.removeClass("in"),a.support.transition&&f.hasClass("fade")?f.one(a.support.transition.end,c).emulateTransitionEnd(150):c())};var d=a.fn.alert;a.fn.alert=function(b){return this.each(function(){var d=a(this),e=d.data("bs.alert");e||d.data("bs.alert",e=new c(this)),"string"==typeof b&&e[b].call(d)})},a.fn.alert.Constructor=c,a.fn.alert.noConflict=function(){return a.fn.alert=d,this},a(document).on("click.bs.alert.data-api",b,c.prototype.close)}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d)};b.DEFAULTS={loadingText:"loading..."},b.prototype.setState=function(a){var b="disabled",c=this.$element,d=c.is("input")?"val":"html",e=c.data();a+="Text",e.resetText||c.data("resetText",c[d]()),c[d](e[a]||this.options[a]),setTimeout(function(){"loadingText"==a?c.addClass(b).attr(b,b):c.removeClass(b).removeAttr(b)},0)},b.prototype.toggle=function(){var a=this.$element.closest('[data-toggle="buttons"]'),b=!0;if(a.length){var c=this.$element.find("input");"radio"===c.prop("type")&&(c.prop("checked")&&this.$element.hasClass("active")?b=!1:a.find(".active").removeClass("active")),b&&c.prop("checked",!this.$element.hasClass("active")).trigger("change")}b&&this.$element.toggleClass("active")};var c=a.fn.button;a.fn.button=function(c){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof c&&c;e||d.data("bs.button",e=new b(this,f)),"toggle"==c?e.toggle():c&&e.setState(c)})},a.fn.button.Constructor=b,a.fn.button.noConflict=function(){return a.fn.button=c,this},a(document).on("click.bs.button.data-api","[data-toggle^=button]",function(b){var c=a(b.target);c.hasClass("btn")||(c=c.closest(".btn")),c.button("toggle"),b.preventDefault()})}(jQuery),+function(a){"use strict";var b=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=this.sliding=this.interval=this.$active=this.$items=null,"hover"==this.options.pause&&this.$element.on("mouseenter",a.proxy(this.pause,this)).on("mouseleave",a.proxy(this.cycle,this))};b.DEFAULTS={interval:5e3,pause:"hover",wrap:!0},b.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},b.prototype.getActiveIndex=function(){return this.$active=this.$element.find(".item.active"),this.$items=this.$active.parent().children(),this.$items.index(this.$active)},b.prototype.to=function(b){var c=this,d=this.getActiveIndex();return b>this.$items.length-1||0>b?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){c.to(b)}):d==b?this.pause().cycle():this.slide(b>d?"next":"prev",a(this.$items[b]))},b.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition.end&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},b.prototype.next=function(){return this.sliding?void 0:this.slide("next")},b.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},b.prototype.slide=function(b,c){var d=this.$element.find(".item.active"),e=c||d[b](),f=this.interval,g="next"==b?"left":"right",h="next"==b?"first":"last",i=this;if(!e.length){if(!this.options.wrap)return;e=this.$element.find(".item")[h]()}this.sliding=!0,f&&this.pause();var j=a.Event("slide.bs.carousel",{relatedTarget:e[0],direction:g});if(!e.hasClass("active")){if(this.$indicators.length&&(this.$indicators.find(".active").removeClass("active"),this.$element.one("slid.bs.carousel",function(){var b=a(i.$indicators.children()[i.getActiveIndex()]);b&&b.addClass("active")})),a.support.transition&&this.$element.hasClass("slide")){if(this.$element.trigger(j),j.isDefaultPrevented())return;e.addClass(b),e[0].offsetWidth,d.addClass(g),e.addClass(g),d.one(a.support.transition.end,function(){e.removeClass([b,g].join(" ")).addClass("active"),d.removeClass(["active",g].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger("slid.bs.carousel")},0)}).emulateTransitionEnd(600)}else{if(this.$element.trigger(j),j.isDefaultPrevented())return;d.removeClass("active"),e.addClass("active"),this.sliding=!1,this.$element.trigger("slid.bs.carousel")}return f&&this.cycle(),this}};var c=a.fn.carousel;a.fn.carousel=function(c){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c),g="string"==typeof c?c:f.slide;e||d.data("bs.carousel",e=new b(this,f)),"number"==typeof c?e.to(c):g?e[g]():f.interval&&e.pause().cycle()})},a.fn.carousel.Constructor=b,a.fn.carousel.noConflict=function(){return a.fn.carousel=c,this},a(document).on("click.bs.carousel.data-api","[data-slide], [data-slide-to]",function(b){var c,d=a(this),e=a(d.attr("data-target")||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"")),f=a.extend({},e.data(),d.data()),g=d.attr("data-slide-to");g&&(f.interval=!1),e.carousel(f),(g=d.attr("data-slide-to"))&&e.data("bs.carousel").to(g),b.preventDefault()}),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var b=a(this);b.carousel(b.data())})})}(jQuery),+function(a){"use strict";var b=function(c,d){this.$element=a(c),this.options=a.extend({},b.DEFAULTS,d),this.transitioning=null,this.options.parent&&(this.$parent=a(this.options.parent)),this.options.toggle&&this.toggle()};b.DEFAULTS={toggle:!0},b.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},b.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b=a.Event("show.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.$parent&&this.$parent.find("> .panel > .in");if(c&&c.length){var d=c.data("bs.collapse");if(d&&d.transitioning)return;c.collapse("hide"),d||c.data("bs.collapse",null)}var e=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[e](0),this.transitioning=1;var f=function(){this.$element.removeClass("collapsing").addClass("in")[e]("auto"),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return f.call(this);var g=a.camelCase(["scroll",e].join("-"));this.$element.one(a.support.transition.end,a.proxy(f,this)).emulateTransitionEnd(350)[e](this.$element[0][g])}}},b.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse").removeClass("in"),this.transitioning=1;var d=function(){this.transitioning=0,this.$element.trigger("hidden.bs.collapse").removeClass("collapsing").addClass("collapse")};return a.support.transition?(this.$element[c](0).one(a.support.transition.end,a.proxy(d,this)).emulateTransitionEnd(350),void 0):d.call(this)}}},b.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()};var c=a.fn.collapse;a.fn.collapse=function(c){return this.each(function(){var d=a(this),e=d.data("bs.collapse"),f=a.extend({},b.DEFAULTS,d.data(),"object"==typeof c&&c);e||d.data("bs.collapse",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.collapse.Constructor=b,a.fn.collapse.noConflict=function(){return a.fn.collapse=c,this},a(document).on("click.bs.collapse.data-api","[data-toggle=collapse]",function(b){var c,d=a(this),e=d.attr("data-target")||b.preventDefault()||(c=d.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,""),f=a(e),g=f.data("bs.collapse"),h=g?"toggle":d.data(),i=d.attr("data-parent"),j=i&&a(i);g&&g.transitioning||(j&&j.find('[data-toggle=collapse][data-parent="'+i+'"]').not(d).addClass("collapsed"),d[f.hasClass("in")?"addClass":"removeClass"]("collapsed")),f.collapse(h)})}(jQuery),+function(a){"use strict";function b(){a(d).remove(),a(e).each(function(b){var d=c(a(this));d.hasClass("open")&&(d.trigger(b=a.Event("hide.bs.dropdown")),b.isDefaultPrevented()||d.removeClass("open").trigger("hidden.bs.dropdown"))})}function c(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}var d=".dropdown-backdrop",e="[data-toggle=dropdown]",f=function(b){a(b).on("click.bs.dropdown",this.toggle)};f.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=c(e),g=f.hasClass("open");if(b(),!g){if("ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(''}),b.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),b.prototype.constructor=b,b.prototype.getDefaults=function(){return b.DEFAULTS},b.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content")[this.options.html?"html":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},b.prototype.hasContent=function(){return this.getTitle()||this.getContent()},b.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},b.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")},b.prototype.tip=function(){return this.$tip||(this.$tip=a(this.options.template)),this.$tip};var c=a.fn.popover;a.fn.popover=function(c){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof c&&c;e||d.data("bs.popover",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.popover.Constructor=b,a.fn.popover.noConflict=function(){return a.fn.popover=c,this}}(jQuery),+function(a){"use strict";function b(c,d){var e,f=a.proxy(this.process,this);this.$element=a(c).is("body")?a(window):a(c),this.$body=a("body"),this.$scrollElement=this.$element.on("scroll.bs.scroll-spy.data-api",f),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||(e=a(c).attr("href"))&&e.replace(/.*(?=#[^\s]+$)/,"")||"")+" .nav li > a",this.offsets=a([]),this.targets=a([]),this.activeTarget=null,this.refresh(),this.process()}b.DEFAULTS={offset:10},b.prototype.refresh=function(){var b=this.$element[0]==window?"offset":"position";this.offsets=a([]),this.targets=a([]);var c=this;this.$body.find(this.selector).map(function(){var d=a(this),e=d.data("target")||d.attr("href"),f=/^#\w/.test(e)&&a(e);return f&&f.length&&[[f[b]().top+(!a.isWindow(c.$scrollElement.get(0))&&c.$scrollElement.scrollTop()),e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){c.offsets.push(this[0]),c.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.$scrollElement[0].scrollHeight||this.$body[0].scrollHeight,d=c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(b>=d)return g!=(a=f.last()[0])&&this.activate(a);for(a=e.length;a--;)g!=f[a]&&b>=e[a]&&(!e[a+1]||b<=e[a+1])&&this.activate(f[a])},b.prototype.activate=function(b){this.activeTarget=b,a(this.selector).parents(".active").removeClass("active");var c=this.selector+'[data-target="'+b+'"],'+this.selector+'[href="'+b+'"]',d=a(c).parents("li").addClass("active");d.parent(".dropdown-menu").length&&(d=d.closest("li.dropdown").addClass("active")),d.trigger("activate.bs.scrollspy")};var c=a.fn.scrollspy;a.fn.scrollspy=function(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.scrollspy.Constructor=b,a.fn.scrollspy.noConflict=function(){return a.fn.scrollspy=c,this},a(window).on("load",function(){a('[data-spy="scroll"]').each(function(){var b=a(this);b.scrollspy(b.data())})})}(jQuery),+function(a){"use strict";var b=function(b){this.element=a(b)};b.prototype.show=function(){var b=this.element,c=b.closest("ul:not(.dropdown-menu)"),d=b.data("target");if(d||(d=b.attr("href"),d=d&&d.replace(/.*(?=#[^\s]*$)/,"")),!b.parent("li").hasClass("active")){var e=c.find(".active:last a")[0],f=a.Event("show.bs.tab",{relatedTarget:e});if(b.trigger(f),!f.isDefaultPrevented()){var g=a(d);this.activate(b.parent("li"),c),this.activate(g,g.parent(),function(){b.trigger({type:"shown.bs.tab",relatedTarget:e})})}}},b.prototype.activate=function(b,c,d){function e(){f.removeClass("active").find("> .dropdown-menu > .active").removeClass("active"),b.addClass("active"),g?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu")&&b.closest("li.dropdown").addClass("active"),d&&d()}var f=c.find("> .active"),g=d&&a.support.transition&&f.hasClass("fade");g?f.one(a.support.transition.end,e).emulateTransitionEnd(150):e(),f.removeClass("in")};var c=a.fn.tab;a.fn.tab=function(c){return this.each(function(){var d=a(this),e=d.data("bs.tab");e||d.data("bs.tab",e=new b(this)),"string"==typeof c&&e[c]()})},a.fn.tab.Constructor=b,a.fn.tab.noConflict=function(){return a.fn.tab=c,this},a(document).on("click.bs.tab.data-api",'[data-toggle="tab"], [data-toggle="pill"]',function(b){b.preventDefault(),a(this).tab("show")})}(jQuery),+function(a){"use strict";var b=function(c,d){this.options=a.extend({},b.DEFAULTS,d),this.$window=a(window).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(c),this.affixed=this.unpin=null,this.checkPosition()};b.RESET="affix affix-top affix-bottom",b.DEFAULTS={offset:0},b.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},b.prototype.checkPosition=function(){if(this.$element.is(":visible")){var c=a(document).height(),d=this.$window.scrollTop(),e=this.$element.offset(),f=this.options.offset,g=f.top,h=f.bottom;"object"!=typeof f&&(h=g=f),"function"==typeof g&&(g=f.top()),"function"==typeof h&&(h=f.bottom());var i=null!=this.unpin&&d+this.unpin<=e.top?!1:null!=h&&e.top+this.$element.height()>=c-h?"bottom":null!=g&&g>=d?"top":!1;this.affixed!==i&&(this.unpin&&this.$element.css("top",""),this.affixed=i,this.unpin="bottom"==i?e.top-d:null,this.$element.removeClass(b.RESET).addClass("affix"+(i?"-"+i:"")),"bottom"==i&&this.$element.offset({top:document.body.offsetHeight-h-this.$element.height()}))}};var c=a.fn.affix;a.fn.affix=function(c){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof c&&c;e||d.data("bs.affix",e=new b(this,f)),"string"==typeof c&&e[c]()})},a.fn.affix.Constructor=b,a.fn.affix.noConflict=function(){return a.fn.affix=c,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var b=a(this),c=b.data();c.offset=c.offset||{},c.offsetBottom&&(c.offset.bottom=c.offsetBottom),c.offsetTop&&(c.offset.top=c.offsetTop),b.affix(c)})})}(jQuery); \ No newline at end of file diff --git a/dist/scripts/jquery.min.js b/dist/scripts/jquery.min.js new file mode 100644 index 0000000..9a85bd3 --- /dev/null +++ b/dist/scripts/jquery.min.js @@ -0,0 +1,6 @@ +/*! jQuery v2.0.3 | (c) 2005, 2013 jQuery Foundation, Inc. | jquery.org/license +//@ sourceMappingURL=jquery.min.map +*/ +(function(e,undefined){var t,n,r=typeof undefined,i=e.location,o=e.document,s=o.documentElement,a=e.jQuery,u=e.$,l={},c=[],p="2.0.3",f=c.concat,h=c.push,d=c.slice,g=c.indexOf,m=l.toString,y=l.hasOwnProperty,v=p.trim,x=function(e,n){return new x.fn.init(e,n,t)},b=/[+-]?(?:\d*\.|)\d+(?:[eE][+-]?\d+|)/.source,w=/\S+/g,T=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,C=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,k=/^-ms-/,N=/-([\da-z])/gi,E=function(e,t){return t.toUpperCase()},S=function(){o.removeEventListener("DOMContentLoaded",S,!1),e.removeEventListener("load",S,!1),x.ready()};x.fn=x.prototype={jquery:p,constructor:x,init:function(e,t,n){var r,i;if(!e)return this;if("string"==typeof e){if(r="<"===e.charAt(0)&&">"===e.charAt(e.length-1)&&e.length>=3?[null,e,null]:T.exec(e),!r||!r[1]&&t)return!t||t.jquery?(t||n).find(e):this.constructor(t).find(e);if(r[1]){if(t=t instanceof x?t[0]:t,x.merge(this,x.parseHTML(r[1],t&&t.nodeType?t.ownerDocument||t:o,!0)),C.test(r[1])&&x.isPlainObject(t))for(r in t)x.isFunction(this[r])?this[r](t[r]):this.attr(r,t[r]);return this}return i=o.getElementById(r[2]),i&&i.parentNode&&(this.length=1,this[0]=i),this.context=o,this.selector=e,this}return e.nodeType?(this.context=this[0]=e,this.length=1,this):x.isFunction(e)?n.ready(e):(e.selector!==undefined&&(this.selector=e.selector,this.context=e.context),x.makeArray(e,this))},selector:"",length:0,toArray:function(){return d.call(this)},get:function(e){return null==e?this.toArray():0>e?this[this.length+e]:this[e]},pushStack:function(e){var t=x.merge(this.constructor(),e);return t.prevObject=this,t.context=this.context,t},each:function(e,t){return x.each(this,e,t)},ready:function(e){return x.ready.promise().done(e),this},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(e){var t=this.length,n=+e+(0>e?t:0);return this.pushStack(n>=0&&t>n?[this[n]]:[])},map:function(e){return this.pushStack(x.map(this,function(t,n){return e.call(t,n,t)}))},end:function(){return this.prevObject||this.constructor(null)},push:h,sort:[].sort,splice:[].splice},x.fn.init.prototype=x.fn,x.extend=x.fn.extend=function(){var e,t,n,r,i,o,s=arguments[0]||{},a=1,u=arguments.length,l=!1;for("boolean"==typeof s&&(l=s,s=arguments[1]||{},a=2),"object"==typeof s||x.isFunction(s)||(s={}),u===a&&(s=this,--a);u>a;a++)if(null!=(e=arguments[a]))for(t in e)n=s[t],r=e[t],s!==r&&(l&&r&&(x.isPlainObject(r)||(i=x.isArray(r)))?(i?(i=!1,o=n&&x.isArray(n)?n:[]):o=n&&x.isPlainObject(n)?n:{},s[t]=x.extend(l,o,r)):r!==undefined&&(s[t]=r));return s},x.extend({expando:"jQuery"+(p+Math.random()).replace(/\D/g,""),noConflict:function(t){return e.$===x&&(e.$=u),t&&e.jQuery===x&&(e.jQuery=a),x},isReady:!1,readyWait:1,holdReady:function(e){e?x.readyWait++:x.ready(!0)},ready:function(e){(e===!0?--x.readyWait:x.isReady)||(x.isReady=!0,e!==!0&&--x.readyWait>0||(n.resolveWith(o,[x]),x.fn.trigger&&x(o).trigger("ready").off("ready")))},isFunction:function(e){return"function"===x.type(e)},isArray:Array.isArray,isWindow:function(e){return null!=e&&e===e.window},isNumeric:function(e){return!isNaN(parseFloat(e))&&isFinite(e)},type:function(e){return null==e?e+"":"object"==typeof e||"function"==typeof e?l[m.call(e)]||"object":typeof e},isPlainObject:function(e){if("object"!==x.type(e)||e.nodeType||x.isWindow(e))return!1;try{if(e.constructor&&!y.call(e.constructor.prototype,"isPrototypeOf"))return!1}catch(t){return!1}return!0},isEmptyObject:function(e){var t;for(t in e)return!1;return!0},error:function(e){throw Error(e)},parseHTML:function(e,t,n){if(!e||"string"!=typeof e)return null;"boolean"==typeof t&&(n=t,t=!1),t=t||o;var r=C.exec(e),i=!n&&[];return r?[t.createElement(r[1])]:(r=x.buildFragment([e],t,i),i&&x(i).remove(),x.merge([],r.childNodes))},parseJSON:JSON.parse,parseXML:function(e){var t,n;if(!e||"string"!=typeof e)return null;try{n=new DOMParser,t=n.parseFromString(e,"text/xml")}catch(r){t=undefined}return(!t||t.getElementsByTagName("parsererror").length)&&x.error("Invalid XML: "+e),t},noop:function(){},globalEval:function(e){var t,n=eval;e=x.trim(e),e&&(1===e.indexOf("use strict")?(t=o.createElement("script"),t.text=e,o.head.appendChild(t).parentNode.removeChild(t)):n(e))},camelCase:function(e){return e.replace(k,"ms-").replace(N,E)},nodeName:function(e,t){return e.nodeName&&e.nodeName.toLowerCase()===t.toLowerCase()},each:function(e,t,n){var r,i=0,o=e.length,s=j(e);if(n){if(s){for(;o>i;i++)if(r=t.apply(e[i],n),r===!1)break}else for(i in e)if(r=t.apply(e[i],n),r===!1)break}else if(s){for(;o>i;i++)if(r=t.call(e[i],i,e[i]),r===!1)break}else for(i in e)if(r=t.call(e[i],i,e[i]),r===!1)break;return e},trim:function(e){return null==e?"":v.call(e)},makeArray:function(e,t){var n=t||[];return null!=e&&(j(Object(e))?x.merge(n,"string"==typeof e?[e]:e):h.call(n,e)),n},inArray:function(e,t,n){return null==t?-1:g.call(t,e,n)},merge:function(e,t){var n=t.length,r=e.length,i=0;if("number"==typeof n)for(;n>i;i++)e[r++]=t[i];else while(t[i]!==undefined)e[r++]=t[i++];return e.length=r,e},grep:function(e,t,n){var r,i=[],o=0,s=e.length;for(n=!!n;s>o;o++)r=!!t(e[o],o),n!==r&&i.push(e[o]);return i},map:function(e,t,n){var r,i=0,o=e.length,s=j(e),a=[];if(s)for(;o>i;i++)r=t(e[i],i,n),null!=r&&(a[a.length]=r);else for(i in e)r=t(e[i],i,n),null!=r&&(a[a.length]=r);return f.apply([],a)},guid:1,proxy:function(e,t){var n,r,i;return"string"==typeof t&&(n=e[t],t=e,e=n),x.isFunction(e)?(r=d.call(arguments,2),i=function(){return e.apply(t||this,r.concat(d.call(arguments)))},i.guid=e.guid=e.guid||x.guid++,i):undefined},access:function(e,t,n,r,i,o,s){var a=0,u=e.length,l=null==n;if("object"===x.type(n)){i=!0;for(a in n)x.access(e,t,a,n[a],!0,o,s)}else if(r!==undefined&&(i=!0,x.isFunction(r)||(s=!0),l&&(s?(t.call(e,r),t=null):(l=t,t=function(e,t,n){return l.call(x(e),n)})),t))for(;u>a;a++)t(e[a],n,s?r:r.call(e[a],a,t(e[a],n)));return i?e:l?t.call(e):u?t(e[0],n):o},now:Date.now,swap:function(e,t,n,r){var i,o,s={};for(o in t)s[o]=e.style[o],e.style[o]=t[o];i=n.apply(e,r||[]);for(o in t)e.style[o]=s[o];return i}}),x.ready.promise=function(t){return n||(n=x.Deferred(),"complete"===o.readyState?setTimeout(x.ready):(o.addEventListener("DOMContentLoaded",S,!1),e.addEventListener("load",S,!1))),n.promise(t)},x.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(e,t){l["[object "+t+"]"]=t.toLowerCase()});function j(e){var t=e.length,n=x.type(e);return x.isWindow(e)?!1:1===e.nodeType&&t?!0:"array"===n||"function"!==n&&(0===t||"number"==typeof t&&t>0&&t-1 in e)}t=x(o),function(e,undefined){var t,n,r,i,o,s,a,u,l,c,p,f,h,d,g,m,y,v="sizzle"+-new Date,b=e.document,w=0,T=0,C=st(),k=st(),N=st(),E=!1,S=function(e,t){return e===t?(E=!0,0):0},j=typeof undefined,D=1<<31,A={}.hasOwnProperty,L=[],q=L.pop,H=L.push,O=L.push,F=L.slice,P=L.indexOf||function(e){var t=0,n=this.length;for(;n>t;t++)if(this[t]===e)return t;return-1},R="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",W="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",$=W.replace("w","w#"),B="\\["+M+"*("+W+")"+M+"*(?:([*^$|!~]?=)"+M+"*(?:(['\"])((?:\\\\.|[^\\\\])*?)\\3|("+$+")|)|)"+M+"*\\]",I=":("+W+")(?:\\(((['\"])((?:\\\\.|[^\\\\])*?)\\3|((?:\\\\.|[^\\\\()[\\]]|"+B.replace(3,8)+")*)|.*)\\)|)",z=RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),_=RegExp("^"+M+"*,"+M+"*"),X=RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=RegExp(M+"*[+~]"),Y=RegExp("="+M+"*([^\\]'\"]*)"+M+"*\\]","g"),V=RegExp(I),G=RegExp("^"+$+"$"),J={ID:RegExp("^#("+W+")"),CLASS:RegExp("^\\.("+W+")"),TAG:RegExp("^("+W.replace("w","w*")+")"),ATTR:RegExp("^"+B),PSEUDO:RegExp("^"+I),CHILD:RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:RegExp("^(?:"+R+")$","i"),needsContext:RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Q=/^[^{]+\{\s*\[native \w/,K=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,Z=/^(?:input|select|textarea|button)$/i,et=/^h\d$/i,tt=/'|\\/g,nt=RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),rt=function(e,t,n){var r="0x"+t-65536;return r!==r||n?t:0>r?String.fromCharCode(r+65536):String.fromCharCode(55296|r>>10,56320|1023&r)};try{O.apply(L=F.call(b.childNodes),b.childNodes),L[b.childNodes.length].nodeType}catch(it){O={apply:L.length?function(e,t){H.apply(e,F.call(t))}:function(e,t){var n=e.length,r=0;while(e[n++]=t[r++]);e.length=n-1}}}function ot(e,t,r,i){var o,s,a,u,l,f,g,m,x,w;if((t?t.ownerDocument||t:b)!==p&&c(t),t=t||p,r=r||[],!e||"string"!=typeof e)return r;if(1!==(u=t.nodeType)&&9!==u)return[];if(h&&!i){if(o=K.exec(e))if(a=o[1]){if(9===u){if(s=t.getElementById(a),!s||!s.parentNode)return r;if(s.id===a)return r.push(s),r}else if(t.ownerDocument&&(s=t.ownerDocument.getElementById(a))&&y(t,s)&&s.id===a)return r.push(s),r}else{if(o[2])return O.apply(r,t.getElementsByTagName(e)),r;if((a=o[3])&&n.getElementsByClassName&&t.getElementsByClassName)return O.apply(r,t.getElementsByClassName(a)),r}if(n.qsa&&(!d||!d.test(e))){if(m=g=v,x=t,w=9===u&&e,1===u&&"object"!==t.nodeName.toLowerCase()){f=gt(e),(g=t.getAttribute("id"))?m=g.replace(tt,"\\$&"):t.setAttribute("id",m),m="[id='"+m+"'] ",l=f.length;while(l--)f[l]=m+mt(f[l]);x=U.test(e)&&t.parentNode||t,w=f.join(",")}if(w)try{return O.apply(r,x.querySelectorAll(w)),r}catch(T){}finally{g||t.removeAttribute("id")}}}return kt(e.replace(z,"$1"),t,r,i)}function st(){var e=[];function t(n,r){return e.push(n+=" ")>i.cacheLength&&delete t[e.shift()],t[n]=r}return t}function at(e){return e[v]=!0,e}function ut(e){var t=p.createElement("div");try{return!!e(t)}catch(n){return!1}finally{t.parentNode&&t.parentNode.removeChild(t),t=null}}function lt(e,t){var n=e.split("|"),r=e.length;while(r--)i.attrHandle[n[r]]=t}function ct(e,t){var n=t&&e,r=n&&1===e.nodeType&&1===t.nodeType&&(~t.sourceIndex||D)-(~e.sourceIndex||D);if(r)return r;if(n)while(n=n.nextSibling)if(n===t)return-1;return e?1:-1}function pt(e){return function(t){var n=t.nodeName.toLowerCase();return"input"===n&&t.type===e}}function ft(e){return function(t){var n=t.nodeName.toLowerCase();return("input"===n||"button"===n)&&t.type===e}}function ht(e){return at(function(t){return t=+t,at(function(n,r){var i,o=e([],n.length,t),s=o.length;while(s--)n[i=o[s]]&&(n[i]=!(r[i]=n[i]))})})}s=ot.isXML=function(e){var t=e&&(e.ownerDocument||e).documentElement;return t?"HTML"!==t.nodeName:!1},n=ot.support={},c=ot.setDocument=function(e){var t=e?e.ownerDocument||e:b,r=t.defaultView;return t!==p&&9===t.nodeType&&t.documentElement?(p=t,f=t.documentElement,h=!s(t),r&&r.attachEvent&&r!==r.top&&r.attachEvent("onbeforeunload",function(){c()}),n.attributes=ut(function(e){return e.className="i",!e.getAttribute("className")}),n.getElementsByTagName=ut(function(e){return e.appendChild(t.createComment("")),!e.getElementsByTagName("*").length}),n.getElementsByClassName=ut(function(e){return e.innerHTML="
",e.firstChild.className="i",2===e.getElementsByClassName("i").length}),n.getById=ut(function(e){return f.appendChild(e).id=v,!t.getElementsByName||!t.getElementsByName(v).length}),n.getById?(i.find.ID=function(e,t){if(typeof t.getElementById!==j&&h){var n=t.getElementById(e);return n&&n.parentNode?[n]:[]}},i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){return e.getAttribute("id")===t}}):(delete i.find.ID,i.filter.ID=function(e){var t=e.replace(nt,rt);return function(e){var n=typeof e.getAttributeNode!==j&&e.getAttributeNode("id");return n&&n.value===t}}),i.find.TAG=n.getElementsByTagName?function(e,t){return typeof t.getElementsByTagName!==j?t.getElementsByTagName(e):undefined}:function(e,t){var n,r=[],i=0,o=t.getElementsByTagName(e);if("*"===e){while(n=o[i++])1===n.nodeType&&r.push(n);return r}return o},i.find.CLASS=n.getElementsByClassName&&function(e,t){return typeof t.getElementsByClassName!==j&&h?t.getElementsByClassName(e):undefined},g=[],d=[],(n.qsa=Q.test(t.querySelectorAll))&&(ut(function(e){e.innerHTML="",e.querySelectorAll("[selected]").length||d.push("\\["+M+"*(?:value|"+R+")"),e.querySelectorAll(":checked").length||d.push(":checked")}),ut(function(e){var n=t.createElement("input");n.setAttribute("type","hidden"),e.appendChild(n).setAttribute("t",""),e.querySelectorAll("[t^='']").length&&d.push("[*^$]="+M+"*(?:''|\"\")"),e.querySelectorAll(":enabled").length||d.push(":enabled",":disabled"),e.querySelectorAll("*,:x"),d.push(",.*:")})),(n.matchesSelector=Q.test(m=f.webkitMatchesSelector||f.mozMatchesSelector||f.oMatchesSelector||f.msMatchesSelector))&&ut(function(e){n.disconnectedMatch=m.call(e,"div"),m.call(e,"[s!='']:x"),g.push("!=",I)}),d=d.length&&RegExp(d.join("|")),g=g.length&&RegExp(g.join("|")),y=Q.test(f.contains)||f.compareDocumentPosition?function(e,t){var n=9===e.nodeType?e.documentElement:e,r=t&&t.parentNode;return e===r||!(!r||1!==r.nodeType||!(n.contains?n.contains(r):e.compareDocumentPosition&&16&e.compareDocumentPosition(r)))}:function(e,t){if(t)while(t=t.parentNode)if(t===e)return!0;return!1},S=f.compareDocumentPosition?function(e,r){if(e===r)return E=!0,0;var i=r.compareDocumentPosition&&e.compareDocumentPosition&&e.compareDocumentPosition(r);return i?1&i||!n.sortDetached&&r.compareDocumentPosition(e)===i?e===t||y(b,e)?-1:r===t||y(b,r)?1:l?P.call(l,e)-P.call(l,r):0:4&i?-1:1:e.compareDocumentPosition?-1:1}:function(e,n){var r,i=0,o=e.parentNode,s=n.parentNode,a=[e],u=[n];if(e===n)return E=!0,0;if(!o||!s)return e===t?-1:n===t?1:o?-1:s?1:l?P.call(l,e)-P.call(l,n):0;if(o===s)return ct(e,n);r=e;while(r=r.parentNode)a.unshift(r);r=n;while(r=r.parentNode)u.unshift(r);while(a[i]===u[i])i++;return i?ct(a[i],u[i]):a[i]===b?-1:u[i]===b?1:0},t):p},ot.matches=function(e,t){return ot(e,null,null,t)},ot.matchesSelector=function(e,t){if((e.ownerDocument||e)!==p&&c(e),t=t.replace(Y,"='$1']"),!(!n.matchesSelector||!h||g&&g.test(t)||d&&d.test(t)))try{var r=m.call(e,t);if(r||n.disconnectedMatch||e.document&&11!==e.document.nodeType)return r}catch(i){}return ot(t,p,null,[e]).length>0},ot.contains=function(e,t){return(e.ownerDocument||e)!==p&&c(e),y(e,t)},ot.attr=function(e,t){(e.ownerDocument||e)!==p&&c(e);var r=i.attrHandle[t.toLowerCase()],o=r&&A.call(i.attrHandle,t.toLowerCase())?r(e,t,!h):undefined;return o===undefined?n.attributes||!h?e.getAttribute(t):(o=e.getAttributeNode(t))&&o.specified?o.value:null:o},ot.error=function(e){throw Error("Syntax error, unrecognized expression: "+e)},ot.uniqueSort=function(e){var t,r=[],i=0,o=0;if(E=!n.detectDuplicates,l=!n.sortStable&&e.slice(0),e.sort(S),E){while(t=e[o++])t===e[o]&&(i=r.push(o));while(i--)e.splice(r[i],1)}return e},o=ot.getText=function(e){var t,n="",r=0,i=e.nodeType;if(i){if(1===i||9===i||11===i){if("string"==typeof e.textContent)return e.textContent;for(e=e.firstChild;e;e=e.nextSibling)n+=o(e)}else if(3===i||4===i)return e.nodeValue}else for(;t=e[r];r++)n+=o(t);return n},i=ot.selectors={cacheLength:50,createPseudo:at,match:J,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(e){return e[1]=e[1].replace(nt,rt),e[3]=(e[4]||e[5]||"").replace(nt,rt),"~="===e[2]&&(e[3]=" "+e[3]+" "),e.slice(0,4)},CHILD:function(e){return e[1]=e[1].toLowerCase(),"nth"===e[1].slice(0,3)?(e[3]||ot.error(e[0]),e[4]=+(e[4]?e[5]+(e[6]||1):2*("even"===e[3]||"odd"===e[3])),e[5]=+(e[7]+e[8]||"odd"===e[3])):e[3]&&ot.error(e[0]),e},PSEUDO:function(e){var t,n=!e[5]&&e[2];return J.CHILD.test(e[0])?null:(e[3]&&e[4]!==undefined?e[2]=e[4]:n&&V.test(n)&&(t=gt(n,!0))&&(t=n.indexOf(")",n.length-t)-n.length)&&(e[0]=e[0].slice(0,t),e[2]=n.slice(0,t)),e.slice(0,3))}},filter:{TAG:function(e){var t=e.replace(nt,rt).toLowerCase();return"*"===e?function(){return!0}:function(e){return e.nodeName&&e.nodeName.toLowerCase()===t}},CLASS:function(e){var t=C[e+" "];return t||(t=RegExp("(^|"+M+")"+e+"("+M+"|$)"))&&C(e,function(e){return t.test("string"==typeof e.className&&e.className||typeof e.getAttribute!==j&&e.getAttribute("class")||"")})},ATTR:function(e,t,n){return function(r){var i=ot.attr(r,e);return null==i?"!="===t:t?(i+="","="===t?i===n:"!="===t?i!==n:"^="===t?n&&0===i.indexOf(n):"*="===t?n&&i.indexOf(n)>-1:"$="===t?n&&i.slice(-n.length)===n:"~="===t?(" "+i+" ").indexOf(n)>-1:"|="===t?i===n||i.slice(0,n.length+1)===n+"-":!1):!0}},CHILD:function(e,t,n,r,i){var o="nth"!==e.slice(0,3),s="last"!==e.slice(-4),a="of-type"===t;return 1===r&&0===i?function(e){return!!e.parentNode}:function(t,n,u){var l,c,p,f,h,d,g=o!==s?"nextSibling":"previousSibling",m=t.parentNode,y=a&&t.nodeName.toLowerCase(),x=!u&&!a;if(m){if(o){while(g){p=t;while(p=p[g])if(a?p.nodeName.toLowerCase()===y:1===p.nodeType)return!1;d=g="only"===e&&!d&&"nextSibling"}return!0}if(d=[s?m.firstChild:m.lastChild],s&&x){c=m[v]||(m[v]={}),l=c[e]||[],h=l[0]===w&&l[1],f=l[0]===w&&l[2],p=h&&m.childNodes[h];while(p=++h&&p&&p[g]||(f=h=0)||d.pop())if(1===p.nodeType&&++f&&p===t){c[e]=[w,h,f];break}}else if(x&&(l=(t[v]||(t[v]={}))[e])&&l[0]===w)f=l[1];else while(p=++h&&p&&p[g]||(f=h=0)||d.pop())if((a?p.nodeName.toLowerCase()===y:1===p.nodeType)&&++f&&(x&&((p[v]||(p[v]={}))[e]=[w,f]),p===t))break;return f-=i,f===r||0===f%r&&f/r>=0}}},PSEUDO:function(e,t){var n,r=i.pseudos[e]||i.setFilters[e.toLowerCase()]||ot.error("unsupported pseudo: "+e);return r[v]?r(t):r.length>1?(n=[e,e,"",t],i.setFilters.hasOwnProperty(e.toLowerCase())?at(function(e,n){var i,o=r(e,t),s=o.length;while(s--)i=P.call(e,o[s]),e[i]=!(n[i]=o[s])}):function(e){return r(e,0,n)}):r}},pseudos:{not:at(function(e){var t=[],n=[],r=a(e.replace(z,"$1"));return r[v]?at(function(e,t,n,i){var o,s=r(e,null,i,[]),a=e.length;while(a--)(o=s[a])&&(e[a]=!(t[a]=o))}):function(e,i,o){return t[0]=e,r(t,null,o,n),!n.pop()}}),has:at(function(e){return function(t){return ot(e,t).length>0}}),contains:at(function(e){return function(t){return(t.textContent||t.innerText||o(t)).indexOf(e)>-1}}),lang:at(function(e){return G.test(e||"")||ot.error("unsupported lang: "+e),e=e.replace(nt,rt).toLowerCase(),function(t){var n;do if(n=h?t.lang:t.getAttribute("xml:lang")||t.getAttribute("lang"))return n=n.toLowerCase(),n===e||0===n.indexOf(e+"-");while((t=t.parentNode)&&1===t.nodeType);return!1}}),target:function(t){var n=e.location&&e.location.hash;return n&&n.slice(1)===t.id},root:function(e){return e===f},focus:function(e){return e===p.activeElement&&(!p.hasFocus||p.hasFocus())&&!!(e.type||e.href||~e.tabIndex)},enabled:function(e){return e.disabled===!1},disabled:function(e){return e.disabled===!0},checked:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&!!e.checked||"option"===t&&!!e.selected},selected:function(e){return e.parentNode&&e.parentNode.selectedIndex,e.selected===!0},empty:function(e){for(e=e.firstChild;e;e=e.nextSibling)if(e.nodeName>"@"||3===e.nodeType||4===e.nodeType)return!1;return!0},parent:function(e){return!i.pseudos.empty(e)},header:function(e){return et.test(e.nodeName)},input:function(e){return Z.test(e.nodeName)},button:function(e){var t=e.nodeName.toLowerCase();return"input"===t&&"button"===e.type||"button"===t},text:function(e){var t;return"input"===e.nodeName.toLowerCase()&&"text"===e.type&&(null==(t=e.getAttribute("type"))||t.toLowerCase()===e.type)},first:ht(function(){return[0]}),last:ht(function(e,t){return[t-1]}),eq:ht(function(e,t,n){return[0>n?n+t:n]}),even:ht(function(e,t){var n=0;for(;t>n;n+=2)e.push(n);return e}),odd:ht(function(e,t){var n=1;for(;t>n;n+=2)e.push(n);return e}),lt:ht(function(e,t,n){var r=0>n?n+t:n;for(;--r>=0;)e.push(r);return e}),gt:ht(function(e,t,n){var r=0>n?n+t:n;for(;t>++r;)e.push(r);return e})}},i.pseudos.nth=i.pseudos.eq;for(t in{radio:!0,checkbox:!0,file:!0,password:!0,image:!0})i.pseudos[t]=pt(t);for(t in{submit:!0,reset:!0})i.pseudos[t]=ft(t);function dt(){}dt.prototype=i.filters=i.pseudos,i.setFilters=new dt;function gt(e,t){var n,r,o,s,a,u,l,c=k[e+" "];if(c)return t?0:c.slice(0);a=e,u=[],l=i.preFilter;while(a){(!n||(r=_.exec(a)))&&(r&&(a=a.slice(r[0].length)||a),u.push(o=[])),n=!1,(r=X.exec(a))&&(n=r.shift(),o.push({value:n,type:r[0].replace(z," ")}),a=a.slice(n.length));for(s in i.filter)!(r=J[s].exec(a))||l[s]&&!(r=l[s](r))||(n=r.shift(),o.push({value:n,type:s,matches:r}),a=a.slice(n.length));if(!n)break}return t?a.length:a?ot.error(e):k(e,u).slice(0)}function mt(e){var t=0,n=e.length,r="";for(;n>t;t++)r+=e[t].value;return r}function yt(e,t,n){var i=t.dir,o=n&&"parentNode"===i,s=T++;return t.first?function(t,n,r){while(t=t[i])if(1===t.nodeType||o)return e(t,n,r)}:function(t,n,a){var u,l,c,p=w+" "+s;if(a){while(t=t[i])if((1===t.nodeType||o)&&e(t,n,a))return!0}else while(t=t[i])if(1===t.nodeType||o)if(c=t[v]||(t[v]={}),(l=c[i])&&l[0]===p){if((u=l[1])===!0||u===r)return u===!0}else if(l=c[i]=[p],l[1]=e(t,n,a)||r,l[1]===!0)return!0}}function vt(e){return e.length>1?function(t,n,r){var i=e.length;while(i--)if(!e[i](t,n,r))return!1;return!0}:e[0]}function xt(e,t,n,r,i){var o,s=[],a=0,u=e.length,l=null!=t;for(;u>a;a++)(o=e[a])&&(!n||n(o,r,i))&&(s.push(o),l&&t.push(a));return s}function bt(e,t,n,r,i,o){return r&&!r[v]&&(r=bt(r)),i&&!i[v]&&(i=bt(i,o)),at(function(o,s,a,u){var l,c,p,f=[],h=[],d=s.length,g=o||Ct(t||"*",a.nodeType?[a]:a,[]),m=!e||!o&&t?g:xt(g,f,e,a,u),y=n?i||(o?e:d||r)?[]:s:m;if(n&&n(m,y,a,u),r){l=xt(y,h),r(l,[],a,u),c=l.length;while(c--)(p=l[c])&&(y[h[c]]=!(m[h[c]]=p))}if(o){if(i||e){if(i){l=[],c=y.length;while(c--)(p=y[c])&&l.push(m[c]=p);i(null,y=[],l,u)}c=y.length;while(c--)(p=y[c])&&(l=i?P.call(o,p):f[c])>-1&&(o[l]=!(s[l]=p))}}else y=xt(y===s?y.splice(d,y.length):y),i?i(null,s,y,u):O.apply(s,y)})}function wt(e){var t,n,r,o=e.length,s=i.relative[e[0].type],a=s||i.relative[" "],l=s?1:0,c=yt(function(e){return e===t},a,!0),p=yt(function(e){return P.call(t,e)>-1},a,!0),f=[function(e,n,r){return!s&&(r||n!==u)||((t=n).nodeType?c(e,n,r):p(e,n,r))}];for(;o>l;l++)if(n=i.relative[e[l].type])f=[yt(vt(f),n)];else{if(n=i.filter[e[l].type].apply(null,e[l].matches),n[v]){for(r=++l;o>r;r++)if(i.relative[e[r].type])break;return bt(l>1&&vt(f),l>1&&mt(e.slice(0,l-1).concat({value:" "===e[l-2].type?"*":""})).replace(z,"$1"),n,r>l&&wt(e.slice(l,r)),o>r&&wt(e=e.slice(r)),o>r&&mt(e))}f.push(n)}return vt(f)}function Tt(e,t){var n=0,o=t.length>0,s=e.length>0,a=function(a,l,c,f,h){var d,g,m,y=[],v=0,x="0",b=a&&[],T=null!=h,C=u,k=a||s&&i.find.TAG("*",h&&l.parentNode||l),N=w+=null==C?1:Math.random()||.1;for(T&&(u=l!==p&&l,r=n);null!=(d=k[x]);x++){if(s&&d){g=0;while(m=e[g++])if(m(d,l,c)){f.push(d);break}T&&(w=N,r=++n)}o&&((d=!m&&d)&&v--,a&&b.push(d))}if(v+=x,o&&x!==v){g=0;while(m=t[g++])m(b,y,l,c);if(a){if(v>0)while(x--)b[x]||y[x]||(y[x]=q.call(f));y=xt(y)}O.apply(f,y),T&&!a&&y.length>0&&v+t.length>1&&ot.uniqueSort(f)}return T&&(w=N,u=C),b};return o?at(a):a}a=ot.compile=function(e,t){var n,r=[],i=[],o=N[e+" "];if(!o){t||(t=gt(e)),n=t.length;while(n--)o=wt(t[n]),o[v]?r.push(o):i.push(o);o=N(e,Tt(i,r))}return o};function Ct(e,t,n){var r=0,i=t.length;for(;i>r;r++)ot(e,t[r],n);return n}function kt(e,t,r,o){var s,u,l,c,p,f=gt(e);if(!o&&1===f.length){if(u=f[0]=f[0].slice(0),u.length>2&&"ID"===(l=u[0]).type&&n.getById&&9===t.nodeType&&h&&i.relative[u[1].type]){if(t=(i.find.ID(l.matches[0].replace(nt,rt),t)||[])[0],!t)return r;e=e.slice(u.shift().value.length)}s=J.needsContext.test(e)?0:u.length;while(s--){if(l=u[s],i.relative[c=l.type])break;if((p=i.find[c])&&(o=p(l.matches[0].replace(nt,rt),U.test(u[0].type)&&t.parentNode||t))){if(u.splice(s,1),e=o.length&&mt(u),!e)return O.apply(r,o),r;break}}}return a(e,f)(o,t,!h,r,U.test(e)),r}n.sortStable=v.split("").sort(S).join("")===v,n.detectDuplicates=E,c(),n.sortDetached=ut(function(e){return 1&e.compareDocumentPosition(p.createElement("div"))}),ut(function(e){return e.innerHTML="","#"===e.firstChild.getAttribute("href")})||lt("type|href|height|width",function(e,t,n){return n?undefined:e.getAttribute(t,"type"===t.toLowerCase()?1:2)}),n.attributes&&ut(function(e){return e.innerHTML="",e.firstChild.setAttribute("value",""),""===e.firstChild.getAttribute("value")})||lt("value",function(e,t,n){return n||"input"!==e.nodeName.toLowerCase()?undefined:e.defaultValue}),ut(function(e){return null==e.getAttribute("disabled")})||lt(R,function(e,t,n){var r;return n?undefined:(r=e.getAttributeNode(t))&&r.specified?r.value:e[t]===!0?t.toLowerCase():null}),x.find=ot,x.expr=ot.selectors,x.expr[":"]=x.expr.pseudos,x.unique=ot.uniqueSort,x.text=ot.getText,x.isXMLDoc=ot.isXML,x.contains=ot.contains}(e);var D={};function A(e){var t=D[e]={};return x.each(e.match(w)||[],function(e,n){t[n]=!0}),t}x.Callbacks=function(e){e="string"==typeof e?D[e]||A(e):x.extend({},e);var t,n,r,i,o,s,a=[],u=!e.once&&[],l=function(p){for(t=e.memory&&p,n=!0,s=i||0,i=0,o=a.length,r=!0;a&&o>s;s++)if(a[s].apply(p[0],p[1])===!1&&e.stopOnFalse){t=!1;break}r=!1,a&&(u?u.length&&l(u.shift()):t?a=[]:c.disable())},c={add:function(){if(a){var n=a.length;(function s(t){x.each(t,function(t,n){var r=x.type(n);"function"===r?e.unique&&c.has(n)||a.push(n):n&&n.length&&"string"!==r&&s(n)})})(arguments),r?o=a.length:t&&(i=n,l(t))}return this},remove:function(){return a&&x.each(arguments,function(e,t){var n;while((n=x.inArray(t,a,n))>-1)a.splice(n,1),r&&(o>=n&&o--,s>=n&&s--)}),this},has:function(e){return e?x.inArray(e,a)>-1:!(!a||!a.length)},empty:function(){return a=[],o=0,this},disable:function(){return a=u=t=undefined,this},disabled:function(){return!a},lock:function(){return u=undefined,t||c.disable(),this},locked:function(){return!u},fireWith:function(e,t){return!a||n&&!u||(t=t||[],t=[e,t.slice?t.slice():t],r?u.push(t):l(t)),this},fire:function(){return c.fireWith(this,arguments),this},fired:function(){return!!n}};return c},x.extend({Deferred:function(e){var t=[["resolve","done",x.Callbacks("once memory"),"resolved"],["reject","fail",x.Callbacks("once memory"),"rejected"],["notify","progress",x.Callbacks("memory")]],n="pending",r={state:function(){return n},always:function(){return i.done(arguments).fail(arguments),this},then:function(){var e=arguments;return x.Deferred(function(n){x.each(t,function(t,o){var s=o[0],a=x.isFunction(e[t])&&e[t];i[o[1]](function(){var e=a&&a.apply(this,arguments);e&&x.isFunction(e.promise)?e.promise().done(n.resolve).fail(n.reject).progress(n.notify):n[s+"With"](this===r?n.promise():this,a?[e]:arguments)})}),e=null}).promise()},promise:function(e){return null!=e?x.extend(e,r):r}},i={};return r.pipe=r.then,x.each(t,function(e,o){var s=o[2],a=o[3];r[o[1]]=s.add,a&&s.add(function(){n=a},t[1^e][2].disable,t[2][2].lock),i[o[0]]=function(){return i[o[0]+"With"](this===i?r:this,arguments),this},i[o[0]+"With"]=s.fireWith}),r.promise(i),e&&e.call(i,i),i},when:function(e){var t=0,n=d.call(arguments),r=n.length,i=1!==r||e&&x.isFunction(e.promise)?r:0,o=1===i?e:x.Deferred(),s=function(e,t,n){return function(r){t[e]=this,n[e]=arguments.length>1?d.call(arguments):r,n===a?o.notifyWith(t,n):--i||o.resolveWith(t,n)}},a,u,l;if(r>1)for(a=Array(r),u=Array(r),l=Array(r);r>t;t++)n[t]&&x.isFunction(n[t].promise)?n[t].promise().done(s(t,l,n)).fail(o.reject).progress(s(t,u,a)):--i;return i||o.resolveWith(l,n),o.promise()}}),x.support=function(t){var n=o.createElement("input"),r=o.createDocumentFragment(),i=o.createElement("div"),s=o.createElement("select"),a=s.appendChild(o.createElement("option"));return n.type?(n.type="checkbox",t.checkOn=""!==n.value,t.optSelected=a.selected,t.reliableMarginRight=!0,t.boxSizingReliable=!0,t.pixelPosition=!1,n.checked=!0,t.noCloneChecked=n.cloneNode(!0).checked,s.disabled=!0,t.optDisabled=!a.disabled,n=o.createElement("input"),n.value="t",n.type="radio",t.radioValue="t"===n.value,n.setAttribute("checked","t"),n.setAttribute("name","t"),r.appendChild(n),t.checkClone=r.cloneNode(!0).cloneNode(!0).lastChild.checked,t.focusinBubbles="onfocusin"in e,i.style.backgroundClip="content-box",i.cloneNode(!0).style.backgroundClip="",t.clearCloneStyle="content-box"===i.style.backgroundClip,x(function(){var n,r,s="padding:0;margin:0;border:0;display:block;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box",a=o.getElementsByTagName("body")[0];a&&(n=o.createElement("div"),n.style.cssText="border:0;width:0;height:0;position:absolute;top:0;left:-9999px;margin-top:1px",a.appendChild(n).appendChild(i),i.innerHTML="",i.style.cssText="-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box;padding:1px;border:1px;display:block;width:4px;margin-top:1%;position:absolute;top:1%",x.swap(a,null!=a.style.zoom?{zoom:1}:{},function(){t.boxSizing=4===i.offsetWidth}),e.getComputedStyle&&(t.pixelPosition="1%"!==(e.getComputedStyle(i,null)||{}).top,t.boxSizingReliable="4px"===(e.getComputedStyle(i,null)||{width:"4px"}).width,r=i.appendChild(o.createElement("div")),r.style.cssText=i.style.cssText=s,r.style.marginRight=r.style.width="0",i.style.width="1px",t.reliableMarginRight=!parseFloat((e.getComputedStyle(r,null)||{}).marginRight)),a.removeChild(n))}),t):t}({});var L,q,H=/(?:\{[\s\S]*\}|\[[\s\S]*\])$/,O=/([A-Z])/g;function F(){Object.defineProperty(this.cache={},0,{get:function(){return{}}}),this.expando=x.expando+Math.random()}F.uid=1,F.accepts=function(e){return e.nodeType?1===e.nodeType||9===e.nodeType:!0},F.prototype={key:function(e){if(!F.accepts(e))return 0;var t={},n=e[this.expando];if(!n){n=F.uid++;try{t[this.expando]={value:n},Object.defineProperties(e,t)}catch(r){t[this.expando]=n,x.extend(e,t)}}return this.cache[n]||(this.cache[n]={}),n},set:function(e,t,n){var r,i=this.key(e),o=this.cache[i];if("string"==typeof t)o[t]=n;else if(x.isEmptyObject(o))x.extend(this.cache[i],t);else for(r in t)o[r]=t[r];return o},get:function(e,t){var n=this.cache[this.key(e)];return t===undefined?n:n[t]},access:function(e,t,n){var r;return t===undefined||t&&"string"==typeof t&&n===undefined?(r=this.get(e,t),r!==undefined?r:this.get(e,x.camelCase(t))):(this.set(e,t,n),n!==undefined?n:t)},remove:function(e,t){var n,r,i,o=this.key(e),s=this.cache[o];if(t===undefined)this.cache[o]={};else{x.isArray(t)?r=t.concat(t.map(x.camelCase)):(i=x.camelCase(t),t in s?r=[t,i]:(r=i,r=r in s?[r]:r.match(w)||[])),n=r.length;while(n--)delete s[r[n]]}},hasData:function(e){return!x.isEmptyObject(this.cache[e[this.expando]]||{})},discard:function(e){e[this.expando]&&delete this.cache[e[this.expando]]}},L=new F,q=new F,x.extend({acceptData:F.accepts,hasData:function(e){return L.hasData(e)||q.hasData(e)},data:function(e,t,n){return L.access(e,t,n)},removeData:function(e,t){L.remove(e,t)},_data:function(e,t,n){return q.access(e,t,n)},_removeData:function(e,t){q.remove(e,t)}}),x.fn.extend({data:function(e,t){var n,r,i=this[0],o=0,s=null;if(e===undefined){if(this.length&&(s=L.get(i),1===i.nodeType&&!q.get(i,"hasDataAttrs"))){for(n=i.attributes;n.length>o;o++)r=n[o].name,0===r.indexOf("data-")&&(r=x.camelCase(r.slice(5)),P(i,r,s[r]));q.set(i,"hasDataAttrs",!0)}return s}return"object"==typeof e?this.each(function(){L.set(this,e)}):x.access(this,function(t){var n,r=x.camelCase(e);if(i&&t===undefined){if(n=L.get(i,e),n!==undefined)return n;if(n=L.get(i,r),n!==undefined)return n;if(n=P(i,r,undefined),n!==undefined)return n}else this.each(function(){var n=L.get(this,r);L.set(this,r,t),-1!==e.indexOf("-")&&n!==undefined&&L.set(this,e,t)})},null,t,arguments.length>1,null,!0)},removeData:function(e){return this.each(function(){L.remove(this,e)})}});function P(e,t,n){var r;if(n===undefined&&1===e.nodeType)if(r="data-"+t.replace(O,"-$1").toLowerCase(),n=e.getAttribute(r),"string"==typeof n){try{n="true"===n?!0:"false"===n?!1:"null"===n?null:+n+""===n?+n:H.test(n)?JSON.parse(n):n}catch(i){}L.set(e,t,n)}else n=undefined;return n}x.extend({queue:function(e,t,n){var r;return e?(t=(t||"fx")+"queue",r=q.get(e,t),n&&(!r||x.isArray(n)?r=q.access(e,t,x.makeArray(n)):r.push(n)),r||[]):undefined},dequeue:function(e,t){t=t||"fx";var n=x.queue(e,t),r=n.length,i=n.shift(),o=x._queueHooks(e,t),s=function(){x.dequeue(e,t) +};"inprogress"===i&&(i=n.shift(),r--),i&&("fx"===t&&n.unshift("inprogress"),delete o.stop,i.call(e,s,o)),!r&&o&&o.empty.fire()},_queueHooks:function(e,t){var n=t+"queueHooks";return q.get(e,n)||q.access(e,n,{empty:x.Callbacks("once memory").add(function(){q.remove(e,[t+"queue",n])})})}}),x.fn.extend({queue:function(e,t){var n=2;return"string"!=typeof e&&(t=e,e="fx",n--),n>arguments.length?x.queue(this[0],e):t===undefined?this:this.each(function(){var n=x.queue(this,e,t);x._queueHooks(this,e),"fx"===e&&"inprogress"!==n[0]&&x.dequeue(this,e)})},dequeue:function(e){return this.each(function(){x.dequeue(this,e)})},delay:function(e,t){return e=x.fx?x.fx.speeds[e]||e:e,t=t||"fx",this.queue(t,function(t,n){var r=setTimeout(t,e);n.stop=function(){clearTimeout(r)}})},clearQueue:function(e){return this.queue(e||"fx",[])},promise:function(e,t){var n,r=1,i=x.Deferred(),o=this,s=this.length,a=function(){--r||i.resolveWith(o,[o])};"string"!=typeof e&&(t=e,e=undefined),e=e||"fx";while(s--)n=q.get(o[s],e+"queueHooks"),n&&n.empty&&(r++,n.empty.add(a));return a(),i.promise(t)}});var R,M,W=/[\t\r\n\f]/g,$=/\r/g,B=/^(?:input|select|textarea|button)$/i;x.fn.extend({attr:function(e,t){return x.access(this,x.attr,e,t,arguments.length>1)},removeAttr:function(e){return this.each(function(){x.removeAttr(this,e)})},prop:function(e,t){return x.access(this,x.prop,e,t,arguments.length>1)},removeProp:function(e){return this.each(function(){delete this[x.propFix[e]||e]})},addClass:function(e){var t,n,r,i,o,s=0,a=this.length,u="string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).addClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):" ")){o=0;while(i=t[o++])0>r.indexOf(" "+i+" ")&&(r+=i+" ");n.className=x.trim(r)}return this},removeClass:function(e){var t,n,r,i,o,s=0,a=this.length,u=0===arguments.length||"string"==typeof e&&e;if(x.isFunction(e))return this.each(function(t){x(this).removeClass(e.call(this,t,this.className))});if(u)for(t=(e||"").match(w)||[];a>s;s++)if(n=this[s],r=1===n.nodeType&&(n.className?(" "+n.className+" ").replace(W," "):"")){o=0;while(i=t[o++])while(r.indexOf(" "+i+" ")>=0)r=r.replace(" "+i+" "," ");n.className=e?x.trim(r):""}return this},toggleClass:function(e,t){var n=typeof e;return"boolean"==typeof t&&"string"===n?t?this.addClass(e):this.removeClass(e):x.isFunction(e)?this.each(function(n){x(this).toggleClass(e.call(this,n,this.className,t),t)}):this.each(function(){if("string"===n){var t,i=0,o=x(this),s=e.match(w)||[];while(t=s[i++])o.hasClass(t)?o.removeClass(t):o.addClass(t)}else(n===r||"boolean"===n)&&(this.className&&q.set(this,"__className__",this.className),this.className=this.className||e===!1?"":q.get(this,"__className__")||"")})},hasClass:function(e){var t=" "+e+" ",n=0,r=this.length;for(;r>n;n++)if(1===this[n].nodeType&&(" "+this[n].className+" ").replace(W," ").indexOf(t)>=0)return!0;return!1},val:function(e){var t,n,r,i=this[0];{if(arguments.length)return r=x.isFunction(e),this.each(function(n){var i;1===this.nodeType&&(i=r?e.call(this,n,x(this).val()):e,null==i?i="":"number"==typeof i?i+="":x.isArray(i)&&(i=x.map(i,function(e){return null==e?"":e+""})),t=x.valHooks[this.type]||x.valHooks[this.nodeName.toLowerCase()],t&&"set"in t&&t.set(this,i,"value")!==undefined||(this.value=i))});if(i)return t=x.valHooks[i.type]||x.valHooks[i.nodeName.toLowerCase()],t&&"get"in t&&(n=t.get(i,"value"))!==undefined?n:(n=i.value,"string"==typeof n?n.replace($,""):null==n?"":n)}}}),x.extend({valHooks:{option:{get:function(e){var t=e.attributes.value;return!t||t.specified?e.value:e.text}},select:{get:function(e){var t,n,r=e.options,i=e.selectedIndex,o="select-one"===e.type||0>i,s=o?null:[],a=o?i+1:r.length,u=0>i?a:o?i:0;for(;a>u;u++)if(n=r[u],!(!n.selected&&u!==i||(x.support.optDisabled?n.disabled:null!==n.getAttribute("disabled"))||n.parentNode.disabled&&x.nodeName(n.parentNode,"optgroup"))){if(t=x(n).val(),o)return t;s.push(t)}return s},set:function(e,t){var n,r,i=e.options,o=x.makeArray(t),s=i.length;while(s--)r=i[s],(r.selected=x.inArray(x(r).val(),o)>=0)&&(n=!0);return n||(e.selectedIndex=-1),o}}},attr:function(e,t,n){var i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return typeof e.getAttribute===r?x.prop(e,t,n):(1===s&&x.isXMLDoc(e)||(t=t.toLowerCase(),i=x.attrHooks[t]||(x.expr.match.bool.test(t)?M:R)),n===undefined?i&&"get"in i&&null!==(o=i.get(e,t))?o:(o=x.find.attr(e,t),null==o?undefined:o):null!==n?i&&"set"in i&&(o=i.set(e,n,t))!==undefined?o:(e.setAttribute(t,n+""),n):(x.removeAttr(e,t),undefined))},removeAttr:function(e,t){var n,r,i=0,o=t&&t.match(w);if(o&&1===e.nodeType)while(n=o[i++])r=x.propFix[n]||n,x.expr.match.bool.test(n)&&(e[r]=!1),e.removeAttribute(n)},attrHooks:{type:{set:function(e,t){if(!x.support.radioValue&&"radio"===t&&x.nodeName(e,"input")){var n=e.value;return e.setAttribute("type",t),n&&(e.value=n),t}}}},propFix:{"for":"htmlFor","class":"className"},prop:function(e,t,n){var r,i,o,s=e.nodeType;if(e&&3!==s&&8!==s&&2!==s)return o=1!==s||!x.isXMLDoc(e),o&&(t=x.propFix[t]||t,i=x.propHooks[t]),n!==undefined?i&&"set"in i&&(r=i.set(e,n,t))!==undefined?r:e[t]=n:i&&"get"in i&&null!==(r=i.get(e,t))?r:e[t]},propHooks:{tabIndex:{get:function(e){return e.hasAttribute("tabindex")||B.test(e.nodeName)||e.href?e.tabIndex:-1}}}}),M={set:function(e,t,n){return t===!1?x.removeAttr(e,n):e.setAttribute(n,n),n}},x.each(x.expr.match.bool.source.match(/\w+/g),function(e,t){var n=x.expr.attrHandle[t]||x.find.attr;x.expr.attrHandle[t]=function(e,t,r){var i=x.expr.attrHandle[t],o=r?undefined:(x.expr.attrHandle[t]=undefined)!=n(e,t,r)?t.toLowerCase():null;return x.expr.attrHandle[t]=i,o}}),x.support.optSelected||(x.propHooks.selected={get:function(e){var t=e.parentNode;return t&&t.parentNode&&t.parentNode.selectedIndex,null}}),x.each(["tabIndex","readOnly","maxLength","cellSpacing","cellPadding","rowSpan","colSpan","useMap","frameBorder","contentEditable"],function(){x.propFix[this.toLowerCase()]=this}),x.each(["radio","checkbox"],function(){x.valHooks[this]={set:function(e,t){return x.isArray(t)?e.checked=x.inArray(x(e).val(),t)>=0:undefined}},x.support.checkOn||(x.valHooks[this].get=function(e){return null===e.getAttribute("value")?"on":e.value})});var I=/^key/,z=/^(?:mouse|contextmenu)|click/,_=/^(?:focusinfocus|focusoutblur)$/,X=/^([^.]*)(?:\.(.+)|)$/;function U(){return!0}function Y(){return!1}function V(){try{return o.activeElement}catch(e){}}x.event={global:{},add:function(e,t,n,i,o){var s,a,u,l,c,p,f,h,d,g,m,y=q.get(e);if(y){n.handler&&(s=n,n=s.handler,o=s.selector),n.guid||(n.guid=x.guid++),(l=y.events)||(l=y.events={}),(a=y.handle)||(a=y.handle=function(e){return typeof x===r||e&&x.event.triggered===e.type?undefined:x.event.dispatch.apply(a.elem,arguments)},a.elem=e),t=(t||"").match(w)||[""],c=t.length;while(c--)u=X.exec(t[c])||[],d=m=u[1],g=(u[2]||"").split(".").sort(),d&&(f=x.event.special[d]||{},d=(o?f.delegateType:f.bindType)||d,f=x.event.special[d]||{},p=x.extend({type:d,origType:m,data:i,handler:n,guid:n.guid,selector:o,needsContext:o&&x.expr.match.needsContext.test(o),namespace:g.join(".")},s),(h=l[d])||(h=l[d]=[],h.delegateCount=0,f.setup&&f.setup.call(e,i,g,a)!==!1||e.addEventListener&&e.addEventListener(d,a,!1)),f.add&&(f.add.call(e,p),p.handler.guid||(p.handler.guid=n.guid)),o?h.splice(h.delegateCount++,0,p):h.push(p),x.event.global[d]=!0);e=null}},remove:function(e,t,n,r,i){var o,s,a,u,l,c,p,f,h,d,g,m=q.hasData(e)&&q.get(e);if(m&&(u=m.events)){t=(t||"").match(w)||[""],l=t.length;while(l--)if(a=X.exec(t[l])||[],h=g=a[1],d=(a[2]||"").split(".").sort(),h){p=x.event.special[h]||{},h=(r?p.delegateType:p.bindType)||h,f=u[h]||[],a=a[2]&&RegExp("(^|\\.)"+d.join("\\.(?:.*\\.|)")+"(\\.|$)"),s=o=f.length;while(o--)c=f[o],!i&&g!==c.origType||n&&n.guid!==c.guid||a&&!a.test(c.namespace)||r&&r!==c.selector&&("**"!==r||!c.selector)||(f.splice(o,1),c.selector&&f.delegateCount--,p.remove&&p.remove.call(e,c));s&&!f.length&&(p.teardown&&p.teardown.call(e,d,m.handle)!==!1||x.removeEvent(e,h,m.handle),delete u[h])}else for(h in u)x.event.remove(e,h+t[l],n,r,!0);x.isEmptyObject(u)&&(delete m.handle,q.remove(e,"events"))}},trigger:function(t,n,r,i){var s,a,u,l,c,p,f,h=[r||o],d=y.call(t,"type")?t.type:t,g=y.call(t,"namespace")?t.namespace.split("."):[];if(a=u=r=r||o,3!==r.nodeType&&8!==r.nodeType&&!_.test(d+x.event.triggered)&&(d.indexOf(".")>=0&&(g=d.split("."),d=g.shift(),g.sort()),c=0>d.indexOf(":")&&"on"+d,t=t[x.expando]?t:new x.Event(d,"object"==typeof t&&t),t.isTrigger=i?2:3,t.namespace=g.join("."),t.namespace_re=t.namespace?RegExp("(^|\\.)"+g.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,t.result=undefined,t.target||(t.target=r),n=null==n?[t]:x.makeArray(n,[t]),f=x.event.special[d]||{},i||!f.trigger||f.trigger.apply(r,n)!==!1)){if(!i&&!f.noBubble&&!x.isWindow(r)){for(l=f.delegateType||d,_.test(l+d)||(a=a.parentNode);a;a=a.parentNode)h.push(a),u=a;u===(r.ownerDocument||o)&&h.push(u.defaultView||u.parentWindow||e)}s=0;while((a=h[s++])&&!t.isPropagationStopped())t.type=s>1?l:f.bindType||d,p=(q.get(a,"events")||{})[t.type]&&q.get(a,"handle"),p&&p.apply(a,n),p=c&&a[c],p&&x.acceptData(a)&&p.apply&&p.apply(a,n)===!1&&t.preventDefault();return t.type=d,i||t.isDefaultPrevented()||f._default&&f._default.apply(h.pop(),n)!==!1||!x.acceptData(r)||c&&x.isFunction(r[d])&&!x.isWindow(r)&&(u=r[c],u&&(r[c]=null),x.event.triggered=d,r[d](),x.event.triggered=undefined,u&&(r[c]=u)),t.result}},dispatch:function(e){e=x.event.fix(e);var t,n,r,i,o,s=[],a=d.call(arguments),u=(q.get(this,"events")||{})[e.type]||[],l=x.event.special[e.type]||{};if(a[0]=e,e.delegateTarget=this,!l.preDispatch||l.preDispatch.call(this,e)!==!1){s=x.event.handlers.call(this,e,u),t=0;while((i=s[t++])&&!e.isPropagationStopped()){e.currentTarget=i.elem,n=0;while((o=i.handlers[n++])&&!e.isImmediatePropagationStopped())(!e.namespace_re||e.namespace_re.test(o.namespace))&&(e.handleObj=o,e.data=o.data,r=((x.event.special[o.origType]||{}).handle||o.handler).apply(i.elem,a),r!==undefined&&(e.result=r)===!1&&(e.preventDefault(),e.stopPropagation()))}return l.postDispatch&&l.postDispatch.call(this,e),e.result}},handlers:function(e,t){var n,r,i,o,s=[],a=t.delegateCount,u=e.target;if(a&&u.nodeType&&(!e.button||"click"!==e.type))for(;u!==this;u=u.parentNode||this)if(u.disabled!==!0||"click"!==e.type){for(r=[],n=0;a>n;n++)o=t[n],i=o.selector+" ",r[i]===undefined&&(r[i]=o.needsContext?x(i,this).index(u)>=0:x.find(i,this,null,[u]).length),r[i]&&r.push(o);r.length&&s.push({elem:u,handlers:r})}return t.length>a&&s.push({elem:this,handlers:t.slice(a)}),s},props:"altKey bubbles cancelable ctrlKey currentTarget eventPhase metaKey relatedTarget shiftKey target timeStamp view which".split(" "),fixHooks:{},keyHooks:{props:"char charCode key keyCode".split(" "),filter:function(e,t){return null==e.which&&(e.which=null!=t.charCode?t.charCode:t.keyCode),e}},mouseHooks:{props:"button buttons clientX clientY offsetX offsetY pageX pageY screenX screenY toElement".split(" "),filter:function(e,t){var n,r,i,s=t.button;return null==e.pageX&&null!=t.clientX&&(n=e.target.ownerDocument||o,r=n.documentElement,i=n.body,e.pageX=t.clientX+(r&&r.scrollLeft||i&&i.scrollLeft||0)-(r&&r.clientLeft||i&&i.clientLeft||0),e.pageY=t.clientY+(r&&r.scrollTop||i&&i.scrollTop||0)-(r&&r.clientTop||i&&i.clientTop||0)),e.which||s===undefined||(e.which=1&s?1:2&s?3:4&s?2:0),e}},fix:function(e){if(e[x.expando])return e;var t,n,r,i=e.type,s=e,a=this.fixHooks[i];a||(this.fixHooks[i]=a=z.test(i)?this.mouseHooks:I.test(i)?this.keyHooks:{}),r=a.props?this.props.concat(a.props):this.props,e=new x.Event(s),t=r.length;while(t--)n=r[t],e[n]=s[n];return e.target||(e.target=o),3===e.target.nodeType&&(e.target=e.target.parentNode),a.filter?a.filter(e,s):e},special:{load:{noBubble:!0},focus:{trigger:function(){return this!==V()&&this.focus?(this.focus(),!1):undefined},delegateType:"focusin"},blur:{trigger:function(){return this===V()&&this.blur?(this.blur(),!1):undefined},delegateType:"focusout"},click:{trigger:function(){return"checkbox"===this.type&&this.click&&x.nodeName(this,"input")?(this.click(),!1):undefined},_default:function(e){return x.nodeName(e.target,"a")}},beforeunload:{postDispatch:function(e){e.result!==undefined&&(e.originalEvent.returnValue=e.result)}}},simulate:function(e,t,n,r){var i=x.extend(new x.Event,n,{type:e,isSimulated:!0,originalEvent:{}});r?x.event.trigger(i,null,t):x.event.dispatch.call(t,i),i.isDefaultPrevented()&&n.preventDefault()}},x.removeEvent=function(e,t,n){e.removeEventListener&&e.removeEventListener(t,n,!1)},x.Event=function(e,t){return this instanceof x.Event?(e&&e.type?(this.originalEvent=e,this.type=e.type,this.isDefaultPrevented=e.defaultPrevented||e.getPreventDefault&&e.getPreventDefault()?U:Y):this.type=e,t&&x.extend(this,t),this.timeStamp=e&&e.timeStamp||x.now(),this[x.expando]=!0,undefined):new x.Event(e,t)},x.Event.prototype={isDefaultPrevented:Y,isPropagationStopped:Y,isImmediatePropagationStopped:Y,preventDefault:function(){var e=this.originalEvent;this.isDefaultPrevented=U,e&&e.preventDefault&&e.preventDefault()},stopPropagation:function(){var e=this.originalEvent;this.isPropagationStopped=U,e&&e.stopPropagation&&e.stopPropagation()},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=U,this.stopPropagation()}},x.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(e,t){x.event.special[e]={delegateType:t,bindType:t,handle:function(e){var n,r=this,i=e.relatedTarget,o=e.handleObj;return(!i||i!==r&&!x.contains(r,i))&&(e.type=o.origType,n=o.handler.apply(this,arguments),e.type=t),n}}}),x.support.focusinBubbles||x.each({focus:"focusin",blur:"focusout"},function(e,t){var n=0,r=function(e){x.event.simulate(t,e.target,x.event.fix(e),!0)};x.event.special[t]={setup:function(){0===n++&&o.addEventListener(e,r,!0)},teardown:function(){0===--n&&o.removeEventListener(e,r,!0)}}}),x.fn.extend({on:function(e,t,n,r,i){var o,s;if("object"==typeof e){"string"!=typeof t&&(n=n||t,t=undefined);for(s in e)this.on(s,t,n,e[s],i);return this}if(null==n&&null==r?(r=t,n=t=undefined):null==r&&("string"==typeof t?(r=n,n=undefined):(r=n,n=t,t=undefined)),r===!1)r=Y;else if(!r)return this;return 1===i&&(o=r,r=function(e){return x().off(e),o.apply(this,arguments)},r.guid=o.guid||(o.guid=x.guid++)),this.each(function(){x.event.add(this,e,r,n,t)})},one:function(e,t,n,r){return this.on(e,t,n,r,1)},off:function(e,t,n){var r,i;if(e&&e.preventDefault&&e.handleObj)return r=e.handleObj,x(e.delegateTarget).off(r.namespace?r.origType+"."+r.namespace:r.origType,r.selector,r.handler),this;if("object"==typeof e){for(i in e)this.off(i,t,e[i]);return this}return(t===!1||"function"==typeof t)&&(n=t,t=undefined),n===!1&&(n=Y),this.each(function(){x.event.remove(this,e,n,t)})},trigger:function(e,t){return this.each(function(){x.event.trigger(e,t,this)})},triggerHandler:function(e,t){var n=this[0];return n?x.event.trigger(e,t,n,!0):undefined}});var G=/^.[^:#\[\.,]*$/,J=/^(?:parents|prev(?:Until|All))/,Q=x.expr.match.needsContext,K={children:!0,contents:!0,next:!0,prev:!0};x.fn.extend({find:function(e){var t,n=[],r=this,i=r.length;if("string"!=typeof e)return this.pushStack(x(e).filter(function(){for(t=0;i>t;t++)if(x.contains(r[t],this))return!0}));for(t=0;i>t;t++)x.find(e,r[t],n);return n=this.pushStack(i>1?x.unique(n):n),n.selector=this.selector?this.selector+" "+e:e,n},has:function(e){var t=x(e,this),n=t.length;return this.filter(function(){var e=0;for(;n>e;e++)if(x.contains(this,t[e]))return!0})},not:function(e){return this.pushStack(et(this,e||[],!0))},filter:function(e){return this.pushStack(et(this,e||[],!1))},is:function(e){return!!et(this,"string"==typeof e&&Q.test(e)?x(e):e||[],!1).length},closest:function(e,t){var n,r=0,i=this.length,o=[],s=Q.test(e)||"string"!=typeof e?x(e,t||this.context):0;for(;i>r;r++)for(n=this[r];n&&n!==t;n=n.parentNode)if(11>n.nodeType&&(s?s.index(n)>-1:1===n.nodeType&&x.find.matchesSelector(n,e))){n=o.push(n);break}return this.pushStack(o.length>1?x.unique(o):o)},index:function(e){return e?"string"==typeof e?g.call(x(e),this[0]):g.call(this,e.jquery?e[0]:e):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(e,t){var n="string"==typeof e?x(e,t):x.makeArray(e&&e.nodeType?[e]:e),r=x.merge(this.get(),n);return this.pushStack(x.unique(r))},addBack:function(e){return this.add(null==e?this.prevObject:this.prevObject.filter(e))}});function Z(e,t){while((e=e[t])&&1!==e.nodeType);return e}x.each({parent:function(e){var t=e.parentNode;return t&&11!==t.nodeType?t:null},parents:function(e){return x.dir(e,"parentNode")},parentsUntil:function(e,t,n){return x.dir(e,"parentNode",n)},next:function(e){return Z(e,"nextSibling")},prev:function(e){return Z(e,"previousSibling")},nextAll:function(e){return x.dir(e,"nextSibling")},prevAll:function(e){return x.dir(e,"previousSibling")},nextUntil:function(e,t,n){return x.dir(e,"nextSibling",n)},prevUntil:function(e,t,n){return x.dir(e,"previousSibling",n)},siblings:function(e){return x.sibling((e.parentNode||{}).firstChild,e)},children:function(e){return x.sibling(e.firstChild)},contents:function(e){return e.contentDocument||x.merge([],e.childNodes)}},function(e,t){x.fn[e]=function(n,r){var i=x.map(this,t,n);return"Until"!==e.slice(-5)&&(r=n),r&&"string"==typeof r&&(i=x.filter(r,i)),this.length>1&&(K[e]||x.unique(i),J.test(e)&&i.reverse()),this.pushStack(i)}}),x.extend({filter:function(e,t,n){var r=t[0];return n&&(e=":not("+e+")"),1===t.length&&1===r.nodeType?x.find.matchesSelector(r,e)?[r]:[]:x.find.matches(e,x.grep(t,function(e){return 1===e.nodeType}))},dir:function(e,t,n){var r=[],i=n!==undefined;while((e=e[t])&&9!==e.nodeType)if(1===e.nodeType){if(i&&x(e).is(n))break;r.push(e)}return r},sibling:function(e,t){var n=[];for(;e;e=e.nextSibling)1===e.nodeType&&e!==t&&n.push(e);return n}});function et(e,t,n){if(x.isFunction(t))return x.grep(e,function(e,r){return!!t.call(e,r,e)!==n});if(t.nodeType)return x.grep(e,function(e){return e===t!==n});if("string"==typeof t){if(G.test(t))return x.filter(t,e,n);t=x.filter(t,e)}return x.grep(e,function(e){return g.call(t,e)>=0!==n})}var tt=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,nt=/<([\w:]+)/,rt=/<|&#?\w+;/,it=/<(?:script|style|link)/i,ot=/^(?:checkbox|radio)$/i,st=/checked\s*(?:[^=]|=\s*.checked.)/i,at=/^$|\/(?:java|ecma)script/i,ut=/^true\/(.*)/,lt=/^\s*\s*$/g,ct={option:[1,""],thead:[1,"","
"],col:[2,"","
"],tr:[2,"","
"],td:[3,"","
"],_default:[0,"",""]};ct.optgroup=ct.option,ct.tbody=ct.tfoot=ct.colgroup=ct.caption=ct.thead,ct.th=ct.td,x.fn.extend({text:function(e){return x.access(this,function(e){return e===undefined?x.text(this):this.empty().append((this[0]&&this[0].ownerDocument||o).createTextNode(e))},null,e,arguments.length)},append:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=pt(this,e);t.appendChild(e)}})},prepend:function(){return this.domManip(arguments,function(e){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var t=pt(this,e);t.insertBefore(e,t.firstChild)}})},before:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this)})},after:function(){return this.domManip(arguments,function(e){this.parentNode&&this.parentNode.insertBefore(e,this.nextSibling)})},remove:function(e,t){var n,r=e?x.filter(e,this):this,i=0;for(;null!=(n=r[i]);i++)t||1!==n.nodeType||x.cleanData(mt(n)),n.parentNode&&(t&&x.contains(n.ownerDocument,n)&&dt(mt(n,"script")),n.parentNode.removeChild(n));return this},empty:function(){var e,t=0;for(;null!=(e=this[t]);t++)1===e.nodeType&&(x.cleanData(mt(e,!1)),e.textContent="");return this},clone:function(e,t){return e=null==e?!1:e,t=null==t?e:t,this.map(function(){return x.clone(this,e,t)})},html:function(e){return x.access(this,function(e){var t=this[0]||{},n=0,r=this.length;if(e===undefined&&1===t.nodeType)return t.innerHTML;if("string"==typeof e&&!it.test(e)&&!ct[(nt.exec(e)||["",""])[1].toLowerCase()]){e=e.replace(tt,"<$1>");try{for(;r>n;n++)t=this[n]||{},1===t.nodeType&&(x.cleanData(mt(t,!1)),t.innerHTML=e);t=0}catch(i){}}t&&this.empty().append(e)},null,e,arguments.length)},replaceWith:function(){var e=x.map(this,function(e){return[e.nextSibling,e.parentNode]}),t=0;return this.domManip(arguments,function(n){var r=e[t++],i=e[t++];i&&(r&&r.parentNode!==i&&(r=this.nextSibling),x(this).remove(),i.insertBefore(n,r))},!0),t?this:this.remove()},detach:function(e){return this.remove(e,!0)},domManip:function(e,t,n){e=f.apply([],e);var r,i,o,s,a,u,l=0,c=this.length,p=this,h=c-1,d=e[0],g=x.isFunction(d);if(g||!(1>=c||"string"!=typeof d||x.support.checkClone)&&st.test(d))return this.each(function(r){var i=p.eq(r);g&&(e[0]=d.call(this,r,i.html())),i.domManip(e,t,n)});if(c&&(r=x.buildFragment(e,this[0].ownerDocument,!1,!n&&this),i=r.firstChild,1===r.childNodes.length&&(r=i),i)){for(o=x.map(mt(r,"script"),ft),s=o.length;c>l;l++)a=r,l!==h&&(a=x.clone(a,!0,!0),s&&x.merge(o,mt(a,"script"))),t.call(this[l],a,l);if(s)for(u=o[o.length-1].ownerDocument,x.map(o,ht),l=0;s>l;l++)a=o[l],at.test(a.type||"")&&!q.access(a,"globalEval")&&x.contains(u,a)&&(a.src?x._evalUrl(a.src):x.globalEval(a.textContent.replace(lt,"")))}return this}}),x.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(e,t){x.fn[e]=function(e){var n,r=[],i=x(e),o=i.length-1,s=0;for(;o>=s;s++)n=s===o?this:this.clone(!0),x(i[s])[t](n),h.apply(r,n.get());return this.pushStack(r)}}),x.extend({clone:function(e,t,n){var r,i,o,s,a=e.cloneNode(!0),u=x.contains(e.ownerDocument,e);if(!(x.support.noCloneChecked||1!==e.nodeType&&11!==e.nodeType||x.isXMLDoc(e)))for(s=mt(a),o=mt(e),r=0,i=o.length;i>r;r++)yt(o[r],s[r]);if(t)if(n)for(o=o||mt(e),s=s||mt(a),r=0,i=o.length;i>r;r++)gt(o[r],s[r]);else gt(e,a);return s=mt(a,"script"),s.length>0&&dt(s,!u&&mt(e,"script")),a},buildFragment:function(e,t,n,r){var i,o,s,a,u,l,c=0,p=e.length,f=t.createDocumentFragment(),h=[];for(;p>c;c++)if(i=e[c],i||0===i)if("object"===x.type(i))x.merge(h,i.nodeType?[i]:i);else if(rt.test(i)){o=o||f.appendChild(t.createElement("div")),s=(nt.exec(i)||["",""])[1].toLowerCase(),a=ct[s]||ct._default,o.innerHTML=a[1]+i.replace(tt,"<$1>")+a[2],l=a[0];while(l--)o=o.lastChild;x.merge(h,o.childNodes),o=f.firstChild,o.textContent=""}else h.push(t.createTextNode(i));f.textContent="",c=0;while(i=h[c++])if((!r||-1===x.inArray(i,r))&&(u=x.contains(i.ownerDocument,i),o=mt(f.appendChild(i),"script"),u&&dt(o),n)){l=0;while(i=o[l++])at.test(i.type||"")&&n.push(i)}return f},cleanData:function(e){var t,n,r,i,o,s,a=x.event.special,u=0;for(;(n=e[u])!==undefined;u++){if(F.accepts(n)&&(o=n[q.expando],o&&(t=q.cache[o]))){if(r=Object.keys(t.events||{}),r.length)for(s=0;(i=r[s])!==undefined;s++)a[i]?x.event.remove(n,i):x.removeEvent(n,i,t.handle);q.cache[o]&&delete q.cache[o]}delete L.cache[n[L.expando]]}},_evalUrl:function(e){return x.ajax({url:e,type:"GET",dataType:"script",async:!1,global:!1,"throws":!0})}});function pt(e,t){return x.nodeName(e,"table")&&x.nodeName(1===t.nodeType?t:t.firstChild,"tr")?e.getElementsByTagName("tbody")[0]||e.appendChild(e.ownerDocument.createElement("tbody")):e}function ft(e){return e.type=(null!==e.getAttribute("type"))+"/"+e.type,e}function ht(e){var t=ut.exec(e.type);return t?e.type=t[1]:e.removeAttribute("type"),e}function dt(e,t){var n=e.length,r=0;for(;n>r;r++)q.set(e[r],"globalEval",!t||q.get(t[r],"globalEval"))}function gt(e,t){var n,r,i,o,s,a,u,l;if(1===t.nodeType){if(q.hasData(e)&&(o=q.access(e),s=q.set(t,o),l=o.events)){delete s.handle,s.events={};for(i in l)for(n=0,r=l[i].length;r>n;n++)x.event.add(t,i,l[i][n])}L.hasData(e)&&(a=L.access(e),u=x.extend({},a),L.set(t,u))}}function mt(e,t){var n=e.getElementsByTagName?e.getElementsByTagName(t||"*"):e.querySelectorAll?e.querySelectorAll(t||"*"):[];return t===undefined||t&&x.nodeName(e,t)?x.merge([e],n):n}function yt(e,t){var n=t.nodeName.toLowerCase();"input"===n&&ot.test(e.type)?t.checked=e.checked:("input"===n||"textarea"===n)&&(t.defaultValue=e.defaultValue)}x.fn.extend({wrapAll:function(e){var t;return x.isFunction(e)?this.each(function(t){x(this).wrapAll(e.call(this,t))}):(this[0]&&(t=x(e,this[0].ownerDocument).eq(0).clone(!0),this[0].parentNode&&t.insertBefore(this[0]),t.map(function(){var e=this;while(e.firstElementChild)e=e.firstElementChild;return e}).append(this)),this)},wrapInner:function(e){return x.isFunction(e)?this.each(function(t){x(this).wrapInner(e.call(this,t))}):this.each(function(){var t=x(this),n=t.contents();n.length?n.wrapAll(e):t.append(e)})},wrap:function(e){var t=x.isFunction(e);return this.each(function(n){x(this).wrapAll(t?e.call(this,n):e)})},unwrap:function(){return this.parent().each(function(){x.nodeName(this,"body")||x(this).replaceWith(this.childNodes)}).end()}});var vt,xt,bt=/^(none|table(?!-c[ea]).+)/,wt=/^margin/,Tt=RegExp("^("+b+")(.*)$","i"),Ct=RegExp("^("+b+")(?!px)[a-z%]+$","i"),kt=RegExp("^([+-])=("+b+")","i"),Nt={BODY:"block"},Et={position:"absolute",visibility:"hidden",display:"block"},St={letterSpacing:0,fontWeight:400},jt=["Top","Right","Bottom","Left"],Dt=["Webkit","O","Moz","ms"];function At(e,t){if(t in e)return t;var n=t.charAt(0).toUpperCase()+t.slice(1),r=t,i=Dt.length;while(i--)if(t=Dt[i]+n,t in e)return t;return r}function Lt(e,t){return e=t||e,"none"===x.css(e,"display")||!x.contains(e.ownerDocument,e)}function qt(t){return e.getComputedStyle(t,null)}function Ht(e,t){var n,r,i,o=[],s=0,a=e.length;for(;a>s;s++)r=e[s],r.style&&(o[s]=q.get(r,"olddisplay"),n=r.style.display,t?(o[s]||"none"!==n||(r.style.display=""),""===r.style.display&&Lt(r)&&(o[s]=q.access(r,"olddisplay",Rt(r.nodeName)))):o[s]||(i=Lt(r),(n&&"none"!==n||!i)&&q.set(r,"olddisplay",i?n:x.css(r,"display"))));for(s=0;a>s;s++)r=e[s],r.style&&(t&&"none"!==r.style.display&&""!==r.style.display||(r.style.display=t?o[s]||"":"none"));return e}x.fn.extend({css:function(e,t){return x.access(this,function(e,t,n){var r,i,o={},s=0;if(x.isArray(t)){for(r=qt(e),i=t.length;i>s;s++)o[t[s]]=x.css(e,t[s],!1,r);return o}return n!==undefined?x.style(e,t,n):x.css(e,t)},e,t,arguments.length>1)},show:function(){return Ht(this,!0)},hide:function(){return Ht(this)},toggle:function(e){return"boolean"==typeof e?e?this.show():this.hide():this.each(function(){Lt(this)?x(this).show():x(this).hide()})}}),x.extend({cssHooks:{opacity:{get:function(e,t){if(t){var n=vt(e,"opacity");return""===n?"1":n}}}},cssNumber:{columnCount:!0,fillOpacity:!0,fontWeight:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,widows:!0,zIndex:!0,zoom:!0},cssProps:{"float":"cssFloat"},style:function(e,t,n,r){if(e&&3!==e.nodeType&&8!==e.nodeType&&e.style){var i,o,s,a=x.camelCase(t),u=e.style;return t=x.cssProps[a]||(x.cssProps[a]=At(u,a)),s=x.cssHooks[t]||x.cssHooks[a],n===undefined?s&&"get"in s&&(i=s.get(e,!1,r))!==undefined?i:u[t]:(o=typeof n,"string"===o&&(i=kt.exec(n))&&(n=(i[1]+1)*i[2]+parseFloat(x.css(e,t)),o="number"),null==n||"number"===o&&isNaN(n)||("number"!==o||x.cssNumber[a]||(n+="px"),x.support.clearCloneStyle||""!==n||0!==t.indexOf("background")||(u[t]="inherit"),s&&"set"in s&&(n=s.set(e,n,r))===undefined||(u[t]=n)),undefined)}},css:function(e,t,n,r){var i,o,s,a=x.camelCase(t);return t=x.cssProps[a]||(x.cssProps[a]=At(e.style,a)),s=x.cssHooks[t]||x.cssHooks[a],s&&"get"in s&&(i=s.get(e,!0,n)),i===undefined&&(i=vt(e,t,r)),"normal"===i&&t in St&&(i=St[t]),""===n||n?(o=parseFloat(i),n===!0||x.isNumeric(o)?o||0:i):i}}),vt=function(e,t,n){var r,i,o,s=n||qt(e),a=s?s.getPropertyValue(t)||s[t]:undefined,u=e.style;return s&&(""!==a||x.contains(e.ownerDocument,e)||(a=x.style(e,t)),Ct.test(a)&&wt.test(t)&&(r=u.width,i=u.minWidth,o=u.maxWidth,u.minWidth=u.maxWidth=u.width=a,a=s.width,u.width=r,u.minWidth=i,u.maxWidth=o)),a};function Ot(e,t,n){var r=Tt.exec(t);return r?Math.max(0,r[1]-(n||0))+(r[2]||"px"):t}function Ft(e,t,n,r,i){var o=n===(r?"border":"content")?4:"width"===t?1:0,s=0;for(;4>o;o+=2)"margin"===n&&(s+=x.css(e,n+jt[o],!0,i)),r?("content"===n&&(s-=x.css(e,"padding"+jt[o],!0,i)),"margin"!==n&&(s-=x.css(e,"border"+jt[o]+"Width",!0,i))):(s+=x.css(e,"padding"+jt[o],!0,i),"padding"!==n&&(s+=x.css(e,"border"+jt[o]+"Width",!0,i)));return s}function Pt(e,t,n){var r=!0,i="width"===t?e.offsetWidth:e.offsetHeight,o=qt(e),s=x.support.boxSizing&&"border-box"===x.css(e,"boxSizing",!1,o);if(0>=i||null==i){if(i=vt(e,t,o),(0>i||null==i)&&(i=e.style[t]),Ct.test(i))return i;r=s&&(x.support.boxSizingReliable||i===e.style[t]),i=parseFloat(i)||0}return i+Ft(e,t,n||(s?"border":"content"),r,o)+"px"}function Rt(e){var t=o,n=Nt[e];return n||(n=Mt(e,t),"none"!==n&&n||(xt=(xt||x("