mirror of
https://github.com/samsonjs/DeferredVis-server.git
synced 2026-03-25 09:15:49 +00:00
first commit
This commit is contained in:
commit
d284851af6
12 changed files with 760 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
node_modules
|
||||||
|
*.tmproj
|
||||||
24
config.js
Normal file
24
config.js
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
module.exports =
|
||||||
|
|
||||||
|
{ env: process.env.NODE_ENV || 'development'
|
||||||
|
|
||||||
|
, loggerFormat: [ ':remote-addr'
|
||||||
|
, '-'
|
||||||
|
, ':response-timems'
|
||||||
|
, '[:date]'
|
||||||
|
, '":method :url HTTP/:http-version"'
|
||||||
|
, ':status'
|
||||||
|
, ':res[content-length]'
|
||||||
|
, '":referrer"'
|
||||||
|
, '":user-agent"'
|
||||||
|
].join(' ')
|
||||||
|
|
||||||
|
, sessionSecret: '8dce9a5af7733469651f81390d2cb3dda5bfb1ef'
|
||||||
|
|
||||||
|
, host: '0.0.0.0'
|
||||||
|
, port: 3030
|
||||||
|
|
||||||
|
, webHost: '0.0.0.0'
|
||||||
|
, webPort: 8080
|
||||||
|
|
||||||
|
}
|
||||||
125
model.js
Normal file
125
model.js
Normal file
|
|
@ -0,0 +1,125 @@
|
||||||
|
//////////////
|
||||||
|
/// Models ///
|
||||||
|
//////////////
|
||||||
|
|
||||||
|
function RemoteDataSource(conn, id) {
|
||||||
|
this.id = id
|
||||||
|
this.connection = conn
|
||||||
|
this.deferreds = []
|
||||||
|
this.executed = false
|
||||||
|
this.cancelled = false
|
||||||
|
this.completed = false
|
||||||
|
this.result = null
|
||||||
|
this.events = []
|
||||||
|
|
||||||
|
this.addEvent('created')
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDataSource.prototype.addEvent = function(/* name, ... */) {
|
||||||
|
var args = [].slice.call(arguments)
|
||||||
|
args.unshift(new Date())
|
||||||
|
this.events.push(args)
|
||||||
|
// TODO emit a node event
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDataSource.prototype.removeDeferred = function(d) {
|
||||||
|
this.deferreds.push(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDataSource.prototype.removeDeferred = function(d) {
|
||||||
|
var i = this.deferreds.indexOf(d)
|
||||||
|
if (i > -1) {
|
||||||
|
this.deferreds.splice(i, 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDataSource.prototype.remove = function(data) {
|
||||||
|
this.addEvent('removed', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDataSource.prototype.cancel = function(data) {
|
||||||
|
this.cancelled = new Date()
|
||||||
|
this.addEvent('cancelled', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDataSource.prototype.execute = function(data) {
|
||||||
|
this.executed = new Date()
|
||||||
|
this.addEvent('executed', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDataSource.prototype.complete = function(result, data) {
|
||||||
|
this.completed = new Date()
|
||||||
|
this.result = result
|
||||||
|
this.addEvent('completed', result, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
function RemoteDeferred(conn, id, data) {
|
||||||
|
this.id = id
|
||||||
|
this.connection = conn
|
||||||
|
this.links = []
|
||||||
|
this.linkIndex = 0
|
||||||
|
this.called = false
|
||||||
|
this.running = false
|
||||||
|
this.pauseCount = 0
|
||||||
|
this.finalized = false
|
||||||
|
this.hasFinalizer = false
|
||||||
|
this.resolved = false
|
||||||
|
this.rejected = false
|
||||||
|
this.result = null
|
||||||
|
this.events = []
|
||||||
|
|
||||||
|
this.addEvent('created', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDeferred.prototype.addEvent = function(/* name, ... */) {
|
||||||
|
var args = [].slice.call(arguments)
|
||||||
|
args.unshift(new Date())
|
||||||
|
this.events.push(args)
|
||||||
|
// TODO emit a node event
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDeferred.prototype.remove = function(data) {
|
||||||
|
if (this.dataSource) {
|
||||||
|
this.dataSource.removeDeferred(this)
|
||||||
|
}
|
||||||
|
this.addEvent('removed', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDeferred.prototype.cancel = function(data) {
|
||||||
|
this.cancelled = new Date()
|
||||||
|
this.addEvent('cancelled', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDeferred.prototype.resolve = function(data) {
|
||||||
|
this.resolved = new Date()
|
||||||
|
this.result = data.result
|
||||||
|
this.addEvent('resolved', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDeferred.prototype.reject = function(data) {
|
||||||
|
this.rejected = new Date()
|
||||||
|
this.result = data.result
|
||||||
|
this.addEvent('rejected', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDeferred.prototype.addLink = function(data) {
|
||||||
|
this.links.push(tools.mixin({ ran: false }, data))
|
||||||
|
this.addEvent('link-added', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDeferred.prototype.runLink = function(data) {
|
||||||
|
var link = this.links[this.linkIndex++]
|
||||||
|
link.ran = new Date()
|
||||||
|
tools.mixin(link, data) // data contains: result, file, and line
|
||||||
|
this.addEvent('link-ran', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDeferred.prototype.addFinalizer = function(data) {
|
||||||
|
this.hasFinalizer = new Date()
|
||||||
|
this.addEvent('finalizer-added', data)
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteDeferred.prototype.finalize = function(data) {
|
||||||
|
this.finalized = new Date()
|
||||||
|
this.addEvent('finalized', data)
|
||||||
|
}
|
||||||
32
package.json
Normal file
32
package.json
Normal file
|
|
@ -0,0 +1,32 @@
|
||||||
|
{ "name" : "deferredvis-server"
|
||||||
|
, "version" : "0.0.1"
|
||||||
|
, "description" : "Visualize your Deferreds"
|
||||||
|
, "keywords" :
|
||||||
|
[ "deferred"
|
||||||
|
, "visualize"
|
||||||
|
]
|
||||||
|
, "author" : "Sami Samhuri <sami@samhuri.net>"
|
||||||
|
, "maintainers" : [
|
||||||
|
"Sami Samhuri <sami@samhuri.net>"
|
||||||
|
]
|
||||||
|
, "homepage" : "http://samhuri.net/proj/DeferredVis-server"
|
||||||
|
, "main" : "server"
|
||||||
|
, "bin" : { "deferredvis-server" : "./server.js" }
|
||||||
|
, "engines" : { "node" : "0.4.x" }
|
||||||
|
, "repository" :
|
||||||
|
{ "type" : "git"
|
||||||
|
,"url" : "git://github.com/samsonjs/DeferredVis-server.git"
|
||||||
|
}
|
||||||
|
, "licenses" :
|
||||||
|
[ { "type" : "MIT"
|
||||||
|
, "url" : "http://github.com/samsonjs/DeferredVis-server/raw/master/LICENSE"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
, "dependencies" :
|
||||||
|
{ "connect" : "1.4.x"
|
||||||
|
, "connect-redis" : "1.0.x"
|
||||||
|
, "express" : "2.4.x"
|
||||||
|
, "socket.io" : "0.7.x"
|
||||||
|
}
|
||||||
|
, "devDependencies" : {}
|
||||||
|
}
|
||||||
15
protocol.txt
Normal file
15
protocol.txt
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
datasource-created 0x12345
|
||||||
|
datasource-removed 0x12345
|
||||||
|
datasource-cancelled 0x12345
|
||||||
|
datasource-executed 0x12345
|
||||||
|
datasource-completed 0x12345 { result: _, success: _ }
|
||||||
|
|
||||||
|
deferred-created 0x67890 { dataSourceId: ? }
|
||||||
|
deferred-removed 0x67890
|
||||||
|
deferred-cancelled 0x67890
|
||||||
|
deferred-resolved 0x67890 { result: _ }
|
||||||
|
deferred-rejected 0x67890 { result: _ }
|
||||||
|
deferred-paused 0x67890 { myLink: ?, distantLink: ? }
|
||||||
|
deferred-unpaused 0x67890
|
||||||
|
deferred-link-added 0x67890 { linkId: _, finalizer: _, file: _, line: _ }
|
||||||
|
deferred-link-ran 0x67890 { linkId: _, finalizer: _, result: _ }
|
||||||
5
public/css/style.css
Normal file
5
public/css/style.css
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
body { margin: 0
|
||||||
|
; padding: 20px
|
||||||
|
; font: 12px/17px 'Helvetica Neue', Helvetica, sans-serif
|
||||||
|
; background: #f9f9f9
|
||||||
|
}
|
||||||
13
public/index.html
Normal file
13
public/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
<!doctype html>
|
||||||
|
<meta charset=utf-8>
|
||||||
|
<title>Deferred Visualizer</title>
|
||||||
|
<link rel="stylesheet" href="css/style.css">
|
||||||
|
|
||||||
|
<canvas id="viewport" width="800" height="600"></canvas>
|
||||||
|
|
||||||
|
<noscript><p align="center">You need JavaScript for this bro</p></noscript>
|
||||||
|
|
||||||
|
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js"></script>
|
||||||
|
<script src="js/arbor.js"></script>
|
||||||
|
<script src="/socket.io/socket.io.js"></script>
|
||||||
|
<script src="js/main.js"></script>
|
||||||
86
public/js/arbor-tween.js
Normal file
86
public/js/arbor-tween.js
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
//
|
||||||
|
// arbor-tween.js
|
||||||
|
// smooth transitions with a realtime clock
|
||||||
|
//
|
||||||
|
// Copyright (c) 2011 Samizdat Drafting Co.
|
||||||
|
//
|
||||||
|
// Permission is hereby granted, free of charge, to any person
|
||||||
|
// obtaining a copy of this software and associated documentation
|
||||||
|
// files (the "Software"), to deal in the Software without
|
||||||
|
// restriction, including without limitation the rights to use,
|
||||||
|
// copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
// copies of the Software, and to permit persons to whom the
|
||||||
|
// Software is furnished to do so, subject to the following
|
||||||
|
// conditions:
|
||||||
|
//
|
||||||
|
// The above copyright notice and this permission notice shall be
|
||||||
|
// included in all copies or substantial portions of the Software.
|
||||||
|
//
|
||||||
|
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||||
|
// OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||||
|
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||||
|
// HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
// WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||||
|
// OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
//
|
||||||
|
|
||||||
|
// Easing Equations in easing.js:
|
||||||
|
// Copyright © 2001 Robert Penner. All rights reserved.
|
||||||
|
//
|
||||||
|
// Open source under the BSD License. Redistribution and use in source
|
||||||
|
// and binary forms, with or without modification, are permitted
|
||||||
|
// provided that the following conditions are met:
|
||||||
|
//
|
||||||
|
// Redistributions of source code must retain the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer.
|
||||||
|
// Redistributions in binary form must reproduce the above copyright
|
||||||
|
// notice, this list of conditions and the following disclaimer in the
|
||||||
|
// documentation and/or other materials provided with the distribution.
|
||||||
|
//
|
||||||
|
// Neither the name of the author nor the names of contributors may be
|
||||||
|
// used to endorse or promote products derived from this software
|
||||||
|
// without specific prior written permission.
|
||||||
|
//
|
||||||
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
|
||||||
|
|
||||||
|
(function($){
|
||||||
|
|
||||||
|
/* etc.js */ var trace=function(msg){if(typeof(window)=="undefined"||!window.console){return}var len=arguments.length;var args=[];for(var i=0;i<len;i++){args.push("arguments["+i+"]")}eval("console.log("+args.join(",")+")")};var dirname=function(a){var b=a.replace(/^\/?(.*?)\/?$/,"$1").split("/");b.pop();return"/"+b.join("/")};var basename=function(b){var c=b.replace(/^\/?(.*?)\/?$/,"$1").split("/");var a=c.pop();if(a==""){return null}else{return a}};var _ordinalize_re=/(\d)(?=(\d\d\d)+(?!\d))/g;var ordinalize=function(a){var b=""+a;if(a<11000){b=(""+a).replace(_ordinalize_re,"$1,")}else{if(a<1000000){b=Math.floor(a/1000)+"k"}else{if(a<1000000000){b=(""+Math.floor(a/1000)).replace(_ordinalize_re,"$1,")+"m"}}}return b};var nano=function(a,b){return a.replace(/\{([\w\-\.]*)}/g,function(f,c){var d=c.split("."),e=b[d.shift()];$.each(d,function(){if(e.hasOwnProperty(this)){e=e[this]}else{e=f}});return e})};var objcopy=function(a){if(a===undefined){return undefined}if(a===null){return null}if(a.parentNode){return a}switch(typeof a){case"string":return a.substring(0);break;case"number":return a+0;break;case"boolean":return a===true;break}var b=($.isArray(a))?[]:{};$.each(a,function(d,c){b[d]=objcopy(c)});return b};var objmerge=function(d,b){d=d||{};b=b||{};var c=objcopy(d);for(var a in b){c[a]=b[a]}return c};var objcmp=function(e,c,d){if(!e||!c){return e===c}if(typeof e!=typeof c){return false}if(typeof e!="object"){return e===c}else{if($.isArray(e)){if(!($.isArray(c))){return false}if(e.length!=c.length){return false}}else{var h=[];for(var f in e){if(e.hasOwnProperty(f)){h.push(f)}}var g=[];for(var f in c){if(c.hasOwnProperty(f)){g.push(f)}}if(!d){h.sort();g.sort()}if(h.join(",")!==g.join(",")){return false}}var i=true;$.each(e,function(a){var b=objcmp(e[a],c[a]);i=i&&b;if(!i){return false}});return i}};var objkeys=function(b){var a=[];$.each(b,function(d,c){if(b.hasOwnProperty(d)){a.push(d)}});return a};var objcontains=function(c){if(!c||typeof c!="object"){return false}for(var b=1,a=arguments.length;b<a;b++){if(c.hasOwnProperty(arguments[b])){return true}}return false};var uniq=function(b){var a=b.length;var d={};for(var c=0;c<a;c++){d[b[c]]=true}return objkeys(d)};var arbor_path=function(){var a=$("script").map(function(b){var c=$(this).attr("src");if(!c){return}if(c.match(/arbor[^\/\.]*.js|dev.js/)){return c.match(/.*\//)||"/"}});if(a.length>0){return a[0]}else{return null}};
|
||||||
|
/* colors.js */ var Colors=(function(){var f=/#[0-9a-f]{6}/i;var b=/#(..)(..)(..)/;var c=function(h){var g=h.toString(16);return(g.length==2)?g:"0"+g};var a=function(g){return parseInt(g,16)};var d=function(g){if(!g||typeof g!="object"){return false}var h=objkeys(g).sort().join("");if(h=="abgr"){return true}};var e={CSS:{aliceblue:"#f0f8ff",antiquewhite:"#faebd7",aqua:"#00ffff",aquamarine:"#7fffd4",azure:"#f0ffff",beige:"#f5f5dc",bisque:"#ffe4c4",black:"#000000",blanchedalmond:"#ffebcd",blue:"#0000ff",blueviolet:"#8a2be2",brown:"#a52a2a",burlywood:"#deb887",cadetblue:"#5f9ea0",chartreuse:"#7fff00",chocolate:"#d2691e",coral:"#ff7f50",cornflowerblue:"#6495ed",cornsilk:"#fff8dc",crimson:"#dc143c",cyan:"#00ffff",darkblue:"#00008b",darkcyan:"#008b8b",darkgoldenrod:"#b8860b",darkgray:"#a9a9a9",darkgrey:"#a9a9a9",darkgreen:"#006400",darkkhaki:"#bdb76b",darkmagenta:"#8b008b",darkolivegreen:"#556b2f",darkorange:"#ff8c00",darkorchid:"#9932cc",darkred:"#8b0000",darksalmon:"#e9967a",darkseagreen:"#8fbc8f",darkslateblue:"#483d8b",darkslategray:"#2f4f4f",darkslategrey:"#2f4f4f",darkturquoise:"#00ced1",darkviolet:"#9400d3",deeppink:"#ff1493",deepskyblue:"#00bfff",dimgray:"#696969",dimgrey:"#696969",dodgerblue:"#1e90ff",firebrick:"#b22222",floralwhite:"#fffaf0",forestgreen:"#228b22",fuchsia:"#ff00ff",gainsboro:"#dcdcdc",ghostwhite:"#f8f8ff",gold:"#ffd700",goldenrod:"#daa520",gray:"#808080",grey:"#808080",green:"#008000",greenyellow:"#adff2f",honeydew:"#f0fff0",hotpink:"#ff69b4",indianred:"#cd5c5c",indigo:"#4b0082",ivory:"#fffff0",khaki:"#f0e68c",lavender:"#e6e6fa",lavenderblush:"#fff0f5",lawngreen:"#7cfc00",lemonchiffon:"#fffacd",lightblue:"#add8e6",lightcoral:"#f08080",lightcyan:"#e0ffff",lightgoldenrodyellow:"#fafad2",lightgray:"#d3d3d3",lightgrey:"#d3d3d3",lightgreen:"#90ee90",lightpink:"#ffb6c1",lightsalmon:"#ffa07a",lightseagreen:"#20b2aa",lightskyblue:"#87cefa",lightslategray:"#778899",lightslategrey:"#778899",lightsteelblue:"#b0c4de",lightyellow:"#ffffe0",lime:"#00ff00",limegreen:"#32cd32",linen:"#faf0e6",magenta:"#ff00ff",maroon:"#800000",mediumaquamarine:"#66cdaa",mediumblue:"#0000cd",mediumorchid:"#ba55d3",mediumpurple:"#9370d8",mediumseagreen:"#3cb371",mediumslateblue:"#7b68ee",mediumspringgreen:"#00fa9a",mediumturquoise:"#48d1cc",mediumvioletred:"#c71585",midnightblue:"#191970",mintcream:"#f5fffa",mistyrose:"#ffe4e1",moccasin:"#ffe4b5",navajowhite:"#ffdead",navy:"#000080",oldlace:"#fdf5e6",olive:"#808000",olivedrab:"#6b8e23",orange:"#ffa500",orangered:"#ff4500",orchid:"#da70d6",palegoldenrod:"#eee8aa",palegreen:"#98fb98",paleturquoise:"#afeeee",palevioletred:"#d87093",papayawhip:"#ffefd5",peachpuff:"#ffdab9",peru:"#cd853f",pink:"#ffc0cb",plum:"#dda0dd",powderblue:"#b0e0e6",purple:"#800080",red:"#ff0000",rosybrown:"#bc8f8f",royalblue:"#4169e1",saddlebrown:"#8b4513",salmon:"#fa8072",sandybrown:"#f4a460",seagreen:"#2e8b57",seashell:"#fff5ee",sienna:"#a0522d",silver:"#c0c0c0",skyblue:"#87ceeb",slateblue:"#6a5acd",slategray:"#708090",slategrey:"#708090",snow:"#fffafa",springgreen:"#00ff7f",steelblue:"#4682b4",tan:"#d2b48c",teal:"#008080",thistle:"#d8bfd8",tomato:"#ff6347",turquoise:"#40e0d0",violet:"#ee82ee",wheat:"#f5deb3",white:"#ffffff",whitesmoke:"#f5f5f5",yellow:"#ffff00",yellowgreen:"#9acd32"},decode:function(h){var g=arguments.length;for(var l=g-1;l>=0;l--){if(arguments[l]===undefined){g--}}var k=arguments;if(!h){return null}if(g==1&&d(h)){return h}var j=null;if(typeof h=="string"){var o=1;if(g==2){o=k[1]}var n=e.CSS[h.toLowerCase()];if(n!==undefined){h=n}var m=h.match(f);if(m){vals=h.match(b);if(!vals||!vals.length||vals.length!=4){return null}j={r:a(vals[1]),g:a(vals[2]),b:a(vals[3]),a:o}}}else{if(typeof h=="number"){if(g>=3){j={r:k[0],g:k[1],b:k[2],a:1};if(g>=4){j.a*=k[3]}}else{if(g>=1){j={r:k[0],g:k[0],b:k[0],a:1};if(g==2){j.a*=k[1]}}}}}return j},validate:function(g){if(!g||typeof g!="string"){return false}if(e.CSS[g.toLowerCase()]!==undefined){return true}if(g.match(f)){return true}return false},mix:function(h,g,k){var j=e.decode(h);var i=e.decode(g)},blend:function(g,j){j=(j!==undefined)?Math.max(0,Math.min(1,j)):1;var h=e.decode(g);if(!h){return null}if(j==1){return g}var h=g;if(typeof g=="string"){h=e.decode(g)}var i=objcopy(h);i.a*=j;return nano("rgba({r},{g},{b},{a})",i)},encode:function(g){if(!d(g)){g=e.decode(g);if(!d(g)){return null}}if(g.a==1){return nano("#{r}{g}{b}",{r:c(g.r),g:c(g.g),b:c(g.b)})}else{return nano("rgba({r},{g},{b},{a})",g)}}};return e})();
|
||||||
|
/* easing.js */ var Easing=(function(){var a={linear:function(f,e,h,g){return h*(f/g)+e},quadin:function(f,e,h,g){return h*(f/=g)*f+e},quadout:function(f,e,h,g){return -h*(f/=g)*(f-2)+e},quadinout:function(f,e,h,g){if((f/=g/2)<1){return h/2*f*f+e}return -h/2*((--f)*(f-2)-1)+e},cubicin:function(f,e,h,g){return h*(f/=g)*f*f+e},cubicout:function(f,e,h,g){return h*((f=f/g-1)*f*f+1)+e},cubicinout:function(f,e,h,g){if((f/=g/2)<1){return h/2*f*f*f+e}return h/2*((f-=2)*f*f+2)+e},quartin:function(f,e,h,g){return h*(f/=g)*f*f*f+e},quartout:function(f,e,h,g){return -h*((f=f/g-1)*f*f*f-1)+e},quartinout:function(f,e,h,g){if((f/=g/2)<1){return h/2*f*f*f*f+e}return -h/2*((f-=2)*f*f*f-2)+e},quintin:function(f,e,h,g){return h*(f/=g)*f*f*f*f+e},quintout:function(f,e,h,g){return h*((f=f/g-1)*f*f*f*f+1)+e},quintinout:function(f,e,h,g){if((f/=g/2)<1){return h/2*f*f*f*f*f+e}return h/2*((f-=2)*f*f*f*f+2)+e},sinein:function(f,e,h,g){return -h*Math.cos(f/g*(Math.PI/2))+h+e},sineout:function(f,e,h,g){return h*Math.sin(f/g*(Math.PI/2))+e},sineinout:function(f,e,h,g){return -h/2*(Math.cos(Math.PI*f/g)-1)+e},expoin:function(f,e,h,g){return(f==0)?e:h*Math.pow(2,10*(f/g-1))+e},expoout:function(f,e,h,g){return(f==g)?e+h:h*(-Math.pow(2,-10*f/g)+1)+e},expoinout:function(f,e,h,g){if(f==0){return e}if(f==g){return e+h}if((f/=g/2)<1){return h/2*Math.pow(2,10*(f-1))+e}return h/2*(-Math.pow(2,-10*--f)+2)+e},circin:function(f,e,h,g){return -h*(Math.sqrt(1-(f/=g)*f)-1)+e},circout:function(f,e,h,g){return h*Math.sqrt(1-(f=f/g-1)*f)+e},circinout:function(f,e,h,g){if((f/=g/2)<1){return -h/2*(Math.sqrt(1-f*f)-1)+e}return h/2*(Math.sqrt(1-(f-=2)*f)+1)+e},elasticin:function(g,e,k,j){var h=1.70158;var i=0;var f=k;if(g==0){return e}if((g/=j)==1){return e+k}if(!i){i=j*0.3}if(f<Math.abs(k)){f=k;var h=i/4}else{var h=i/(2*Math.PI)*Math.asin(k/f)}return -(f*Math.pow(2,10*(g-=1))*Math.sin((g*j-h)*(2*Math.PI)/i))+e},elasticout:function(g,e,k,j){var h=1.70158;var i=0;var f=k;if(g==0){return e}if((g/=j)==1){return e+k}if(!i){i=j*0.3}if(f<Math.abs(k)){f=k;var h=i/4}else{var h=i/(2*Math.PI)*Math.asin(k/f)}return f*Math.pow(2,-10*g)*Math.sin((g*j-h)*(2*Math.PI)/i)+k+e},elasticinout:function(g,e,k,j){var h=1.70158;var i=0;var f=k;if(g==0){return e}if((g/=j/2)==2){return e+k}if(!i){i=j*(0.3*1.5)}if(f<Math.abs(k)){f=k;var h=i/4}else{var h=i/(2*Math.PI)*Math.asin(k/f)}if(g<1){return -0.5*(f*Math.pow(2,10*(g-=1))*Math.sin((g*j-h)*(2*Math.PI)/i))+e}return f*Math.pow(2,-10*(g-=1))*Math.sin((g*j-h)*(2*Math.PI)/i)*0.5+k+e},backin:function(f,e,i,h,g){if(g==undefined){g=1.70158}return i*(f/=h)*f*((g+1)*f-g)+e},backout:function(f,e,i,h,g){if(g==undefined){g=1.70158}return i*((f=f/h-1)*f*((g+1)*f+g)+1)+e},backinout:function(f,e,i,h,g){if(g==undefined){g=1.70158}if((f/=h/2)<1){return i/2*(f*f*(((g*=(1.525))+1)*f-g))+e}return i/2*((f-=2)*f*(((g*=(1.525))+1)*f+g)+2)+e},bouncein:function(f,e,h,g){return h-a.bounceOut(g-f,0,h,g)+e},bounceout:function(f,e,h,g){if((f/=g)<(1/2.75)){return h*(7.5625*f*f)+e}else{if(f<(2/2.75)){return h*(7.5625*(f-=(1.5/2.75))*f+0.75)+e}else{if(f<(2.5/2.75)){return h*(7.5625*(f-=(2.25/2.75))*f+0.9375)+e}else{return h*(7.5625*(f-=(2.625/2.75))*f+0.984375)+e}}}},bounceinout:function(f,e,h,g){if(f<g/2){return a.bounceIn(f*2,0,h,g)*0.5+e}return a.bounceOut(f*2-g,0,h,g)*0.5+h*0.5+e}};return a})();
|
||||||
|
/* tween.js */ var Tween=function(){var a={};var c=true;var b={init:function(){return b},busy:function(){var e=false;for(var d in a){e=true;break}return e},to:function(g,e,p){var f=new Date().valueOf();var d={};var q={from:{},to:{},colors:{},node:g,t0:f,t1:f+e*1000,dur:e*1000};var o="linear";for(var j in p){if(j=="easing"){var h=p[j].toLowerCase();if(h in Easing){o=h}continue}else{if(j=="delay"){var m=(p[j]||0)*1000;q.t0+=m;q.t1+=m;continue}}if(Colors.validate(p[j])){q.colors[j]=[Colors.decode(g.data[j]),Colors.decode(p[j]),p[j]];d[j]=true}else{q.from[j]=(g.data[j]!=undefined)?g.data[j]:p[j];q.to[j]=p[j];d[j]=true}}q.ease=Easing[o];if(a[g._id]===undefined){a[g._id]=[]}a[g._id].push(q);if(a.length>1){for(var l=a.length-2;l>=0;l++){var n=a[l];for(var j in n.to){if(j in d){delete n.to[j]}else{d[j]=true}}for(var j in n.colors){if(j in d){delete n.colors[j]}else{d[j]=true}}if($.isEmptyObject(n.colors)&&$.isEmptyObject(n.to)){a.splice(l,1)}}}c=false},interpolate:function(e,h,i,g){g=(g||"").toLowerCase();var d=Easing.linear;if(g in Easing){d=Easing[g]}var f=d(e,0,1,1);if(Colors.validate(h)&&Colors.validate(i)){return lerpRGB(f,h,i)}else{if(!isNaN(h)){return lerpNumber(f,h,i)}else{if(typeof h=="string"){return(f<0.5)?h:i}}}},tick:function(){var f=true;for(var d in a){f=false;break}if(f){return}var e=new Date().valueOf();$.each(a,function(i,h){var g=false;$.each(h,function(p,t){var o=t.ease((e-t.t0),0,1,t.dur);o=Math.min(1,o);var r=t.from;var s=t.to;var j=t.colors;var l=t.node.data;var m=(o==1);for(var n in s){switch(typeof s[n]){case"number":l[n]=lerpNumber(o,r[n],s[n]);if(n=="alpha"){l[n]=Math.max(0,Math.min(1,l[n]))}break;case"string":if(m){l[n]=s[n]}break}}for(var n in j){if(m){l[n]=j[n][2]}else{var q=lerpRGB(o,j[n][0],j[n][1]);l[n]=Colors.encode(q)}}if(m){t.completed=true;g=true}});if(g){a[i]=$.map(h,function(j){if(!j.completed){return j}});if(a[i].length==0){delete a[i]}}});c=$.isEmptyObject(a);return c}};return b.init()};var lerpNumber=function(a,c,b){return c+a*(b-c)};var lerpRGB=function(b,d,c){b=Math.max(Math.min(b,1),0);var a={};$.each("rgba".split(""),function(e,f){a[f]=Math.round(d[f]+b*(c[f]-d[f]))});return a};
|
||||||
|
|
||||||
|
arbor = (typeof(arbor)!=='undefined') ? arbor : {}
|
||||||
|
$.extend(arbor, {
|
||||||
|
// not really user-serviceable; use the ParticleSystem’s .tween* methods instead
|
||||||
|
Tween:Tween,
|
||||||
|
|
||||||
|
// immutable object with useful methods
|
||||||
|
colors:{
|
||||||
|
CSS:Colors.CSS, // dictionary: {colorname:#fef2e2,...}
|
||||||
|
validate:Colors.validate, // ƒ(str) -> t/f
|
||||||
|
decode:Colors.decode, // ƒ(hexString_or_cssColor) -> {r,g,b,a}
|
||||||
|
encode:Colors.encode, // ƒ({r,g,b,a}) -> hexOrRgbaString
|
||||||
|
blend:Colors.blend // ƒ(color, opacity) -> rgbaString
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
})(this.jQuery)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
67
public/js/arbor.js
Normal file
67
public/js/arbor.js
Normal file
File diff suppressed because one or more lines are too long
249
public/js/main.js
Normal file
249
public/js/main.js
Normal file
|
|
@ -0,0 +1,249 @@
|
||||||
|
(function($) {
|
||||||
|
|
||||||
|
var Renderer = function(canvas) {
|
||||||
|
var canvas = $(canvas).get(0)
|
||||||
|
var ctx = canvas.getContext('2d');
|
||||||
|
var win = $(window)
|
||||||
|
var particleSystem
|
||||||
|
|
||||||
|
var that = {
|
||||||
|
init: function(system) {
|
||||||
|
//
|
||||||
|
// the particle system will call the init function once, right before the
|
||||||
|
// first frame is to be drawn. it's a good place to set up the canvas and
|
||||||
|
// to pass the canvas size to the particle system
|
||||||
|
//
|
||||||
|
// save a reference to the particle system for use in the .redraw() loop
|
||||||
|
particleSystem = system
|
||||||
|
|
||||||
|
win.resize(that.resize)
|
||||||
|
that.resize()
|
||||||
|
|
||||||
|
// set up some event handlers to allow for node-dragging
|
||||||
|
that.initMouseHandling()
|
||||||
|
},
|
||||||
|
|
||||||
|
// XXX why doesn't this fill the screen?
|
||||||
|
resize: function() {
|
||||||
|
canvas.width = win.width() - 160
|
||||||
|
canvas.height = .75 * win.height() - 160
|
||||||
|
particleSystem.screenSize(canvas.width, canvas.height)
|
||||||
|
particleSystem.screenPadding(80) // leave an extra 80px of whitespace per side
|
||||||
|
that.redraw()
|
||||||
|
},
|
||||||
|
|
||||||
|
redraw: function() {
|
||||||
|
if (!particleSystem) return
|
||||||
|
|
||||||
|
//
|
||||||
|
// redraw will be called repeatedly during the run whenever the node positions
|
||||||
|
// change. the new positions for the nodes can be accessed by looking at the
|
||||||
|
// .p attribute of a given node. however the p.x & p.y values are in the coordinates
|
||||||
|
// of the particle system rather than the screen. you can either map them to
|
||||||
|
// the screen yourself, or use the convenience iterators .eachNode (and .eachEdge)
|
||||||
|
// which allow you to step through the actual node objects but also pass an
|
||||||
|
// x,y point in the screen's coordinate system
|
||||||
|
//
|
||||||
|
ctx.fillStyle = "white"
|
||||||
|
ctx.fillRect(0,0, canvas.width, canvas.height)
|
||||||
|
|
||||||
|
particleSystem.eachEdge(function(edge, pt1, pt2) {
|
||||||
|
// edge: {source:Node, target:Node, length:#, data:{}}
|
||||||
|
// pt1: {x:#, y:#} source position in screen coords
|
||||||
|
// pt2: {x:#, y:#} target position in screen coords
|
||||||
|
|
||||||
|
// draw a line from pt1 to pt2
|
||||||
|
ctx.strokeStyle = edge.data.color || '#444'
|
||||||
|
ctx.lineWidth = 1
|
||||||
|
ctx.beginPath()
|
||||||
|
ctx.moveTo(pt1.x, pt1.y)
|
||||||
|
ctx.lineTo(pt2.x, pt2.y)
|
||||||
|
ctx.stroke()
|
||||||
|
})
|
||||||
|
|
||||||
|
particleSystem.eachNode(function(node, pt) {
|
||||||
|
// node: {mass:#, p:{x,y}, name:"", data:{}}
|
||||||
|
// pt: {x:#, y:#} node position in screen coords
|
||||||
|
|
||||||
|
// draw a rectangle centered at pt
|
||||||
|
var w = 10
|
||||||
|
ctx.fillStyle = node.data.color || 'black'
|
||||||
|
ctx.fillRect(pt.x-w/2, pt.y-w/2, w,w)
|
||||||
|
|
||||||
|
ctx.font = "14pt Helvetica"
|
||||||
|
ctx.fillText(node.data.type + ' ' + node.data.name, pt.x + w, pt.y)
|
||||||
|
if (node.data.extra) {
|
||||||
|
ctx.fillText(node.data.extra, pt.x + w, pt.y + 15)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
initMouseHandling: function() {
|
||||||
|
// no-nonsense drag and drop (thanks springy.js)
|
||||||
|
var dragged = null;
|
||||||
|
|
||||||
|
// set up a handler object that will initially listen for mousedowns then
|
||||||
|
// for moves and mouseups while dragging
|
||||||
|
var handler = {
|
||||||
|
clicked: function(e) {
|
||||||
|
var pos = $(canvas).offset();
|
||||||
|
_mouseP = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
|
||||||
|
dragged = particleSystem.nearest(_mouseP);
|
||||||
|
|
||||||
|
if (dragged && dragged.node !== null) {
|
||||||
|
console.log('clicked ' + dragged.node.name)
|
||||||
|
// while we're dragging, don't let physics move the node
|
||||||
|
dragged.node.fixed = true
|
||||||
|
}
|
||||||
|
|
||||||
|
$(canvas).bind('mousemove', handler.dragged)
|
||||||
|
win.bind('mouseup', handler.dropped)
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
dragged: function(e) {
|
||||||
|
var pos = $(canvas).offset();
|
||||||
|
var s = arbor.Point(e.pageX-pos.left, e.pageY-pos.top)
|
||||||
|
|
||||||
|
if (dragged && dragged.node !== null) {
|
||||||
|
var p = particleSystem.fromScreen(s)
|
||||||
|
dragged.node.p = p
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
},
|
||||||
|
|
||||||
|
dropped: function(e) {
|
||||||
|
if (dragged === null || dragged.node === undefined) return
|
||||||
|
if (dragged.node !== null) dragged.node.fixed = false
|
||||||
|
dragged.node.tempMass = 1000
|
||||||
|
dragged = null
|
||||||
|
$(canvas).unbind('mousemove', handler.dragged)
|
||||||
|
win.unbind('mouseup', handler.dropped)
|
||||||
|
_mouseP = null
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// start listening
|
||||||
|
$(canvas).mousedown(handler.clicked);
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
}
|
||||||
|
return that
|
||||||
|
}
|
||||||
|
|
||||||
|
var lastLinks = {} // maps deferred IDs to the last link of their chain
|
||||||
|
, EventHandlers =
|
||||||
|
{ 'datasource-created': function(sys, ev) {
|
||||||
|
sys.addNode(ev.id, { mass: 20.0, fixed: true, color: 'goldenrod', type: ev.data.class || 'data source', name: ev.id })
|
||||||
|
}
|
||||||
|
, 'datasource-removed': function(sys, ev) {
|
||||||
|
sys.getNode(ev.id).data.extra = '(complete)'
|
||||||
|
}
|
||||||
|
, 'datasource-cancelled': function(sys, ev) {
|
||||||
|
sys.getNode(ev.id).data.color = 'yellow'
|
||||||
|
}
|
||||||
|
, 'datasource-executed': function(sys, ev) {
|
||||||
|
sys.getNode(ev.id).data.color = '#005'
|
||||||
|
}
|
||||||
|
, 'datasource-completed': function(sys, ev) {
|
||||||
|
var node = sys.getNode(ev.id)
|
||||||
|
node.data.color = '#050'
|
||||||
|
node.data.extra = ev.data.result
|
||||||
|
}
|
||||||
|
|
||||||
|
, 'deferred-created': function(sys, ev) {
|
||||||
|
sys.addNode(ev.id, { mass: 10.0, type: ev.data.class || 'deferred', name: ev.id })
|
||||||
|
lastLinks[ev.id] = ev.id
|
||||||
|
console.log('deferred-created: data source id is ', ev.data.dataSourceId, 'ev.id = ', ev.id)
|
||||||
|
if (ev.data.dataSourceId) {
|
||||||
|
sys.addEdge(ev.data.dataSourceId, ev.id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, 'deferred-removed': function(sys, ev) {
|
||||||
|
// delete lastLinks[ev.id]
|
||||||
|
// if (ev.data && ev.data.dataSourceId) {
|
||||||
|
// sys.pruneEdge(sys.getEdge(ev.data.dataSourceId, ev.id))
|
||||||
|
// }
|
||||||
|
// else {
|
||||||
|
// sys.pruneNode(ev.id)
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
, 'deferred-cancelled': function(sys, ev) {
|
||||||
|
sys.getNode(ev.id).data.color = 'yellow'
|
||||||
|
}
|
||||||
|
, 'deferred-resolved': function(sys, ev) {
|
||||||
|
var node = sys.getNode(ev.id)
|
||||||
|
node.data.color = '#050'
|
||||||
|
node.data.extra = '(complete: ' + ev.data.result + ')'
|
||||||
|
}
|
||||||
|
, 'deferred-rejected': function(sys, ev) {
|
||||||
|
var node = sys.getNode(ev.id)
|
||||||
|
node.data.color = '#500'
|
||||||
|
node.data.extra = '(complete: ' + ev.data.result + ')'
|
||||||
|
}
|
||||||
|
, 'deferred-paused': function(sys, ev) {
|
||||||
|
sys.getNode(ev.id).data.extra = '(paused)'
|
||||||
|
if (ev.data.distantLink) {
|
||||||
|
sys.addEdge(ev.data.distantLink, ev.data.myLink, { color: '#FF510C' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, 'deferred-unpaused': function(sys, ev) {
|
||||||
|
delete sys.getNode(ev.id).data.extra
|
||||||
|
}
|
||||||
|
, 'deferred-link-added': function(sys, ev) {
|
||||||
|
var data = { mass: 5.0, name: ev.data.linkId, extra: ev.data.file + ':' + ev.data.line }
|
||||||
|
if (ev.data.finalizer) {
|
||||||
|
data.type = 'finalizer'
|
||||||
|
data.color = '#744'
|
||||||
|
sys.addNode(ev.data.linkId, data)
|
||||||
|
sys.addEdge(ev.id, ev.data.linkId)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
data.type = 'link'
|
||||||
|
data.color = '#666'
|
||||||
|
sys.addNode(ev.data.linkId, data)
|
||||||
|
sys.addEdge(lastLinks[ev.id], ev.data.linkId)
|
||||||
|
lastLinks[ev.id] = ev.data.linkId
|
||||||
|
}
|
||||||
|
}
|
||||||
|
, 'deferred-link-ran': function(sys, ev) {
|
||||||
|
var node = sys.getNode(ev.data.linkId)
|
||||||
|
node.data.color = '#999'
|
||||||
|
node.data.extra += '\n' + ev.data.result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$(document).ready(function() {
|
||||||
|
var sys = arbor.ParticleSystem(50, 1, 0.9) // create the system with sensible repulsion/stiffness/friction
|
||||||
|
sys.parameters({ gravity: false }) // use center-gravity to make the graph settle nicely (ymmv)
|
||||||
|
sys.renderer = Renderer("#viewport") // our newly created renderer will have its .init() method called shortly by sys...
|
||||||
|
|
||||||
|
var events = []
|
||||||
|
, deferreds = {}
|
||||||
|
var socket = io.connect('http://localhost')
|
||||||
|
socket.on('event', function (ev) {
|
||||||
|
events.push(ev)
|
||||||
|
console.log('"' + ev.name + '"', '"' + ev.id + '"', ev.payload)
|
||||||
|
if (ev.payload && ev.payload !== '(null)') {
|
||||||
|
try {
|
||||||
|
ev.data = JSON.parse(ev.payload)
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
console.warn('payload is not JSON: ', ev.payload)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var handler = EventHandlers[ev.name]
|
||||||
|
if (typeof handler === 'function') {
|
||||||
|
handler(sys, ev)
|
||||||
|
sys.renderer.redraw()
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.error('no handler for ' + ev.name)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
})(this.jQuery)
|
||||||
129
server.js
Normal file
129
server.js
Normal file
|
|
@ -0,0 +1,129 @@
|
||||||
|
var fs = require('fs')
|
||||||
|
, net = require('net')
|
||||||
|
, express = require('express')
|
||||||
|
, RedisStore = require('connect-redis')(express)
|
||||||
|
, socketIO = require('socket.io')
|
||||||
|
, config = require('./config')
|
||||||
|
, tools = require('./tools')
|
||||||
|
|
||||||
|
// tcp server, communicates w/ Deferred host
|
||||||
|
, server
|
||||||
|
|
||||||
|
// express and socket.io app
|
||||||
|
, app
|
||||||
|
, io
|
||||||
|
|
||||||
|
// Event log
|
||||||
|
, events = []
|
||||||
|
|
||||||
|
exports.start = function() {
|
||||||
|
startEventListener()
|
||||||
|
startWebServer()
|
||||||
|
}
|
||||||
|
|
||||||
|
function parseEvents(s) {
|
||||||
|
var eventStrings = s.trim().split('\r\n')
|
||||||
|
eventStrings.forEach(function(s) {
|
||||||
|
var i = s.indexOf(' ')
|
||||||
|
, j = s.indexOf(' ', i + 1)
|
||||||
|
, ev = {}
|
||||||
|
if (j === -1) j = s.length
|
||||||
|
ev.name = s.slice(0, i)
|
||||||
|
ev.id = s.slice(i + 1, j)
|
||||||
|
ev.payload = s.slice(j + 1) || null
|
||||||
|
events.push(ev)
|
||||||
|
io.sockets.emit('event', ev)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function startEventListener() {
|
||||||
|
server = net.createServer(function(conn) {
|
||||||
|
var buf = ''
|
||||||
|
|
||||||
|
console.log(conn.fd + ' CONNECT')
|
||||||
|
|
||||||
|
conn.on('data', function(d) {
|
||||||
|
buf += d
|
||||||
|
if (buf[buf.length - 1] === '\n') {
|
||||||
|
parseEvents(buf)
|
||||||
|
buf = ''
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
conn.on('end', function() {
|
||||||
|
console.log(conn.fd + ' END')
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
server.on('error', function(e) {
|
||||||
|
console.log(e)
|
||||||
|
console.log(e.stack)
|
||||||
|
})
|
||||||
|
|
||||||
|
server.listen(config.port, config.host)
|
||||||
|
}
|
||||||
|
|
||||||
|
// A custom static file handler
|
||||||
|
|
||||||
|
var FileTypes =
|
||||||
|
{ css: 'text/css'
|
||||||
|
, html: 'text/html'
|
||||||
|
, js: 'application/javascript'
|
||||||
|
}
|
||||||
|
|
||||||
|
function staticFileHandler(file, type) {
|
||||||
|
var path = file.charAt(0) === '/' ? file : (__dirname + '/public/' + file)
|
||||||
|
, data = fs.readFileSync(path)
|
||||||
|
, dev = config.env === 'development'
|
||||||
|
, ext
|
||||||
|
|
||||||
|
if (!type) {
|
||||||
|
ext = (file || '').toLowerCase().split('.').pop()
|
||||||
|
type = '' + (FileTypes[ext] || 'application/octet-stream')
|
||||||
|
}
|
||||||
|
|
||||||
|
return function(req, res) {
|
||||||
|
// Always reload in development
|
||||||
|
if (dev) data = fs.readFileSync(path, 'binary')
|
||||||
|
res.send(data, { 'content-type': type })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function startWebServer() {
|
||||||
|
app = express.createServer(
|
||||||
|
express.logger({ format: config.loggerFormat })
|
||||||
|
, express.bodyParser()
|
||||||
|
, express.methodOverride()
|
||||||
|
, express.cookieParser()
|
||||||
|
, express.session({ secret: config.sessionSecret, store: new RedisStore() })
|
||||||
|
)
|
||||||
|
|
||||||
|
app.configure('development', function() {
|
||||||
|
app.use(express.errorHandler({ dumpExceptions: true, showStack: true }))
|
||||||
|
})
|
||||||
|
|
||||||
|
app.configure('production', function() {
|
||||||
|
app.use(express.errorHandler())
|
||||||
|
})
|
||||||
|
|
||||||
|
app.get('/', staticFileHandler('index.html'))
|
||||||
|
app.get('/js/main.js', staticFileHandler('js/main.js'))
|
||||||
|
app.get('/js/arbor.js', staticFileHandler('js/arbor.js'))
|
||||||
|
app.get('/js/arbor-tween.js', staticFileHandler('js/arbor-tween.js'))
|
||||||
|
app.get('/css/style.css', staticFileHandler('css/style.css'))
|
||||||
|
|
||||||
|
socketize(app)
|
||||||
|
app.listen(config.webPort, config.webHost)
|
||||||
|
}
|
||||||
|
|
||||||
|
function socketize(app) {
|
||||||
|
io = socketIO.listen(app)
|
||||||
|
io.sockets.on('connection', function(socket) {
|
||||||
|
// New clients get the backlog of events
|
||||||
|
events.forEach(function(ev) {
|
||||||
|
socket.emit('event', ev)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (require.main === module) exports.start()
|
||||||
13
tools.js
Normal file
13
tools.js
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
module.exports = { mixin: mixin }
|
||||||
|
|
||||||
|
function mixin(a, b, c) {
|
||||||
|
for (var key in b) {
|
||||||
|
a[key] = b[key]
|
||||||
|
}
|
||||||
|
if (c) {
|
||||||
|
var args = [].slice.call(arguments)
|
||||||
|
args.splice(1, 1)
|
||||||
|
return mixin.apply(null, args)
|
||||||
|
}
|
||||||
|
return a
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue