Comment system added
comment author = ip comment author info = origin of ip ip addr is jank ( uses php public server for ip get) fuck apache reverse proxy ip change thigy thing comment command started config stuff started planning logging systemmaster
parent
4a02389eba
commit
92b5eeb2ef
|
@ -1,17 +1,87 @@
|
|||
class comment {
|
||||
constructor( comment ) {
|
||||
this.selector = ".comments"
|
||||
this.content = comment
|
||||
|
||||
// add
|
||||
// escape body (just to be sure) + newlines | author dosnt has to be escaped cuz its generated by srv (its the ip or later mby reverse dns)
|
||||
comment.body = comment.body.replaceAll("<", "<").replaceAll(">", "<").replaceAll("\n","<br>")
|
||||
|
||||
// construct time:
|
||||
let date = new Date(this.content.time)
|
||||
let time = String(date.getHours()).padStart(2,"0") + ":"
|
||||
time += String(date.getMinutes()).padStart(2,"0") + ":"
|
||||
time += String(date.getSeconds()).padStart(2,"0") + ":"
|
||||
time += String(date.getMilliseconds()).padStart(3,"0") + " "
|
||||
time += String(date.getDate()).padStart(2,"0") + "-"
|
||||
time += String(date.getMonth()).padStart(2,"0") + "-"
|
||||
time += String(date.getFullYear())
|
||||
|
||||
// construct author hover info
|
||||
let authorinfo = ""
|
||||
try {
|
||||
authorinfo = " (" + this.content.authorinfo.country + ") "
|
||||
} catch {}
|
||||
|
||||
// assemble element
|
||||
let elem = document.createElement("DIV")
|
||||
elem.classList = ["comment"]
|
||||
elem.innerHTML = `
|
||||
<div class="authorbox">
|
||||
<span class="author">${this.content.author}${authorinfo}</span>
|
||||
<span class="timestamp">wrote at ${time}</span>
|
||||
</div>
|
||||
<hr class="speperator" \\>
|
||||
<div class="body">${comment.body}</div>`
|
||||
|
||||
$( this.selector ).append( elem )
|
||||
}
|
||||
|
||||
reply() {
|
||||
// WIP / planned
|
||||
}
|
||||
}
|
||||
|
||||
function displaycomments() {
|
||||
commenting = new metaentry(undefined, "comment:", ".content",{"content":
|
||||
`Your comment: (no MD)<br>
|
||||
<textarea class="comment commtext"></textarea>
|
||||
<input class="comment" onclick="comment($('.commtext').val())" type="submit" value="Submit!" \\>`})
|
||||
<input class="comment" onclick="sendcomment($('.commtext').val())" type="submit" value="Submit!" \\>`})
|
||||
|
||||
comments = []
|
||||
comments.push( new metaentry(undefined, "comments:", ".content", {"content":
|
||||
`These comments are user generated! they are <strong>NOT</strong> (actively) moderated!`, "class":"comments"}))
|
||||
|
||||
// load comments:
|
||||
reloadcommenting()
|
||||
}
|
||||
|
||||
function comment(comment) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/comment",
|
||||
data: {
|
||||
comment: comment
|
||||
},
|
||||
dataType: "json"
|
||||
function reloadcommenting() {
|
||||
$(".comments").html("")
|
||||
fetch("/comments?post=" + postDATA.id).then(d=>d.json()).then(data=>{
|
||||
for (i in data.content) {
|
||||
comments.push( new comment( data.content[i] ) )
|
||||
}
|
||||
// not jank boxsizefix:
|
||||
let elem = document.createElement("div")
|
||||
elem.innerHTML = "."
|
||||
elem.classList = ["hidden"]
|
||||
$( ".comments" ).append( elem )
|
||||
})
|
||||
}
|
||||
|
||||
function sendcomment(comment) {
|
||||
// get ip:
|
||||
let token = Math.floor( Math.random()*10**16 )
|
||||
fetch("//derzombiiie.com/getip.php?settoken=" + token, {mode: "no-cors"}).then(()=>{
|
||||
// comment
|
||||
console.log(postDATA.id, comment, token)
|
||||
$.post("/comments", {
|
||||
post: postDATA.id,
|
||||
body: comment,
|
||||
ip: token
|
||||
}).then(
|
||||
reloadcommenting()
|
||||
)
|
||||
})
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ class metaentry {
|
|||
` <div class="entry metaentry">
|
||||
<div class="title" href="/posts?post=${this.id}">${this.title}</div>
|
||||
<hr class="seperator" \\>
|
||||
<div class="preview">${this.additional.content}</div>
|
||||
<div class="preview ${this.additional.class}">${this.additional.content}</div>
|
||||
</div>`
|
||||
element.classList = ["entrybox"]
|
||||
return element
|
||||
|
|
|
@ -6,3 +6,4 @@ $(document).ready(() => {
|
|||
displayshare()
|
||||
displaycomments()
|
||||
})
|
||||
|
||||
|
|
|
@ -69,3 +69,33 @@
|
|||
border-top-width: 1px;
|
||||
border-top-style: solid;
|
||||
}
|
||||
|
||||
/* comments */
|
||||
.comments > .comment {
|
||||
border-style: solid;
|
||||
border-width: 1px;
|
||||
border-color: #606060;
|
||||
border-top-style: solid;
|
||||
border-top-width: 0.4em;
|
||||
border-top-color: #606060;
|
||||
}
|
||||
.comments > .comment:hover {
|
||||
background-color: #60606033
|
||||
}
|
||||
.comments > .comment > .authorbox > .author {
|
||||
position: relative;
|
||||
font-size: 1.5em;
|
||||
font-family: slkscr;
|
||||
color: black;
|
||||
}
|
||||
.comments > .comment > .authorbox > .timestamp {
|
||||
font-size: 0.8em;
|
||||
font-style: italic;
|
||||
}
|
||||
.comments > .hidden {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
|
||||
/* other stuff */
|
||||
|
||||
|
|
|
@ -105,3 +105,28 @@ textarea.comment {
|
|||
width: calc( 100% - 0.5em);
|
||||
height: 5em;
|
||||
}
|
||||
|
||||
.comments > .comment {
|
||||
position: relative;
|
||||
width: calc( 100% - 4em );
|
||||
left: 1em;
|
||||
top: 1em;
|
||||
padding-left: 0.5em;
|
||||
padding-right: 0.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
.comments > .comment > .author {
|
||||
position: relative;
|
||||
left: 0px;
|
||||
}
|
||||
.comments > .comment > .seperator {
|
||||
position: relative;
|
||||
top: 0.2em;
|
||||
height: 0px;
|
||||
}
|
||||
.comments > .comment > .body {
|
||||
position: relative;
|
||||
top: 0.5em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
|
|
56
node/app.js
56
node/app.js
|
@ -1,15 +1,13 @@
|
|||
const fs = require("fs")
|
||||
const express = require("express")
|
||||
const fetch = require("node-fetch")
|
||||
const geoip = require("geoip-ultralight")
|
||||
const filestuff = require("./filestuff")
|
||||
var con = require("./console")
|
||||
const JSONdb = require("simple-json-db")
|
||||
var posts = require("./posts")
|
||||
var comments = require("./comments")
|
||||
//var conf = require("./config")
|
||||
conf = {
|
||||
"debug": true
|
||||
}
|
||||
var conf = require("./config")
|
||||
|
||||
// general TODO:
|
||||
// config! ./config.js for some configuration in js just some form stuff
|
||||
|
@ -58,23 +56,24 @@ con.registercmd( "comment", (arg => {
|
|||
break
|
||||
|
||||
case "push":
|
||||
if (!arg[1] || !arg[2] || !arg[3]) {
|
||||
return console.log("Not all args specified!\nUsage: comment push <time> <author> <content>...")
|
||||
if (!arg[1] || !arg[2] || !arg[3] || !arg[4]) {
|
||||
return console.log("Not all args specified!\nUsage: comment push <post> <time> <author> <content>...")
|
||||
}
|
||||
body = Object.assign([], arg[3])
|
||||
body = Object.assign([], arg)
|
||||
body.shift()
|
||||
body.shift()
|
||||
body.shift()
|
||||
body.shift()
|
||||
body.shift()
|
||||
|
||||
let time = arg[1]
|
||||
if (arg[1] == "auto") {
|
||||
let time = arg[2]
|
||||
if (arg[2] == "auto") {
|
||||
time = new Date().getTime()
|
||||
console.log( "Auto-time: using time: " + time)
|
||||
}
|
||||
console.log( comments.push({
|
||||
console.log( comments.push(arg[1], {
|
||||
"time": time,
|
||||
"author":arg[2],
|
||||
"author":arg[3],
|
||||
"body": body.join(" ")
|
||||
}) )
|
||||
break
|
||||
|
@ -92,7 +91,7 @@ con.registercmd( "comment", (arg => {
|
|||
|
||||
case "sync":
|
||||
console.log("syncing...")
|
||||
t = commetDB.sync()
|
||||
t = commetsDB.sync()
|
||||
if (t) console.log(t)
|
||||
console.log("DONE!")
|
||||
break
|
||||
|
@ -192,26 +191,41 @@ app.get("/posts", (req, res) => {
|
|||
})
|
||||
|
||||
// comments:
|
||||
app.get("/comments", (req, res) => {
|
||||
switch (true) {
|
||||
case typeof( req.query.post ) != "undefined":
|
||||
if ( req.query.body && req.query.ip ) {
|
||||
app.use(express.json())
|
||||
app.use(express.urlencoded({extended: true}))
|
||||
app.all("/comments", (req, res) => {
|
||||
res.type("application/json")
|
||||
let ret
|
||||
let waiting
|
||||
if ( req.body.body && req.body.ip && req.body.post ) {
|
||||
console.log("commentetded!")
|
||||
// get ip from tkn
|
||||
fetch("https://derzombiiie.com/getip.php?token="+req.query.ip).then(
|
||||
waiting = true
|
||||
fetch("https://derzombiiie.com/getip.php?token="+req.body.ip).then(
|
||||
d => d.text()).then(data=>{
|
||||
if (data == "") {
|
||||
res.end(JSON.stringify({"type":"err","text":"ip-tkn"}))
|
||||
return
|
||||
}
|
||||
comments.push({
|
||||
ret = comments.push(req.body.post, {
|
||||
"time": new Date().getTime(),
|
||||
"author":data,
|
||||
"body": req.query.body
|
||||
"authorinfo":{"country":geoip.lookupCountry(data)},
|
||||
"body": req.body.body
|
||||
})
|
||||
res.end(JSON.stringify({"type":"s","text":"commented!"}))
|
||||
if ( ret.type == "err" ) res.status( 400 )
|
||||
res.end(JSON.stringify( ret ))
|
||||
})
|
||||
}
|
||||
break
|
||||
|
||||
if ( req.query.post ) {
|
||||
ret = comments.get(req.query.post, req.query.len ? req.query.len : undefined)
|
||||
if ( ret.type == "err" ) res.status( 400 )
|
||||
res.end( JSON.stringify(ret) )
|
||||
}
|
||||
if ( !res.finished && !waiting ) {
|
||||
res.status ( 501 )
|
||||
res.end(JSON.stringify({"type":"err","text":"not implemented!"}))
|
||||
}
|
||||
})
|
||||
|
||||
|
|
|
@ -3,40 +3,34 @@ this.init = (db) => {
|
|||
return this
|
||||
}
|
||||
|
||||
// key structur:
|
||||
// postID-commentID
|
||||
this.push = (post, comment) => {
|
||||
if (!post) return {"type":"err","text":"no post"}
|
||||
if (!comment.body || !comment.time || !comment.author) return {"type":"err","text":"Comment not complete"}
|
||||
|
||||
// how 2 requests:
|
||||
// POST /comment // create comment
|
||||
// comment:
|
||||
// ---
|
||||
// GET /comment // read out comments
|
||||
// post:
|
||||
let len = this.db.get(post + "-len") ? this.db.get(post + "-len") : 0
|
||||
this.db.set(post + "-" + len, comment)
|
||||
this.db.set(post + "-len", len + 1)
|
||||
|
||||
// CMDs
|
||||
// comment postID [ commentID ]
|
||||
return {"type":"s", "text":"Commented!", "content":len}
|
||||
}
|
||||
|
||||
this.get = (post, len) => {
|
||||
if ( !post ) return {"type":"err","text":"no post"}
|
||||
if ( !(typeof(len) == "number" || typeof(len) == "undefined") ) return {"type":"err","text":"bad length"}
|
||||
|
||||
let length = this.db.get(post + "-len") ? this.db.get(post + "-len") : 0
|
||||
let abslen = this.db.get(post + "-len") ? this.db.get(post + "-len") : 0
|
||||
if ( len == 0 ) return {"type":"s","content":[]}
|
||||
if ( typeof(len) == "number") {
|
||||
if ( len < length ) length = len
|
||||
}
|
||||
|
||||
// calc offset (if any)
|
||||
let offset = abslen - length
|
||||
|
||||
// generating "hot" + "new" articles etc. (should run 1 / 0:30h and at startup + on new article)
|
||||
this.read = (count, sort) => {
|
||||
let ret = []
|
||||
debugger
|
||||
switch (sort) {
|
||||
case "hot":
|
||||
this.ranking.hot.length
|
||||
for ( let i = 0 ; i < count ; i++ ) {
|
||||
if ( typeof( this.ranking.hot[i] ) == "number" ) {
|
||||
ret.push( this.db.get( this.ranking.hot[i] ) )
|
||||
for ( let i = length-1 ; i > -1 ; i-- ) {
|
||||
ret.push( this.db.get(post + "-" + (i + offset) ) )
|
||||
}
|
||||
}
|
||||
|
||||
break
|
||||
|
||||
default:
|
||||
for ( let i = 0 ; i < count ; i++ ) {
|
||||
if ( this.db.get( i ) )
|
||||
ret.push( this.db.get( i ) )
|
||||
}
|
||||
break
|
||||
}
|
||||
return ret
|
||||
return {"type":"s","content":ret}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
this = {
|
||||
// config go here:
|
||||
debug: false, // debug dosnt rly do anything
|
||||
logging: 0 // WIP - adjust logging levels
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
[submodule "deps/nodeunit"]
|
||||
path = deps/nodeunit
|
||||
url = git://github.com/caolan/nodeunit.git
|
||||
[submodule "deps/UglifyJS"]
|
||||
path = deps/UglifyJS
|
||||
url = https://github.com/mishoo/UglifyJS.git
|
||||
[submodule "deps/nodelint"]
|
||||
path = deps/nodelint
|
||||
url = https://github.com/tav/nodelint.git
|
|
@ -0,0 +1,4 @@
|
|||
deps
|
||||
dist
|
||||
test
|
||||
nodelint.cfg
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2010 Caolan McMahon
|
||||
|
||||
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.
|
|
@ -0,0 +1,25 @@
|
|||
PACKAGE = asyncjs
|
||||
NODEJS = $(if $(shell test -f /usr/bin/nodejs && echo "true"),nodejs,node)
|
||||
CWD := $(shell pwd)
|
||||
NODEUNIT = $(CWD)/node_modules/nodeunit/bin/nodeunit
|
||||
UGLIFY = $(CWD)/node_modules/uglify-js/bin/uglifyjs
|
||||
NODELINT = $(CWD)/node_modules/nodelint/nodelint
|
||||
|
||||
BUILDDIR = dist
|
||||
|
||||
all: clean test build
|
||||
|
||||
build: $(wildcard lib/*.js)
|
||||
mkdir -p $(BUILDDIR)
|
||||
$(UGLIFY) lib/async.js > $(BUILDDIR)/async.min.js
|
||||
|
||||
test:
|
||||
$(NODEUNIT) test
|
||||
|
||||
clean:
|
||||
rm -rf $(BUILDDIR)
|
||||
|
||||
lint:
|
||||
$(NODELINT) --config nodelint.cfg lib/async.js
|
||||
|
||||
.PHONY: test build all
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,3 @@
|
|||
// This file is just added for convenience so this repository can be
|
||||
// directly checked out into a project's deps folder
|
||||
module.exports = require('./lib/async');
|
|
@ -0,0 +1,692 @@
|
|||
/*global setTimeout: false, console: false */
|
||||
(function () {
|
||||
|
||||
var async = {};
|
||||
|
||||
// global on the server, window in the browser
|
||||
var root = this,
|
||||
previous_async = root.async;
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = async;
|
||||
}
|
||||
else {
|
||||
root.async = async;
|
||||
}
|
||||
|
||||
async.noConflict = function () {
|
||||
root.async = previous_async;
|
||||
return async;
|
||||
};
|
||||
|
||||
//// cross-browser compatiblity functions ////
|
||||
|
||||
var _forEach = function (arr, iterator) {
|
||||
if (arr.forEach) {
|
||||
return arr.forEach(iterator);
|
||||
}
|
||||
for (var i = 0; i < arr.length; i += 1) {
|
||||
iterator(arr[i], i, arr);
|
||||
}
|
||||
};
|
||||
|
||||
var _map = function (arr, iterator) {
|
||||
if (arr.map) {
|
||||
return arr.map(iterator);
|
||||
}
|
||||
var results = [];
|
||||
_forEach(arr, function (x, i, a) {
|
||||
results.push(iterator(x, i, a));
|
||||
});
|
||||
return results;
|
||||
};
|
||||
|
||||
var _reduce = function (arr, iterator, memo) {
|
||||
if (arr.reduce) {
|
||||
return arr.reduce(iterator, memo);
|
||||
}
|
||||
_forEach(arr, function (x, i, a) {
|
||||
memo = iterator(memo, x, i, a);
|
||||
});
|
||||
return memo;
|
||||
};
|
||||
|
||||
var _keys = function (obj) {
|
||||
if (Object.keys) {
|
||||
return Object.keys(obj);
|
||||
}
|
||||
var keys = [];
|
||||
for (var k in obj) {
|
||||
if (obj.hasOwnProperty(k)) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
return keys;
|
||||
};
|
||||
|
||||
//// exported async module functions ////
|
||||
|
||||
//// nextTick implementation with browser-compatible fallback ////
|
||||
if (typeof process === 'undefined' || !(process.nextTick)) {
|
||||
async.nextTick = function (fn) {
|
||||
setTimeout(fn, 0);
|
||||
};
|
||||
}
|
||||
else {
|
||||
async.nextTick = process.nextTick;
|
||||
}
|
||||
|
||||
async.forEach = function (arr, iterator, callback) {
|
||||
callback = callback || function () {};
|
||||
if (!arr.length) {
|
||||
return callback();
|
||||
}
|
||||
var completed = 0;
|
||||
_forEach(arr, function (x) {
|
||||
iterator(x, function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed += 1;
|
||||
if (completed === arr.length) {
|
||||
callback(null);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
async.forEachSeries = function (arr, iterator, callback) {
|
||||
callback = callback || function () {};
|
||||
if (!arr.length) {
|
||||
return callback();
|
||||
}
|
||||
var completed = 0;
|
||||
var iterate = function () {
|
||||
iterator(arr[completed], function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed += 1;
|
||||
if (completed === arr.length) {
|
||||
callback(null);
|
||||
}
|
||||
else {
|
||||
iterate();
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
iterate();
|
||||
};
|
||||
|
||||
async.forEachLimit = function (arr, limit, iterator, callback) {
|
||||
callback = callback || function () {};
|
||||
if (!arr.length || limit <= 0) {
|
||||
return callback();
|
||||
}
|
||||
var completed = 0;
|
||||
var started = 0;
|
||||
var running = 0;
|
||||
|
||||
(function replenish () {
|
||||
if (completed === arr.length) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
while (running < limit && started < arr.length) {
|
||||
started += 1;
|
||||
running += 1;
|
||||
iterator(arr[started - 1], function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
completed += 1;
|
||||
running -= 1;
|
||||
if (completed === arr.length) {
|
||||
callback();
|
||||
}
|
||||
else {
|
||||
replenish();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
})();
|
||||
};
|
||||
|
||||
|
||||
var doParallel = function (fn) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return fn.apply(null, [async.forEach].concat(args));
|
||||
};
|
||||
};
|
||||
var doSeries = function (fn) {
|
||||
return function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
return fn.apply(null, [async.forEachSeries].concat(args));
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
var _asyncMap = function (eachfn, arr, iterator, callback) {
|
||||
var results = [];
|
||||
arr = _map(arr, function (x, i) {
|
||||
return {index: i, value: x};
|
||||
});
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x.value, function (err, v) {
|
||||
results[x.index] = v;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
};
|
||||
async.map = doParallel(_asyncMap);
|
||||
async.mapSeries = doSeries(_asyncMap);
|
||||
|
||||
|
||||
// reduce only has a series version, as doing reduce in parallel won't
|
||||
// work in many situations.
|
||||
async.reduce = function (arr, memo, iterator, callback) {
|
||||
async.forEachSeries(arr, function (x, callback) {
|
||||
iterator(memo, x, function (err, v) {
|
||||
memo = v;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, memo);
|
||||
});
|
||||
};
|
||||
// inject alias
|
||||
async.inject = async.reduce;
|
||||
// foldl alias
|
||||
async.foldl = async.reduce;
|
||||
|
||||
async.reduceRight = function (arr, memo, iterator, callback) {
|
||||
var reversed = _map(arr, function (x) {
|
||||
return x;
|
||||
}).reverse();
|
||||
async.reduce(reversed, memo, iterator, callback);
|
||||
};
|
||||
// foldr alias
|
||||
async.foldr = async.reduceRight;
|
||||
|
||||
var _filter = function (eachfn, arr, iterator, callback) {
|
||||
var results = [];
|
||||
arr = _map(arr, function (x, i) {
|
||||
return {index: i, value: x};
|
||||
});
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x.value, function (v) {
|
||||
if (v) {
|
||||
results.push(x);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
callback(_map(results.sort(function (a, b) {
|
||||
return a.index - b.index;
|
||||
}), function (x) {
|
||||
return x.value;
|
||||
}));
|
||||
});
|
||||
};
|
||||
async.filter = doParallel(_filter);
|
||||
async.filterSeries = doSeries(_filter);
|
||||
// select alias
|
||||
async.select = async.filter;
|
||||
async.selectSeries = async.filterSeries;
|
||||
|
||||
var _reject = function (eachfn, arr, iterator, callback) {
|
||||
var results = [];
|
||||
arr = _map(arr, function (x, i) {
|
||||
return {index: i, value: x};
|
||||
});
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x.value, function (v) {
|
||||
if (!v) {
|
||||
results.push(x);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
callback(_map(results.sort(function (a, b) {
|
||||
return a.index - b.index;
|
||||
}), function (x) {
|
||||
return x.value;
|
||||
}));
|
||||
});
|
||||
};
|
||||
async.reject = doParallel(_reject);
|
||||
async.rejectSeries = doSeries(_reject);
|
||||
|
||||
var _detect = function (eachfn, arr, iterator, main_callback) {
|
||||
eachfn(arr, function (x, callback) {
|
||||
iterator(x, function (result) {
|
||||
if (result) {
|
||||
main_callback(x);
|
||||
main_callback = function () {};
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
});
|
||||
}, function (err) {
|
||||
main_callback();
|
||||
});
|
||||
};
|
||||
async.detect = doParallel(_detect);
|
||||
async.detectSeries = doSeries(_detect);
|
||||
|
||||
async.some = function (arr, iterator, main_callback) {
|
||||
async.forEach(arr, function (x, callback) {
|
||||
iterator(x, function (v) {
|
||||
if (v) {
|
||||
main_callback(true);
|
||||
main_callback = function () {};
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
main_callback(false);
|
||||
});
|
||||
};
|
||||
// any alias
|
||||
async.any = async.some;
|
||||
|
||||
async.every = function (arr, iterator, main_callback) {
|
||||
async.forEach(arr, function (x, callback) {
|
||||
iterator(x, function (v) {
|
||||
if (!v) {
|
||||
main_callback(false);
|
||||
main_callback = function () {};
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}, function (err) {
|
||||
main_callback(true);
|
||||
});
|
||||
};
|
||||
// all alias
|
||||
async.all = async.every;
|
||||
|
||||
async.sortBy = function (arr, iterator, callback) {
|
||||
async.map(arr, function (x, callback) {
|
||||
iterator(x, function (err, criteria) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
}
|
||||
else {
|
||||
callback(null, {value: x, criteria: criteria});
|
||||
}
|
||||
});
|
||||
}, function (err, results) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
else {
|
||||
var fn = function (left, right) {
|
||||
var a = left.criteria, b = right.criteria;
|
||||
return a < b ? -1 : a > b ? 1 : 0;
|
||||
};
|
||||
callback(null, _map(results.sort(fn), function (x) {
|
||||
return x.value;
|
||||
}));
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async.auto = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
var keys = _keys(tasks);
|
||||
if (!keys.length) {
|
||||
return callback(null);
|
||||
}
|
||||
|
||||
var results = {};
|
||||
|
||||
var listeners = [];
|
||||
var addListener = function (fn) {
|
||||
listeners.unshift(fn);
|
||||
};
|
||||
var removeListener = function (fn) {
|
||||
for (var i = 0; i < listeners.length; i += 1) {
|
||||
if (listeners[i] === fn) {
|
||||
listeners.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
var taskComplete = function () {
|
||||
_forEach(listeners.slice(0), function (fn) {
|
||||
fn();
|
||||
});
|
||||
};
|
||||
|
||||
addListener(function () {
|
||||
if (_keys(results).length === keys.length) {
|
||||
callback(null, results);
|
||||
callback = function () {};
|
||||
}
|
||||
});
|
||||
|
||||
_forEach(keys, function (k) {
|
||||
var task = (tasks[k] instanceof Function) ? [tasks[k]]: tasks[k];
|
||||
var taskCallback = function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
// stop subsequent errors hitting callback multiple times
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
results[k] = args;
|
||||
taskComplete();
|
||||
}
|
||||
};
|
||||
var requires = task.slice(0, Math.abs(task.length - 1)) || [];
|
||||
var ready = function () {
|
||||
return _reduce(requires, function (a, x) {
|
||||
return (a && results.hasOwnProperty(x));
|
||||
}, true) && !results.hasOwnProperty(k);
|
||||
};
|
||||
if (ready()) {
|
||||
task[task.length - 1](taskCallback, results);
|
||||
}
|
||||
else {
|
||||
var listener = function () {
|
||||
if (ready()) {
|
||||
removeListener(listener);
|
||||
task[task.length - 1](taskCallback, results);
|
||||
}
|
||||
};
|
||||
addListener(listener);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
async.waterfall = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
if (!tasks.length) {
|
||||
return callback();
|
||||
}
|
||||
var wrapIterator = function (iterator) {
|
||||
return function (err) {
|
||||
if (err) {
|
||||
callback(err);
|
||||
callback = function () {};
|
||||
}
|
||||
else {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
var next = iterator.next();
|
||||
if (next) {
|
||||
args.push(wrapIterator(next));
|
||||
}
|
||||
else {
|
||||
args.push(callback);
|
||||
}
|
||||
async.nextTick(function () {
|
||||
iterator.apply(null, args);
|
||||
});
|
||||
}
|
||||
};
|
||||
};
|
||||
wrapIterator(async.iterator(tasks))();
|
||||
};
|
||||
|
||||
async.parallel = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
if (tasks.constructor === Array) {
|
||||
async.map(tasks, function (fn, callback) {
|
||||
if (fn) {
|
||||
fn(function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
callback.call(null, err, args);
|
||||
});
|
||||
}
|
||||
}, callback);
|
||||
}
|
||||
else {
|
||||
var results = {};
|
||||
async.forEach(_keys(tasks), function (k, callback) {
|
||||
tasks[k](function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
results[k] = args;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async.series = function (tasks, callback) {
|
||||
callback = callback || function () {};
|
||||
if (tasks.constructor === Array) {
|
||||
async.mapSeries(tasks, function (fn, callback) {
|
||||
if (fn) {
|
||||
fn(function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
callback.call(null, err, args);
|
||||
});
|
||||
}
|
||||
}, callback);
|
||||
}
|
||||
else {
|
||||
var results = {};
|
||||
async.forEachSeries(_keys(tasks), function (k, callback) {
|
||||
tasks[k](function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (args.length <= 1) {
|
||||
args = args[0];
|
||||
}
|
||||
results[k] = args;
|
||||
callback(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, results);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
async.iterator = function (tasks) {
|
||||
var makeCallback = function (index) {
|
||||
var fn = function () {
|
||||
if (tasks.length) {
|
||||
tasks[index].apply(null, arguments);
|
||||
}
|
||||
return fn.next();
|
||||
};
|
||||
fn.next = function () {
|
||||
return (index < tasks.length - 1) ? makeCallback(index + 1): null;
|
||||
};
|
||||
return fn;
|
||||
};
|
||||
return makeCallback(0);
|
||||
};
|
||||
|
||||
async.apply = function (fn) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
return function () {
|
||||
return fn.apply(
|
||||
null, args.concat(Array.prototype.slice.call(arguments))
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
var _concat = function (eachfn, arr, fn, callback) {
|
||||
var r = [];
|
||||
eachfn(arr, function (x, cb) {
|
||||
fn(x, function (err, y) {
|
||||
r = r.concat(y || []);
|
||||
cb(err);
|
||||
});
|
||||
}, function (err) {
|
||||
callback(err, r);
|
||||
});
|
||||
};
|
||||
async.concat = doParallel(_concat);
|
||||
async.concatSeries = doSeries(_concat);
|
||||
|
||||
async.whilst = function (test, iterator, callback) {
|
||||
if (test()) {
|
||||
iterator(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.whilst(test, iterator, callback);
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
async.until = function (test, iterator, callback) {
|
||||
if (!test()) {
|
||||
iterator(function (err) {
|
||||
if (err) {
|
||||
return callback(err);
|
||||
}
|
||||
async.until(test, iterator, callback);
|
||||
});
|
||||
}
|
||||
else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
|
||||
async.queue = function (worker, concurrency) {
|
||||
var workers = 0;
|
||||
var q = {
|
||||
tasks: [],
|
||||
concurrency: concurrency,
|
||||
saturated: null,
|
||||
empty: null,
|
||||
drain: null,
|
||||
push: function (data, callback) {
|
||||
if(data.constructor !== Array) {
|
||||
data = [data];
|
||||
}
|
||||
_forEach(data, function(task) {
|
||||
q.tasks.push({
|
||||
data: task,
|
||||
callback: typeof callback === 'function' ? callback : null
|
||||
});
|
||||
if (q.saturated && q.tasks.length == concurrency) {
|
||||
q.saturated();
|
||||
}
|
||||
async.nextTick(q.process);
|
||||
});
|
||||
},
|
||||
process: function () {
|
||||
if (workers < q.concurrency && q.tasks.length) {
|
||||
var task = q.tasks.shift();
|
||||
if(q.empty && q.tasks.length == 0) q.empty();
|
||||
workers += 1;
|
||||
worker(task.data, function () {
|
||||
workers -= 1;
|
||||
if (task.callback) {
|
||||
task.callback.apply(task, arguments);
|
||||
}
|
||||
if(q.drain && q.tasks.length + workers == 0) q.drain();
|
||||
q.process();
|
||||
});
|
||||
}
|
||||
},
|
||||
length: function () {
|
||||
return q.tasks.length;
|
||||
},
|
||||
running: function () {
|
||||
return workers;
|
||||
}
|
||||
};
|
||||
return q;
|
||||
};
|
||||
|
||||
var _console_fn = function (name) {
|
||||
return function (fn) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
fn.apply(null, args.concat([function (err) {
|
||||
var args = Array.prototype.slice.call(arguments, 1);
|
||||
if (typeof console !== 'undefined') {
|
||||
if (err) {
|
||||
if (console.error) {
|
||||
console.error(err);
|
||||
}
|
||||
}
|
||||
else if (console[name]) {
|
||||
_forEach(args, function (x) {
|
||||
console[name](x);
|
||||
});
|
||||
}
|
||||
}
|
||||
}]));
|
||||
};
|
||||
};
|
||||
async.log = _console_fn('log');
|
||||
async.dir = _console_fn('dir');
|
||||
/*async.info = _console_fn('info');
|
||||
async.warn = _console_fn('warn');
|
||||
async.error = _console_fn('error');*/
|
||||
|
||||
async.memoize = function (fn, hasher) {
|
||||
var memo = {};
|
||||
var queues = {};
|
||||
hasher = hasher || function (x) {
|
||||
return x;
|
||||
};
|
||||
var memoized = function () {
|
||||
var args = Array.prototype.slice.call(arguments);
|
||||
var callback = args.pop();
|
||||
var key = hasher.apply(null, args);
|
||||
if (key in memo) {
|
||||
callback.apply(null, memo[key]);
|
||||
}
|
||||
else if (key in queues) {
|
||||
queues[key].push(callback);
|
||||
}
|
||||
else {
|
||||
queues[key] = [callback];
|
||||
fn.apply(null, args.concat([function () {
|
||||
memo[key] = arguments;
|
||||
var q = queues[key];
|
||||
delete queues[key];
|
||||
for (var i = 0, l = q.length; i < l; i++) {
|
||||
q[i].apply(null, arguments);
|
||||
}
|
||||
}]));
|
||||
}
|
||||
};
|
||||
memoized.unmemoized = fn;
|
||||
return memoized;
|
||||
};
|
||||
|
||||
async.unmemoize = function (fn) {
|
||||
return function () {
|
||||
return (fn.unmemoized || fn).apply(null, arguments);
|
||||
};
|
||||
};
|
||||
|
||||
}());
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"_from": "async@~0.1.22",
|
||||
"_id": "async@0.1.22",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-D8GqoIig4+8Ovi2IMbqw3PiEUGE=",
|
||||
"_location": "/async",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "async@~0.1.22",
|
||||
"name": "async",
|
||||
"escapedName": "async",
|
||||
"rawSpec": "~0.1.22",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "~0.1.22"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/geoip-ultralight"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/async/-/async-0.1.22.tgz",
|
||||
"_shasum": "0fc1aaa088a0e3ef0ebe2d8831bab0dcf8845061",
|
||||
"_spec": "async@~0.1.22",
|
||||
"_where": "/home/pi/blog.testing.derzombiiie.com/node_modules/geoip-ultralight",
|
||||
"author": {
|
||||
"name": "Caolan McMahon"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "http://github.com/caolan/async/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "Higher-order functions and common patterns for asynchronous code",
|
||||
"devDependencies": {
|
||||
"nodelint": ">0.0.0",
|
||||
"nodeunit": ">0.0.0",
|
||||
"uglify-js": "1.2.x"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"homepage": "https://github.com/caolan/async#readme",
|
||||
"licenses": [
|
||||
{
|
||||
"type": "MIT",
|
||||
"url": "http://github.com/caolan/async/raw/master/LICENSE"
|
||||
}
|
||||
],
|
||||
"main": "./index",
|
||||
"name": "async",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/caolan/async.git"
|
||||
},
|
||||
"version": "0.1.22"
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
node_modules
|
|
@ -0,0 +1,4 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 0.4
|
||||
- 0.6
|
|
@ -0,0 +1,177 @@
|
|||
binary
|
||||
======
|
||||
|
||||
Unpack multibyte binary values from buffers and streams.
|
||||
You can specify the endianness and signedness of the fields to be unpacked too.
|
||||
|
||||
This module is a cleaner and more complete version of
|
||||
[bufferlist](https://github.com/substack/node-bufferlist)'s binary module that
|
||||
runs on pre-allocated buffers instead of a linked list.
|
||||
|
||||
[![build status](https://secure.travis-ci.org/substack/node-binary.png)](http://travis-ci.org/substack/node-binary)
|
||||
|
||||
examples
|
||||
========
|
||||
|
||||
stream.js
|
||||
---------
|
||||
|
||||
``` js
|
||||
var binary = require('binary');
|
||||
|
||||
var ws = binary()
|
||||
.word32lu('x')
|
||||
.word16bs('y')
|
||||
.word16bu('z')
|
||||
.tap(function (vars) {
|
||||
console.dir(vars);
|
||||
})
|
||||
;
|
||||
process.stdin.pipe(ws);
|
||||
process.stdin.resume();
|
||||
```
|
||||
|
||||
output:
|
||||
|
||||
```
|
||||
$ node examples/stream.js
|
||||
abcdefgh
|
||||
{ x: 1684234849, y: 25958, z: 26472 }
|
||||
^D
|
||||
```
|
||||
|
||||
parse.js
|
||||
--------
|
||||
|
||||
``` js
|
||||
var buf = new Buffer([ 97, 98, 99, 100, 101, 102, 0 ]);
|
||||
|
||||
var binary = require('binary');
|
||||
var vars = binary.parse(buf)
|
||||
.word16ls('ab')
|
||||
.word32bu('cf')
|
||||
.word8('x')
|
||||
.vars
|
||||
;
|
||||
console.dir(vars);
|
||||
```
|
||||
|
||||
output:
|
||||
|
||||
```
|
||||
{ ab: 25185, cf: 1667523942, x: 0 }
|
||||
```
|
||||
|
||||
methods
|
||||
=======
|
||||
|
||||
`var binary = require('binary')`
|
||||
|
||||
var b = binary()
|
||||
----------------
|
||||
|
||||
Return a new writable stream `b` that has the chainable methods documented below
|
||||
for buffering binary input.
|
||||
|
||||
binary.parse(buf)
|
||||
-----------------
|
||||
|
||||
Parse a static buffer in one pass. Returns a chainable interface with the
|
||||
methods below plus a `vars` field to get at the variable stash as the last item
|
||||
in a chain.
|
||||
|
||||
In parse mode, methods will set their keys to `null` if the buffer isn't big
|
||||
enough except `buffer()` and `scan()` which read up up to the end of the buffer
|
||||
and stop.
|
||||
|
||||
b.word{8,16,32,64}{l,b}{e,u,s}(key)
|
||||
-----------------------------------
|
||||
|
||||
Parse bytes in the buffer or stream given:
|
||||
|
||||
* number of bits
|
||||
* endianness ( l : little, b : big ),
|
||||
* signedness ( u and e : unsigned, s : signed )
|
||||
|
||||
These functions won't start parsing until all previous parser functions have run
|
||||
and the data is available.
|
||||
|
||||
The result of the parse goes into the variable stash at `key`.
|
||||
If `key` has dots (`.`s), it refers to a nested address. If parent container
|
||||
values don't exist they will be created automatically, so for instance you can
|
||||
assign into `dst.addr` and `dst.port` and the `dst` key in the variable stash
|
||||
will be `{ addr : x, port : y }` afterwards.
|
||||
|
||||
b.buffer(key, size)
|
||||
-------------------
|
||||
|
||||
Take `size` bytes directly off the buffer stream, putting the resulting buffer
|
||||
slice in the variable stash at `key`. If `size` is a string, use the value at
|
||||
`vars[size]`. The key follows the same dotted address rules as the word
|
||||
functions.
|
||||
|
||||
b.scan(key, buffer)
|
||||
-------------------
|
||||
|
||||
Search for `buffer` in the stream and store all the intervening data in the
|
||||
stash at at `key`, excluding the search buffer. If `buffer` passed as a string,
|
||||
it will be converted into a Buffer internally.
|
||||
|
||||
For example, to read in a line you can just do:
|
||||
|
||||
``` js
|
||||
var b = binary()
|
||||
.scan('line', new Buffer('\r\n'))
|
||||
.tap(function (vars) {
|
||||
console.log(vars.line)
|
||||
})
|
||||
;
|
||||
stream.pipe(b);
|
||||
```
|
||||
|
||||
b.tap(cb)
|
||||
---------
|
||||
|
||||
The callback `cb` is provided with the variable stash from all the previous
|
||||
actions once they've all finished.
|
||||
|
||||
You can nest additional actions onto `this` inside the callback.
|
||||
|
||||
b.into(key, cb)
|
||||
---------------
|
||||
|
||||
Like `.tap()`, except all nested actions will assign into a `key` in the `vars`
|
||||
stash.
|
||||
|
||||
b.loop(cb)
|
||||
----------
|
||||
|
||||
Loop, each time calling `cb(end, vars)` for function `end` and the variable
|
||||
stash with `this` set to a new chain for nested parsing. The loop terminates
|
||||
once `end` is called.
|
||||
|
||||
b.flush()
|
||||
---------
|
||||
|
||||
Clear the variable stash entirely.
|
||||
|
||||
installation
|
||||
============
|
||||
|
||||
To install with [npm](http://github.com/isaacs/npm):
|
||||
|
||||
```
|
||||
npm install binary
|
||||
```
|
||||
|
||||
notes
|
||||
=====
|
||||
|
||||
The word64 functions will only return approximations since javascript uses ieee
|
||||
floating point for all number types. Mind the loss of precision.
|
||||
|
||||
license
|
||||
=======
|
||||
|
||||
MIT
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
var buf = new Buffer([ 97, 98, 99, 100, 101, 102, 0 ]);
|
||||
|
||||
var binary = require('binary');
|
||||
binary(buf)
|
||||
.word16ls('ab')
|
||||
.word32bu('cf')
|
||||
.word8('x')
|
||||
.tap(function (vars) {
|
||||
console.dir(vars);
|
||||
})
|
||||
;
|
|
@ -0,0 +1,10 @@
|
|||
var buf = new Buffer([ 97, 98, 99, 100, 101, 102, 0 ]);
|
||||
|
||||
var binary = require('binary');
|
||||
var vars = binary.parse(buf)
|
||||
.word16ls('ab')
|
||||
.word32bu('cf')
|
||||
.word8('x')
|
||||
.vars
|
||||
;
|
||||
console.dir(vars);
|
|
@ -0,0 +1,12 @@
|
|||
var binary = require('binary');
|
||||
|
||||
var ws = binary()
|
||||
.word32lu('x')
|
||||
.word16bs('y')
|
||||
.word16bu('z')
|
||||
.tap(function (vars) {
|
||||
console.dir(vars);
|
||||
})
|
||||
;
|
||||
process.stdin.pipe(ws);
|
||||
process.stdin.resume();
|
|
@ -0,0 +1,397 @@
|
|||
var Chainsaw = require('chainsaw');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var Buffers = require('buffers');
|
||||
var Vars = require('./lib/vars.js');
|
||||
var Stream = require('stream').Stream;
|
||||
|
||||
exports = module.exports = function (bufOrEm, eventName) {
|
||||
if (Buffer.isBuffer(bufOrEm)) {
|
||||
return exports.parse(bufOrEm);
|
||||
}
|
||||
|
||||
var s = exports.stream();
|
||||
if (bufOrEm && bufOrEm.pipe) {
|
||||
bufOrEm.pipe(s);
|
||||
}
|
||||
else if (bufOrEm) {
|
||||
bufOrEm.on(eventName || 'data', function (buf) {
|
||||
s.write(buf);
|
||||
});
|
||||
|
||||
bufOrEm.on('end', function () {
|
||||
s.end();
|
||||
});
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
exports.stream = function (input) {
|
||||
if (input) return exports.apply(null, arguments);
|
||||
|
||||
var pending = null;
|
||||
function getBytes (bytes, cb, skip) {
|
||||
pending = {
|
||||
bytes : bytes,
|
||||
skip : skip,
|
||||
cb : function (buf) {
|
||||
pending = null;
|
||||
cb(buf);
|
||||
},
|
||||
};
|
||||
dispatch();
|
||||
}
|
||||
|
||||
var offset = null;
|
||||
function dispatch () {
|
||||
if (!pending) {
|
||||
if (caughtEnd) done = true;
|
||||
return;
|
||||
}
|
||||
if (typeof pending === 'function') {
|
||||
pending();
|
||||
}
|
||||
else {
|
||||
var bytes = offset + pending.bytes;
|
||||
|
||||
if (buffers.length >= bytes) {
|
||||
var buf;
|
||||
if (offset == null) {
|
||||
buf = buffers.splice(0, bytes);
|
||||
if (!pending.skip) {
|
||||
buf = buf.slice();
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (!pending.skip) {
|
||||
buf = buffers.slice(offset, bytes);
|
||||
}
|
||||
offset = bytes;
|
||||
}
|
||||
|
||||
if (pending.skip) {
|
||||
pending.cb();
|
||||
}
|
||||
else {
|
||||
pending.cb(buf);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function builder (saw) {
|
||||
function next () { if (!done) saw.next() }
|
||||
|
||||
var self = words(function (bytes, cb) {
|
||||
return function (name) {
|
||||
getBytes(bytes, function (buf) {
|
||||
vars.set(name, cb(buf));
|
||||
next();
|
||||
});
|
||||
};
|
||||
});
|
||||
|
||||
self.tap = function (cb) {
|
||||
saw.nest(cb, vars.store);
|
||||
};
|
||||
|
||||
self.into = function (key, cb) {
|
||||
if (!vars.get(key)) vars.set(key, {});
|
||||
var parent = vars;
|
||||
vars = Vars(parent.get(key));
|
||||
|
||||
saw.nest(function () {
|
||||
cb.apply(this, arguments);
|
||||
this.tap(function () {
|
||||
vars = parent;
|
||||
});
|
||||
}, vars.store);
|
||||
};
|
||||
|
||||
self.flush = function () {
|
||||
vars.store = {};
|
||||
next();
|
||||
};
|
||||
|
||||
self.loop = function (cb) {
|
||||
var end = false;
|
||||
|
||||
saw.nest(false, function loop () {
|
||||
this.vars = vars.store;
|
||||
cb.call(this, function () {
|
||||
end = true;
|
||||
next();
|
||||
}, vars.store);
|
||||
this.tap(function () {
|
||||
if (end) saw.next()
|
||||
else loop.call(this)
|
||||
}.bind(this));
|
||||
}, vars.store);
|
||||
};
|
||||
|
||||
self.buffer = function (name, bytes) {
|
||||
if (typeof bytes === 'string') {
|
||||
bytes = vars.get(bytes);
|
||||
}
|
||||
|
||||
getBytes(bytes, function (buf) {
|
||||
vars.set(name, buf);
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
self.skip = function (bytes) {
|
||||
if (typeof bytes === 'string') {
|
||||
bytes = vars.get(bytes);
|
||||
}
|
||||
|
||||
getBytes(bytes, function () {
|
||||
next();
|
||||
});
|
||||
};
|
||||
|
||||
self.scan = function find (name, search) {
|
||||
if (typeof search === 'string') {
|
||||
search = new Buffer(search);
|
||||
}
|
||||
else if (!Buffer.isBuffer(search)) {
|
||||
throw new Error('search must be a Buffer or a string');
|
||||
}
|
||||
|
||||
var taken = 0;
|
||||
pending = function () {
|
||||
var pos = buffers.indexOf(search, offset + taken);
|
||||
var i = pos-offset-taken;
|
||||
if (pos !== -1) {
|
||||
pending = null;
|
||||
if (offset != null) {
|
||||
vars.set(
|
||||
name,
|
||||
buffers.slice(offset, offset + taken + i)
|
||||
);
|
||||
offset += taken + i + search.length;
|
||||
}
|
||||
else {
|
||||
vars.set(
|
||||
name,
|
||||
buffers.slice(0, taken + i)
|
||||
);
|
||||
buffers.splice(0, taken + i + search.length);
|
||||
}
|
||||
next();
|
||||
dispatch();
|
||||
} else {
|
||||
i = Math.max(buffers.length - search.length - offset - taken, 0);
|
||||
}
|
||||
taken += i;
|
||||
};
|
||||
dispatch();
|
||||
};
|
||||
|
||||
self.peek = function (cb) {
|
||||
offset = 0;
|
||||
saw.nest(function () {
|
||||
cb.call(this, vars.store);
|
||||
this.tap(function () {
|
||||
offset = null;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
var stream = Chainsaw.light(builder);
|
||||
stream.writable = true;
|
||||
|
||||
var buffers = Buffers();
|
||||
|
||||
stream.write = function (buf) {
|
||||
buffers.push(buf);
|
||||
dispatch();
|
||||
};
|
||||
|
||||
var vars = Vars();
|
||||
|
||||
var done = false, caughtEnd = false;
|
||||
stream.end = function () {
|
||||
caughtEnd = true;
|
||||
};
|
||||
|
||||
stream.pipe = Stream.prototype.pipe;
|
||||
Object.getOwnPropertyNames(EventEmitter.prototype).forEach(function (name) {
|
||||
stream[name] = EventEmitter.prototype[name];
|
||||
});
|
||||
|
||||
return stream;
|
||||
};
|
||||
|
||||
exports.parse = function parse (buffer) {
|
||||
var self = words(function (bytes, cb) {
|
||||
return function (name) {
|
||||
if (offset + bytes <= buffer.length) {
|
||||
var buf = buffer.slice(offset, offset + bytes);
|
||||
offset += bytes;
|
||||
vars.set(name, cb(buf));
|
||||
}
|
||||
else {
|
||||
vars.set(name, null);
|
||||
}
|
||||
return self;
|
||||
};
|
||||
});
|
||||
|
||||
var offset = 0;
|
||||
var vars = Vars();
|
||||
self.vars = vars.store;
|
||||
|
||||
self.tap = function (cb) {
|
||||
cb.call(self, vars.store);
|
||||
return self;
|
||||
};
|
||||
|
||||
self.into = function (key, cb) {
|
||||
if (!vars.get(key)) {
|
||||
vars.set(key, {});
|
||||
}
|
||||
var parent = vars;
|
||||
vars = Vars(parent.get(key));
|
||||
cb.call(self, vars.store);
|
||||
vars = parent;
|
||||
return self;
|
||||
};
|
||||
|
||||
self.loop = function (cb) {
|
||||
var end = false;
|
||||
var ender = function () { end = true };
|
||||
while (end === false) {
|
||||
cb.call(self, ender, vars.store);
|
||||
}
|
||||
return self;
|
||||
};
|
||||
|
||||
self.buffer = function (name, size) {
|
||||
if (typeof size === 'string') {
|
||||
size = vars.get(size);
|
||||
}
|
||||
var buf = buffer.slice(offset, Math.min(buffer.length, offset + size));
|
||||
offset += size;
|
||||
vars.set(name, buf);
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
self.skip = function (bytes) {
|
||||
if (typeof bytes === 'string') {
|
||||
bytes = vars.get(bytes);
|
||||
}
|
||||
offset += bytes;
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
self.scan = function (name, search) {
|
||||
if (typeof search === 'string') {
|
||||
search = new Buffer(search);
|
||||
}
|
||||
else if (!Buffer.isBuffer(search)) {
|
||||
throw new Error('search must be a Buffer or a string');
|
||||
}
|
||||
vars.set(name, null);
|
||||
|
||||
// simple but slow string search
|
||||
for (var i = 0; i + offset <= buffer.length - search.length + 1; i++) {
|
||||
for (
|
||||
var j = 0;
|
||||
j < search.length && buffer[offset+i+j] === search[j];
|
||||
j++
|
||||
);
|
||||
if (j === search.length) break;
|
||||
}
|
||||
|
||||
vars.set(name, buffer.slice(offset, offset + i));
|
||||
offset += i + search.length;
|
||||
return self;
|
||||
};
|
||||
|
||||
self.peek = function (cb) {
|
||||
var was = offset;
|
||||
cb.call(self, vars.store);
|
||||
offset = was;
|
||||
return self;
|
||||
};
|
||||
|
||||
self.flush = function () {
|
||||
vars.store = {};
|
||||
return self;
|
||||
};
|
||||
|
||||
self.eof = function () {
|
||||
return offset >= buffer.length;
|
||||
};
|
||||
|
||||
return self;
|
||||
};
|
||||
|
||||
// convert byte strings to unsigned little endian numbers
|
||||
function decodeLEu (bytes) {
|
||||
var acc = 0;
|
||||
for (var i = 0; i < bytes.length; i++) {
|
||||
acc += Math.pow(256,i) * bytes[i];
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// convert byte strings to unsigned big endian numbers
|
||||
function decodeBEu (bytes) {
|
||||
var acc = 0;
|
||||
for (var i = 0; i < bytes.length; i++) {
|
||||
acc += Math.pow(256, bytes.length - i - 1) * bytes[i];
|
||||
}
|
||||
return acc;
|
||||
}
|
||||
|
||||
// convert byte strings to signed big endian numbers
|
||||
function decodeBEs (bytes) {
|
||||
var val = decodeBEu(bytes);
|
||||
if ((bytes[0] & 0x80) == 0x80) {
|
||||
val -= Math.pow(256, bytes.length);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
// convert byte strings to signed little endian numbers
|
||||
function decodeLEs (bytes) {
|
||||
var val = decodeLEu(bytes);
|
||||
if ((bytes[bytes.length - 1] & 0x80) == 0x80) {
|
||||
val -= Math.pow(256, bytes.length);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
function words (decode) {
|
||||
var self = {};
|
||||
|
||||
[ 1, 2, 4, 8 ].forEach(function (bytes) {
|
||||
var bits = bytes * 8;
|
||||
|
||||
self['word' + bits + 'le']
|
||||
= self['word' + bits + 'lu']
|
||||
= decode(bytes, decodeLEu);
|
||||
|
||||
self['word' + bits + 'ls']
|
||||
= decode(bytes, decodeLEs);
|
||||
|
||||
self['word' + bits + 'be']
|
||||
= self['word' + bits + 'bu']
|
||||
= decode(bytes, decodeBEu);
|
||||
|
||||
self['word' + bits + 'bs']
|
||||
= decode(bytes, decodeBEs);
|
||||
});
|
||||
|
||||
// word8be(n) == word8le(n) for all n
|
||||
self.word8 = self.word8u = self.word8be;
|
||||
self.word8s = self.word8bs;
|
||||
|
||||
return self;
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
module.exports = function (store) {
|
||||
function getset (name, value) {
|
||||
var node = vars.store;
|
||||
var keys = name.split('.');
|
||||
keys.slice(0,-1).forEach(function (k) {
|
||||
if (node[k] === undefined) node[k] = {};
|
||||
node = node[k]
|
||||
});
|
||||
var key = keys[keys.length - 1];
|
||||
if (arguments.length == 1) {
|
||||
return node[key];
|
||||
}
|
||||
else {
|
||||
return node[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
var vars = {
|
||||
get : function (name) {
|
||||
return getset(name);
|
||||
},
|
||||
set : function (name, value) {
|
||||
return getset(name, value);
|
||||
},
|
||||
store : store || {},
|
||||
};
|
||||
return vars;
|
||||
};
|
|
@ -0,0 +1,70 @@
|
|||
{
|
||||
"_from": "binary@~0.3.0",
|
||||
"_id": "binary@0.3.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=",
|
||||
"_location": "/binary",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "binary@~0.3.0",
|
||||
"name": "binary",
|
||||
"escapedName": "binary",
|
||||
"rawSpec": "~0.3.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "~0.3.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/unzip"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz",
|
||||
"_shasum": "9f60553bc5ce8c3386f3b553cff47462adecaa79",
|
||||
"_spec": "binary@~0.3.0",
|
||||
"_where": "/home/pi/blog.testing.derzombiiie.com/node_modules/unzip",
|
||||
"author": {
|
||||
"name": "James Halliday",
|
||||
"email": "mail@substack.net",
|
||||
"url": "http://substack.net"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/substack/node-binary/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"buffers": "~0.1.1",
|
||||
"chainsaw": "~0.1.0"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "Unpack multibyte binary values from buffers",
|
||||
"devDependencies": {
|
||||
"seq": "~0.2.5",
|
||||
"tap": "~0.2.4"
|
||||
},
|
||||
"engine": {
|
||||
"node": ">=0.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"homepage": "https://github.com/substack/node-binary#readme",
|
||||
"keywords": [
|
||||
"binary",
|
||||
"decode",
|
||||
"endian",
|
||||
"unpack",
|
||||
"signed",
|
||||
"unsigned"
|
||||
],
|
||||
"license": "MIT",
|
||||
"main": "./index.js",
|
||||
"name": "binary",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/substack/node-binary.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "tap test/*.js"
|
||||
},
|
||||
"version": "0.3.0"
|
||||
}
|
|
@ -0,0 +1,92 @@
|
|||
var Seq = require('seq');
|
||||
var Hash = require('hashish');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
var Bin = require('binary');
|
||||
var Buf = require('bufferlist/binary');
|
||||
var BufferList = require('bufferlist');
|
||||
|
||||
console.log('loop');
|
||||
function emitter () {
|
||||
var em = new EventEmitter;
|
||||
|
||||
var i = 0;
|
||||
var iv = setInterval(function () {
|
||||
var buf = new Buffer(10000);
|
||||
buf[0] = 0xff;
|
||||
|
||||
if (++ i >= 2000) {
|
||||
buf[0] = 0;
|
||||
clearInterval(iv);
|
||||
}
|
||||
em.emit('data', buf);
|
||||
}, 1);
|
||||
|
||||
return em;
|
||||
}
|
||||
|
||||
Seq()
|
||||
.seq(function () {
|
||||
var next = this.bind({}, null);
|
||||
bufferlist(next);
|
||||
})
|
||||
.seq(function () {
|
||||
var next = this.bind({}, null);
|
||||
binary(next);
|
||||
})
|
||||
;
|
||||
|
||||
function binary (next) {
|
||||
var em = emitter();
|
||||
var t0 = Date.now();
|
||||
|
||||
Bin(em)
|
||||
.loop(function (end) {
|
||||
this
|
||||
.word8('x')
|
||||
.word8('y')
|
||||
.word32be('z')
|
||||
.word32le('w')
|
||||
.buffer('buf', 10000 - 10)
|
||||
.tap(function (vars) {
|
||||
if (vars.x === 0) {
|
||||
var tf = Date.now();
|
||||
console.log(' binary: ' + (tf - t0) + ' ms');
|
||||
end();
|
||||
setTimeout(next, 20);
|
||||
}
|
||||
})
|
||||
})
|
||||
;
|
||||
}
|
||||
|
||||
function bufferlist (next) {
|
||||
var em = emitter();
|
||||
var t0 = Date.now();
|
||||
|
||||
var blist = new BufferList;
|
||||
em.on('data', function (buf) {
|
||||
blist.push(buf);
|
||||
});
|
||||
|
||||
Buf(blist)
|
||||
.forever(function () {
|
||||
var top = this;
|
||||
this
|
||||
.getWord8('x')
|
||||
.getWord8('y')
|
||||
.getWord32be('z')
|
||||
.getWord32le('w')
|
||||
.getBuffer('buf', 10000 - 10)
|
||||
.tap(function (vars) {
|
||||
if (vars.x === 0) {
|
||||
var tf = Date.now();
|
||||
console.log(' bufferlist: ' + (tf - t0) + ' ms');
|
||||
top.exit();
|
||||
setTimeout(next, 20);
|
||||
}
|
||||
})
|
||||
})
|
||||
.end()
|
||||
;
|
||||
}
|
|
@ -0,0 +1,80 @@
|
|||
var Seq = require('seq');
|
||||
var Hash = require('hashish');
|
||||
|
||||
var Bin = require('binary');
|
||||
var Buf = require('bufferlist/binary');
|
||||
var BufferList = require('bufferlist');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
function binary (buf, cb) {
|
||||
Bin(buf)
|
||||
.word32le('x')
|
||||
.word16be('y')
|
||||
.word16be('z')
|
||||
.word32le('w')
|
||||
.tap(cb)
|
||||
;
|
||||
};
|
||||
|
||||
function stream (buf, cb) {
|
||||
var em = new EventEmitter;
|
||||
Bin(em)
|
||||
.word32le('x')
|
||||
.word16be('y')
|
||||
.word16be('z')
|
||||
.word32le('w')
|
||||
.tap(cb)
|
||||
;
|
||||
em.emit('data', buf);
|
||||
};
|
||||
|
||||
function parse (buf, cb) {
|
||||
cb(Bin.parse(buf)
|
||||
.word32le('x')
|
||||
.word16be('y')
|
||||
.word16be('z')
|
||||
.word32le('w')
|
||||
.vars
|
||||
);
|
||||
};
|
||||
|
||||
function bufferlist (buf, cb) {
|
||||
var blist = new BufferList;
|
||||
blist.push(buf);
|
||||
Buf(blist)
|
||||
.getWord32le('x')
|
||||
.getWord16be('y')
|
||||
.getWord16be('z')
|
||||
.getWord32le('w')
|
||||
.tap(cb)
|
||||
.end()
|
||||
;
|
||||
};
|
||||
|
||||
|
||||
var buffers = [];
|
||||
for (var i = 0; i < 200; i++) {
|
||||
buffers.push(new Buffer(12));
|
||||
}
|
||||
|
||||
console.log('small');
|
||||
Seq(binary, stream, parse, bufferlist)
|
||||
.seqEach(function (f) {
|
||||
var t = this;
|
||||
var t0 = Date.now();
|
||||
Seq()
|
||||
.extend(buffers)
|
||||
.seqEach(function (buf) {
|
||||
f(buf, this.bind(this, null));
|
||||
})
|
||||
.seq(function () {
|
||||
var tf = Date.now();
|
||||
console.log(' ' + f.name + ': ' + (tf - t0));
|
||||
t(null);
|
||||
})
|
||||
;
|
||||
})
|
||||
.seq(function () {
|
||||
this(null);
|
||||
})
|
||||
;
|
|
@ -0,0 +1,46 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('bu', function (t) {
|
||||
t.plan(8);
|
||||
|
||||
// note: can't store -12667700813876161 exactly in an ieee float
|
||||
|
||||
var buf = new Buffer([
|
||||
44, // a == 44
|
||||
2, 43, // b == 555
|
||||
164, 213, 37, 37, // c == 2765432101
|
||||
29, 81, 180, 20, 155, 115, 203, 193, // d == 2112667700813876161
|
||||
]);
|
||||
|
||||
binary.parse(buf)
|
||||
.word8bu('a')
|
||||
.word16bu('b')
|
||||
.word32bu('c')
|
||||
.word64bu('d')
|
||||
.tap(function (vars) {
|
||||
t.same(vars.a, 44);
|
||||
t.same(vars.b, 555);
|
||||
t.same(vars.c, 2765432101);
|
||||
t.ok(
|
||||
Math.abs(vars.d - 2112667700813876161) < 1500
|
||||
);
|
||||
})
|
||||
;
|
||||
|
||||
// also check aliases here:
|
||||
binary.parse(buf)
|
||||
.word8be('a')
|
||||
.word16be('b')
|
||||
.word32be('c')
|
||||
.word64be('d')
|
||||
.tap(function (vars) {
|
||||
t.same(vars.a, 44);
|
||||
t.same(vars.b, 555);
|
||||
t.same(vars.c, 2765432101);
|
||||
t.ok(
|
||||
Math.abs(vars.d - 2112667700813876161) < 1500
|
||||
);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,20 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('deferred', function (t) {
|
||||
t.plan(1);
|
||||
|
||||
var em = new EventEmitter;
|
||||
binary.stream(em)
|
||||
.word8('a')
|
||||
.word16be('bc')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, { a : 97, bc : 25187 });
|
||||
})
|
||||
;
|
||||
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([ 97, 98, 99 ]));
|
||||
}, 10);
|
||||
});
|
|
@ -0,0 +1,23 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('dots', function (t) {
|
||||
t.plan(1);
|
||||
|
||||
binary.parse(new Buffer([ 97, 98, 99, 100, 101, 102 ]))
|
||||
.word8('a')
|
||||
.word16be('b.x')
|
||||
.word16be('b.y')
|
||||
.word8('b.z')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, {
|
||||
a : 97,
|
||||
b : {
|
||||
x : 256 * 98 + 99,
|
||||
y : 256 * 100 + 101,
|
||||
z : 102
|
||||
},
|
||||
});
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('eof', function (t) {
|
||||
t.plan(4);
|
||||
|
||||
var stream = new EventEmitter;
|
||||
binary.stream(stream)
|
||||
.buffer('sixone', 5)
|
||||
.peek(function () {
|
||||
this.word32le('len');
|
||||
})
|
||||
.buffer('buf', 'len')
|
||||
.word8('x')
|
||||
.tap(function (vars) {
|
||||
t.same(
|
||||
[].slice.call(vars.sixone),
|
||||
[].slice.call(new Buffer([ 6, 1, 6, 1, 6 ]))
|
||||
);
|
||||
t.same(vars.buf.length, vars.len);
|
||||
t.same(
|
||||
[].slice.call(vars.buf),
|
||||
[ 9, 0, 0, 0, 97, 98, 99, 100, 101 ]
|
||||
);
|
||||
t.same(vars.x, 102);
|
||||
})
|
||||
;
|
||||
|
||||
var bufs = [
|
||||
new Buffer([ 6, 1, 6, 1, 6, 9, 0, 0, 0, 97 ]),
|
||||
new Buffer([ 98, 99 ]),
|
||||
new Buffer([ 100, 101, 102 ]),
|
||||
];
|
||||
|
||||
bufs.forEach(function (buf) {
|
||||
stream.emit('data', buf);
|
||||
});
|
||||
|
||||
stream.emit('end');
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('flush', function (t) {
|
||||
t.plan(1);
|
||||
|
||||
binary.parse(new Buffer([ 97, 98, 99, 100, 101, 102 ]))
|
||||
.word8('a')
|
||||
.word16be('b')
|
||||
.word16be('c')
|
||||
.flush()
|
||||
.word8('d')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, { d : 102 });
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('from buffer', function (t) {
|
||||
t.plan(1);
|
||||
|
||||
binary(new Buffer([ 97, 98, 99 ]))
|
||||
.word8('a')
|
||||
.word16be('bc')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, { a : 97, bc : 25187 });
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('get buffer', function (t) {
|
||||
t.plan(4);
|
||||
|
||||
var buf = new Buffer([ 4, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 ]);
|
||||
binary.parse(buf)
|
||||
.word8('a')
|
||||
.buffer('b', 7)
|
||||
.word16lu('c')
|
||||
.tap(function (vars) {
|
||||
t.equal(vars.a, 4);
|
||||
t.equal(
|
||||
vars.b.toString(),
|
||||
new Buffer([ 2, 3, 4, 5, 6, 7, 8 ]).toString()
|
||||
);
|
||||
t.equal(vars.c, 2569);
|
||||
})
|
||||
.buffer('d', 'a')
|
||||
.tap(function (vars) {
|
||||
t.equal(
|
||||
vars.d.toString(),
|
||||
new Buffer([ 11, 12, 13, 14 ]).toString()
|
||||
);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('immediate', function (t) {
|
||||
t.plan(1);
|
||||
|
||||
var em = new EventEmitter;
|
||||
binary.stream(em, 'moo')
|
||||
.word8('a')
|
||||
.word16be('bc')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, { a : 97, bc : 25187 });
|
||||
})
|
||||
;
|
||||
|
||||
em.emit('moo', new Buffer([ 97, 98, 99 ]));
|
||||
});
|
|
@ -0,0 +1,38 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('interval', function (t) {
|
||||
t.plan(1);
|
||||
|
||||
var em = new EventEmitter;
|
||||
var i = 0;
|
||||
var iv = setInterval(function () {
|
||||
var buf = new Buffer(1000);
|
||||
buf[0] = 0xff;
|
||||
if (++i >= 1000) {
|
||||
clearInterval(iv);
|
||||
buf[0] = 0;
|
||||
}
|
||||
em.emit('data', buf);
|
||||
}, 1);
|
||||
|
||||
var loops = 0;
|
||||
binary(em)
|
||||
.loop(function (end) {
|
||||
this
|
||||
.word8('x')
|
||||
.word8('y')
|
||||
.word32be('z')
|
||||
.word32le('w')
|
||||
.buffer('buf', 1000 - 10)
|
||||
.tap(function (vars) {
|
||||
loops ++;
|
||||
if (vars.x == 0) end();
|
||||
})
|
||||
})
|
||||
.tap(function () {
|
||||
t.same(loops, 1000);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('intoBuffer', function (t) {
|
||||
t.plan(3);
|
||||
var buf = new Buffer([ 1, 2, 3, 4, 5, 6 ])
|
||||
|
||||
binary.parse(buf)
|
||||
.into('moo', function () {
|
||||
this
|
||||
.word8('x')
|
||||
.word8('y')
|
||||
.word8('z')
|
||||
;
|
||||
})
|
||||
.tap(function (vars) {
|
||||
t.same(vars, { moo : { x : 1, y : 2, z : 3 } });
|
||||
})
|
||||
.word8('w')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, {
|
||||
moo : { x : 1, y : 2, z : 3 },
|
||||
w : 4,
|
||||
});
|
||||
})
|
||||
.word8('x')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, {
|
||||
moo : { x : 1, y : 2, z : 3 },
|
||||
w : 4,
|
||||
x : 5,
|
||||
});
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,43 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('into stream', function (t) {
|
||||
t.plan(3);
|
||||
|
||||
var digits = [ 1, 2, 3, 4, 5, 6 ];
|
||||
var stream = new EventEmitter;
|
||||
var iv = setInterval(function () {
|
||||
var d = digits.shift();
|
||||
if (d) stream.emit('data', new Buffer([ d ]))
|
||||
else clearInterval(iv)
|
||||
}, 20);
|
||||
|
||||
binary.stream(stream)
|
||||
.into('moo', function () {
|
||||
this
|
||||
.word8('x')
|
||||
.word8('y')
|
||||
.word8('z')
|
||||
;
|
||||
})
|
||||
.tap(function (vars) {
|
||||
t.same(vars, { moo : { x : 1, y : 2, z : 3 } });
|
||||
})
|
||||
.word8('w')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, {
|
||||
moo : { x : 1, y : 2, z : 3 },
|
||||
w : 4,
|
||||
});
|
||||
})
|
||||
.word8('x')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, {
|
||||
moo : { x : 1, y : 2, z : 3 },
|
||||
w : 4,
|
||||
x : 5,
|
||||
});
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,44 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('loop', function (t) {
|
||||
t.plan(3 * 2 + 1);
|
||||
|
||||
var em = new EventEmitter;
|
||||
|
||||
binary.stream(em)
|
||||
.loop(function (end, vars) {
|
||||
t.strictEqual(vars, this.vars);
|
||||
this
|
||||
.word16lu('a')
|
||||
.word8u('b')
|
||||
.word8s('c')
|
||||
.tap(function (vars_) {
|
||||
t.strictEqual(vars, vars_);
|
||||
if (vars.c < 0) end();
|
||||
})
|
||||
;
|
||||
})
|
||||
.tap(function (vars) {
|
||||
t.same(vars, { a : 1337, b : 55, c : -5 });
|
||||
})
|
||||
;
|
||||
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([ 2, 10, 88 ]));
|
||||
}, 10);
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([ 100, 3, 6, 242, 30 ]));
|
||||
}, 20);
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([ 60, 60, 199, 44 ]));
|
||||
}, 30);
|
||||
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([ 57, 5 ]));
|
||||
}, 80);
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([ 55, 251 ]));
|
||||
}, 90);
|
||||
});
|
|
@ -0,0 +1,54 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('loop scan', function (t) {
|
||||
t.plan(8 + 6 + 2);
|
||||
|
||||
var em = new EventEmitter;
|
||||
|
||||
binary.stream(em)
|
||||
.loop(function (end) {
|
||||
var vars_ = this.vars;
|
||||
this
|
||||
.scan('filler', 'BEGINMSG')
|
||||
.buffer('cmd', 3)
|
||||
.word8('num')
|
||||
.tap(function (vars) {
|
||||
t.strictEqual(vars, vars_);
|
||||
if (vars.num != 0x02 && vars.num != 0x06) {
|
||||
t.same(vars.filler.length, 0);
|
||||
}
|
||||
if (vars.cmd.toString() == 'end') end();
|
||||
})
|
||||
;
|
||||
})
|
||||
.tap(function (vars) {
|
||||
t.same(vars.cmd.toString(), 'end');
|
||||
t.same(vars.num, 0x08);
|
||||
})
|
||||
;
|
||||
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer(
|
||||
'BEGINMSGcmd\x01'
|
||||
+ 'GARBAGEDATAXXXX'
|
||||
+ 'BEGINMSGcmd\x02'
|
||||
+ 'BEGINMSGcmd\x03'
|
||||
));
|
||||
}, 10);
|
||||
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer(
|
||||
'BEGINMSGcmd\x04'
|
||||
+ 'BEGINMSGcmd\x05'
|
||||
+ 'GARBAGEDATAXXXX'
|
||||
+ 'BEGINMSGcmd\x06'
|
||||
));
|
||||
em.emit('data', new Buffer('BEGINMSGcmd\x07'));
|
||||
}, 20);
|
||||
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer('BEGINMSGend\x08'));
|
||||
}, 30);
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('lu', function (t) {
|
||||
t.plan(8);
|
||||
|
||||
// note: can't store -12667700813876161 exactly in an ieee float
|
||||
|
||||
var buf = new Buffer([
|
||||
44, // a == 44
|
||||
43, 2, // b == 555
|
||||
37, 37, 213, 164, // c == 2765432101
|
||||
193, 203, 115, 155, 20, 180, 81, 29, // d == 2112667700813876161
|
||||
]);
|
||||
|
||||
binary.parse(buf)
|
||||
.word8lu('a')
|
||||
.word16lu('b')
|
||||
.word32lu('c')
|
||||
.word64lu('d')
|
||||
.tap(function (vars) {
|
||||
t.same(vars.a, 44);
|
||||
t.same(vars.b, 555);
|
||||
t.same(vars.c, 2765432101);
|
||||
t.ok(
|
||||
Math.abs(vars.d - 2112667700813876161) < 1500
|
||||
);
|
||||
})
|
||||
;
|
||||
|
||||
// also check aliases here:
|
||||
binary.parse(buf)
|
||||
.word8le('a')
|
||||
.word16le('b')
|
||||
.word32le('c')
|
||||
.word64le('d')
|
||||
.tap(function (vars) {
|
||||
t.same(vars.a, 44);
|
||||
t.same(vars.b, 555);
|
||||
t.same(vars.c, 2765432101);
|
||||
t.ok(
|
||||
Math.abs(vars.d - 2112667700813876161) < 1500
|
||||
);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('negbs', function (t) {
|
||||
t.plan(4);
|
||||
// note: can't store -12667700813876161 exactly in an ieee float
|
||||
|
||||
var buf = new Buffer([
|
||||
226, // a == -30
|
||||
246, 219, // b == -2341
|
||||
255, 243, 245, 236, // c == -789012
|
||||
255, 210, 254, 203, 16, 222, 52, 63, // d == -12667700813876161
|
||||
]);
|
||||
|
||||
binary.parse(buf)
|
||||
.word8bs('a')
|
||||
.word16bs('b')
|
||||
.word32bs('c')
|
||||
.word64bs('d')
|
||||
.tap(function (vars) {
|
||||
t.same(vars.a, -30);
|
||||
t.same(vars.b, -2341);
|
||||
t.same(vars.c, -789012);
|
||||
t.ok(
|
||||
Math.abs(vars.d - -12667700813876161) < 1500
|
||||
);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('negls', function (t) {
|
||||
t.plan(4);
|
||||
// note: can't store -12667700813876161 exactly in an ieee float
|
||||
|
||||
var buf = new Buffer([
|
||||
226, // a == -30
|
||||
219, 246, // b == -2341
|
||||
236, 245, 243, 255, // c == -789012
|
||||
63, 52, 222, 16, 203, 254, 210, 255, // d == -12667700813876161
|
||||
]);
|
||||
|
||||
binary.parse(buf)
|
||||
.word8ls('a')
|
||||
.word16ls('b')
|
||||
.word32ls('c')
|
||||
.word64ls('d')
|
||||
.tap(function (vars) {
|
||||
t.same(vars.a, -30);
|
||||
t.same(vars.b, -2341);
|
||||
t.same(vars.c, -789012);
|
||||
t.ok(
|
||||
Math.abs(vars.d - -12667700813876161) < 1000
|
||||
);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,35 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('nested', function (t) {
|
||||
t.plan(3);
|
||||
var insideDone = false;
|
||||
|
||||
var em = new EventEmitter;
|
||||
binary.stream(em)
|
||||
.word16be('ab')
|
||||
.tap(function () {
|
||||
this
|
||||
.word8('c')
|
||||
.word8('d')
|
||||
.tap(function () {
|
||||
insideDone = true;
|
||||
})
|
||||
;
|
||||
})
|
||||
.tap(function (vars) {
|
||||
t.ok(insideDone);
|
||||
t.same(vars.c, 'c'.charCodeAt(0));
|
||||
t.same(vars.d, 'd'.charCodeAt(0));
|
||||
|
||||
})
|
||||
;
|
||||
|
||||
var strs = [ 'abc', 'def', 'hi', 'jkl' ];
|
||||
var iv = setInterval(function () {
|
||||
var s = strs.shift();
|
||||
if (s) em.emit('data', new Buffer(s));
|
||||
else clearInterval(iv);
|
||||
}, 50);
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('not enough buf', function (t) {
|
||||
t.plan(3);
|
||||
|
||||
var vars = binary(new Buffer([1,2,3,4]))
|
||||
.word8('a')
|
||||
.buffer('b', 10)
|
||||
.word8('c')
|
||||
.vars
|
||||
;
|
||||
|
||||
t.same(vars.a, 1);
|
||||
t.equal(vars.b.toString(), new Buffer([2,3,4]).toString());
|
||||
t.strictEqual(vars.c, null);
|
||||
});
|
|
@ -0,0 +1,19 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('not enough parse', function (t) {
|
||||
t.plan(4);
|
||||
|
||||
var vars = binary(new Buffer([1,2]))
|
||||
.word8('a')
|
||||
.word8('b')
|
||||
.word8('c')
|
||||
.word8('d')
|
||||
.vars
|
||||
;
|
||||
|
||||
t.same(vars.a, 1);
|
||||
t.same(vars.b, 2);
|
||||
t.strictEqual(vars.c, null);
|
||||
t.strictEqual(vars.d, null);
|
||||
});
|
|
@ -0,0 +1,54 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('parse', function (t) {
|
||||
t.plan(6);
|
||||
var res = binary.parse(new Buffer([ 97, 98, 99, 99, 99, 99, 1, 2, 3 ]))
|
||||
.word8('a')
|
||||
.word16be('bc')
|
||||
.skip(3)
|
||||
.buffer('def', 3)
|
||||
.tap(function (vars) {
|
||||
t.equal(vars.a, 97);
|
||||
t.equal(vars.bc, 25187);
|
||||
t.same(
|
||||
[].slice.call(vars.def),
|
||||
[].slice.call(new Buffer([ 1, 2, 3]))
|
||||
);
|
||||
})
|
||||
.vars
|
||||
;
|
||||
t.equal(res.a, 97);
|
||||
t.equal(res.bc, 25187);
|
||||
t.same(
|
||||
[].slice.call(res.def),
|
||||
[].slice.call(new Buffer([ 1, 2, 3 ]))
|
||||
);
|
||||
});
|
||||
|
||||
test('loop', function (t) {
|
||||
t.plan(2);
|
||||
var res = binary.parse(new Buffer([ 97, 98, 99, 4, 5, 2, -3, 9 ]))
|
||||
.word8('a')
|
||||
.word16be('bc')
|
||||
.loop(function (end) {
|
||||
var x = this.word8s('x').vars.x;
|
||||
if (x < 0) end();
|
||||
})
|
||||
.tap(function (vars) {
|
||||
t.same(vars, {
|
||||
a : 97,
|
||||
bc : 25187,
|
||||
x : -3,
|
||||
});
|
||||
})
|
||||
.word8('y')
|
||||
.vars
|
||||
;
|
||||
t.same(res, {
|
||||
a : 97,
|
||||
bc : 25187,
|
||||
x : -3,
|
||||
y : 9,
|
||||
});
|
||||
});
|
|
@ -0,0 +1,40 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('peek', function (t) {
|
||||
t.plan(4);
|
||||
var bufs = [
|
||||
new Buffer([ 6, 1, 6, 1, 6, 9, 0, 0, 0, 97 ]),
|
||||
new Buffer([ 98, 99 ]),
|
||||
new Buffer([ 100, 101, 102 ]),
|
||||
];
|
||||
|
||||
var stream = new EventEmitter;
|
||||
var iv = setInterval(function () {
|
||||
var buf = bufs.shift();
|
||||
if (buf) stream.emit('data', buf)
|
||||
else clearInterval(iv)
|
||||
}, 20);
|
||||
|
||||
binary.stream(stream)
|
||||
.buffer('sixone', 5)
|
||||
.peek(function () {
|
||||
this.word32le('len');
|
||||
})
|
||||
.buffer('buf', 'len')
|
||||
.word8('x')
|
||||
.tap(function (vars) {
|
||||
t.same(
|
||||
[].slice.call(vars.sixone),
|
||||
[].slice.call(new Buffer([ 6, 1, 6, 1, 6 ]))
|
||||
);
|
||||
t.same(vars.buf.length, vars.len);
|
||||
t.same(
|
||||
[].slice.call(vars.buf),
|
||||
[ 9, 0, 0, 0, 97, 98, 99, 100, 101 ]
|
||||
);
|
||||
t.same(vars.x, 102);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,49 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var Stream = require('stream').Stream;
|
||||
|
||||
test('loop', function (t) {
|
||||
t.plan(3 * 2 + 1);
|
||||
|
||||
var rs = new Stream;
|
||||
rs.readable = true;
|
||||
|
||||
var ws = binary()
|
||||
.loop(function (end, vars) {
|
||||
t.strictEqual(vars, this.vars);
|
||||
this
|
||||
.word16lu('a')
|
||||
.word8u('b')
|
||||
.word8s('c')
|
||||
.tap(function (vars_) {
|
||||
t.strictEqual(vars, vars_);
|
||||
if (vars.c < 0) end();
|
||||
})
|
||||
;
|
||||
})
|
||||
.tap(function (vars) {
|
||||
t.same(vars, { a : 1337, b : 55, c : -5 });
|
||||
})
|
||||
;
|
||||
rs.pipe(ws);
|
||||
|
||||
setTimeout(function () {
|
||||
rs.emit('data', new Buffer([ 2, 10, 88 ]));
|
||||
}, 10);
|
||||
setTimeout(function () {
|
||||
rs.emit('data', new Buffer([ 100, 3, 6, 242, 30 ]));
|
||||
}, 20);
|
||||
setTimeout(function () {
|
||||
rs.emit('data', new Buffer([ 60, 60, 199, 44 ]));
|
||||
}, 30);
|
||||
|
||||
setTimeout(function () {
|
||||
rs.emit('data', new Buffer([ 57, 5 ]));
|
||||
}, 80);
|
||||
setTimeout(function () {
|
||||
rs.emit('data', new Buffer([ 55, 251 ]));
|
||||
}, 90);
|
||||
setTimeout(function () {
|
||||
rs.emit('end');
|
||||
}, 100);
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('posbs', function (t) {
|
||||
t.plan(4);
|
||||
// note: can't store 12667700813876161 exactly in an ieee float
|
||||
|
||||
var buf = new Buffer([
|
||||
30, // a == -30
|
||||
9, 37, // b == -2341
|
||||
0, 12, 10, 20, // c == -789012
|
||||
0, 45, 1, 52, 239, 33, 203, 193, // d == 12667700813876161
|
||||
]);
|
||||
|
||||
binary.parse(buf)
|
||||
.word8bs('a')
|
||||
.word16bs('b')
|
||||
.word32bs('c')
|
||||
.word64bs('d')
|
||||
.tap(function (vars) {
|
||||
t.same(vars.a, 30);
|
||||
t.same(vars.b, 2341);
|
||||
t.same(vars.c, 789012);
|
||||
t.ok(
|
||||
Math.abs(vars.d - 12667700813876161) < 1000
|
||||
);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,29 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('posls', function (t) {
|
||||
t.plan(4);
|
||||
|
||||
// note: can't store 12667700813876161 exactly in an ieee float
|
||||
var buf = new Buffer([
|
||||
30, // a == -30
|
||||
37, 9, // b == -2341
|
||||
20, 10, 12, 0, // c == -789012
|
||||
193, 203, 33, 239, 52, 1, 45, 0, // d == 12667700813876161
|
||||
]);
|
||||
|
||||
binary.parse(buf)
|
||||
.word8ls('a')
|
||||
.word16ls('b')
|
||||
.word32ls('c')
|
||||
.word64ls('d')
|
||||
.tap(function (vars) {
|
||||
t.same(vars.a, 30);
|
||||
t.same(vars.b, 2341);
|
||||
t.same(vars.c, 789012);
|
||||
t.ok(
|
||||
Math.abs(vars.d - 12667700813876161) < 1000
|
||||
);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,33 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('scan', function (t) {
|
||||
t.plan(4);
|
||||
|
||||
var em = new EventEmitter;
|
||||
binary(em)
|
||||
.word8('a')
|
||||
.scan('l1', new Buffer('\r\n'))
|
||||
.scan('l2', '\r\n')
|
||||
.word8('z')
|
||||
.tap(function (vars) {
|
||||
t.same(vars.a, 99);
|
||||
t.same(vars.l1.toString(), 'foo bar');
|
||||
t.same(vars.l2.toString(), 'baz');
|
||||
t.same(vars.z, 42);
|
||||
})
|
||||
;
|
||||
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([99,0x66,0x6f,0x6f,0x20]));
|
||||
}, 20);
|
||||
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer('bar\r'));
|
||||
}, 40);
|
||||
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer('\nbaz\r\n*'));
|
||||
}, 60);
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('scan buf', function (t) {
|
||||
t.plan(4);
|
||||
|
||||
var vars = binary(new Buffer('\x63foo bar\r\nbaz\r\n*'))
|
||||
.word8('a')
|
||||
.scan('l1', new Buffer('\r\n'))
|
||||
.scan('l2', '\r\n')
|
||||
.word8('z')
|
||||
.vars
|
||||
;
|
||||
t.same(vars.a, 99);
|
||||
t.same(vars.z, 42);
|
||||
t.same(vars.l1.toString(), 'foo bar');
|
||||
t.same(vars.l2.toString(), 'baz');
|
||||
});
|
|
@ -0,0 +1,16 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
|
||||
test('scan buf null', function (t) {
|
||||
t.plan(3);
|
||||
var vars = binary(new Buffer('\x63foo bar baz'))
|
||||
.word8('a')
|
||||
.scan('b', '\r\n')
|
||||
.word8('c')
|
||||
.vars
|
||||
;
|
||||
|
||||
t.same(vars.a, 99);
|
||||
t.same(vars.b.toString(), 'foo bar baz');
|
||||
t.strictEqual(vars.c, null);
|
||||
});
|
|
@ -0,0 +1,58 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
var seq = require('seq');
|
||||
|
||||
test('skip', function (t) {
|
||||
t.plan(7);
|
||||
var em = new EventEmitter;
|
||||
var state = 0;
|
||||
|
||||
binary(em)
|
||||
.word16lu('a')
|
||||
.tap(function () { state = 1 })
|
||||
.skip(7)
|
||||
.tap(function () { state = 2 })
|
||||
.word8('b')
|
||||
.tap(function () { state = 3 })
|
||||
.tap(function (vars) {
|
||||
t.same(state, 3);
|
||||
t.same(vars, {
|
||||
a : 2569,
|
||||
b : 8,
|
||||
});
|
||||
})
|
||||
;
|
||||
|
||||
seq()
|
||||
.seq(setTimeout, seq, 20)
|
||||
.seq(function () {
|
||||
t.same(state, 0);
|
||||
em.emit('data', new Buffer([ 9 ]));
|
||||
this(null);
|
||||
})
|
||||
.seq(setTimeout, seq, 5)
|
||||
.seq(function () {
|
||||
t.same(state, 0);
|
||||
em.emit('data', new Buffer([ 10, 1, 2 ]));
|
||||
this(null);
|
||||
})
|
||||
.seq(setTimeout, seq, 30)
|
||||
.seq(function () {
|
||||
t.same(state, 1);
|
||||
em.emit('data', new Buffer([ 3, 4, 5 ]));
|
||||
this(null);
|
||||
})
|
||||
.seq(setTimeout, seq, 15)
|
||||
.seq(function () {
|
||||
t.same(state, 1);
|
||||
em.emit('data', new Buffer([ 6, 7 ]));
|
||||
this(null);
|
||||
})
|
||||
.seq(function () {
|
||||
t.same(state, 2);
|
||||
em.emit('data', new Buffer([ 8 ]));
|
||||
this(null);
|
||||
})
|
||||
;
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
var binary = require('../');
|
||||
var test = require('tap').test;
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
test('split', function (t) {
|
||||
t.plan(1);
|
||||
|
||||
var em = new EventEmitter;
|
||||
binary.stream(em)
|
||||
.word8('a')
|
||||
.word16be('bc')
|
||||
.word32ls('x')
|
||||
.word32bs('y')
|
||||
.tap(function (vars) {
|
||||
t.same(vars, {
|
||||
a : 97,
|
||||
bc : 25187,
|
||||
x : 621609828,
|
||||
y : 621609828,
|
||||
});
|
||||
})
|
||||
;
|
||||
|
||||
em.emit('data', new Buffer([ 97, 98 ]));
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([ 99, 100 ]));
|
||||
}, 25);
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([ 3, 13, 37, 37 ]));
|
||||
}, 30);
|
||||
setTimeout(function () {
|
||||
em.emit('data', new Buffer([ 13, 3, 100 ]));
|
||||
}, 40);
|
||||
});
|
|
@ -0,0 +1,122 @@
|
|||
buffers
|
||||
=======
|
||||
|
||||
Treat a collection of Buffers as a single contiguous partially mutable Buffer.
|
||||
|
||||
Where possible, operations execute without creating a new Buffer and copying
|
||||
everything over.
|
||||
|
||||
This is a cleaner more Buffery rehash of
|
||||
[bufferlist](http://github.com/substack/node-bufferlist).
|
||||
|
||||
example
|
||||
=======
|
||||
|
||||
slice
|
||||
-----
|
||||
|
||||
var Buffers = require('buffers');
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer([1,2,3]));
|
||||
bufs.push(new Buffer([4,5,6,7]));
|
||||
bufs.push(new Buffer([8,9,10]));
|
||||
|
||||
console.dir(bufs.slice(2,8))
|
||||
|
||||
output:
|
||||
|
||||
$ node examples/slice.js
|
||||
<Buffer 03 04 05 06 07 08>
|
||||
|
||||
splice
|
||||
------
|
||||
|
||||
var Buffers = require('buffers');
|
||||
var bufs = Buffers([
|
||||
new Buffer([1,2,3]),
|
||||
new Buffer([4,5,6,7]),
|
||||
new Buffer([8,9,10]),
|
||||
]);
|
||||
|
||||
var removed = bufs.splice(2, 4);
|
||||
console.dir({
|
||||
removed : removed.slice(),
|
||||
bufs : bufs.slice(),
|
||||
});
|
||||
|
||||
output:
|
||||
|
||||
$ node examples/splice.js
|
||||
{ removed: <Buffer 03 04 05 06>,
|
||||
bufs: <Buffer 01 02 07 08 09 0a> }
|
||||
|
||||
methods
|
||||
=======
|
||||
|
||||
Buffers(buffers)
|
||||
----------------
|
||||
|
||||
Create a Buffers with an array of `Buffer`s if specified, else `[]`.
|
||||
|
||||
.push(buf1, buf2...)
|
||||
--------------------
|
||||
|
||||
Push buffers onto the end. Just like `Array.prototype.push`.
|
||||
|
||||
.unshift(buf1, buf2...)
|
||||
-----------------------
|
||||
|
||||
Unshift buffers onto the head. Just like `Array.prototype.unshift`.
|
||||
|
||||
.slice(i, j)
|
||||
------------
|
||||
|
||||
Slice a range out of the buffer collection as if it were contiguous.
|
||||
Works just like the `Array.prototype.slice` version.
|
||||
|
||||
.splice(i, howMany, replacements)
|
||||
---------------------------------
|
||||
|
||||
Splice the buffer collection as if it were contiguous.
|
||||
Works just like `Array.prototype.splice`, even the replacement part!
|
||||
|
||||
.copy(dst, dstStart, start, end)
|
||||
--------------------------------
|
||||
|
||||
Copy the buffer collection as if it were contiguous to the `dst` Buffer with the
|
||||
specified bounds.
|
||||
Works just like `Buffer.prototype.copy`.
|
||||
|
||||
.get(i)
|
||||
-------
|
||||
|
||||
Get a single element at index `i`.
|
||||
|
||||
.set(i, x)
|
||||
----------
|
||||
|
||||
Set a single element's value at index `i`.
|
||||
|
||||
.indexOf(needle, offset)
|
||||
----------
|
||||
|
||||
Find a string or buffer `needle` inside the buffer collection. Returns
|
||||
the position of the search string or -1 if the search string was not
|
||||
found.
|
||||
|
||||
Provide an `offset` to skip that number of characters at the beginning
|
||||
of the search. This can be used to find additional matches.
|
||||
|
||||
This function will return the correct result even if the search string
|
||||
is spread out over multiple internal buffers.
|
||||
|
||||
.toBuffer()
|
||||
-----------
|
||||
|
||||
Convert the buffer collection to a single buffer, equivalent with `.slice(0, buffers.length)`;
|
||||
|
||||
.toString(encoding, start, end)
|
||||
-----------
|
||||
|
||||
Decodes and returns a string from the buffer collection.
|
||||
Works just like `Buffer.prototype.toString`
|
|
@ -0,0 +1,9 @@
|
|||
var Buffers = require('buffers');
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer([1,2,3]));
|
||||
bufs.push(new Buffer([4,5,6,7]));
|
||||
bufs.push(new Buffer([8,9,10]));
|
||||
|
||||
console.dir(bufs.slice(2,8))
|
||||
|
||||
// Output: <Buffer 03 04 05 06 07 08>
|
|
@ -0,0 +1,17 @@
|
|||
var Buffers = require('buffers');
|
||||
var bufs = Buffers([
|
||||
new Buffer([1,2,3]),
|
||||
new Buffer([4,5,6,7]),
|
||||
new Buffer([8,9,10]),
|
||||
]);
|
||||
|
||||
var removed = bufs.splice(2, 4, new Buffer('ab'), new Buffer('cd'));
|
||||
console.dir({
|
||||
removed : removed.slice(),
|
||||
bufs : bufs.slice(),
|
||||
});
|
||||
|
||||
/* Output:
|
||||
{ removed: <Buffer 03 04 05 06>,
|
||||
bufs: <Buffer 01 02 07 08 09 0a> }
|
||||
*/
|
|
@ -0,0 +1,269 @@
|
|||
module.exports = Buffers;
|
||||
|
||||
function Buffers (bufs) {
|
||||
if (!(this instanceof Buffers)) return new Buffers(bufs);
|
||||
this.buffers = bufs || [];
|
||||
this.length = this.buffers.reduce(function (size, buf) {
|
||||
return size + buf.length
|
||||
}, 0);
|
||||
}
|
||||
|
||||
Buffers.prototype.push = function () {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (!Buffer.isBuffer(arguments[i])) {
|
||||
throw new TypeError('Tried to push a non-buffer');
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var buf = arguments[i];
|
||||
this.buffers.push(buf);
|
||||
this.length += buf.length;
|
||||
}
|
||||
return this.length;
|
||||
};
|
||||
|
||||
Buffers.prototype.unshift = function () {
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
if (!Buffer.isBuffer(arguments[i])) {
|
||||
throw new TypeError('Tried to unshift a non-buffer');
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = 0; i < arguments.length; i++) {
|
||||
var buf = arguments[i];
|
||||
this.buffers.unshift(buf);
|
||||
this.length += buf.length;
|
||||
}
|
||||
return this.length;
|
||||
};
|
||||
|
||||
Buffers.prototype.copy = function (dst, dStart, start, end) {
|
||||
return this.slice(start, end).copy(dst, dStart, 0, end - start);
|
||||
};
|
||||
|
||||
Buffers.prototype.splice = function (i, howMany) {
|
||||
var buffers = this.buffers;
|
||||
var index = i >= 0 ? i : this.length - i;
|
||||
var reps = [].slice.call(arguments, 2);
|
||||
|
||||
if (howMany === undefined) {
|
||||
howMany = this.length - index;
|
||||
}
|
||||
else if (howMany > this.length - index) {
|
||||
howMany = this.length - index;
|
||||
}
|
||||
|
||||
for (var i = 0; i < reps.length; i++) {
|
||||
this.length += reps[i].length;
|
||||
}
|
||||
|
||||
var removed = new Buffers();
|
||||
var bytes = 0;
|
||||
|
||||
var startBytes = 0;
|
||||
for (
|
||||
var ii = 0;
|
||||
ii < buffers.length && startBytes + buffers[ii].length < index;
|
||||
ii ++
|
||||
) { startBytes += buffers[ii].length }
|
||||
|
||||
if (index - startBytes > 0) {
|
||||
var start = index - startBytes;
|
||||
|
||||
if (start + howMany < buffers[ii].length) {
|
||||
removed.push(buffers[ii].slice(start, start + howMany));
|
||||
|
||||
var orig = buffers[ii];
|
||||
//var buf = new Buffer(orig.length - howMany);
|
||||
var buf0 = new Buffer(start);
|
||||
for (var i = 0; i < start; i++) {
|
||||
buf0[i] = orig[i];
|
||||
}
|
||||
|
||||
var buf1 = new Buffer(orig.length - start - howMany);
|
||||
for (var i = start + howMany; i < orig.length; i++) {
|
||||
buf1[ i - howMany - start ] = orig[i]
|
||||
}
|
||||
|
||||
if (reps.length > 0) {
|
||||
var reps_ = reps.slice();
|
||||
reps_.unshift(buf0);
|
||||
reps_.push(buf1);
|
||||
buffers.splice.apply(buffers, [ ii, 1 ].concat(reps_));
|
||||
ii += reps_.length;
|
||||
reps = [];
|
||||
}
|
||||
else {
|
||||
buffers.splice(ii, 1, buf0, buf1);
|
||||
//buffers[ii] = buf;
|
||||
ii += 2;
|
||||
}
|
||||
}
|
||||
else {
|
||||
removed.push(buffers[ii].slice(start));
|
||||
buffers[ii] = buffers[ii].slice(0, start);
|
||||
ii ++;
|
||||
}
|
||||
}
|
||||
|
||||
if (reps.length > 0) {
|
||||
buffers.splice.apply(buffers, [ ii, 0 ].concat(reps));
|
||||
ii += reps.length;
|
||||
}
|
||||
|
||||
while (removed.length < howMany) {
|
||||
var buf = buffers[ii];
|
||||
var len = buf.length;
|
||||
var take = Math.min(len, howMany - removed.length);
|
||||
|
||||
if (take === len) {
|
||||
removed.push(buf);
|
||||
buffers.splice(ii, 1);
|
||||
}
|
||||
else {
|
||||
removed.push(buf.slice(0, take));
|
||||
buffers[ii] = buffers[ii].slice(take);
|
||||
}
|
||||
}
|
||||
|
||||
this.length -= removed.length;
|
||||
|
||||
return removed;
|
||||
};
|
||||
|
||||
Buffers.prototype.slice = function (i, j) {
|
||||
var buffers = this.buffers;
|
||||
if (j === undefined) j = this.length;
|
||||
if (i === undefined) i = 0;
|
||||
|
||||
if (j > this.length) j = this.length;
|
||||
|
||||
var startBytes = 0;
|
||||
for (
|
||||
var si = 0;
|
||||
si < buffers.length && startBytes + buffers[si].length <= i;
|
||||
si ++
|
||||
) { startBytes += buffers[si].length }
|
||||
|
||||
var target = new Buffer(j - i);
|
||||
|
||||
var ti = 0;
|
||||
for (var ii = si; ti < j - i && ii < buffers.length; ii++) {
|
||||
var len = buffers[ii].length;
|
||||
|
||||
var start = ti === 0 ? i - startBytes : 0;
|
||||
var end = ti + len >= j - i
|
||||
? Math.min(start + (j - i) - ti, len)
|
||||
: len
|
||||
;
|
||||
|
||||
buffers[ii].copy(target, ti, start, end);
|
||||
ti += end - start;
|
||||
}
|
||||
|
||||
return target;
|
||||
};
|
||||
|
||||
Buffers.prototype.pos = function (i) {
|
||||
if (i < 0 || i >= this.length) throw new Error('oob');
|
||||
var l = i, bi = 0, bu = null;
|
||||
for (;;) {
|
||||
bu = this.buffers[bi];
|
||||
if (l < bu.length) {
|
||||
return {buf: bi, offset: l};
|
||||
} else {
|
||||
l -= bu.length;
|
||||
}
|
||||
bi++;
|
||||
}
|
||||
};
|
||||
|
||||
Buffers.prototype.get = function get (i) {
|
||||
var pos = this.pos(i);
|
||||
|
||||
return this.buffers[pos.buf].get(pos.offset);
|
||||
};
|
||||
|
||||
Buffers.prototype.set = function set (i, b) {
|
||||
var pos = this.pos(i);
|
||||
|
||||
return this.buffers[pos.buf].set(pos.offset, b);
|
||||
};
|
||||
|
||||
Buffers.prototype.indexOf = function (needle, offset) {
|
||||
if ("string" === typeof needle) {
|
||||
needle = new Buffer(needle);
|
||||
} else if (needle instanceof Buffer) {
|
||||
// already a buffer
|
||||
} else {
|
||||
throw new Error('Invalid type for a search string');
|
||||
}
|
||||
|
||||
if (!needle.length) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!this.length) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var i = 0, j = 0, match = 0, mstart, pos = 0;
|
||||
|
||||
// start search from a particular point in the virtual buffer
|
||||
if (offset) {
|
||||
var p = this.pos(offset);
|
||||
i = p.buf;
|
||||
j = p.offset;
|
||||
pos = offset;
|
||||
}
|
||||
|
||||
// for each character in virtual buffer
|
||||
for (;;) {
|
||||
while (j >= this.buffers[i].length) {
|
||||
j = 0;
|
||||
i++;
|
||||
|
||||
if (i >= this.buffers.length) {
|
||||
// search string not found
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
var char = this.buffers[i][j];
|
||||
|
||||
if (char == needle[match]) {
|
||||
// keep track where match started
|
||||
if (match == 0) {
|
||||
mstart = {
|
||||
i: i,
|
||||
j: j,
|
||||
pos: pos
|
||||
};
|
||||
}
|
||||
match++;
|
||||
if (match == needle.length) {
|
||||
// full match
|
||||
return mstart.pos;
|
||||
}
|
||||
} else if (match != 0) {
|
||||
// a partial match ended, go back to match starting position
|
||||
// this will continue the search at the next character
|
||||
i = mstart.i;
|
||||
j = mstart.j;
|
||||
pos = mstart.pos;
|
||||
match = 0;
|
||||
}
|
||||
|
||||
j++;
|
||||
pos++;
|
||||
}
|
||||
};
|
||||
|
||||
Buffers.prototype.toBuffer = function() {
|
||||
return this.slice();
|
||||
}
|
||||
|
||||
Buffers.prototype.toString = function(encoding, start, end) {
|
||||
return this.slice(start, end).toString(encoding);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
{
|
||||
"_from": "buffers@~0.1.1",
|
||||
"_id": "buffers@0.1.1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=",
|
||||
"_location": "/buffers",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "buffers@~0.1.1",
|
||||
"name": "buffers",
|
||||
"escapedName": "buffers",
|
||||
"rawSpec": "~0.1.1",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "~0.1.1"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/binary"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz",
|
||||
"_shasum": "b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb",
|
||||
"_spec": "buffers@~0.1.1",
|
||||
"_where": "/home/pi/blog.testing.derzombiiie.com/node_modules/binary",
|
||||
"author": {
|
||||
"name": "James Halliday",
|
||||
"email": "mail@substack.net",
|
||||
"url": "http://substack.net"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/substack/node-buffers/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "Treat a collection of Buffers as a single contiguous partially mutable Buffer.",
|
||||
"engines": {
|
||||
"node": ">=0.2.0"
|
||||
},
|
||||
"homepage": "https://github.com/substack/node-buffers#readme",
|
||||
"main": "./index",
|
||||
"name": "buffers",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/substack/node-buffers.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "expresso"
|
||||
},
|
||||
"version": "0.1.1"
|
||||
}
|
|
@ -0,0 +1,209 @@
|
|||
var assert = require('assert');
|
||||
var Buffers = require('../');
|
||||
|
||||
function create (xs, split) {
|
||||
var bufs = Buffers();
|
||||
var offset = 0;
|
||||
split.forEach(function (i) {
|
||||
bufs.push(new Buffer(xs.slice(offset, offset + i)));
|
||||
offset += i;
|
||||
});
|
||||
return bufs;
|
||||
}
|
||||
|
||||
exports.slice = function () {
|
||||
var xs = [0,1,2,3,4,5,6,7,8,9];
|
||||
var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];
|
||||
|
||||
splits.forEach(function (split) {
|
||||
var bufs = create(xs, split);
|
||||
assert.eql(new Buffer(xs), bufs.slice(),
|
||||
'[' + xs.join(',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + [].join.call(bufs.slice(), ',') + ']'
|
||||
);
|
||||
|
||||
for (var i = 0; i < xs.length; i++) {
|
||||
for (var j = i; j < xs.length; j++) {
|
||||
var a = bufs.slice(i,j);
|
||||
var b = new Buffer(xs.slice(i,j));
|
||||
|
||||
assert.eql(a, b,
|
||||
'[' + [].join.call(a, ',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + [].join.call(b, ',') + ']'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.splice = function () {
|
||||
var xs = [0,1,2,3,4,5,6,7,8,9];
|
||||
var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];
|
||||
|
||||
splits.forEach(function (split) {
|
||||
for (var i = 0; i < xs.length; i++) {
|
||||
for (var j = i; j < xs.length; j++) {
|
||||
var bufs = create(xs, split);
|
||||
var xs_ = xs.slice();
|
||||
|
||||
var a_ = bufs.splice(i,j);
|
||||
var a = [].slice.call(a_.slice());
|
||||
var b = xs_.splice(i,j);
|
||||
assert.eql(a, b,
|
||||
'[' + a.join(',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + b.join(',') + ']'
|
||||
);
|
||||
|
||||
assert.eql(bufs.slice(), new Buffer(xs_),
|
||||
'[' + [].join.call(bufs.slice(), ',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + [].join.call(xs_, ',') + ']'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.spliceRep = function () {
|
||||
var xs = [0,1,2,3,4,5,6,7,8,9];
|
||||
var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];
|
||||
var reps = [ [], [1], [5,6], [3,1,3,3,7], [9,8,7,6,5,4,3,2,1,2,3,4,5] ];
|
||||
|
||||
splits.forEach(function (split) {
|
||||
reps.forEach(function (rep) {
|
||||
for (var i = 0; i < xs.length; i++) {
|
||||
for (var j = i; j < xs.length; j++) {
|
||||
var bufs = create(xs, split);
|
||||
var xs_ = xs.slice();
|
||||
|
||||
var a_ = bufs.splice.apply(
|
||||
bufs, [ i, j ].concat(new Buffer(rep))
|
||||
);
|
||||
var a = [].slice.call(a_.slice());
|
||||
var b = xs_.splice.apply(xs_, [ i, j ].concat(rep));
|
||||
|
||||
assert.eql(a, b,
|
||||
'[' + a.join(',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + b.join(',') + ']'
|
||||
);
|
||||
|
||||
assert.eql(bufs.slice(), new Buffer(xs_),
|
||||
'[' + [].join.call(bufs.slice(), ',') + ']'
|
||||
+ ' != ' +
|
||||
'[' + [].join.call(xs_, ',') + ']'
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
exports.copy = function () {
|
||||
var xs = [0,1,2,3,4,5,6,7,8,9];
|
||||
var splits = [ [4,2,3,1], [2,2,2,2,2], [1,6,3,1], [9,2], [10], [5,5] ];
|
||||
|
||||
splits.forEach(function (split) {
|
||||
var bufs = create(xs, split);
|
||||
var buf = new Buffer(xs);
|
||||
|
||||
for (var i = 0; i < xs.length; i++) {
|
||||
for (var j = i; j < xs.length; j++) {
|
||||
var t0 = new Buffer(j - i);
|
||||
var t1 = new Buffer(j - i);
|
||||
|
||||
assert.eql(
|
||||
bufs.copy(t0, 0, i, j),
|
||||
buf.copy(t1, 0, i, j)
|
||||
);
|
||||
|
||||
assert.eql(
|
||||
[].slice.call(t0),
|
||||
[].slice.call(t1)
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
exports.push = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer([0]));
|
||||
bufs.push(new Buffer([1,2,3]));
|
||||
bufs.push(new Buffer([4,5]));
|
||||
bufs.push(new Buffer([6,7,8,9]));
|
||||
assert.eql(
|
||||
[].slice.call(bufs.slice()),
|
||||
[0,1,2,3,4,5,6,7,8,9]
|
||||
);
|
||||
|
||||
assert.throws(function () {
|
||||
bufs.push(new Buffer([11,12]), 'moo');
|
||||
});
|
||||
assert.eql(bufs.buffers.length, 4);
|
||||
};
|
||||
|
||||
exports.unshift = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.unshift(new Buffer([6,7,8,9]));
|
||||
bufs.unshift(new Buffer([4,5]));
|
||||
bufs.unshift(new Buffer([1,2,3]));
|
||||
bufs.unshift(new Buffer([0]));
|
||||
assert.eql(
|
||||
[].slice.call(bufs.slice()),
|
||||
[0,1,2,3,4,5,6,7,8,9]
|
||||
);
|
||||
assert.throws(function () {
|
||||
bufs.unshift(new Buffer([-2,-1]), 'moo');
|
||||
});
|
||||
assert.eql(bufs.buffers.length, 4);
|
||||
};
|
||||
|
||||
exports.get = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.unshift(new Buffer([6,7,8,9]));
|
||||
bufs.unshift(new Buffer([4,5]));
|
||||
bufs.unshift(new Buffer([1,2,3]));
|
||||
bufs.unshift(new Buffer([0]));
|
||||
assert.eql( bufs.get(0), 0 );
|
||||
assert.eql( bufs.get(1), 1 );
|
||||
assert.eql( bufs.get(2), 2 );
|
||||
assert.eql( bufs.get(3), 3 );
|
||||
assert.eql( bufs.get(4), 4 );
|
||||
assert.eql( bufs.get(5), 5 );
|
||||
assert.eql( bufs.get(6), 6 );
|
||||
assert.eql( bufs.get(7), 7 );
|
||||
assert.eql( bufs.get(8), 8 );
|
||||
assert.eql( bufs.get(9), 9 );
|
||||
};
|
||||
|
||||
exports.set = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer("Hel"));
|
||||
bufs.push(new Buffer("lo"));
|
||||
bufs.push(new Buffer("!"));
|
||||
bufs.set(0, 'h'.charCodeAt(0) );
|
||||
bufs.set(3, 'L'.charCodeAt(0) );
|
||||
bufs.set(5, '.'.charCodeAt(0) );
|
||||
assert.eql( bufs.slice(0).toString(), 'helLo.' );
|
||||
};
|
||||
|
||||
exports.indexOf = function () {
|
||||
var bufs = Buffers();
|
||||
bufs.push(new Buffer("Hel"));
|
||||
bufs.push(new Buffer("lo,"));
|
||||
bufs.push(new Buffer(" how are "));
|
||||
bufs.push(new Buffer("you"));
|
||||
bufs.push(new Buffer("?"));
|
||||
assert.eql( bufs.indexOf("Hello"), 0 );
|
||||
assert.eql( bufs.indexOf("Hello", 1), -1 );
|
||||
assert.eql( bufs.indexOf("ello"), 1 );
|
||||
assert.eql( bufs.indexOf("ello", 1), 1 );
|
||||
assert.eql( bufs.indexOf("ello", 2), -1 );
|
||||
assert.eql( bufs.indexOf("e"), 1 );
|
||||
assert.eql( bufs.indexOf("e", 2), 13 );
|
||||
assert.eql( bufs.indexOf(new Buffer([0x65]), 2), 13 );
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
node_modules
|
|
@ -0,0 +1,157 @@
|
|||
Chainsaw
|
||||
========
|
||||
|
||||
Build chainable fluent interfaces the easy way in node.js.
|
||||
|
||||
With this meta-module you can write modules with chainable interfaces.
|
||||
Chainsaw takes care of all of the boring details and makes nested flow control
|
||||
super simple too.
|
||||
|
||||
Just call `Chainsaw` with a constructor function like in the examples below.
|
||||
In your methods, just do `saw.next()` to move along to the next event and
|
||||
`saw.nest()` to create a nested chain.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
add_do.js
|
||||
---------
|
||||
|
||||
This silly example adds values with a chainsaw.
|
||||
|
||||
var Chainsaw = require('chainsaw');
|
||||
|
||||
function AddDo (sum) {
|
||||
return Chainsaw(function (saw) {
|
||||
this.add = function (n) {
|
||||
sum += n;
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, sum);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
AddDo(0)
|
||||
.add(5)
|
||||
.add(10)
|
||||
.do(function (sum) {
|
||||
if (sum > 12) this.add(-10);
|
||||
})
|
||||
.do(function (sum) {
|
||||
console.log('Sum: ' + sum);
|
||||
})
|
||||
;
|
||||
|
||||
Output:
|
||||
Sum: 5
|
||||
|
||||
prompt.js
|
||||
---------
|
||||
|
||||
This example provides a wrapper on top of stdin with the help of
|
||||
[node-lazy](https://github.com/pkrumins/node-lazy) for line-processing.
|
||||
|
||||
var Chainsaw = require('chainsaw');
|
||||
var Lazy = require('lazy');
|
||||
|
||||
module.exports = Prompt;
|
||||
function Prompt (stream) {
|
||||
var waiting = [];
|
||||
var lines = [];
|
||||
var lazy = Lazy(stream).lines.map(String)
|
||||
.forEach(function (line) {
|
||||
if (waiting.length) {
|
||||
var w = waiting.shift();
|
||||
w(line);
|
||||
}
|
||||
else lines.push(line);
|
||||
})
|
||||
;
|
||||
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.getline = function (f) {
|
||||
var g = function (line) {
|
||||
saw.nest(f, line, vars);
|
||||
};
|
||||
|
||||
if (lines.length) g(lines.shift());
|
||||
else waiting.push(g);
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, vars);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
And now for the new Prompt() module in action:
|
||||
|
||||
var util = require('util');
|
||||
var stdin = process.openStdin();
|
||||
|
||||
Prompt(stdin)
|
||||
.do(function () {
|
||||
util.print('x = ');
|
||||
})
|
||||
.getline(function (line, vars) {
|
||||
vars.x = parseInt(line, 10);
|
||||
})
|
||||
.do(function () {
|
||||
util.print('y = ');
|
||||
})
|
||||
.getline(function (line, vars) {
|
||||
vars.y = parseInt(line, 10);
|
||||
})
|
||||
.do(function (vars) {
|
||||
if (vars.x + vars.y < 10) {
|
||||
util.print('z = ');
|
||||
this.getline(function (line) {
|
||||
vars.z = parseInt(line, 10);
|
||||
})
|
||||
}
|
||||
else {
|
||||
vars.z = 0;
|
||||
}
|
||||
})
|
||||
.do(function (vars) {
|
||||
console.log('x + y + z = ' + (vars.x + vars.y + vars.z));
|
||||
process.exit();
|
||||
})
|
||||
;
|
||||
|
||||
Installation
|
||||
============
|
||||
|
||||
With [npm](http://github.com/isaacs/npm), just do:
|
||||
npm install chainsaw
|
||||
|
||||
or clone this project on github:
|
||||
|
||||
git clone http://github.com/substack/node-chainsaw.git
|
||||
|
||||
To run the tests with [expresso](http://github.com/visionmedia/expresso),
|
||||
just do:
|
||||
|
||||
expresso
|
||||
|
||||
|
||||
Light Mode vs Full Mode
|
||||
=======================
|
||||
|
||||
`node-chainsaw` supports two different modes. In full mode, every
|
||||
action is recorded, which allows you to replay actions using the
|
||||
`jump()`, `trap()` and `down()` methods.
|
||||
|
||||
However, if your chainsaws are long-lived, recording every action can
|
||||
consume a tremendous amount of memory, so we also offer a "light" mode
|
||||
where actions are not recorded and the aforementioned methods are
|
||||
disabled.
|
||||
|
||||
To enable light mode simply use `Chainsaw.light()` to construct your
|
||||
saw, instead of `Chainsaw()`.
|
||||
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
var Chainsaw = require('chainsaw');
|
||||
|
||||
function AddDo (sum) {
|
||||
return Chainsaw(function (saw) {
|
||||
this.add = function (n) {
|
||||
sum += n;
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, sum);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
AddDo(0)
|
||||
.add(5)
|
||||
.add(10)
|
||||
.do(function (sum) {
|
||||
if (sum > 12) this.add(-10);
|
||||
})
|
||||
.do(function (sum) {
|
||||
console.log('Sum: ' + sum);
|
||||
})
|
||||
;
|
|
@ -0,0 +1,67 @@
|
|||
var Chainsaw = require('chainsaw');
|
||||
var Lazy = require('lazy');
|
||||
|
||||
module.exports = Prompt;
|
||||
function Prompt (stream) {
|
||||
var waiting = [];
|
||||
var lines = [];
|
||||
var lazy = Lazy(stream).lines.map(String)
|
||||
.forEach(function (line) {
|
||||
if (waiting.length) {
|
||||
var w = waiting.shift();
|
||||
w(line);
|
||||
}
|
||||
else lines.push(line);
|
||||
})
|
||||
;
|
||||
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.getline = function (f) {
|
||||
var g = function (line) {
|
||||
saw.nest(f, line, vars);
|
||||
};
|
||||
|
||||
if (lines.length) g(lines.shift());
|
||||
else waiting.push(g);
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, vars);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
var util = require('util');
|
||||
if (__filename === process.argv[1]) {
|
||||
var stdin = process.openStdin();
|
||||
Prompt(stdin)
|
||||
.do(function () {
|
||||
util.print('x = ');
|
||||
})
|
||||
.getline(function (line, vars) {
|
||||
vars.x = parseInt(line, 10);
|
||||
})
|
||||
.do(function () {
|
||||
util.print('y = ');
|
||||
})
|
||||
.getline(function (line, vars) {
|
||||
vars.y = parseInt(line, 10);
|
||||
})
|
||||
.do(function (vars) {
|
||||
if (vars.x + vars.y < 10) {
|
||||
util.print('z = ');
|
||||
this.getline(function (line) {
|
||||
vars.z = parseInt(line, 10);
|
||||
})
|
||||
}
|
||||
else {
|
||||
vars.z = 0;
|
||||
}
|
||||
})
|
||||
.do(function (vars) {
|
||||
console.log('x + y + z = ' + (vars.x + vars.y + vars.z));
|
||||
process.exit();
|
||||
})
|
||||
;
|
||||
}
|
|
@ -0,0 +1,145 @@
|
|||
var Traverse = require('traverse');
|
||||
var EventEmitter = require('events').EventEmitter;
|
||||
|
||||
module.exports = Chainsaw;
|
||||
function Chainsaw (builder) {
|
||||
var saw = Chainsaw.saw(builder, {});
|
||||
var r = builder.call(saw.handlers, saw);
|
||||
if (r !== undefined) saw.handlers = r;
|
||||
saw.record();
|
||||
return saw.chain();
|
||||
};
|
||||
|
||||
Chainsaw.light = function ChainsawLight (builder) {
|
||||
var saw = Chainsaw.saw(builder, {});
|
||||
var r = builder.call(saw.handlers, saw);
|
||||
if (r !== undefined) saw.handlers = r;
|
||||
return saw.chain();
|
||||
};
|
||||
|
||||
Chainsaw.saw = function (builder, handlers) {
|
||||
var saw = new EventEmitter;
|
||||
saw.handlers = handlers;
|
||||
saw.actions = [];
|
||||
|
||||
saw.chain = function () {
|
||||
var ch = Traverse(saw.handlers).map(function (node) {
|
||||
if (this.isRoot) return node;
|
||||
var ps = this.path;
|
||||
|
||||
if (typeof node === 'function') {
|
||||
this.update(function () {
|
||||
saw.actions.push({
|
||||
path : ps,
|
||||
args : [].slice.call(arguments)
|
||||
});
|
||||
return ch;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
process.nextTick(function () {
|
||||
saw.emit('begin');
|
||||
saw.next();
|
||||
});
|
||||
|
||||
return ch;
|
||||
};
|
||||
|
||||
saw.pop = function () {
|
||||
return saw.actions.shift();
|
||||
};
|
||||
|
||||
saw.next = function () {
|
||||
var action = saw.pop();
|
||||
|
||||
if (!action) {
|
||||
saw.emit('end');
|
||||
}
|
||||
else if (!action.trap) {
|
||||
var node = saw.handlers;
|
||||
action.path.forEach(function (key) { node = node[key] });
|
||||
node.apply(saw.handlers, action.args);
|
||||
}
|
||||
};
|
||||
|
||||
saw.nest = function (cb) {
|
||||
var args = [].slice.call(arguments, 1);
|
||||
var autonext = true;
|
||||
|
||||
if (typeof cb === 'boolean') {
|
||||
var autonext = cb;
|
||||
cb = args.shift();
|
||||
}
|
||||
|
||||
var s = Chainsaw.saw(builder, {});
|
||||
var r = builder.call(s.handlers, s);
|
||||
|
||||
if (r !== undefined) s.handlers = r;
|
||||
|
||||
// If we are recording...
|
||||
if ("undefined" !== typeof saw.step) {
|
||||
// ... our children should, too
|
||||
s.record();
|
||||
}
|
||||
|
||||
cb.apply(s.chain(), args);
|
||||
if (autonext !== false) s.on('end', saw.next);
|
||||
};
|
||||
|
||||
saw.record = function () {
|
||||
upgradeChainsaw(saw);
|
||||
};
|
||||
|
||||
['trap', 'down', 'jump'].forEach(function (method) {
|
||||
saw[method] = function () {
|
||||
throw new Error("To use the trap, down and jump features, please "+
|
||||
"call record() first to start recording actions.");
|
||||
};
|
||||
});
|
||||
|
||||
return saw;
|
||||
};
|
||||
|
||||
function upgradeChainsaw(saw) {
|
||||
saw.step = 0;
|
||||
|
||||
// override pop
|
||||
saw.pop = function () {
|
||||
return saw.actions[saw.step++];
|
||||
};
|
||||
|
||||
saw.trap = function (name, cb) {
|
||||
var ps = Array.isArray(name) ? name : [name];
|
||||
saw.actions.push({
|
||||
path : ps,
|
||||
step : saw.step,
|
||||
cb : cb,
|
||||
trap : true
|
||||
});
|
||||
};
|
||||
|
||||
saw.down = function (name) {
|
||||
var ps = (Array.isArray(name) ? name : [name]).join('/');
|
||||
var i = saw.actions.slice(saw.step).map(function (x) {
|
||||
if (x.trap && x.step <= saw.step) return false;
|
||||
return x.path.join('/') == ps;
|
||||
}).indexOf(true);
|
||||
|
||||
if (i >= 0) saw.step += i;
|
||||
else saw.step = saw.actions.length;
|
||||
|
||||
var act = saw.actions[saw.step - 1];
|
||||
if (act && act.trap) {
|
||||
// It's a trap!
|
||||
saw.step = act.step;
|
||||
act.cb();
|
||||
}
|
||||
else saw.next();
|
||||
};
|
||||
|
||||
saw.jump = function (step) {
|
||||
saw.step = step;
|
||||
saw.next();
|
||||
};
|
||||
};
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"_from": "chainsaw@~0.1.0",
|
||||
"_id": "chainsaw@0.1.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=",
|
||||
"_location": "/chainsaw",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "chainsaw@~0.1.0",
|
||||
"name": "chainsaw",
|
||||
"escapedName": "chainsaw",
|
||||
"rawSpec": "~0.1.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "~0.1.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/binary"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz",
|
||||
"_shasum": "5eab50b28afe58074d0d58291388828b5e5fbc98",
|
||||
"_spec": "chainsaw@~0.1.0",
|
||||
"_where": "/home/pi/blog.testing.derzombiiie.com/node_modules/binary",
|
||||
"author": {
|
||||
"name": "James Halliday",
|
||||
"email": "mail@substack.net",
|
||||
"url": "http://substack.net"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/substack/node-chainsaw/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"traverse": ">=0.3.0 <0.4"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "Build chainable fluent interfaces the easy way... with a freakin' chainsaw!",
|
||||
"engine": {
|
||||
"node": ">=0.4.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "*"
|
||||
},
|
||||
"homepage": "https://github.com/substack/node-chainsaw#readme",
|
||||
"keywords": [
|
||||
"chain",
|
||||
"fluent",
|
||||
"interface",
|
||||
"monad",
|
||||
"monadic"
|
||||
],
|
||||
"license": "MIT/X11",
|
||||
"main": "./index.js",
|
||||
"name": "chainsaw",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/substack/node-chainsaw.git"
|
||||
},
|
||||
"version": "0.1.0"
|
||||
}
|
|
@ -0,0 +1,418 @@
|
|||
var assert = require('assert');
|
||||
var Chainsaw = require('../index');
|
||||
|
||||
exports.getset = function () {
|
||||
var to = setTimeout(function () {
|
||||
assert.fail('builder never fired');
|
||||
}, 1000);
|
||||
|
||||
var ch = Chainsaw(function (saw) {
|
||||
clearTimeout(to);
|
||||
var num = 0;
|
||||
|
||||
this.get = function (cb) {
|
||||
cb(num);
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.set = function (n) {
|
||||
num = n;
|
||||
saw.next();
|
||||
};
|
||||
|
||||
var ti = setTimeout(function () {
|
||||
assert.fail('end event not emitted');
|
||||
}, 50);
|
||||
|
||||
saw.on('end', function () {
|
||||
clearTimeout(ti);
|
||||
assert.equal(times, 3);
|
||||
});
|
||||
});
|
||||
|
||||
var times = 0;
|
||||
ch
|
||||
.get(function (x) {
|
||||
assert.equal(x, 0);
|
||||
times ++;
|
||||
})
|
||||
.set(10)
|
||||
.get(function (x) {
|
||||
assert.equal(x, 10);
|
||||
times ++;
|
||||
})
|
||||
.set(20)
|
||||
.get(function (x) {
|
||||
assert.equal(x, 20);
|
||||
times ++;
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.nest = function () {
|
||||
var ch = (function () {
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, vars);
|
||||
};
|
||||
});
|
||||
})();
|
||||
|
||||
var order = [];
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("Didn't get to the end");
|
||||
}, 50);
|
||||
|
||||
ch
|
||||
.do(function (vars) {
|
||||
vars.x = 'y';
|
||||
order.push(1);
|
||||
|
||||
this
|
||||
.do(function (vs) {
|
||||
order.push(2);
|
||||
vs.x = 'x';
|
||||
})
|
||||
.do(function (vs) {
|
||||
order.push(3);
|
||||
vs.z = 'z';
|
||||
})
|
||||
;
|
||||
})
|
||||
.do(function (vars) {
|
||||
vars.y = 'y';
|
||||
order.push(4);
|
||||
})
|
||||
.do(function (vars) {
|
||||
assert.eql(order, [1,2,3,4]);
|
||||
assert.eql(vars, { x : 'x', y : 'y', z : 'z' });
|
||||
clearTimeout(to);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.nestWait = function () {
|
||||
var ch = (function () {
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.do = function (cb) {
|
||||
saw.nest(cb, vars);
|
||||
};
|
||||
|
||||
this.wait = function (n) {
|
||||
setTimeout(function () {
|
||||
saw.next();
|
||||
}, n);
|
||||
};
|
||||
});
|
||||
})();
|
||||
|
||||
var order = [];
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("Didn't get to the end");
|
||||
}, 1000);
|
||||
|
||||
var times = {};
|
||||
|
||||
ch
|
||||
.do(function (vars) {
|
||||
vars.x = 'y';
|
||||
order.push(1);
|
||||
|
||||
this
|
||||
.do(function (vs) {
|
||||
order.push(2);
|
||||
vs.x = 'x';
|
||||
times.x = Date.now();
|
||||
})
|
||||
.wait(50)
|
||||
.do(function (vs) {
|
||||
order.push(3);
|
||||
vs.z = 'z';
|
||||
|
||||
times.z = Date.now();
|
||||
var dt = times.z - times.x;
|
||||
assert.ok(dt >= 50 && dt < 75);
|
||||
})
|
||||
;
|
||||
})
|
||||
.do(function (vars) {
|
||||
vars.y = 'y';
|
||||
order.push(4);
|
||||
|
||||
times.y = Date.now();
|
||||
})
|
||||
.wait(100)
|
||||
.do(function (vars) {
|
||||
assert.eql(order, [1,2,3,4]);
|
||||
assert.eql(vars, { x : 'x', y : 'y', z : 'z' });
|
||||
clearTimeout(to);
|
||||
|
||||
times.end = Date.now();
|
||||
var dt = times.end - times.y;
|
||||
assert.ok(dt >= 100 && dt < 125)
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.nestNext = function () {
|
||||
var ch = (function () {
|
||||
var vars = {};
|
||||
return Chainsaw(function (saw) {
|
||||
this.do = function (cb) {
|
||||
saw.nest(false, function () {
|
||||
var args = [].slice.call(arguments);
|
||||
args.push(saw.next);
|
||||
cb.apply(this, args);
|
||||
}, vars);
|
||||
};
|
||||
});
|
||||
})();
|
||||
|
||||
var order = [];
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("Didn't get to the end");
|
||||
}, 500);
|
||||
|
||||
var times = [];
|
||||
|
||||
ch
|
||||
.do(function (vars, next_) {
|
||||
vars.x = 'y';
|
||||
order.push(1);
|
||||
|
||||
this
|
||||
.do(function (vs, next) {
|
||||
order.push(2);
|
||||
vs.x = 'x';
|
||||
setTimeout(next, 30);
|
||||
})
|
||||
.do(function (vs, next) {
|
||||
order.push(3);
|
||||
vs.z = 'z';
|
||||
setTimeout(next, 10);
|
||||
})
|
||||
.do(function () {
|
||||
setTimeout(next_, 20);
|
||||
})
|
||||
;
|
||||
})
|
||||
.do(function (vars, next) {
|
||||
vars.y = 'y';
|
||||
order.push(4);
|
||||
setTimeout(next, 5);
|
||||
})
|
||||
.do(function (vars) {
|
||||
assert.eql(order, [1,2,3,4]);
|
||||
assert.eql(vars, { x : 'x', y : 'y', z : 'z' });
|
||||
|
||||
clearTimeout(to);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.builder = function () {
|
||||
var cx = Chainsaw(function (saw) {
|
||||
this.x = function () {};
|
||||
});
|
||||
assert.ok(cx.x);
|
||||
|
||||
var cy = Chainsaw(function (saw) {
|
||||
return { y : function () {} };
|
||||
});
|
||||
assert.ok(cy.y);
|
||||
|
||||
var cz = Chainsaw(function (saw) {
|
||||
return { z : function (cb) { saw.nest(cb) } };
|
||||
});
|
||||
assert.ok(cz.z);
|
||||
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("Nested z didn't run");
|
||||
}, 50);
|
||||
|
||||
cz.z(function () {
|
||||
clearTimeout(to);
|
||||
assert.ok(this.z);
|
||||
});
|
||||
};
|
||||
|
||||
this.attr = function () {
|
||||
var to = setTimeout(function () {
|
||||
assert.fail("attr chain didn't finish");
|
||||
}, 50);
|
||||
|
||||
var xy = [];
|
||||
var ch = Chainsaw(function (saw) {
|
||||
this.h = {
|
||||
x : function () {
|
||||
xy.push('x');
|
||||
saw.next();
|
||||
},
|
||||
y : function () {
|
||||
xy.push('y');
|
||||
saw.next();
|
||||
assert.eql(xy, ['x','y']);
|
||||
clearTimeout(to);
|
||||
}
|
||||
};
|
||||
});
|
||||
assert.ok(ch.h);
|
||||
assert.ok(ch.h.x);
|
||||
assert.ok(ch.h.y);
|
||||
|
||||
ch.h.x().h.y();
|
||||
};
|
||||
|
||||
exports.down = function () {
|
||||
var error = null;
|
||||
var s;
|
||||
var ch = Chainsaw(function (saw) {
|
||||
s = saw;
|
||||
this.raise = function (err) {
|
||||
error = err;
|
||||
saw.down('catch');
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
cb.call(this);
|
||||
};
|
||||
|
||||
this.catch = function (cb) {
|
||||
if (error) {
|
||||
saw.nest(cb, error);
|
||||
error = null;
|
||||
}
|
||||
else saw.next();
|
||||
};
|
||||
});
|
||||
|
||||
var to = setTimeout(function () {
|
||||
assert.fail(".do() after .catch() didn't fire");
|
||||
}, 50);
|
||||
|
||||
ch
|
||||
.do(function () {
|
||||
this.raise('pow');
|
||||
})
|
||||
.do(function () {
|
||||
assert.fail("raise didn't skip over this do block");
|
||||
})
|
||||
.catch(function (err) {
|
||||
assert.equal(err, 'pow');
|
||||
})
|
||||
.do(function () {
|
||||
clearTimeout(to);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.trap = function () {
|
||||
var error = null;
|
||||
var ch = Chainsaw(function (saw) {
|
||||
var pars = 0;
|
||||
var stack = [];
|
||||
var i = 0;
|
||||
|
||||
this.par = function (cb) {
|
||||
pars ++;
|
||||
var j = i ++;
|
||||
cb.call(function () {
|
||||
pars --;
|
||||
stack[j] = [].slice.call(arguments);
|
||||
saw.down('result');
|
||||
});
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.join = function (cb) {
|
||||
saw.trap('result', function () {
|
||||
if (pars == 0) {
|
||||
cb.apply(this, stack);
|
||||
saw.next();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
this.raise = function (err) {
|
||||
error = err;
|
||||
saw.down('catch');
|
||||
};
|
||||
|
||||
this.do = function (cb) {
|
||||
cb.call(this);
|
||||
};
|
||||
|
||||
this.catch = function (cb) {
|
||||
if (error) {
|
||||
saw.nest(cb, error);
|
||||
error = null;
|
||||
}
|
||||
else saw.next();
|
||||
};
|
||||
});
|
||||
|
||||
var to = setTimeout(function () {
|
||||
assert.fail(".do() after .join() didn't fire");
|
||||
}, 100);
|
||||
var tj = setTimeout(function () {
|
||||
assert.fail('.join() never fired');
|
||||
}, 100);
|
||||
|
||||
var joined = false;
|
||||
ch
|
||||
.par(function () {
|
||||
setTimeout(this.bind(null, 1), 50);
|
||||
})
|
||||
.par(function () {
|
||||
setTimeout(this.bind(null, 2), 25);
|
||||
})
|
||||
.join(function (x, y) {
|
||||
assert.equal(x[0], 1);
|
||||
assert.equal(y[0], 2);
|
||||
clearTimeout(tj);
|
||||
joined = true;
|
||||
})
|
||||
.do(function () {
|
||||
clearTimeout(to);
|
||||
assert.ok(joined);
|
||||
})
|
||||
;
|
||||
};
|
||||
|
||||
exports.jump = function () {
|
||||
var to = setTimeout(function () {
|
||||
assert.fail('builder never fired');
|
||||
}, 50);
|
||||
|
||||
var xs = [ 4, 5, 6, -4, 8, 9, -1, 8 ];
|
||||
var xs_ = [];
|
||||
|
||||
var ch = Chainsaw(function (saw) {
|
||||
this.x = function (i) {
|
||||
xs_.push(i);
|
||||
saw.next();
|
||||
};
|
||||
|
||||
this.y = function (step) {
|
||||
var x = xs.shift();
|
||||
if (x > 0) saw.jump(step);
|
||||
else saw.next();
|
||||
};
|
||||
|
||||
saw.on('end', function () {
|
||||
clearTimeout(to);
|
||||
assert.eql(xs, [ 8 ]);
|
||||
assert.eql(xs_, [ 1, 1, 1, 1, 2, 3, 2, 3, 2, 3 ]);
|
||||
});
|
||||
});
|
||||
|
||||
ch
|
||||
.x(1)
|
||||
.y(0)
|
||||
.x(2)
|
||||
.x(3)
|
||||
.y(2)
|
||||
;
|
||||
};
|
|
@ -0,0 +1,22 @@
|
|||
Copyright (c) 2010
|
||||
|
||||
Marak Squires
|
||||
Alexis Sellier (cloudhead)
|
||||
|
||||
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.
|
|
@ -0,0 +1,77 @@
|
|||
# colors.js - get color and style in your node.js console ( and browser ) like what
|
||||
|
||||
<img src="http://i.imgur.com/goJdO.png" border = "0"/>
|
||||
|
||||
|
||||
## Installation
|
||||
|
||||
npm install colors
|
||||
|
||||
## colors and styles!
|
||||
|
||||
- bold
|
||||
- italic
|
||||
- underline
|
||||
- inverse
|
||||
- yellow
|
||||
- cyan
|
||||
- white
|
||||
- magenta
|
||||
- green
|
||||
- red
|
||||
- grey
|
||||
- blue
|
||||
- rainbow
|
||||
- zebra
|
||||
- random
|
||||
|
||||
## Usage
|
||||
|
||||
``` js
|
||||
var colors = require('./colors');
|
||||
|
||||
console.log('hello'.green); // outputs green text
|
||||
console.log('i like cake and pies'.underline.red) // outputs red underlined text
|
||||
console.log('inverse the color'.inverse); // inverses the color
|
||||
console.log('OMG Rainbows!'.rainbow); // rainbow (ignores spaces)
|
||||
```
|
||||
|
||||
# Creating Custom themes
|
||||
|
||||
```js
|
||||
|
||||
var require('colors');
|
||||
|
||||
colors.setTheme({
|
||||
silly: 'rainbow',
|
||||
input: 'grey',
|
||||
verbose: 'cyan',
|
||||
prompt: 'grey',
|
||||
info: 'green',
|
||||
data: 'grey',
|
||||
help: 'cyan',
|
||||
warn: 'yellow',
|
||||
debug: 'blue',
|
||||
error: 'red'
|
||||
});
|
||||
|
||||
// outputs red text
|
||||
console.log("this is an error".error);
|
||||
|
||||
// outputs yellow text
|
||||
console.log("this is a warning".warn);
|
||||
```
|
||||
|
||||
|
||||
### Contributors
|
||||
|
||||
Marak (Marak Squires)
|
||||
Alexis Sellier (cloudhead)
|
||||
mmalecki (Maciej Małecki)
|
||||
nicoreed (Nico Reed)
|
||||
morganrallen (Morgan Allen)
|
||||
JustinCampbell (Justin Campbell)
|
||||
ded (Dustin Diaz)
|
||||
|
||||
|
||||
#### , Marak Squires , Justin Campbell, Dustin Diaz (@ded)
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
colors.js
|
||||
|
||||
Copyright (c) 2010
|
||||
|
||||
Marak Squires
|
||||
Alexis Sellier (cloudhead)
|
||||
|
||||
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.
|
||||
|
||||
*/
|
||||
|
||||
var isHeadless = false;
|
||||
|
||||
if (typeof module !== 'undefined') {
|
||||
isHeadless = true;
|
||||
}
|
||||
|
||||
if (!isHeadless) {
|
||||
var exports = {};
|
||||
var module = {};
|
||||
var colors = exports;
|
||||
exports.mode = "browser";
|
||||
} else {
|
||||
exports.mode = "console";
|
||||
}
|
||||
|
||||
//
|
||||
// Prototypes the string object to have additional method calls that add terminal colors
|
||||
//
|
||||
var addProperty = function (color, func) {
|
||||
var allowOverride = ['bold'];
|
||||
exports[color] = function(str) {
|
||||
return func.apply(str);
|
||||
};
|
||||
String.prototype.__defineGetter__(color, func);
|
||||
}
|
||||
|
||||
//
|
||||
// Iterate through all default styles and colors
|
||||
//
|
||||
|
||||
var x = ['bold', 'underline', 'italic', 'inverse', 'grey', 'black', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta'];
|
||||
x.forEach(function (style) {
|
||||
|
||||
// __defineGetter__ at the least works in more browsers
|
||||
// http://robertnyman.com/javascript/javascript-getters-setters.html
|
||||
// Object.defineProperty only works in Chrome
|
||||
addProperty(style, function () {
|
||||
return stylize(this, style);
|
||||
});
|
||||
});
|
||||
|
||||
function sequencer(map) {
|
||||
return function () {
|
||||
if (!isHeadless) {
|
||||
return this.replace(/( )/, '$1');
|
||||
}
|
||||
var exploded = this.split("");
|
||||
var i = 0;
|
||||
exploded = exploded.map(map);
|
||||
return exploded.join("");
|
||||
}
|
||||
}
|
||||
|
||||
var rainbowMap = (function () {
|
||||
var rainbowColors = ['red','yellow','green','blue','magenta']; //RoY G BiV
|
||||
return function (letter, i, exploded) {
|
||||
if (letter == " ") {
|
||||
return letter;
|
||||
} else {
|
||||
return stylize(letter, rainbowColors[i++ % rainbowColors.length]);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
exports.addSequencer = function (name, map) {
|
||||
addProperty(name, sequencer(map));
|
||||
}
|
||||
|
||||
exports.addSequencer('rainbow', rainbowMap);
|
||||
exports.addSequencer('zebra', function (letter, i, exploded) {
|
||||
return i % 2 === 0 ? letter : letter.inverse;
|
||||
});
|
||||
|
||||
exports.setTheme = function (theme) {
|
||||
Object.keys(theme).forEach(function(prop){
|
||||
addProperty(prop, function(){
|
||||
return exports[theme[prop]](this);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function stylize(str, style) {
|
||||
|
||||
if (exports.mode == 'console') {
|
||||
var styles = {
|
||||
//styles
|
||||
'bold' : ['\033[1m', '\033[22m'],
|
||||
'italic' : ['\033[3m', '\033[23m'],
|
||||
'underline' : ['\033[4m', '\033[24m'],
|
||||
'inverse' : ['\033[7m', '\033[27m'],
|
||||
//grayscale
|
||||
'white' : ['\033[37m', '\033[39m'],
|
||||
'grey' : ['\033[90m', '\033[39m'],
|
||||
'black' : ['\033[30m', '\033[39m'],
|
||||
//colors
|
||||
'blue' : ['\033[34m', '\033[39m'],
|
||||
'cyan' : ['\033[36m', '\033[39m'],
|
||||
'green' : ['\033[32m', '\033[39m'],
|
||||
'magenta' : ['\033[35m', '\033[39m'],
|
||||
'red' : ['\033[31m', '\033[39m'],
|
||||
'yellow' : ['\033[33m', '\033[39m']
|
||||
};
|
||||
} else if (exports.mode == 'browser') {
|
||||
var styles = {
|
||||
//styles
|
||||
'bold' : ['<b>', '</b>'],
|
||||
'italic' : ['<i>', '</i>'],
|
||||
'underline' : ['<u>', '</u>'],
|
||||
'inverse' : ['<span style="background-color:black;color:white;">', '</span>'],
|
||||
//grayscale
|
||||
'white' : ['<span style="color:white;">', '</span>'],
|
||||
'grey' : ['<span style="color:grey;">', '</span>'],
|
||||
'black' : ['<span style="color:black;">', '</span>'],
|
||||
//colors
|
||||
'blue' : ['<span style="color:blue;">', '</span>'],
|
||||
'cyan' : ['<span style="color:cyan;">', '</span>'],
|
||||
'green' : ['<span style="color:green;">', '</span>'],
|
||||
'magenta' : ['<span style="color:magenta;">', '</span>'],
|
||||
'red' : ['<span style="color:red;">', '</span>'],
|
||||
'yellow' : ['<span style="color:yellow;">', '</span>']
|
||||
};
|
||||
} else if (exports.mode == 'none') {
|
||||
return str;
|
||||
} else {
|
||||
console.log('unsupported mode, try "browser", "console" or "none"');
|
||||
}
|
||||
return styles[style][0] + str + styles[style][1];
|
||||
};
|
||||
|
||||
// don't summon zalgo
|
||||
addProperty('zalgo', function () {
|
||||
return zalgo(this);
|
||||
});
|
||||
|
||||
// please no
|
||||
function zalgo(text, options) {
|
||||
var soul = {
|
||||
"up" : [
|
||||
'̍','̎','̄','̅',
|
||||
'̿','̑','̆','̐',
|
||||
'͒','͗','͑','̇',
|
||||
'̈','̊','͂','̓',
|
||||
'̈','͊','͋','͌',
|
||||
'̃','̂','̌','͐',
|
||||
'̀','́','̋','̏',
|
||||
'̒','̓','̔','̽',
|
||||
'̉','ͣ','ͤ','ͥ',
|
||||
'ͦ','ͧ','ͨ','ͩ',
|
||||
'ͪ','ͫ','ͬ','ͭ',
|
||||
'ͮ','ͯ','̾','͛',
|
||||
'͆','̚'
|
||||
],
|
||||
"down" : [
|
||||
'̖','̗','̘','̙',
|
||||
'̜','̝','̞','̟',
|
||||
'̠','̤','̥','̦',
|
||||
'̩','̪','̫','̬',
|
||||
'̭','̮','̯','̰',
|
||||
'̱','̲','̳','̹',
|
||||
'̺','̻','̼','ͅ',
|
||||
'͇','͈','͉','͍',
|
||||
'͎','͓','͔','͕',
|
||||
'͖','͙','͚','̣'
|
||||
],
|
||||
"mid" : [
|
||||
'̕','̛','̀','́',
|
||||
'͘','̡','̢','̧',
|
||||
'̨','̴','̵','̶',
|
||||
'͜','͝','͞',
|
||||
'͟','͠','͢','̸',
|
||||
'̷','͡',' ҉'
|
||||
]
|
||||
},
|
||||
all = [].concat(soul.up, soul.down, soul.mid),
|
||||
zalgo = {};
|
||||
|
||||
function randomNumber(range) {
|
||||
r = Math.floor(Math.random()*range);
|
||||
return r;
|
||||
};
|
||||
|
||||
function is_char(character) {
|
||||
var bool = false;
|
||||
all.filter(function(i){
|
||||
bool = (i == character);
|
||||
});
|
||||
return bool;
|
||||
}
|
||||
|
||||
function heComes(text, options){
|
||||
result = '';
|
||||
options = options || {};
|
||||
options["up"] = options["up"] || true;
|
||||
options["mid"] = options["mid"] || true;
|
||||
options["down"] = options["down"] || true;
|
||||
options["size"] = options["size"] || "maxi";
|
||||
var counts;
|
||||
text = text.split('');
|
||||
for(var l in text){
|
||||
if(is_char(l)) { continue; }
|
||||
result = result + text[l];
|
||||
|
||||
counts = {"up" : 0, "down" : 0, "mid" : 0};
|
||||
|
||||
switch(options.size) {
|
||||
case 'mini':
|
||||
counts.up = randomNumber(8);
|
||||
counts.min= randomNumber(2);
|
||||
counts.down = randomNumber(8);
|
||||
break;
|
||||
case 'maxi':
|
||||
counts.up = randomNumber(16) + 3;
|
||||
counts.min = randomNumber(4) + 1;
|
||||
counts.down = randomNumber(64) + 3;
|
||||
break;
|
||||
default:
|
||||
counts.up = randomNumber(8) + 1;
|
||||
counts.mid = randomNumber(6) / 2;
|
||||
counts.down= randomNumber(8) + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
var arr = ["up", "mid", "down"];
|
||||
for(var d in arr){
|
||||
var index = arr[d];
|
||||
for (var i = 0 ; i <= counts[index]; i++)
|
||||
{
|
||||
if(options[index]) {
|
||||
result = result + soul[index][randomNumber(soul[index].length)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
};
|
||||
return heComes(text);
|
||||
}
|
||||
|
||||
addProperty('stripColors', function() {
|
||||
return ("" + this).replace(/\u001b\[\d+m/g,'');
|
||||
});
|
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
||||
<title>Colors Example</title>
|
||||
<script src="colors.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
|
||||
var test = colors.red("hopefully colorless output");
|
||||
|
||||
document.write('Rainbows are fun!'.rainbow + '<br/>');
|
||||
document.write('So '.italic + 'are'.underline + ' styles! '.bold + 'inverse'.inverse); // styles not widely supported
|
||||
document.write('Chains are also cool.'.bold.italic.underline.red); // styles not widely supported
|
||||
//document.write('zalgo time!'.zalgo);
|
||||
document.write(test.stripColors);
|
||||
document.write("a".grey + " b".black);
|
||||
|
||||
document.write("Zebras are so fun!".zebra);
|
||||
|
||||
document.write(colors.rainbow('Rainbows are fun!'));
|
||||
document.write(colors.italic('So ') + colors.underline('are') + colors.bold(' styles! ') + colors.inverse('inverse')); // styles not widely supported
|
||||
document.write(colors.bold(colors.italic(colors.underline(colors.red('Chains are also cool.'))))); // styles not widely supported
|
||||
//document.write(colors.zalgo('zalgo time!'));
|
||||
document.write(colors.stripColors(test));
|
||||
document.write(colors.grey("a") + colors.black(" b"));
|
||||
|
||||
colors.addSequencer("america", function(letter, i, exploded) {
|
||||
if(letter === " ") return letter;
|
||||
switch(i%3) {
|
||||
case 0: return letter.red;
|
||||
case 1: return letter.white;
|
||||
case 2: return letter.blue;
|
||||
}
|
||||
});
|
||||
|
||||
colors.addSequencer("random", (function() {
|
||||
var available = ['bold', 'underline', 'italic', 'inverse', 'grey', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta'];
|
||||
|
||||
return function(letter, i, exploded) {
|
||||
return letter === " " ? letter : letter[available[Math.round(Math.random() * (available.length - 1))]];
|
||||
};
|
||||
})());
|
||||
|
||||
document.write("AMERICA! F--K YEAH!".america);
|
||||
document.write("So apparently I've been to Mars, with all the little green men. But you know, I don't recall.".random);
|
||||
|
||||
//
|
||||
// Custom themes
|
||||
//
|
||||
|
||||
colors.setTheme({
|
||||
silly: 'rainbow',
|
||||
input: 'grey',
|
||||
verbose: 'cyan',
|
||||
prompt: 'grey',
|
||||
info: 'green',
|
||||
data: 'grey',
|
||||
help: 'cyan',
|
||||
warn: 'yellow',
|
||||
debug: 'blue',
|
||||
error: 'red'
|
||||
});
|
||||
|
||||
// outputs red text
|
||||
document.write("this is an error".error);
|
||||
|
||||
// outputs yellow text
|
||||
document.write("this is a warning".warn);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,65 @@
|
|||
var colors = require('./colors');
|
||||
|
||||
//colors.mode = "browser";
|
||||
|
||||
var test = colors.red("hopefully colorless output");
|
||||
console.log('Rainbows are fun!'.rainbow);
|
||||
console.log('So '.italic + 'are'.underline + ' styles! '.bold + 'inverse'.inverse); // styles not widely supported
|
||||
console.log('Chains are also cool.'.bold.italic.underline.red); // styles not widely supported
|
||||
//console.log('zalgo time!'.zalgo);
|
||||
console.log(test.stripColors);
|
||||
console.log("a".grey + " b".black);
|
||||
|
||||
console.log("Zebras are so fun!".zebra);
|
||||
|
||||
console.log(colors.rainbow('Rainbows are fun!'));
|
||||
console.log(colors.italic('So ') + colors.underline('are') + colors.bold(' styles! ') + colors.inverse('inverse')); // styles not widely supported
|
||||
console.log(colors.bold(colors.italic(colors.underline(colors.red('Chains are also cool.'))))); // styles not widely supported
|
||||
//console.log(colors.zalgo('zalgo time!'));
|
||||
console.log(colors.stripColors(test));
|
||||
console.log(colors.grey("a") + colors.black(" b"));
|
||||
|
||||
colors.addSequencer("america", function(letter, i, exploded) {
|
||||
if(letter === " ") return letter;
|
||||
switch(i%3) {
|
||||
case 0: return letter.red;
|
||||
case 1: return letter.white;
|
||||
case 2: return letter.blue;
|
||||
}
|
||||
});
|
||||
|
||||
colors.addSequencer("random", (function() {
|
||||
var available = ['bold', 'underline', 'italic', 'inverse', 'grey', 'yellow', 'red', 'green', 'blue', 'white', 'cyan', 'magenta'];
|
||||
|
||||
return function(letter, i, exploded) {
|
||||
return letter === " " ? letter : letter[available[Math.round(Math.random() * (available.length - 1))]];
|
||||
};
|
||||
})());
|
||||
|
||||
console.log("AMERICA! F--K YEAH!".america);
|
||||
console.log("So apparently I've been to Mars, with all the little green men. But you know, I don't recall.".random);
|
||||
|
||||
//
|
||||
// Custom themes
|
||||
//
|
||||
|
||||
colors.setTheme({
|
||||
silly: 'rainbow',
|
||||
input: 'grey',
|
||||
verbose: 'cyan',
|
||||
prompt: 'grey',
|
||||
info: 'green',
|
||||
data: 'grey',
|
||||
help: 'cyan',
|
||||
warn: 'yellow',
|
||||
debug: 'blue',
|
||||
error: 'red'
|
||||
});
|
||||
|
||||
// outputs red text
|
||||
console.log("this is an error".error);
|
||||
|
||||
// outputs yellow text
|
||||
console.log("this is a warning".warn);
|
||||
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"_from": "colors@0.6.0-1",
|
||||
"_id": "colors@0.6.0-1",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-bbtozri8YPKzE9zFzhWZ8G0Z5no=",
|
||||
"_location": "/colors",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "colors@0.6.0-1",
|
||||
"name": "colors",
|
||||
"escapedName": "colors",
|
||||
"rawSpec": "0.6.0-1",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "0.6.0-1"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/geoip-ultralight"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/colors/-/colors-0.6.0-1.tgz",
|
||||
"_shasum": "6dbb68ceb8bc60f2b313dcc5ce1599f06d19e67a",
|
||||
"_spec": "colors@0.6.0-1",
|
||||
"_where": "/home/pi/blog.testing.derzombiiie.com/node_modules/geoip-ultralight",
|
||||
"author": {
|
||||
"name": "Marak Squires"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/Marak/colors.js/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "get colors in your node.js console like what",
|
||||
"engines": {
|
||||
"node": ">=0.1.90"
|
||||
},
|
||||
"homepage": "https://github.com/Marak/colors.js#readme",
|
||||
"main": "colors",
|
||||
"name": "colors",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+ssh://git@github.com/Marak/colors.js.git"
|
||||
},
|
||||
"version": "0.6.0-1"
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
var assert = require('assert'),
|
||||
colors = require('./colors');
|
||||
|
||||
//
|
||||
// This is a pretty nice example on how tests shouldn't be written. However,
|
||||
// it's more about API stability than about really testing it (although it's
|
||||
// a pretty complete test suite).
|
||||
//
|
||||
|
||||
var s = 'string';
|
||||
|
||||
function a(s, code) {
|
||||
return '\033[' + code.toString() + 'm' + s + '\033[39m';
|
||||
}
|
||||
|
||||
function aE(s, color, code) {
|
||||
assert.equal(s[color], a(s, code));
|
||||
assert.equal(colors[color](s), a(s, code));
|
||||
assert.equal(s[color], colors[color](s));
|
||||
assert.equal(s[color].stripColors, s);
|
||||
assert.equal(s[color].stripColors, colors.stripColors(s));
|
||||
}
|
||||
|
||||
function h(s, color) {
|
||||
return '<span style="color:' + color + ';">' + s + '</span>';
|
||||
// that's pretty dumb approach to testing it
|
||||
}
|
||||
|
||||
var stylesColors = ['white', 'grey', 'black', 'blue', 'cyan', 'green', 'magenta', 'red', 'yellow'];
|
||||
var stylesAll = stylesColors.concat(['bold', 'italic', 'underline', 'inverse', 'rainbow']);
|
||||
|
||||
colors.mode = 'console';
|
||||
assert.equal(s.bold, '\033[1m' + s + '\033[22m');
|
||||
assert.equal(s.italic, '\033[3m' + s + '\033[23m');
|
||||
assert.equal(s.underline, '\033[4m' + s + '\033[24m');
|
||||
assert.equal(s.inverse, '\033[7m' + s + '\033[27m');
|
||||
assert.ok(s.rainbow);
|
||||
aE(s, 'white', 37);
|
||||
aE(s, 'grey', 90);
|
||||
aE(s, 'black', 30);
|
||||
aE(s, 'blue', 34);
|
||||
aE(s, 'cyan', 36);
|
||||
aE(s, 'green', 32);
|
||||
aE(s, 'magenta', 35);
|
||||
aE(s, 'red', 31);
|
||||
aE(s, 'yellow', 33);
|
||||
assert.equal(s, 'string');
|
||||
|
||||
colors.mode = 'browser';
|
||||
assert.equal(s.bold, '<b>' + s + '</b>');
|
||||
assert.equal(s.italic, '<i>' + s + '</i>');
|
||||
assert.equal(s.underline, '<u>' + s + '</u>');
|
||||
assert.equal(s.inverse, '<span style="background-color:black;color:white;">' + s + '</span>');
|
||||
assert.ok(s.rainbow);
|
||||
stylesColors.forEach(function (color) {
|
||||
assert.equal(s[color], h(s, color));
|
||||
assert.equal(colors[color](s), h(s, color));
|
||||
});
|
||||
|
||||
colors.mode = 'none';
|
||||
stylesAll.forEach(function (style) {
|
||||
assert.equal(s[style], s);
|
||||
assert.equal(colors[style](s), s);
|
||||
});
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
.*.swp
|
||||
node_modules/
|
||||
examples/deep-copy/
|
||||
examples/path/
|
||||
examples/filter-copy/
|
|
@ -0,0 +1,3 @@
|
|||
language: node_js
|
||||
node_js:
|
||||
- 0.6
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) Isaac Z. Schlueter ("Author")
|
||||
All rights reserved.
|
||||
|
||||
The BSD License
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. 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.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
|
|
@ -0,0 +1,76 @@
|
|||
Like FS streams, but with stat on them, and supporting directories and
|
||||
symbolic links, as well as normal files. Also, you can use this to set
|
||||
the stats on a file, even if you don't change its contents, or to create
|
||||
a symlink, etc.
|
||||
|
||||
So, for example, you can "write" a directory, and it'll call `mkdir`. You
|
||||
can specify a uid and gid, and it'll call `chown`. You can specify a
|
||||
`mtime` and `atime`, and it'll call `utimes`. You can call it a symlink
|
||||
and provide a `linkpath` and it'll call `symlink`.
|
||||
|
||||
Note that it won't automatically resolve symbolic links. So, if you
|
||||
call `fstream.Reader('/some/symlink')` then you'll get an object
|
||||
that stats and then ends immediately (since it has no data). To follow
|
||||
symbolic links, do this: `fstream.Reader({path:'/some/symlink', follow:
|
||||
true })`.
|
||||
|
||||
There are various checks to make sure that the bytes emitted are the
|
||||
same as the intended size, if the size is set.
|
||||
|
||||
## Examples
|
||||
|
||||
```javascript
|
||||
fstream
|
||||
.Writer({ path: "path/to/file"
|
||||
, mode: 0755
|
||||
, size: 6
|
||||
})
|
||||
.write("hello\n")
|
||||
.end()
|
||||
```
|
||||
|
||||
This will create the directories if they're missing, and then write
|
||||
`hello\n` into the file, chmod it to 0755, and assert that 6 bytes have
|
||||
been written when it's done.
|
||||
|
||||
```javascript
|
||||
fstream
|
||||
.Writer({ path: "path/to/file"
|
||||
, mode: 0755
|
||||
, size: 6
|
||||
, flags: "a"
|
||||
})
|
||||
.write("hello\n")
|
||||
.end()
|
||||
```
|
||||
|
||||
You can pass flags in, if you want to append to a file.
|
||||
|
||||
```javascript
|
||||
fstream
|
||||
.Writer({ path: "path/to/symlink"
|
||||
, linkpath: "./file"
|
||||
, SymbolicLink: true
|
||||
, mode: "0755" // octal strings supported
|
||||
})
|
||||
.end()
|
||||
```
|
||||
|
||||
If isSymbolicLink is a function, it'll be called, and if it returns
|
||||
true, then it'll treat it as a symlink. If it's not a function, then
|
||||
any truish value will make a symlink, or you can set `type:
|
||||
'SymbolicLink'`, which does the same thing.
|
||||
|
||||
Note that the linkpath is relative to the symbolic link location, not
|
||||
the parent dir or cwd.
|
||||
|
||||
```javascript
|
||||
fstream
|
||||
.Reader("path/to/dir")
|
||||
.pipe(fstream.Writer("path/to/other/dir"))
|
||||
```
|
||||
|
||||
This will do like `cp -Rp path/to/dir path/to/other/dir`. If the other
|
||||
dir exists and isn't a directory, then it'll emit an error. It'll also
|
||||
set the uid, gid, mode, etc. to be identical. In this way, it's more
|
||||
like `rsync -a` than simply a copy.
|
|
@ -0,0 +1,131 @@
|
|||
var fstream = require("../fstream.js")
|
||||
var path = require("path")
|
||||
|
||||
var r = fstream.Reader({ path: path.dirname(__dirname)
|
||||
, filter: function () {
|
||||
return !this.basename.match(/^\./) &&
|
||||
!this.basename.match(/^node_modules$/)
|
||||
!this.basename.match(/^deep-copy$/)
|
||||
!this.basename.match(/^filter-copy$/)
|
||||
}
|
||||
})
|
||||
|
||||
// this writer will only write directories
|
||||
var w = fstream.Writer({ path: path.resolve(__dirname, "filter-copy")
|
||||
, type: "Directory"
|
||||
, filter: function () {
|
||||
return this.type === "Directory"
|
||||
}
|
||||
})
|
||||
|
||||
var indent = ""
|
||||
var escape = {}
|
||||
|
||||
r.on("entry", appears)
|
||||
r.on("ready", function () {
|
||||
console.error("ready to begin!", r.path)
|
||||
})
|
||||
|
||||
function appears (entry) {
|
||||
console.error(indent + "a %s appears!", entry.type, entry.basename, typeof entry.basename)
|
||||
if (foggy) {
|
||||
console.error("FOGGY!")
|
||||
var p = entry
|
||||
do {
|
||||
console.error(p.depth, p.path, p._paused)
|
||||
} while (p = p.parent)
|
||||
|
||||
throw new Error("\033[mshould not have entries while foggy")
|
||||
}
|
||||
indent += "\t"
|
||||
entry.on("data", missile(entry))
|
||||
entry.on("end", runaway(entry))
|
||||
entry.on("entry", appears)
|
||||
}
|
||||
|
||||
var foggy
|
||||
function missile (entry) {
|
||||
if (entry.type === "Directory") {
|
||||
var ended = false
|
||||
entry.once("end", function () { ended = true })
|
||||
return function (c) {
|
||||
// throw in some pathological pause()/resume() behavior
|
||||
// just for extra fun.
|
||||
process.nextTick(function () {
|
||||
if (!foggy && !ended) { // && Math.random() < 0.3) {
|
||||
console.error(indent +"%s casts a spell", entry.basename)
|
||||
console.error("\na slowing fog comes over the battlefield...\n\033[32m")
|
||||
entry.pause()
|
||||
entry.once("resume", liftFog)
|
||||
foggy = setTimeout(liftFog, 1000)
|
||||
|
||||
function liftFog (who) {
|
||||
if (!foggy) return
|
||||
if (who) {
|
||||
console.error("%s breaks the spell!", who && who.path)
|
||||
} else {
|
||||
console.error("the spell expires!")
|
||||
}
|
||||
console.error("\033[mthe fog lifts!\n")
|
||||
clearTimeout(foggy)
|
||||
foggy = null
|
||||
if (entry._paused) entry.resume()
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return function (c) {
|
||||
var e = Math.random() < 0.5
|
||||
console.error(indent + "%s %s for %d damage!",
|
||||
entry.basename,
|
||||
e ? "is struck" : "fires a chunk",
|
||||
c.length)
|
||||
}
|
||||
}
|
||||
|
||||
function runaway (entry) { return function () {
|
||||
var e = Math.random() < 0.5
|
||||
console.error(indent + "%s %s",
|
||||
entry.basename,
|
||||
e ? "turns to flee" : "is vanquished!")
|
||||
indent = indent.slice(0, -1)
|
||||
}}
|
||||
|
||||
|
||||
w.on("entry", attacks)
|
||||
//w.on("ready", function () { attacks(w) })
|
||||
function attacks (entry) {
|
||||
console.error(indent + "%s %s!", entry.basename,
|
||||
entry.type === "Directory" ? "calls for backup" : "attacks")
|
||||
entry.on("entry", attacks)
|
||||
}
|
||||
|
||||
ended = false
|
||||
var i = 1
|
||||
r.on("end", function () {
|
||||
if (foggy) clearTimeout(foggy)
|
||||
console.error("\033[mIT'S OVER!!")
|
||||
console.error("A WINNAR IS YOU!")
|
||||
|
||||
console.log("ok " + (i ++) + " A WINNAR IS YOU")
|
||||
ended = true
|
||||
// now go through and verify that everything in there is a dir.
|
||||
var p = path.resolve(__dirname, "filter-copy")
|
||||
var checker = fstream.Reader({ path: p })
|
||||
checker.checker = true
|
||||
checker.on("child", function (e) {
|
||||
var ok = e.type === "Directory"
|
||||
console.log((ok ? "" : "not ") + "ok " + (i ++) +
|
||||
" should be a dir: " +
|
||||
e.path.substr(checker.path.length + 1))
|
||||
})
|
||||
})
|
||||
|
||||
process.on("exit", function () {
|
||||
console.log((ended ? "" : "not ") + "ok " + (i ++) + " ended")
|
||||
})
|
||||
|
||||
r.pipe(w)
|
|
@ -0,0 +1,115 @@
|
|||
var fstream = require("../fstream.js")
|
||||
var path = require("path")
|
||||
|
||||
var r = fstream.Reader({ path: path.dirname(__dirname)
|
||||
, filter: function () {
|
||||
return !this.basename.match(/^\./) &&
|
||||
!this.basename.match(/^node_modules$/)
|
||||
!this.basename.match(/^deep-copy$/)
|
||||
}
|
||||
})
|
||||
|
||||
var w = fstream.Writer({ path: path.resolve(__dirname, "deep-copy")
|
||||
, type: "Directory"
|
||||
})
|
||||
|
||||
var indent = ""
|
||||
var escape = {}
|
||||
|
||||
r.on("entry", appears)
|
||||
r.on("ready", function () {
|
||||
console.error("ready to begin!", r.path)
|
||||
})
|
||||
|
||||
function appears (entry) {
|
||||
console.error(indent + "a %s appears!", entry.type, entry.basename, typeof entry.basename, entry)
|
||||
if (foggy) {
|
||||
console.error("FOGGY!")
|
||||
var p = entry
|
||||
do {
|
||||
console.error(p.depth, p.path, p._paused)
|
||||
} while (p = p.parent)
|
||||
|
||||
throw new Error("\033[mshould not have entries while foggy")
|
||||
}
|
||||
indent += "\t"
|
||||
entry.on("data", missile(entry))
|
||||
entry.on("end", runaway(entry))
|
||||
entry.on("entry", appears)
|
||||
}
|
||||
|
||||
var foggy
|
||||
function missile (entry) {
|
||||
if (entry.type === "Directory") {
|
||||
var ended = false
|
||||
entry.once("end", function () { ended = true })
|
||||
return function (c) {
|
||||
// throw in some pathological pause()/resume() behavior
|
||||
// just for extra fun.
|
||||
process.nextTick(function () {
|
||||
if (!foggy && !ended) { // && Math.random() < 0.3) {
|
||||
console.error(indent +"%s casts a spell", entry.basename)
|
||||
console.error("\na slowing fog comes over the battlefield...\n\033[32m")
|
||||
entry.pause()
|
||||
entry.once("resume", liftFog)
|
||||
foggy = setTimeout(liftFog, 10)
|
||||
|
||||
function liftFog (who) {
|
||||
if (!foggy) return
|
||||
if (who) {
|
||||
console.error("%s breaks the spell!", who && who.path)
|
||||
} else {
|
||||
console.error("the spell expires!")
|
||||
}
|
||||
console.error("\033[mthe fog lifts!\n")
|
||||
clearTimeout(foggy)
|
||||
foggy = null
|
||||
if (entry._paused) entry.resume()
|
||||
}
|
||||
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return function (c) {
|
||||
var e = Math.random() < 0.5
|
||||
console.error(indent + "%s %s for %d damage!",
|
||||
entry.basename,
|
||||
e ? "is struck" : "fires a chunk",
|
||||
c.length)
|
||||
}
|
||||
}
|
||||
|
||||
function runaway (entry) { return function () {
|
||||
var e = Math.random() < 0.5
|
||||
console.error(indent + "%s %s",
|
||||
entry.basename,
|
||||
e ? "turns to flee" : "is vanquished!")
|
||||
indent = indent.slice(0, -1)
|
||||
}}
|
||||
|
||||
|
||||
w.on("entry", attacks)
|
||||
//w.on("ready", function () { attacks(w) })
|
||||
function attacks (entry) {
|
||||
console.error(indent + "%s %s!", entry.basename,
|
||||
entry.type === "Directory" ? "calls for backup" : "attacks")
|
||||
entry.on("entry", attacks)
|
||||
}
|
||||
|
||||
ended = false
|
||||
r.on("end", function () {
|
||||
if (foggy) clearTimeout(foggy)
|
||||
console.error("\033[mIT'S OVER!!")
|
||||
console.error("A WINNAR IS YOU!")
|
||||
|
||||
console.log("ok 1 A WINNAR IS YOU")
|
||||
ended = true
|
||||
})
|
||||
|
||||
process.on("exit", function () {
|
||||
console.log((ended ? "" : "not ") + "ok 2 ended")
|
||||
})
|
||||
|
||||
r.pipe(w)
|
|
@ -0,0 +1,54 @@
|
|||
var fstream = require("../fstream.js")
|
||||
var tap = require("tap")
|
||||
var fs = require("fs")
|
||||
var path = require("path")
|
||||
var children = -1
|
||||
var dir = path.dirname(__dirname)
|
||||
|
||||
var gotReady = false
|
||||
var ended = false
|
||||
|
||||
tap.test("reader test", function (t) {
|
||||
|
||||
var r = fstream.Reader({ path: dir
|
||||
, filter: function () {
|
||||
// return this.parent === r
|
||||
return this.parent === r || this === r
|
||||
}
|
||||
})
|
||||
|
||||
r.on("ready", function () {
|
||||
gotReady = true
|
||||
children = fs.readdirSync(dir).length
|
||||
console.error("Setting expected children to "+children)
|
||||
t.equal(r.type, "Directory", "should be a directory")
|
||||
})
|
||||
|
||||
r.on("entry", function (entry) {
|
||||
children --
|
||||
if (!gotReady) {
|
||||
t.fail("children before ready!")
|
||||
}
|
||||
t.equal(entry.dirname, r.path, "basename is parent dir")
|
||||
})
|
||||
|
||||
r.on("error", function (er) {
|
||||
t.fail(er)
|
||||
t.end()
|
||||
process.exit(1)
|
||||
})
|
||||
|
||||
r.on("end", function () {
|
||||
t.equal(children, 0, "should have seen all children")
|
||||
ended = true
|
||||
})
|
||||
|
||||
var closed = false
|
||||
r.on("close", function () {
|
||||
t.ok(ended, "saw end before close")
|
||||
t.notOk(closed, "close should only happen once")
|
||||
closed = true
|
||||
t.end()
|
||||
})
|
||||
|
||||
})
|
|
@ -0,0 +1,24 @@
|
|||
var fstream = require("../fstream.js")
|
||||
, closed = false
|
||||
|
||||
fstream
|
||||
.Writer({ path: "path/to/symlink"
|
||||
, linkpath: "./file"
|
||||
, isSymbolicLink: true
|
||||
, mode: "0755" // octal strings supported
|
||||
})
|
||||
.on("close", function () {
|
||||
closed = true
|
||||
var fs = require("fs")
|
||||
var s = fs.lstatSync("path/to/symlink")
|
||||
var isSym = s.isSymbolicLink()
|
||||
console.log((isSym?"":"not ") +"ok 1 should be symlink")
|
||||
var t = fs.readlinkSync("path/to/symlink")
|
||||
var isTarget = t === "./file"
|
||||
console.log((isTarget?"":"not ") +"ok 2 should link to ./file")
|
||||
})
|
||||
.end()
|
||||
|
||||
process.on("exit", function () {
|
||||
console.log((closed?"":"not ")+"ok 3 should be closed")
|
||||
})
|
|
@ -0,0 +1,31 @@
|
|||
exports.Abstract = require("./lib/abstract.js")
|
||||
exports.Reader = require("./lib/reader.js")
|
||||
exports.Writer = require("./lib/writer.js")
|
||||
|
||||
exports.File =
|
||||
{ Reader: require("./lib/file-reader.js")
|
||||
, Writer: require("./lib/file-writer.js") }
|
||||
|
||||
exports.Dir =
|
||||
{ Reader : require("./lib/dir-reader.js")
|
||||
, Writer : require("./lib/dir-writer.js") }
|
||||
|
||||
exports.Link =
|
||||
{ Reader : require("./lib/link-reader.js")
|
||||
, Writer : require("./lib/link-writer.js") }
|
||||
|
||||
exports.Proxy =
|
||||
{ Reader : require("./lib/proxy-reader.js")
|
||||
, Writer : require("./lib/proxy-writer.js") }
|
||||
|
||||
exports.Reader.Dir = exports.DirReader = exports.Dir.Reader
|
||||
exports.Reader.File = exports.FileReader = exports.File.Reader
|
||||
exports.Reader.Link = exports.LinkReader = exports.Link.Reader
|
||||
exports.Reader.Proxy = exports.ProxyReader = exports.Proxy.Reader
|
||||
|
||||
exports.Writer.Dir = exports.DirWriter = exports.Dir.Writer
|
||||
exports.Writer.File = exports.FileWriter = exports.File.Writer
|
||||
exports.Writer.Link = exports.LinkWriter = exports.Link.Writer
|
||||
exports.Writer.Proxy = exports.ProxyWriter = exports.Proxy.Writer
|
||||
|
||||
exports.collect = require("./lib/collect.js")
|
|
@ -0,0 +1,85 @@
|
|||
// the parent class for all fstreams.
|
||||
|
||||
module.exports = Abstract
|
||||
|
||||
var Stream = require("stream").Stream
|
||||
, inherits = require("inherits")
|
||||
|
||||
function Abstract () {
|
||||
Stream.call(this)
|
||||
}
|
||||
|
||||
inherits(Abstract, Stream)
|
||||
|
||||
Abstract.prototype.on = function (ev, fn) {
|
||||
if (ev === "ready" && this.ready) {
|
||||
process.nextTick(fn.bind(this))
|
||||
} else {
|
||||
Stream.prototype.on.call(this, ev, fn)
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
Abstract.prototype.abort = function () {
|
||||
this._aborted = true
|
||||
this.emit("abort")
|
||||
}
|
||||
|
||||
Abstract.prototype.destroy = function () {}
|
||||
|
||||
Abstract.prototype.warn = function (msg, code) {
|
||||
var me = this
|
||||
, er = decorate(msg, code, me)
|
||||
if (!me.listeners("warn")) {
|
||||
console.error("%s %s\n" +
|
||||
"path = %s\n" +
|
||||
"syscall = %s\n" +
|
||||
"fstream_type = %s\n" +
|
||||
"fstream_path = %s\n" +
|
||||
"fstream_unc_path = %s\n" +
|
||||
"fstream_class = %s\n" +
|
||||
"fstream_stack =\n%s\n",
|
||||
code || "UNKNOWN",
|
||||
er.stack,
|
||||
er.path,
|
||||
er.syscall,
|
||||
er.fstream_type,
|
||||
er.fstream_path,
|
||||
er.fstream_unc_path,
|
||||
er.fstream_class,
|
||||
er.fstream_stack.join("\n"))
|
||||
} else {
|
||||
me.emit("warn", er)
|
||||
}
|
||||
}
|
||||
|
||||
Abstract.prototype.info = function (msg, code) {
|
||||
this.emit("info", msg, code)
|
||||
}
|
||||
|
||||
Abstract.prototype.error = function (msg, code, th) {
|
||||
var er = decorate(msg, code, this)
|
||||
if (th) throw er
|
||||
else this.emit("error", er)
|
||||
}
|
||||
|
||||
function decorate (er, code, me) {
|
||||
if (!(er instanceof Error)) er = new Error(er)
|
||||
er.code = er.code || code
|
||||
er.path = er.path || me.path
|
||||
er.fstream_type = er.fstream_type || me.type
|
||||
er.fstream_path = er.fstream_path || me.path
|
||||
if (me._path !== me.path) {
|
||||
er.fstream_unc_path = er.fstream_unc_path || me._path
|
||||
}
|
||||
if (me.linkpath) {
|
||||
er.fstream_linkpath = er.fstream_linkpath || me.linkpath
|
||||
}
|
||||
er.fstream_class = er.fstream_class || me.constructor.name
|
||||
er.fstream_stack = er.fstream_stack ||
|
||||
new Error().stack.split(/\n/).slice(3).map(function (s) {
|
||||
return s.replace(/^ at /, "")
|
||||
})
|
||||
|
||||
return er
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
module.exports = collect
|
||||
|
||||
function collect (stream) {
|
||||
if (stream._collected) return
|
||||
|
||||
stream._collected = true
|
||||
stream.pause()
|
||||
|
||||
stream.on("data", save)
|
||||
stream.on("end", save)
|
||||
var buf = []
|
||||
function save (b) {
|
||||
if (typeof b === "string") b = new Buffer(b)
|
||||
if (Buffer.isBuffer(b) && !b.length) return
|
||||
buf.push(b)
|
||||
}
|
||||
|
||||
stream.on("entry", saveEntry)
|
||||
var entryBuffer = []
|
||||
function saveEntry (e) {
|
||||
collect(e)
|
||||
entryBuffer.push(e)
|
||||
}
|
||||
|
||||
stream.on("proxy", proxyPause)
|
||||
function proxyPause (p) {
|
||||
p.pause()
|
||||
}
|
||||
|
||||
|
||||
// replace the pipe method with a new version that will
|
||||
// unlock the buffered stuff. if you just call .pipe()
|
||||
// without a destination, then it'll re-play the events.
|
||||
stream.pipe = (function (orig) { return function (dest) {
|
||||
// console.error(" === open the pipes", dest && dest.path)
|
||||
|
||||
// let the entries flow through one at a time.
|
||||
// Once they're all done, then we can resume completely.
|
||||
var e = 0
|
||||
;(function unblockEntry () {
|
||||
var entry = entryBuffer[e++]
|
||||
// console.error(" ==== unblock entry", entry && entry.path)
|
||||
if (!entry) return resume()
|
||||
entry.on("end", unblockEntry)
|
||||
if (dest) dest.add(entry)
|
||||
else stream.emit("entry", entry)
|
||||
})()
|
||||
|
||||
function resume () {
|
||||
stream.removeListener("entry", saveEntry)
|
||||
stream.removeListener("data", save)
|
||||
stream.removeListener("end", save)
|
||||
|
||||
stream.pipe = orig
|
||||
if (dest) stream.pipe(dest)
|
||||
|
||||
buf.forEach(function (b) {
|
||||
if (b) stream.emit("data", b)
|
||||
else stream.emit("end")
|
||||
})
|
||||
|
||||
stream.resume()
|
||||
}
|
||||
|
||||
return dest
|
||||
}})(stream.pipe)
|
||||
}
|
|
@ -0,0 +1,251 @@
|
|||
// A thing that emits "entry" events with Reader objects
|
||||
// Pausing it causes it to stop emitting entry events, and also
|
||||
// pauses the current entry if there is one.
|
||||
|
||||
module.exports = DirReader
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, fstream = require("../fstream.js")
|
||||
, Reader = fstream.Reader
|
||||
, inherits = require("inherits")
|
||||
, mkdir = require("mkdirp")
|
||||
, path = require("path")
|
||||
, Reader = require("./reader.js")
|
||||
, assert = require("assert").ok
|
||||
|
||||
inherits(DirReader, Reader)
|
||||
|
||||
function DirReader (props) {
|
||||
var me = this
|
||||
if (!(me instanceof DirReader)) throw new Error(
|
||||
"DirReader must be called as constructor.")
|
||||
|
||||
// should already be established as a Directory type
|
||||
if (props.type !== "Directory" || !props.Directory) {
|
||||
throw new Error("Non-directory type "+ props.type)
|
||||
}
|
||||
|
||||
me.entries = null
|
||||
me._index = -1
|
||||
me._paused = false
|
||||
me._length = -1
|
||||
|
||||
if (props.sort) {
|
||||
this.sort = props.sort
|
||||
}
|
||||
|
||||
Reader.call(this, props)
|
||||
}
|
||||
|
||||
DirReader.prototype._getEntries = function () {
|
||||
var me = this
|
||||
|
||||
// race condition. might pause() before calling _getEntries,
|
||||
// and then resume, and try to get them a second time.
|
||||
if (me._gotEntries) return
|
||||
me._gotEntries = true
|
||||
|
||||
fs.readdir(me._path, function (er, entries) {
|
||||
if (er) return me.error(er)
|
||||
|
||||
me.entries = entries
|
||||
|
||||
me.emit("entries", entries)
|
||||
if (me._paused) me.once("resume", processEntries)
|
||||
else processEntries()
|
||||
|
||||
function processEntries () {
|
||||
me._length = me.entries.length
|
||||
if (typeof me.sort === "function") {
|
||||
me.entries = me.entries.sort(me.sort.bind(me))
|
||||
}
|
||||
me._read()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// start walking the dir, and emit an "entry" event for each one.
|
||||
DirReader.prototype._read = function () {
|
||||
var me = this
|
||||
|
||||
if (!me.entries) return me._getEntries()
|
||||
|
||||
if (me._paused || me._currentEntry || me._aborted) {
|
||||
// console.error("DR paused=%j, current=%j, aborted=%j", me._paused, !!me._currentEntry, me._aborted)
|
||||
return
|
||||
}
|
||||
|
||||
me._index ++
|
||||
if (me._index >= me.entries.length) {
|
||||
if (!me._ended) {
|
||||
me._ended = true
|
||||
me.emit("end")
|
||||
me.emit("close")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// ok, handle this one, then.
|
||||
|
||||
// save creating a proxy, by stat'ing the thing now.
|
||||
var p = path.resolve(me._path, me.entries[me._index])
|
||||
assert(p !== me._path)
|
||||
assert(me.entries[me._index])
|
||||
|
||||
// set this to prevent trying to _read() again in the stat time.
|
||||
me._currentEntry = p
|
||||
fs[ me.props.follow ? "stat" : "lstat" ](p, function (er, stat) {
|
||||
if (er) return me.error(er)
|
||||
|
||||
var who = me._proxy || me
|
||||
|
||||
stat.path = p
|
||||
stat.basename = path.basename(p)
|
||||
stat.dirname = path.dirname(p)
|
||||
var childProps = me.getChildProps.call(who, stat)
|
||||
childProps.path = p
|
||||
childProps.basename = path.basename(p)
|
||||
childProps.dirname = path.dirname(p)
|
||||
|
||||
var entry = Reader(childProps, stat)
|
||||
|
||||
// console.error("DR Entry", p, stat.size)
|
||||
|
||||
me._currentEntry = entry
|
||||
|
||||
// "entry" events are for direct entries in a specific dir.
|
||||
// "child" events are for any and all children at all levels.
|
||||
// This nomenclature is not completely final.
|
||||
|
||||
entry.on("pause", function (who) {
|
||||
if (!me._paused && !entry._disowned) {
|
||||
me.pause(who)
|
||||
}
|
||||
})
|
||||
|
||||
entry.on("resume", function (who) {
|
||||
if (me._paused && !entry._disowned) {
|
||||
me.resume(who)
|
||||
}
|
||||
})
|
||||
|
||||
entry.on("stat", function (props) {
|
||||
me.emit("_entryStat", entry, props)
|
||||
if (entry._aborted) return
|
||||
if (entry._paused) entry.once("resume", function () {
|
||||
me.emit("entryStat", entry, props)
|
||||
})
|
||||
else me.emit("entryStat", entry, props)
|
||||
})
|
||||
|
||||
entry.on("ready", function EMITCHILD () {
|
||||
// console.error("DR emit child", entry._path)
|
||||
if (me._paused) {
|
||||
// console.error(" DR emit child - try again later")
|
||||
// pause the child, and emit the "entry" event once we drain.
|
||||
// console.error("DR pausing child entry")
|
||||
entry.pause(me)
|
||||
return me.once("resume", EMITCHILD)
|
||||
}
|
||||
|
||||
// skip over sockets. they can't be piped around properly,
|
||||
// so there's really no sense even acknowledging them.
|
||||
// if someone really wants to see them, they can listen to
|
||||
// the "socket" events.
|
||||
if (entry.type === "Socket") {
|
||||
me.emit("socket", entry)
|
||||
} else {
|
||||
me.emitEntry(entry)
|
||||
}
|
||||
})
|
||||
|
||||
var ended = false
|
||||
entry.on("close", onend)
|
||||
entry.on("disown", onend)
|
||||
function onend () {
|
||||
if (ended) return
|
||||
ended = true
|
||||
me.emit("childEnd", entry)
|
||||
me.emit("entryEnd", entry)
|
||||
me._currentEntry = null
|
||||
if (!me._paused) {
|
||||
me._read()
|
||||
}
|
||||
}
|
||||
|
||||
// XXX Remove this. Works in node as of 0.6.2 or so.
|
||||
// Long filenames should not break stuff.
|
||||
entry.on("error", function (er) {
|
||||
if (entry._swallowErrors) {
|
||||
me.warn(er)
|
||||
entry.emit("end")
|
||||
entry.emit("close")
|
||||
} else {
|
||||
me.emit("error", er)
|
||||
}
|
||||
})
|
||||
|
||||
// proxy up some events.
|
||||
; [ "child"
|
||||
, "childEnd"
|
||||
, "warn"
|
||||
].forEach(function (ev) {
|
||||
entry.on(ev, me.emit.bind(me, ev))
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
DirReader.prototype.disown = function (entry) {
|
||||
entry.emit("beforeDisown")
|
||||
entry._disowned = true
|
||||
entry.parent = entry.root = null
|
||||
if (entry === this._currentEntry) {
|
||||
this._currentEntry = null
|
||||
}
|
||||
entry.emit("disown")
|
||||
}
|
||||
|
||||
DirReader.prototype.getChildProps = function (stat) {
|
||||
return { depth: this.depth + 1
|
||||
, root: this.root || this
|
||||
, parent: this
|
||||
, follow: this.follow
|
||||
, filter: this.filter
|
||||
, sort: this.props.sort
|
||||
, hardlinks: this.props.hardlinks
|
||||
}
|
||||
}
|
||||
|
||||
DirReader.prototype.pause = function (who) {
|
||||
var me = this
|
||||
if (me._paused) return
|
||||
who = who || me
|
||||
me._paused = true
|
||||
if (me._currentEntry && me._currentEntry.pause) {
|
||||
me._currentEntry.pause(who)
|
||||
}
|
||||
me.emit("pause", who)
|
||||
}
|
||||
|
||||
DirReader.prototype.resume = function (who) {
|
||||
var me = this
|
||||
if (!me._paused) return
|
||||
who = who || me
|
||||
|
||||
me._paused = false
|
||||
// console.error("DR Emit Resume", me._path)
|
||||
me.emit("resume", who)
|
||||
if (me._paused) {
|
||||
// console.error("DR Re-paused", me._path)
|
||||
return
|
||||
}
|
||||
|
||||
if (me._currentEntry) {
|
||||
if (me._currentEntry.resume) me._currentEntry.resume(who)
|
||||
} else me._read()
|
||||
}
|
||||
|
||||
DirReader.prototype.emitEntry = function (entry) {
|
||||
this.emit("entry", entry)
|
||||
this.emit("child", entry)
|
||||
}
|
|
@ -0,0 +1,171 @@
|
|||
// It is expected that, when .add() returns false, the consumer
|
||||
// of the DirWriter will pause until a "drain" event occurs. Note
|
||||
// that this is *almost always going to be the case*, unless the
|
||||
// thing being written is some sort of unsupported type, and thus
|
||||
// skipped over.
|
||||
|
||||
module.exports = DirWriter
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, fstream = require("../fstream.js")
|
||||
, Writer = require("./writer.js")
|
||||
, inherits = require("inherits")
|
||||
, mkdir = require("mkdirp")
|
||||
, path = require("path")
|
||||
, collect = require("./collect.js")
|
||||
|
||||
inherits(DirWriter, Writer)
|
||||
|
||||
function DirWriter (props) {
|
||||
var me = this
|
||||
if (!(me instanceof DirWriter)) me.error(
|
||||
"DirWriter must be called as constructor.", null, true)
|
||||
|
||||
// should already be established as a Directory type
|
||||
if (props.type !== "Directory" || !props.Directory) {
|
||||
me.error("Non-directory type "+ props.type + " " +
|
||||
JSON.stringify(props), null, true)
|
||||
}
|
||||
|
||||
Writer.call(this, props)
|
||||
}
|
||||
|
||||
DirWriter.prototype._create = function () {
|
||||
var me = this
|
||||
mkdir(me._path, Writer.dirmode, function (er) {
|
||||
if (er) return me.error(er)
|
||||
// ready to start getting entries!
|
||||
me.ready = true
|
||||
me.emit("ready")
|
||||
me._process()
|
||||
})
|
||||
}
|
||||
|
||||
// a DirWriter has an add(entry) method, but its .write() doesn't
|
||||
// do anything. Why a no-op rather than a throw? Because this
|
||||
// leaves open the door for writing directory metadata for
|
||||
// gnu/solaris style dumpdirs.
|
||||
DirWriter.prototype.write = function () {
|
||||
return true
|
||||
}
|
||||
|
||||
DirWriter.prototype.end = function () {
|
||||
this._ended = true
|
||||
this._process()
|
||||
}
|
||||
|
||||
DirWriter.prototype.add = function (entry) {
|
||||
var me = this
|
||||
|
||||
// console.error("\tadd", entry._path, "->", me._path)
|
||||
collect(entry)
|
||||
if (!me.ready || me._currentEntry) {
|
||||
me._buffer.push(entry)
|
||||
return false
|
||||
}
|
||||
|
||||
// create a new writer, and pipe the incoming entry into it.
|
||||
if (me._ended) {
|
||||
return me.error("add after end")
|
||||
}
|
||||
|
||||
me._buffer.push(entry)
|
||||
me._process()
|
||||
|
||||
return 0 === this._buffer.length
|
||||
}
|
||||
|
||||
DirWriter.prototype._process = function () {
|
||||
var me = this
|
||||
|
||||
// console.error("DW Process p=%j", me._processing, me.basename)
|
||||
|
||||
if (me._processing) return
|
||||
|
||||
var entry = me._buffer.shift()
|
||||
if (!entry) {
|
||||
// console.error("DW Drain")
|
||||
me.emit("drain")
|
||||
if (me._ended) me._finish()
|
||||
return
|
||||
}
|
||||
|
||||
me._processing = true
|
||||
// console.error("DW Entry", entry._path)
|
||||
|
||||
me.emit("entry", entry)
|
||||
|
||||
// ok, add this entry
|
||||
//
|
||||
// don't allow recursive copying
|
||||
var p = entry
|
||||
do {
|
||||
var pp = p._path || p.path
|
||||
if (pp === me.root._path || pp === me._path ||
|
||||
(pp && pp.indexOf(me._path) === 0)) {
|
||||
// console.error("DW Exit (recursive)", entry.basename, me._path)
|
||||
me._processing = false
|
||||
if (entry._collected) entry.pipe()
|
||||
return me._process()
|
||||
}
|
||||
} while (p = p.parent)
|
||||
|
||||
// console.error("DW not recursive")
|
||||
|
||||
// chop off the entry's root dir, replace with ours
|
||||
var props = { parent: me
|
||||
, root: me.root || me
|
||||
, type: entry.type
|
||||
, depth: me.depth + 1 }
|
||||
|
||||
var p = entry._path || entry.path || entry.props.path
|
||||
if (entry.parent) {
|
||||
p = p.substr(entry.parent._path.length + 1)
|
||||
}
|
||||
// get rid of any ../../ shenanigans
|
||||
props.path = path.join(me.path, path.join("/", p))
|
||||
|
||||
// if i have a filter, the child should inherit it.
|
||||
props.filter = me.filter
|
||||
|
||||
// all the rest of the stuff, copy over from the source.
|
||||
Object.keys(entry.props).forEach(function (k) {
|
||||
if (!props.hasOwnProperty(k)) {
|
||||
props[k] = entry.props[k]
|
||||
}
|
||||
})
|
||||
|
||||
// not sure at this point what kind of writer this is.
|
||||
var child = me._currentChild = new Writer(props)
|
||||
child.on("ready", function () {
|
||||
// console.error("DW Child Ready", child.type, child._path)
|
||||
// console.error(" resuming", entry._path)
|
||||
entry.pipe(child)
|
||||
entry.resume()
|
||||
})
|
||||
|
||||
// XXX Make this work in node.
|
||||
// Long filenames should not break stuff.
|
||||
child.on("error", function (er) {
|
||||
if (child._swallowErrors) {
|
||||
me.warn(er)
|
||||
child.emit("end")
|
||||
child.emit("close")
|
||||
} else {
|
||||
me.emit("error", er)
|
||||
}
|
||||
})
|
||||
|
||||
// we fire _end internally *after* end, so that we don't move on
|
||||
// until any "end" listeners have had their chance to do stuff.
|
||||
child.on("close", onend)
|
||||
var ended = false
|
||||
function onend () {
|
||||
if (ended) return
|
||||
ended = true
|
||||
// console.error("* DW Child end", child.basename)
|
||||
me._currentChild = null
|
||||
me._processing = false
|
||||
me._process()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
// Basically just a wrapper around an fs.ReadStream
|
||||
|
||||
module.exports = FileReader
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, fstream = require("../fstream.js")
|
||||
, Reader = fstream.Reader
|
||||
, inherits = require("inherits")
|
||||
, mkdir = require("mkdirp")
|
||||
, Reader = require("./reader.js")
|
||||
, EOF = {EOF: true}
|
||||
, CLOSE = {CLOSE: true}
|
||||
|
||||
inherits(FileReader, Reader)
|
||||
|
||||
function FileReader (props) {
|
||||
// console.error(" FR create", props.path, props.size, new Error().stack)
|
||||
var me = this
|
||||
if (!(me instanceof FileReader)) throw new Error(
|
||||
"FileReader must be called as constructor.")
|
||||
|
||||
// should already be established as a File type
|
||||
// XXX Todo: preserve hardlinks by tracking dev+inode+nlink,
|
||||
// with a HardLinkReader class.
|
||||
if (!((props.type === "Link" && props.Link) ||
|
||||
(props.type === "File" && props.File))) {
|
||||
throw new Error("Non-file type "+ props.type)
|
||||
}
|
||||
|
||||
me._buffer = []
|
||||
me._bytesEmitted = 0
|
||||
Reader.call(me, props)
|
||||
}
|
||||
|
||||
FileReader.prototype._getStream = function () {
|
||||
var me = this
|
||||
, stream = me._stream = fs.createReadStream(me._path, me.props)
|
||||
|
||||
if (me.props.blksize) {
|
||||
stream.bufferSize = me.props.blksize
|
||||
}
|
||||
|
||||
stream.on("open", me.emit.bind(me, "open"))
|
||||
|
||||
stream.on("data", function (c) {
|
||||
// console.error("\t\t%d %s", c.length, me.basename)
|
||||
me._bytesEmitted += c.length
|
||||
// no point saving empty chunks
|
||||
if (!c.length) return
|
||||
else if (me._paused || me._buffer.length) {
|
||||
me._buffer.push(c)
|
||||
me._read()
|
||||
} else me.emit("data", c)
|
||||
})
|
||||
|
||||
stream.on("end", function () {
|
||||
if (me._paused || me._buffer.length) {
|
||||
// console.error("FR Buffering End", me._path)
|
||||
me._buffer.push(EOF)
|
||||
me._read()
|
||||
} else {
|
||||
me.emit("end")
|
||||
}
|
||||
|
||||
if (me._bytesEmitted !== me.props.size) {
|
||||
me.error("Didn't get expected byte count\n"+
|
||||
"expect: "+me.props.size + "\n" +
|
||||
"actual: "+me._bytesEmitted)
|
||||
}
|
||||
})
|
||||
|
||||
stream.on("close", function () {
|
||||
if (me._paused || me._buffer.length) {
|
||||
// console.error("FR Buffering Close", me._path)
|
||||
me._buffer.push(CLOSE)
|
||||
me._read()
|
||||
} else {
|
||||
// console.error("FR close 1", me._path)
|
||||
me.emit("close")
|
||||
}
|
||||
})
|
||||
|
||||
me._read()
|
||||
}
|
||||
|
||||
FileReader.prototype._read = function () {
|
||||
var me = this
|
||||
// console.error("FR _read", me._path)
|
||||
if (me._paused) {
|
||||
// console.error("FR _read paused", me._path)
|
||||
return
|
||||
}
|
||||
|
||||
if (!me._stream) {
|
||||
// console.error("FR _getStream calling", me._path)
|
||||
return me._getStream()
|
||||
}
|
||||
|
||||
// clear out the buffer, if there is one.
|
||||
if (me._buffer.length) {
|
||||
// console.error("FR _read has buffer", me._buffer.length, me._path)
|
||||
var buf = me._buffer
|
||||
for (var i = 0, l = buf.length; i < l; i ++) {
|
||||
var c = buf[i]
|
||||
if (c === EOF) {
|
||||
// console.error("FR Read emitting buffered end", me._path)
|
||||
me.emit("end")
|
||||
} else if (c === CLOSE) {
|
||||
// console.error("FR Read emitting buffered close", me._path)
|
||||
me.emit("close")
|
||||
} else {
|
||||
// console.error("FR Read emitting buffered data", me._path)
|
||||
me.emit("data", c)
|
||||
}
|
||||
|
||||
if (me._paused) {
|
||||
// console.error("FR Read Re-pausing at "+i, me._path)
|
||||
me._buffer = buf.slice(i)
|
||||
return
|
||||
}
|
||||
}
|
||||
me._buffer.length = 0
|
||||
}
|
||||
// console.error("FR _read done")
|
||||
// that's about all there is to it.
|
||||
}
|
||||
|
||||
FileReader.prototype.pause = function (who) {
|
||||
var me = this
|
||||
// console.error("FR Pause", me._path)
|
||||
if (me._paused) return
|
||||
who = who || me
|
||||
me._paused = true
|
||||
if (me._stream) me._stream.pause()
|
||||
me.emit("pause", who)
|
||||
}
|
||||
|
||||
FileReader.prototype.resume = function (who) {
|
||||
var me = this
|
||||
// console.error("FR Resume", me._path)
|
||||
if (!me._paused) return
|
||||
who = who || me
|
||||
me.emit("resume", who)
|
||||
me._paused = false
|
||||
if (me._stream) me._stream.resume()
|
||||
me._read()
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
module.exports = FileWriter
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, mkdir = require("mkdirp")
|
||||
, Writer = require("./writer.js")
|
||||
, inherits = require("inherits")
|
||||
, EOF = {}
|
||||
|
||||
inherits(FileWriter, Writer)
|
||||
|
||||
function FileWriter (props) {
|
||||
var me = this
|
||||
if (!(me instanceof FileWriter)) throw new Error(
|
||||
"FileWriter must be called as constructor.")
|
||||
|
||||
// should already be established as a File type
|
||||
if (props.type !== "File" || !props.File) {
|
||||
throw new Error("Non-file type "+ props.type)
|
||||
}
|
||||
|
||||
me._buffer = []
|
||||
me._bytesWritten = 0
|
||||
|
||||
Writer.call(this, props)
|
||||
}
|
||||
|
||||
FileWriter.prototype._create = function () {
|
||||
var me = this
|
||||
if (me._stream) return
|
||||
|
||||
var so = {}
|
||||
if (me.props.flags) so.flags = me.props.flags
|
||||
so.mode = Writer.filemode
|
||||
if (me._old && me._old.blksize) so.bufferSize = me._old.blksize
|
||||
|
||||
me._stream = fs.createWriteStream(me._path, so)
|
||||
|
||||
me._stream.on("open", function (fd) {
|
||||
// console.error("FW open", me._buffer, me._path)
|
||||
me.ready = true
|
||||
me._buffer.forEach(function (c) {
|
||||
if (c === EOF) me._stream.end()
|
||||
else me._stream.write(c)
|
||||
})
|
||||
me.emit("ready")
|
||||
// give this a kick just in case it needs it.
|
||||
me.emit("drain")
|
||||
})
|
||||
|
||||
me._stream.on("drain", function () { me.emit("drain") })
|
||||
|
||||
me._stream.on("close", function () {
|
||||
// console.error("\n\nFW Stream Close", me._path, me.size)
|
||||
me._finish()
|
||||
})
|
||||
}
|
||||
|
||||
FileWriter.prototype.write = function (c) {
|
||||
var me = this
|
||||
|
||||
me._bytesWritten += c.length
|
||||
|
||||
if (!me.ready) {
|
||||
if (!Buffer.isBuffer(c) && typeof c !== 'string')
|
||||
throw new Error('invalid write data')
|
||||
me._buffer.push(c)
|
||||
return false
|
||||
}
|
||||
|
||||
var ret = me._stream.write(c)
|
||||
// console.error("\t-- fw wrote, _stream says", ret, me._stream._queue.length)
|
||||
|
||||
// allow 2 buffered writes, because otherwise there's just too
|
||||
// much stop and go bs.
|
||||
if (ret === false && me._stream._queue) {
|
||||
return me._stream._queue.length <= 2;
|
||||
} else {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
FileWriter.prototype.end = function (c) {
|
||||
var me = this
|
||||
|
||||
if (c) me.write(c)
|
||||
|
||||
if (!me.ready) {
|
||||
me._buffer.push(EOF)
|
||||
return false
|
||||
}
|
||||
|
||||
return me._stream.end()
|
||||
}
|
||||
|
||||
FileWriter.prototype._finish = function () {
|
||||
var me = this
|
||||
if (typeof me.size === "number" && me._bytesWritten != me.size) {
|
||||
me.error(
|
||||
"Did not get expected byte count.\n" +
|
||||
"expect: " + me.size + "\n" +
|
||||
"actual: " + me._bytesWritten)
|
||||
}
|
||||
Writer.prototype._finish.call(me)
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
module.exports = getType
|
||||
|
||||
function getType (st) {
|
||||
var types =
|
||||
[ "Directory"
|
||||
, "File"
|
||||
, "SymbolicLink"
|
||||
, "Link" // special for hardlinks from tarballs
|
||||
, "BlockDevice"
|
||||
, "CharacterDevice"
|
||||
, "FIFO"
|
||||
, "Socket" ]
|
||||
, type
|
||||
|
||||
if (st.type && -1 !== types.indexOf(st.type)) {
|
||||
st[st.type] = true
|
||||
return st.type
|
||||
}
|
||||
|
||||
for (var i = 0, l = types.length; i < l; i ++) {
|
||||
type = types[i]
|
||||
var is = st[type] || st["is" + type]
|
||||
if (typeof is === "function") is = is.call(st)
|
||||
if (is) {
|
||||
st[type] = true
|
||||
st.type = type
|
||||
return type
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
// Basically just a wrapper around an fs.readlink
|
||||
//
|
||||
// XXX: Enhance this to support the Link type, by keeping
|
||||
// a lookup table of {<dev+inode>:<path>}, so that hardlinks
|
||||
// can be preserved in tarballs.
|
||||
|
||||
module.exports = LinkReader
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, fstream = require("../fstream.js")
|
||||
, inherits = require("inherits")
|
||||
, mkdir = require("mkdirp")
|
||||
, Reader = require("./reader.js")
|
||||
|
||||
inherits(LinkReader, Reader)
|
||||
|
||||
function LinkReader (props) {
|
||||
var me = this
|
||||
if (!(me instanceof LinkReader)) throw new Error(
|
||||
"LinkReader must be called as constructor.")
|
||||
|
||||
if (!((props.type === "Link" && props.Link) ||
|
||||
(props.type === "SymbolicLink" && props.SymbolicLink))) {
|
||||
throw new Error("Non-link type "+ props.type)
|
||||
}
|
||||
|
||||
Reader.call(me, props)
|
||||
}
|
||||
|
||||
// When piping a LinkReader into a LinkWriter, we have to
|
||||
// already have the linkpath property set, so that has to
|
||||
// happen *before* the "ready" event, which means we need to
|
||||
// override the _stat method.
|
||||
LinkReader.prototype._stat = function (currentStat) {
|
||||
var me = this
|
||||
fs.readlink(me._path, function (er, linkpath) {
|
||||
if (er) return me.error(er)
|
||||
me.linkpath = me.props.linkpath = linkpath
|
||||
me.emit("linkpath", linkpath)
|
||||
Reader.prototype._stat.call(me, currentStat)
|
||||
})
|
||||
}
|
||||
|
||||
LinkReader.prototype._read = function () {
|
||||
var me = this
|
||||
if (me._paused) return
|
||||
// basically just a no-op, since we got all the info we need
|
||||
// from the _stat method
|
||||
if (!me._ended) {
|
||||
me.emit("end")
|
||||
me.emit("close")
|
||||
me._ended = true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,95 @@
|
|||
|
||||
module.exports = LinkWriter
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, Writer = require("./writer.js")
|
||||
, inherits = require("inherits")
|
||||
, path = require("path")
|
||||
, rimraf = require("rimraf")
|
||||
|
||||
inherits(LinkWriter, Writer)
|
||||
|
||||
function LinkWriter (props) {
|
||||
var me = this
|
||||
if (!(me instanceof LinkWriter)) throw new Error(
|
||||
"LinkWriter must be called as constructor.")
|
||||
|
||||
// should already be established as a Link type
|
||||
if (!((props.type === "Link" && props.Link) ||
|
||||
(props.type === "SymbolicLink" && props.SymbolicLink))) {
|
||||
throw new Error("Non-link type "+ props.type)
|
||||
}
|
||||
|
||||
if (props.linkpath === "") props.linkpath = "."
|
||||
if (!props.linkpath) {
|
||||
me.error("Need linkpath property to create " + props.type)
|
||||
}
|
||||
|
||||
Writer.call(this, props)
|
||||
}
|
||||
|
||||
LinkWriter.prototype._create = function () {
|
||||
// console.error(" LW _create")
|
||||
var me = this
|
||||
, hard = me.type === "Link" || process.platform === "win32"
|
||||
, link = hard ? "link" : "symlink"
|
||||
, lp = hard ? path.resolve(me.dirname, me.linkpath) : me.linkpath
|
||||
|
||||
// can only change the link path by clobbering
|
||||
// For hard links, let's just assume that's always the case, since
|
||||
// there's no good way to read them if we don't already know.
|
||||
if (hard) return clobber(me, lp, link)
|
||||
|
||||
fs.readlink(me._path, function (er, p) {
|
||||
// only skip creation if it's exactly the same link
|
||||
if (p && p === lp) return finish(me)
|
||||
clobber(me, lp, link)
|
||||
})
|
||||
}
|
||||
|
||||
function clobber (me, lp, link) {
|
||||
rimraf(me._path, function (er) {
|
||||
if (er) return me.error(er)
|
||||
create(me, lp, link)
|
||||
})
|
||||
}
|
||||
|
||||
function create (me, lp, link) {
|
||||
fs[link](lp, me._path, function (er) {
|
||||
// if this is a hard link, and we're in the process of writing out a
|
||||
// directory, it's very possible that the thing we're linking to
|
||||
// doesn't exist yet (especially if it was intended as a symlink),
|
||||
// so swallow ENOENT errors here and just soldier in.
|
||||
// Additionally, an EPERM or EACCES can happen on win32 if it's trying
|
||||
// to make a link to a directory. Again, just skip it.
|
||||
// A better solution would be to have fs.symlink be supported on
|
||||
// windows in some nice fashion.
|
||||
if (er) {
|
||||
if ((er.code === "ENOENT" ||
|
||||
er.code === "EACCES" ||
|
||||
er.code === "EPERM" ) && process.platform === "win32") {
|
||||
me.ready = true
|
||||
me.emit("ready")
|
||||
me.emit("end")
|
||||
me.emit("close")
|
||||
me.end = me._finish = function () {}
|
||||
} else return me.error(er)
|
||||
}
|
||||
finish(me)
|
||||
})
|
||||
}
|
||||
|
||||
function finish (me) {
|
||||
me.ready = true
|
||||
me.emit("ready")
|
||||
if (me._ended && !me._finished) me._finish()
|
||||
}
|
||||
|
||||
LinkWriter.prototype.end = function () {
|
||||
// console.error("LW finish in end")
|
||||
this._ended = true
|
||||
if (this.ready) {
|
||||
this._finished = true
|
||||
this._finish()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,93 @@
|
|||
// A reader for when we don't yet know what kind of thing
|
||||
// the thing is.
|
||||
|
||||
module.exports = ProxyReader
|
||||
|
||||
var Reader = require("./reader.js")
|
||||
, getType = require("./get-type.js")
|
||||
, inherits = require("inherits")
|
||||
, fs = require("graceful-fs")
|
||||
|
||||
inherits(ProxyReader, Reader)
|
||||
|
||||
function ProxyReader (props) {
|
||||
var me = this
|
||||
if (!(me instanceof ProxyReader)) throw new Error(
|
||||
"ProxyReader must be called as constructor.")
|
||||
|
||||
me.props = props
|
||||
me._buffer = []
|
||||
me.ready = false
|
||||
|
||||
Reader.call(me, props)
|
||||
}
|
||||
|
||||
ProxyReader.prototype._stat = function () {
|
||||
var me = this
|
||||
, props = me.props
|
||||
// stat the thing to see what the proxy should be.
|
||||
, stat = props.follow ? "stat" : "lstat"
|
||||
|
||||
fs[stat](props.path, function (er, current) {
|
||||
var type
|
||||
if (er || !current) {
|
||||
type = "File"
|
||||
} else {
|
||||
type = getType(current)
|
||||
}
|
||||
|
||||
props[type] = true
|
||||
props.type = me.type = type
|
||||
|
||||
me._old = current
|
||||
me._addProxy(Reader(props, current))
|
||||
})
|
||||
}
|
||||
|
||||
ProxyReader.prototype._addProxy = function (proxy) {
|
||||
var me = this
|
||||
if (me._proxyTarget) {
|
||||
return me.error("proxy already set")
|
||||
}
|
||||
|
||||
me._proxyTarget = proxy
|
||||
proxy._proxy = me
|
||||
|
||||
; [ "error"
|
||||
, "data"
|
||||
, "end"
|
||||
, "close"
|
||||
, "linkpath"
|
||||
, "entry"
|
||||
, "entryEnd"
|
||||
, "child"
|
||||
, "childEnd"
|
||||
, "warn"
|
||||
, "stat"
|
||||
].forEach(function (ev) {
|
||||
// console.error("~~ proxy event", ev, me.path)
|
||||
proxy.on(ev, me.emit.bind(me, ev))
|
||||
})
|
||||
|
||||
me.emit("proxy", proxy)
|
||||
|
||||
proxy.on("ready", function () {
|
||||
// console.error("~~ proxy is ready!", me.path)
|
||||
me.ready = true
|
||||
me.emit("ready")
|
||||
})
|
||||
|
||||
var calls = me._buffer
|
||||
me._buffer.length = 0
|
||||
calls.forEach(function (c) {
|
||||
proxy[c[0]].apply(proxy, c[1])
|
||||
})
|
||||
}
|
||||
|
||||
ProxyReader.prototype.pause = function () {
|
||||
return this._proxyTarget ? this._proxyTarget.pause() : false
|
||||
}
|
||||
|
||||
ProxyReader.prototype.resume = function () {
|
||||
return this._proxyTarget ? this._proxyTarget.resume() : false
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
// A writer for when we don't know what kind of thing
|
||||
// the thing is. That is, it's not explicitly set,
|
||||
// so we're going to make it whatever the thing already
|
||||
// is, or "File"
|
||||
//
|
||||
// Until then, collect all events.
|
||||
|
||||
module.exports = ProxyWriter
|
||||
|
||||
var Writer = require("./writer.js")
|
||||
, getType = require("./get-type.js")
|
||||
, inherits = require("inherits")
|
||||
, collect = require("./collect.js")
|
||||
, fs = require("fs")
|
||||
|
||||
inherits(ProxyWriter, Writer)
|
||||
|
||||
function ProxyWriter (props) {
|
||||
var me = this
|
||||
if (!(me instanceof ProxyWriter)) throw new Error(
|
||||
"ProxyWriter must be called as constructor.")
|
||||
|
||||
me.props = props
|
||||
me._needDrain = false
|
||||
|
||||
Writer.call(me, props)
|
||||
}
|
||||
|
||||
ProxyWriter.prototype._stat = function () {
|
||||
var me = this
|
||||
, props = me.props
|
||||
// stat the thing to see what the proxy should be.
|
||||
, stat = props.follow ? "stat" : "lstat"
|
||||
|
||||
fs[stat](props.path, function (er, current) {
|
||||
var type
|
||||
if (er || !current) {
|
||||
type = "File"
|
||||
} else {
|
||||
type = getType(current)
|
||||
}
|
||||
|
||||
props[type] = true
|
||||
props.type = me.type = type
|
||||
|
||||
me._old = current
|
||||
me._addProxy(Writer(props, current))
|
||||
})
|
||||
}
|
||||
|
||||
ProxyWriter.prototype._addProxy = function (proxy) {
|
||||
// console.error("~~ set proxy", this.path)
|
||||
var me = this
|
||||
if (me._proxy) {
|
||||
return me.error("proxy already set")
|
||||
}
|
||||
|
||||
me._proxy = proxy
|
||||
; [ "ready"
|
||||
, "error"
|
||||
, "close"
|
||||
, "pipe"
|
||||
, "drain"
|
||||
, "warn"
|
||||
].forEach(function (ev) {
|
||||
proxy.on(ev, me.emit.bind(me, ev))
|
||||
})
|
||||
|
||||
me.emit("proxy", proxy)
|
||||
|
||||
var calls = me._buffer
|
||||
calls.forEach(function (c) {
|
||||
// console.error("~~ ~~ proxy buffered call", c[0], c[1])
|
||||
proxy[c[0]].apply(proxy, c[1])
|
||||
})
|
||||
me._buffer.length = 0
|
||||
if (me._needsDrain) me.emit("drain")
|
||||
}
|
||||
|
||||
ProxyWriter.prototype.add = function (entry) {
|
||||
// console.error("~~ proxy add")
|
||||
collect(entry)
|
||||
|
||||
if (!this._proxy) {
|
||||
this._buffer.push(["add", [entry]])
|
||||
this._needDrain = true
|
||||
return false
|
||||
}
|
||||
return this._proxy.add(entry)
|
||||
}
|
||||
|
||||
ProxyWriter.prototype.write = function (c) {
|
||||
// console.error("~~ proxy write")
|
||||
if (!this._proxy) {
|
||||
this._buffer.push(["write", [c]])
|
||||
this._needDrain = true
|
||||
return false
|
||||
}
|
||||
return this._proxy.write(c)
|
||||
}
|
||||
|
||||
ProxyWriter.prototype.end = function (c) {
|
||||
// console.error("~~ proxy end")
|
||||
if (!this._proxy) {
|
||||
this._buffer.push(["end", [c]])
|
||||
return false
|
||||
}
|
||||
return this._proxy.end(c)
|
||||
}
|
|
@ -0,0 +1,262 @@
|
|||
|
||||
module.exports = Reader
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, Stream = require("stream").Stream
|
||||
, inherits = require("inherits")
|
||||
, path = require("path")
|
||||
, getType = require("./get-type.js")
|
||||
, hardLinks = Reader.hardLinks = {}
|
||||
, Abstract = require("./abstract.js")
|
||||
|
||||
// Must do this *before* loading the child classes
|
||||
inherits(Reader, Abstract)
|
||||
|
||||
var DirReader = require("./dir-reader.js")
|
||||
, FileReader = require("./file-reader.js")
|
||||
, LinkReader = require("./link-reader.js")
|
||||
, SocketReader = require("./socket-reader.js")
|
||||
, ProxyReader = require("./proxy-reader.js")
|
||||
|
||||
function Reader (props, currentStat) {
|
||||
var me = this
|
||||
if (!(me instanceof Reader)) return new Reader(props, currentStat)
|
||||
|
||||
if (typeof props === "string") {
|
||||
props = { path: props }
|
||||
}
|
||||
|
||||
if (!props.path) {
|
||||
me.error("Must provide a path", null, true)
|
||||
}
|
||||
|
||||
// polymorphism.
|
||||
// call fstream.Reader(dir) to get a DirReader object, etc.
|
||||
// Note that, unlike in the Writer case, ProxyReader is going
|
||||
// to be the *normal* state of affairs, since we rarely know
|
||||
// the type of a file prior to reading it.
|
||||
|
||||
|
||||
var type
|
||||
, ClassType
|
||||
|
||||
if (props.type && typeof props.type === "function") {
|
||||
type = props.type
|
||||
ClassType = type
|
||||
} else {
|
||||
type = getType(props)
|
||||
ClassType = Reader
|
||||
}
|
||||
|
||||
if (currentStat && !type) {
|
||||
type = getType(currentStat)
|
||||
props[type] = true
|
||||
props.type = type
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case "Directory":
|
||||
ClassType = DirReader
|
||||
break
|
||||
|
||||
case "Link":
|
||||
// XXX hard links are just files.
|
||||
// However, it would be good to keep track of files' dev+inode
|
||||
// and nlink values, and create a HardLinkReader that emits
|
||||
// a linkpath value of the original copy, so that the tar
|
||||
// writer can preserve them.
|
||||
// ClassType = HardLinkReader
|
||||
// break
|
||||
|
||||
case "File":
|
||||
ClassType = FileReader
|
||||
break
|
||||
|
||||
case "SymbolicLink":
|
||||
ClassType = LinkReader
|
||||
break
|
||||
|
||||
case "Socket":
|
||||
ClassType = SocketReader
|
||||
break
|
||||
|
||||
case null:
|
||||
ClassType = ProxyReader
|
||||
break
|
||||
}
|
||||
|
||||
if (!(me instanceof ClassType)) {
|
||||
return new ClassType(props)
|
||||
}
|
||||
|
||||
Abstract.call(me)
|
||||
|
||||
me.readable = true
|
||||
me.writable = false
|
||||
|
||||
me.type = type
|
||||
me.props = props
|
||||
me.depth = props.depth = props.depth || 0
|
||||
me.parent = props.parent || null
|
||||
me.root = props.root || (props.parent && props.parent.root) || me
|
||||
|
||||
me._path = me.path = path.resolve(props.path)
|
||||
if (process.platform === "win32") {
|
||||
me.path = me._path = me.path.replace(/\?/g, "_")
|
||||
if (me._path.length >= 260) {
|
||||
// how DOES one create files on the moon?
|
||||
// if the path has spaces in it, then UNC will fail.
|
||||
me._swallowErrors = true
|
||||
//if (me._path.indexOf(" ") === -1) {
|
||||
me._path = "\\\\?\\" + me.path.replace(/\//g, "\\")
|
||||
//}
|
||||
}
|
||||
}
|
||||
me.basename = props.basename = path.basename(me.path)
|
||||
me.dirname = props.dirname = path.dirname(me.path)
|
||||
|
||||
// these have served their purpose, and are now just noisy clutter
|
||||
props.parent = props.root = null
|
||||
|
||||
// console.error("\n\n\n%s setting size to", props.path, props.size)
|
||||
me.size = props.size
|
||||
me.filter = typeof props.filter === "function" ? props.filter : null
|
||||
if (props.sort === "alpha") props.sort = alphasort
|
||||
|
||||
// start the ball rolling.
|
||||
// this will stat the thing, and then call me._read()
|
||||
// to start reading whatever it is.
|
||||
// console.error("calling stat", props.path, currentStat)
|
||||
me._stat(currentStat)
|
||||
}
|
||||
|
||||
function alphasort (a, b) {
|
||||
return a === b ? 0
|
||||
: a.toLowerCase() > b.toLowerCase() ? 1
|
||||
: a.toLowerCase() < b.toLowerCase() ? -1
|
||||
: a > b ? 1
|
||||
: -1
|
||||
}
|
||||
|
||||
Reader.prototype._stat = function (currentStat) {
|
||||
var me = this
|
||||
, props = me.props
|
||||
, stat = props.follow ? "stat" : "lstat"
|
||||
// console.error("Reader._stat", me._path, currentStat)
|
||||
if (currentStat) process.nextTick(statCb.bind(null, null, currentStat))
|
||||
else fs[stat](me._path, statCb)
|
||||
|
||||
|
||||
function statCb (er, props_) {
|
||||
// console.error("Reader._stat, statCb", me._path, props_, props_.nlink)
|
||||
if (er) return me.error(er)
|
||||
|
||||
Object.keys(props_).forEach(function (k) {
|
||||
props[k] = props_[k]
|
||||
})
|
||||
|
||||
// if it's not the expected size, then abort here.
|
||||
if (undefined !== me.size && props.size !== me.size) {
|
||||
return me.error("incorrect size")
|
||||
}
|
||||
me.size = props.size
|
||||
|
||||
var type = getType(props)
|
||||
var handleHardlinks = props.hardlinks !== false
|
||||
|
||||
// special little thing for handling hardlinks.
|
||||
if (handleHardlinks && type !== "Directory" && props.nlink && props.nlink > 1) {
|
||||
var k = props.dev + ":" + props.ino
|
||||
// console.error("Reader has nlink", me._path, k)
|
||||
if (hardLinks[k] === me._path || !hardLinks[k]) hardLinks[k] = me._path
|
||||
else {
|
||||
// switch into hardlink mode.
|
||||
type = me.type = me.props.type = "Link"
|
||||
me.Link = me.props.Link = true
|
||||
me.linkpath = me.props.linkpath = hardLinks[k]
|
||||
// console.error("Hardlink detected, switching mode", me._path, me.linkpath)
|
||||
// Setting __proto__ would arguably be the "correct"
|
||||
// approach here, but that just seems too wrong.
|
||||
me._stat = me._read = LinkReader.prototype._read
|
||||
}
|
||||
}
|
||||
|
||||
if (me.type && me.type !== type) {
|
||||
me.error("Unexpected type: " + type)
|
||||
}
|
||||
|
||||
// if the filter doesn't pass, then just skip over this one.
|
||||
// still have to emit end so that dir-walking can move on.
|
||||
if (me.filter) {
|
||||
var who = me._proxy || me
|
||||
// special handling for ProxyReaders
|
||||
if (!me.filter.call(who, who, props)) {
|
||||
if (!me._disowned) {
|
||||
me.abort()
|
||||
me.emit("end")
|
||||
me.emit("close")
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// last chance to abort or disown before the flow starts!
|
||||
var events = ["_stat", "stat", "ready"]
|
||||
var e = 0
|
||||
;(function go () {
|
||||
if (me._aborted) {
|
||||
me.emit("end")
|
||||
me.emit("close")
|
||||
return
|
||||
}
|
||||
|
||||
if (me._paused && me.type !== "Directory") {
|
||||
me.once("resume", go)
|
||||
return
|
||||
}
|
||||
|
||||
var ev = events[e ++]
|
||||
if (!ev) {
|
||||
return me._read()
|
||||
}
|
||||
me.emit(ev, props)
|
||||
go()
|
||||
})()
|
||||
}
|
||||
}
|
||||
|
||||
Reader.prototype.pipe = function (dest, opts) {
|
||||
var me = this
|
||||
if (typeof dest.add === "function") {
|
||||
// piping to a multi-compatible, and we've got directory entries.
|
||||
me.on("entry", function (entry) {
|
||||
var ret = dest.add(entry)
|
||||
if (false === ret) {
|
||||
me.pause()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// console.error("R Pipe apply Stream Pipe")
|
||||
return Stream.prototype.pipe.apply(this, arguments)
|
||||
}
|
||||
|
||||
Reader.prototype.pause = function (who) {
|
||||
this._paused = true
|
||||
who = who || this
|
||||
this.emit("pause", who)
|
||||
if (this._stream) this._stream.pause(who)
|
||||
}
|
||||
|
||||
Reader.prototype.resume = function (who) {
|
||||
this._paused = false
|
||||
who = who || this
|
||||
this.emit("resume", who)
|
||||
if (this._stream) this._stream.resume(who)
|
||||
this._read()
|
||||
}
|
||||
|
||||
Reader.prototype._read = function () {
|
||||
this.error("Cannot read unknown type: "+this.type)
|
||||
}
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
// Just get the stats, and then don't do anything.
|
||||
// You can't really "read" from a socket. You "connect" to it.
|
||||
// Mostly, this is here so that reading a dir with a socket in it
|
||||
// doesn't blow up.
|
||||
|
||||
module.exports = SocketReader
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, fstream = require("../fstream.js")
|
||||
, inherits = require("inherits")
|
||||
, mkdir = require("mkdirp")
|
||||
, Reader = require("./reader.js")
|
||||
|
||||
inherits(SocketReader, Reader)
|
||||
|
||||
function SocketReader (props) {
|
||||
var me = this
|
||||
if (!(me instanceof SocketReader)) throw new Error(
|
||||
"SocketReader must be called as constructor.")
|
||||
|
||||
if (!(props.type === "Socket" && props.Socket)) {
|
||||
throw new Error("Non-socket type "+ props.type)
|
||||
}
|
||||
|
||||
Reader.call(me, props)
|
||||
}
|
||||
|
||||
SocketReader.prototype._read = function () {
|
||||
var me = this
|
||||
if (me._paused) return
|
||||
// basically just a no-op, since we got all the info we have
|
||||
// from the _stat method
|
||||
if (!me._ended) {
|
||||
me.emit("end")
|
||||
me.emit("close")
|
||||
me._ended = true
|
||||
}
|
||||
}
|
|
@ -0,0 +1,389 @@
|
|||
|
||||
module.exports = Writer
|
||||
|
||||
var fs = require("graceful-fs")
|
||||
, inherits = require("inherits")
|
||||
, rimraf = require("rimraf")
|
||||
, mkdir = require("mkdirp")
|
||||
, path = require("path")
|
||||
, umask = process.platform === "win32" ? 0 : process.umask()
|
||||
, getType = require("./get-type.js")
|
||||
, Abstract = require("./abstract.js")
|
||||
|
||||
// Must do this *before* loading the child classes
|
||||
inherits(Writer, Abstract)
|
||||
|
||||
Writer.dirmode = 0777 & (~umask)
|
||||
Writer.filemode = 0666 & (~umask)
|
||||
|
||||
var DirWriter = require("./dir-writer.js")
|
||||
, LinkWriter = require("./link-writer.js")
|
||||
, FileWriter = require("./file-writer.js")
|
||||
, ProxyWriter = require("./proxy-writer.js")
|
||||
|
||||
// props is the desired state. current is optionally the current stat,
|
||||
// provided here so that subclasses can avoid statting the target
|
||||
// more than necessary.
|
||||
function Writer (props, current) {
|
||||
var me = this
|
||||
|
||||
if (typeof props === "string") {
|
||||
props = { path: props }
|
||||
}
|
||||
|
||||
if (!props.path) me.error("Must provide a path", null, true)
|
||||
|
||||
// polymorphism.
|
||||
// call fstream.Writer(dir) to get a DirWriter object, etc.
|
||||
var type = getType(props)
|
||||
, ClassType = Writer
|
||||
|
||||
switch (type) {
|
||||
case "Directory":
|
||||
ClassType = DirWriter
|
||||
break
|
||||
case "File":
|
||||
ClassType = FileWriter
|
||||
break
|
||||
case "Link":
|
||||
case "SymbolicLink":
|
||||
ClassType = LinkWriter
|
||||
break
|
||||
case null:
|
||||
// Don't know yet what type to create, so we wrap in a proxy.
|
||||
ClassType = ProxyWriter
|
||||
break
|
||||
}
|
||||
|
||||
if (!(me instanceof ClassType)) return new ClassType(props)
|
||||
|
||||
// now get down to business.
|
||||
|
||||
Abstract.call(me)
|
||||
|
||||
// props is what we want to set.
|
||||
// set some convenience properties as well.
|
||||
me.type = props.type
|
||||
me.props = props
|
||||
me.depth = props.depth || 0
|
||||
me.clobber = false === props.clobber ? props.clobber : true
|
||||
me.parent = props.parent || null
|
||||
me.root = props.root || (props.parent && props.parent.root) || me
|
||||
|
||||
me._path = me.path = path.resolve(props.path)
|
||||
if (process.platform === "win32") {
|
||||
me.path = me._path = me.path.replace(/\?/g, "_")
|
||||
if (me._path.length >= 260) {
|
||||
me._swallowErrors = true
|
||||
me._path = "\\\\?\\" + me.path.replace(/\//g, "\\")
|
||||
}
|
||||
}
|
||||
me.basename = path.basename(props.path)
|
||||
me.dirname = path.dirname(props.path)
|
||||
me.linkpath = props.linkpath || null
|
||||
|
||||
props.parent = props.root = null
|
||||
|
||||
// console.error("\n\n\n%s setting size to", props.path, props.size)
|
||||
me.size = props.size
|
||||
|
||||
if (typeof props.mode === "string") {
|
||||
props.mode = parseInt(props.mode, 8)
|
||||
}
|
||||
|
||||
me.readable = false
|
||||
me.writable = true
|
||||
|
||||
// buffer until ready, or while handling another entry
|
||||
me._buffer = []
|
||||
me.ready = false
|
||||
|
||||
me.filter = typeof props.filter === "function" ? props.filter: null
|
||||
|
||||
// start the ball rolling.
|
||||
// this checks what's there already, and then calls
|
||||
// me._create() to call the impl-specific creation stuff.
|
||||
me._stat(current)
|
||||
}
|
||||
|
||||
// Calling this means that it's something we can't create.
|
||||
// Just assert that it's already there, otherwise raise a warning.
|
||||
Writer.prototype._create = function () {
|
||||
var me = this
|
||||
fs[me.props.follow ? "stat" : "lstat"](me._path, function (er, current) {
|
||||
if (er) {
|
||||
return me.warn("Cannot create " + me._path + "\n" +
|
||||
"Unsupported type: "+me.type, "ENOTSUP")
|
||||
}
|
||||
me._finish()
|
||||
})
|
||||
}
|
||||
|
||||
Writer.prototype._stat = function (current) {
|
||||
var me = this
|
||||
, props = me.props
|
||||
, stat = props.follow ? "stat" : "lstat"
|
||||
, who = me._proxy || me
|
||||
|
||||
if (current) statCb(null, current)
|
||||
else fs[stat](me._path, statCb)
|
||||
|
||||
function statCb (er, current) {
|
||||
if (me.filter && !me.filter.call(who, who, current)) {
|
||||
me._aborted = true
|
||||
me.emit("end")
|
||||
me.emit("close")
|
||||
return
|
||||
}
|
||||
|
||||
// if it's not there, great. We'll just create it.
|
||||
// if it is there, then we'll need to change whatever differs
|
||||
if (er || !current) {
|
||||
return create(me)
|
||||
}
|
||||
|
||||
me._old = current
|
||||
var currentType = getType(current)
|
||||
|
||||
// if it's a type change, then we need to clobber or error.
|
||||
// if it's not a type change, then let the impl take care of it.
|
||||
if (currentType !== me.type) {
|
||||
return rimraf(me._path, function (er) {
|
||||
if (er) return me.error(er)
|
||||
me._old = null
|
||||
create(me)
|
||||
})
|
||||
}
|
||||
|
||||
// otherwise, just handle in the app-specific way
|
||||
// this creates a fs.WriteStream, or mkdir's, or whatever
|
||||
create(me)
|
||||
}
|
||||
}
|
||||
|
||||
function create (me) {
|
||||
// console.error("W create", me._path, Writer.dirmode)
|
||||
|
||||
// XXX Need to clobber non-dirs that are in the way,
|
||||
// unless { clobber: false } in the props.
|
||||
mkdir(path.dirname(me._path), Writer.dirmode, function (er, made) {
|
||||
// console.error("W created", path.dirname(me._path), er)
|
||||
if (er) return me.error(er)
|
||||
|
||||
// later on, we have to set the mode and owner for these
|
||||
me._madeDir = made
|
||||
return me._create()
|
||||
})
|
||||
}
|
||||
|
||||
function endChmod (me, want, current, path, cb) {
|
||||
var wantMode = want.mode
|
||||
, chmod = want.follow || me.type !== "SymbolicLink"
|
||||
? "chmod" : "lchmod"
|
||||
|
||||
if (!fs[chmod]) return cb()
|
||||
if (typeof wantMode !== "number") return cb()
|
||||
|
||||
var curMode = current.mode & 0777
|
||||
wantMode = wantMode & 0777
|
||||
if (wantMode === curMode) return cb()
|
||||
|
||||
fs[chmod](path, wantMode, cb)
|
||||
}
|
||||
|
||||
|
||||
function endChown (me, want, current, path, cb) {
|
||||
// Don't even try it unless root. Too easy to EPERM.
|
||||
if (process.platform === "win32") return cb()
|
||||
if (!process.getuid || !process.getuid() === 0) return cb()
|
||||
if (typeof want.uid !== "number" &&
|
||||
typeof want.gid !== "number" ) return cb()
|
||||
|
||||
if (current.uid === want.uid &&
|
||||
current.gid === want.gid) return cb()
|
||||
|
||||
var chown = (me.props.follow || me.type !== "SymbolicLink")
|
||||
? "chown" : "lchown"
|
||||
if (!fs[chown]) return cb()
|
||||
|
||||
if (typeof want.uid !== "number") want.uid = current.uid
|
||||
if (typeof want.gid !== "number") want.gid = current.gid
|
||||
|
||||
fs[chown](path, want.uid, want.gid, cb)
|
||||
}
|
||||
|
||||
function endUtimes (me, want, current, path, cb) {
|
||||
if (!fs.utimes || process.platform === "win32") return cb()
|
||||
|
||||
var utimes = (want.follow || me.type !== "SymbolicLink")
|
||||
? "utimes" : "lutimes"
|
||||
|
||||
if (utimes === "lutimes" && !fs[utimes]) {
|
||||
utimes = "utimes"
|
||||
}
|
||||
|
||||
if (!fs[utimes]) return cb()
|
||||
|
||||
var curA = current.atime
|
||||
, curM = current.mtime
|
||||
, meA = want.atime
|
||||
, meM = want.mtime
|
||||
|
||||
if (meA === undefined) meA = curA
|
||||
if (meM === undefined) meM = curM
|
||||
|
||||
if (!isDate(meA)) meA = new Date(meA)
|
||||
if (!isDate(meM)) meA = new Date(meM)
|
||||
|
||||
if (meA.getTime() === curA.getTime() &&
|
||||
meM.getTime() === curM.getTime()) return cb()
|
||||
|
||||
fs[utimes](path, meA, meM, cb)
|
||||
}
|
||||
|
||||
|
||||
// XXX This function is beastly. Break it up!
|
||||
Writer.prototype._finish = function () {
|
||||
var me = this
|
||||
|
||||
// console.error(" W Finish", me._path, me.size)
|
||||
|
||||
// set up all the things.
|
||||
// At this point, we're already done writing whatever we've gotta write,
|
||||
// adding files to the dir, etc.
|
||||
var todo = 0
|
||||
var errState = null
|
||||
var done = false
|
||||
|
||||
if (me._old) {
|
||||
// the times will almost *certainly* have changed.
|
||||
// adds the utimes syscall, but remove another stat.
|
||||
me._old.atime = new Date(0)
|
||||
me._old.mtime = new Date(0)
|
||||
// console.error(" W Finish Stale Stat", me._path, me.size)
|
||||
setProps(me._old)
|
||||
} else {
|
||||
var stat = me.props.follow ? "stat" : "lstat"
|
||||
// console.error(" W Finish Stating", me._path, me.size)
|
||||
fs[stat](me._path, function (er, current) {
|
||||
// console.error(" W Finish Stated", me._path, me.size, current)
|
||||
if (er) {
|
||||
// if we're in the process of writing out a
|
||||
// directory, it's very possible that the thing we're linking to
|
||||
// doesn't exist yet (especially if it was intended as a symlink),
|
||||
// so swallow ENOENT errors here and just soldier on.
|
||||
if (er.code === "ENOENT" &&
|
||||
(me.type === "Link" || me.type === "SymbolicLink") &&
|
||||
process.platform === "win32") {
|
||||
me.ready = true
|
||||
me.emit("ready")
|
||||
me.emit("end")
|
||||
me.emit("close")
|
||||
me.end = me._finish = function () {}
|
||||
return
|
||||
} else return me.error(er)
|
||||
}
|
||||
setProps(me._old = current)
|
||||
})
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
function setProps (current) {
|
||||
todo += 3
|
||||
endChmod(me, me.props, current, me._path, next("chmod"))
|
||||
endChown(me, me.props, current, me._path, next("chown"))
|
||||
endUtimes(me, me.props, current, me._path, next("utimes"))
|
||||
}
|
||||
|
||||
function next (what) {
|
||||
return function (er) {
|
||||
// console.error(" W Finish", what, todo)
|
||||
if (errState) return
|
||||
if (er) {
|
||||
er.fstream_finish_call = what
|
||||
return me.error(errState = er)
|
||||
}
|
||||
if (--todo > 0) return
|
||||
if (done) return
|
||||
done = true
|
||||
|
||||
// we may still need to set the mode/etc. on some parent dirs
|
||||
// that were created previously. delay end/close until then.
|
||||
if (!me._madeDir) return end()
|
||||
else endMadeDir(me, me._path, end)
|
||||
|
||||
function end (er) {
|
||||
if (er) {
|
||||
er.fstream_finish_call = "setupMadeDir"
|
||||
return me.error(er)
|
||||
}
|
||||
// all the props have been set, so we're completely done.
|
||||
me.emit("end")
|
||||
me.emit("close")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function endMadeDir (me, p, cb) {
|
||||
var made = me._madeDir
|
||||
// everything *between* made and path.dirname(me._path)
|
||||
// needs to be set up. Note that this may just be one dir.
|
||||
var d = path.dirname(p)
|
||||
|
||||
endMadeDir_(me, d, function (er) {
|
||||
if (er) return cb(er)
|
||||
if (d === made) {
|
||||
return cb()
|
||||
}
|
||||
endMadeDir(me, d, cb)
|
||||
})
|
||||
}
|
||||
|
||||
function endMadeDir_ (me, p, cb) {
|
||||
var dirProps = {}
|
||||
Object.keys(me.props).forEach(function (k) {
|
||||
dirProps[k] = me.props[k]
|
||||
|
||||
// only make non-readable dirs if explicitly requested.
|
||||
if (k === "mode" && me.type !== "Directory") {
|
||||
dirProps[k] = dirProps[k] | 0111
|
||||
}
|
||||
})
|
||||
|
||||
var todo = 3
|
||||
, errState = null
|
||||
fs.stat(p, function (er, current) {
|
||||
if (er) return cb(errState = er)
|
||||
endChmod(me, dirProps, current, p, next)
|
||||
endChown(me, dirProps, current, p, next)
|
||||
endUtimes(me, dirProps, current, p, next)
|
||||
})
|
||||
|
||||
function next (er) {
|
||||
if (errState) return
|
||||
if (er) return cb(errState = er)
|
||||
if (-- todo === 0) return cb()
|
||||
}
|
||||
}
|
||||
|
||||
Writer.prototype.pipe = function () {
|
||||
this.error("Can't pipe from writable stream")
|
||||
}
|
||||
|
||||
Writer.prototype.add = function () {
|
||||
this.error("Cannot add to non-Directory type")
|
||||
}
|
||||
|
||||
Writer.prototype.write = function () {
|
||||
return true
|
||||
}
|
||||
|
||||
function objectToString (d) {
|
||||
return Object.prototype.toString.call(d)
|
||||
}
|
||||
|
||||
function isDate(d) {
|
||||
return typeof d === 'object' && objectToString(d) === '[object Date]';
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
../mkdirp/bin/cmd.js
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue