From 664bfa78dbfcdc3ac35dc168350c03a524145900 Mon Sep 17 00:00:00 2001 From: andy Date: Fri, 4 Apr 2014 12:01:06 +0100 Subject: [PATCH] [FIX] issue 14, improve formating/jsdoc --- README.md | 12 +- changelog.md | 5 + debug.js | 22 +- docs/global.html | 3121 ++++++++++++++++++ docs/index.html | 63 + docs/index.js.html | 555 ++++ docs/scripts/linenumber.js | 25 + docs/scripts/prettify/Apache-License-2.0.txt | 202 ++ docs/scripts/prettify/lang-css.js | 2 + docs/scripts/prettify/prettify.js | 28 + docs/styles/jsdoc-default.css | 334 ++ docs/styles/prettify-jsdoc.css | 111 + docs/styles/prettify-tomorrow.css | 132 + examples/EventBasics.js | 7 + examples/EventExample.js | 28 + examples/EventSync.js | 7 + examples/Example.js | 7 + examples/Httpserver.js | 7 + examples/RawExample.js | 7 + examples/TableExample.js | 13 + examples/issue14.js | 21 + examples/json.js | 7 + examples/json2.js | 7 + examples/manysessions.js | 6 + examples/raw.js | 7 + index.js | 790 +++-- lib/basexstream.js | 43 +- lib/parser2.js | 23 +- lib/query.js | 92 +- lib/queue.js | 34 + lib/watch.js | 38 +- test/test-commands.js | 64 +- test/test-stress.js | 8 +- 33 files changed, 5456 insertions(+), 372 deletions(-) create mode 100644 docs/global.html create mode 100644 docs/index.html create mode 100644 docs/index.js.html create mode 100644 docs/scripts/linenumber.js create mode 100644 docs/scripts/prettify/Apache-License-2.0.txt create mode 100644 docs/scripts/prettify/lang-css.js create mode 100644 docs/scripts/prettify/prettify.js create mode 100644 docs/styles/jsdoc-default.css create mode 100644 docs/styles/prettify-jsdoc.css create mode 100644 docs/styles/prettify-tomorrow.css create mode 100644 examples/issue14.js diff --git a/README.md b/README.md index 7c5a589..ea37cb8 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,9 @@ # basex - A BaseX client for node.js =========================== -This is BaseX client for Node.js. It is work in progress. It uses the client interface via a socket connection to the BaseX server. +This is BaseX client for Node.js. It is work in progress. +It uses the [client interface](http://docs.basex.org/wiki/Server_Protocol) + via a socket connection to the BaseX server. [BaseX](http://basex.org/) is a very light-weight, high-performance and scalable XML Database engine and XPath/XQuery 3.0 Processor, @@ -38,7 +40,7 @@ Once BaseX is installed and the BaseX server is running, test it. ## Installing BaseX 1. Java is required 1. [Download](http://basex.org/products/download/all-downloads/) and install BaseX -(tested against version 7.6) +(tested against version 7.7) 1. Run `basexserver -S` ## API specification @@ -89,6 +91,10 @@ mocha -R spec test/test-commands.js 13 tests complete (408 ms) ``` +# Coding + +Javascript is formated using js-beautify `js-beautify -r index.js` +Documentation is generated using `jsdoc -r -d docs --verbose index.js` # TODO @@ -98,7 +104,7 @@ mocha -R spec test/test-commands.js # Inspiration Parts inspired by [node_redis](https://github.com/mranney/node_redis), -[BaseX Java client](https://github.com/BaseXdb/basex-examples/blob/master/src/main/java/org/basex/examples/api/BaseXClient.java) +[BaseX Java client](https://github.com/BaseXdb/basex/blob/master/basex-examples/src/main/java/org/basex/examples/api/BaseXClient.java) #license diff --git a/changelog.md b/changelog.md index b8f5dad..16fe6b5 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,8 @@ +## v0.6.3 - 2014-04-03 +- fix handling for add where doc is invalid (#14) +- fix tests +- applied js-beautify to the code + ## v0.6.2 - 2013-07-13 - fix query array result timing issue (#10) - fix session.create diff --git a/debug.js b/debug.js index c002dc8..b1c7911 100644 --- a/debug.js +++ b/debug.js @@ -1,6 +1,14 @@ -/* example functions for use as test callbacks -* andy bunce 2011-2012 -*/ +/** + * example functions for use as test callbacks + * andy bunce 2011-2014 + **/ + +/** + * @method print + * @param {} err + * @param {} reply + * @return + */ function print(err, reply) { if (err) { console.log("Error: " + err); @@ -8,7 +16,13 @@ function print(err, reply) { console.dir(reply); } }; -// show supplied msg then basex server response + +/** + * show supplied msg then basex server response + * @method printMsg + * @param {} msg + * @return FunctionExpression + */ function printMsg(msg) { return function(err, reply){ console.log("printMsg: ",msg); diff --git a/docs/global.html b/docs/global.html new file mode 100644 index 0000000..2295002 --- /dev/null +++ b/docs/global.html @@ -0,0 +1,3121 @@ + + + + + JSDoc: Global + + + + + + + + + + +
+ +

Global

+ + + + + +
+ +
+

+ +

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

Methods

+ +
+ +
+

add(path, input, callback)

+ + +
+
+ + +
+ Adds a document to the current database from an input stream: +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
path + +
input + +
callback + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

callback(err, reply)

+ + +
+
+ + +
+ Description +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
err + +
reply + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

callback()

+ + +
+
+ + +
+ Description +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

close(callback)

+ + +
+
+ + +
+ end the session, sash callback, for stream end +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
callback + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

create(name, input, callback)

+ + +
+
+ + +
+ Creates a database from an input stream: +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + +
input + +
callback + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

execute(cmd, callback)

+ + +
+
+ + +
+ add command and returns the result: +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
cmd + +
callback + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

md5(str)

+ + +
+
+ + +
+ hash str using md5 +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
str + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ CallExpression +
+ + + + + + +
+ + + +
+

onData()

+ + +
+
+ + +
+ respond to data arrival +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

parser()

+ + +
+
+ + +
+ Description +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ CallExpression +
+ + + + + + +
+ + + +
+

parser1()

+ + +
+
+ + +
+ standard parser read 2 lines and byte +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ CallExpression +
+ + + + + + +
+ + + +
+

parser2()

+ + +
+
+ + +
+ read line and byte possible error info +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

parserOk()

+ + +
+
+ + +
+ read status byte +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ CallExpression +
+ + + + + + +
+ + + +
+

parsewatch()

+ + +
+
+ + +
+ parse 1st watch response, expect port,id +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

parsewatch2()

+ + +
+
+ + +
+ parse other watch response +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

query(query)

+ + +
+
+ + +
+ Returns a query object for the specified query: +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
query + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ NewExpression +
+ + + + + + +
+ + + +
+

replace(path, input, callback)

+ + +
+
+ + +
+ Replaces a document with the specified input stream: +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
path + +
input + +
callback + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

reset()

+ + +
+
+ + +
+ reset state +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

send(s)

+ + +
+
+ + +
+ send to server +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
s + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

send_command(cmd)

+ + +
+
+ + +
+ queue a command to server +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
cmd + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

sendQueueItem()

+ + +
+
+ + +
+ do the next queued command, if any +
+ + + + + + + + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + +
+ boolean true if command was sent +
+ + + + + + +
+ + + +
+

Session(host, port, username, password)

+ + +
+
+ + +
+ Create a session connection +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
host + +
port + +
username + +
password + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

setBlock(state)

+ + +
+
+ + +
+ set or clear flag allowing sending to server +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
state + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

socketError(e)

+ + +
+
+ + +
+ Description +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
e + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

store(path, input, callback)

+ + +
+
+ + +
+ Stores raw data at the specified path: +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
path + +
input + +
callback + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

unwatch(name, callback)

+ + +
+
+ + +
+ unsubscribe to event +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + +
callback + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ + + +
+

watch(name, notification, callback)

+ + +
+
+ + +
+ Subscribe to event +
+ + + + + + + +
Parameters:
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NameTypeDescription
name + +
notification + +
callback + +
+ + + +
+ + + + + + + + + + + + + + + + + + + +
Source:
+
+ + + + + + + +
+ + + + + + + + + + + + + +
Returns:
+ + + + + + + +
+ +
+ + + + + +
+ +
+ + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..0e0a278 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,63 @@ + + + + + JSDoc: Index + + + + + + + + + + +
+ +

Index

+ + + + + + + +

+ + + + + + + + + + + + + + + + + + + +
+ + + +
+ + + + + + + \ No newline at end of file diff --git a/docs/index.js.html b/docs/index.js.html new file mode 100644 index 0000000..829c4b9 --- /dev/null +++ b/docs/index.js.html @@ -0,0 +1,555 @@ + + + + + JSDoc: Source: index.js + + + + + + + + + + +
+ +

Source: index.js

+ + + + + +
+
+
/* BaseX Node.js client
+ * http://docs.basex.org/wiki/Server_Protocol
+ * andy bunce 2011-2012
+ */
+
+// set this to true to enable console.log msgs for all connections
+exports.debug_mode = false;
+
+var net = require("net"),
+    util = require("util"),
+    events = require("events"),
+    crypto = require("crypto"),
+    Query = require("./lib/query").Query,
+    Watch = require("./lib/watch").Watch,
+    parser2 = require("./lib/parser2"),
+    Queue = require("./lib/queue").Queue;
+
+var states = {
+    DISCONNECTED: 0,
+    CONNECTING: 1,
+    AUTHORIZE: 2,
+    CONNECTED: 3,
+    CLOSING: 4
+};
+
+var tagid = 0; // used to give each Session a unique .tag property
+
+/**
+ * Create a session connection
+ * @method Session
+ * @param {} host
+ * @param {} port
+ * @param {} username
+ * @param {} password
+ * @return
+ */
+var Session = function(host, port, username, password) {
+    var self = this;
+    this.host = host || "127.0.0.1";
+    this.port = port || 1984;
+    this.username = username || "admin";
+    this.password = password || "admin";
+    this.tag = "S" + (++tagid);
+    this.commands_sent = 0;
+    this.parser2part = null; // parser2 flag
+    // reset
+    /**
+     * reset state
+     * @method reset
+     * @return
+     */
+    this.reset = function() {
+        this.state = states.DISCONNECTED;
+        this.current_command = null; // waiting for response to this
+        this.closefn = null;
+        this.blocked = false; // can't send until reply
+        this.buffer = "";
+        this.q_pending = new Queue(); // holds commands to send
+        this.q_sent = new Queue(); // holds commands sent
+        // event stuff
+        this.event = null;
+        // initial parser for auth
+        /**
+         * Description
+         * @method parser
+         * @return CallExpression
+         */
+        this.parser = function() {
+            return self.bxp.need(["data"], false) //timestamp
+        };
+    };
+    this.reset();
+
+    var stream = net.createConnection(this.port, this.host);
+    //stream.setEncoding('utf-8');
+    this.bxp = new parser2.parse(stream);
+    //this.bxp.on("data",function(d){console.log("ping",d.toString())});
+    this.stream = stream;
+
+    this.stream.on("connect", function() {
+        self.state = states.CONNECTING;
+        if (exports.debug_mode) {
+            console.log(self.tag + ": stream connected");
+        }
+    });
+
+    this.stream.on("data", function(reply) {
+        if (exports.debug_mode) {
+            console.log(self.tag + "<<");
+            console.dir(reply.toString());
+        }
+        if (self.state == states.CONNECTED) {
+            self.onData()
+        } else if (self.state == states.CONNECTING) {
+            var read = self.parser();
+            if (read) {
+                self.send(self.username + "\0");
+                var s = md5(md5(self.password) + read.data);
+                self.send(s + "\0");
+                self.state = states.AUTHORIZE;
+            }
+        } else if (self.state == states.AUTHORIZE) {
+            var read = self.bxp.popByte()
+            if (read) {
+                if (read.data != "\0") {
+                    //console.log("data",self.bxp.data,"buff: ",self.bxp.buffer)
+                    throw "Access denied.";
+                }
+                self.state = states.CONNECTED;
+                if (exports.debug_mode) {
+                    console.log(self.tag + ": authorized");
+                }
+                self.emit("connect", 1);
+                self.sendQueueItem();
+            }
+        } else {
+            throw "Bad state.";
+        }
+    });
+
+    /**
+     * respond to data arrival
+     * @method onData
+     * @return
+     */
+    this.onData = function() {
+        // console.log("onData");
+        var r, cc = self.current_command;
+        while (cc && (r = cc.parser())) {
+            if (exports.debug_mode) {
+                console.log("response: ", r);
+            }
+            if (cc.callback) {
+                cc.callback(r.ok ? null : r.info, r);
+            }
+
+            cc = self.current_command = self.q_sent.shift();
+            //console.log("next is:");
+            //console.dir(self.current_command);
+        }
+    };
+
+    this.stream.on("error", socketError);
+
+    this.stream.on("close", function() {
+        if (exports.debug_mode) {
+            console.log(self.tag + ": stream closed");
+        }
+        if (self.event) self.event.close()
+
+        if (self.closefn) {
+            self.closefn();
+            self.closefn = null;
+        };
+    });
+
+    this.stream.on("end", function() {
+        // console.log(self.tag+": stream end");
+    });
+
+    this.stream.on("drain", function() {
+        // console.log("drain");
+    });
+
+    /**
+     * send to server
+     * @method send
+     * @param {} s
+     * @return
+     */
+    this.send = function(s) {
+        if (typeof s === "function")
+            s = s();
+
+        if (exports.debug_mode) {
+            console.log(self.tag + ">>");
+            console.dir(s);
+        };
+        self.stream.write(s);
+        self.commands_sent += 1;
+    };
+
+
+
+    /**
+     * standard parser read 2 lines and byte
+     * @method parser1
+     * @return CallExpression
+     */
+    this.parser1 = function() {
+        return self.bxp.need(["result", "info"], true)
+    };
+
+    /**
+     * read status byte
+     * @method parserOk
+     * @return CallExpression
+     */
+    this.parserOk = function() {
+        return self.bxp.popByte()
+    };
+
+
+    /**
+     * read line and byte possible error info
+     * @method parser2
+     * @return
+     */
+    this.parser2 = function() {
+        if (!self.parser2part) {
+            var r = self.bxp.need(["result"], true)
+            if (!r) return
+            if (r.ok) {
+                return {
+                    ok: true,
+                    result: r.result
+                }
+            } else {
+                self.parser2part = r;
+            }
+        } else {
+            var r = self.bxp.need(["info"], false)
+            if (!r) return
+            var res = {
+                ok: false,
+                info: r.info,
+                result: self.parser2part.result
+            }
+            self.parser2part = null;
+            return res
+        }
+    };
+
+
+    /**
+     * add command and returns the result:
+     * @method execute
+     * @param {} cmd
+     * @param {} callback
+     * @return
+     */
+    this.execute = function(cmd, callback) {
+        self.send_command({
+            send: cmd + "\0",
+            parser: self.parser1,
+            callback: callback
+        });
+    };
+
+    /**
+     * Returns a query object for the specified query:
+     * @method query
+     * @param {} query
+     * @return NewExpression
+     */
+    this.query = function(query) {
+        return new Query(self, query);
+    };
+
+    /**
+     * Creates a database from an input stream:
+     * @method create
+     * @param {} name
+     * @param {} input
+     * @param {} callback
+     * @return
+     */
+    this.create = function(name, input, callback) {
+        self._dbop("\x08", name, input, callback)
+    };
+
+    /**
+     * Adds a document to the current database from an input stream:
+     * @method add
+     * @param {} path
+     * @param {} input
+     * @param {} callback
+     * @return
+     */
+    this.add = function(path, input, callback) {
+        self._dbop("\x09", path, input, callback)
+    };
+
+    /**
+     * Replaces a document with the specified input stream:
+     * @method replace
+     * @param {} path
+     * @param {} input
+     * @param {} callback
+     * @return
+     */
+    this.replace = function(path, input, callback) {
+        self._dbop("\x0C", path, input, callback)
+    };
+
+    /**
+     * Stores raw data at the specified path:
+     * @method store
+     * @param {} path
+     * @param {} input
+     * @param {} callback
+     * @return
+     */
+    this.store = function(path, input, callback) {
+        self._dbop("\x0D", path, input, callback)
+    };
+    //@TODO allow input to be stream, currently just string
+    this._dbop = function(op, path, input, callback) {
+        self.send_command({
+            send: op + path + "\0" + input + "\0",
+            parser: self.parser2,
+            callback: callback
+        });
+    };
+
+    /**
+     * Subscribe to event
+     * @method watch
+     * @param {} name
+     * @param {} notification
+     * @param {} callback
+     * @return
+     */
+    this.watch = function(name, notification, callback) {
+        if (self.event == null) { //1st time 
+            self.send_command({
+                send: "\x0A",
+                parser: self.parsewatch,
+                /**
+                 * Description
+                 * @method callback
+                 * @return
+                 */
+                callback: function() {
+
+                    self.event.add(name, notification)
+                    // add at front
+                    self.send_command({
+                        send: name + "\0",
+                        parser: self.parsewatch2,
+                        callback: callback
+                    });
+                    self.setBlock(false);
+                },
+                blocking: true
+            })
+        } else {
+            self.event.add(name, notification)
+            self.send_command({
+                send: "\x0A" + name + "\0",
+                parser: self.parsewatch2,
+                callback: callback
+            });
+        }
+
+    };
+
+    /**
+     * parse 1st watch response, expect port,id
+     * @method parsewatch
+     * @return
+     */
+    this.parsewatch = function() {
+        var flds = self.bxp.need(["eport", "id"], false)
+        if (flds) {
+            if (self.event == null) {
+                self.event = new Watch(self.host, flds.eport, flds.id)
+                self.event.on("connect", self.onData) //need to wait
+            }
+            flds.ok = true // expected by reader
+            return flds;
+        }
+    };
+
+
+    /**
+     * parse other watch response
+     * @method parsewatch2
+     * @return
+     */
+    this.parsewatch2 = function() {
+        // wait info and connected
+        //console.log("qqqq",self.event.isConnected)
+        //console.log(".....parsewatch2",self.buffer)
+        if (self.event.isConnected) return self.bxp.need(["info"], true)
+    };
+
+    /**
+     * unsubscribe to event
+     * @method unwatch
+     * @param {} name
+     * @param {} callback
+     * @return
+     */
+    this.unwatch = function(name, callback) {
+        self.send_command({
+            send: "\x0B" + name + "\0",
+            parser: self.parser2,
+            /**
+             * Description
+             * @method callback
+             * @param {} err
+             * @param {} reply
+             * @return
+             */
+            callback: function(err, reply) {
+                self.event.remove(name);
+                callback(err, reply);
+            }
+        });
+    };
+
+    /**
+     * end the session, sash callback, for stream end
+     * @method close
+     * @param {} callback
+     * @return
+     */
+    this.close = function(callback) {
+        self.closefn = callback;
+        self.send_command({
+            send: "exit" + "\0",
+            parser: self.parserOk
+            // sometime get this, timing
+        });
+    };
+    // -------end of commands ---------
+
+    // 
+    /**
+     * queue a command to server
+     * @method send_command
+     * @param {} cmd
+     * @return
+     */
+    this.send_command = function(cmd) {
+        self.q_pending.push(cmd);
+        self.sendQueueItem();
+    };
+
+    /**
+     * do the next queued command, if any
+     * @method sendQueueItem
+     * @return boolean true if command was sent
+     */
+    this.sendQueueItem = function() {
+        // console.log("queues waiting send: ",self.q_pending.length,", waiting
+        // reply:",self.q_sent.length)
+
+        if (self.q_pending.length == 0 || self.blocked || self.state != states.CONNECTED)
+            return false
+
+        var cmd = self.q_pending.shift();
+        if (!self.current_command) {
+            self.current_command = cmd;
+            // console.log("current command: ",cmd.send)
+
+        } else {
+            self.q_sent.push(cmd);
+        };
+
+        self.send(cmd.send);
+        self.setBlock(cmd.blocking ? true : false);
+        return true;
+    };
+
+    /**
+     * set or clear flag allowing sending to server
+     * @method setBlock
+     * @param {} state
+     * @return
+     */
+    this.setBlock = function(state) {
+        //console.log("blocked:",state)
+        self.blocked = state;
+        while (self.sendQueueItem()) {};
+    };
+    events.EventEmitter.call(this);
+};
+
+/**
+ * Description
+ * @method socketError
+ * @param {} e
+ * @return
+ */
+function socketError(e) {
+    if (e.code == 'ECONNREFUSED') {
+        console.log('ECONNREFUSED: connection refused. Check BaseX server is running.');
+    } else {
+        console.log(e);
+    }
+}
+/**
+ * hash str using md5
+ * @method md5
+ * @param {} str
+ * @return CallExpression
+ */
+function md5(str) {
+    return crypto.createHash('md5').update(str).digest("hex");
+};
+
+util.inherits(Session, events.EventEmitter);
+exports.Session = Session;
+
+
+
+ + + + +
+ + + +
+ + + + + + + diff --git a/docs/scripts/linenumber.js b/docs/scripts/linenumber.js new file mode 100644 index 0000000..8d52f7e --- /dev/null +++ b/docs/scripts/linenumber.js @@ -0,0 +1,25 @@ +/*global document */ +(function() { + var source = document.getElementsByClassName('prettyprint source linenums'); + var i = 0; + var lineNumber = 0; + var lineId; + var lines; + var totalLines; + var anchorHash; + + if (source && source[0]) { + anchorHash = document.location.hash.substring(1); + lines = source[0].getElementsByTagName('li'); + totalLines = lines.length; + + for (; i < totalLines; i++) { + lineNumber++; + lineId = 'line' + lineNumber; + lines[i].id = lineId; + if (lineId === anchorHash) { + lines[i].className += ' selected'; + } + } + } +})(); diff --git a/docs/scripts/prettify/Apache-License-2.0.txt b/docs/scripts/prettify/Apache-License-2.0.txt new file mode 100644 index 0000000..d645695 --- /dev/null +++ b/docs/scripts/prettify/Apache-License-2.0.txt @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/docs/scripts/prettify/lang-css.js b/docs/scripts/prettify/lang-css.js new file mode 100644 index 0000000..041e1f5 --- /dev/null +++ b/docs/scripts/prettify/lang-css.js @@ -0,0 +1,2 @@ +PR.registerLangHandler(PR.createSimpleLexer([["pln",/^[\t\n\f\r ]+/,null," \t\r\n "]],[["str",/^"(?:[^\n\f\r"\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*"/,null],["str",/^'(?:[^\n\f\r'\\]|\\(?:\r\n?|\n|\f)|\\[\S\s])*'/,null],["lang-css-str",/^url\(([^"')]*)\)/i],["kwd",/^(?:url|rgb|!important|@import|@page|@media|@charset|inherit)(?=[^\w-]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*)\s*:/i],["com",/^\/\*[^*]*\*+(?:[^*/][^*]*\*+)*\//],["com", +/^(?:<\!--|--\>)/],["lit",/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],["lit",/^#[\da-f]{3,6}/i],["pln",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i],["pun",/^[^\s\w"']+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[["kwd",/^-?(?:[_a-z]|\\[\da-f]+ ?)(?:[\w-]|\\\\[\da-f]+ ?)*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[["str",/^[^"')]+/]]),["css-str"]); diff --git a/docs/scripts/prettify/prettify.js b/docs/scripts/prettify/prettify.js new file mode 100644 index 0000000..eef5ad7 --- /dev/null +++ b/docs/scripts/prettify/prettify.js @@ -0,0 +1,28 @@ +var q=null;window.PR_SHOULD_USE_CONTINUATION=!0; +(function(){function L(a){function m(a){var f=a.charCodeAt(0);if(f!==92)return f;var b=a.charAt(1);return(f=r[b])?f:"0"<=b&&b<="7"?parseInt(a.substring(1),8):b==="u"||b==="x"?parseInt(a.substring(2),16):a.charCodeAt(1)}function e(a){if(a<32)return(a<16?"\\x0":"\\x")+a.toString(16);a=String.fromCharCode(a);if(a==="\\"||a==="-"||a==="["||a==="]")a="\\"+a;return a}function h(a){for(var f=a.substring(1,a.length-1).match(/\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\[0-3][0-7]{0,2}|\\[0-7]{1,2}|\\[\S\s]|[^\\]/g),a= +[],b=[],o=f[0]==="^",c=o?1:0,i=f.length;c122||(d<65||j>90||b.push([Math.max(65,j)|32,Math.min(d,90)|32]),d<97||j>122||b.push([Math.max(97,j)&-33,Math.min(d,122)&-33]))}}b.sort(function(a,f){return a[0]-f[0]||f[1]-a[1]});f=[];j=[NaN,NaN];for(c=0;ci[0]&&(i[1]+1>i[0]&&b.push("-"),b.push(e(i[1])));b.push("]");return b.join("")}function y(a){for(var f=a.source.match(/\[(?:[^\\\]]|\\[\S\s])*]|\\u[\dA-Fa-f]{4}|\\x[\dA-Fa-f]{2}|\\\d+|\\[^\dux]|\(\?[!:=]|[()^]|[^()[\\^]+/g),b=f.length,d=[],c=0,i=0;c=2&&a==="["?f[c]=h(j):a!=="\\"&&(f[c]=j.replace(/[A-Za-z]/g,function(a){a=a.charCodeAt(0);return"["+String.fromCharCode(a&-33,a|32)+"]"}));return f.join("")}for(var t=0,s=!1,l=!1,p=0,d=a.length;p=5&&"lang-"===b.substring(0,5))&&!(o&&typeof o[1]==="string"))c=!1,b="src";c||(r[f]=b)}i=d;d+=f.length;if(c){c=o[1];var j=f.indexOf(c),k=j+c.length;o[2]&&(k=f.length-o[2].length,j=k-c.length);b=b.substring(5);B(l+i,f.substring(0,j),e,p);B(l+i+j,c,C(b,c),p);B(l+i+k,f.substring(k),e,p)}else p.push(l+i,b)}a.e=p}var h={},y;(function(){for(var e=a.concat(m), +l=[],p={},d=0,g=e.length;d=0;)h[n.charAt(k)]=r;r=r[1];n=""+r;p.hasOwnProperty(n)||(l.push(r),p[n]=q)}l.push(/[\S\s]/);y=L(l)})();var t=m.length;return e}function u(a){var m=[],e=[];a.tripleQuotedStrings?m.push(["str",/^(?:'''(?:[^'\\]|\\[\S\s]|''?(?=[^']))*(?:'''|$)|"""(?:[^"\\]|\\[\S\s]|""?(?=[^"]))*(?:"""|$)|'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$))/,q,"'\""]):a.multiLineStrings?m.push(["str",/^(?:'(?:[^'\\]|\\[\S\s])*(?:'|$)|"(?:[^"\\]|\\[\S\s])*(?:"|$)|`(?:[^\\`]|\\[\S\s])*(?:`|$))/, +q,"'\"`"]):m.push(["str",/^(?:'(?:[^\n\r'\\]|\\.)*(?:'|$)|"(?:[^\n\r"\\]|\\.)*(?:"|$))/,q,"\"'"]);a.verbatimStrings&&e.push(["str",/^@"(?:[^"]|"")*(?:"|$)/,q]);var h=a.hashComments;h&&(a.cStyleComments?(h>1?m.push(["com",/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,q,"#"]):m.push(["com",/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\n\r]*)/,q,"#"]),e.push(["str",/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,q])):m.push(["com",/^#[^\n\r]*/, +q,"#"]));a.cStyleComments&&(e.push(["com",/^\/\/[^\n\r]*/,q]),e.push(["com",/^\/\*[\S\s]*?(?:\*\/|$)/,q]));a.regexLiterals&&e.push(["lang-regex",/^(?:^^\.?|[!+-]|!=|!==|#|%|%=|&|&&|&&=|&=|\(|\*|\*=|\+=|,|-=|->|\/|\/=|:|::|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|[?@[^]|\^=|\^\^|\^\^=|{|\||\|=|\|\||\|\|=|~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\s*(\/(?=[^*/])(?:[^/[\\]|\\[\S\s]|\[(?:[^\\\]]|\\[\S\s])*(?:]|$))+\/)/]);(h=a.types)&&e.push(["typ",h]);a=(""+a.keywords).replace(/^ | $/g, +"");a.length&&e.push(["kwd",RegExp("^(?:"+a.replace(/[\s,]+/g,"|")+")\\b"),q]);m.push(["pln",/^\s+/,q," \r\n\t\xa0"]);e.push(["lit",/^@[$_a-z][\w$@]*/i,q],["typ",/^(?:[@_]?[A-Z]+[a-z][\w$@]*|\w+_t\b)/,q],["pln",/^[$_a-z][\w$@]*/i,q],["lit",/^(?:0x[\da-f]+|(?:\d(?:_\d+)*\d*(?:\.\d*)?|\.\d\+)(?:e[+-]?\d+)?)[a-z]*/i,q,"0123456789"],["pln",/^\\[\S\s]?/,q],["pun",/^.[^\s\w"-$'./@\\`]*/,q]);return x(m,e)}function D(a,m){function e(a){switch(a.nodeType){case 1:if(k.test(a.className))break;if("BR"===a.nodeName)h(a), +a.parentNode&&a.parentNode.removeChild(a);else for(a=a.firstChild;a;a=a.nextSibling)e(a);break;case 3:case 4:if(p){var b=a.nodeValue,d=b.match(t);if(d){var c=b.substring(0,d.index);a.nodeValue=c;(b=b.substring(d.index+d[0].length))&&a.parentNode.insertBefore(s.createTextNode(b),a.nextSibling);h(a);c||a.parentNode.removeChild(a)}}}}function h(a){function b(a,d){var e=d?a.cloneNode(!1):a,f=a.parentNode;if(f){var f=b(f,1),g=a.nextSibling;f.appendChild(e);for(var h=g;h;h=g)g=h.nextSibling,f.appendChild(h)}return e} +for(;!a.nextSibling;)if(a=a.parentNode,!a)return;for(var a=b(a.nextSibling,0),e;(e=a.parentNode)&&e.nodeType===1;)a=e;d.push(a)}var k=/(?:^|\s)nocode(?:\s|$)/,t=/\r\n?|\n/,s=a.ownerDocument,l;a.currentStyle?l=a.currentStyle.whiteSpace:window.getComputedStyle&&(l=s.defaultView.getComputedStyle(a,q).getPropertyValue("white-space"));var p=l&&"pre"===l.substring(0,3);for(l=s.createElement("LI");a.firstChild;)l.appendChild(a.firstChild);for(var d=[l],g=0;g=0;){var h=m[e];A.hasOwnProperty(h)?window.console&&console.warn("cannot override language handler %s",h):A[h]=a}}function C(a,m){if(!a||!A.hasOwnProperty(a))a=/^\s*=o&&(h+=2);e>=c&&(a+=2)}}catch(w){"console"in window&&console.log(w&&w.stack?w.stack:w)}}var v=["break,continue,do,else,for,if,return,while"],w=[[v,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"], +"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"],F=[w,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"],G=[w,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"], +H=[G,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"],w=[w,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"],I=[v,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"], +J=[v,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"],v=[v,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"],K=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/,N=/\S/,O=u({keywords:[F,H,w,"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END"+ +I,J,v],hashComments:!0,cStyleComments:!0,multiLineStrings:!0,regexLiterals:!0}),A={};k(O,["default-code"]);k(x([],[["pln",/^[^]*(?:>|$)/],["com",/^<\!--[\S\s]*?(?:--\>|$)/],["lang-",/^<\?([\S\s]+?)(?:\?>|$)/],["lang-",/^<%([\S\s]+?)(?:%>|$)/],["pun",/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\S\s]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\S\s]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\S\s]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]), +["default-markup","htm","html","mxml","xhtml","xml","xsl"]);k(x([["pln",/^\s+/,q," \t\r\n"],["atv",/^(?:"[^"]*"?|'[^']*'?)/,q,"\"'"]],[["tag",/^^<\/?[a-z](?:[\w-.:]*\w)?|\/?>$/i],["atn",/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^\s"'>]*(?:[^\s"'/>]|\/(?=\s)))/],["pun",/^[/<->]+/],["lang-js",/^on\w+\s*=\s*"([^"]+)"/i],["lang-js",/^on\w+\s*=\s*'([^']+)'/i],["lang-js",/^on\w+\s*=\s*([^\s"'>]+)/i],["lang-css",/^style\s*=\s*"([^"]+)"/i],["lang-css",/^style\s*=\s*'([^']+)'/i],["lang-css", +/^style\s*=\s*([^\s"'>]+)/i]]),["in.tag"]);k(x([],[["atv",/^[\S\s]+/]]),["uq.val"]);k(u({keywords:F,hashComments:!0,cStyleComments:!0,types:K}),["c","cc","cpp","cxx","cyc","m"]);k(u({keywords:"null,true,false"}),["json"]);k(u({keywords:H,hashComments:!0,cStyleComments:!0,verbatimStrings:!0,types:K}),["cs"]);k(u({keywords:G,cStyleComments:!0}),["java"]);k(u({keywords:v,hashComments:!0,multiLineStrings:!0}),["bsh","csh","sh"]);k(u({keywords:I,hashComments:!0,multiLineStrings:!0,tripleQuotedStrings:!0}), +["cv","py"]);k(u({keywords:"caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END",hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["perl","pl","pm"]);k(u({keywords:J,hashComments:!0,multiLineStrings:!0,regexLiterals:!0}),["rb"]);k(u({keywords:w,cStyleComments:!0,regexLiterals:!0}),["js"]);k(u({keywords:"all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes", +hashComments:3,cStyleComments:!0,multilineStrings:!0,tripleQuotedStrings:!0,regexLiterals:!0}),["coffee"]);k(x([],[["str",/^[\S\s]+/]]),["regex"]);window.prettyPrintOne=function(a,m,e){var h=document.createElement("PRE");h.innerHTML=a;e&&D(h,e);E({g:m,i:e,h:h});return h.innerHTML};window.prettyPrint=function(a){function m(){for(var e=window.PR_SHOULD_USE_CONTINUATION?l.now()+250:Infinity;p=0){var k=k.match(g),f,b;if(b= +!k){b=n;for(var o=void 0,c=b.firstChild;c;c=c.nextSibling)var i=c.nodeType,o=i===1?o?b:c:i===3?N.test(c.nodeValue)?b:o:o;b=(f=o===b?void 0:o)&&"CODE"===f.tagName}b&&(k=f.className.match(g));k&&(k=k[1]);b=!1;for(o=n.parentNode;o;o=o.parentNode)if((o.tagName==="pre"||o.tagName==="code"||o.tagName==="xmp")&&o.className&&o.className.indexOf("prettyprint")>=0){b=!0;break}b||((b=(b=n.className.match(/\blinenums\b(?::(\d+))?/))?b[1]&&b[1].length?+b[1]:!0:!1)&&D(n,b),d={g:k,h:n,i:b},E(d))}}p p:first-child +{ + margin-top: 0; + padding-top: 0; +} + +.params td.description > p:last-child +{ + margin-bottom: 0; + padding-bottom: 0; +} + +.disabled { + color: #454545; +} diff --git a/docs/styles/prettify-jsdoc.css b/docs/styles/prettify-jsdoc.css new file mode 100644 index 0000000..5a2526e --- /dev/null +++ b/docs/styles/prettify-jsdoc.css @@ -0,0 +1,111 @@ +/* JSDoc prettify.js theme */ + +/* plain text */ +.pln { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* string content */ +.str { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a keyword */ +.kwd { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a comment */ +.com { + font-weight: normal; + font-style: italic; +} + +/* a type name */ +.typ { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a literal value */ +.lit { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* punctuation */ +.pun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp open bracket */ +.opn { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* lisp close bracket */ +.clo { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a markup tag name */ +.tag { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute name */ +.atn { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a markup attribute value */ +.atv { + color: #006400; + font-weight: normal; + font-style: normal; +} + +/* a declaration */ +.dec { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* a variable name */ +.var { + color: #000000; + font-weight: normal; + font-style: normal; +} + +/* a function name */ +.fun { + color: #000000; + font-weight: bold; + font-style: normal; +} + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; +} diff --git a/docs/styles/prettify-tomorrow.css b/docs/styles/prettify-tomorrow.css new file mode 100644 index 0000000..aa2908c --- /dev/null +++ b/docs/styles/prettify-tomorrow.css @@ -0,0 +1,132 @@ +/* Tomorrow Theme */ +/* Original theme - https://github.com/chriskempson/tomorrow-theme */ +/* Pretty printing styles. Used with prettify.js. */ +/* SPAN elements with the classes below are added by prettyprint. */ +/* plain text */ +.pln { + color: #4d4d4c; } + +@media screen { + /* string content */ + .str { + color: #718c00; } + + /* a keyword */ + .kwd { + color: #8959a8; } + + /* a comment */ + .com { + color: #8e908c; } + + /* a type name */ + .typ { + color: #4271ae; } + + /* a literal value */ + .lit { + color: #f5871f; } + + /* punctuation */ + .pun { + color: #4d4d4c; } + + /* lisp open bracket */ + .opn { + color: #4d4d4c; } + + /* lisp close bracket */ + .clo { + color: #4d4d4c; } + + /* a markup tag name */ + .tag { + color: #c82829; } + + /* a markup attribute name */ + .atn { + color: #f5871f; } + + /* a markup attribute value */ + .atv { + color: #3e999f; } + + /* a declaration */ + .dec { + color: #f5871f; } + + /* a variable name */ + .var { + color: #c82829; } + + /* a function name */ + .fun { + color: #4271ae; } } +/* Use higher contrast and text-weight for printable form. */ +@media print, projection { + .str { + color: #060; } + + .kwd { + color: #006; + font-weight: bold; } + + .com { + color: #600; + font-style: italic; } + + .typ { + color: #404; + font-weight: bold; } + + .lit { + color: #044; } + + .pun, .opn, .clo { + color: #440; } + + .tag { + color: #006; + font-weight: bold; } + + .atn { + color: #404; } + + .atv { + color: #060; } } +/* Style */ +/* +pre.prettyprint { + background: white; + font-family: Menlo, Monaco, Consolas, monospace; + font-size: 12px; + line-height: 1.5; + border: 1px solid #ccc; + padding: 10px; } +*/ + +/* Specify class=linenums on a pre to get line numbering */ +ol.linenums { + margin-top: 0; + margin-bottom: 0; } + +/* IE indents via margin-left */ +li.L0, +li.L1, +li.L2, +li.L3, +li.L4, +li.L5, +li.L6, +li.L7, +li.L8, +li.L9 { + /* */ } + +/* Alternate shading for lines */ +li.L1, +li.L3, +li.L5, +li.L7, +li.L9 { + /* */ } diff --git a/examples/EventBasics.js b/examples/EventBasics.js index e78ae0e..37aaabf 100644 --- a/examples/EventBasics.js +++ b/examples/EventBasics.js @@ -16,6 +16,13 @@ session1.unwatch("testevent",d.print); session1.execute("drop event testevent",d.printMsg("S1:drop event")); session1.close(d.print); +/** + * Description + * @method watchCallback + * @param {} name + * @param {} msg + * @return + */ function watchCallback(name,msg){ console.log("watch update-----> ",msg) }; \ No newline at end of file diff --git a/examples/EventExample.js b/examples/EventExample.js index 4c6add3..093f6d7 100644 --- a/examples/EventExample.js +++ b/examples/EventExample.js @@ -14,6 +14,13 @@ basex.debug_mode = false; session1.execute("create event test_evt", afterCreate); // watch for it in second session +/** + * Description + * @method afterCreate + * @param {} err + * @param {} reply + * @return + */ function afterCreate(err, reply) { console.log("running afterCreate..."); if (err) @@ -22,6 +29,13 @@ function afterCreate(err, reply) { }; // run long query in sessions, and fire event from session1 +/** + * Description + * @method afterWatch + * @param {} err + * @param {} reply + * @return + */ function afterWatch(err, reply) { console.log("running afterWatch..."); if (err) @@ -34,11 +48,25 @@ function afterWatch(err, reply) { }; // on event received show and unwatch +/** + * Description + * @method watchCallback + * @param {} name + * @param {} msg + * @return + */ function watchCallback(name, msg) { console.log("watch event received-----> ", msg) session2.unwatch("test_evt", teardown); }; // close all +/** + * Description + * @method teardown + * @param {} err + * @param {} reply + * @return + */ function teardown(err, reply) { console.log("unwatch:", err, reply) session1.execute("drop event test_evt", d.printMsg("S1:drop event")); diff --git a/examples/EventSync.js b/examples/EventSync.js index d2ebb14..fcaf51a 100644 --- a/examples/EventSync.js +++ b/examples/EventSync.js @@ -20,6 +20,13 @@ function() { session1.execute("XQUERY db:event('messenger', 99)", this); }); +/** + * Description + * @method watchCallback + * @param {} name + * @param {} msg + * @return + */ function watchCallback(name, msg) { console.log("watch update-----> ", msg) step( diff --git a/examples/Example.js b/examples/Example.js index 2d110a6..f3a9fb3 100644 --- a/examples/Example.js +++ b/examples/Example.js @@ -5,6 +5,13 @@ var basex = require("../index"); var client = new basex.Session("127.0.0.1", 1984, "admin", "admin"); basex.debug_mode = false; +/** + * Description + * @method print + * @param {} err + * @param {} reply + * @return + */ function print(err, reply) { if (err) { console.log("Error: " + err); diff --git a/examples/Httpserver.js b/examples/Httpserver.js index 6792eab..eec232a 100644 --- a/examples/Httpserver.js +++ b/examples/Httpserver.js @@ -3,6 +3,13 @@ var basex = require("../index"); var http = require('http'); +/** + * Description + * @method list + * @param {} req + * @param {} res + * @return + */ function list(req, res) { res.writeHead(200, {"Content-Type": "text/html"}); session.execute("info", function(err, r) { diff --git a/examples/RawExample.js b/examples/RawExample.js index a84f669..358d94b 100644 --- a/examples/RawExample.js +++ b/examples/RawExample.js @@ -5,6 +5,13 @@ var basex = require("../index"); var client = new basex.Session(); basex.debug_mode = true; +/** + * Description + * @method print + * @param {} err + * @param {} reply + * @return + */ function print(err, reply) { if (err) { console.log("Error: " + err); diff --git a/examples/TableExample.js b/examples/TableExample.js index e7ab276..9398d8e 100644 --- a/examples/TableExample.js +++ b/examples/TableExample.js @@ -10,9 +10,22 @@ basex.debug_mode = false; // commands to be performed var xq=["factbook_countries.xq","xslt_countries.xq"]; +/** + * Description + * @method loadfile + * @param {} src + * @return CallExpression + */ function loadfile(src){ return fs.readFileSync(__dirname+"/"+src,'utf-8') }; +/** + * Description + * @method list + * @param {} req + * @param {} res + * @return + */ function list(req, res) { res.writeHead(200, {"Content-Type": "text/html"}); var query1 = session.query(cmd); diff --git a/examples/issue14.js b/examples/issue14.js new file mode 100644 index 0000000..2ee4785 --- /dev/null +++ b/examples/issue14.js @@ -0,0 +1,21 @@ +/* + * This example shows an invalid document being added. + * + */ +var basex = require("../index"); +var log = require("../debug"); +// create session +var client = new basex.Session("localhost", 1984, "admin", "admin"); +basex.debug_mode = true; +// create new database +client.execute("create db database", log.print); + +// add document +client.add("/world/World.xml", "<\"x\">Hello World!", log.print); + + +// drop database +client.execute("drop db database", log.print); + +// close session +client.close(); diff --git a/examples/json.js b/examples/json.js index dc4a74a..aa3b0df 100644 --- a/examples/json.js +++ b/examples/json.js @@ -3,6 +3,13 @@ */ var basex = require("../index"); var client = new basex.Session(); +/** + * Description + * @method print + * @param {} err + * @param {} reply + * @return + */ function print(err, reply) { if (err) { console.log("Error: " + err); diff --git a/examples/json2.js b/examples/json2.js index 8111185..5863a14 100644 --- a/examples/json2.js +++ b/examples/json2.js @@ -3,6 +3,13 @@ */ var basex = require("../index"); var client = new basex.Session(); +/** + * Description + * @method print + * @param {} err + * @param {} reply + * @return + */ function print(err, reply) { if (err) { console.log("Error: " + err); diff --git a/examples/manysessions.js b/examples/manysessions.js index 66a4fa5..daca673 100644 --- a/examples/manysessions.js +++ b/examples/manysessions.js @@ -6,6 +6,12 @@ basex.debug_mode = false; var sCount=200; // max 200 var sessions=[]; //show supplied msg then basex server response +/** + * Description + * @method track + * @param {} msg + * @return FunctionExpression + */ function track(msg) { return function(err, reply){ sCount--; diff --git a/examples/raw.js b/examples/raw.js index 163a996..874c590 100644 --- a/examples/raw.js +++ b/examples/raw.js @@ -3,6 +3,13 @@ */ var basex = require("../index"); var client = new basex.Session(); +/** + * Description + * @method print + * @param {} err + * @param {} reply + * @return + */ function print(err, reply) { if (err) { console.log("Error: " + err); diff --git a/index.js b/index.js index e19e194..e4a4bc9 100644 --- a/index.js +++ b/index.js @@ -1,346 +1,490 @@ /* BaseX Node.js client * http://docs.basex.org/wiki/Server_Protocol - * andy bunce 2011-2012 + * @author andy bunce + * @date 2011-2014 */ // set this to true to enable console.log msgs for all connections -exports.debug_mode =false; +exports.debug_mode = false; -var net = require("net"), -util = require("util"), -events = require("events"), -crypto = require("crypto"), -Query = require("./lib/query").Query, -Watch = require("./lib/watch").Watch, -parser2 = require("./lib/parser2"), -Queue = require("./lib/queue").Queue; +var net = require("net"), + util = require("util"), + events = require("events"), + crypto = require("crypto"), + Query = require("./lib/query").Query, + Watch = require("./lib/watch").Watch, + parser2 = require("./lib/parser2"), + Queue = require("./lib/queue").Queue; var states = { - DISCONNECTED : 0, - CONNECTING : 1, - AUTHORIZE : 2, - CONNECTED : 3, - CLOSING : 4 + DISCONNECTED: 0, + CONNECTING: 1, + AUTHORIZE: 2, + CONNECTED: 3, + CLOSING: 4 }; var tagid = 0; // used to give each Session a unique .tag property +/** + * Create a session connection + * @constructor Session + * @param {} host + * @param {} port + * @param {} username + * @param {} password + * @return + */ var Session = function(host, port, username, password) { - var self = this; - this.host = host || "127.0.0.1"; - this.port = port || 1984; - this.username = username || "admin"; - this.password = password || "admin"; - this.tag = "S" + (++tagid); - this.commands_sent = 0; - this.parser2part=null; // parser2 flag - // reset - this.reset = function() { - this.state = states.DISCONNECTED; - this.current_command = null; // waiting for response to this - this.closefn = null; - this.blocked = false; // can't send until reply - this.buffer = ""; - this.q_pending = new Queue(); // holds commands to send - this.q_sent = new Queue(); // holds commands sent - // event stuff - this.event = null; - // initial parser for auth - this.parser = function() { - return self.bxp.need(["data"],false) //timestamp - }; - }; - this.reset(); - - var stream = net.createConnection(this.port, this.host); - //stream.setEncoding('utf-8'); - this.bxp=new parser2.parse(stream); - //this.bxp.on("data",function(d){console.log("ping",d.toString())}); - this.stream = stream; - - this.stream.on("connect", function() { - self.state = states.CONNECTING; - if (exports.debug_mode) { - console.log(self.tag + ": stream connected"); - } - }); - - this.stream.on("data", function(reply) { - if (exports.debug_mode) { - console.log(self.tag + "<<"); - console.dir(reply.toString()); - } + var self = this; + this.host = host || "127.0.0.1"; + this.port = port || 1984; + this.username = username || "admin"; + this.password = password || "admin"; + this.tag = "S" + (++tagid); + this.commands_sent = 0; + /** + * reset state + * @method reset + * @return + */ + this.reset = function() { + this.state = states.DISCONNECTED; + this.current_command = null; // waiting for response to this + this.closefn = null; + this.blocked = false; // can't send until reply + this.buffer = ""; + this.q_pending = new Queue(); // holds commands to send + this.q_sent = new Queue(); // holds commands sent + // event stuff + this.event = null; + // initial parser for auth + /** + * Description + * @method parser + * @return CallExpression + */ + this.parser = function() { + return self.bxp.need(["data"], false) //timestamp + }; + }; + this.reset(); + + var stream = net.createConnection(this.port, this.host); + //stream.setEncoding('utf-8'); + this.bxp = new parser2.parse(stream); + //this.bxp.on("data",function(d){console.log("ping",d.toString())}); + this.stream = stream; + + this.stream.on("connect", function() { + self.state = states.CONNECTING; + if (exports.debug_mode) { + console.log(self.tag + ": stream connected"); + } + }); + + this.stream.on("data", function(reply) { + if (exports.debug_mode) { + console.log(self.tag + "<<"); + console.dir(reply.toString()); + } if (self.state == states.CONNECTED) { - self.onData() - }else if (self.state == states.CONNECTING) { - var read=self.parser(); - if(read){ - self.send(self.username+"\0"); - var s = md5(md5(self.password) + read.data); - self.send(s+"\0"); - self.state = states.AUTHORIZE; - } - } else if (self.state == states.AUTHORIZE) { - var read=self.bxp.popByte() - if(read){ - if(read.data!="\0"){ - //console.log("data",self.bxp.data,"buff: ",self.bxp.buffer) - throw "Access denied."; - } - self.state = states.CONNECTED; - if (exports.debug_mode) { - console.log(self.tag + ": authorized"); - } - self.emit("connect", 1); - self.sendQueueItem(); - } - } else { - throw "Bad state."; - } - }); - - // respond to data arrival - this.onData=function(){ - // console.log("onData"); - var r,cc=self.current_command; - while (cc && (r = cc.parser())) { - if (exports.debug_mode) { - console.log("response: ", r); - } - if (cc.callback) { - cc.callback(r.ok ? null : r.info, r); - } - - cc=self.current_command = self.q_sent.shift(); - //console.log("next is:"); - //console.dir(self.current_command); - } + self.onData() + } else if (self.state == states.CONNECTING) { + var read = self.parser(); + if (read) { + self.send(self.username + "\0"); + var s = md5(md5(self.password) + read.data); + self.send(s + "\0"); + self.state = states.AUTHORIZE; + } + } else if (self.state == states.AUTHORIZE) { + var read = self.bxp.popByte() + if (read) { + if (read.data != "\0") { + //console.log("data",self.bxp.data,"buff: ",self.bxp.buffer) + throw "Access denied."; + } + self.state = states.CONNECTED; + if (exports.debug_mode) { + console.log(self.tag + ": authorized"); + } + self.emit("connect", 1); + self.sendQueueItem(); + } + } else { + throw "Bad state."; + } + }); + + /** + * respond to data arrival + * @method onData + * @return + */ + this.onData = function() { + // console.log("onData"); + var r, cc = self.current_command; + while (cc && (r = cc.parser())) { + if (exports.debug_mode) { + console.log("response: ", r); + } + if (cc.callback) { + cc.callback(r.ok ? null : r.info, r); + } + + cc = self.current_command = self.q_sent.shift(); + //console.log("next is:"); + //console.dir(self.current_command); + } + }; + + this.stream.on("error", socketError); + + this.stream.on("close", function() { + if (exports.debug_mode) { + console.log(self.tag + ": stream closed"); + } + if (self.event) self.event.close() + + if (self.closefn) { + self.closefn(); + self.closefn = null; + }; + }); + + this.stream.on("end", function() { + // console.log(self.tag+": stream end"); + }); + + this.stream.on("drain", function() { + // console.log("drain"); + }); + + /** + * send to server + * @method send + * @param {} s + * @return + */ + this.send = function(s) { + if (typeof s === "function") + s = s(); + + if (exports.debug_mode) { + console.log(self.tag + ">>"); + console.dir(s); + }; + self.stream.write(s); + self.commands_sent += 1; + }; + + + + /** + * standard parser read 2 lines and byte + * @method parser1 + * @return {ok:bool,result:?,info:?} or null if not in buffer + */ + this.parser1 = function() { + return self.bxp.need(["result", "info"], true) + }; + + /** + * read status byte + * @method parserOk + * @return CallExpression + */ + this.parserOk = function() { + return self.bxp.popByte() + }; + + + /** + * read line and byte, if error move result to info + * @method parser2 + * @return {ok:bool,result:?,info:?} or null if not in buffer + */ + this.parser2 = function() { + var r = self.bxp.need(["result"], true) + if (!r) return + if (r.ok) { + return r + } else { + return { + ok: false, + info: r.result + } + } + }; + + /** + * add command and returns the result: + * @method execute + * @param {} cmd + * @param {} callback + * @return + */ + this.execute = function(cmd, callback) { + self.send_command({ + send: cmd + "\0", + parser: self.parser1, + callback: callback + }); + }; + + /** + * Returns a query object for the specified query: + * @method query + * @param {} query + * @return NewExpression + */ + this.query = function(query) { + return new Query(self, query); + }; + + /** + * Creates a database from an input stream: + * @method create + * @param {} name + * @param {} input + * @param {} callback + * @return + */ + this.create = function(name, input, callback) { + self._dbop("\x08", name, input, callback) + }; + + /** + * Adds a document to the current database from an input stream: + * @method add + * @param {} path + * @param {} input + * @param {} callback + * @return + */ + this.add = function(path, input, callback) { + self._dbop("\x09", path, input, callback) + }; + + /** + * Replaces a document with the specified input stream: + * @method replace + * @param {} path + * @param {} input + * @param {} callback + * @return + */ + this.replace = function(path, input, callback) { + self._dbop("\x0C", path, input, callback) + }; + + /** + * Stores raw data at the specified path: + * @method store + * @param {} path + * @param {} input + * @param {} callback + * @return + */ + this.store = function(path, input, callback) { + self._dbop("\x0D", path, input, callback) + }; + //@TODO allow input to be stream, currently just string + this._dbop = function(op, path, input, callback) { + self.send_command({ + send: op + path + "\0" + input + "\0", + parser: self.parser2, + callback: callback + }); + }; + + /** + * Subscribe to a named event + * @method watch + * @param {} name + * @param {} notification function to call on event + * @param {} callback for this command + * @return + */ + this.watch = function(name, notification, callback) { + if (self.event === null) { //1st time + self.send_command({ + send: "\x0A", + parser: self.parsewatch, + /** + * Description + * @method callback + * @return + */ + callback: function() { + + self.event.add(name, notification) + // add at front + self.send_command({ + send: name + "\0", + parser: self.parsewatch2, + callback: callback + }); + self.setBlock(false); + }, + blocking: true + }) + } else { + self.event.add(name, notification) + self.send_command({ + send: "\x0A" + name + "\0", + parser: self.parsewatch2, + callback: callback + }); + } + + }; + + /** + * parse 1st watch response, expect port,id + * @method parsewatch + * @return + */ + this.parsewatch = function() { + var flds = self.bxp.need(["eport", "id"], false) + if (flds) { + if (self.event === null) { + self.event = new Watch(self.host, flds.eport, flds.id) + self.event.on("connect", self.onData) //need to wait + } + flds.ok = true // expected by reader + return flds; + } }; - - this.stream.on("error",socketError); - - this.stream.on("close", function() { - if (exports.debug_mode) { - console.log(self.tag + ": stream closed"); - } - if (self.event) self.event.close() - - if (self.closefn) { - self.closefn(); - self.closefn = null; - } - ; - }); - - this.stream.on("end", function() { - // console.log(self.tag+": stream end"); - }); - - this.stream.on("drain", function() { - // console.log("drain"); - }); - - this.send = function(s) { - if (typeof s === "function") - s = s(); - - if (exports.debug_mode) { - console.log(self.tag + ">>"); - console.dir(s ); - } - ; - self.stream.write(s ); - self.commands_sent += 1; - }; - - - // standard parser read 2 lines and byte - this.parser1 = function() { - return self.bxp.need([ "result", "info" ],true) - }; - // read status byte - this.parserOk = function() { - return self.bxp.popByte() - }; - - // read line and byte possible error info - this.parser2 = function() { - if(!self.parser2part){ - var r=self.bxp.need(["result"],true) - if(!r)return - if(r.ok){ - return {ok:true,result:r.result} - }else{ - self.parser2part=r; - } - }else{ - var r=self.bxp.need(["info"],false) - if(!r)return - var res={ok:false, - info:r.info, - result:self.parser2part.result} - self.parser2part=null; - return res - } - }; - - - // add command and returns the result: - this.execute = function(cmd, callback) { - self.send_command({ - send : cmd+"\0", - parser : self.parser1, - callback : callback - }); - }; - // Returns a query object for the specified query: - this.query = function(query) { - return new Query(self, query); - }; - // Creates a database from an input stream: - this.create = function(name, input, callback) { - self._dbop("\x08" ,name , input,callback) - }; - // Adds a document to the current database from an input stream: - this.add = function(path, input, callback) { - self._dbop("\x09" ,path , input,callback) - }; - // Replaces a document with the specified input stream: - this.replace = function(path, input, callback) { - self._dbop("\x0C" ,path , input,callback) - }; - // Stores raw data at the specified path: - this.store = function(path, input, callback) { - self._dbop("\x0D" ,path , input,callback) - }; - //@TODO allow input to be stream, currently just string - this._dbop=function(op,path, input, callback) { - self.send_command({ - send : op + path + "\0" + input+"\0", - parser : self.parser2, - callback : callback - }); - }; - // watch - this.watch = function(name, notification, callback) { - if (self.event == null) { //1st time - self.send_command({ - send : "\x0A", - parser : self.parsewatch, - callback :function(){ - - self.event.add(name, notification) - // add at front - self.send_command({ - send : name+"\0", - parser : self.parsewatch2, - callback : callback - }); - self.setBlock(false); - }, - blocking:true - }) - } else { - self.event.add(name, notification) - self.send_command({ - send : "\x0A" +name+"\0", - parser : self.parsewatch2, - callback : callback - }); - } - - }; - - // parse 1st watch response, expect port,id - this.parsewatch = function() { - var flds=self.bxp.need([ "eport", "id" ],false) - if (flds) { - if (self.event == null) { - self.event = new Watch(self.host, flds.eport,flds.id) - self.event.on("connect",self.onData) //need to wait - } - flds.ok=true // expected by reader - return flds; - } - }; - - // parse other watch response - this.parsewatch2 = function() { - // wait info and connected - //console.log("qqqq",self.event.isConnected) - //console.log(".....parsewatch2",self.buffer) - if (self.event.isConnected) return self.bxp.need([ "info" ],true) - }; - - // unwatch - this.unwatch = function(name, callback) { - self.send_command({ - send : "\x0B" + name+"\0", - parser : self.parser2, - callback : function(err,reply){ - self.event.remove(name); - callback(err,reply); - } - }); - }; - // end the session, sash callback, for stream end - this.close = function(callback) { - self.closefn = callback; - self.send_command({ - send : "exit"+"\0", - parser : self.parserOk - // sometime get this, timing - }); - }; - // -------end of commands --------- - - // - this.send_command = function(cmd) { - self.q_pending.push(cmd); - self.sendQueueItem(); - }; - // do the next queued command, if any, return true if sent - this.sendQueueItem = function() { - // console.log("queues waiting send: ",self.q_pending.length,", waiting - // reply:",self.q_sent.length) - - if (self.q_pending.length == 0 || self.blocked - || self.state != states.CONNECTED) - return false - - var cmd = self.q_pending.shift(); - if (!self.current_command) { - self.current_command = cmd; - // console.log("current command: ",cmd.send) - - } else { - self.q_sent.push(cmd); - }; - - self.send(cmd.send); - self.setBlock(cmd.blocking ? true : false); - return true; - }; - - this.setBlock = function(state) { - //console.log("blocked:",state) - self.blocked = state; - while (self.sendQueueItem()) { - } - ; - }; - events.EventEmitter.call(this); + + + /** + * parse other watch response + * @method parsewatch2 + * @return + */ + this.parsewatch2 = function() { + // wait info and connected + //console.log("qqqq",self.event.isConnected) + //console.log(".....parsewatch2",self.buffer) + if (self.event.isConnected) return self.bxp.need(["info"], true) + }; + + /** + * unsubscribe to event + * @method unwatch + * @param {} name + * @param {} callback + * @return + */ + this.unwatch = function(name, callback) { + self.send_command({ + send: "\x0B" + name + "\0", + parser: self.parser2, + /** + * Description + * @method callback + * @param {} err + * @param {} reply + * @return + */ + callback: function(err, reply) { + self.event.remove(name); + callback(err, reply); + } + }); + }; + + /** + * end the session, sash callback, for stream end + * @method close + * @param {} callback + * @return + */ + this.close = function(callback) { + self.closefn = callback; + self.send_command({ + send: "exit" + "\0", + parser: self.parserOk + // sometime get this, timing + }); + }; + // -------end of commands --------- + + // + /** + * queue a command to server + * @method send_command + * @param {} cmd + * @return + */ + this.send_command = function(cmd) { + self.q_pending.push(cmd); + self.sendQueueItem(); + }; + + /** + * do the next queued command, if any + * @method sendQueueItem + * @return boolean true if command was sent + */ + this.sendQueueItem = function() { + // console.log("queues waiting send: ",self.q_pending.length,", waiting + // reply:",self.q_sent.length) + + if (self.q_pending.length === 0 || self.blocked || self.state != states.CONNECTED) + return false + + var cmd = self.q_pending.shift(); + if (!self.current_command) { + self.current_command = cmd; + // console.log("current command: ",cmd.send) + + } else { + self.q_sent.push(cmd); + }; + + self.send(cmd.send); + self.setBlock(cmd.blocking ? true : false); + return true; + }; + + /** + * set or clear flag allowing sending to server + * @method setBlock + * @param {} state + * @return + */ + this.setBlock = function(state) { + //console.log("blocked:",state) + self.blocked = state; + while (self.sendQueueItem()) {}; + }; + events.EventEmitter.call(this); }; +/** + * Description + * @method socketError + * @param {} e + * @return + */ function socketError(e) { - if (e.code == 'ECONNREFUSED') { - console.log('ECONNREFUSED: connection refused. Check BaseX server is running.'); - } else { - console.log(e); - } + if (e.code == 'ECONNREFUSED') { + console.log('ECONNREFUSED: connection refused. Check BaseX server is running.'); + } else { + console.log(e); + } } +/** + * hash str using md5 + * @method md5 + * @param {} str + * @return md5 value + */ function md5(str) { - return crypto.createHash('md5').update(str).digest("hex"); + return crypto.createHash('md5').update(str).digest("hex"); }; util.inherits(Session, events.EventEmitter); diff --git a/lib/basexstream.js b/lib/basexstream.js index 40b9f3c..551d186 100644 --- a/lib/basexstream.js +++ b/lib/basexstream.js @@ -1,12 +1,16 @@ /* * Send and receive streams for BaseX. They expect and emit Buffers - * SendStream: write 0xFF before 00 or FF - * ReceiveStream: read next byte if 0xFF is received - * andy bunce 2011-2012 + * + * andy bunce 2011-2014 * see http://loose-bits.com/2012/08/02/nodejs-read-write-streams-pipes.html */ -// ----------SendStream------------------------- + +/** + * SendStream: write 0xFF before 00 or FF + * @constructor SendStream + * @return + */ var SendStream = function() { this.readable = true; this.writable = true; @@ -32,6 +36,8 @@ SendStream.prototype._transform = function(data) { /** * Stream write (override). + * @method write + * @return */ SendStream.prototype.write = function() { this._transform.apply(this, arguments); @@ -39,6 +45,8 @@ SendStream.prototype.write = function() { /** * Stream end (override). + * @method end + * @return */ SendStream.prototype.end = function() { this._transform.apply(this, arguments); @@ -46,7 +54,11 @@ SendStream.prototype.end = function() { }; -// ----------ReceiveStream------------------------- +/** + * ReceiveStream: read next byte if 0xFF is received + * @constructor ReceiveStream + * @return + */ var ReceiveStream = function() { this.readable = true; this.writable = true; @@ -78,6 +90,8 @@ ReceiveStream.prototype._transform = function(data) { /** * Stream write (override). + * @method write + * @return */ ReceiveStream.prototype.write = function() { this._transform.apply(this, arguments); @@ -85,13 +99,20 @@ ReceiveStream.prototype.write = function() { /** * Stream end (override). + * @method end + * @return */ ReceiveStream.prototype.end = function() { this._transform.apply(this, arguments); this.emit("end"); }; -//----------NopStream------------------------------- + +/** + * pass thru, does nothing + * @constructor NopStream + * @return + */ var NopStream = function () { this.readable = true; this.writable = true; @@ -100,12 +121,22 @@ var NopStream = function () { require('util').inherits(NopStream, require('stream')); // Extract args to `write` and emit as `data` event. +/** + * Description + * @method write + * @return + */ NopStream.prototype.write = function () { var args = Array.prototype.slice.call(arguments, 0); this.emit.apply(this, ['data'].concat(args)) }; // Extract args to `end` and emit as `end` event. +/** + * Description + * @method end + * @return + */ NopStream.prototype.end = function () { var args = Array.prototype.slice.call(arguments, 0); this.emit.apply(this, ['end'].concat(args)) diff --git a/lib/parser2.js b/lib/parser2.js index 8306a3c..77de92f 100644 --- a/lib/parser2.js +++ b/lib/parser2.js @@ -10,6 +10,12 @@ util = require("util"), events = require("events"); // @param stream socket stream +/** + * reads from i/p stream from basex server and generates events from the protocol + * @method parse + * @param {} stream + * @return + */ var parse = function(stream) { var self=this; var r=new BaseXStream.ReceiveStream(); @@ -29,9 +35,13 @@ var parse = function(stream) { this.data=[]; this.popStatus=false; this.buffer=""; - // take fields named in array flds delimited by \0 from buffer - // @param popStatus boolean if true read 1byte status 0/1 into ok: - // @return object or empty if message not fully present in buffer. + /** + * take fields named in array flds delimited by \0 from buffer + * @method need + * @param {} flds + * @param {} popStatus boolean if true read 1byte status 0/1 into ok: + * @return object or empty if message not fully present in buffer. + */ this.need=function(flds,popStatus){ if(self.data.lengthHello World!", function(e, + r) { + reply = r; + err = e; + done(); + }); + }); + + it('It should error', function() { + should.exist(err); + }); +}); + describe('drop db database', function() { var reply, err; before(function(done) { @@ -156,30 +172,32 @@ describe('Send a xquery and iterate over the result items', function() { }); }); -describe('create query and bind ', function() { - var reply, err; - before(function(done) { - var input = "declare variable $name external; for $i in 1 to 10 return element { $name } { $i }"; - var query = session.query(input); - - // bind variable - query.bind("name", "nodex"); - - // print results - query.execute(function(e, r) { - reply = r; - err = e; - done(); +describe( + 'create query and bind ', + function() { + var reply, err; + before(function(done) { + var input = "declare variable $name external; for $i in 1 to 10 return element { $name } { $i }"; + var query = session.query(input); + + // bind variable + query.bind("name", "nodex"); + + // print results + query.execute(function(e, r) { + reply = r; + err = e; + done(); + }); + }); + + it('It should not error', function() { + should.not.exist(err); + }); + it('It should return a string', function() { + reply.result.should.be.a.String + }); }); - }); - - it('It should not error', function() { - should.not.exist(err); - }); - it('It should return a string', function() { - reply.result.should.be.a.String - }); -}); //----------------------------- beforeEach(function() { diff --git a/test/test-stress.js b/test/test-stress.js index ab52931..9faa5be 100644 --- a/test/test-stress.js +++ b/test/test-stress.js @@ -1,4 +1,4 @@ -/* basex-node test interface using vows large data volumes +/* basex-node test interface using mocha, with large data volumes * */ @@ -7,11 +7,11 @@ var should = require("should"); var session = new basex.Session(); -describe('Send a xquery and iterate over the 10000 result items ', function() { +describe('Send a xquery and iterate over the 100000 result items ', function() { var reply, err; before(function(done) { // create query instance - var input = 'for $i in 1 to 10000 return Text { $i }'; + var input = 'for $i in 1 to 100000 return Text { $i }'; var query = session.query(input); query.results( function(e, r) { @@ -46,8 +46,6 @@ describe('return megabyte result from execute ', function() { describe('return megabyte result from query ', function() { var reply, err; before(function(done) { - // create query instance - var input = 'xquery (1 to 100000)!"abcdefghij"'; // create query instance var input = '(1 to 100000)!"abcdefghij"'; var query = session.query(input);