Separated QCore into a separate .js file -- should probably go into a lib directory. index.html favicon.png changed to /favicon.png. Added tmp directory creation.
							parent
							
								
									fcfe8aac45
								
							
						
					
					
						commit
						2144e6d877
					
				| 
						 | 
					@ -0,0 +1,261 @@
 | 
				
			||||||
 | 
					/*********************************
 | 
				
			||||||
 | 
					 * QCore                         *
 | 
				
			||||||
 | 
					 *********************************/
 | 
				
			||||||
 | 
					var http = require('http');
 | 
				
			||||||
 | 
					var fs = require('fs');
 | 
				
			||||||
 | 
					var m_path = require('path');
 | 
				
			||||||
 | 
					var formidable = require('formidable');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var QCore = function() {
 | 
				
			||||||
 | 
					  var acts = {};
 | 
				
			||||||
 | 
					  this.acts = acts;
 | 
				
			||||||
 | 
					  self = this;
 | 
				
			||||||
 | 
					  this.mimetypes = {};
 | 
				
			||||||
 | 
					  this.index = null;
 | 
				
			||||||
 | 
					  this.index_parts = [];
 | 
				
			||||||
 | 
					  this.last_part = { start: 0, end: 0 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this.etags = {}; // etags cache, obj: uri { etag: '...', timestamp: }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this.res_cache = {}; // resource cache ('/view.png' => '03xc345');
 | 
				
			||||||
 | 
					  this.rules = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this.http = http.createServer(function(req, res) {
 | 
				
			||||||
 | 
					    var last = req.url.lastIndexOf("/");
 | 
				
			||||||
 | 
					    var act = req.url.substr(last+1);
 | 
				
			||||||
 | 
					    if (acts[act]) {
 | 
				
			||||||
 | 
					      req.url = req.url.substr(0, last); // rewrite the url without the act
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      act = "";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    req.fields = {};
 | 
				
			||||||
 | 
					    req.files = {};
 | 
				
			||||||
 | 
					    if (req.method == 'POST') {
 | 
				
			||||||
 | 
					      var form = new formidable.IncomingForm();
 | 
				
			||||||
 | 
					      form.multiples = true;
 | 
				
			||||||
 | 
					      form.uploadDir = process.cwd()+'/tmp';
 | 
				
			||||||
 | 
					      form.parse(req, function(err, fields, files) {
 | 
				
			||||||
 | 
					        // TODO: handle err
 | 
				
			||||||
 | 
					        req.fields = fields;
 | 
				
			||||||
 | 
					        req.files = files;
 | 
				
			||||||
 | 
					        for (var i = 0, keys = Object.keys(acts[act]); i < keys.length; i++) {
 | 
				
			||||||
 | 
					          acts[act][i](req, res);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      for (var i = 0, keys = Object.keys(acts[act]); i < keys.length; i++) {
 | 
				
			||||||
 | 
					        acts[act][i](req, res);
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  this.act = function(act, callback) {
 | 
				
			||||||
 | 
					    if (typeof acts[act] === 'undefined') {
 | 
				
			||||||
 | 
					      acts[act] = [];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    acts[act].push(callback);
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  this.rule = function(zone, replace, callback) {
 | 
				
			||||||
 | 
					    if (typeof this.rules[zone] === 'undefined') {
 | 
				
			||||||
 | 
					      this.rules[zone] = {};
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    this.rules[zone][replace] = callback;
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  var fs_timeout; // timer to bypass multiple fs.watch triggering due to unstable API
 | 
				
			||||||
 | 
					  this.loadIndex = function(event, filename) {
 | 
				
			||||||
 | 
					    if (fs_timeout) return;
 | 
				
			||||||
 | 
					    fs_timeout = setTimeout(function() { fs_timeout = null; }, 100);
 | 
				
			||||||
 | 
					    var reg = /@@[a-z]+@@/ig;
 | 
				
			||||||
 | 
					    fs.readFile('index.html', function(err, data) {
 | 
				
			||||||
 | 
					      if (err) throw err;
 | 
				
			||||||
 | 
					      self.index = data;
 | 
				
			||||||
 | 
					      var search;
 | 
				
			||||||
 | 
					      self.index_parts = [];
 | 
				
			||||||
 | 
					      while ((search = reg.exec(data)) !== null) {
 | 
				
			||||||
 | 
					        self.index_parts.push({ replace: search[0], start: search.index, end: reg.lastIndex });
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  // TODO: loadActs should allow for dynamic updates by using fs.watch and clearing the require cache
 | 
				
			||||||
 | 
					  this.loadActs = function(cb) {
 | 
				
			||||||
 | 
					    var self = this;
 | 
				
			||||||
 | 
					    fs.readdir('acts', function(err, files) {
 | 
				
			||||||
 | 
					      if (err) throw err;
 | 
				
			||||||
 | 
					      for (var i = 0; i < files.length; i++) {
 | 
				
			||||||
 | 
					        var ext = self.getExt(files[i]);
 | 
				
			||||||
 | 
					        if (ext == 'js') {
 | 
				
			||||||
 | 
					          console.log('Loading act: ' + files[i]);
 | 
				
			||||||
 | 
					          require('./acts/'+files[i])(self);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      (typeof cb !== 'undefined' ? cb() : '');
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  fs.watch('index.html', this.loadIndex);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.defaults = {};
 | 
				
			||||||
 | 
					QCore.prototype.listen = function(port, cb) {
 | 
				
			||||||
 | 
					  this.loadActs();
 | 
				
			||||||
 | 
					  this.loadIndex();
 | 
				
			||||||
 | 
					  if (typeof cb !== 'undefined') cb();
 | 
				
			||||||
 | 
					  try { 
 | 
				
			||||||
 | 
					    this.http.listen(port);
 | 
				
			||||||
 | 
					  } catch(err) {
 | 
				
			||||||
 | 
					    console.log('uhoh, could not listen on port '+port);
 | 
				
			||||||
 | 
					    console.log(err);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.setDefault = function(key, value) {
 | 
				
			||||||
 | 
					  this.defaults[key] = value;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.getDefault = function(key) {
 | 
				
			||||||
 | 
					  if (key in this.defaults) {
 | 
				
			||||||
 | 
					    return this.defaults[key];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return '';
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.addMIMEtype = function(ext, mimetype) {
 | 
				
			||||||
 | 
					  if (typeof this.mimetypes[ext] === 'undefined') this.mimetypes[ext] = [];
 | 
				
			||||||
 | 
					  this.mimetypes[ext].push(mimetype);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.getMIMEtype = function(ext) {
 | 
				
			||||||
 | 
					  if (this.mimetypes[ext]) return this.mimetypes[ext][0];
 | 
				
			||||||
 | 
					  return '';
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.parsePage = function(zone, fallback_zone, req, res) {
 | 
				
			||||||
 | 
					  var qparse = new QCore.prototype.QParse();
 | 
				
			||||||
 | 
					  qparse.indices = this.index_parts.slice(); // clone it TODO: just make an indices (number-only) array
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var context = this;
 | 
				
			||||||
 | 
					  var cb = function(qparse, callback) {
 | 
				
			||||||
 | 
					    res.write(context.index.slice(qparse.offset, qparse.current.start));
 | 
				
			||||||
 | 
					    if (typeof context.rules[zone] === 'undefined') { zone = fallback_zone; }
 | 
				
			||||||
 | 
					    if (typeof context.rules[zone] === 'undefined') { zone = ''; }
 | 
				
			||||||
 | 
					    if (typeof context.rules[zone] !== 'undefined') {
 | 
				
			||||||
 | 
					      if (typeof context.rules[zone][qparse.current.replace] !== 'undefined') {
 | 
				
			||||||
 | 
					        context.rules[zone][qparse.current.replace](req, res, qparse, callback);
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        // fallback action
 | 
				
			||||||
 | 
					        if (typeof context.rules[fallback_zone][qparse.current.replace] !== 'undefined') {
 | 
				
			||||||
 | 
					          context.rules[fallback_zone][qparse.current.replace](req, res, qparse, callback);
 | 
				
			||||||
 | 
					        // default action
 | 
				
			||||||
 | 
					        } else if (typeof context.rules[''][qparse.current.replace] !== 'undefined') {
 | 
				
			||||||
 | 
					          context.rules[''][qparse.current.replace](req, res, qparse, callback);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          callback();
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  var series = function(qparse) {
 | 
				
			||||||
 | 
					    if (qparse.current) {
 | 
				
			||||||
 | 
					      cb(qparse, function(next) {
 | 
				
			||||||
 | 
					        if (qparse.next) {
 | 
				
			||||||
 | 
					          res.write(context.index.slice(qparse.current.end, qparse.next.start));
 | 
				
			||||||
 | 
					          qparse.offset = qparse.next.end;
 | 
				
			||||||
 | 
					          qparse.current = qparse.indices.shift();
 | 
				
			||||||
 | 
					          qparse.next = qparse.indices[0];
 | 
				
			||||||
 | 
					          series(qparse);
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          res.write(context.index.slice(qparse.current.end));
 | 
				
			||||||
 | 
					          qparse.current = qparse.indices.shift();
 | 
				
			||||||
 | 
					          qparse.next = qparse.indices[0];
 | 
				
			||||||
 | 
					          series(qparse);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      res.end();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  };
 | 
				
			||||||
 | 
					  qparse.current = qparse.indices.shift();
 | 
				
			||||||
 | 
					  qparse.next = qparse.indices[0];
 | 
				
			||||||
 | 
					  series(qparse);
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					QCore.prototype.readFile = function(stream, path, cb) {
 | 
				
			||||||
 | 
					  fs.exists(path, function(exists) {
 | 
				
			||||||
 | 
					    if (exists) {
 | 
				
			||||||
 | 
					      var rs = fs.createReadStream(path);
 | 
				
			||||||
 | 
					      rs.on('data', function(chunk) {
 | 
				
			||||||
 | 
					        stream.write(chunk);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      rs.on('end', function() {
 | 
				
			||||||
 | 
					        cb('EOF');
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					      rs.on('error', function(err) {
 | 
				
			||||||
 | 
					        cb('ERR', err);
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      cb('FNF'); // file not found
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.readFiles = function(dir, callback) {
 | 
				
			||||||
 | 
					  fs.readdir(dir, function(err, files) {
 | 
				
			||||||
 | 
					    files.forEach(function(file) {
 | 
				
			||||||
 | 
					      var stat_file = dir+'/'+file
 | 
				
			||||||
 | 
					      fs.stat(stat_file, function(err, stats) {
 | 
				
			||||||
 | 
					        if (stats.isDirectory()) {
 | 
				
			||||||
 | 
					          QCore.prototype.readFiles(stat_file, callback);
 | 
				
			||||||
 | 
					          callback(err, stat_file, true);
 | 
				
			||||||
 | 
					        } else if (stats.isFile()) {
 | 
				
			||||||
 | 
					          callback(err, stat_file, false);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.readDirectory = function(dir, callback) {
 | 
				
			||||||
 | 
					  var all_files = [];
 | 
				
			||||||
 | 
					  fs.readdir(dir, function(err, files) {
 | 
				
			||||||
 | 
					    if (err) {
 | 
				
			||||||
 | 
					      callback(err, null);
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    var len = files.length;
 | 
				
			||||||
 | 
					    var total = 0;
 | 
				
			||||||
 | 
					    if (len == 0) callback(err, all_files);
 | 
				
			||||||
 | 
					    files.forEach(function(file, index) {
 | 
				
			||||||
 | 
					      var stat_file = dir + '/' + file;
 | 
				
			||||||
 | 
					      fs.stat(stat_file, function(err, stats) {
 | 
				
			||||||
 | 
					        if (stats.isDirectory()) {
 | 
				
			||||||
 | 
					          all_files.push({'is_dir': true, 'name': file});
 | 
				
			||||||
 | 
					        } else if (stats.isFile()) {
 | 
				
			||||||
 | 
					          all_files.push({'is_dir': false, 'name': file});
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        if (++total == len) {
 | 
				
			||||||
 | 
					          callback(err, all_files);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      });
 | 
				
			||||||
 | 
					    });
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.getExt = function(file) {
 | 
				
			||||||
 | 
					  if (typeof file === 'undefined') return '';
 | 
				
			||||||
 | 
					  var last = file.lastIndexOf(".");
 | 
				
			||||||
 | 
					  if (last == -1) return '';
 | 
				
			||||||
 | 
					  var ext = file.substr(last+1);
 | 
				
			||||||
 | 
					  return ext;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					QCore.prototype.r_mkdir = function(dir, mode, cb) {
 | 
				
			||||||
 | 
					  console.log('creating '+dir);
 | 
				
			||||||
 | 
					  fs.mkdir(dir, mode, function(err) {
 | 
				
			||||||
 | 
					    if (err && err.code == 'ENOENT') {
 | 
				
			||||||
 | 
					      QCore.r_mkdir(m_path.dirname(dir), mode, QCore.r_mkdir.bind(this, dir, mode, cb));
 | 
				
			||||||
 | 
					    } else if (typeof cb !== 'undefined') {
 | 
				
			||||||
 | 
					      cb(err);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					/*********************************
 | 
				
			||||||
 | 
					 * QParse                         *
 | 
				
			||||||
 | 
					 *********************************/
 | 
				
			||||||
 | 
					QCore.prototype.QParse = function() {};
 | 
				
			||||||
 | 
					QCore.prototype.QParse.prototype.indices = {};
 | 
				
			||||||
 | 
					QCore.prototype.QParse.prototype.offset = 0;
 | 
				
			||||||
 | 
					QCore.prototype.QParse.prototype.current = null;
 | 
				
			||||||
 | 
					QCore.prototype.QParse.prototype.next = null;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module.exports = QCore;
 | 
				
			||||||
| 
						 | 
					@ -1,7 +1,7 @@
 | 
				
			||||||
<!DOCTYPE html>
 | 
					<!DOCTYPE html>
 | 
				
			||||||
<html smlns="http://www.w3.org/1999/xhtml" lang="en-US">
 | 
					<html smlns="http://www.w3.org/1999/xhtml" lang="en-US">
 | 
				
			||||||
  <head>
 | 
					  <head>
 | 
				
			||||||
    <link rel="icon" type="image/png" href="favicon.png">
 | 
					    <link rel="icon" type="image/png" href="/favicon.png">
 | 
				
			||||||
    <title>qwiki @@PAGE@@</title>
 | 
					    <title>qwiki @@PAGE@@</title>
 | 
				
			||||||
    <meta charset="UTF-8" />
 | 
					    <meta charset="UTF-8" />
 | 
				
			||||||
    <link href='https://fonts.googleapis.com/css?family=Oswald|Roboto+Slab:300,700|Roboto+Mono' rel='stylesheet' type='text/css'>
 | 
					    <link href='https://fonts.googleapis.com/css?family=Oswald|Roboto+Slab:300,700|Roboto+Mono' rel='stylesheet' type='text/css'>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										322
									
								
								index.js
								
								
								
								
							
							
						
						
									
										322
									
								
								index.js
								
								
								
								
							| 
						 | 
					@ -1,133 +1,47 @@
 | 
				
			||||||
#!/usr/bin/env node
 | 
					#!/usr/bin/env node
 | 
				
			||||||
var http = require('http');
 | 
					 | 
				
			||||||
var fs = require('fs');
 | 
					var fs = require('fs');
 | 
				
			||||||
var m_path = require('path');
 | 
					var m_path = require('path');
 | 
				
			||||||
var util = require('util');
 | 
					var util = require('util');
 | 
				
			||||||
var formidable = require('formidable');
 | 
					var QCore = require('./QCore.js');
 | 
				
			||||||
/* qwiki
 | 
					/* qwiki
 | 
				
			||||||
Node.js based Implementation of the proposed qwiki document
 | 
					Node.js based Implementation of the proposed qwiki document
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
/********************************* 
 | 
					/********************************* 
 | 
				
			||||||
 * QCore                         *
 | 
					 * QWiki                         *
 | 
				
			||||||
 *********************************/
 | 
					 *********************************/
 | 
				
			||||||
var QCore = function() {
 | 
					var QWiki = function() {
 | 
				
			||||||
  var acts = {};
 | 
					  QCore.call(this);
 | 
				
			||||||
  this.acts = acts;
 | 
					  // make cache directory
 | 
				
			||||||
  self = this;
 | 
					 | 
				
			||||||
  this.mimetypes = {};
 | 
					 | 
				
			||||||
  this.index = null;
 | 
					 | 
				
			||||||
  this.index_parts = [];
 | 
					 | 
				
			||||||
  this.last_part = { start: 0, end: 0 };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  this.etags = {}; // etags cache, obj: uri { etag: '...', timestamp: }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  this.res_cache = {}; // resource cache ('/view.png' => '03xc345');
 | 
					 | 
				
			||||||
  this.rules = {};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  this.http = http.createServer(function(req, res) {
 | 
					 | 
				
			||||||
    var last = req.url.lastIndexOf("/");
 | 
					 | 
				
			||||||
    var act = req.url.substr(last+1);
 | 
					 | 
				
			||||||
    if (acts[act]) {
 | 
					 | 
				
			||||||
      req.url = req.url.substr(0, last); // rewrite the url without the act
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      act = "";
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    req.fields = {};
 | 
					 | 
				
			||||||
    req.files = {};
 | 
					 | 
				
			||||||
    if (req.method == 'POST') {
 | 
					 | 
				
			||||||
      var form = new formidable.IncomingForm();
 | 
					 | 
				
			||||||
      form.multiples = true;
 | 
					 | 
				
			||||||
      form.uploadDir = process.cwd()+'/tmp';
 | 
					 | 
				
			||||||
      form.parse(req, function(err, fields, files) {
 | 
					 | 
				
			||||||
        // TODO: handle err
 | 
					 | 
				
			||||||
        req.fields = fields;
 | 
					 | 
				
			||||||
        req.files = files;
 | 
					 | 
				
			||||||
        for (var i = 0, keys = Object.keys(acts[act]); i < keys.length; i++) {
 | 
					 | 
				
			||||||
          acts[act][i](req, res);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      for (var i = 0, keys = Object.keys(acts[act]); i < keys.length; i++) {
 | 
					 | 
				
			||||||
        acts[act][i](req, res);
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  this.act = function(act, callback) {
 | 
					 | 
				
			||||||
    if (typeof acts[act] === 'undefined') {
 | 
					 | 
				
			||||||
      acts[act] = [];
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    acts[act].push(callback);
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  this.rule = function(zone, replace, callback) {
 | 
					 | 
				
			||||||
    if (typeof this.rules[zone] === 'undefined') {
 | 
					 | 
				
			||||||
      this.rules[zone] = {};
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    this.rules[zone][replace] = callback;
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  var fs_timeout; // timer to bypass multiple fs.watch triggering due to unstable API
 | 
					 | 
				
			||||||
  this.loadIndex = function(event, filename) {
 | 
					 | 
				
			||||||
    if (fs_timeout) return;
 | 
					 | 
				
			||||||
    fs_timeout = setTimeout(function() { fs_timeout = null; }, 100);
 | 
					 | 
				
			||||||
    var reg = /@@[a-z]+@@/ig;
 | 
					 | 
				
			||||||
    fs.readFile('index.html', function(err, data) {
 | 
					 | 
				
			||||||
      if (err) throw err;
 | 
					 | 
				
			||||||
      self.index = data;
 | 
					 | 
				
			||||||
      var search;
 | 
					 | 
				
			||||||
      self.index_parts = [];
 | 
					 | 
				
			||||||
      while ((search = reg.exec(data)) !== null) {
 | 
					 | 
				
			||||||
        self.index_parts.push({ replace: search[0], start: search.index, end: reg.lastIndex });
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  // TODO: loadActs should allow for dynamic updates by using fs.watch and clearing the require cache
 | 
					 | 
				
			||||||
  this.loadActs = function(cb) {
 | 
					 | 
				
			||||||
    var self = this;
 | 
					 | 
				
			||||||
    fs.readdir('acts', function(err, files) {
 | 
					 | 
				
			||||||
      if (err) throw err;
 | 
					 | 
				
			||||||
      for (var i = 0; i < files.length; i++) {
 | 
					 | 
				
			||||||
        var ext = self.getExt(files[i]);
 | 
					 | 
				
			||||||
        if (ext == 'js') {
 | 
					 | 
				
			||||||
          console.log('Loading act: ' + files[i]);
 | 
					 | 
				
			||||||
          require('./acts/'+files[i])(self);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      (typeof cb !== 'undefined' ? cb() : '');
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  fs.watch('index.html', this.loadIndex);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
QCore.prototype.defaults = {};
 | 
					 | 
				
			||||||
QCore.prototype.listen = function(port) {
 | 
					 | 
				
			||||||
  this.loadActs();
 | 
					 | 
				
			||||||
  this.loadIndex();
 | 
					 | 
				
			||||||
  this.loadWikiIndex();
 | 
					 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    this.http.listen(port);
 | 
					    var stats = fs.statSync('cache');
 | 
				
			||||||
 | 
					    if (!stats.isDirectory()) {
 | 
				
			||||||
 | 
					      console.log('error, "cache" is not a directory');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
  } catch (err) {
 | 
					  } catch (err) {
 | 
				
			||||||
    console.log('uhoh, could not listen on port '+port);
 | 
					    if (err.code == 'ENOENT') {
 | 
				
			||||||
    console.log(err);
 | 
					      fs.mkdirSync('cache');
 | 
				
			||||||
 | 
					      console.log('Created "cache/" -- this is where processed wiki pages are stored as HTML');
 | 
				
			||||||
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.log('error while loading cache');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
};
 | 
					 | 
				
			||||||
QCore.prototype.setDefault = function(key, value) {
 | 
					 | 
				
			||||||
  this.defaults[key] = value;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
QCore.prototype.getDefault = function(key) {
 | 
					 | 
				
			||||||
  if (key in this.defaults) {
 | 
					 | 
				
			||||||
    return this.defaults[key];
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return '';
 | 
					  // make tmp directory
 | 
				
			||||||
};
 | 
					  try {
 | 
				
			||||||
QCore.prototype.addMIMEtype = function(ext, mimetype) {
 | 
					    var stats = fs.statSync('tmp');
 | 
				
			||||||
  if (typeof this.mimetypes[ext] === 'undefined') this.mimetypes[ext] = [];
 | 
					    if (!stats.isDirectory()) {
 | 
				
			||||||
  this.mimetypes[ext].push(mimetype);
 | 
					      console.log('error, "tmp" is not a directory');
 | 
				
			||||||
};
 | 
					    }
 | 
				
			||||||
QCore.prototype.getMIMEtype = function(ext) {
 | 
					  } catch (err) {
 | 
				
			||||||
  if (this.mimetypes[ext]) return this.mimetypes[ext][0];
 | 
					    if (err.code == 'ENOENT') {
 | 
				
			||||||
  return '';
 | 
					      fs.mkdirSync('tmp');
 | 
				
			||||||
};
 | 
					      console.log('Created "tmp/" -- this is where uploaded files are temporarily stored');
 | 
				
			||||||
QCore.prototype.loadWikiIndex = function() {
 | 
					    } else {
 | 
				
			||||||
 | 
					      console.log('error while loading tmp directory');
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}; util.inherits(QWiki, QCore);
 | 
				
			||||||
 | 
					QWiki.prototype.loadWikiIndex = function() {
 | 
				
			||||||
  var self = this;
 | 
					  var self = this;
 | 
				
			||||||
  try {
 | 
					  try {
 | 
				
			||||||
    this.wiki_index = JSON.parse(fs.readFileSync('index.json'));
 | 
					    this.wiki_index = JSON.parse(fs.readFileSync('index.json'));
 | 
				
			||||||
| 
						 | 
					@ -137,7 +51,7 @@ QCore.prototype.loadWikiIndex = function() {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  var wiki_index = this.wiki_index;
 | 
					  var wiki_index = this.wiki_index;
 | 
				
			||||||
  console.log('loaded indices for ' + Object.keys(this.wiki_index.pages).length + ' pages');
 | 
					  console.log('loaded indices for ' + Object.keys(this.wiki_index.pages).length + ' pages');
 | 
				
			||||||
  readFiles('wiki', function(err, file, is_dir) {
 | 
					  QCore.prototype.readFiles('wiki', function(err, file, is_dir) {
 | 
				
			||||||
    var wiki_file = file.replace('wiki/', '');
 | 
					    var wiki_file = file.replace('wiki/', '');
 | 
				
			||||||
    if (is_dir) {
 | 
					    if (is_dir) {
 | 
				
			||||||
      if (wiki_file in wiki_index.pages) {
 | 
					      if (wiki_file in wiki_index.pages) {
 | 
				
			||||||
| 
						 | 
					@ -146,7 +60,7 @@ QCore.prototype.loadWikiIndex = function() {
 | 
				
			||||||
        wiki_index.pages[wiki_file] = { dir: true };
 | 
					        wiki_index.pages[wiki_file] = { dir: true };
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    } else {
 | 
					    } else {
 | 
				
			||||||
      if (self.getExt(file) == 'qwk') {
 | 
					      if (QCore.prototype.getExt(file) == 'qwk') {
 | 
				
			||||||
        if (wiki_file in wiki_index.pages) {
 | 
					        if (wiki_file in wiki_index.pages) {
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          var name = wiki_file.slice(0, -4);
 | 
					          var name = wiki_file.slice(0, -4);
 | 
				
			||||||
| 
						 | 
					@ -161,83 +75,6 @@ QCore.prototype.loadWikiIndex = function() {
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
QCore.prototype.parsePage = function(zone, fallback_zone, req, res) {
 | 
					 | 
				
			||||||
  var qparse = new QParse();
 | 
					 | 
				
			||||||
  qparse.indices = this.index_parts.slice(); // clone it TODO: just make an indices (number-only) array
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var context = this;
 | 
					 | 
				
			||||||
  var cb = function(qparse, callback) {
 | 
					 | 
				
			||||||
    res.write(context.index.slice(qparse.offset, qparse.current.start));
 | 
					 | 
				
			||||||
    if (typeof context.rules[zone] === 'undefined') { zone = fallback_zone; }
 | 
					 | 
				
			||||||
    if (typeof context.rules[zone] === 'undefined') { zone = ''; }
 | 
					 | 
				
			||||||
    if (typeof context.rules[zone] !== 'undefined') {
 | 
					 | 
				
			||||||
      if (typeof context.rules[zone][qparse.current.replace] !== 'undefined') {
 | 
					 | 
				
			||||||
        context.rules[zone][qparse.current.replace](req, res, qparse, callback);
 | 
					 | 
				
			||||||
      } else {
 | 
					 | 
				
			||||||
        // fallback action
 | 
					 | 
				
			||||||
        if (typeof context.rules[fallback_zone][qparse.current.replace] !== 'undefined') {
 | 
					 | 
				
			||||||
          context.rules[fallback_zone][qparse.current.replace](req, res, qparse, callback);
 | 
					 | 
				
			||||||
        // default action
 | 
					 | 
				
			||||||
        } else if (typeof context.rules[''][qparse.current.replace] !== 'undefined') {
 | 
					 | 
				
			||||||
          context.rules[''][qparse.current.replace](req, res, qparse, callback);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          callback();
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  var series = function(qparse) {
 | 
					 | 
				
			||||||
    if (qparse.current) {
 | 
					 | 
				
			||||||
      cb(qparse, function(next) {
 | 
					 | 
				
			||||||
        if (qparse.next) {
 | 
					 | 
				
			||||||
          res.write(context.index.slice(qparse.current.end, qparse.next.start));
 | 
					 | 
				
			||||||
          qparse.offset = qparse.next.end;
 | 
					 | 
				
			||||||
          qparse.current = qparse.indices.shift();
 | 
					 | 
				
			||||||
          qparse.next = qparse.indices[0];
 | 
					 | 
				
			||||||
          series(qparse);
 | 
					 | 
				
			||||||
        } else {
 | 
					 | 
				
			||||||
          res.write(context.index.slice(qparse.current.end));
 | 
					 | 
				
			||||||
          qparse.current = qparse.indices.shift();
 | 
					 | 
				
			||||||
          qparse.next = qparse.indices[0];
 | 
					 | 
				
			||||||
          series(qparse);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      res.end();
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  };
 | 
					 | 
				
			||||||
  qparse.current = qparse.indices.shift();
 | 
					 | 
				
			||||||
  qparse.next = qparse.indices[0];
 | 
					 | 
				
			||||||
  series(qparse);
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
/*********************************
 | 
					 | 
				
			||||||
 * QParse                         *
 | 
					 | 
				
			||||||
 *********************************/
 | 
					 | 
				
			||||||
var QParse = function() {};
 | 
					 | 
				
			||||||
QParse.prototype.indices = {};
 | 
					 | 
				
			||||||
QParse.prototype.offset = 0;
 | 
					 | 
				
			||||||
QParse.prototype.current = null;
 | 
					 | 
				
			||||||
QParse.prototype.next = null;
 | 
					 | 
				
			||||||
/********************************* 
 | 
					 | 
				
			||||||
 * QWiki                         *
 | 
					 | 
				
			||||||
 *********************************/
 | 
					 | 
				
			||||||
var QWiki = function() {
 | 
					 | 
				
			||||||
  QCore.call(this);
 | 
					 | 
				
			||||||
  try {
 | 
					 | 
				
			||||||
    var stats = fs.statSync('cache');
 | 
					 | 
				
			||||||
    if (!stats.isDirectory()) {
 | 
					 | 
				
			||||||
      console.log('error, "cache" is not a directory');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  } catch (err) {
 | 
					 | 
				
			||||||
    if (err.code == 'ENOENT') {
 | 
					 | 
				
			||||||
      fs.mkdirSync('cache');
 | 
					 | 
				
			||||||
      console.log('Created "cache/" -- this is where processed wiki pages are stored as HTML');
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      console.log('error while loading cache');
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}; util.inherits(QWiki, QCore);
 | 
					 | 
				
			||||||
QWiki.prototype.formats = {
 | 
					QWiki.prototype.formats = {
 | 
				
			||||||
  raw: {
 | 
					  raw: {
 | 
				
			||||||
    name: 'raw',
 | 
					    name: 'raw',
 | 
				
			||||||
| 
						 | 
					@ -270,8 +107,8 @@ QWiki.prototype.convertAndSave = function(path, name, source, cb) {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  console.log('converting to ' + name + ' with path ' + path);
 | 
					  console.log('converting to ' + name + ' with path ' + path);
 | 
				
			||||||
  var converted = this.formats[name].convert(source);
 | 
					  var converted = this.formats[name].convert(source);
 | 
				
			||||||
  r_mkdir(m_path.dirname('cache'+path+'.html'), 0777, function() {
 | 
					  QCore.prototype.r_mkdir(m_path.dirname('cache/'+path+'.html'), 0777, function() {
 | 
				
			||||||
    fs.writeFile('cache'+path+'.html', converted, function(err) {
 | 
					    fs.writeFile('cache/'+path+'.html', converted, function(err) {
 | 
				
			||||||
      if (err) {
 | 
					      if (err) {
 | 
				
			||||||
        console.log('error writing "cache'+ path + '.html"');
 | 
					        console.log('error writing "cache'+ path + '.html"');
 | 
				
			||||||
      } else {
 | 
					      } else {
 | 
				
			||||||
| 
						 | 
					@ -299,7 +136,7 @@ QWiki.prototype.createCache = function(wiki_page, source, cb) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var path = 'cache/' + wiki_page + '.html';
 | 
					  var path = 'cache/' + wiki_page + '.html';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  r_mkdir(m_path.dirname(path), 0777, function() {
 | 
					  QCore.prototype.r_mkdir(m_path.dirname(path), 0777, function() {
 | 
				
			||||||
    fs.writeFile(path, converted, function(err) {
 | 
					    fs.writeFile(path, converted, function(err) {
 | 
				
			||||||
      if (err) {
 | 
					      if (err) {
 | 
				
			||||||
        console.log('createCache: error writing "' + path + '"');
 | 
					        console.log('createCache: error writing "' + path + '"');
 | 
				
			||||||
| 
						 | 
					@ -315,7 +152,7 @@ QWiki.prototype.deletePage = function(wiki_page, cb) {
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
QWiki.prototype.savePage = function(wiki_page, content, cb) {
 | 
					QWiki.prototype.savePage = function(wiki_page, content, cb) {
 | 
				
			||||||
  var path = 'wiki/' + wiki_page + '.qwk';
 | 
					  var path = 'wiki/' + wiki_page + '.qwk';
 | 
				
			||||||
  r_mkdir(m_path.dirname(path), 0777, function() {
 | 
					  QCore.prototype.r_mkdir(m_path.dirname(path), 0777, function() {
 | 
				
			||||||
    console.log('savePage: writing ' + path);
 | 
					    console.log('savePage: writing ' + path);
 | 
				
			||||||
    qwiki.deleteCache(wiki_page, function() {
 | 
					    qwiki.deleteCache(wiki_page, function() {
 | 
				
			||||||
      fs.writeFile(path, content, function(err) {
 | 
					      fs.writeFile(path, content, function(err) {
 | 
				
			||||||
| 
						 | 
					@ -328,7 +165,6 @@ QWiki.prototype.savePage = function(wiki_page, content, cb) {
 | 
				
			||||||
QWiki.prototype.deleteFile = function(file, cb) {
 | 
					QWiki.prototype.deleteFile = function(file, cb) {
 | 
				
			||||||
  fs.unlink('wiki/'+file, cb);
 | 
					  fs.unlink('wiki/'+file, cb);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
/********************************* 
 | 
					/********************************* 
 | 
				
			||||||
 * qwiki instance                *
 | 
					 * qwiki instance                *
 | 
				
			||||||
 *********************************/
 | 
					 *********************************/
 | 
				
			||||||
| 
						 | 
					@ -354,88 +190,6 @@ qwiki.addFormat('md', 'Markdown', function(source) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
qwiki.setDefault('format', 'md');
 | 
					qwiki.setDefault('format', 'md');
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var readFile = function(stream, path, cb) {
 | 
					 | 
				
			||||||
  fs.exists(path, function(exists) {
 | 
					 | 
				
			||||||
    if (exists) {
 | 
					 | 
				
			||||||
      var rs = fs.createReadStream(path);
 | 
					 | 
				
			||||||
      rs.on('data', function(chunk) {
 | 
					 | 
				
			||||||
        stream.write(chunk);
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      rs.on('end', function() {
 | 
					 | 
				
			||||||
        cb('EOF');
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
      rs.on('error', function(err) {
 | 
					 | 
				
			||||||
        cb('ERR', err);
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    } else {
 | 
					 | 
				
			||||||
      cb('FNF'); // file not found
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
QCore.prototype.readFile = readFile;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var readFiles = function(dir, callback) {
 | 
					 | 
				
			||||||
  fs.readdir(dir, function(err, files) {
 | 
					 | 
				
			||||||
    files.forEach(function(file) {
 | 
					 | 
				
			||||||
      var stat_file = dir+'/'+file
 | 
					 | 
				
			||||||
      fs.stat(stat_file, function(err, stats) {
 | 
					 | 
				
			||||||
        if (stats.isDirectory()) {
 | 
					 | 
				
			||||||
          readFiles(stat_file, callback);
 | 
					 | 
				
			||||||
          callback(err, stat_file, true);
 | 
					 | 
				
			||||||
        } else if (stats.isFile()) {
 | 
					 | 
				
			||||||
          callback(err, stat_file, false);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
QCore.prototype.readDirectory = function(dir, callback) {
 | 
					 | 
				
			||||||
  var all_files = [];
 | 
					 | 
				
			||||||
  fs.readdir(dir, function(err, files) {
 | 
					 | 
				
			||||||
    if (err) {
 | 
					 | 
				
			||||||
      callback(err, null);
 | 
					 | 
				
			||||||
      return;
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    var len = files.length;
 | 
					 | 
				
			||||||
    var total = 0;
 | 
					 | 
				
			||||||
    if (len == 0) callback(err, all_files);
 | 
					 | 
				
			||||||
    files.forEach(function(file, index) {
 | 
					 | 
				
			||||||
      var stat_file = dir + '/' + file;
 | 
					 | 
				
			||||||
      fs.stat(stat_file, function(err, stats) {
 | 
					 | 
				
			||||||
        if (stats.isDirectory()) {
 | 
					 | 
				
			||||||
          all_files.push({'is_dir': true, 'name': file});
 | 
					 | 
				
			||||||
        } else if (stats.isFile()) {
 | 
					 | 
				
			||||||
          all_files.push({'is_dir': false, 'name': file});
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
        if (++total == len) {
 | 
					 | 
				
			||||||
          callback(err, all_files);
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      });
 | 
					 | 
				
			||||||
    });
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
QCore.prototype.getExt = function(file) {
 | 
					 | 
				
			||||||
  if (typeof file === 'undefined') return '';
 | 
					 | 
				
			||||||
  var last = file.lastIndexOf(".");
 | 
					 | 
				
			||||||
  if (last == -1) return '';
 | 
					 | 
				
			||||||
  var ext = file.substr(last+1);
 | 
					 | 
				
			||||||
  return ext;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var r_mkdir = function(dir, mode, cb) {
 | 
					 | 
				
			||||||
  console.log('creating '+dir);
 | 
					 | 
				
			||||||
  fs.mkdir(dir, mode, function(err) {
 | 
					 | 
				
			||||||
    if (err && err.code == 'ENOENT') {
 | 
					 | 
				
			||||||
      r_mkdir(m_path.dirname(dir), mode, r_mkdir.bind(this, dir, mode, cb));
 | 
					 | 
				
			||||||
    } else if (typeof cb !== 'undefined') {
 | 
					 | 
				
			||||||
      cb(err);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
  });
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
QCore.prototype.r_mkdir = r_mkdir;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
var port = 8080;
 | 
					var port = 8080;
 | 
				
			||||||
process.argv.forEach(function (val, index, array) {
 | 
					process.argv.forEach(function (val, index, array) {
 | 
				
			||||||
  var parts = val.split('=');
 | 
					  var parts = val.split('=');
 | 
				
			||||||
| 
						 | 
					@ -454,4 +208,4 @@ process.argv.forEach(function (val, index, array) {
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
					
 | 
				
			||||||
qwiki.listen(port);
 | 
					qwiki.listen(port, function() { qwiki.loadWikiIndex(); });
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue