Permalink
Browse files

first commit

  • Loading branch information...
0 parents commit df2b9a809f0f9cd5e2c7f7d59157a74f8e822abf @teknopaul committed Aug 4, 2011
Showing with 13,047 additions and 0 deletions.
  1. +17 −0 .project
  2. +7 −0 .settings/.jsdtscope
  3. +1 −0 .settings/org.eclipse.wst.jsdt.ui.superType.container
  4. +1 −0 .settings/org.eclipse.wst.jsdt.ui.superType.name
  5. +27 −0 app/404.html
  6. +89 −0 app/doc.html
  7. BIN app/favicon.png
  8. +8 −0 app/inc.html
  9. +6,240 −0 app/js/jquery-1.4.2.js
  10. +82 −0 app/js/jquery.closure.js
  11. +89 −0 app/js/jquery.htmlbuffer.js
  12. +3,510 −0 app/js/jquery.jstree.js
  13. +286 −0 app/js/nodetoy-test.js
  14. +189 −0 app/js/popups.js
  15. BIN app/js/themes/default/d.gif
  16. BIN app/js/themes/default/d.png
  17. +73 −0 app/js/themes/default/style.css
  18. BIN app/js/themes/default/throbber.gif
  19. +230 −0 app/js/todo.js
  20. BIN app/skin/img/back-1x36.png
  21. BIN app/skin/img/back.png
  22. BIN app/skin/img/background.png
  23. BIN app/skin/img/blank.gif
  24. BIN app/skin/img/throbber.gif
  25. +7 −0 app/skin/layout-bottom.html
  26. +25 −0 app/skin/layout-top.html
  27. +18 −0 app/skin/public.css
  28. +456 −0 app/skin/style.css
  29. +104 −0 app/test.html
  30. +51 −0 app/todo.html
  31. +10 −0 bin/installdeps
  32. +6 −0 bin/start
  33. +12 −0 bin/stop
  34. +17 −0 conf/config.xml
  35. +7 −0 conf/ssi-environment.xml
  36. +5 −0 data/Abdul.json
  37. +5 −0 data/Teknopaul.json
  38. +20 −0 data/todos/life.json
  39. +1 −0 data/todos/shopping.json
  40. +5 −0 data/todos/work.json
  41. +53 −0 server/http_mods/app.js
  42. +37 −0 server/http_mods/config.js
  43. +119 −0 server/http_mods/default.js
  44. +28 −0 server/http_mods/favicon.js
  45. +164 −0 server/http_mods/rest.js
  46. +67 −0 server/persistence/file-resolve.js
  47. +17 −0 server/server/attributes-filter.js
  48. +35 −0 server/server/init.js
  49. +16 −0 server/server/log-request-filter.js
  50. +67 −0 server/server/router.js
  51. +15 −0 server/server/server-header-filter.js
  52. +47 −0 server/server/server.js
  53. +32 −0 server/server/status-socket.js
  54. +32 −0 server/ssi/ssi-environment.js
  55. +204 −0 server/ssi/ssi-handler.js
  56. +161 −0 server/ssi/ssi-parser.js
  57. +21 −0 server/ssi/ssi-util.js
  58. +3 −0 server/start.js
  59. +65 −0 server/util/2d-xml2json.js
  60. +45 −0 server/util/buffered-stream.js
  61. +85 −0 server/util/config.js
  62. +136 −0 server/util/date.js
17 .project
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+ <name>nodetoy</name>
+ <comment></comment>
+ <projects>
+ </projects>
+ <buildSpec>
+ <buildCommand>
+ <name>org.eclipse.wst.jsdt.core.javascriptValidator</name>
+ <arguments>
+ </arguments>
+ </buildCommand>
+ </buildSpec>
+ <natures>
+ <nature>org.eclipse.wst.jsdt.core.jsNature</nature>
+ </natures>
+</projectDescription>
7 .settings/.jsdtscope
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<classpath>
+ <classpathentry kind="src" path=""/>
+ <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.JRE_CONTAINER"/>
+ <classpathentry kind="con" path="org.eclipse.wst.jsdt.launching.baseBrowserLibrary"/>
+ <classpathentry kind="output" path=""/>
+</classpath>
1 .settings/org.eclipse.wst.jsdt.ui.superType.container
@@ -0,0 +1 @@
+org.eclipse.wst.jsdt.launching.baseBrowserLibrary
1 .settings/org.eclipse.wst.jsdt.ui.superType.name
@@ -0,0 +1 @@
+Window
27 app/404.html
@@ -0,0 +1,27 @@
+<html id="pw-page">
+
+<head>
+
+<title>Page Not Found</title>
+
+<!--#include virtual="/app/inc.html" -->
+
+</head>
+
+<body>
+
+<!--#include virtual="/app/skin/layout-top.html" -->
+
+<div id="pw-title">404 Page Not Found</div>
+
+<div id="pw-content">
+
+Page not found
+
+</div><!--pw-content-->
+
+<!--#include virtual="/app/skin/layout-bottom.html" -->
+
+</body>
+
+</html>
89 app/doc.html
@@ -0,0 +1,89 @@
+<html id="pw-page">
+
+<head>
+
+<title>Node Toy Docs</title>
+
+<!--#include virtual="/app/inc.html" -->
+
+<script type="text/javascript">
+ var m3TestPage = new nt.TestPage();
+ jQuery(function() {
+ m3TestPage.init();
+ });
+</script>
+
+</head>
+
+<body id="pw-body">
+
+<!--#include virtual="/app/skin/layout-top.html" -->
+
+<div id="pw-title">Node Toy</div>
+
+<div id="pw-content">
+
+Node toy is a node server that does JSON CRUD, so you can write desktop APPs that use the browser by writing just HTML and JavaScript.
+<br/><br/>
+
+The server has no database, JSON is stored directly on the file system.
+<br/><br/>
+
+The server responds to AJAX requests containing JSON and follows <b>REST</b> principals.
+
+<h3>The Rules</h3>
+
+All requests to process JSON data must start with /data/.
+<br/><br/>
+
+If you <b>POST</b> {"userID" : 1, "Bob"} to /data/user/Bob.json
+<br/>
+The server will create a file called users/Bob.json in the directory configured to store data.
+<br/><br/>
+
+The JSON can be retrieved by doing a <b>GET</b> from /data/user/Bob.json
+<br/><br/>
+
+The file is deleted by sending a <b>DELETE</b> to /data/user/Bob.json.
+<br/><br/>
+
+To get a list of all the <b>*.json</b> files in a directory send a GET to the directory /data/user/.
+
+<h3>The Test Page</h3>
+If that was not simple enough already you can try it out using the test page at
+<br/><a href="/app/test.html">/app/test.html</a> before you write any JavaScript.
+<br/>This gives you a GUI view of the requests.
+<br/><br/>
+
+The server also serves HTML, CSS, JS and images from a directory called /app/ this is where you add your front end code.
+
+<h3>Configuration</h3>
+Configuration is simple there is an XML file conf/config.xml containing a few variables including the port to listen on, the location of /app/ and the location of /data/
+<br/><br/>
+
+You can add other paramters in config if, is made available as JSON if you send a <b>GET</b> to /config/
+<br/><br/>
+
+<h3>Server Side Includes</h3>
+All HTML has SSI includes processed, although the full spec of Apache SSI is not supported, including files and variables is.
+<br/><br/>
+You can add variables to the SSI environment (Like Apache&apos;s SetEnv command) by editing the /config/ssi-environment.xml file.
+
+<h3>Security</h3>
+
+There is no security, there are no users, no logins and all files are accessible even if the files are not JSON.
+If that bothers you, use a firewall.
+<br/><br/>
+
+If your app ever grows up to the extent that you want to publish on t'internet it you can add a security filter.
+<br/><br/>
+
+
+</div><!--pw-content-->
+
+<!--#include virtual="/app/skin/layout-bottom.html" -->
+<br/>
+</body>
+
+</html>
+
BIN app/favicon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
8 app/inc.html
@@ -0,0 +1,8 @@
+
+<link rel="SHORTCUT_ICON" href="/app/facicon.png" />
+<link href="/app/skin/style.css" rel="stylesheet" type="text/css" />
+<link href="/app/skin/public.css" rel="stylesheet" type="text/css" />
+<script src="/app/js/jquery-1.4.2.js"></script>
+<script src="/app/js/jquery.closure.js"></script>
+<script src="/app/js/jquery.htmlbuffer.js"></script>
+<script src="/app/js/popups.js"></script>
6,240 app/js/jquery-1.4.2.js
6,240 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
82 app/js/jquery.closure.js
@@ -0,0 +1,82 @@
+
+/**
+ * jQuery Closure plugin.
+ *
+ * <br>Copyright (c) 2010 Paul Hinds
+ * <br>Dual licensed under the MIT and GPL licenses:
+ * <br>http://www.opensource.org/licenses/mit-license.php
+ * <br>http://www.gnu.org/licenses/gpl.html
+ * @fileoverview
+ */
+
+/**
+ * Can be used to reduce closure on a function.
+ * <br/>
+ * e.g.
+ * <pre>
+ *
+ * jQuery('#theId').click($.callback(this, this.myEventHandler));
+ *
+ * </pre>
+ * instead of this pattern
+ * <pre>
+ * var html = '&lt;div id="theId"&gt;&lt;div&gt;';
+ * jQuery('somthing').html(html);
+ * jQuery('#theId').click(function(){
+ * this.myEventHandler();
+ * });
+ *
+ * </pre>
+ * which creates a closure on the unwanted html variable.
+ * <br/>
+ * Only two parameters may be given to this method, the method will be run with
+ * the arguments of the code that calls the callback.
+ *
+ * @param object the context object for the method call
+ * @param method the function to be called with parenthasis
+ * @static
+ */
+jQuery.callback = function(object, method){
+ return function() {
+ return method.apply(object, arguments);
+ };
+};
+
+
+/**
+ * Closes a function and its arguments.
+ * <br/><br/>
+ * e.g.
+ * <br/>
+ * <pre>
+ * for(i=0; i<=5 ; i++) {
+ * jQuery('#id' + i).click($.closeArgs(this, this.myEventHandler, i, value));
+ * }
+ * </pre>
+ *
+ * The <code>i</code> and the <code>value</code> variables will remain as expected.
+ * <br/><br/>
+ *
+ * With the following code, i is always 5 because the same variable becomes part of the function's
+ * closure.
+ * <pre>
+ * for(i=0; i<=5 ; i++) {
+ * jQuery('#theId').click(function(e){
+ * myEventHandler(i, value);
+ * }
+ * }
+ * </pre>
+ * Any number of arguments may be given to this method but the callback's arguments will be
+ * lost. In the example above <code>e</code> is the lost argument.
+ *
+ * @param object the context object for the method call
+ * @param method the function to be called with parenthasis
+ * @param var_args any number of additional parameters may be added and will be added to the closure
+ * @static
+ */
+jQuery.closeArgs = function(object, method) {
+ var args = Array.prototype.slice.call(arguments, 2);
+ return function(){
+ return method.apply(object, args);
+ };
+};
89 app/js/jquery.htmlbuffer.js
@@ -0,0 +1,89 @@
+/**
+ * Utility class for generating HTML strings.
+ * <br><br>
+ *
+ * The idea of this class is that it is easy to use without introducing cross-site scripting
+ * (XSS) security risks.
+ * <br><br>
+ *
+ * Any text that is constant HTML strings should be added to the buffer with the html()
+ * method, similar to jQuerys' html() method.
+ * <br><br>
+ *
+ * Any untrusted values from JavaScript objects should be added with the text() method
+ * similar to jQuerys text() method.
+ * <br><br>
+ *
+ * Be careful, writing html(obj.value) is still possible, and introduces XSS risks, just
+ * as it does with jQuery.
+ * <br><br>
+ *
+ * <pre>
+ *
+ * var html = new $.htmlBuffer();
+ * html.html('&lt;div&gt;blah&lt;/div&gt;')
+ * .text(data.someObject)
+ * .html('&lt;div&gt;blah&lt;/div&gt;')
+ * .toString();
+ *
+ * </pre>
+ *
+ * This class only escapes the following characters &amp; &lt; &gt; &quot; &apos;
+ * Which means the page should be UTF-8 to be able to represent a full characterset.
+ * For example the &euro; sign could be escaped as &amp;euro; but it is not, if you want support
+ * for non UTF-8 JavaScript and HTML pages you need to put a lot more replacements into
+ * the toHtml() method.
+ *
+ * @class htmlBuffer
+ */
+jQuery.htmlBuffer = function() {
+ this.buffer = '';
+};
+/**
+ * Add HTML strings to the buffer, the string <b>will not be escaped</b>.
+ * @param string a string of HTML
+ * @return this
+ */
+jQuery.htmlBuffer.prototype.html = function(string) {
+ this.buffer += string;
+ return this;
+};
+/**
+ * Add arbitrary values to the buffer.
+ * If the value is a String, HTML entities such as &amp;
+ * <b>will be escaped</b> to &amp;amp;
+ * <br/><br/>
+ *
+ * Numbers and booleans can also be added, and will have normal object toString representations
+ * from string concatenation rules in JavaScript.
+ * @param string an arbitrary String or any Object
+ * @return this
+ */
+jQuery.htmlBuffer.prototype.text = function(string) {
+ this.buffer += this.toHtml(string);
+ return this;
+};
+/**
+ * return the whole buffer, as a string.
+ */
+jQuery.htmlBuffer.prototype.toString = function() {
+ return this.buffer;
+};
+/**
+ * Utility method to escape HTML entities.
+ *
+ * Only the following values are escaped
+ * &amp; &lt; &gt; &quot; &apos;
+ *
+ * @private
+ */
+jQuery.htmlBuffer.prototype.toHtml = function(value) {
+ if(typeof value == 'string') {
+ value = value.replace(/&/g, '&amp;');
+ value = value.replace(/</g, '&lt;');
+ value = value.replace(/>/g, '&gt;');
+ value = value.replace(/"/g, '&quot;');
+ value = value.replace(/'/g, '&apos;');
+ }
+ return value;
+};
3,510 app/js/jquery.jstree.js
3,510 additions, 0 deletions not shown because the diff is too large. Please use a local Git client to view these changes.
286 app/js/nodetoy-test.js
@@ -0,0 +1,286 @@
+if(typeof nt=='undefined') {
+ /**
+ * @namespace nt namespace object
+ */
+ var nt = new Object();
+}
+
+//Ugly global but this is global to the page too
+var popup = new pw.Popup();
+
+nt.TestPage = function() {
+ this.api = false;
+ this.handler = null;
+ this.method = null;
+ this.lastResponse = false;
+};
+nt.TestPage.prototype.init = function() {
+ var self = this;
+ this.throbberOff();
+ jQuery('#m3-tree').click(function() {
+ if (self.lastResponse) self.print(self.lastResponse);
+ });
+ jQuery('#m3-print').click(function(){
+ if (self.lastResponse) self.print(self.lastResponse);
+ });
+ jQuery('#m3-open-all').click(function(){
+ self.openAll();
+ });
+ jQuery('#m3-close-all').click(function(){
+ self.closeAll();
+ });
+ jQuery('#m3-search').click(function(){
+ self.search();
+ });
+}
+nt.TestPage.prototype.reset = function() {
+ if (! this.api) return;
+ var form = jQuery('#m3-test-form')[0];
+ if (form.suggest.checked) {
+ form.data.value = this.method.queryString;
+ }
+ else {
+ form.data.value = '';
+ }
+};
+
+/**
+ * Executes the command Query or UserQuery.
+ * and prit the results.
+ */
+nt.TestPage.prototype.exec = function() {
+ try {
+ var self = this;
+ this.throbberOn();
+ this.clear();
+ var form = jQuery('#m3-test-form')[0];
+
+ if (form.url.value.indexOf("/data/") != 0 &&
+ form.url.value.indexOf("/config/") != 0 &&
+ form.url.value.indexOf("/app/") != 0
+ ) {
+
+ popup.alert("Error", "URL should start with /data/", $.callback(this, this.throbberOff));
+
+ return;
+ }
+
+ var method = "GET";
+ if (form.httpMethod[0].checked) method = 'GET';
+ if (form.httpMethod[1].checked) method = 'POST';
+ if (form.httpMethod[2].checked) method = 'DELETE';
+
+ jQuery.ajax({
+ url : form.url.value,
+ type : method,
+ data : form.data.value,
+ success : function(response) {
+ self.lastResponse = response;
+ self.print(response);
+ },
+ error : function(err) {
+ self.lastResponse = null;
+ self.printSystemError(err);
+ },
+ complete : function(req) {
+ self.throbberOff();
+ self.printHeaders(req);
+ }
+ });
+ } catch(err) {
+ return false;
+ }
+};
+/**
+ * Resets the headers panel the treeview or text view and
+ * removes the JSON search buttons
+ */
+nt.TestPage.prototype.clear = function() {
+ jQuery('#m3-headers').html('');
+ jQuery('#m3-js-tree-view').html('');
+ jQuery('#m3-tree-view').html('');
+ jQuery('#m3-output').html('');
+ jQuery('#m3-buttons').css('visibility', 'hidden');
+};
+/**
+ * Prints the JSON, either as a TreeView or as text
+ */
+nt.TestPage.prototype.print = function(jsonObject) {
+ try {
+ this.clear();
+ var form = jQuery('#m3-test-form')[0];
+ if (form.viewType[0].checked) {
+ jQuery('#m3-output').css('display', 'none');
+ jQuery('#m3-buttons').css('visibility', 'visible');
+ this.jsTreeView(jsonObject);
+ } else {
+ jQuery('#m3-output').css('display', 'block');
+ jQuery('#m3-output').text(JSON.stringify(jsonObject), null, 4);
+ }
+ } catch (error) {
+ jQuery('#m3-output').text('Error' + error);
+ }
+};
+/**
+ * Prints an m3_error
+ */
+nt.TestPage.prototype.printError = function(jsonObject) {
+ var html = new jQuery.htmlBuffer();
+ html.html('<div class="m3-region ui-state-error ui-corner-all">')
+ .html('<span style="float: left; margin-right: 0.3em;" class="ui-icon ui-icon-info"></span>')
+ .text(jsonObject.m3_error.code);
+ if(jsonObject.m3_error.name != null) {
+ html.text(' : ')
+ .text(jsonObject.m3_error.name);
+ }
+ if(jsonObject.m3_error.detail){
+ if(jsonObject.m3_error.detail.trace) {
+ html.html('<pre class="m3-trace">');
+ html.text(jsonObject.m3_error.detail.trace);
+ html.html('</pre>');
+ }
+ else {
+ this.jsTreeView(jsonObject.m3_error.detail);
+ }
+ }
+ html.html('</div>');
+ jQuery('#m3-output').html(html.toString());
+ jQuery('#m3-output').css('display', 'block');
+};
+nt.TestPage.prototype.printSystemError = function(response) {
+ var html = new jQuery.htmlBuffer();
+ html.html('<div class="m3-region ui-state-error ui-corner-all">')
+ .html('<span style="float: left; margin-right: 0.3em;" class="ui-icon ui-icon-info"></span>')
+ .html('<em>System error</em> ').text(response.status)
+ .html('</div>');
+ jQuery('#m3-output').html(html.toString());
+ jQuery('#m3-output').css('display', 'block');
+};
+/**
+ * Prints an m3_condition
+ */
+nt.TestPage.prototype.printCondition = function(jsonObject) {
+ var html = new jQuery.htmlBuffer();
+ html.html('<div class="m3-region ui-state-highlight ui-corner-all">')
+ .html('<span style="float: left; margin-right: 0.3em;" class="ui-icon ui-icon-alert"></span>')
+ .text(jsonObject.m3_condition.code);
+ if(jsonObject.m3_condition.name != null) {
+ html.text(' : ')
+ .text(jsonObject.m3_condition.name);
+ }
+ if(jsonObject.m3_condition.detail) {
+ this.jsTreeView(jsonObject.m3_error.detail);
+ }
+ html.html('</div>');
+ jQuery('#m3-output').html(html.toString());
+};
+/**
+ * Creates populates and displays the jQuery jsTree view
+ */
+nt.TestPage.prototype.jsTreeView = function(jsonObject) {
+ //instantiate the TreeView control:
+ jQuery("#m3-js-tree-view").jstree({
+ 'core' : {animation: 250},
+ 'json_data' : {
+ 'data' : [this.createJsTree(jsonObject)]
+ },
+ 'plugins': ['themes', 'json_data', 'search']
+ });
+};
+/**
+ * expands all the nodes in the Treeview
+ */
+nt.TestPage.prototype.openAll = function() {
+ jQuery("#m3-js-tree-view").jstree('open_all', -1);
+};
+/**
+ * Contracts or closes all the nodes
+ */
+nt.TestPage.prototype.closeAll = function() {
+ jQuery("#m3-js-tree-view").jstree('close_all', -1);
+};
+/**
+ * Searches the tree for a given string
+ */
+nt.TestPage.prototype.search = function() {
+ var text = jQuery("#m3-search-text").val();
+ jQuery("#m3-js-tree-view").jstree("search", text);
+};
+/**
+ * Convert an arbitrary JSON object to the JSON object supported by jstree.
+ */
+nt.TestPage.prototype.createJsTree = function (jsonObject) {
+ var root = {data: "{}", state: 'open', children: new Array()};
+ this.toJsTree(jsonObject, root.children, 0, true);
+ return root;
+};
+
+nt.TestPage.prototype.toJsTree = function(jsonObject, children, depth, isObject) {
+ for (var name in jsonObject) {
+ var value = jsonObject[name];
+ if (isObject) {
+ name = name + ' : ';
+ }
+ else { // array
+ name = '';
+ }
+ if (
+ (typeof value == "number") ||
+ (typeof value == "boolean")
+ ) {
+ children.push({'data': name + value});
+ }
+ else if(typeof value == "string") {
+ children.push({'data': name + '"' + this.escapeJava(value) + '"'});
+ }
+ else if(value == null) {
+ children.push({data: name + 'null'});
+ }
+ else if(value instanceof Array ) {
+ var node = {data: name + '[]', state: depth < 3 ? 'open' : 'closed', children: new Array()};
+ children.push(node);
+ this.toJsTree(value, node.children, depth + 1, false);
+ }
+ else if(value instanceof Object) {
+ var node = {data: name + '{}', state: depth < 3 ? 'open' : 'closed', children: new Array()};
+ children.push(node);
+ this.toJsTree(value, node.children, depth + 1, true);
+ }
+ }
+};
+nt.TestPage.prototype.printHeaders = function(req) {
+ var dom = jQuery('#m3-headers');
+ try {
+ var html = new jQuery.htmlBuffer();
+ var date = req.getResponseHeader('Date');
+ var expires = req.getResponseHeader('Expires');
+ if(expires == null) {
+ var ttl = 'Infinite';
+ } else {
+ var jDate = new Date(date);
+ var jExpires = new Date(expires);
+ var ttl = jExpires.getTime() - jDate.getTime();
+ }
+ html.html('<div>TTL: ').text(ttl).html('</div>');
+
+ dom.html(html.toString());
+ } catch(err) {
+ dom.text('Headers not available:' + err);
+ }
+};
+
+//utils
+nt.TestPage.prototype.throbberOn = function(){
+ jQuery('.m3-throbber').attr('src', '/app/skin/img/throbber.gif');
+};
+nt.TestPage.prototype.throbberOff = function(){
+ jQuery('.m3-throbber').attr('src', '/app/skin/img/blank.gif');
+};
+/**
+ * replace \n and \t with HTML elements for new line and non-blanking space.
+ */
+nt.TestPage.prototype.escapeJava = function(string){
+ string = string.replace(/\n/g, '<br/>');
+ string = string.replace(/\t/g, '&nbsp;&nbsp;');
+ return string;
+};
189 app/js/popups.js
@@ -0,0 +1,189 @@
+/**
+ * "popup" dialog using divs. implementes the classic alert, confirm and prompt methods and a generig popup which accepts any ol HTML.
+ * Should not really be needed but FF and other browsers have added the annoying checkbox to the native widgets, permitting the user to
+ * discard them and not permit any more.
+ *
+ * however since this is CSS stylable it can also look much better.
+ */
+
+
+if(typeof pw=='undefined') {
+ /**
+ * @namespace pw namespace object
+ */
+ var pw = new Object();
+}
+
+/**
+ * Constructor
+ *
+ * @param div optional jQuery selector for a hidden div to which the html should be added.
+ * if missing one is created with id "pw-popup" which can be positioned with CSS.
+ *
+ */
+pw.Popup = function(div) {
+ this.div = div || null;
+ this.type = null;
+ this.callback = null;
+};
+
+pw.Popup.prototype.popup = function(html) {
+ var elem = this.getDiv();
+ elem.html(html);
+ elem.css('display', 'block');
+};
+
+pw.Popup.prototype.alert = function(title, text, callback) {
+ try {
+ this.type = 'alert';
+ this.callback = callback;
+ var sb = new $.htmlBuffer();
+ sb.html('<h3>');
+ sb.text(title);
+ sb.html('</h3>');
+ if(text){
+ sb.html('<div>').text(text).html('</div>');
+ }
+ sb.html('<form id="pw-popup-form">')
+ .html('<span id="pw-popup-buttons">')
+ .html('<input type="button" id="pw-popup-ok" value="OK"></input>')
+ .html('</span>')
+ .html('</form>');
+ this.popup(sb.toString());
+ jQuery('#pw-popup-form').submit(jQuery.callback(this, this.submit));
+ jQuery('#pw-popup-ok').click(jQuery.callback(this, this.submit));
+ jQuery('#pw-popup-ok').focus();
+ } catch(err) {
+ alert(err);
+ }
+};
+
+pw.Popup.prototype.confirm = function(title, text, callback) {
+ try {
+ this.type = 'confirm';
+ this.callback = callback;
+ var sb = new $.htmlBuffer();
+ sb.html('<h3>');
+ sb.text(title);
+ sb.html('</h3>');
+ if(text){
+ sb.html('<div>').text(text).html('</div>');
+ }
+ sb.html('<form id="pw-popup-form">')
+ .html('<span id="pw-popup-buttons">')
+ .html('<input type="button" id="pw-popup-ok" value="OK"></input>')
+ .html('<input type="button" id="pw-popup-cancel" value="Cancel"></input>')
+ .html('</span>')
+ .html('</form>');
+ this.popup(sb.toString());
+ jQuery('#pw-popup-ok').click(jQuery.closeArgs(this, this.submit, true));
+ jQuery('#pw-popup-cancel').click(jQuery.closeArgs(this, this.submit, false));
+ jQuery('#pw-popup-cancel').focus();
+ } catch(err) {
+ alert(err);
+ }
+};
+pw.Popup.prototype.prompt = function(title, text , callback) {
+ try {
+ this.type = 'prompt';
+ this.callback = callback;
+ var sb = new $.htmlBuffer();
+ sb.html('<h3>')
+ .text(title)
+ .html('</h3>');
+ if(text){
+ sb.html('<div>').text(text).html('</div>');
+ }
+ sb.html('<form id="pw-popup-form">')
+ .html('<input type="text" id="pw-popup-answer"></input><br/>')
+ .html('<span id="pw-popup-buttons">')
+ .html('<input type="button" id="pw-popup-ok" value="OK"></input>')
+ .html('<input type="button" id="pw-popup-cancel" value="Cancel"></input>')
+ .html('</span>')
+ .html('</form>');
+ this.popup(sb.toString());
+ jQuery('#pw-popup-form').submit(jQuery.callback(this, this.submit));
+ jQuery('#pw-popup-ok').click(jQuery.callback(this, this.submit));
+ jQuery('#pw-popup-cancel').click(jQuery.callback(this, this.dispose));
+ jQuery('#pw-popup-answer').focus();
+ } catch(err) {
+ alert(err);
+ }
+};
+
+pw.Popup.prototype.login = function(title, text, callback) {
+ try {
+ this.type = 'login';
+ this.callback = callback;
+ var sb = new $.htmlBuffer();
+ sb.html('<h3>')
+ .text(title)
+ .html('</h3>');
+ if(text){
+ sb.html('<div>').text(text).html('</div>');
+ }
+ sb.html('<form id="pw-popup-form">')
+ .html('<label>Username</label><input type="text" id="pw-popup-username"></input><br/>')
+ .html('<label>Password</label><input type="password" id="pw-popup-password"></input><br/>')
+ .html('<span id="pw-popup-buttons">')
+ .html('<input type="button" id="pw-popup-ok" value="Login"></input>')
+ .html('<input type="button" id="pw-popup-cancel" value="Cancel"></input>')
+ .html('</span>')
+ .html('</form>');
+ this.popup(sb.toString());
+ jQuery('#pw-popup-form').submit(jQuery.callback(this, this.submit));
+ jQuery('#pw-popup-ok').click(jQuery.callback(this, this.submit));
+ jQuery('#pw-popup-cancel').click(jQuery.callback(this, this.dispose));
+ jQuery('#pw-popup-username').focus();
+ } catch(err) {
+ alert(err);
+ }
+};
+
+
+pw.Popup.prototype.submit = function(ok) {
+ if (this.type == 'alert') {
+ if (this.callback) {
+ this.callback(ok);
+ }
+ }
+ else if (this.type == 'confirm') {
+ if (ok && this.callback) {
+ this.callback(ok);
+ }
+ }
+ else if (this.type == 'prompt') {
+ var answer = jQuery('#pw-popup-answer').val();
+ if (this.callback) {
+ this.callback(answer);
+ }
+ }
+ else if (this.type == 'login') {
+ var username = jQuery('#pw-popup-username').val();
+ var password = jQuery('#pw-popup-password').val();
+ if (this.callback) {
+ this.callback(username, password);
+ }
+ }
+ this.dispose();
+};
+
+pw.Popup.prototype.dispose = function() {
+ var div = this.getDiv();
+ div.html('');
+ div.css('display', 'none');
+ this.type = null;
+ this.callback = null;
+};
+
+pw.Popup.prototype.getDiv = function() {
+ if (this.div) {
+ return this.div;
+ }
+ this.div = jQuery("#pw-popup");
+ if (this.div.length == 0) {
+ jQuery("body").append('<div id="pw-popup" style="display:none"/>');
+ this.div = jQuery("#pw-popup");
+ }
+ return this.div;
+};
BIN app/js/themes/default/d.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN app/js/themes/default/d.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
73 app/js/themes/default/style.css
@@ -0,0 +1,73 @@
+/*
+ * jsTree default theme 1.0
+ * Supported features: dots/no-dots, icons/no-icons, focused, loading
+ * Supported plugins: ui (hovered, clicked), checkbox, contextmenu, search
+ */
+
+.jstree-default li,
+.jstree-default ins { background-image:url("d.png"); background-repeat:no-repeat; background-color:transparent; }
+.jstree-default li { background-position:-90px 0; background-repeat:repeat-y; }
+.jstree-default li.jstree-last { background:transparent; }
+.jstree-default .jstree-open > ins { background-position:-72px 0; }
+.jstree-default .jstree-closed > ins { background-position:-54px 0; }
+.jstree-default .jstree-leaf > ins { background-position:-36px 0; }
+
+.jstree-default .jstree-hovered { background:#e7f4f9; border:1px solid #d8f0fa; padding:0 2px 0 1px; }
+.jstree-default .jstree-clicked { background:#beebff; border:1px solid #99defd; padding:0 2px 0 1px; }
+.jstree-default a .jstree-icon { background-position:-56px -19px; }
+.jstree-default a.jstree-loading .jstree-icon { background:url("throbber.gif") center center no-repeat !important; }
+
+.jstree-default.jstree-focused { background:#ffffee; }
+
+.jstree-default .jstree-no-dots li,
+.jstree-default .jstree-no-dots .jstree-leaf > ins { background:transparent; }
+.jstree-default .jstree-no-dots .jstree-open > ins { background-position:-18px 0; }
+.jstree-default .jstree-no-dots .jstree-closed > ins { background-position:0 0; }
+
+.jstree-default .jstree-no-icons a .jstree-icon { display:none; }
+
+.jstree-default .jstree-search { font-style:italic; }
+
+.jstree-default .jstree-no-icons .jstree-checkbox { display:inline-block; }
+.jstree-default .jstree-no-checkboxes .jstree-checkbox { display:none !important; }
+.jstree-default .jstree-checked > a > .jstree-checkbox { background-position:-38px -19px; }
+.jstree-default .jstree-unchecked > a > .jstree-checkbox { background-position:-2px -19px; }
+.jstree-default .jstree-undetermined > a > .jstree-checkbox { background-position:-20px -19px; }
+.jstree-default .jstree-checked > a > .jstree-checkbox:hover { background-position:-38px -37px; }
+.jstree-default .jstree-unchecked > a > .jstree-checkbox:hover { background-position:-2px -37px; }
+.jstree-default .jstree-undetermined > a > .jstree-checkbox:hover { background-position:-20px -37px; }
+
+#vakata-dragged.jstree-default ins { background:transparent !important; }
+#vakata-dragged.jstree-default .jstree-ok { background:url("d.png") -2px -53px no-repeat !important; }
+#vakata-dragged.jstree-default .jstree-invalid { background:url("d.png") -18px -53px no-repeat !important; }
+#jstree-marker.jstree-default { background:url("d.png") -41px -57px no-repeat !important; }
+
+.jstree-default a.jstree-search { color:aqua; }
+
+#vakata-contextmenu.jstree-default-context,
+#vakata-contextmenu.jstree-default-context li ul { background:#f0f0f0; border:1px solid #979797; -moz-box-shadow: 1px 1px 2px #999; -webkit-box-shadow: 1px 1px 2px #999; box-shadow: 1px 1px 2px #999; }
+#vakata-contextmenu.jstree-default-context li { }
+#vakata-contextmenu.jstree-default-context a { color:black; }
+#vakata-contextmenu.jstree-default-context a:hover,
+#vakata-contextmenu.jstree-default-context .vakata-hover > a { padding:0 5px; background:#e8eff7; border:1px solid #aecff7; color:black; -moz-border-radius:2px; -webkit-border-radius:2px; border-radius:2px; }
+#vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a,
+#vakata-contextmenu.jstree-default-context li.jstree-contextmenu-disabled a:hover { color:silver; background:transparent; border:0; padding:1px 4px; }
+#vakata-contextmenu.jstree-default-context li.vakata-separator { background:white; border-top:1px solid #e0e0e0; margin:0; }
+#vakata-contextmenu.jstree-default-context li ul { margin-left:-4px; }
+
+/* IE6 BEGIN */
+.jstree-default li,
+.jstree-default ins,
+#vakata-dragged.jstree-default .jstree-invalid,
+#vakata-dragged.jstree-default .jstree-ok,
+#jstree-marker.jstree-default { _background-image:url("d.gif"); }
+.jstree-default .jstree-open ins { _background-position:-72px 0; }
+.jstree-default .jstree-closed ins { _background-position:-54px 0; }
+.jstree-default .jstree-leaf ins { _background-position:-36px 0; }
+.jstree-default a ins.jstree-icon { _background-position:-56px -19px; }
+#vakata-contextmenu.jstree-default-context ins { _display:none; }
+#vakata-contextmenu.jstree-default-context li { _zoom:1; }
+.jstree-default .jstree-undetermined a .jstree-checkbox { _background-position:-20px -19px; }
+.jstree-default .jstree-checked a .jstree-checkbox { _background-position:-38px -19px; }
+.jstree-default .jstree-unchecked a .jstree-checkbox { _background-position:-2px -19px; }
+/* IE6 END */
BIN app/js/themes/default/throbber.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
230 app/js/todo.js
@@ -0,0 +1,230 @@
+if(typeof nt=='undefined') {
+ /**
+ * @namespace nt namespace object
+ */
+ var nt = new Object();
+}
+
+
+SECTIONS = ['work', 'life', 'shopping'];
+
+
+/////// Model
+
+nt.TodoModel = function() {
+ this.sections = SECTIONS;
+ this.data = {};
+ for(var i = 0 ; i < this.sections.length ; i++) {
+ var section = this.sections[i];
+ this.data[section] = {
+ name : section,
+ todos : new Array()
+ };
+ }
+};
+nt.TodoModel.prototype.add = function(section, text) {
+ this.data[section].todos.push(text);
+};
+nt.TodoModel.prototype.count = function(section) {
+ if(typeof section == 'number') {
+ section = SECTIONS[section];
+ }
+ return this.data[section].todos.length;
+};
+
+
+// end model //
+
+
+
+//////// View ////////
+
+nt.TodoUI = function(controller, model) {
+ this.controller = controller;
+ this.model = model;
+};
+
+nt.TodoUI.prototype.feedback = function(message) {
+ jQuery("#nt-messages").text(message);
+};
+
+nt.TodoUI.prototype.renderAll = function() {
+ var dom = jQuery("#nt-todos");
+ var html = new $.htmlBuffer();
+ html.html('<div id="accordion">');
+ for(var i = 0 ; i < this.model.sections.length ; i++) {
+ var section = this.model.sections[i];
+ html.html('<h3>').text(section).html('</h3>');
+ html.html('<div>').html(this.renderSection(section, i)).html('</div>');
+ }
+ html.html('</div>');
+
+ dom.html(html.toString());
+
+ // bind adds
+ for(var i = 0 ; i < this.model.sections.length ; i++) {
+ jQuery("#add-todo-" + i).change($.closeArgs(this, this.handleAdd, i));
+ jQuery("#add-todo-form-" + i).submit(this.halt);
+ }
+ this.bindDeletes();
+};
+
+nt.TodoUI.prototype.halt = function() {
+ return false;
+};
+
+nt.TodoUI.prototype.renderSection = function(section, idx) {
+ var todos = this.model.data[section].todos;
+ var html = new $.htmlBuffer();
+ html.html('<div class="section" id="section-' + idx + '">');
+ for(var i = 0 ; i < todos.length ; i++) {
+ // todo links and editable
+ if (todos[i].indexOf("DONE") != 0) {
+ html.html(this.renderTodo(todos[i], idx, i));
+ }
+ }
+ html.html('<form id="add-todo-form-' + idx + '" onsubmit="return false;">')
+ .html('<input type="text" id="add-todo-').text(idx).html('"></input>')
+ .html('</form>');
+
+ html.html('</div>');
+ return html.toString();
+};
+nt.TodoUI.prototype.renderTodo = function(text, secIdx, todoIdx) {
+ var html = new $.htmlBuffer();
+ html.html('<div>')
+ .html('<a href="delete" class="del" id="todo-' + secIdx + '-' + todoIdx + '" >x</a> ')
+ .text(text)
+ .html('</div>');
+ return html.toString();
+};
+
+nt.TodoUI.prototype.bindDeletes = function(source) {
+ jQuery(".del").click($.callback(this, this.handleDelete));
+};
+nt.TodoUI.prototype.handleDelete = function(e) {
+ try {
+ var todoId = e.target.id;
+ var parts = todoId.split('-');
+ this.markDone(parseInt(parts[1]), parseInt(parts[2]));
+ jQuery("#" + todoId).parent().remove();
+ } catch(err) {
+ this.feedback(err);
+ }
+ return false;
+};
+
+nt.TodoUI.prototype.handleAdd = function(i) {
+ try {
+ var section = SECTIONS[i];
+ var text = jQuery("#add-todo-" + i).val();
+ this.addTodo(section, text);
+ var html = this.renderTodo(text, i, this.model.count(section));
+ var form = jQuery("#add-todo-form-" + i);
+ form.before(html);
+ this.bindDeletes();
+ }
+ catch(err) {
+ this.feedback(err);
+ }
+ return false;
+};
+nt.TodoUI.prototype.addTodo = function(section, text) {
+ this.model.add(section, text);
+ this.controller.serializeSection(section);
+};
+
+nt.TodoUI.prototype.markDone = function(section, idx) {
+ if (typeof section == 'number') {
+ section = SECTIONS[section];
+ }
+ var text = this.model.data[section].todos[idx];
+ this.model.data[section].todos[idx] = "DONE " + text;
+ this.controller.serializeSection(section);
+};
+
+// end View //
+
+///////// Controller /////////
+
+nt.Todo = function() {
+ this.model = new nt.TodoModel;
+ this.ui = new nt.TodoUI(this, this.model);
+ this.loading = 0;
+};
+nt.Todo.prototype.init = function() {
+ this.fetchAll();
+};
+
+// materialize methods //
+nt.Todo.prototype.fetchAll = function() {
+ this.loading = SECTIONS.length;
+ jQuery.ajax({
+ type : 'GET',
+ url : '/data/todos/',
+ success : $.callback(this, this.materializeAll),
+ failure : $.closeArgs(this.ui, this.ui.feedback, "Error loading todos"),
+ });
+};
+
+nt.Todo.prototype.materializeAll = function(response) {
+ if (response.items) {
+ var items = response.items;
+ this.loading = items.length;
+ for (var i = 0 ; i < items.length; i++) {
+ this.fetchSection(items[i]);
+ }
+ }
+};
+
+nt.Todo.prototype.fetchSection = function(section) {
+ var self = this;
+ jQuery.ajax({
+ type : 'GET',
+ url : '/data/todos/' + section + '.json',
+ success : function(response) {
+ self.materializeSection(section, response);
+ },
+ failure : $.callback(this, this.loadError),
+ });
+};
+
+nt.Todo.prototype.materializeSection = function(section, response) {
+ this.loading--;
+ this.model.data[section].todos = response;
+ if (this.loading <= 0) {
+ this.ui.renderAll();
+ }
+};
+
+nt.Todo.prototype.loadError = function(message) {
+ this.loading--;
+ this.ui.feedback(message);
+};
+
+nt.Todo.prototype.feedback = function(message) {
+ this.ui.feedback(message);
+};
+
+//materialize methods -end //
+
+// serialize methods //
+nt.Todo.prototype.serializeAll = function() {
+ for(var i = 0 ; i < this.model.sections.length ; i++) {
+ var section = this.model.sections[i];
+ this.serializeSection(section);
+ }
+};
+nt.Todo.prototype.serializeSection = function(section) {
+ jQuery.ajax({
+ type : 'POST',
+ url : '/data/todos/' + section + '.json',
+ data : JSON.stringify(this.model.data[section].todos),
+ success : $.closeArgs(this, this.feedback, 'Saved'),
+ failure : $.closeArgs(this, this.feedback, 'Error saving'),
+ });
+};
+//serialize methods - end //
+
+
+
BIN app/skin/img/back-1x36.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN app/skin/img/back.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN app/skin/img/background.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN app/skin/img/blank.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
BIN app/skin/img/throbber.gif
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
7 app/skin/layout-bottom.html
@@ -0,0 +1,7 @@
+ </div>
+ </div>
+ <div id="footer">
+ <!--#echo var="copyRight" -->
+ </div>
+
+</div>
25 app/skin/layout-top.html
@@ -0,0 +1,25 @@
+<div id="container">
+ <div id="header">
+ <span class="title"><!--#echo var="serverName" --></span>
+ </div>
+ <div id="content">
+ <div id="menu"><div id="pw-popup" style="display:none;position:absolute"></div>
+ <!-- main menu -->
+ <ul>
+ <li><a class="help-link" href="/app/test.html">
+ <span class="tooltip">
+ Use this page to test making Ajax requests to the server.
+ </span>
+ Test Page</a>
+ </li>
+ <li><a class="help-link" href="/app/doc.html">
+ <span class="tooltip">
+ Documentation.
+ </span>
+ Docs</a>
+ </li>
+ </ul>
+ </div>
+ <div id="main">
+
+
18 app/skin/public.css
@@ -0,0 +1,18 @@
+/* CSS that can be used in the wiki pages */
+
+
+.mono {
+ font-family : monospace;
+ padding-left : 5px;
+ padding-right : 5px;
+ font-size: 10pt;
+}
+
+.pw-title {
+ font-size: 20px;
+ font-weight: bold;
+ padding-bottom: 5px;
+ margin-bottom: 5px;
+ text-shadow: 2px 2px 2px #ddd;
+ border-bottom: 1px dotted #eee;
+}
456 app/skin/style.css
@@ -0,0 +1,456 @@
+/*
+ main content width
+ min 900px
+*/
+#container{
+ width:950px;
+}
+/*
+ font-size for file system view
+ 8pt 10pt for smime images
+ 12 - 15pt for larger images
+*/
+.filesystem li {
+ font-size:14pt;
+}
+/*
+ reduce this if you are using small fonts
+*/
+body{
+ line-height:1.4em;
+}
+
+/* HTML components */
+body{
+margin:0px;padding:0px;border:0px;
+font-family:Verdana,Arial,sans-serif;
+font-weight:normal;
+text-decoration:none;
+font-size:12pt;
+font-style:normal;
+}
+iframe{
+border:0px;margin:0px;padding:0px;
+}
+h1,h2,h3,h4,h5,b,em{font-weight:bold;}
+h4{font-size:13pt;}
+h3{font-size:13pt;}
+h2{font-size:15pt;}
+h1{font-size:16pt;}
+p{
+margin:0px;padding:0px;
+}
+ul{
+margin-top:10px; margin-bottom: 10px;
+}
+code,pre{
+font-family:mono-spaced;
+font-weight:normal;
+text-decoration:none;
+font-size:14px
+}
+samp {
+font-size:12px
+}
+hr{
+border:1px grey etched;
+background: silver;
+margin-top:4px;
+margin-bottom:4px;
+padding:0px;
+}
+table{
+border-collapse:collapse;
+}
+th{
+text-align:left;
+color:#fff;
+padding:2px 4px 2px 4px;
+}
+td{
+padding:2px 4px 2px 4px;
+}
+a img{
+border:0px;
+}
+a{
+color:#111;
+text-decoration:none;
+}
+a:hover{
+color:#111;
+text-decoration:underline;
+}
+img {
+vertical-align: middle;
+}
+/* end HTML components */
+
+/* center column */
+body#pw-body {
+ margin-top:15px;
+ text-align:center;
+ background: url(img/background.png) repeat-x 0 0;
+}
+#container{
+
+ text-align:left;
+ padding-top:0px;
+ margin-top:0px;
+ margin:auto;
+
+ box-shadow: 1px 1px 3px #333;
+ -moz-box-shadow: 1px 1px 3px #333;
+ -webkit-box-shadow: 1px 1px 3px #333;
+ -khtml-box-shadow: 1px 1px 3px #333;
+
+ border-radius: 8px;
+ -moz-border-radius: 8px;
+ -webkit-border-radius: 8px;
+ -khtml-border-radius: 8px;
+}
+#content{
+ position:relative;
+ min-height:450px;
+ /*tthis empty border is an IE hack ?! !WFT */
+ border: solid 1px white;
+ background: #fff;
+}
+
+table.frame{
+ width: 100%;
+}
+table.frame td{
+ margin:0px;padding:0px;border:0px;
+}
+
+/* header is the blue top bar */
+#header{
+ background: url(img/back-1x36.png) repeat-x 0 0;
+ border :1px solid; border-color : #efefef #bbb #bbb #efefef;
+ padding-top: 7px; padding-bottom: 6px;
+ border-radius: 8px 8px 0px 0px;
+ -moz-border-radius: 8px 8px 0px 0px;
+ -webkit-border-radius: 8px 8px 0px 0px;
+ -khtml-border-radius: 8px 8px 0px 0px;
+}
+#header span.title {
+ vertical-align:top;
+ padding-left: 45px; /*13px;*/
+ color: #335;
+ font-weight: normal;
+ font-size:1.2em;
+ background: url(/favicon.png) no-repeat 9px 0;
+ text-shadow: 1px 1px 0 #EEEEEE;
+}
+
+/* footer */
+#footer{
+ background: url(img/back.png) repeat-x 0 0;
+ border-left: 1px solid white
+ margin-bottom:16px;
+ color: #999;
+ font-size: 11px;
+ line-height:12px;
+ white-space:nowrap;
+ text-align:center;
+ text-shadow: 1px 1px 0 #EEEEEF;
+ padding-top:5px;
+ padding-bottom:5px;
+ border-radius: 0px 0px 8px 8px;
+ -moz-border-radius: 0px 0px 8px 8px;
+ -webkit-border-radius: 0px 0px 8px 8px;
+ -khtml-border-radius: 0px 0px 8px 8px;
+}
+
+/* in main all the central content */
+#main{
+ margin-left:150px;
+ margin-right:15px;
+ padding-top: 10px;
+ /*FF etc*/
+ min-height:350px;
+ /*IE hacks etc*/
+ height:auto !important;
+ height:350px;
+}
+
+/* the side menu */
+#menu{
+ position:absolute;
+ top:16px;
+ left:16px;
+ width:115px;
+ padding-top: 10px;
+ padding-bottom: 10px;
+
+ /*background-color: #f9f9ff;*/
+ background: url("img/background.png") repeat-x scroll 0 0 #eaeaea;
+ text-shadow: 1px 1px 0px #FFF;
+
+ box-shadow: 1px 1px 3px #333;
+ -moz-box-shadow: 1px 1px 3px #333;
+ -webkit-box-shadow: 1px 1px 3px #333;
+ -khtml-box-shadow: 1px 1px 3px #333;
+
+ border-radius: 8px 0px 8px 0px;
+ -moz-border-radius: 8px 0px 8px 0px;
+ -webkit-border-radius: 8px 0px 8px 0px;
+ -khtml-border-radius: 8px 0px 8px 0px;
+}
+#menu ul{
+ padding-left:16px;
+ margin:0px;
+}
+#menu li{
+ list-style:none;
+ font-size:9pt;
+ line-height:14px;
+ margin-bottom:5px;
+}
+#menu li a{
+ color:black;
+ text-decoration:none;
+}
+#menu li a:hover{
+ text-decoration:none;
+}
+a.help-link {
+ position:relative;
+}
+a.help-link:hover{ z-index:23; color: #000}
+a.help-link:hover span.tooltip {
+ /* pos */
+ display:block;
+ position:absolute;
+ top:28px; left:32;
+ padding:10px;
+ /* style */
+ text-align: left;
+ white-space: nowrap;
+ color: #fff;
+ background-color: #000;
+ font-size: 9pt;
+ font-weight: normal;
+ text-decoration:none;
+ /* effect */
+ border-radius :8px;
+ -moz-border-radius :8px;
+ -webkit-border-radius:8px;
+ -khtml-border-radius:8px;
+}
+span.tooltip {
+ text-shadow: 0px 0px 0px;
+}
+
+/* the indexes */
+#title {
+ margin:0px 0px 15px 0px;
+}
+#root {
+ font-size:14pt;
+ font-weight:bold;
+ cursor:pointer;
+}
+#root-img {
+ padding-right:5px;
+}
+
+.tooltip {
+ display: none;
+}
+a.flink {
+ position:relative;
+}
+a.flink:hover{ z-index:23; color: #000}
+a.flink:hover span.tooltip{
+ /* pos */
+ display:block;
+ position:absolute;
+ top:28px; left:32;
+ padding:5px;
+ padding-top:2px;
+ padding-bottom:2px;
+ /* style */
+ text-align: left;
+ white-space: nowrap;
+ color: #ccc;
+ background-color: #222;
+ font-size: 9pt;
+ font-weight: bold;
+ text-decoration:none;
+ /* effect */
+ -moz-border-radius :8px;
+ -webkit-border-radius:8px;
+ -khtml-border-radius:8px;
+ filter: alpha(opacity:80);
+ KHTMLOpacity: 0.80;
+ MozOpacity: 0.80;
+ opacity: 0.80;
+}
+
+.pw-html-editor {
+ width: 765px;
+}
+.pw-breadcrumb-trail {
+ font-size: 11px;
+ color: silver;
+ padding-bottom: 5px;
+}
+div.pw-breadcrumb-trail span {
+ padding: 2px;
+ border-bottom : 1px dotted #eee;
+}
+.pw-breadcrumb-trail a{
+ color: silver;
+}
+div.pw-created {
+ font-size: 8pt;
+ font-weight: bold;
+}
+div.pw-edited {
+ font-size: 8pt;
+}
+div.pw-metadata {
+ margin-right: 10px;
+ padding: 5px;
+ border: 1px solid #eee;
+ display:block;
+ max-height: 100px;
+ overflow: scroll;
+}
+span.pw-date {
+ padding-left: 10px;
+}
+
+ul.pw-toc {
+ margin-top: 0px;
+}
+div.pw-content p {
+ margin: 0px; padding: 0px;
+}
+div#pw-history-title {
+ text-align: right;
+ margin-right: 10px;
+}
+.pw-css-button {
+ background: url("img/back-1x36.png") repeat-x scroll 0 0 transparent;
+ padding: 0px 5px 0px 5px;
+ border: 1px solid silver;
+ font-size: 10px;
+ font-weight: bold;
+ text-decoration:none;
+ text-shadow: 1px 1px 0 #fff;
+ border-radius: 4px 4px 4px 4px;
+}
+.warning {
+ font-size: 10px;
+}
+div#content table {
+ width: 100%;
+}
+div#pw-content samp {
+ font-family : monospace;
+ padding : 5px;
+ border: 1px solid black dotted;
+ background-color : #eee;
+ display: block;
+}
+#pw-popup {
+ position : absolute;
+ top: 50px;
+ left: 50px;
+ z-index : 999;
+ background: #fff;
+ padding : 0px;
+ box-shadow: 2px 2px 6px 0px #666;
+ -moz-box-shadow: 2px 2px 6px 0px #666;
+ -webkit-box-shadow: 2px 2px 6px 0px #666;
+ -khtml-box-shadow: 2px 2px 6px 0px #666;
+ border-radius: 8px;
+ -moz-border-radius: 8px;
+ -webkit-border-radius: 8px;
+ -khtml-border-radius: 8px;
+ font-size: 10pt;
+}
+#pw-popup h3 {
+ padding : 10px;
+ padding-top : 7px;
+ background: url("img/back-1x36.png") repeat-x scroll 0 0 transparent;
+ margin-top: 0px;
+ margin-bottom: 0px;
+ font-size: 11pt;
+}
+#pw-popup div {
+ padding : 10px;
+}
+#pw-popup form {
+ padding : 10px;
+ margin: 0px;
+ padding-bootom: 15px;
+}
+#pw-popup form label {
+ float: left;
+ width: 100px;
+ padding-right: 10px;
+ text-align: right;
+}
+#pw-popup {
+ width: 420px;
+}
+#pw-popup input#pw-popup-answer {
+ padding: 5px;
+ font-size: 20px;
+ text-shadow: 2px 2px 2px #DDDDDD;
+}
+#pw-popup-buttons {
+ margin-top: 15px;
+ display: block;
+ text-align : center;
+}
+
+#pw-popup br {
+ clear:both;
+}
+#pw-title {
+ font-size: 20px;
+ font-weight: bold;
+ padding-bottom: 5px;
+ margin-bottom: 5px;
+ text-shadow: 2px 2px 2px #ddd;
+ border-bottom: 1px dotted #eee;
+}
+#pw-close {
+ float: right;
+ margin: 10px;
+}
+/*
+ * CSS for TestPage components
+ */
+#nt-url {
+ width: 500px;
+}
+#m3-js-tree-view {
+ font-size: 10px;
+ overflow: auto;
+}
+#m3-js-tree-view a {
+ font-weight: normal;
+}
+#m3-output {
+ font-size: 10px;
+ font-family: mono;
+ overflow:auto;
+}
+.m3-region {
+ position:relative;
+ padding: 5px;
+ margin-top: 0px;
+ margin-bottom: 5px;
+ background: #fff;
+ border: 1px solid #ddd;
+ border-radius :8px;
+ -moz-border-radius :8px;
+ -webkit-border-radius:8px;
+ -khtml-border-radius:8px;
+}
104 app/test.html
@@ -0,0 +1,104 @@
+<html id="pw-page">
+
+<head>
+
+<title>Node Toy</title>
+<!--#include virtual="/app/inc.html" -->
+<script src="/app/js/jquery.jstree.js"></script>
+<script src="/app/js/nodetoy-test.js"></script>
+<script type="text/javascript">
+ var m3TestPage = new nt.TestPage();
+ jQuery(function() {
+ m3TestPage.init();
+ });
+ </script>
+</head>
+
+<body id="pw-body">
+<div id="container">
+ <div id="header">
+ <span class="title">Node Toy</span>
+ <span class="m3-auth-status"></span>
+ </div>
+ <div id="content">
+ <div id="menu"><div id="pw-popup" style="display:none;position:absolute"></div>
+ <!-- main menu -->
+ <ul>
+ <li><a class="help-link" href="/app/test/index.html">
+ <span class="tooltip">
+ Use this page to test making Ajax requests to the server.
+ </span>
+ Test Page</a>
+ </li>
+ <li><a class="help-link" href="/app/doc.html">
+ <span class="tooltip">
+ Doucmentation.
+ </span>
+ Docs</a>
+ </li>
+ </ul>
+ </div>
+ <div id="main">
+
+<div id="pw-content">
+ <form id="m3-test-form" action="#" onsubmit="m3TestPage.exec(); return false;" class="ui-widget">
+ <div class="ui-widget ui-widget-content ui-corner-all m3-region">
+ <b>URL</b>: <input type="text" name="url" id="nt-url"/>
+ <input type="submit" value="Send request"/>
+ <img src="/app/skin/img/throbber.gif" class="m3-throbber" width="20" height="20"/>
+ </div>
+ <div class="ui-widget ui-widget-content ui-corner-all m3-region">
+ <div id="m3-request-type" class="ui-helper-hidden">
+ <input type="radio" name="httpMethod" id="m3-GET" value="GET"/>GET<br/>
+ <input type="radio" name="httpMethod" id="m3-POST" value="POST"/>POST<br/>
+ <input type="radio" name="httpMethod" id="m3-DELETE" value="DELETE"/>DELETE<br/>
+ </div>
+
+ </div>
+ <div class="ui-widget ui-widget-content ui-corner-all m3-region">
+ <textarea name="data" id="m3-input" cols="60" rows="5"></textarea>
+ <div style="padding-left:5px;padding-right:5px"><span class="m3-light">POST data</span>
+ <a href="#" title="hide" onclick="jQuery('#m3-parameters').hide(); return false;"><span style="float:right" class="ui-icon ui-icon-circle-triangle-n"></span></a>
+ <a href="#" title="show" onclick="jQuery('#m3-parameters').show(); return false;"><span style="float:right" class="ui-icon ui-icon-circle-triangle-s"></span></a>
+ <a href="#" title="clear" onclick="jQuery('#m3-input').val(''); return false;"><span style="float:right" class="ui-icon ui-icon-trash"></span></a>
+ <a href="#" title="reset" onclick="m3TestPage.reset(); return false;"><span style="float:right" class="ui-icon ui-icon-refresh"></span></a>
+ </div>
+ </div>
+ <div class="ui-widget ui-widget-content ui-corner-all m3-region">
+ <input type="submit" value="Send request"/>
+ <img src="/app/skin/img/throbber.gif" class="m3-throbber" width="20" height="20"/>
+ <input type="radio" name="viewType" id="m3-tree" value="tree" checked="true"/>Tree view
+ <input type="radio" name="viewType" id="m3-print" value="print"/>Text
+ <span id="m3-buttons" style="visibility:hidden">
+ <input type="button" id="m3-open-all" value="expand"></input>
+ <input type="button" id="m3-close-all" value="close"></input>
+ <input type="text" id="m3-search-text" value=""></input>
+ <input type="button" id="m3-search" value="search"></input>
+ </span>
+ </div>
+ <div class="ui-widget ui-widget-content ui-corner-all m3-region">
+ <pre id="m3-output"></pre>
+ <div id="m3-js-tree-view"></div>
+ </div>
+ </form>
+
+ <div id="m3-headers"></div>
+
+ <div id="m3-tree-view"></div>
+
+
+
+</div><!--pw-content-->
+
+ </div>
+ </div>
+ <div id="footer">
+ nodetoy from riereta.net
+ </div>
+
+</div>
+
+</body>
+
+</html>
+
51 app/todo.html
@@ -0,0 +1,51 @@
+<html id="pw-page">
+
+<head>
+
+<title>TODO</title>
+
+<!--#include virtual="/app/inc.html" -->
+<script src="/app/js/todo.js"></script>
+<script type="text/javascript">
+ var todo = new nt.Todo();
+ jQuery(function() {
+ todo.init();
+ });
+</script>
+<style>
+body {
+ font-size: 9pt;
+}
+.section{
+ margin-left: 5px;
+}
+.section div{
+ margin-bottom: 5px;
+}
+a.del {
+ border: 1px solid silver;
+ border-radius: 5px;
+ padding-left: 4px;
+ padding-right: 4px;
+ background : url("/app/skin/img/back-1x36.png") repeat-x 0 0 transparent;
+}
+h3 {
+ text-shadow: 1px 1px 0 #EEEEEE;
+ padding: 5px;
+ background : url("/app/skin/img/back-1x36.png") repeat-x 0 0 transparent;
+}
+</style>
+
+</head>
+
+<body>
+
+<div id="nt-messages">&nbsp;</div>
+
+<div id="pw-title">TODO Lists</div>
+
+<div id="nt-todos"></div>
+
+</body>
+
+</html>
10 bin/installdeps
@@ -0,0 +1,10 @@
+#!/bin/bash
+#
+#
+#
+cd `dirname $0`
+cd ..
+
+npm install filter-chain
+npm install sax
+npm install dom-js
6 bin/start
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+cd `dirname $0`
+cd ../server
+node start
+
12 bin/stop
@@ -0,0 +1,12 @@
+#!/bin/bash
+#
+# Stop the server
+#
+
+if [ -f /tmp/nodetoy.sock ] ; then
+ echo EXIT | nc -U /tmp/nodetoy.sock
+ if [ $? -eq 0 ] ; then
+ rm /tmp/nodetoy.sock
+ fi
+fi
+
17 conf/config.xml
@@ -0,0 +1,17 @@
+<config>
+ <!--
+ The port on which the HTTP server listens.
+ -->
+ <port>8023</port>
+
+ <!--
+ Base directory for the JavaScript files used by the GUI frontend.
+ -->
+ <basedir>../app</basedir>
+
+ <!--
+ Base directory for JSON files
+ -->
+ <datadir>../data</datadir>
+
+</config>
7 conf/ssi-environment.xml
@@ -0,0 +1,7 @@
+<env>
+
+ <serverName> &amp;laquo; Node Toy &amp;raquo; </serverName>
+
+ <copyRight&amp;laquo; &amp;copy; riereta.net - Node Toy Server - powered by node.js &amp;raquo; ·</copyRight>
+
+</env>
5 data/Abdul.json
@@ -0,0 +1,5 @@
+{
+ "name": "Abdul Pallarés",
+ "handle": "abdulet",
+ "bio": "Puttin the freak in friki since 1999"
+}
5 data/Teknopaul.json
@@ -0,0 +1,5 @@
+{
+ "name": "Paul Hinds",
+ "handle": "teknopaul",
+ "bio": "techie"
+}
20 data/todos/life.json
@@ -0,0 +1,20 @@
+[
+ "DONE DONE DONE DONE DONE DONE oioi",
+ "get one",
+ "get out more",
+ "DONE oioi",
+ "DONE oioi",
+ "DONE 2323",
+ "DONE 232",
+ "DONE oioi",
+ "DONE oioi",
+ "DONE oioi",
+ "DONE oioi",
+ "DONE oioi",
+ "oioi",
+ "DONE DONE DONE DONE DONE oio",
+ "DONE DONE DONE DONE oi",
+ "DONE DONE DONE o",
+ "DONE DONE ",
+ "DONE undefined"
+]
1 data/todos/shopping.json
@@ -0,0 +1 @@
+[ "fish", "chips" , "peas", "gravy" ]
5 data/todos/work.json
@@ -0,0 +1,5 @@
+[
+ "css styling",
+ "open in side bar",
+ "add to github"
+]
53 server/http_mods/app.js
@@ -0,0 +1,53 @@
+var path = require('path');
+var fs = require('fs');
+
+var defaults = require('./default');
+var resolve = require('../persistence/file-resolve');
+var ssiParser = require('../ssi/ssi-parser');
+var ssi = require('../ssi/ssi-handler');
+/**
+ * Stream a named file to the response.
+ */
+function doGet(request, response, url) {
+
+ resolve.resolveApp(url.pathname, true, function(fileSystemPath) {
+
+ // open the file
+ var instream = fs.createReadStream(fileSystemPath , { flags: 'r' });
+
+ instream.on('error', function() {
+ // TODO this is not correct FNF should be detected some other way
+ defaults.fileNotFound(response);
+ });
+
+ // set default HTTP headers
+ response.statusCode = 200;
+ var mime = defaults.mimeMagic(response, url.pathname);
+ response.setHeader("Content-Type", mime);
+ defaults.addNoCacheHeaders(response);
+
+ // If it is HTML parse SSI
+ if (mime == "text/html") {
+
+ response.setHeader("Content-Type", "text/html;charset=utf-8");
+
+ // set up SSI
+ var handler = new ssi.SsiHandler();
+ var parser = new ssiParser.Parser("Http Server" , instream, response, handler);
+
+ // exec
+ parser.exec(function(message) {
+ response.end();
+ });
+ }
+ // otherwise stream the response as is.
+ else {
+ instream.pipe(response);
+ }
+
+ });
+
+};
+
+
+exports.doGet = doGet;
37 server/http_mods/config.js
@@ -0,0 +1,37 @@
+var path = require('path');
+var fs = require('fs');
+var util = require('util');
+
+var defaults = require('./default');
+var config = require('../util/config.js').configData;
+
+/**
+ * Return the Table Of Contents as a fragment of HTML for AJAX requests.
+ */
+function doGet(request, response, url) {
+
+ try {
+
+ defaults.addNoCacheHeaders(response);
+
+ var configJson = JSON.stringify(config);
+
+ response.writeHead(200, "OK", {
+ "Content-Type" : "application/json",
+ "Content-Length" : "" + Buffer.byteLength(configJson, 'utf-8')
+ });
+ response.write(configJson);
+ response.end();
+
+ }
+ catch(err) {
+
+ console.error("Error sendig config : " + url.pathname + " " + err);
+ response.writeHead(400, "OK");
+ response.end();
+
+ }
+
+};
+
+exports.doGet = doGet;
119 server/http_mods/default.js
@@ -0,0 +1,119 @@
+var fs = require('fs');
+
+var date = require("../util/date.js");
+var ssiUtil = require("../ssi/ssi-util.js");
+
+/**
+ * Static functions for adding standard headers and standard responses.
+ *
+ * Also includes mime magic.
+ *
+ * @param response
+ */
+
+function addDefaultHtmlHeaders(response) {
+ response.setHeader("Content-Type", "text/html;charset=utf8");
+};
+
+function addNoCacheHeaders(response) {
+ response.setHeader("Cache-control", "no-cache");
+ response.setHeader("Expires", "Fri, 10 Jun 2011 23:23:23 GMT");
+};
+
+function addCache1DayHeaders(response) {
+ response.setHeader("Cache-control", "public");
+ var expires = new Date();
+ expires.setTime(expires.getTime() + (1000 * 60 * 60 * 24));
+ response.setHeader("Expires", date.toHttpDate(expires));
+};
+
+function mimeMagic(response, pathname) {
+ if (pathname.lastIndexOf(".html") == pathname.length - 5) {
+ response.setHeader("Content-Type", "text/html;charset=utf8");
+ return "text/html";
+ }
+ else if (pathname.lastIndexOf(".htm") == pathname.length - 4) {
+ response.setHeader("Content-Type", "text/html;charset=utf8");
+ return "text/html";
+ }
+ else if (pathname.lastIndexOf(".shtml") == pathname.length - 6) {
+ response.setHeader("Content-Type", "text/html;charset=utf8");
+ return "text/html";
+ }
+ else if (pathname.lastIndexOf(".css") == pathname.length - 4) {
+ response.setHeader("Content-Type", "text/css");
+ return "text/css";
+ }
+ else if (pathname.lastIndexOf(".js") == pathname.length - 3) {
+ response.setHeader("Content-Type", "text/javascript");
+ return "text/javascript";
+ }
+ else if (pathname.lastIndexOf(".png") == pathname.length - 4) {
+ response.setHeader("Content-Type", "image/png");
+ return "image/png";
+ }
+ else if (pathname.lastIndexOf(".gif") == pathname.length - 4) {
+ response.setHeader("Content-Type", "image/gif");
+ return "image/gif";
+ }
+ else if (pathname.lastIndexOf(".jpg") == pathname.length - 4) {
+ response.setHeader("Content-Type", "image/jpeg");
+ return "image/jpeg";
+ }
+ else if (pathname.lastIndexOf(".jpeg") == pathname.length - 4) {
+ response.setHeader("Content-Type", "image/jpeg");
+ return "image/jpeg";
+ }
+ else if (pathname.lastIndexOf(".json") == pathname.length - 5) {
+ response.setHeader("Content-Type", "application/json");
+ return "application/json";
+ }
+ else {
+ response.setHeader("Content-Type", "text/plain");