<commit_before>// Requiring modules
var slackBotAPI = require('slackbotapi'),
    slack = require('slack-api'),
    Codebase = require('node-codebase'),
    creds = require('./creds.json'),
    _ = require('lodash'),
    os = require('os'),
    logger = require('jethro'),
    logging = true;

// Starting slackbot
var slackBot = new slackBotAPI({
	'token': creds.slack.authToken,
	'logging': logging
});

var outputLog = function(obj) {
    if(logging) {
        logger.output(obj);
    }
}

//process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

//takes an incoming message, parses ticket number(s)
//in it and sends off message to slack channel with ticket
//details. Then touches the related Codebase ticket(s) with the message from
// slack.
var TicketInfoToPost = function( data ) {
    //are there tickets in this?
    var regex = /(#[0-9\-a-z]*)/g,
        tickets = data.text.match(regex),
        ticketQuery = {},
        ticketProject = '',
        ticketProjectRef = '',
        ticketNum = '',
        channelProject = '';
    if ( ! tickets ) {
        return;
    }
    tickets.forEach( function(ticket){
        /**
         * parse ticket for possible reference
         * infer project to grab ticket details from
         * channel or explicit reference
         * setup ticketQuery(ies) to send in an array and then loop through
         * and get ticket details via codebase api.
         * generate attachment (for each ticket) for posting to slack and
         * send.
         * touch codebase tickets with link to related message triggering
         * that ticket.
         */
        regex = /[#0-9-]/g;
        ticketProjectRef = ticket.replace(regex, '');

        //ensure just ticket num
        regex = /[#a-z-]/g;
        ticketNum = ticket.replace(regex, '');

        //inferred channel project
        channelProject = typeof creds.codebaseMap.channels[data.channel] !== 'undefined' ? creds.codebaseMap.channels[data.channel].projectSlug : creds.codebaseMap.channels.default.projectSlug;

        //if we have a project inferred from ticket use it, otherwise infer
        //from channel.
        ticketProject = ticketProjectRef !== '' ? creds.codebaseMap.projects[ticketProjectRef].projectSlug : channelProject;
        ticketProjectRef = ticketProjectRef === '' ? 'eecore' : ticketProjectRef;

        //ticketQuery
        if ( typeof ticketQuery[ticketProject] !== 'undefined' ) {
            ticketQuery[ticketProject].query += ',' + ticketNum;
        } else {
            ticketQuery[ticketProject] = {};
            ticketQuery[ticketProject].project = ticketProject;
            ticketQuery[ticketProject].projectRef = ticketProjectRef;
            ticketQuery[ticketProject].query = 'id:'+ticketNum
        }
    });
    if ( ticketQuery ) {
        //send ticketQuery for retrieving from codebase and posting to slack
        getAndPostTickets(ticketQuery, data);
    }
};

var getAndPostTickets = function( ticketQuery, data ) {
    var cb = {},
        slattachments = [],
        codebaseMessage = '',
        channelInfo = {};

    //channel info
    slack.channel.info( { "token" : creds.slack.authToken, "channel" : data.channel }, function( slerr, slcdata ) {
        if ( slerr ) {
            console.log( 'Channel Info Error:' );
            console.log( slcdata );
        } else {
            channelInfo = slcdata;
            //loop through ticketQuery and retrieve, then post info related to each ticket
            cb = new Codebase( creds.codebaseMap.baseEndpoint, creds.codebaseMap.users.eebot.cbAuth.events );
            _.each( ticketQuery, function( queryString, n) {
                cb.tickets.allQuery( queryString.project, { query : queryString.query }, function( cberr, cbdata ) {
                    if ( cberr ) {
                        console.log( 'Error:' );
                        console.log( cbdata );
                        return;
                    } else {
                        //all went well so let's loop through the tickets and grab
                        //the details:
                        if ( cbdata.tickets ) {
                            cbdata.tickets.ticket.forEach( function( ticket ) {
                                slattachments.push({
                                    "fallback" : "Ticket " + ticket.ticketId[0]._ + ': ' + ticket.summary[0],
                                    "color" : creds.codebaseMap.colors[ticket.ticketType[0]] ? creds.codebaseMap.colors[ticket.ticketType[0]] : creds.codebaseMap.colors.default,
                                    "title" : "Ticket: " + ticket.ticketId[0]._ + " (" + ticket.milestone[0].name[0] + ")",
                                    "title_link" : creds.codebaseMap.projects[queryString.projectRef].url + '/tickets/' + ticket.ticketId[0]._,
                                    "text" : "*Ticket Type:* _" + ticket.ticketType[0] + "_ \n" + ticket.summary[0],
                                    "fields" : [
                                        {
                                            "title" : "Priority",
                                            "value" : ticket.priority[0].name[0],
                                            "short" : true
                                        },
                                        {
                                            "title" : "Status",
                                            "value" : ticket.status[0].name[0],
                                            "short" : true
                                        }
                                    ],
                                    "mrkdwn_in" : ["text","pretext"]
                                });

                                //touch ticket in codebase
                                codebaseMessage = "![enter image description here][1] This ticket was [talked about in Slack][2] \n\n " + data.text + "\n\n [1]: https://events.codebasehq.com/upload/71d24d9a-22da-4a45-1cb4-4da98d6b0974/show/original \n\n[2]: " + creds.slack.archivesEndpoint + channelInfo.channel.name + '/p' + data.ts.replace('.', '');
                                cb.tickets.notes.create( queryString.project, { ticket_id : ticket.ticketId[0]._, content : codebaseMessage }, function( cbterr, cbtdata ) {
                                    if ( cbterr ) {
                                        console.log( 'Codebase ticket update error: ' );
                                        console.log( cbtdata );
                                    }
                                });
                            });

                            slattachments = JSON.stringify( slattachments );
                            //send message to slack
                            slack.chat.postMessage( { token : creds.slack.authToken, text : "", channel : data.channel, attachments : slattachments, as_user : true }, function( slcherr, slchdata ) {
                                if ( slcherr ) {
                                    console.log(slchdata);
                                } else {
                                    outputLog({severity: 'info', source: 'getAndPostTickets', message: 'Sent ticket info to slack', timestamp: new Date(), location: os.hostname() } );
                                }
                            });

                        }
                    }
                });

            });
        }
    });

}

var ChatWithBot = function ( data ) {
    //match the first word in the chat to get the command
    var command = data.text.split( ' ' );

    switch ( command[0].toLowerCase() ) {
        case "hello" :
            slackBot.sendMsg( data.channel, "Oh, hello @" + slackBot.getUser(data.user).name+" !" );
            break;

        case "help" :
            //if help is alone, then let's send a list of commands they can learn more about.
            if ( typeof command[1] === 'undefined' ) {
                var helpcommands = [
                    "*I'm here to serve!* Here's some commands I know that I can give more information on, just type `help` followed by the item in the list below.",
                    "• `tickets` - _more info on how ticket stuff works_"
                ]
            }
            slackBot.sendMsg( data.channel, helpcommands.join("\n") );
            break;

    }
}

// Slack on EVENT message, send data.
slackBot.on('message', function(data) {
	// If no text, return.
	if(typeof data.text == 'undefined') return;

	// If someone says 'cake!!' respond to their message with "@user OOH, CAKE!! :cake"
	if(data.text === 'cake!!') slackBot.sendMsg(data.channel, "@"+slackBot.getUser(data.user).name+" OOH, CAKE!! :cake:");

    // if the "#[0-9]" is found anywhere in the message look up the ticket.
    TicketInfoToPost( data );

    //if pm with eebot, let's run pm commands
    if ( data.channel == creds.slack.botId ) {
        ChatWithBot( data );
    }

	// If the first character starts with /, you can change this to your own prefix of course.
	if(data.text.charAt(0) === '%') {
		// Split the command and it's arguments into an array
		var command = data.text.substring(1).split(' ');

		// If command[2] is not undefined use command[1] to have all arguments in comand[1]
		if (typeof command[2] != "undefined") {
			for (var i = 2; i < command.length; i++) {
				command[1] = command[1] + ' ' + command[i];
			}
		}

		// Switch to check which command has been requested.
		switch (command[0].toLowerCase()) {

			case "say":
				var say = data.text.split('/say ');
				slackBot.sendMsg(data.channel, say[1]);
			break;

            //testing codebase
            case "cbtest":
                var cb = new Codebase( creds.codebaseMap.baseEndpoint, creds.codebaseMap.users.darren.cbAuth.events );
                console.log('got the message');
                cb.tickets.allQuery( 'event-espresso', { query : 'id:1000,1001' }, function( err, cbdata ) {
                    console.log('begin');
                   if ( err ) {
                       console.log('Error:');
                       console.log( cbdata );
                       return;
                   }
                    console.log( cbdata.tickets.ticket[0].status );
                    console.log( cbdata.tickets.ticket[0].priority );
                    console.log( cbdata.tickets.ticket[0].ticketId);
                    console.log( cbdata.tickets.ticket[0].milestone );
                    console.log( cbdata.tickets.ticket );
                    console.log(cbdata);
                });
                break;

			case "debug":
				console.log(slackBot.data.ims);
			break;
		}
	}
});
<commit_msg>- add some debugging<commit_after>// Requiring modules
var slackBotAPI = require('slackbotapi'),
    slack = require('slack-api'),
    Codebase = require('node-codebase'),
    creds = require('./creds.json'),
    _ = require('lodash'),
    os = require('os'),
    logger = require('jethro'),
    logging = true;

// Starting slackbot
var slackBot = new slackBotAPI({
	'token': creds.slack.authToken,
	'logging': logging
});

var outputLog = function(obj) {
    if(logging) {
        logger.output(obj);
    }
}

//process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";

//takes an incoming message, parses ticket number(s)
//in it and sends off message to slack channel with ticket
//details. Then touches the related Codebase ticket(s) with the message from
// slack.
var TicketInfoToPost = function( data ) {
    //are there tickets in this?
    var regex = /(#[0-9\-a-z]*)/g,
        tickets = data.text.match(regex),
        ticketQuery = {},
        ticketProject = '',
        ticketProjectRef = '',
        ticketNum = '',
        channelProject = '';
    if ( ! tickets ) {
        return;
    }
    tickets.forEach( function(ticket){
        /**
         * parse ticket for possible reference
         * infer project to grab ticket details from
         * channel or explicit reference
         * setup ticketQuery(ies) to send in an array and then loop through
         * and get ticket details via codebase api.
         * generate attachment (for each ticket) for posting to slack and
         * send.
         * touch codebase tickets with link to related message triggering
         * that ticket.
         */
        regex = /[#0-9-]/g;
        ticketProjectRef = ticket.replace(regex, '');

        //ensure just ticket num
        regex = /[#a-z-]/g;
        ticketNum = ticket.replace(regex, '');

        //inferred channel project
        channelProject = typeof creds.codebaseMap.channels[data.channel] !== 'undefined' ? creds.codebaseMap.channels[data.channel].projectSlug : creds.codebaseMap.channels.default.projectSlug;

        //if we have a project inferred from ticket use it, otherwise infer
        //from channel.
        ticketProject = ticketProjectRef !== '' ? creds.codebaseMap.projects[ticketProjectRef].projectSlug : channelProject;
        ticketProjectRef = ticketProjectRef === '' ? 'eecore' : ticketProjectRef;

        //ticketQuery
        if ( typeof ticketQuery[ticketProject] !== 'undefined' ) {
            ticketQuery[ticketProject].query += ',' + ticketNum;
        } else {
            ticketQuery[ticketProject] = {};
            ticketQuery[ticketProject].project = ticketProject;
            ticketQuery[ticketProject].projectRef = ticketProjectRef;
            ticketQuery[ticketProject].query = 'id:'+ticketNum
        }
    });
    if ( ticketQuery ) {
        //send ticketQuery for retrieving from codebase and posting to slack
        getAndPostTickets(ticketQuery, data);
    }
};

var getAndPostTickets = function( ticketQuery, data ) {
    var cb = {},
        slattachments = [],
        codebaseMessage = '',
        channelInfo = {};

    //channel info
    slack.channel.info( { "token" : creds.slack.authToken, "channel" : data.channel }, function( slerr, slcdata ) {
        if ( slerr ) {
            console.log( 'Channel Info Error:' );
            console.log( slcdata );
        } else {
            channelInfo = slcdata;
            //loop through ticketQuery and retrieve, then post info related to each ticket
            cb = new Codebase( creds.codebaseMap.baseEndpoint, creds.codebaseMap.users.eebot.cbAuth.events );
            _.each( ticketQuery, function( queryString, n) {
                cb.tickets.allQuery( queryString.project, { query : queryString.query }, function( cberr, cbdata ) {
                    if ( cberr ) {
                        console.log( 'Error:' );
                        console.log( cbdata );
                        return;
                    } else {
                        //all went well so let's loop through the tickets and grab
                        //the details:
                        if ( cbdata.tickets ) {
                            cbdata.tickets.ticket.forEach( function( ticket ) {
                                slattachments.push({
                                    "fallback" : "Ticket " + ticket.ticketId[0]._ + ': ' + ticket.summary[0],
                                    "color" : creds.codebaseMap.colors[ticket.ticketType[0]] ? creds.codebaseMap.colors[ticket.ticketType[0]] : creds.codebaseMap.colors.default,
                                    "title" : "Ticket: " + ticket.ticketId[0]._ + " (" + ticket.milestone[0].name[0] + ")",
                                    "title_link" : creds.codebaseMap.projects[queryString.projectRef].url + '/tickets/' + ticket.ticketId[0]._,
                                    "text" : "*Ticket Type:* _" + ticket.ticketType[0] + "_ \n" + ticket.summary[0],
                                    "fields" : [
                                        {
                                            "title" : "Priority",
                                            "value" : ticket.priority[0].name[0],
                                            "short" : true
                                        },
                                        {
                                            "title" : "Status",
                                            "value" : ticket.status[0].name[0],
                                            "short" : true
                                        }
                                    ],
                                    "mrkdwn_in" : ["text","pretext"]
                                });

                                //touch ticket in codebase
                                codebaseMessage = "![enter image description here][1] This ticket was [talked about in Slack][2] \n\n " + data.text + "\n\n [1]: https://events.codebasehq.com/upload/71d24d9a-22da-4a45-1cb4-4da98d6b0974/show/original \n\n[2]: " + creds.slack.archivesEndpoint + channelInfo.channel.name + '/p' + data.ts.replace('.', '');
                                cb.tickets.notes.create( queryString.project, { ticket_id : ticket.ticketId[0]._, content : codebaseMessage }, function( cbterr, cbtdata ) {
                                    if ( cbterr ) {
                                        console.log( 'Codebase ticket update error: ' );
                                        console.log( cbtdata );
                                    }
                                });
                            });

                            slattachments = JSON.stringify( slattachments );
                            //send message to slack
                            slack.chat.postMessage( { token : creds.slack.authToken, text : "", channel : data.channel, attachments : slattachments, as_user : true }, function( slcherr, slchdata ) {
                                if ( slcherr ) {
                                    console.log(slchdata);
                                } else {
                                    outputLog({severity: 'info', source: 'getAndPostTickets', message: 'Sent ticket info to slack', timestamp: new Date(), location: os.hostname() } );
                                }
                            });

                        }
                    }
                });

            });
        }
    });

}

var ChatWithBot = function ( data ) {
    //match the first word in the chat to get the command
    var command = data.text.split( ' ' );

    switch ( command[0].toLowerCase() ) {
        case "hello" :
            slackBot.sendMsg( data.channel, "Oh, hello @" + slackBot.getUser(data.user).name+" !" );
            break;

        case "help" :
            //if help is alone, then let's send a list of commands they can learn more about.
            if ( typeof command[1] === 'undefined' ) {
                var helpcommands = [
                    "*I'm here to serve!* Here's some commands I know that I can give more information on, just type `help` followed by the item in the list below.",
                    "• `tickets` - _more info on how ticket stuff works_"
                ]
            }
            slackBot.sendMsg( data.channel, helpcommands.join("\n") );
            break;

    }
}

// Slack on EVENT message, send data.
slackBot.on('message', function(data) {
	// If no text, return.
	if(typeof data.text == 'undefined') return;

	// If someone says 'cake!!' respond to their message with "@user OOH, CAKE!! :cake"
	if(data.text === 'cake!!') slackBot.sendMsg(data.channel, "@"+slackBot.getUser(data.user).name+" OOH, CAKE!! :cake:");

    // if the "#[0-9]" is found anywhere in the message look up the ticket.
    TicketInfoToPost( data );
    console.log( data.channel );
    console.log( creds.slack.botID );
    //if pm with eebot, let's run pm commands
    if ( data.channel == creds.slack.botId ) {
        ChatWithBot( data );
    }

	// If the first character starts with /, you can change this to your own prefix of course.
	if(data.text.charAt(0) === '%') {
		// Split the command and it's arguments into an array
		var command = data.text.substring(1).split(' ');

		// If command[2] is not undefined use command[1] to have all arguments in comand[1]
		if (typeof command[2] != "undefined") {
			for (var i = 2; i < command.length; i++) {
				command[1] = command[1] + ' ' + command[i];
			}
		}

		// Switch to check which command has been requested.
		switch (command[0].toLowerCase()) {

			case "say":
				var say = data.text.split('/say ');
				slackBot.sendMsg(data.channel, say[1]);
			break;

            //testing codebase
            case "cbtest":
                var cb = new Codebase( creds.codebaseMap.baseEndpoint, creds.codebaseMap.users.darren.cbAuth.events );
                console.log('got the message');
                cb.tickets.allQuery( 'event-espresso', { query : 'id:1000,1001' }, function( err, cbdata ) {
                    console.log('begin');
                   if ( err ) {
                       console.log('Error:');
                       console.log( cbdata );
                       return;
                   }
                    console.log( cbdata.tickets.ticket[0].status );
                    console.log( cbdata.tickets.ticket[0].priority );
                    console.log( cbdata.tickets.ticket[0].ticketId);
                    console.log( cbdata.tickets.ticket[0].milestone );
                    console.log( cbdata.tickets.ticket );
                    console.log(cbdata);
                });
                break;

			case "debug":
				console.log(slackBot.data.ims);
			break;
		}
	}
});
<|endoftext|>


<commit_before>;(function(undefined) {
	"use strict";

	var $scope
	, conflict, conflictResolution = [];
	if (typeof global == 'object' && global) {
		$scope = global;
	} else if (typeof window !== 'undefined'){
		$scope = window;
	} else {
		$scope = {};
	}
	conflict = $scope.DeepDiff;
	if (conflict) {
		conflictResolution.push(
			function() {
				if ('undefined' !== typeof conflict && $scope.DeepDiff === accumulateDiff) {
					$scope.DeepDiff = conflict;
					conflict = undefined;
				}
			});
	}

	// nodejs compatible on server side and in the browser.
  function inherits(ctor, superCtor) {
    ctor.super_ = superCtor;
    ctor.prototype = Object.create(superCtor.prototype, {
      constructor: {
        value: ctor,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  }

  function Diff(kind, path) {
  	Object.defineProperty(this, 'kind', { value: kind, enumerable: true });
  	if (path && path.length) {
  		Object.defineProperty(this, 'path', { value: path, enumerable: true });
  	}
  }

  function DiffEdit(path, origin, value) {
  	DiffEdit.super_.call(this, 'E', path);
  	Object.defineProperty(this, 'lhs', { value: origin, enumerable: true });
  	Object.defineProperty(this, 'rhs', { value: value, enumerable: true });
  }
  inherits(DiffEdit, Diff);

  function DiffNew(path, value) {
  	DiffNew.super_.call(this, 'N', path);
  	Object.defineProperty(this, 'rhs', { value: value, enumerable: true });
  }
  inherits(DiffNew, Diff);

  function DiffDeleted(path, value) {
  	DiffDeleted.super_.call(this, 'D', path);
  	Object.defineProperty(this, 'lhs', { value: value, enumerable: true });
  }
  inherits(DiffDeleted, Diff);

  function DiffArray(path, index, item) {
  	DiffArray.super_.call(this, 'A', path);
  	Object.defineProperty(this, 'index', { value: index, enumerable: true });
  	Object.defineProperty(this, 'item', { value: item, enumerable: true });
  }
  inherits(DiffArray, Diff);

  function arrayRemove(arr, from, to) {
  	var rest = arr.slice((to || from) + 1 || arr.length);
  	arr.length = from < 0 ? arr.length + from : from;
  	arr.push.apply(arr, rest);
  	return arr;
  }

  function deepDiff(lhs, rhs, changes, prefilter, path, key, stack) {
  	path = path || [];
  	var currentPath = path.slice(0);
  	if (key) {
  		if (prefilter && prefilter(currentPath, key)) return;
  		currentPath.push(key);
  	}
  	var ltype = typeof lhs;
  	var rtype = typeof rhs;
  	if (ltype === 'undefined') {
  		if (rtype !== 'undefined') {
  			changes(new DiffNew(currentPath, rhs ));
  		}
  	} else if (rtype === 'undefined') {
  		changes(new DiffDeleted(currentPath, lhs));
  	} else if (ltype !== rtype) {
  		changes(new DiffEdit(currentPath, lhs, rhs));
  	} else if (lhs instanceof Date && rhs instanceof Date && ((lhs-rhs) != 0) ) {
  		changes(new DiffEdit(currentPath, lhs, rhs));
  	} else if (ltype === 'object' && lhs != null && rhs != null) {
  		stack = stack || [];
  		if (stack.indexOf(lhs) < 0) {
  			stack.push(lhs);
  			if (Array.isArray(lhs)) {
  				var i
  				, len = lhs.length
  				, ea = function(d) {
  					changes(new DiffArray(currentPath, i, d));
  				};
  				for(i = 0; i < lhs.length; i++) {
  					if (i >= rhs.length) {
  						changes(new DiffArray(currentPath, i, new DiffDeleted(undefined, lhs[i])));
  					} else {
  						deepDiff(lhs[i], rhs[i], ea, prefilter, [], null, stack);
  					}
  				}
  				while(i < rhs.length) {
  					changes(new DiffArray(currentPath, i, new DiffNew(undefined, rhs[i++])));
  				}
  			} else {
  				var akeys = Object.keys(lhs);
  				var pkeys = Object.keys(rhs);
  				akeys.forEach(function(k) {
  					var i = pkeys.indexOf(k);
  					if (i >= 0) {
  						deepDiff(lhs[k], rhs[k], changes, prefilter, currentPath, k, stack);
  						pkeys = arrayRemove(pkeys, i);
  					} else {
  						deepDiff(lhs[k], undefined, changes, prefilter, currentPath, k, stack);
  					}
  				});
  				pkeys.forEach(function(k) {
  					deepDiff(undefined, rhs[k], changes, prefilter, currentPath, k, stack);
  				});
  			}
  			stack.length = stack.length - 1;
  		}
  	} else if (lhs !== rhs) {
      if(!(ltype === "number" && isNaN(lhs) && isNaN(rhs))) {
  		  changes(new DiffEdit(currentPath, lhs, rhs));
      }
  	}
  }

  function accumulateDiff(lhs, rhs, prefilter, accum) {
  	accum = accum || [];
  	deepDiff(lhs, rhs,
  		function(diff) {
  			if (diff) {
  				accum.push(diff);
  			}
  		},
  		prefilter);
  	return (accum.length) ? accum : undefined;
  }

	function applyArrayChange(arr, index, change) {
		if (change.path && change.path.length) {
			// the structure of the object at the index has changed...
			var it = arr[index], i, u = change.path.length - 1;
			for(i = 0; i < u; i++){
				it = it[change.path[i]];
			}
			switch(change.kind) {
				case 'A':
					// Array was modified...
					// it will be an array...
					applyArrayChange(it[change.path[i]], change.index, change.item);
					break;
				case 'D':
					// Item was deleted...
					delete it[change.path[i]];
					break;
				case 'E':
				case 'N':
					// Item was edited or is new...
					it[change.path[i]] = change.rhs;
					break;
			}
		} else {
			// the array item is different...
			switch(change.kind) {
				case 'A':
					// Array was modified...
					// it will be an array...
					applyArrayChange(arr[index], change.index, change.item);
					break;
				case 'D':
					// Item was deleted...
					arr = arrayRemove(arr, index);
					break;
				case 'E':
				case 'N':
					// Item was edited or is new...
					arr[index] = change.rhs;
					break;
			}
		}
		return arr;
	}

	function applyChange(target, source, change) {
		if (!(change instanceof Diff)) {
			throw new TypeError('[Object] change must be instanceof Diff');
		}
		if (target && source && change) {
			var it = target, i, u;
			u = change.path.length - 1;
			for(i = 0; i < u; i++){
				if (typeof it[change.path[i]] === 'undefined') {
					it[change.path[i]] = {};
				}
				it = it[change.path[i]];
			}
			switch(change.kind) {
				case 'A':
					// Array was modified...
					// it will be an array...
					applyArrayChange(it[change.path[i]], change.index, change.item);
					break;
				case 'D':
					// Item was deleted...
					delete it[change.path[i]];
					break;
				case 'E':
				case 'N':
					// Item was edited or is new...
					it[change.path[i]] = change.rhs;
					break;
				}
			}
		}

	function applyDiff(target, source, filter) {
		if (target && source) {
			var onChange = function(change) {
				if (!filter || filter(target, source, change)) {
					applyChange(target, source, change);
				}
			};
			deepDiff(target, source, onChange);
		}
	}

	Object.defineProperties(accumulateDiff, {

		diff: { value: accumulateDiff, enumerable:true },
		observableDiff: { value: deepDiff, enumerable:true },
		applyDiff: { value: applyDiff, enumerable:true },
		applyChange: { value: applyChange, enumerable:true },
		isConflict: { get: function() { return 'undefined' !== typeof conflict; }, enumerable: true },
		noConflict: {
			value: function () {
				if (conflictResolution) {
					conflictResolution.forEach(function (it) { it(); });
					conflictResolution = null;
				}
				return accumulateDiff;
			},
			enumerable: true
		}
	});

	if (typeof module != 'undefined' && module && typeof exports == 'object' && exports && module.exports === exports) {
		module.exports = accumulateDiff; // nodejs
	} else {
		$scope.DeepDiff = accumulateDiff; // other... browser?
	}
}());

<commit_msg>Convert all tabs to spaces<commit_after>;(function(undefined) {
  "use strict";

  var $scope
  , conflict, conflictResolution = [];
  if (typeof global == 'object' && global) {
    $scope = global;
  } else if (typeof window !== 'undefined'){
    $scope = window;
  } else {
    $scope = {};
  }
  conflict = $scope.DeepDiff;
  if (conflict) {
    conflictResolution.push(
      function() {
        if ('undefined' !== typeof conflict && $scope.DeepDiff === accumulateDiff) {
          $scope.DeepDiff = conflict;
          conflict = undefined;
        }
      });
  }

  // nodejs compatible on server side and in the browser.
  function inherits(ctor, superCtor) {
    ctor.super_ = superCtor;
    ctor.prototype = Object.create(superCtor.prototype, {
      constructor: {
        value: ctor,
        enumerable: false,
        writable: true,
        configurable: true
      }
    });
  }

  function Diff(kind, path) {
    Object.defineProperty(this, 'kind', { value: kind, enumerable: true });
    if (path && path.length) {
      Object.defineProperty(this, 'path', { value: path, enumerable: true });
    }
  }

  function DiffEdit(path, origin, value) {
    DiffEdit.super_.call(this, 'E', path);
    Object.defineProperty(this, 'lhs', { value: origin, enumerable: true });
    Object.defineProperty(this, 'rhs', { value: value, enumerable: true });
  }
  inherits(DiffEdit, Diff);

  function DiffNew(path, value) {
    DiffNew.super_.call(this, 'N', path);
    Object.defineProperty(this, 'rhs', { value: value, enumerable: true });
  }
  inherits(DiffNew, Diff);

  function DiffDeleted(path, value) {
    DiffDeleted.super_.call(this, 'D', path);
    Object.defineProperty(this, 'lhs', { value: value, enumerable: true });
  }
  inherits(DiffDeleted, Diff);

  function DiffArray(path, index, item) {
    DiffArray.super_.call(this, 'A', path);
    Object.defineProperty(this, 'index', { value: index, enumerable: true });
    Object.defineProperty(this, 'item', { value: item, enumerable: true });
  }
  inherits(DiffArray, Diff);

  function arrayRemove(arr, from, to) {
    var rest = arr.slice((to || from) + 1 || arr.length);
    arr.length = from < 0 ? arr.length + from : from;
    arr.push.apply(arr, rest);
    return arr;
  }

  function deepDiff(lhs, rhs, changes, prefilter, path, key, stack) {
    path = path || [];
    var currentPath = path.slice(0);
    if (key) {
      if (prefilter && prefilter(currentPath, key)) return;
      currentPath.push(key);
    }
    var ltype = typeof lhs;
    var rtype = typeof rhs;
    if (ltype === 'undefined') {
      if (rtype !== 'undefined') {
        changes(new DiffNew(currentPath, rhs ));
      }
    } else if (rtype === 'undefined') {
      changes(new DiffDeleted(currentPath, lhs));
    } else if (ltype !== rtype) {
      changes(new DiffEdit(currentPath, lhs, rhs));
    } else if (lhs instanceof Date && rhs instanceof Date && ((lhs-rhs) != 0) ) {
      changes(new DiffEdit(currentPath, lhs, rhs));
    } else if (ltype === 'object' && lhs != null && rhs != null) {
      stack = stack || [];
      if (stack.indexOf(lhs) < 0) {
        stack.push(lhs);
        if (Array.isArray(lhs)) {
          var i
          , len = lhs.length
          , ea = function(d) {
            changes(new DiffArray(currentPath, i, d));
          };
          for(i = 0; i < lhs.length; i++) {
            if (i >= rhs.length) {
              changes(new DiffArray(currentPath, i, new DiffDeleted(undefined, lhs[i])));
            } else {
              deepDiff(lhs[i], rhs[i], ea, prefilter, [], null, stack);
            }
          }
          while(i < rhs.length) {
            changes(new DiffArray(currentPath, i, new DiffNew(undefined, rhs[i++])));
          }
        } else {
          var akeys = Object.keys(lhs);
          var pkeys = Object.keys(rhs);
          akeys.forEach(function(k) {
            var i = pkeys.indexOf(k);
            if (i >= 0) {
              deepDiff(lhs[k], rhs[k], changes, prefilter, currentPath, k, stack);
              pkeys = arrayRemove(pkeys, i);
            } else {
              deepDiff(lhs[k], undefined, changes, prefilter, currentPath, k, stack);
            }
          });
          pkeys.forEach(function(k) {
            deepDiff(undefined, rhs[k], changes, prefilter, currentPath, k, stack);
          });
        }
        stack.length = stack.length - 1;
      }
    } else if (lhs !== rhs) {
      if(!(ltype === "number" && isNaN(lhs) && isNaN(rhs))) {
        changes(new DiffEdit(currentPath, lhs, rhs));
      }
    }
  }

  function accumulateDiff(lhs, rhs, prefilter, accum) {
    accum = accum || [];
    deepDiff(lhs, rhs,
      function(diff) {
        if (diff) {
          accum.push(diff);
        }
      },
      prefilter);
    return (accum.length) ? accum : undefined;
  }

  function applyArrayChange(arr, index, change) {
    if (change.path && change.path.length) {
      // the structure of the object at the index has changed...
      var it = arr[index], i, u = change.path.length - 1;
      for(i = 0; i < u; i++){
        it = it[change.path[i]];
      }
      switch(change.kind) {
        case 'A':
          // Array was modified...
          // it will be an array...
          applyArrayChange(it[change.path[i]], change.index, change.item);
          break;
        case 'D':
          // Item was deleted...
          delete it[change.path[i]];
          break;
        case 'E':
        case 'N':
          // Item was edited or is new...
          it[change.path[i]] = change.rhs;
          break;
      }
    } else {
      // the array item is different...
      switch(change.kind) {
        case 'A':
          // Array was modified...
          // it will be an array...
          applyArrayChange(arr[index], change.index, change.item);
          break;
        case 'D':
          // Item was deleted...
          arr = arrayRemove(arr, index);
          break;
        case 'E':
        case 'N':
          // Item was edited or is new...
          arr[index] = change.rhs;
          break;
      }
    }
    return arr;
  }

  function applyChange(target, source, change) {
    if (!(change instanceof Diff)) {
      throw new TypeError('[Object] change must be instanceof Diff');
    }
    if (target && source && change) {
      var it = target, i, u;
      u = change.path.length - 1;
      for(i = 0; i < u; i++){
        if (typeof it[change.path[i]] === 'undefined') {
          it[change.path[i]] = {};
        }
        it = it[change.path[i]];
      }
      switch(change.kind) {
        case 'A':
          // Array was modified...
          // it will be an array...
          applyArrayChange(it[change.path[i]], change.index, change.item);
          break;
        case 'D':
          // Item was deleted...
          delete it[change.path[i]];
          break;
        case 'E':
        case 'N':
          // Item was edited or is new...
          it[change.path[i]] = change.rhs;
          break;
      }
    }
  }

  function applyDiff(target, source, filter) {
    if (target && source) {
      var onChange = function(change) {
        if (!filter || filter(target, source, change)) {
          applyChange(target, source, change);
        }
      };
      deepDiff(target, source, onChange);
    }
  }

  Object.defineProperties(accumulateDiff, {

    diff: { value: accumulateDiff, enumerable:true },
    observableDiff: { value: deepDiff, enumerable:true },
    applyDiff: { value: applyDiff, enumerable:true },
    applyChange: { value: applyChange, enumerable:true },
    isConflict: { get: function() { return 'undefined' !== typeof conflict; }, enumerable: true },
    noConflict: {
      value: function () {
        if (conflictResolution) {
          conflictResolution.forEach(function (it) { it(); });
          conflictResolution = null;
        }
        return accumulateDiff;
      },
      enumerable: true
    }
  });

  if (typeof module != 'undefined' && module && typeof exports == 'object' && exports && module.exports === exports) {
    module.exports = accumulateDiff; // nodejs
  } else {
    $scope.DeepDiff = accumulateDiff; // other... browser?
  }
}());<|endoftext|>


<commit_before>'use strict';
function parseJs(code, define) {
    //js模板解析

    var promise = {
        data: [],
        len: 0,

        get: function() {
            return this.data[this.len - 1];
        },
        getState: function(all) {
            //是否为true
            var l = this.len - 1;
            if (all === true) {
                l++;
            }
            for (var i = 0; i < l; i++) {
                if (!this.data[i])
                    return false;
            }
            return true;
        },
        set: function(state) {
            this.data[this.len - 1] = this.data[this.len - 1] || state;
            return state;
        },
        pop: function() {
            this.len--;
            return this.len > 0 ? this.data.pop() : this.len = 0;
        },
        push: function(state) {
            this.len++;
            return this.data.push(state),
            state;
        }
    };

    return function() {

        var res = code.replace(/(\{\{.*?\}\})(((?!\{\{)[\s\S])*)/g, function(phrase, condition, codeSniffer) {
            //暂不支持自定义openTag和closeTag
            condition = condition.replace(/(^\{\{|\}\}$)/g, '').trim();
            if (condition.indexOf('=') === 0) {
                return promise.getState(true) ? eval(condition.substring(1)) : '';
            } else {
                var _promise = condition.replace(/^(elseif|else if|\/if|if|else)(.*)/i, function() {
                    var bool = eval(arguments[2]);
                    switch (arguments[1]) {
                    case 'if':
                        promise.push(!!bool);
                        return promise.getState() && !!bool;
                        break;
                    case 'else':
                        return promise.getState() ? promise.get() === false ? true : false : false;
                        break;
                    case 'else if':
                        ;
                    case 'elseif':
                        var old = promise.get();
                        bool = bool && (promise.getState() ? promise.get() === false ? true : false : false);
                        promise.set(bool);
                        return old === false ? bool : false;

                        break;
                    case '/if':
                        promise.pop();
                        return promise.get() && promise.getState();
                        break;
                    }
                }
                );

                if (_promise == 'true') {
                    return codeSniffer;
                }
            }
            return '';
        }
        );
        return res;
    }
}

function render(content, data) {
    data = data || {};
    return parseJs(content, data)();
}

module.exports = function(content, file, conf) {
    if (!content) return '';
    if (content.trim() == '') {
        return content;
    }

    var content = render(content, conf.define);

    return content;
};
<commit_msg>change varible<commit_after>'use strict';
function parseJs(code, define) {
    //js模板解析

    var promise = {
        data: [],
        len: 0,

        get: function() {
            return this.data[this.len - 1];
        },
        getState: function(all) {
            //是否为true
            var l = this.len - 1;
            if (all === true) {
                l++;
            }
            for (var i = 0; i < l; i++) {
                if (!this.data[i])
                    return false;
            }
            return true;
        },
        set: function(state) {
            this.data[this.len - 1] = this.data[this.len - 1] || state;
            return state;
        },
        pop: function() {
            this.len--;
            return this.len > 0 ? this.data.pop() : this.len = 0;
        },
        push: function(state) {
            this.len++;
            return this.data.push(state),
            state;
        }
    };

    return function() {

        var res = code.replace(/(\{\{.*?\}\})(((?!\{\{)[\s\S])*)/g, function(phrase, condition, codeSniffer) {
            //暂不支持自定义openTag和closeTag
            condition = condition.replace(/(^\{\{|\}\}$)/g, '').trim();
            if (condition.indexOf('=') === 0) {
                return promise.getState(true) ? eval('define.' + condition.substring(1)) : '';
            } else {
                var _promise = condition.replace(/^(elseif|else if|\/if|if|else)(.*)/i, function() {
                    var bool = eval(arguments[2]);
                    switch (arguments[1]) {
                    case 'if':
                        promise.push(!!bool);
                        return promise.getState() && !!bool;
                        break;
                    case 'else':
                        return promise.getState() ? promise.get() === false ? true : false : false;
                        break;
                    case 'else if':
                        ;
                    case 'elseif':
                        var old = promise.get();
                        bool = bool && (promise.getState() ? promise.get() === false ? true : false : false);
                        promise.set(bool);
                        return old === false ? bool : false;

                        break;
                    case '/if':
                        promise.pop();
                        return promise.get() && promise.getState();
                        break;
                    }
                }
                );

                if (_promise == 'true') {
                    return codeSniffer;
                }
            }
            return '';
        }
        );
        return res;
    }
}

function render(content, data) {
    data = data || {};
    return parseJs(content, data)();
}

module.exports = function(content, file, conf) {
    if (!content) return '';
    if (content.trim() == '') {
        return content;
    }

    var content = render(content, conf.define);

    return content;
};
<|endoftext|>


<commit_before>define([
    'amber/deploy',
    // --- packages to be deployed begin here ---
    'amber-myappamber/Myappamber'
    // --- packages to be deployed end here ---
], function (amber) {
    return amber;
});
<commit_msg>Delete deploy.js<commit_after><|endoftext|>


<commit_before>var http = require('http');
var args = require('minimist')(process.argv, {
  alias:{
    p: 'port',
    v: 'verbose',
    r: 'root'
  },
  default:{
    port: 80
  },
  boolean: ['verbose'],
  string: ['root']
})

if (!args.root || args.root == "") {
  console.log("\nNo --root specified. Exiting.\n\n");
  process.exit();
}

if (!args.root.startsWith("/")) {
  console.log("\nOnly absolute paths allowed for --root. Exiting.\n\n");
  process.exit();
}

process.on('SIGINT', function() {
  process.exit();
})

var Server = require('./server');
console.log("Root file path: " + args.root);
var server = http.createServer(Server(args));

server.listen(args.port, function() {
  console.log('Server listening on port: ' + args.port);
  if (args.verbose){
    console.log('Verbose flag is on');
  }
})<commit_msg>More cleanup<commit_after>var http = require('http');

// Commandline argument parsing
var args = require('minimist')(process.argv, {
  alias:{
    p: 'port',
    v: 'verbose',
    r: 'root'
  },
  default:{
    port: 80
  },
  boolean: ['verbose'],
  string: ['root']
})

// Make sure a root path is specified
if (!args.root || args.root == "") {
  console.log("\nNo --root specified. Exiting.\n\n");
  process.exit();
}

// Check for absolute root path
if (!args.root.startsWith("/")) {
  console.log("\nOnly absolute paths allowed for --root. Exiting.\n\n");
  process.exit();
}

// Make sure we exit on CTRL-C
process.on('SIGINT', function() {
  process.exit();
})

// Create the HTTP server
var Server = require('./server');
console.log("Root file path: " + args.root);
var server = http.createServer(Server(args));

// Start listening for HTTP requests
server.listen(args.port, function() {
  console.log('Server listening on port: ' + args.port);
  if (args.verbose){
    console.log('Verbose flag is on');
  }
})<|endoftext|>


<commit_before>var JOT = {}
JOT.textarea = document.getElementById ('jot')
JOT.controls = {}
JOT.controls.contrast = document.getElementById ('contrast')

JOT.update = function () {
    document.body.setAttribute ('class', localStorage.classes || '')
    JOT.textarea.value = localStorage.text || ''

    JOT.textarea.focus ()

    JOT.textarea.selectionStart = localStorage.selectionStart || 0
    JOT.textarea.selectionEnd = localStorage.selectionEnd || 0

    JOT.textarea.scrollTop = localStorage.scroll || 0
}
window.addEventListener ('storage', function () {
    // just queue it if we're unfocused
    if (!JOT.focused) {
        JOT.shouldUpdate = true
        return
    }

    JOT.update ()
})

// WINDOW FREEZE ON BLUR MECHANISM
window.addEventListener ('focus', function () { JOT.focus () })
document.addEventListener ('visibilitychange', function () {
    console.log ('vc')
    if (document.visibilityState === 'visible') JOT.focus ()
    else JOT.blur () })
window.addEventListener ('blur', function () { JOT.blur () })
JOT.focus = function () {
    JOT.focused = true
    console.log ('in')

    // if there've been changes, update (see 'storage' listener)
    if (JOT.shouldUpdate) {
        JOT.update ()
        JOT.shouldUpdate = false
    }

    JOT.textarea.disabled = false
}
JOT.blur  = function () {
    JOT.focused = false
    JOT.textarea.disabled = true
}
JOT.update ()

// TEXTAREA SAVE CHANGES MECHANISM
var throttle = function (fn, threshold) {
    var last, deferTimer
    return function () {
        var now = Date.now (),
            args = arguments
        if (last && now < last + threshold) {
            clearTimeout (deferTimer)
            setTimeout (function () {
                last = now
                fn.apply (this, args)
            }, threshold)
        } else {
            last = now
            fn.apply (this, args)
        }
    }
}
JOT.textarea.addEventListener ('keyup', throttle (function () {
    localStorage.text = JOT.textarea.value
}, 200))

// SCROLL SAVE MECHANISM
JOT.textarea.addEventListener ('scroll', throttle (function () {
    localStorage.scroll = JOT.textarea.scrollTop
}, 200))

// SELECTION SAVE MECHANISM
JOT.selectChange = throttle (function () {
    localStorage.selectionStart = JOT.textarea.selectionStart
    localStorage.selectionEnd = JOT.textarea.selectionEnd
}, 200)
document.addEventListener ('select', JOT.selectChange)
document.addEventListener ('selectionchange', JOT.selectChange)

// LIGHTNESS SAVE MECHANISM
JOT.controls.contrast.addEventListener ('click', function () {
    var classes = (localStorage.classes || '').split (/ +/g)

    var index = classes.indexOf ('light')
    if (index === -1) classes.push ('light')
    else classes.splice (classes.indexOf ('light'), 1)

    localStorage.classes = classes.join (' ')
    JOT.update ()
})

JOT.textarea.addEventListener ('keydown', function (ev) {
    if (ev.keyCode === 9) { // don't leave on <Tab>
        ev.preventDefault ()
    }
})
<commit_msg>suppressing ctrl+s shortcut<commit_after>var JOT = {}
JOT.textarea = document.getElementById ('jot')
JOT.controls = {}
JOT.controls.contrast = document.getElementById ('contrast')

JOT.update = function () {
    document.body.setAttribute ('class', localStorage.classes || '')
    JOT.textarea.value = localStorage.text || ''

    JOT.textarea.focus ()

    JOT.textarea.selectionStart = localStorage.selectionStart || 0
    JOT.textarea.selectionEnd = localStorage.selectionEnd || 0

    JOT.textarea.scrollTop = localStorage.scroll || 0
}
window.addEventListener ('storage', function () {
    // just queue it if we're unfocused
    if (!JOT.focused) {
        JOT.shouldUpdate = true
        return
    }

    JOT.update ()
})

// WINDOW FREEZE ON BLUR MECHANISM
window.addEventListener ('focus', function () { JOT.focus () })
document.addEventListener ('visibilitychange', function () {
    console.log ('vc')
    if (document.visibilityState === 'visible') JOT.focus ()
    else JOT.blur () })
window.addEventListener ('blur', function () { JOT.blur () })
JOT.focus = function () {
    JOT.focused = true
    console.log ('in')

    // if there've been changes, update (see 'storage' listener)
    if (JOT.shouldUpdate) {
        JOT.update ()
        JOT.shouldUpdate = false
    }

    JOT.textarea.disabled = false
}
JOT.blur  = function () {
    JOT.focused = false
    JOT.textarea.disabled = true
}
JOT.update ()

// TEXTAREA SAVE CHANGES MECHANISM
var throttle = function (fn, threshold) {
    var last, deferTimer
    return function () {
        var now = Date.now (),
            args = arguments
        if (last && now < last + threshold) {
            clearTimeout (deferTimer)
            setTimeout (function () {
                last = now
                fn.apply (this, args)
            }, threshold)
        } else {
            last = now
            fn.apply (this, args)
        }
    }
}
JOT.textarea.addEventListener ('keyup', throttle (function () {
    localStorage.text = JOT.textarea.value
}, 200))

// SCROLL SAVE MECHANISM
JOT.textarea.addEventListener ('scroll', throttle (function () {
    localStorage.scroll = JOT.textarea.scrollTop
}, 200))

// SELECTION SAVE MECHANISM
JOT.selectChange = throttle (function () {
    localStorage.selectionStart = JOT.textarea.selectionStart
    localStorage.selectionEnd = JOT.textarea.selectionEnd
}, 200)
document.addEventListener ('select', JOT.selectChange)
document.addEventListener ('selectionchange', JOT.selectChange)

// LIGHTNESS SAVE MECHANISM
JOT.controls.contrast.addEventListener ('click', function () {
    var classes = (localStorage.classes || '').split (/ +/g)

    var index = classes.indexOf ('light')
    if (index === -1) classes.push ('light')
    else classes.splice (classes.indexOf ('light'), 1)

    localStorage.classes = classes.join (' ')
    JOT.update ()
})

JOT.textarea.addEventListener ('keydown', function (ev) {
    if (ev.keyCode === 9 || // suppress leave on <Tab>
        ev.ctrlKey && ev.keyCode === 83) {  // suppress Ctrl+S
        ev.preventDefault ()
    }
})
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict'

var util = require('util')
var spawnSync = require('child_process').spawnSync

class Deployer {
  // branch 分支的名称
  // dirname 站点（blog）的路径在哪里
  constructor(branch, dirname) {
    if (!branch) {
      console.log('Please tell me which branch you want to push.');
      console.log('e.g. node deploy.js articles');
      process.exit(0)
    }
    if(!dirname) var dirname = __dirname
    this.root = dirname
  }

  command(c) {
    if (this.path) {
      this[c]()
    } else {
      console.log('This path MUST be here.')
    }
  }

  start() {
    // console.log(process.argv)
    this.comment = process.argv[2] || 'deploy site'
    this['exec-sh'](this['push-github-master']('for test ' + (new Date())))
    this['exec-sh'](this['push-github-articles']('for test ' + (new Date())))
  }

  'exec-sh' (sh) {
    sh.every(function(cwd) {
      if (typeof cwd === 'string' || cwd instanceof String) {
        cwd = cwd.split(' ')
      }
      console.log(`-> ${cwd.join(' ')}`);
      var rlt = spawnSync(cwd.shift(), cwd)
      if (rlt.status === null) {
        console.log(`command cannot found: ${rlt.args[0]}`)
        process.exit(1)
      }
      if (rlt.status === 0) {
        // var stdout = rlt.stdout.toString()
        // console.log(stdout)
        return true
      } else {
        if (rlt.stderr) console.log(rlt.stderr.toString())
        if (rlt.stdout) console.log(rlt.stdout.toString())
        return false
      }
    })
  }

  'push-github-articles' (comment) {
    return [
      `cd ${this.root}`,
      `git add _articles static`,
      ['git', 'commit', '-m', `"${comment}"`],
      `git push origin articles`
    ]
  }

  'push-github-master' (comment) {
    return [
      `cd ${this.root}/public`,
      `git add .`,
      ['git', 'commit', '-m', `"${comment}"`],
      `git push origin master`
    ]
  }
}

(function() {
  'use strict'
  // console.log = function () {}
  var d = new Deployer('articles')
  d.start()
})()
<commit_msg>"for test Sat Nov 14 2015 15:54:52 GMT+0800 (CST)"<commit_after>#!/usr/bin/env node

'use strict'

var util = require('util')
var child_process = require('child_process')
var spawnSync = require('child_process').spawnSync

class Deployer {
  // branch 分支的名称
  // dirname 站点（blog）的路径在哪里
  constructor() {
    if(!dirname) var dirname = __dirname
    this.root = dirname
  }

  command(c) {
    if (this.path) {
      this[c]()
    } else {
      console.log('This path MUST be here.')
    }
  }

  start() {
    // console.log(process.argv)
    this.comment = process.argv[2] || 'deploy site'
    this['exec-sh'](this['push-github-master']('for test ' + (new Date())))
    this['exec-sh'](this['push-github-articles']('for test ' + (new Date())))
  }

  'exec-sh' (sh) {
    sh.every(function(cwd) {
      if (typeof cwd === 'string' || cwd instanceof String) {
        cwd = cwd.split(' ')
      }
      console.log(`-> ${cwd.join(' ')}`);
      var rlt = spawnSync(cwd.shift(), cwd)
      if (rlt.status === null) {
        console.log(`command cannot found: ${rlt.args[0]}`)
        process.exit(1)
      }
      if (rlt.status === 0) {
        // var stdout = rlt.stdout.toString()
        // console.log(stdout)
        return true
      } else {
        if (rlt.stderr) console.log(rlt.stderr.toString())
        if (rlt.stdout) console.log(rlt.stdout.toString())
        return false
      }
    })
  }

  'push-github-articles' (comment) {
    return [
      `cd ${this.root}`,
      `git add _articles static`,
      ['git', 'commit', '-m', `"${comment}"`],
      `git push origin articles`
    ]
  }

  'push-github-master' (comment) {
    return [
      `cd ${this.root}/public`,
      `git add .`,
      ['git', 'commit', '-m', `"${comment}"`],
      `git push origin master`
    ]
  }

}

(function() {
  'use strict'
  // console.log = function () {}
  var d = new Deployer
  d.start()
})()
<|endoftext|>


<commit_before>'use strict';

window.Promise || (window.Promise = new function () {

    function toPromise(thenable) {
        if (isPromise(thenable)) {
            return thenable;
        }
        return new Promise(function (resolve, reject) {
            //execute thenable.then asynchronously
            //github.com/getify/native-promise-only/issues/5
            //github.com/domenic/promises-unwrapping/issues/105
            window.setImmediate(function () {
                try {
                    thenable.then(resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });
        });
    }

    function isPromise(anything) {
        return anything instanceof Promise;
    }

    function isThenable(anything) {
        return Object(anything) === anything &&
               'function' == typeof anything.then;
    }

    function isSettled(promise) {
        return promise._fulfilled || promise._rejected;
    }

    function allSettled(promises) {
        return promises.every(isSettled);
    }

    function defaultOnFulfilled(value) {
        return value;
    }

    function defaultOnRejected(reason) {
        throw reason;
    }

    function call(callback) {
        callback();
    }

    function Promise(resolver) {
        Object.assign(this, {
            _fulfilled: false,
            _rejected: false,
            _value: undefined,
            _reason: undefined,
            _onFulfilled: [],
            _onRejected: []
        });
        this._resolve(resolver);
    }

    Object.assign(Promise, {

        resolve: function (value) {
            if (isThenable(value)) {
                return toPromise(value);
            }
            return new Promise(function (resolve) {
                resolve(value);
            });
        },

        reject: function (reason) {
            return new Promise(function (resolve, reject) {
                reject(reason);
            });
        },

        race: function (promises) {
            return new Promise(function (resolve, reject) {
                promises.forEach(function (promise) {
                    toPromise(promise).then(resolve, reject);
                });
            });
        },

        all: function (promises) {
            return new Promise(function (resolve, reject) {
                var values = [];
                promises = promises.map(toPromise);
                promises.forEach(function (promise, index) {
                    promise.then(
                        function (value) {
                            values[index] = value;
                            if (allSettled(promises)) {
                                resolve(values);
                            }
                        },
                        reject
                    );
                });
            });
        }

    });

    Object.assign(Promise.prototype, {

        _resolve: function (resolver) {

            var promise = this;

            function resolve(value) {
                promise._fulfill(value);
            }

            function reject(reason) {
                promise._reject(reason);
            }

            try {
                resolver(resolve, reject);
            } catch(error) {
                if (!isSettled(promise)) {
                    reject(error);
                }
            }

        },

        _fulfill: function (value) {
            if (!isSettled(this)) {
                this._fulfilled = true;
                this._value = value;
                this._onFulfilled.forEach(call);
                this._clearQueue();
            }
        },

        _reject: function (reason) {
            if (!isSettled(this)) {
                this._rejected = true;
                this._reason = reason;
                this._onRejected.forEach(call);
                this._clearQueue();
            }
        },

        _enqueue: function (onFulfilled, onRejected) {
            this._onFulfilled.push(onFulfilled);
            this._onRejected.push(onRejected);
        },

        _clearQueue: function () {
            this._onFulfilled = [];
            this._onRejected = [];
        },

        then: function (onFulfilled, onRejected) {

            var promise = this;

            return new Promise(function (resolve, reject) {

                onFulfilled = onFulfilled || defaultOnFulfilled;
                onRejected = onRejected || defaultOnRejected;

                function asyncOnFulfilled() {
                    window.setImmediate(function () {
                        var value;
                        try {
                            value = onFulfilled(promise._value);
                        } catch (error) {
                            reject(error);
                            return;
                        }
                        if (isThenable(value)) {
                            toPromise(value).then(resolve, reject);
                        } else {
                            resolve(value);
                        }
                    });
                }

                function asyncOnRejected() {
                    window.setImmediate(function () {
                        var reason;
                        try {
                            reason = onRejected(promise._reason);
                        } catch (error) {
                            reject(error);
                            return;
                        }
                        if (isThenable(reason)) {
                            toPromise(reason).then(resolve, reject);
                        } else {
                            resolve(reason);
                        }
                    });
                }

                if (promise._fulfilled) {
                    asyncOnFulfilled();
                } else if (promise._rejected) {
                    asyncOnRejected();
                } else {
                    promise._enqueue(asyncOnFulfilled, asyncOnRejected);
                }

            });

        },

        'catch': function (onRejected) {
            return this.then(undefined, onRejected);
        }

    });

    return Promise;

});
<commit_msg>Promise polyfill: fix octane/Promise #6, #7, #8<commit_after>'use strict';

window.Promise || (window.Promise = new function () {

    function toPromise(thenable) {
        if (isPromise(thenable)) {
            return thenable;
        }
        return new Promise(function (resolve, reject) {
            //execute thenable.then asynchronously
            //github.com/getify/native-promise-only/issues/5
            //github.com/domenic/promises-unwrapping/issues/105
            window.setImmediate(function () {
                try {
                    thenable.then(resolve, reject);
                } catch (error) {
                    reject(error);
                }
            });
        });
    }

    function isPromise(anything) {
        return anything instanceof Promise;
    }

    function isThenable(anything) {
        return Object(anything) === anything &&
               'function' == typeof anything.then;
    }

    function isSettled(promise) {
        return promise._fulfilled || promise._rejected;
    }

    function defaultOnFulfilled(value) {
        return value;
    }

    function defaultOnRejected(reason) {
        throw reason;
    }

    function call(callback) {
        callback();
    }

    function dive(thenable, onFulfilled, onRejected) {
        function interimOnFulfilled(value) {
            if (isThenable(value)) {
                toPromise(value).then(interimOnFulfilled, interimOnRejected);
            } else {
                onFulfilled(value);
            }
        }
        function interimOnRejected(reason) {
            if (isThenable(reason)) {
                toPromise(reason).then(interimOnFulfilled, interimOnRejected);
            } else {
                onRejected(reason);
            }
        }
        toPromise(thenable).then(interimOnFulfilled, interimOnRejected);
    }

    function Promise(resolver) {
        Object.assign(this, {
            _fulfilled: false,
            _rejected: false,
            _value: undefined,
            _reason: undefined,
            _onFulfilled: [],
            _onRejected: []
        });
        this._resolve(resolver);
    }

    Object.assign(Promise, {

        resolve: function (value) {
            if (isThenable(value)) {
                return toPromise(value);
            }
            return new Promise(function (resolve) {
                resolve(value);
            });
        },

        reject: function (reason) {
            return new Promise(function (resolve, reject) {
                reject(reason);
            });
        },

        race: function (values) {
            return new Promise(function (resolve, reject) {
                var value,
                    length = values.length,
                    i = 0;
                while (i < length) {
                    value = values[i];
                    if (isThenable(value)) {
                        dive(value, resolve, reject);
                    } else {
                        resolve(value);
                    }
                    i++;
                }
            });
        },

        all: function (values) {
            return new Promise(function (resolve, reject) {
                var thenables = 0,
                    fulfilled = 0,
                    value,
                    length = values.length,
                    i = 0;
                values = values.slice(0);
                while (i < length) {
                    value = values[i];
                    if (isThenable(value)) {
                        thenables++;
                        dive(
                            value,
                            function (index) {
                                return function (value) {
                                    values[index] = value;
                                    fulfilled++;
                                    if (fulfilled == thenables) {
                                        resolve(values);
                                    }
                                };
                            }(i),
                            reject
                        );
                    } else {
                        //[1, , 3] → [1, undefined, 3]
                        values[i] = value;
                    }
                    i++;
                }
                if (!thenables) {
                    resolve(values);
                }
            });
        }

    });

    Object.assign(Promise.prototype, {

        _resolve: function (resolver) {

            var promise = this;

            function resolve(value) {
                promise._fulfill(value);
            }

            function reject(reason) {
                promise._reject(reason);
            }

            try {
                resolver(resolve, reject);
            } catch(error) {
                if (!isSettled(promise)) {
                    reject(error);
                }
            }

        },

        _fulfill: function (value) {
            if (!isSettled(this)) {
                this._fulfilled = true;
                this._value = value;
                this._onFulfilled.forEach(call);
                this._clearQueue();
            }
        },

        _reject: function (reason) {
            if (!isSettled(this)) {
                this._rejected = true;
                this._reason = reason;
                this._onRejected.forEach(call);
                this._clearQueue();
            }
        },

        _enqueue: function (onFulfilled, onRejected) {
            this._onFulfilled.push(onFulfilled);
            this._onRejected.push(onRejected);
        },

        _clearQueue: function () {
            this._onFulfilled = [];
            this._onRejected = [];
        },

        then: function (onFulfilled, onRejected) {

            var promise = this;

            return new Promise(function (resolve, reject) {

                onFulfilled = onFulfilled || defaultOnFulfilled;
                onRejected = onRejected || defaultOnRejected;

                function asyncOnFulfilled() {
                    window.setImmediate(function () {
                        var value;
                        try {
                            value = onFulfilled(promise._value);
                        } catch (error) {
                            reject(error);
                            return;
                        }
                        if (isThenable(value)) {
                            toPromise(value).then(resolve, reject);
                        } else {
                            resolve(value);
                        }
                    });
                }

                function asyncOnRejected() {
                    window.setImmediate(function () {
                        var reason;
                        try {
                            reason = onRejected(promise._reason);
                        } catch (error) {
                            reject(error);
                            return;
                        }
                        if (isThenable(reason)) {
                            toPromise(reason).then(resolve, reject);
                        } else {
                            resolve(reason);
                        }
                    });
                }

                if (promise._fulfilled) {
                    asyncOnFulfilled();
                } else if (promise._rejected) {
                    asyncOnRejected();
                } else {
                    promise._enqueue(asyncOnFulfilled, asyncOnRejected);
                }

            });

        },

        'catch': function (onRejected) {
            return this.then(undefined, onRejected);
        }

    });

    return Promise;

});
<|endoftext|>


<commit_before>import React, {Component, PropTypes, View, Animated, Dimensions} from 'react-native';

const Screen = Dimensions.get('window');

class Notification extends Component {
  static propTypes = {
    children: PropTypes.node,
    visible: PropTypes.bool,
  }

  static defaultProps = {
    visible: true,
  }

  constructor(props) {
    super(props);

    this.state = {
      opacityValue: new Animated.Value(Notification.minOpacity),
    };
  }

  componentDidMount() {
    if (this.props.visible) {
      this.fadeIn();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.visible && !this.props.visible) {
      this.fadeIn();
    } else {
      if (!nextProps.visible && this.props.visible) {
        this.fadeOut();
      }
    }
  }

  shouldComponentUpdate(nextProps) {
    /* if (this.props.message !== nextProps.message) {
      return true;
    }*/

    if (this.props.visible !== nextProps.visible) {
      return true;
    }

    if (this.state.opacityValue.__getValue() !== this.state.opacityValue.__getValue()) {
      return true;
    }

    return false;
  }

  static fadeTime = 500
  static minOpacity = 0.0
  static maxOpacity = 0.9

  static styles = {
    container: {
      position: 'absolute',
      top: Screen.height - 35 - 20,
      width: Screen.width - 80,
      left: 40,
      right: 40,
      backgroundColor: '#444',
      alignItems: 'center',
      padding: 6,
      opacity: Notification.minOpacity,
      borderRadius: 12,
      shadowColor: '#000',
      shadowOpacity: 0.5,
      shadowRadius: 1,
      shadowOffset: {
        width: 0,
        height: 1,
      },
    },
    message: {
      color: '#fff',
      fontSize: 12,
      textAlign: 'center',
    },
  }

  fadeIn() {
    Animated.timing(this.state.opacityValue, {
      duration: Notification.fadeTime,
      toValue: Notification.maxOpacity,
    }).start();
  }

  fadeOut() {
    Animated.timing(this.state.opacityValue, {
      duration: Notification.fadeTime,
      toValue: Notification.minOpacity,
    }).start();
  }

  render() {
    const message = this.props.children;

    if (!message) {
      return null;
    }

    return (
      <Animated.View style={[Notification.styles.container, {opacity: this.state.opacityValue}]}>
        <Text style={Notification.styles.message}>{message}</Text>
      </Animated.View>
    );
  }
}

export default Notification;
<commit_msg>Update import section<commit_after>import React, {
  Component,
  PropTypes,
  View,
  Animated,
  Dimensions,
} from 'react-native';

const Screen = Dimensions.get('window');

class Notification extends Component {
  static propTypes = {
    children: PropTypes.node,
    visible: PropTypes.bool,
  }

  static defaultProps = {
    visible: true,
  }

  constructor(props) {
    super(props);

    this.state = {
      opacityValue: new Animated.Value(Notification.minOpacity),
    };
  }

  componentDidMount() {
    if (this.props.visible) {
      this.fadeIn();
    }
  }

  componentWillReceiveProps(nextProps) {
    if (nextProps.visible && !this.props.visible) {
      this.fadeIn();
    } else {
      if (!nextProps.visible && this.props.visible) {
        this.fadeOut();
      }
    }
  }

  shouldComponentUpdate(nextProps) {
    /* if (this.props.message !== nextProps.message) {
      return true;
    }*/

    if (this.props.visible !== nextProps.visible) {
      return true;
    }

    if (this.state.opacityValue.__getValue() !== this.state.opacityValue.__getValue()) {
      return true;
    }

    return false;
  }

  static fadeTime = 500
  static minOpacity = 0.0
  static maxOpacity = 0.9

  static styles = {
    container: {
      position: 'absolute',
      top: Screen.height - 35 - 20,
      width: Screen.width - 80,
      left: 40,
      right: 40,
      backgroundColor: '#444',
      alignItems: 'center',
      padding: 6,
      opacity: Notification.minOpacity,
      borderRadius: 12,
      shadowColor: '#000',
      shadowOpacity: 0.5,
      shadowRadius: 1,
      shadowOffset: {
        width: 0,
        height: 1,
      },
    },
    message: {
      color: '#fff',
      fontSize: 12,
      textAlign: 'center',
    },
  }

  fadeIn() {
    Animated.timing(this.state.opacityValue, {
      duration: Notification.fadeTime,
      toValue: Notification.maxOpacity,
    }).start();
  }

  fadeOut() {
    Animated.timing(this.state.opacityValue, {
      duration: Notification.fadeTime,
      toValue: Notification.minOpacity,
    }).start();
  }

  render() {
    const message = this.props.children;

    if (!message) {
      return null;
    }

    return (
      <Animated.View style={[Notification.styles.container, {opacity: this.state.opacityValue}]}>
        <Text style={Notification.styles.message}>{message}</Text>
      </Animated.View>
    );
  }
}

export default Notification;
<|endoftext|>


<commit_before>var events = require('events')
var util = require('util')
var protocol = require('./lib/protocol')
var bufferEquals = require('buffer-equals')
var sodium = require('sodium').api
var tree = require('flat-tree')
var crypto = require('crypto')
var bitfield = require('bitfield')
var eos = require('end-of-stream')
var map = require('numeric-id-map')

var HASH_NONE = new Buffer([0]) // enum to indicate no buffer
var HASH_DUPLICATE = new Buffer([1]) // enum to indicate buffer is a dup

module.exports = Peervision

function Peervision (id, opts) {
  if (!(this instanceof Peervision)) return new Peervision(id, opts)

  this.keypair = id ? {publicKey: id} : sodium.crypto_sign_keypair()
  this.id = this.keypair.publicKey

  this.tree = []
  this.signatures = []
  this.digests = []
  this.blocks = []
  this.have = bitfield(1, {grow: Infinity})
  this.peers = []

  this._pendingRequests = map()
  this._requests = map()
  this._head = -1

  events.EventEmitter.call(this)
}

util.inherits(Peervision, events.EventEmitter)

Peervision.prototype.createStream = function () {
  var self = this

  var stream = protocol({
    protocol: 1,
    blocks: this.have.buffer.slice(0, Math.ceil(this.blocks.length / 8))
  })

  stream.head = -1
  stream.blocks = null

  stream.on('handshake', function (handshake) {
    stream.blocks = bitfield(handshake.blocks ? handshake.blocks : 1, {grow: Infinity})
    stream.head = getHead(stream.blocks)
    self._update()
  })

  stream.on('have', function (have) {
    if (have.index > stream.head) stream.head = have.index
    stream.blocks.set(have.index)
    self._update()
  })

  stream.on('request', function (request) {
    var hashes = []
    for (var i = 0; i < request.tree.length; i++) {
      hashes[i] = self._getTree(request.tree[i])
    }

    stream.response({
      id: request.id,
      tree: hashes,
      signature: request.signature ? self.signatures[request.index] : null,
      data: request.digest ? self.digests[request.index] : self.blocks[request.index]
    })
  })

  this.peers.push(stream)
  eos(stream, function () {
    self.peers.splice(self.peers.indexOf(stream), 1)
  })

  return stream
}

Peervision.prototype.get = function (index, cb) {
  if (!cb) cb = noop
  var i = this._getPeer(index)

  if (i === -1) {
    this._pendingRequests.add([index, cb])
    return
  }

  this._get(this.peers[i], index, cb)
}

Peervision.prototype._get = function (peer, index, cb) {
  if (this.blocks[index]) return cb(null, this.blocks[index])

  var self = this
  var head = this.digests.length - 1

  if (peer.head <= head || index <= head) send()
  else this._getHead(peer, send) // TODO: if the piece we need is peer.head we can save a roundtrip

  function send (err) {
    if (err) return cb(err)
    if (self.blocks[index]) return cb(null, self.blocks[index])

    var treeIndex = tree.index(0, index)

    var roots = tree.fullRoots(treeIndex)
    var treeCache = []
    var treeIndexes = []
    var treeDups = []

    var needed = treeIndex
    var checksum

    for (var i = 0; i < roots.length; i++) {
      pushIndex(self, roots[i], treeIndexes, treeCache)
    }

    while (!(checksum = self.tree[needed])) {
      var sibling = tree.sibling(needed)
      pushIndex(self, sibling, treeIndexes, treeCache, treeDups)
      needed = tree.parent(needed)
    }

    peer.request({index: index, tree: treeIndexes, digest: false}, function (err, res) {
      if (err) return cb(err)

      treeCache = mergeTree(treeCache, res, treeDups)
      needed = treeIndex

      var digest = createHash().update(res.data).digest()
      var treeDigest = treeHash(treeCache, roots.length, digest)
      var sum = treeDigest

      for (var i = roots.length; i < treeCache.length; i++) {
        var sibling = tree.sibling(needed)
        var siblingSum = treeCache[i]

        if (needed > sibling) { // swap so "sum" is always left sibling
          var tmp = sum
          sum = siblingSum
          siblingSum = tmp
        }

        sum = createHash().update(sum).update(siblingSum).digest()
        needed = tree.parent(needed)

        // quick hash to push the possible parents to the response so they'll get stored on validation
        res.tree.push(sum)
        treeIndexes.push(needed)
      }

      if (!bufferEquals(sum, checksum)) return cb(new Error('Tree checksum mismatch'))

      // everything is fine - store the response

      for (var i = 0; i < treeIndexes.length; i++) self.tree[treeIndexes[i]] = res.tree[i]
      self.tree[treeIndex] = treeDigest
      self.blocks[index] = res.data
      self.digests[index] = digest
      self.signatures[index] = res.signature
      self._have(index)

      cb(null, res.data)
    })
  }
}

Peervision.prototype._getHead = function (peer, cb) {
  var self = this
  var peerHead = peer.head
  var treeHead = tree.index(0, peerHead)
  var roots = tree.fullRoots(treeHead)
  var treeCache = []
  var treeIndexes = []
  var treeDups = []

  var prevRoots = tree.fullRoots(tree.index(0, Math.max(0, this.digests.length - 1)))
  var prevRootParents = []

  for (var i = 0; i < roots.length; i++) {
    pushIndex(this, roots[i], treeIndexes, treeCache)
  }

  treeIndexes.push(treeHead)
  treeCache.push(HASH_NONE)

  // filter out dups
  var filter = []
  for (var j = 0; j < prevRoots.length; j++) {
    if (roots.indexOf(prevRoots[j]) === -1) filter.push(prevRoots[j])
  }
  prevRoots = filter

  // migrate from old roots to new roots
  for (var j = 0; j < prevRoots.length; j++) {
    var needed = prevRoots[j]
    var root = -1
    while ((root = roots.indexOf(needed)) === -1) {
      var sibling = tree.sibling(needed)
      pushIndex(this, sibling, treeIndexes, treeCache, treeDups)
      needed = tree.parent(needed)
    }
    prevRootParents.push(root)
  }

  peer.request({index: peerHead, tree: treeIndexes, digest: false, signature: true}, function (err, res) {
    if (self.blocks.length > peerHead) return cb() // this request is no longer relevant
    if (err) return cb(err)

    mergeTree(treeCache, res, treeDups)

    var peerTreeDigest = treeCache[roots.length]
    var signed = sodium.crypto_sign_verify_detached(res.signature, peerTreeDigest, self.id)
    if (!signed) return cb(new Error('Tree signature is invalid'))

    var digest = createHash().update(res.data).digest()
    var treeDigest = treeHash(treeCache, roots.length, digest)

    if (!bufferEquals(treeDigest, peerTreeDigest)) return cb(new Error('Tree checksum mismatch'))

    // haxx - this is mostly duplicate code :/
    needed = prevRoots.length ? prevRoots.shift() : -1

    var sum = self.tree[needed]
    for (var i = roots.length + 1; i < treeCache.length; i++) {
      if (needed !== roots[prevRootParents[0]]) {
        var sibling = tree.sibling(needed)
        var siblingSum = treeCache[i]

        if (needed > sibling) { // swap so "sum" is always left sibling
          var tmp = sum
          sum = siblingSum
          siblingSum = tmp
        }
        sum = createHash().update(sum).update(siblingSum).digest()
        needed = tree.parent(needed)
      }

      // quick hash to push the possible parents to the response so they'll get stored on validation
      res.tree.push(sum)
      treeIndexes.push(needed)

      if (needed === roots[prevRootParents[0]]) {
        var cached = treeCache[prevRootParents.shift()]
        if (!bufferEquals(cached, sum)) {
          return cb(new Error('Tree checksum mismatch'))
        }
        needed = prevRoots.length ? prevRoots.shift() : -1
        sum = self.tree[needed]
      }
    }

    if (needed !== -1) return cb(new Error('Tree checksum mismatch'))

    // everything is fine - store the response

    for (var i = 0; i < treeIndexes.length; i++) self.tree[treeIndexes[i]] = res.tree[i]
    self.blocks[peerHead] = res.data
    self.digests[peerHead] = digest
    self.signatures[peerHead] = res.signature

    self._have(peerHead)

    cb(null)
  })
}

Peervision.prototype._getPeer = function (index) {
  var selected = -1
  var found = 1

  for (var i = 0; i < this.peers.length; i++) {
    var p = this.peers[i]
    if (p && p.blocks && p.blocks.get(index)) {
      if (Math.random() < (1 / found++)) selected = i
    }
  }

  return selected
}

Peervision.prototype.append = function (block, cb) {
  if (!this.keypair.secretKey) throw new Error('Only the producer can append chunks')

  var index = this.blocks.length
  var treeIndex = tree.index(0, index)
  var digest = createHash().update(block).digest()
  var roots = tree.fullRoots(treeIndex)

  var hash = createHash()
  hash.update(digest)
  for (var i = 0; i < roots.length; i++) {
    hash.update(this._getTree(roots[i]))
  }

  var treeDigest = hash.digest()
  var signature = sodium.crypto_sign_detached(treeDigest, this.keypair.secretKey)

  this.blocks[index] = block
  this.digests[index] = digest
  this.signatures[index] = signature
  this.tree[treeIndex] = treeDigest

  this._have(index)

  if (cb) cb()
}

Peervision.prototype._have = function (index) {
  this.have.set(index, true)
  for (var i = 0; i < this.peers.length; i++) {
    this.peers[i].have({index: index})
  }

  if (index > this._head) {
    this._head = index
    this.emit('head', index)
  }
}

Peervision.prototype._update = function () {
  if (!this._pendingRequests.length) return

  for (var i = 0; i < this._pendingRequests.length; i++) {
    var pair = this._pendingRequests.get(i)
    if (!pair) continue

    var index = pair[0]
    var cb = pair[1]

    var peer = this._getPeer(index)
    if (peer === -1) continue

    this._pendingRequests.remove(i)
    this._get(this.peers[peer], index, cb)
  }
}

Peervision.prototype._getTree = function (index) {
  if (index === -1) throw new Error('Invalid index')
  if (this.tree[index]) return this.tree[index]

  var hash = createHash()
    .update(this._getTree(tree.leftChild(index)))
    .update(this._getTree(tree.rightChild(index)))
    .digest()

  this.tree[index] = hash
  return hash
}

function noop () {}

function pushIndex (self, index, treeIndexes, treeCache, treeDups) {
  if (treeDups) {
    var i = treeIndexes.indexOf(index)
    if (i > -1) {
      treeCache.push(HASH_DUPLICATE)
      treeDups.push(i)
      return
    }
  }

  var hash = self.tree[index]

  if (!hash) {
    treeIndexes.push(index)
    treeCache.push(HASH_NONE)
  } else {
    treeCache.push(hash)
  }
}

function mergeTree (tree, res, dups) {
  var offset = 0
  var dupsOffset = 0

  for (var i = 0; i < tree.length; i++) {
    if (tree[i] === HASH_NONE) tree[i] = res.tree[offset++]
    if (tree[i] === HASH_DUPLICATE) tree[i] = res.tree[dups[dupsOffset++]]
  }

  return tree
}

function treeHash (roots, end, digest) {
  var hash = createHash()
  hash.update(digest)
  for (var i = 0; i < end; i++) hash.update(roots[i])
  return hash.digest()
}

function createHash () {
  return crypto.createHash('sha256')
}

function getHead (bitfield) {
  var end = bitfield.buffer.length * 8
  var max = -1
  for (var i = end - 8; i < end; i++) {
    if (bitfield.get(i)) max = i
  }
  return max
}
<commit_msg>add upload and download events<commit_after>var events = require('events')
var util = require('util')
var protocol = require('./lib/protocol')
var bufferEquals = require('buffer-equals')
var sodium = require('sodium').api
var tree = require('flat-tree')
var crypto = require('crypto')
var bitfield = require('bitfield')
var eos = require('end-of-stream')
var map = require('numeric-id-map')

var HASH_NONE = new Buffer([0]) // enum to indicate no buffer
var HASH_DUPLICATE = new Buffer([1]) // enum to indicate buffer is a dup

module.exports = Peervision

function Peervision (id, opts) {
  if (!(this instanceof Peervision)) return new Peervision(id, opts)

  this.keypair = id ? {publicKey: id} : sodium.crypto_sign_keypair()
  this.id = this.keypair.publicKey

  this.tree = []
  this.signatures = []
  this.digests = []
  this.blocks = []
  this.have = bitfield(1, {grow: Infinity})
  this.peers = []

  this._pendingRequests = map()
  this._requests = map()
  this._head = -1

  events.EventEmitter.call(this)
}

util.inherits(Peervision, events.EventEmitter)

Peervision.prototype.createStream = function () {
  var self = this

  var stream = protocol({
    protocol: 1,
    blocks: this.have.buffer.slice(0, Math.ceil(this.blocks.length / 8))
  })

  stream.head = -1
  stream.blocks = null

  stream.on('handshake', function (handshake) {
    stream.blocks = bitfield(handshake.blocks ? handshake.blocks : 1, {grow: Infinity})
    stream.head = getHead(stream.blocks)
    self._update()
  })

  stream.on('have', function (have) {
    if (have.index > stream.head) stream.head = have.index
    stream.blocks.set(have.index)
    self._update()
  })

  stream.on('request', function (request) {
    var hashes = []
    for (var i = 0; i < request.tree.length; i++) {
      hashes[i] = self._getTree(request.tree[i])
    }

    if (!request.digest) {
      self.emit('upload', request.index, self.blocks[request.index])
    }

    stream.response({
      id: request.id,
      tree: hashes,
      signature: request.signature ? self.signatures[request.index] : null,
      data: request.digest ? self.digests[request.index] : self.blocks[request.index]
    })
  })

  this.peers.push(stream)
  eos(stream, function () {
    self.peers.splice(self.peers.indexOf(stream), 1)
  })

  return stream
}

Peervision.prototype.get = function (index, cb) {
  if (!cb) cb = noop
  var i = this._getPeer(index)

  if (i === -1) {
    this._pendingRequests.add([index, cb])
    return
  }

  this._get(this.peers[i], index, cb)
}

Peervision.prototype._get = function (peer, index, cb) {
  if (this.blocks[index]) return cb(null, this.blocks[index])

  var self = this
  var head = this.digests.length - 1

  if (peer.head <= head || index <= head) send()
  else this._getHead(peer, send) // TODO: if the piece we need is peer.head we can save a roundtrip

  function send (err) {
    if (err) return cb(err)
    if (self.blocks[index]) return cb(null, self.blocks[index])

    var treeIndex = tree.index(0, index)

    var roots = tree.fullRoots(treeIndex)
    var treeCache = []
    var treeIndexes = []
    var treeDups = []

    var needed = treeIndex
    var checksum

    for (var i = 0; i < roots.length; i++) {
      pushIndex(self, roots[i], treeIndexes, treeCache)
    }

    while (!(checksum = self.tree[needed])) {
      var sibling = tree.sibling(needed)
      pushIndex(self, sibling, treeIndexes, treeCache, treeDups)
      needed = tree.parent(needed)
    }

    peer.request({index: index, tree: treeIndexes, digest: false}, function (err, res) {
      if (err) return cb(err)

      treeCache = mergeTree(treeCache, res, treeDups)
      needed = treeIndex

      var digest = createHash().update(res.data).digest()
      var treeDigest = treeHash(treeCache, roots.length, digest)
      var sum = treeDigest

      for (var i = roots.length; i < treeCache.length; i++) {
        var sibling = tree.sibling(needed)
        var siblingSum = treeCache[i]

        if (needed > sibling) { // swap so "sum" is always left sibling
          var tmp = sum
          sum = siblingSum
          siblingSum = tmp
        }

        sum = createHash().update(sum).update(siblingSum).digest()
        needed = tree.parent(needed)

        // quick hash to push the possible parents to the response so they'll get stored on validation
        res.tree.push(sum)
        treeIndexes.push(needed)
      }

      if (!bufferEquals(sum, checksum)) return cb(new Error('Tree checksum mismatch'))

      // everything is fine - store the response

      for (var i = 0; i < treeIndexes.length; i++) self.tree[treeIndexes[i]] = res.tree[i]
      self.tree[treeIndex] = treeDigest
      self.blocks[index] = res.data
      self.digests[index] = digest
      self.signatures[index] = res.signature
      self._have(index)
      self.emit('download', index, res.data)

      cb(null, res.data)
    })
  }
}

Peervision.prototype._getHead = function (peer, cb) {
  var self = this
  var peerHead = peer.head
  var treeHead = tree.index(0, peerHead)
  var roots = tree.fullRoots(treeHead)
  var treeCache = []
  var treeIndexes = []
  var treeDups = []

  var prevRoots = tree.fullRoots(tree.index(0, Math.max(0, this.digests.length - 1)))
  var prevRootParents = []

  for (var i = 0; i < roots.length; i++) {
    pushIndex(this, roots[i], treeIndexes, treeCache)
  }

  treeIndexes.push(treeHead)
  treeCache.push(HASH_NONE)

  // filter out dups
  var filter = []
  for (var j = 0; j < prevRoots.length; j++) {
    if (roots.indexOf(prevRoots[j]) === -1) filter.push(prevRoots[j])
  }
  prevRoots = filter

  // migrate from old roots to new roots
  for (var j = 0; j < prevRoots.length; j++) {
    var needed = prevRoots[j]
    var root = -1
    while ((root = roots.indexOf(needed)) === -1) {
      var sibling = tree.sibling(needed)
      pushIndex(this, sibling, treeIndexes, treeCache, treeDups)
      needed = tree.parent(needed)
    }
    prevRootParents.push(root)
  }

  peer.request({index: peerHead, tree: treeIndexes, digest: false, signature: true}, function (err, res) {
    if (self.blocks.length > peerHead) return cb() // this request is no longer relevant
    if (err) return cb(err)

    mergeTree(treeCache, res, treeDups)

    var peerTreeDigest = treeCache[roots.length]
    var signed = sodium.crypto_sign_verify_detached(res.signature, peerTreeDigest, self.id)
    if (!signed) return cb(new Error('Tree signature is invalid'))

    var digest = createHash().update(res.data).digest()
    var treeDigest = treeHash(treeCache, roots.length, digest)

    if (!bufferEquals(treeDigest, peerTreeDigest)) return cb(new Error('Tree checksum mismatch'))

    // haxx - this is mostly duplicate code :/
    needed = prevRoots.length ? prevRoots.shift() : -1

    var sum = self.tree[needed]
    for (var i = roots.length + 1; i < treeCache.length; i++) {
      if (needed !== roots[prevRootParents[0]]) {
        var sibling = tree.sibling(needed)
        var siblingSum = treeCache[i]

        if (needed > sibling) { // swap so "sum" is always left sibling
          var tmp = sum
          sum = siblingSum
          siblingSum = tmp
        }
        sum = createHash().update(sum).update(siblingSum).digest()
        needed = tree.parent(needed)
      }

      // quick hash to push the possible parents to the response so they'll get stored on validation
      res.tree.push(sum)
      treeIndexes.push(needed)

      if (needed === roots[prevRootParents[0]]) {
        var cached = treeCache[prevRootParents.shift()]
        if (!bufferEquals(cached, sum)) {
          return cb(new Error('Tree checksum mismatch'))
        }
        needed = prevRoots.length ? prevRoots.shift() : -1
        sum = self.tree[needed]
      }
    }

    if (needed !== -1) return cb(new Error('Tree checksum mismatch'))

    // everything is fine - store the response

    for (var i = 0; i < treeIndexes.length; i++) self.tree[treeIndexes[i]] = res.tree[i]
    self.blocks[peerHead] = res.data
    self.digests[peerHead] = digest
    self.signatures[peerHead] = res.signature
    self.emit('download', peerHead, res.data)

    self._have(peerHead)

    cb(null)
  })
}

Peervision.prototype._getPeer = function (index) {
  var selected = -1
  var found = 1

  for (var i = 0; i < this.peers.length; i++) {
    var p = this.peers[i]
    if (p && p.blocks && p.blocks.get(index)) {
      if (Math.random() < (1 / found++)) selected = i
    }
  }

  return selected
}

Peervision.prototype.append = function (block, cb) {
  if (!this.keypair.secretKey) throw new Error('Only the producer can append chunks')

  var index = this.blocks.length
  var treeIndex = tree.index(0, index)
  var digest = createHash().update(block).digest()
  var roots = tree.fullRoots(treeIndex)

  var hash = createHash()
  hash.update(digest)
  for (var i = 0; i < roots.length; i++) {
    hash.update(this._getTree(roots[i]))
  }

  var treeDigest = hash.digest()
  var signature = sodium.crypto_sign_detached(treeDigest, this.keypair.secretKey)

  this.blocks[index] = block
  this.digests[index] = digest
  this.signatures[index] = signature
  this.tree[treeIndex] = treeDigest

  this._have(index)

  if (cb) cb()
}

Peervision.prototype._have = function (index) {
  this.have.set(index, true)
  for (var i = 0; i < this.peers.length; i++) {
    this.peers[i].have({index: index})
  }

  if (index > this._head) {
    this._head = index
    this.emit('head', index)
  }
}

Peervision.prototype._update = function () {
  if (!this._pendingRequests.length) return

  for (var i = 0; i < this._pendingRequests.length; i++) {
    var pair = this._pendingRequests.get(i)
    if (!pair) continue

    var index = pair[0]
    var cb = pair[1]

    var peer = this._getPeer(index)
    if (peer === -1) continue

    this._pendingRequests.remove(i)
    this._get(this.peers[peer], index, cb)
  }
}

Peervision.prototype._getTree = function (index) {
  if (index === -1) throw new Error('Invalid index')
  if (this.tree[index]) return this.tree[index]

  var hash = createHash()
    .update(this._getTree(tree.leftChild(index)))
    .update(this._getTree(tree.rightChild(index)))
    .digest()

  this.tree[index] = hash
  return hash
}

function noop () {}

function pushIndex (self, index, treeIndexes, treeCache, treeDups) {
  if (treeDups) {
    var i = treeIndexes.indexOf(index)
    if (i > -1) {
      treeCache.push(HASH_DUPLICATE)
      treeDups.push(i)
      return
    }
  }

  var hash = self.tree[index]

  if (!hash) {
    treeIndexes.push(index)
    treeCache.push(HASH_NONE)
  } else {
    treeCache.push(hash)
  }
}

function mergeTree (tree, res, dups) {
  var offset = 0
  var dupsOffset = 0

  for (var i = 0; i < tree.length; i++) {
    if (tree[i] === HASH_NONE) tree[i] = res.tree[offset++]
    if (tree[i] === HASH_DUPLICATE) tree[i] = res.tree[dups[dupsOffset++]]
  }

  return tree
}

function treeHash (roots, end, digest) {
  var hash = createHash()
  hash.update(digest)
  for (var i = 0; i < end; i++) hash.update(roots[i])
  return hash.digest()
}

function createHash () {
  return crypto.createHash('sha256')
}

function getHead (bitfield) {
  var end = bitfield.buffer.length * 8
  var max = -1
  for (var i = end - 8; i < end; i++) {
    if (bitfield.get(i)) max = i
  }
  return max
}
<|endoftext|>