<commit_before>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var LENGTH_ARRAYS = {};

function TChannelStatsd(channel, statsd) {
    if (!(this instanceof TChannelStatsd)) {
        return new TChannelStatsd(channel, statsd);
    }

    var self = this;
    self.statsd = statsd;
    self.channel = channel;

    self.channel.statEvent.on(onStat);

    function onStat(stat) {
        self.onStat(stat);
    }
}

function clean(str, field) {
    var copy;

    if (!str) {
        return field;
    }

    copy = LENGTH_ARRAYS[str.length];
    if (!copy) {
        copy = LENGTH_ARRAYS[str.length] = [];
    }

    for (var i = 0; i < str.length; i++) {
        var char = str[i];

        if (char === ':' ||
            char === '/' ||
            char === '.' ||
            char === '{' ||
            char === '}'
        ) {
            copy[i] = '-';
        } else {
            copy[i] = char;
        }
    }

    return copy.join('');
}

function cleanHostPort(str, field) {
    var copy;

    if (!str) {
        return field;
    }

    var length = str.indexOf(':');

    copy = LENGTH_ARRAYS[length];
    if (!copy) {
        copy = LENGTH_ARRAYS[length] = [];
    }
    for (var i = 0; i < length; i++) {
        var char = str[i];

        if (char === '/' ||
            char === '.' ||
            char === '{' ||
            char === '}'
        ) {
            copy[i] = '-';
        } else {
            copy[i] = char;
        }
    }

    return copy.join('');
}

function getKey(stat) {
    /*eslint complexity: [2, 50]*/
    var prefix = 'tchannel.' + stat.name;
    switch (stat.name) {
        // outbound
        case 'outbound.calls.sent':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint');

        case 'outbound.calls.success':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint');

        case 'outbound.calls.system-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'outbound.calls.per-attempt.system-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'type') + '.' +
                stat.tags['retry-count'];

        case 'outbound.calls.operational-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'outbound.calls.per-attempt.operational-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'type') + '.' +
                stat.tags['retry-count'];

        case 'outbound.calls.app-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'outbound.calls.per-attempt.app-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'type') + '.' +
                stat.tags['retry-count'];

        case 'outbound.calls.retries':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                stat.tags['retry-count'];

        case 'outbound.request.size':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.targetService, 'no-target-service') + '.' +
                clean(stat.tags.targetEndpoint, 'no-endpoint');

        case 'outbound.response.size':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint');

        case 'outbound.calls.latency':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint');

        case 'outbound.calls.per-attempt-latency':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                stat.tags['retry-count'];

        // inbound
        case 'inbound.calls.recvd':
            return prefix + '.' +
                clean(stat.tags.callingService, 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        case 'inbound.calls.success':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        case 'inbound.calls.system-errors':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'inbound.calls.app-errors':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'inbound.request.size':
            return prefix + '.' +
                clean(stat.tags.callingService, 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        case 'inbound.response.size':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        case 'inbound.protocol-errors':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'peer-host-port');

        case 'inbound.calls.latency':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        // connection
        case 'connections.active':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port');

        case 'connections.initiated':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port');

        case 'connections.connect-errors':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port');

        case 'connections.accepted':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port');

        case 'connections.accept-errors':
            return prefix + '.' +
                cleanHostPort(stat.tags['host-port'], 'no-host-port');

        case 'connections.errors':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'connections.closed':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port') + '.' +
                clean(stat.tags.reason, 'no-reason');

        case 'connections.bytes-sent':
            return prefix + '.' +
                cleanHostPort(stat.tags.peerHostPort, 'no-peer-host-port');

        case 'connections.bytes-recvd':
            return prefix + '.' +
                cleanHostPort(stat.tags.peerHostPort, 'no-peer-host-port');

        // other types
        default:
            return 'tchannel.bad-stat-object';
    }
}

TChannelStatsd.prototype.onStat = function onStat(stat) {
    var self = this;

    var key = getKey(stat);

    switch (stat.type) {
        case 'counter':
            return self.statsd.increment(key, stat.value);

        case 'gauge':
            return self.statsd.gauge(key, stat.value);

        case 'timing':
            return self.statsd.timing(key, stat.value);

        default:
            self.channel.logger.error('Trying to emit an invalid stat object', {
                statType: stat.type,
                statName: stat.name
            });
            break;
    }
};

module.exports = TChannelStatsd;
<commit_msg>lib/statsd: switch to if else branching<commit_after>// Copyright (c) 2015 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

'use strict';

var LENGTH_ARRAYS = {};

function TChannelStatsd(channel, statsd) {
    if (!(this instanceof TChannelStatsd)) {
        return new TChannelStatsd(channel, statsd);
    }

    var self = this;
    self.statsd = statsd;
    self.channel = channel;

    self.channel.statEvent.on(onStat);

    function onStat(stat) {
        self.onStat(stat);
    }
}

function clean(str, field) {
    var copy;

    if (!str) {
        return field;
    }

    copy = LENGTH_ARRAYS[str.length];
    if (!copy) {
        copy = LENGTH_ARRAYS[str.length] = [];
    }

    for (var i = 0; i < str.length; i++) {
        var char = str[i];

        if (char === ':' ||
            char === '/' ||
            char === '.' ||
            char === '{' ||
            char === '}'
        ) {
            copy[i] = '-';
        } else {
            copy[i] = char;
        }
    }

    return copy.join('');
}

function cleanHostPort(str, field) {
    var copy;

    if (!str) {
        return field;
    }

    var length = str.indexOf(':');

    copy = LENGTH_ARRAYS[length];
    if (!copy) {
        copy = LENGTH_ARRAYS[length] = [];
    }
    for (var i = 0; i < length; i++) {
        var char = str[i];

        if (char === '/' ||
            char === '.' ||
            char === '{' ||
            char === '}'
        ) {
            copy[i] = '-';
        } else {
            copy[i] = char;
        }
    }

    return copy.join('');
}

function getKey(stat) {
    /*eslint complexity: [2, 50]*/
    var prefix = 'tchannel.' + stat.name;
    switch (stat.name) {
        // outbound
        case 'outbound.calls.sent':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint');

        case 'outbound.calls.success':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint');

        case 'outbound.calls.system-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'outbound.calls.per-attempt.system-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'type') + '.' +
                stat.tags['retry-count'];

        case 'outbound.calls.operational-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'outbound.calls.per-attempt.operational-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'type') + '.' +
                stat.tags['retry-count'];

        case 'outbound.calls.app-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'outbound.calls.per-attempt.app-errors':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                clean(stat.tags.type, 'type') + '.' +
                stat.tags['retry-count'];

        case 'outbound.calls.retries':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                stat.tags['retry-count'];

        case 'outbound.request.size':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.targetService, 'no-target-service') + '.' +
                clean(stat.tags.targetEndpoint, 'no-endpoint');

        case 'outbound.response.size':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint');

        case 'outbound.calls.latency':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint');

        case 'outbound.calls.per-attempt-latency':
            return prefix + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags['target-service'], 'no-target-service') + '.' +
                clean(stat.tags['target-endpoint'], 'no-endpoint') + '.' +
                stat.tags['retry-count'];

        // inbound
        case 'inbound.calls.recvd':
            return prefix + '.' +
                clean(stat.tags.callingService, 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        case 'inbound.calls.success':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        case 'inbound.calls.system-errors':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'inbound.calls.app-errors':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'inbound.request.size':
            return prefix + '.' +
                clean(stat.tags.callingService, 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        case 'inbound.response.size':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        case 'inbound.protocol-errors':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'peer-host-port');

        case 'inbound.calls.latency':
            return prefix + '.' +
                clean(stat.tags['calling-service'], 'no-calling-service') + '.' +
                clean(stat.tags.service, 'no-service') + '.' +
                clean(stat.tags.endpoint, 'no-endpoint');

        // connection
        case 'connections.active':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port');

        case 'connections.initiated':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port');

        case 'connections.connect-errors':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port');

        case 'connections.accepted':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port');

        case 'connections.accept-errors':
            return prefix + '.' +
                cleanHostPort(stat.tags['host-port'], 'no-host-port');

        case 'connections.errors':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port') + '.' +
                clean(stat.tags.type, 'no-type');

        case 'connections.closed':
            return prefix + '.' +
                cleanHostPort(stat.tags['peer-host-port'], 'no-peer-host-port') + '.' +
                clean(stat.tags.reason, 'no-reason');

        case 'connections.bytes-sent':
            return prefix + '.' +
                cleanHostPort(stat.tags.peerHostPort, 'no-peer-host-port');

        case 'connections.bytes-recvd':
            return prefix + '.' +
                cleanHostPort(stat.tags.peerHostPort, 'no-peer-host-port');

        // other types
        default:
            return 'tchannel.bad-stat-object';
    }
}

TChannelStatsd.prototype.onStat = function onStat(stat) {
    var self = this;

    var key = getKey(stat);

    if (stat.type === 'counter') {
        return self.statsd.increment(key, stat.value);
    } else if (stat.type === 'gauge') {
        return self.statsd.gauge(key, stat.value);
    } else if (stat.type === 'timing') {
        return self.statsd.timing(key, stat.value);
    } else {
        self.channel.logger.error('Trying to emit an invalid stat object', {
            statType: stat.type,
            statName: stat.name
        });
    }
};

module.exports = TChannelStatsd;
<|endoftext|>


<commit_before>'use strict';
module.exports = function() {
  var svn = require('svn-interface');

  svn.status('', {}, function(err, res){
    console.log(res.status.target.entry.map(function(entry){
      return entry['wc-status']._attribute.item + ' - ' + entry._attribute.path;
    }));
  });
}
<commit_msg>:purse: lint fixes<commit_after>'use strict';
module.exports = function() {
  var svn = require('svn-interface');

  svn.status('', {}, function(err, res){
    console.log(res.status.target.entry.map(function(entry){
      return entry['wc-status']._attribute.item + ' - ' + entry._attribute.path;
    }));
  });
};
<|endoftext|>


<commit_before>//use strict might have screwed up my this context, or might not have..
var serialPort = require("serialport");
var async = require("async");
var bufferEqual = require('buffer-equal');

var Cmnd_STK_GET_SYNC = 0x30;
var Cmnd_STK_SET_DEVICE = 0x42;
var Cmnd_STK_ENTER_PROGMODE = 0x50;
var Cmnd_STK_LOAD_ADDRESS = 0x55;
var Cmnd_STK_PROG_PAGE = 0x64;
var Cmnd_STK_LEAVE_PROGMODE = 0x51;

var Sync_CRC_EOP = 0x20;

var Resp_STK_OK = 0x10;
var Resp_STK_INSYNC = 0x14;

var memtype = 0x46;
var timeout = 1000;

//todo abstract out chrome and take serial object shim
function stk500(port) {
	// if (!(this instanceof stk500))
	// return new stk500();

	console.log("constructed");

	this.serialPort = port;

	this.buffer = new Buffer(300);

	this.bufferSize = 0;

};

stk500.prototype.matchReceive = function(buffer, timeout, callback){
	console.log("matching");
	console.log(buffer.toString('hex'));

	var self = this;

	var elapsed = 0;
	var interval = 10;

	var timer = setInterval(check, interval);

	function check(){
		if(elapsed>timeout){
			clearInterval(timer);
			callback(elapsed);
		}
		// console.log(elapsed);
		elapsed = elapsed + interval;

		if(self.bufferSize>=buffer.length){
			console.log(bufferEqual(self.buffer.slice(0,buffer.length), buffer));
			if(bufferEqual(self.buffer.slice(0,buffer.length), buffer)){
				self.buffer = new Buffer(300);
				self.bufferSize = 0;
				clearInterval(timer);
				callback(null);
			}
			else{
				var buffer_copy = new Buffer(self.bufferSize);
				self.buffer.copy(buffer_copy);
				self.buffer = new Buffer(300);
				self.bufferSize = 0;
				clearInterval(timer);
				callback(buffer_copy);
			}
		}
	}
};

//todo use error
stk500.prototype.connect = function(done) {
	console.log("connect");

	var self = this;

	this.serialPort.open(function (error) {

	  if ( error ) {
	    console.log('failed to connect: ' + error);
	    done(error);
	  } else {
	    console.log('connected');
	    self.serialPort.on('data', function(data) {
	      console.log("received " + data.toString('hex'));
	      data.copy(self.buffer, self.bufferSize);
	      self.bufferSize = self.bufferSize + data.length;
	    });
	    done();
	  }
	});

};

//todo can this timeout? or fail?
stk500.prototype.disconnect = function(done) {
	console.log("disconnect");

	var self = this;

	self.serialPort.close(function (error) {
	  if ( error ) {
	    console.log('failed to close: ' + error);
	    done(error);
	  } else {
	    console.log('closed');
	    done();
	  }
	});

};

stk500.prototype.reset = function(delay1, delay2, done){
	console.log("reset");

	var self = this;

	async.series([
	  function(cbdone) {
	  	console.log("asserting");
	    self.serialPort.set({rts:true, dtr:true}, function(result){
	    	console.log("asserted");
	    	if(result) cbdone(result);
	    	else cbdone();
	    });
	  },
	  function(cbdone) {
	  	console.log("wait");
	    setTimeout(cbdone, delay1);
	  },
	  function(cbdone) {
	  	console.log("clearing");
	    self.serialPort.set({rts:false, dtr:false}, function(result){
	    	console.log("clear");
	    	if(result) cbdone(result);
	    	else cbdone();
	    });
	  },
	  function(cbdone) {
	  	console.log("wait");
	    setTimeout(cbdone, delay2);
	  }],
		function(error) {
			console.log("done");

			done(error);
		}
	);
};

stk500.prototype.sync = function(attempts, done) {
	console.log("sync");
	var self = this;
	var tries = 1;

	var cmd = new Buffer([Cmnd_STK_GET_SYNC, Sync_CRC_EOP]);

	attempt();
	function attempt(){
		tries=tries+1;
		console.log(cmd.toString('hex'));
		self.serialPort.write(cmd, function(error, results){
			console.log("confirm sync");
			self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
				if(error) {
					if(typeof error === Buffer){
						console.log("no match");
						done(error);
					}else if(tries<=attempts){
						console.log("failed attempt again");
						attempt();
					}else{
						console.log("failed all attempts");
						done("no response");
					}
				}else{
					console.log("confirmed sync");
					done();
				}
			});
		});
	}
};

stk500.prototype.setOptions = function(options, done) {
	console.log("set device");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_SET_DEVICE, options.devicecode, options.revision, options.progtype, options.parmode, options.polling, options.selftimed, options.lockbytes, options.fusebytes, options.flashpollval1, options.flashpollval2, options.eeprompollval1, options.eeprompollval2, options.pagesizehigh, options.pagesizelow, options.eepromsizehigh, options.eepromsizelow, options.flashsize4, options.flashsize3, options.flashsize2, options.flashsize1, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));
	this.serialPort.write(cmd, function(error, results){
		console.log("confirm set device");
			self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
				console.log("confirmed set device");
				done();
		});
	});
};

stk500.prototype.enterProgrammingMode = function(done) {
	console.log("send enter programming mode");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_ENTER_PROGMODE, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));
	this.serialPort.write(cmd, function(error, results) {
		console.log("sent enter programming mode");
		self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
	  	console.log("confirmed enter programming mode");
	  	done();
		});
	});
};

stk500.prototype.loadAddress = function(useaddr, done) {
	console.log("load address");
	var self = this;

	var addr_low = useaddr & 0xff;
	var addr_high = (useaddr >> 8) & 0xff;

	var cmd = new Buffer([Cmnd_STK_LOAD_ADDRESS, addr_low, addr_high, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));

	this.serialPort.write(cmd, function(error, results) {
		console.log("confirm load address");
  	self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
  		console.log("confirmed load address");
  		done();
  	});

	});

};

stk500.prototype.loadPage = function(writeBytes, done) {
	console.log("load page");
	var self = this;

	var bytes_low = writeBytes.length & 0xff;
	var bytes_high = writeBytes.length >> 8;

	var cmd = new Buffer([Cmnd_STK_PROG_PAGE, bytes_high, bytes_low, memtype]);
	cmd = Buffer.concat([cmd,writeBytes]);
	var end = new Buffer([Sync_CRC_EOP]);
	cmd = Buffer.concat([cmd,end]);
	console.log(cmd.toString('hex'));

	this.serialPort.write(cmd, function(error, results) {
		console.log("loaded page");

		self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
			console.log("confirmed loaded page");
			done();
		});

	});
};

stk500.prototype.upload = function(hex, pageSize, done) {
	console.log("program");

	var pageaddr = 0;
	var writeBytes;
	var useaddr;

	var self = this;

	// program individual pages
  async.whilst(
    function() { return pageaddr < hex.length; },
    function(pagedone) {
			console.log("program page");
      async.series([
      	function(cbdone){
      		useaddr = pageaddr >> 1;
      		cbdone();
      	},
      	function(cbdone){
      		self.loadAddress(useaddr, cbdone);
      	},
        function(cbdone){

					writeBytes = hex.slice(pageaddr, (hex.length > pageSize ? (pageaddr + pageSize) : hex.length - 1))
        	cbdone();
        },
        function(cbdone){
        	self.loadPage(writeBytes, cbdone);
        },
        function(cbdone){
					console.log("programmed page");
        	pageaddr =  pageaddr + writeBytes.length;
        	setTimeout(cbdone, 4);
        }
      ],
      function(error) {
      	console.log("page done");
      	pagedone(error);
      });
    },
    function(error) {
    	console.log("upload done");
    	done(error);
    }
  );
};

stk500.prototype.exitProgrammingMode = function(done) {
	console.log("send leave programming mode");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_LEAVE_PROGMODE, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));

	this.serialPort.write(cmd, function(error, results) {
		console.log("sent leave programming mode");
		self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
			console.log("confirmed leave programming mode");
			done();
		});
	});
};

stk500.prototype.verify = function(hex, done) {
	// console.log("verify");
	// var self = this;

	// serial.send([Cmnd_STK_LOAD_ADDRESS, addr_low, addr_high, Sync_CRC_EOP]) n times
	// self.matchReceive([Resp_STK_INSYNC, Resp_STK_OK]);
	// serial.send ([Cmnd_STK_READ_PAGE, bytes_high, bytes_low, memtype, Sync_CRC_EOP]) n times
	// self.matchReceive([Resp_STK_INSYNC].concat(writeBytes));
	done();
};

//todo convenience function
stk500.prototype.bootload = function (chip, hex, done){
	done();
};

// export the class
module.exports = stk500;<commit_msg>less unsuccessfuly syncs with smaller timeout<commit_after>//use strict might have screwed up my this context, or might not have..
var serialPort = require("serialport");
var async = require("async");
var bufferEqual = require('buffer-equal');

var Cmnd_STK_GET_SYNC = 0x30;
var Cmnd_STK_SET_DEVICE = 0x42;
var Cmnd_STK_ENTER_PROGMODE = 0x50;
var Cmnd_STK_LOAD_ADDRESS = 0x55;
var Cmnd_STK_PROG_PAGE = 0x64;
var Cmnd_STK_LEAVE_PROGMODE = 0x51;

var Sync_CRC_EOP = 0x20;

var Resp_STK_OK = 0x10;
var Resp_STK_INSYNC = 0x14;

var memtype = 0x46;
var timeout = 200;

//todo abstract out chrome and take serial object shim
function stk500(port) {
	// if (!(this instanceof stk500))
	// return new stk500();

	console.log("constructed");

	this.serialPort = port;

	this.buffer = new Buffer(300);

	this.bufferSize = 0;

};

stk500.prototype.matchReceive = function(buffer, timeout, callback){
	console.log("matching");
	console.log(buffer.toString('hex'));

	var self = this;

	var elapsed = 0;
	var interval = 10;

	var timer = setInterval(check, interval);

	function check(){
		if(elapsed>timeout){
			clearInterval(timer);
			callback(elapsed);
		}
		// console.log(elapsed);
		elapsed = elapsed + interval;

		if(self.bufferSize>=buffer.length){
			console.log(bufferEqual(self.buffer.slice(0,buffer.length), buffer));
			if(bufferEqual(self.buffer.slice(0,buffer.length), buffer)){
				self.buffer = new Buffer(300);
				self.bufferSize = 0;
				clearInterval(timer);
				callback(null);
			}
			else{
				var buffer_copy = new Buffer(self.bufferSize);
				self.buffer.copy(buffer_copy);
				self.buffer = new Buffer(300);
				self.bufferSize = 0;
				clearInterval(timer);
				callback(buffer_copy);
			}
		}
	}
};

//todo use error
stk500.prototype.connect = function(done) {
	console.log("connect");

	var self = this;

	this.serialPort.open(function (error) {

	  if ( error ) {
	    console.log('failed to connect: ' + error);
	    done(error);
	  } else {
	    console.log('connected');
	    self.serialPort.on('data', function(data) {
	      console.log("received " + data.toString('hex'));
	      data.copy(self.buffer, self.bufferSize);
	      self.bufferSize = self.bufferSize + data.length;
	    });
	    done();
	  }
	});

};

//todo can this timeout? or fail?
stk500.prototype.disconnect = function(done) {
	console.log("disconnect");

	var self = this;

	self.serialPort.close(function (error) {
	  if ( error ) {
	    console.log('failed to close: ' + error);
	    done(error);
	  } else {
	    console.log('closed');
	    done();
	  }
	});

};

stk500.prototype.reset = function(delay1, delay2, done){
	console.log("reset");

	var self = this;

	async.series([
	  function(cbdone) {
	  	console.log("asserting");
	    self.serialPort.set({rts:true, dtr:true}, function(result){
	    	console.log("asserted");
	    	if(result) cbdone(result);
	    	else cbdone();
	    });
	  },
	  function(cbdone) {
	  	console.log("wait");
	    setTimeout(cbdone, delay1);
	  },
	  function(cbdone) {
	  	console.log("clearing");
	    self.serialPort.set({rts:false, dtr:false}, function(result){
	    	console.log("clear");
	    	if(result) cbdone(result);
	    	else cbdone();
	    });
	  },
	  function(cbdone) {
	  	console.log("wait");
	    setTimeout(cbdone, delay2);
	  }],
		function(error) {
			console.log("done");

			done(error);
		}
	);
};

stk500.prototype.sync = function(attempts, done) {
	console.log("sync");
	var self = this;
	var tries = 1;

	var cmd = new Buffer([Cmnd_STK_GET_SYNC, Sync_CRC_EOP]);

	attempt();
	function attempt(){
		tries=tries+1;
		console.log(cmd.toString('hex'));
		self.serialPort.write(cmd, function(error, results){
			console.log("confirm sync");
			self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(error){
				if(error) {
					if(typeof error === Buffer){
						console.log("no match");
						done(error);
					}else if(tries<=attempts){
						console.log("failed attempt again");
						attempt();
					}else{
						console.log("failed all attempts");
						done("no response");
					}
				}else{
					console.log("confirmed sync");
					done();
				}
			});
		});
	}
};

stk500.prototype.setOptions = function(options, done) {
	console.log("set device");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_SET_DEVICE, options.devicecode, options.revision, options.progtype, options.parmode, options.polling, options.selftimed, options.lockbytes, options.fusebytes, options.flashpollval1, options.flashpollval2, options.eeprompollval1, options.eeprompollval2, options.pagesizehigh, options.pagesizelow, options.eepromsizehigh, options.eepromsizelow, options.flashsize4, options.flashsize3, options.flashsize2, options.flashsize1, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));
	this.serialPort.write(cmd, function(error, results){
		console.log("confirm set device");
			self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
				console.log("confirmed set device");
				done();
		});
	});
};

stk500.prototype.enterProgrammingMode = function(done) {
	console.log("send enter programming mode");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_ENTER_PROGMODE, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));
	this.serialPort.write(cmd, function(error, results) {
		console.log("sent enter programming mode");
		self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
	  	console.log("confirmed enter programming mode");
	  	done();
		});
	});
};

stk500.prototype.loadAddress = function(useaddr, done) {
	console.log("load address");
	var self = this;

	var addr_low = useaddr & 0xff;
	var addr_high = (useaddr >> 8) & 0xff;

	var cmd = new Buffer([Cmnd_STK_LOAD_ADDRESS, addr_low, addr_high, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));

	this.serialPort.write(cmd, function(error, results) {
		console.log("confirm load address");
  	self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
  		console.log("confirmed load address");
  		done();
  	});

	});

};

stk500.prototype.loadPage = function(writeBytes, done) {
	console.log("load page");
	var self = this;

	var bytes_low = writeBytes.length & 0xff;
	var bytes_high = writeBytes.length >> 8;

	var cmd = new Buffer([Cmnd_STK_PROG_PAGE, bytes_high, bytes_low, memtype]);
	cmd = Buffer.concat([cmd,writeBytes]);
	var end = new Buffer([Sync_CRC_EOP]);
	cmd = Buffer.concat([cmd,end]);
	console.log(cmd.toString('hex'));

	this.serialPort.write(cmd, function(error, results) {
		console.log("loaded page");

		self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
			console.log("confirmed loaded page");
			done();
		});

	});
};

stk500.prototype.upload = function(hex, pageSize, done) {
	console.log("program");

	var pageaddr = 0;
	var writeBytes;
	var useaddr;

	var self = this;

	// program individual pages
  async.whilst(
    function() { return pageaddr < hex.length; },
    function(pagedone) {
			console.log("program page");
      async.series([
      	function(cbdone){
      		useaddr = pageaddr >> 1;
      		cbdone();
      	},
      	function(cbdone){
      		self.loadAddress(useaddr, cbdone);
      	},
        function(cbdone){

					writeBytes = hex.slice(pageaddr, (hex.length > pageSize ? (pageaddr + pageSize) : hex.length - 1))
        	cbdone();
        },
        function(cbdone){
        	self.loadPage(writeBytes, cbdone);
        },
        function(cbdone){
					console.log("programmed page");
        	pageaddr =  pageaddr + writeBytes.length;
        	setTimeout(cbdone, 4);
        }
      ],
      function(error) {
      	console.log("page done");
      	pagedone(error);
      });
    },
    function(error) {
    	console.log("upload done");
    	done(error);
    }
  );
};

stk500.prototype.exitProgrammingMode = function(done) {
	console.log("send leave programming mode");
	var self = this;
	var cmd = new Buffer([Cmnd_STK_LEAVE_PROGMODE, Sync_CRC_EOP]);
	console.log(cmd.toString('hex'));

	this.serialPort.write(cmd, function(error, results) {
		console.log("sent leave programming mode");
		self.matchReceive(new Buffer([Resp_STK_INSYNC, Resp_STK_OK]), timeout, function(){
			console.log("confirmed leave programming mode");
			done();
		});
	});
};

stk500.prototype.verify = function(hex, done) {
	// console.log("verify");
	// var self = this;

	// serial.send([Cmnd_STK_LOAD_ADDRESS, addr_low, addr_high, Sync_CRC_EOP]) n times
	// self.matchReceive([Resp_STK_INSYNC, Resp_STK_OK]);
	// serial.send ([Cmnd_STK_READ_PAGE, bytes_high, bytes_low, memtype, Sync_CRC_EOP]) n times
	// self.matchReceive([Resp_STK_INSYNC].concat(writeBytes));
	done();
};

//todo convenience function
stk500.prototype.bootload = function (chip, hex, done){
	done();
};

// export the class
module.exports = stk500;<|endoftext|>


<commit_before>module.exports = {
  monetize: function(n) {
    var s = _.numberFormat(+n, 2);
    return n < 0 ? "(" + s.replace('-','') + ")" : s;
  },
};
<commit_msg>added require, not sure why this worked before<commit_after>var _ = require('underscore');

module.exports = {
  monetize: function(n) {
    var s = _.numberFormat(+n, 2);
    return n < 0 ? "(" + s.replace('-','') + ")" : s;
  },
};
<|endoftext|>


<commit_before>'use strict';

const { parseScriptsAndStyles } = require('./util');

/**
 * Set the styles for a template.
 * @param {Object} req HTTP response object.
 * @param {Object} res HTTP request object.
 * @param {Array} templateStyles An array of script objects.
 * @return {Promise} Resolves to an array or styles.
 */
const setTemplateStyles = (req, res, templateStyles) => new Promise((resolve, reject) => {

    const linz = require('../');

    const defaults = [
        {
            content: '.pace{-webkit-pointer-events:none;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.pace-inactive{display:none}.pace .pace-progress{background:#29d;position:fixed;z-index:2000;top:0;right:100%;width:100%;height:2px}',
            type: 'text/css',
        },
        {
            href: '//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,400,300,600,700',
            rel: 'stylesheet',
        },
        {
            crossorigin: 'anonymous',
            href: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css',
            integrity: 'sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u',
            rel: 'stylesheet',

        },
        {
            crossorigin: 'anonymous',
            href: '//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
            integrity: 'sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN',
            rel: 'stylesheet',
        },
        {
            crossorigin: 'anonymous',
            href: '//cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css',
            integrity: 'sha256-yMjaV542P+q1RnH6XByCPDfUFhmOafWbeLPmqKh11zo=',
            rel: 'stylesheet',
        },
        {
            href: `${linz.get('admin path')}/public/css/multiselect.min.css`,
            rel: 'stylesheet',
        },
        {
            href: `${linz.get('admin path')}/public/css/noty.css`,
            rel: 'stylesheet',
        },
        {
            href: `${linz.get('admin path')}/public/css/offcanvas.min.css`,
            rel: 'stylesheet',
        },
        {
            href: `${linz.get('admin path')}/public/css/linz.css?v7`,
            rel: 'stylesheet',
        },
    ];

    if (linz.app.locals.adminCSSFile) {
        defaults.push({
            href: linz.app.locals.adminCSSFile,
            rel: 'stylesheet',
        });
    }

    res.locals.styles = [].concat(defaults, templateStyles)
        .filter(style => style !== undefined);

    const customStyles = linz.get('styles');

    if (!customStyles) {
        return resolve(parseScriptsAndStyles(res.locals.styles));
    }

    customStyles(req, res)
        .then((result) => resolve(parseScriptsAndStyles(result)))
        .catch(reject);

});

module.exports = { setTemplateStyles };
<commit_msg>cache bust css<commit_after>'use strict';

const { parseScriptsAndStyles } = require('./util');

/**
 * Set the styles for a template.
 * @param {Object} req HTTP response object.
 * @param {Object} res HTTP request object.
 * @param {Array} templateStyles An array of script objects.
 * @return {Promise} Resolves to an array or styles.
 */
const setTemplateStyles = (req, res, templateStyles) => new Promise((resolve, reject) => {

    const linz = require('../');

    const defaults = [
        {
            content: '.pace{-webkit-pointer-events:none;pointer-events:none;-webkit-user-select:none;-moz-user-select:none;user-select:none}.pace-inactive{display:none}.pace .pace-progress{background:#29d;position:fixed;z-index:2000;top:0;right:100%;width:100%;height:2px}',
            type: 'text/css',
        },
        {
            href: '//fonts.googleapis.com/css?family=Open+Sans:300italic,400italic,600italic,700italic,400,300,600,700',
            rel: 'stylesheet',
        },
        {
            crossorigin: 'anonymous',
            href: '//maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css',
            integrity: 'sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u',
            rel: 'stylesheet',

        },
        {
            crossorigin: 'anonymous',
            href: '//maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css',
            integrity: 'sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN',
            rel: 'stylesheet',
        },
        {
            crossorigin: 'anonymous',
            href: '//cdnjs.cloudflare.com/ajax/libs/bootstrap-datetimepicker/4.17.47/css/bootstrap-datetimepicker.min.css',
            integrity: 'sha256-yMjaV542P+q1RnH6XByCPDfUFhmOafWbeLPmqKh11zo=',
            rel: 'stylesheet',
        },
        {
            href: `${linz.get('admin path')}/public/css/multiselect.min.css`,
            rel: 'stylesheet',
        },
        {
            href: `${linz.get('admin path')}/public/css/noty.css`,
            rel: 'stylesheet',
        },
        {
            href: `${linz.get('admin path')}/public/css/offcanvas.min.css`,
            rel: 'stylesheet',
        },
        {
            href: `${linz.get('admin path')}/public/css/linz.css?v8`,
            rel: 'stylesheet',
        },
    ];

    if (linz.app.locals.adminCSSFile) {
        defaults.push({
            href: linz.app.locals.adminCSSFile,
            rel: 'stylesheet',
        });
    }

    res.locals.styles = [].concat(defaults, templateStyles)
        .filter(style => style !== undefined);

    const customStyles = linz.get('styles');

    if (!customStyles) {
        return resolve(parseScriptsAndStyles(res.locals.styles));
    }

    customStyles(req, res)
        .then((result) => resolve(parseScriptsAndStyles(result)))
        .catch(reject);

});

module.exports = { setTemplateStyles };
<|endoftext|>


<commit_before>var utils = require('./utils');
var stringDiff = require('diff');
var specialCharRegExp = require('./specialCharRegExp');

module.exports = function (expect) {
    expect.installTheme({ styles: {
        jsBoolean: 'jsPrimitive',
        jsNumber: 'jsPrimitive',
        error: ['red', 'bold'],
        success: ['green', 'bold'],
        diffAddedLine: 'green',
        diffAddedHighlight: ['bgGreen', 'white'],
        diffAddedSpecialChar: ['bgGreen', 'cyan', 'bold'],
        diffRemovedLine: 'red',
        diffRemovedHighlight: ['bgRed', 'white'],
        diffRemovedSpecialChar: ['bgRed', 'cyan', 'bold'],
        partialMatchHighlight: ['bgYellow']
    }});

    expect.installTheme('html', {
        palette: [
            '#993333', '#669933', '#314575', '#337777', '#710071', '#319916',
            '#BB1A53', '#999933', '#4311C2', '#996633', '#993399', '#333399',
            '#228842', '#C24747', '#336699', '#663399'
        ],
        styles: {
            jsComment: '#969896',
            jsFunctionName: '#795da3',
            jsKeyword: '#a71d5d',
            jsPrimitive: '#0086b3',
            jsRegexp: '#183691',
            jsString: '#df5000',
            jsKey: '#555'
        }
    });

    expect.installTheme('ansi', {
        palette: [
            '#FF1A53', '#E494FF', '#1A53FF', '#FF1AC6', '#1AFF53', '#D557FF',
            '#81FF57', '#C6FF1A', '#531AFF', '#AFFF94', '#C61AFF', '#53FF1A',
            '#FF531A', '#1AFFC6', '#FFC61A', '#1AC6FF'
        ],
        styles: {
            jsComment: 'gray',
            jsFunctionName: 'jsKeyword',
            jsKeyword: 'magenta',
            jsNumber: [],
            jsPrimitive: 'cyan',
            jsRegexp: 'green',
            jsString: 'cyan',
            jsKey: '#666',
            diffAddedHighlight: ['bgGreen', 'black'],
            diffRemovedHighlight: ['bgRed', 'black'],
            partialMatchHighlight: ['bgYellow', 'black']
        }
    });

    expect.addStyle('colorByIndex', function (content, index) {
        var palette = this.theme().palette;
        if (palette) {
            var color = palette[index % palette.length];
            this.text(content, color);
        } else {
            this.text(content);
        }
    });

    expect.addStyle('singleQuotedString', function (content) {
        content = String(content);
        this.jsString("'")
            .jsString(content.replace(/[\\\x00-\x1f']/g, function ($0) {
                if ($0 === '\n') {
                    return '\\n';
                } else if ($0 === '\r') {
                    return '\\r';
                } else if ($0 === "'") {
                    return "\\'";
                } else if ($0 === '\\') {
                    return '\\\\';
                } else if ($0 === '\t') {
                    return '\\t';
                } else if ($0 === '\b') {
                    return '\\b';
                } else if ($0 === '\f') {
                    return '\\f';
                } else {
                    var charCode = $0.charCodeAt(0);
                    return '\\x' + (charCode < 16 ? '0' : '') + charCode.toString(16);
                }
            }))
            .jsString("'");
    });

    expect.addStyle('property', function (key, inspectedValue, isArrayLike) {
        var keyOmitted = false;
        var isSymbol;
        /*jshint ignore:start*/
        isSymbol = typeof key === 'symbol';
        /*jshint ignore:end*/
        if (isSymbol) {
            this.text('[').sp().appendInspected(key).sp().text(']').text(':');
        } else {
            key = String(key);
            if (/^[a-z\$\_][a-z0-9\$\_]*$/i.test(key)) {
                this.text(key, 'jsKey').text(':');
            } else if (/^(?:0|[1-9][0-9]*)$/.test(key)) {
                if (isArrayLike) {
                    keyOmitted = true;
                } else {
                    this.jsNumber(key).text(':');
                }
            } else {
                this.singleQuotedString(key).text(':');
            }
        }

        if (!inspectedValue.isEmpty()) {
            if (!keyOmitted) {
                if (key.length > 5 && inspectedValue.isBlock() && inspectedValue.isMultiline()) {
                    this.indentLines();
                    this.nl().i();
                } else {
                    this.sp();
                }
            }
            this.append(inspectedValue);
        }
    });

    // Intended to be redefined by a plugin that offers syntax highlighting:
    expect.addStyle('code', function (content, language) {
        this.text(content);
    });

    expect.addStyle('annotationBlock', function () {
        var pen = this.getContentFromArguments(arguments);
        var height = pen.size().height;

        if (height > 0) {
            this.block(function () {
                for (var i = 0; i < height; i += 1) {
                    if (0 < i) {
                        this.nl();
                    }
                    this.error('//');
                }
            });
            this.sp().block(pen);
        }
    });

    expect.addStyle('commentBlock', function () {
        var pen = this.getContentFromArguments(arguments);
        var height = pen.size().height;

        if (height > 0) {
            this.block(function () {
                for (var i = 0; i < height; i += 1) {
                    if (0 < i) {
                        this.nl();
                    }
                    this.jsComment('//');
                }
            });
            this.sp().block(pen);
        }
    });

    expect.addStyle('removedHighlight', function (content) {
        this.alt({
            text: function () {
                content.split(/(\n)/).forEach(function (fragment) {
                    if (fragment === '\n') {
                        this.nl();
                    } else {
                        this.block(function () {
                            this.text(fragment).nl().text(fragment.replace(/[\s\S]/g, '^'));
                        });
                    }
                }, this);
            },
            fallback: function () {
                this.diffRemovedHighlight(content);
            }
        });
    });

    expect.addStyle('match', function (content) {
        this.alt({
            text: function () {
                content.split(/(\n)/).forEach(function (fragment) {
                    if (fragment === '\n') {
                        this.nl();
                    } else {
                        this.block(function () {
                            this.text(fragment).nl().text(fragment.replace(/[\s\S]/g, '^'));
                        });
                    }
                }, this);
            },
            fallback: function () {
                this.diffAddedHighlight(content);
            }
        });
    });

    expect.addStyle('partialMatch', function (content) {
        this.alt({
            text: function () {
                // We haven't yet come up with a good styling for partial matches in text mode
                this.match(content);
            },
            fallback: function () {
                this.partialMatchHighlight(content);
            }
        });
    });

    expect.addStyle('shouldEqualError', function (expected) {
        this.error(typeof expected === 'undefined' ? 'should be' : 'should equal').sp().block(function () {
            this.appendInspected(expected);
        });
    });

    expect.addStyle('errorName', function (error) {
        if (typeof error.name === 'string' && error.name !== 'Error') {
            this.text(error.name);
        } else if (error.constructor && typeof error.constructor.name === 'string') {
            this.text(error.constructor.name);
        } else {
            this.text('Error');
        }
    });

    expect.addStyle('appendErrorMessage', function (error, options) {
        if (error && error.isUnexpected) {
            this.append(error.getErrorMessage(utils.extend({ output: this }, options)));
        } else {
            this.appendInspected(error);
        }
    });

    expect.addStyle('appendItems', function (items, separator) {
        var that = this;
        separator = separator || '';
        items.forEach(function (item, index) {
            if (0 < index) {
                that.append(separator);
            }
            that.appendInspected(item);
        });
    });

    expect.addStyle('magicPenLine', function (line, pen) {
        line.forEach(function (lineEntry, j) {
            if (j > 0) {
                this.nl();
            }
            if (lineEntry.style === 'text') {
                var styles = lineEntry.args.styles;
                if (pen && styles.length === 1 && typeof pen[styles[0]] === 'function') {
                    // Text with a single style also available as a method on the pen being inspected:
                    this
                        .text('.')
                        .jsFunctionName(styles[0])
                        .text('(')
                        .singleQuotedString(lineEntry.args.content)
                        .text(')');
                } else {
                    this
                        .text('.')
                        .jsFunctionName('text')
                        .text('(')
                        .singleQuotedString(lineEntry.args.content);
                    if (styles.length > 0) {
                        this
                            .text(', ')
                            .appendInspected(styles.length === 1 && Array.isArray(styles[0]) ? styles[0] : styles);
                    }
                    this.text(')');
                }
            } else {
                // lineEntry.style === 'block'
                this
                    .text('.')
                    .jsFunctionName('block').text('(').jsKeyword('function').text(' () {');
                if (lineEntry.args) {
                    this
                        .nl()
                        .indentLines()
                        .i()
                        .magicPen(pen, lineEntry.args)
                        .outdentLines()
                        .nl();
                }
                this.text('})');
            }
        }, this);
    });

    expect.addStyle('magicPen', function (pen, lines) {
        var isTopLevel = !lines;
        lines = lines || pen.output;
        this.block(function () {
            if (isTopLevel) {
                this.jsFunctionName('magicpen').text('(');
                if (pen.format) {
                    this.singleQuotedString(pen.format);
                }
                this.text(')');
            } else {
                this.jsKeyword('this');
            }
            if (!pen.isEmpty()) {
                var inspectOnMultipleLines = lines.length > 1 || lines[0].length > 1;
                if (inspectOnMultipleLines) {
                    this
                        .nl()
                        .indentLines()
                        .i();
                }
                this.block(function () {
                    lines.forEach(function (line, i) {
                        if (i > 0) {
                            this.text('.').jsFunctionName('nl').text('()').nl();
                        }
                        this.magicPenLine(line, pen);
                    }, this);
                    if (!isTopLevel) {
                        this.text(';');
                    }
                });
                if (inspectOnMultipleLines) {
                    this.outdentLines();
                }
            }
        });

        // If we're at the top level of a non-empty pen compatible with the current output,
        // render the output of the pen in a comment:
        if (isTopLevel && !pen.isEmpty() && (pen.format === this.format || !pen.format)) {
            this.sp().commentBlock(pen);
        }
    });

    expect.addStyle('diffFragment', function (text, baseStyle, specialCharStyle, markUpSpecialCharacters) {
        if (markUpSpecialCharacters) {
            text.split(specialCharRegExp).forEach(function (part) {
                if (specialCharRegExp.test(part)) {
                    this[specialCharStyle || baseStyle](utils.escapeChar(part));
                } else {
                    this[baseStyle](part);
                }
            }, this);
        } else {
            this[baseStyle](text);
        }
    });

    expect.addStyle('stringDiff', function (actual, expected, options) {
        options = options || {};
        var type = options.type || 'WordsWithSpace';
        var diffLines = [];
        var lastPart;
        stringDiff.diffLines(actual, expected).forEach(function (part) {
            if (lastPart && lastPart.added && part.removed) {
                diffLines.push({
                    oldValue: part.value,
                    newValue: lastPart.value,
                    replaced: true
                });
                lastPart = null;
            } else if (lastPart) {
                diffLines.push(lastPart);
                lastPart = part;
            } else {
                lastPart = part;
            }
        });
        if (lastPart) {
            diffLines.push(lastPart);
        }

        diffLines.forEach(function (part, index) {
            var endsWithNewline = /\n$/.test(part.value);
            var value;
            if (part.replaced) {
                var oldLine = this.clone();
                var newLine = this.clone();
                var oldValue = part.oldValue;
                var newValue = part.newValue;
                var oldEndsWithNewline = oldValue.slice(-1) === '\n';
                var newEndsWithNewline = newValue.slice(-1) === '\n';
                if (oldEndsWithNewline) {
                    oldValue = oldValue.slice(0, -1);
                }
                if (newEndsWithNewline) {
                    newValue = newValue.slice(0, -1);
                }

                stringDiff['diff' + type](oldValue, newValue).forEach(function (part) {
                    if (part.added) {
                        newLine.diffFragment(part.value, 'diffAddedHighlight', 'diffAddedSpecialChar', options.markUpSpecialCharacters);
                    } else if (part.removed) {
                        oldLine.diffFragment(part.value, 'diffRemovedHighlight', 'diffRemovedSpecialChar', options.markUpSpecialCharacters);
                    } else {
                        newLine.diffAddedLine(part.value);
                        oldLine.diffRemovedLine(part.value);
                    }
                });
                this.alt({
                    text: function () {
                        oldLine.prependLinesWith(this.clone().diffRemovedLine('-'));
                        newLine.prependLinesWith(this.clone().diffAddedLine('+'));
                    }
                });

                if (oldEndsWithNewline && !newEndsWithNewline) {
                    oldLine.diffRemovedSpecialChar('\\n');
                }

                if (newEndsWithNewline && !oldEndsWithNewline) {
                    newLine.diffAddedSpecialChar('\\n');
                }

                this.append(oldLine).nl().append(newLine);
                if (oldEndsWithNewline && index < diffLines.length - 1) {
                    this.nl();
                }
            } else if (part.added) {
                value = endsWithNewline ?
                    part.value.slice(0, -1) :
                    part.value;

                this.append(function () {
                    this.diffFragment(value, 'diffAddedLine', undefined, options.markUpSpecialCharacters);
                    var outer = this;
                    this.alt({
                        text: function () {
                            outer.prependLinesWith(function () {
                                this.diffAddedLine('+');
                            });
                        }
                    });
                });

                if (endsWithNewline) {
                    this.nl();
                }
            } else if (part.removed) {
                value = endsWithNewline ?
                    part.value.slice(0, -1) :
                    part.value;

                this.append(function () {
                    this.diffFragment(value, 'diffRemovedLine', undefined, options.markUpSpecialCharacters);
                    var outer = this;
                    this.alt({
                        text: function () {
                            outer.prependLinesWith(function () {
                                this.diffRemovedLine('-');
                            });
                        }
                    });
                });

                if (endsWithNewline) {
                    this.nl();
                }
            } else {
                var outer = this;
                this.alt({
                    text: function () {
                        outer.text(part.value.replace(/^(.)/gm, ' $1'));
                    },
                    fallback: function () {
                        this.text(part.value);
                    }
                });
            }
        }, this);
    });
};
<commit_msg>Reduce duplication in the stringDiff style.<commit_after>var utils = require('./utils');
var stringDiff = require('diff');
var specialCharRegExp = require('./specialCharRegExp');

module.exports = function (expect) {
    expect.installTheme({ styles: {
        jsBoolean: 'jsPrimitive',
        jsNumber: 'jsPrimitive',
        error: ['red', 'bold'],
        success: ['green', 'bold'],
        diffAddedLine: 'green',
        diffAddedHighlight: ['bgGreen', 'white'],
        diffAddedSpecialChar: ['bgGreen', 'cyan', 'bold'],
        diffRemovedLine: 'red',
        diffRemovedHighlight: ['bgRed', 'white'],
        diffRemovedSpecialChar: ['bgRed', 'cyan', 'bold'],
        partialMatchHighlight: ['bgYellow']
    }});

    expect.installTheme('html', {
        palette: [
            '#993333', '#669933', '#314575', '#337777', '#710071', '#319916',
            '#BB1A53', '#999933', '#4311C2', '#996633', '#993399', '#333399',
            '#228842', '#C24747', '#336699', '#663399'
        ],
        styles: {
            jsComment: '#969896',
            jsFunctionName: '#795da3',
            jsKeyword: '#a71d5d',
            jsPrimitive: '#0086b3',
            jsRegexp: '#183691',
            jsString: '#df5000',
            jsKey: '#555'
        }
    });

    expect.installTheme('ansi', {
        palette: [
            '#FF1A53', '#E494FF', '#1A53FF', '#FF1AC6', '#1AFF53', '#D557FF',
            '#81FF57', '#C6FF1A', '#531AFF', '#AFFF94', '#C61AFF', '#53FF1A',
            '#FF531A', '#1AFFC6', '#FFC61A', '#1AC6FF'
        ],
        styles: {
            jsComment: 'gray',
            jsFunctionName: 'jsKeyword',
            jsKeyword: 'magenta',
            jsNumber: [],
            jsPrimitive: 'cyan',
            jsRegexp: 'green',
            jsString: 'cyan',
            jsKey: '#666',
            diffAddedHighlight: ['bgGreen', 'black'],
            diffRemovedHighlight: ['bgRed', 'black'],
            partialMatchHighlight: ['bgYellow', 'black']
        }
    });

    expect.addStyle('colorByIndex', function (content, index) {
        var palette = this.theme().palette;
        if (palette) {
            var color = palette[index % palette.length];
            this.text(content, color);
        } else {
            this.text(content);
        }
    });

    expect.addStyle('singleQuotedString', function (content) {
        content = String(content);
        this.jsString("'")
            .jsString(content.replace(/[\\\x00-\x1f']/g, function ($0) {
                if ($0 === '\n') {
                    return '\\n';
                } else if ($0 === '\r') {
                    return '\\r';
                } else if ($0 === "'") {
                    return "\\'";
                } else if ($0 === '\\') {
                    return '\\\\';
                } else if ($0 === '\t') {
                    return '\\t';
                } else if ($0 === '\b') {
                    return '\\b';
                } else if ($0 === '\f') {
                    return '\\f';
                } else {
                    var charCode = $0.charCodeAt(0);
                    return '\\x' + (charCode < 16 ? '0' : '') + charCode.toString(16);
                }
            }))
            .jsString("'");
    });

    expect.addStyle('property', function (key, inspectedValue, isArrayLike) {
        var keyOmitted = false;
        var isSymbol;
        /*jshint ignore:start*/
        isSymbol = typeof key === 'symbol';
        /*jshint ignore:end*/
        if (isSymbol) {
            this.text('[').sp().appendInspected(key).sp().text(']').text(':');
        } else {
            key = String(key);
            if (/^[a-z\$\_][a-z0-9\$\_]*$/i.test(key)) {
                this.text(key, 'jsKey').text(':');
            } else if (/^(?:0|[1-9][0-9]*)$/.test(key)) {
                if (isArrayLike) {
                    keyOmitted = true;
                } else {
                    this.jsNumber(key).text(':');
                }
            } else {
                this.singleQuotedString(key).text(':');
            }
        }

        if (!inspectedValue.isEmpty()) {
            if (!keyOmitted) {
                if (key.length > 5 && inspectedValue.isBlock() && inspectedValue.isMultiline()) {
                    this.indentLines();
                    this.nl().i();
                } else {
                    this.sp();
                }
            }
            this.append(inspectedValue);
        }
    });

    // Intended to be redefined by a plugin that offers syntax highlighting:
    expect.addStyle('code', function (content, language) {
        this.text(content);
    });

    expect.addStyle('annotationBlock', function () {
        var pen = this.getContentFromArguments(arguments);
        var height = pen.size().height;

        if (height > 0) {
            this.block(function () {
                for (var i = 0; i < height; i += 1) {
                    if (0 < i) {
                        this.nl();
                    }
                    this.error('//');
                }
            });
            this.sp().block(pen);
        }
    });

    expect.addStyle('commentBlock', function () {
        var pen = this.getContentFromArguments(arguments);
        var height = pen.size().height;

        if (height > 0) {
            this.block(function () {
                for (var i = 0; i < height; i += 1) {
                    if (0 < i) {
                        this.nl();
                    }
                    this.jsComment('//');
                }
            });
            this.sp().block(pen);
        }
    });

    expect.addStyle('removedHighlight', function (content) {
        this.alt({
            text: function () {
                content.split(/(\n)/).forEach(function (fragment) {
                    if (fragment === '\n') {
                        this.nl();
                    } else {
                        this.block(function () {
                            this.text(fragment).nl().text(fragment.replace(/[\s\S]/g, '^'));
                        });
                    }
                }, this);
            },
            fallback: function () {
                this.diffRemovedHighlight(content);
            }
        });
    });

    expect.addStyle('match', function (content) {
        this.alt({
            text: function () {
                content.split(/(\n)/).forEach(function (fragment) {
                    if (fragment === '\n') {
                        this.nl();
                    } else {
                        this.block(function () {
                            this.text(fragment).nl().text(fragment.replace(/[\s\S]/g, '^'));
                        });
                    }
                }, this);
            },
            fallback: function () {
                this.diffAddedHighlight(content);
            }
        });
    });

    expect.addStyle('partialMatch', function (content) {
        this.alt({
            text: function () {
                // We haven't yet come up with a good styling for partial matches in text mode
                this.match(content);
            },
            fallback: function () {
                this.partialMatchHighlight(content);
            }
        });
    });

    expect.addStyle('shouldEqualError', function (expected) {
        this.error(typeof expected === 'undefined' ? 'should be' : 'should equal').sp().block(function () {
            this.appendInspected(expected);
        });
    });

    expect.addStyle('errorName', function (error) {
        if (typeof error.name === 'string' && error.name !== 'Error') {
            this.text(error.name);
        } else if (error.constructor && typeof error.constructor.name === 'string') {
            this.text(error.constructor.name);
        } else {
            this.text('Error');
        }
    });

    expect.addStyle('appendErrorMessage', function (error, options) {
        if (error && error.isUnexpected) {
            this.append(error.getErrorMessage(utils.extend({ output: this }, options)));
        } else {
            this.appendInspected(error);
        }
    });

    expect.addStyle('appendItems', function (items, separator) {
        var that = this;
        separator = separator || '';
        items.forEach(function (item, index) {
            if (0 < index) {
                that.append(separator);
            }
            that.appendInspected(item);
        });
    });

    expect.addStyle('magicPenLine', function (line, pen) {
        line.forEach(function (lineEntry, j) {
            if (j > 0) {
                this.nl();
            }
            if (lineEntry.style === 'text') {
                var styles = lineEntry.args.styles;
                if (pen && styles.length === 1 && typeof pen[styles[0]] === 'function') {
                    // Text with a single style also available as a method on the pen being inspected:
                    this
                        .text('.')
                        .jsFunctionName(styles[0])
                        .text('(')
                        .singleQuotedString(lineEntry.args.content)
                        .text(')');
                } else {
                    this
                        .text('.')
                        .jsFunctionName('text')
                        .text('(')
                        .singleQuotedString(lineEntry.args.content);
                    if (styles.length > 0) {
                        this
                            .text(', ')
                            .appendInspected(styles.length === 1 && Array.isArray(styles[0]) ? styles[0] : styles);
                    }
                    this.text(')');
                }
            } else {
                // lineEntry.style === 'block'
                this
                    .text('.')
                    .jsFunctionName('block').text('(').jsKeyword('function').text(' () {');
                if (lineEntry.args) {
                    this
                        .nl()
                        .indentLines()
                        .i()
                        .magicPen(pen, lineEntry.args)
                        .outdentLines()
                        .nl();
                }
                this.text('})');
            }
        }, this);
    });

    expect.addStyle('magicPen', function (pen, lines) {
        var isTopLevel = !lines;
        lines = lines || pen.output;
        this.block(function () {
            if (isTopLevel) {
                this.jsFunctionName('magicpen').text('(');
                if (pen.format) {
                    this.singleQuotedString(pen.format);
                }
                this.text(')');
            } else {
                this.jsKeyword('this');
            }
            if (!pen.isEmpty()) {
                var inspectOnMultipleLines = lines.length > 1 || lines[0].length > 1;
                if (inspectOnMultipleLines) {
                    this
                        .nl()
                        .indentLines()
                        .i();
                }
                this.block(function () {
                    lines.forEach(function (line, i) {
                        if (i > 0) {
                            this.text('.').jsFunctionName('nl').text('()').nl();
                        }
                        this.magicPenLine(line, pen);
                    }, this);
                    if (!isTopLevel) {
                        this.text(';');
                    }
                });
                if (inspectOnMultipleLines) {
                    this.outdentLines();
                }
            }
        });

        // If we're at the top level of a non-empty pen compatible with the current output,
        // render the output of the pen in a comment:
        if (isTopLevel && !pen.isEmpty() && (pen.format === this.format || !pen.format)) {
            this.sp().commentBlock(pen);
        }
    });

    expect.addStyle('diffFragment', function (text, baseStyle, specialCharStyle, markUpSpecialCharacters) {
        if (markUpSpecialCharacters) {
            text.split(specialCharRegExp).forEach(function (part) {
                if (specialCharRegExp.test(part)) {
                    this[specialCharStyle || baseStyle](utils.escapeChar(part));
                } else {
                    this[baseStyle](part);
                }
            }, this);
        } else {
            this[baseStyle](text);
        }
    });

    expect.addStyle('stringDiff', function (actual, expected, options) {
        options = options || {};
        var type = options.type || 'WordsWithSpace';
        var diffLines = [];
        var lastPart;
        stringDiff.diffLines(actual, expected).forEach(function (part) {
            if (lastPart && lastPart.added && part.removed) {
                diffLines.push({
                    oldValue: part.value,
                    newValue: lastPart.value,
                    replaced: true
                });
                lastPart = null;
            } else if (lastPart) {
                diffLines.push(lastPart);
                lastPart = part;
            } else {
                lastPart = part;
            }
        });
        if (lastPart) {
            diffLines.push(lastPart);
        }

        diffLines.forEach(function (part, index) {
            if (part.replaced) {
                var oldValue = part.oldValue;
                var newValue = part.newValue;
                var oldLine = this.clone();
                var newLine = this.clone();
                var oldEndsWithNewline = oldValue.slice(-1) === '\n';
                var newEndsWithNewline = newValue.slice(-1) === '\n';
                if (oldEndsWithNewline) {
                    oldValue = oldValue.slice(0, -1);
                }
                if (newEndsWithNewline) {
                    newValue = newValue.slice(0, -1);
                }

                stringDiff['diff' + type](oldValue, newValue).forEach(function (part) {
                    if (part.added) {
                        newLine.diffFragment(part.value, 'diffAddedHighlight', 'diffAddedSpecialChar', options.markUpSpecialCharacters);
                    } else if (part.removed) {
                        oldLine.diffFragment(part.value, 'diffRemovedHighlight', 'diffRemovedSpecialChar', options.markUpSpecialCharacters);
                    } else {
                        newLine.diffAddedLine(part.value);
                        oldLine.diffRemovedLine(part.value);
                    }
                });
                this.alt({
                    text: function () {
                        oldLine.prependLinesWith(this.clone().diffRemovedLine('-'));
                        newLine.prependLinesWith(this.clone().diffAddedLine('+'));
                    }
                });

                if (oldEndsWithNewline && !newEndsWithNewline) {
                    oldLine.diffRemovedSpecialChar('\\n');
                }

                if (newEndsWithNewline && !oldEndsWithNewline) {
                    newLine.diffAddedSpecialChar('\\n');
                }

                this.append(oldLine).nl().append(newLine);
                if (oldEndsWithNewline && index < diffLines.length - 1) {
                    this.nl();
                }
            } else {
                var endsWithNewline = /\n$/.test(part.value);
                var value = endsWithNewline ?
                    part.value.slice(0, -1) :
                    part.value;
                if (part.added) {
                    this.append(function () {
                        this.diffFragment(value, 'diffAddedLine', undefined, options.markUpSpecialCharacters);
                        var outer = this;
                        this.alt({
                            text: function () {
                                outer.prependLinesWith(function () {
                                    this.diffAddedLine('+');
                                });
                            }
                        });
                    });
                } else if (part.removed) {
                    this.append(function () {
                        this.diffFragment(value, 'diffRemovedLine', undefined, options.markUpSpecialCharacters);
                        var outer = this;
                        this.alt({
                            text: function () {
                                outer.prependLinesWith(function () {
                                    this.diffRemovedLine('-');
                                });
                            }
                        });
                    });
                } else {
                    var outer = this;
                    this.alt({
                        text: function () {
                            outer.text(value.replace(/^(.)/gm, ' $1'));
                        },
                        fallback: function () {
                            this.text(value);
                        }
                    });
                }
                if (endsWithNewline) {
                    this.nl();
                }
            }
        }, this);
    });
};
<|endoftext|>


<commit_before>import { ASSEMBLER_INSTANCE_PROPERTY_NAME } from './core'

export class TargetAssembler {
    /**
     * Create the assembler instance and assemble the specified object
     * Initializes the object by all passed init arguments
     * @param {*} [init]
     * @param {*} [rest]
     */
    constructor(init, ...rest) {
        this.assemble(init)
        if(rest.length) {
            rest.forEach(init => this.init(init))
        }
    }

    /**
     * Instantiate and initialize the specified object
     * @param {*} [init] initializing dictionary
     * @returns {Object|*}
     */
    assemble(init) {
        const target = this.create(init)
        this.setInstance(target)
        this.init(init)
        return target
    }

    /**
     * Create the specified object
     * @param {*} [init] initializing dictionary
     * @returns {init}
     */
    create(init) {
        return init && init.constructor === Object?
            this.constructor.create(init) :
            this.constructor.create()
    }

    /**
     * Initialize the object with the defined properties
     * @param {*} init initializing dictionary
     * @returns {Object|*} initialized node
     */
    init(init) {
        const target = this._target
        if(init) {
            const constructor = this.constructor
            for(let prop in init) {
                const value = init[prop]
                if(value !== undefined) {
                    if(prop in this) this[prop] = value
                    else if(prop in constructor) void null
                    else if(prop in target) target[prop] = value
                    else this._onMismatch(prop, value)
                }
            }
        }
    }

    /**
     * @param {Object|*} target
     */
    setInstance(target) {
        this._target = target
        target[ASSEMBLER_INSTANCE_PROPERTY_NAME] = this
    }

    /**
     * @param {Object|*} target
     * @returns {TargetAssembler|*}
     */
    getInstance(target) {
        return this.constructor.getInstance(target)
    }

    /**
     * The init mismatch handler
     * @param {String} prop mismatched property name
     * @param {*} value mismatched property value
     */
    _onMismatch(prop, value) {
        const propval = prop + `="${ value }"`
        const name = this.name
        const message = `The property ${ propval } is not found on the ${ name } instance:`
        console.warn(message, this)
    }

    /**
     * Stub for extension
     */
    static create() {
        throw Error(`Could not create an abstract ${ this.name } instance!`)
    }

    /**
     * @param {Object|*} target
     * @returns {TargetAssembler|*|null}
     */
    static getInstance(target) {
        return target[ASSEMBLER_INSTANCE_PROPERTY_NAME] || null
    }
}

Object.defineProperty(
    TargetAssembler.prototype,
    '_target',
    {
        writable : true,
        value : null
    })
<commit_msg>TargetAssembler: Safely invoke getInstance method<commit_after>import { ASSEMBLER_INSTANCE_PROPERTY_NAME } from './core'

export class TargetAssembler {
    /**
     * Create the assembler instance and assemble the specified object
     * Initializes the object by all passed init arguments
     * @param {*} [init]
     * @param {*} [rest]
     */
    constructor(init, ...rest) {
        this.assemble(init)
        if(rest.length) {
            rest.forEach(init => this.init(init))
        }
    }

    /**
     * Instantiate and initialize the specified object
     * @param {*} [init] initializing dictionary
     * @returns {Object|*}
     */
    assemble(init) {
        const target = this.create(init)
        this.setInstance(target)
        this.init(init)
        return target
    }

    /**
     * Create the specified object
     * @param {*} [init] initializing dictionary
     * @returns {init}
     */
    create(init) {
        return init && init.constructor === Object?
            this.constructor.create(init) :
            this.constructor.create()
    }

    /**
     * Initialize the object with the defined properties
     * @param {*} init initializing dictionary
     * @returns {Object|*} initialized node
     */
    init(init) {
        const target = this._target
        if(init) {
            const constructor = this.constructor
            for(let prop in init) {
                const value = init[prop]
                if(value !== undefined) {
                    if(prop in this) this[prop] = value
                    else if(prop in constructor) void null
                    else if(prop in target) target[prop] = value
                    else this._onMismatch(prop, value)
                }
            }
        }
    }

    /**
     * @param {Object|*} target
     */
    setInstance(target) {
        this._target = target
        target[ASSEMBLER_INSTANCE_PROPERTY_NAME] = this
    }

    /**
     * @param {Object|*} target
     * @returns {TargetAssembler|*}
     */
    getInstance(target) {
        return target? this.constructor.getInstance(target) : null
    }

    /**
     * The init mismatch handler
     * @param {String} prop mismatched property name
     * @param {*} value mismatched property value
     */
    _onMismatch(prop, value) {
        const propval = prop + `="${ value }"`
        const name = this.name
        const message = `The property ${ propval } is not found on the ${ name } instance:`
        console.warn(message, this)
    }

    /**
     * Stub for extension
     */
    static create() {
        throw Error(`Could not create an abstract ${ this.name } instance!`)
    }

    /**
     * @param {Object|*} target
     * @returns {TargetAssembler|*|null}
     */
    static getInstance(target) {
        return target[ASSEMBLER_INSTANCE_PROPERTY_NAME] || null
    }
}

Object.defineProperty(
    TargetAssembler.prototype,
    '_target',
    {
        writable : true,
        value : null
    })
<|endoftext|>