<commit_before>/* @flow */
import type {
  WebviewInputMessage,
  MessageInputContent,
  MessageInputFetching,
  MessageInputTyping,
} from '../webViewHandleUpdates';

// We pull out document.body in one place, and check it's not null, in order
// to provide that assertion to the type-checker.
const documentBody = document.body;
if (!documentBody) {
  throw new Error('No document.body element!');
}

const escapeHtml = (text: string): string => {
  const element = document.createElement('div');
  element.innerText = text;
  return element.innerHTML;
};

const sendMessage = (msg: Object) => {
  window.postMessage(JSON.stringify(msg), '*');
};

window.onerror = (message, source, line, column, error) => {
  if (window.enableWebViewErrorDisplay) {
    const elementJsError = document.getElementById('js-error');
    if (elementJsError) {
      elementJsError.innerHTML = [
        `Message: ${message}`,
        `Source: ${source}`,
        `Line: ${line}:${column}`,
        `Error: ${JSON.stringify(error)}`,
        '',
      ]
        .map(escapeHtml)
        .join('
');
    }
  }

  sendMessage({
    type: 'error',
    details: {
      message,
      source,
      line,
      column,
      error,
    },
  });

  return true;
};

let scrollEventsDisabled = true;
let lastTouchEventTimestamp = 0;
let lastTouchPositionX = -1;
let lastTouchPositionY = -1;

const showHideElement = (elementId: string, show: boolean) => {
  const element = document.getElementById(elementId);
  if (element) {
    element.classList.toggle('hidden', !show);
  }
};

const isNearPositions = (x1: number = 0, y1: number = 0, x2: number = 0, y2: number = 0): boolean =>
  Math.abs(x1 - x2) < 10 && Math.abs(y1 - y2) < 10;

const getMessageNode = (node: Element): Element => {
  let curNode = node;
  while (curNode && curNode.parentNode && curNode.parentNode !== documentBody) {
    curNode = curNode.parentNode;
  }
  return curNode;
};

const getMessageIdFromNode = (node: Node): number => {
  const msgNode = getMessageNode(node);
  return msgNode ? +msgNode.getAttribute('data-msg-id') : -1;
};

const scrollToBottom = () => {
  window.scroll({ left: 0, top: documentBody.scrollHeight, behavior: 'smooth' });
};

const isNearBottom = (): boolean =>
  documentBody.scrollHeight - 100 < documentBody.scrollTop + documentBody.clientHeight;

const scrollToBottomIfNearEnd = () => {
  if (isNearBottom()) {
    scrollToBottom();
  }
};

const scrollToAnchor = (anchor: number) => {
  const anchorNode = document.getElementById(`msg-${anchor}`);

  if (anchorNode) {
    anchorNode.scrollIntoView({ block: 'start' });
  } else {
    window.scroll({ left: 0, top: documentBody.scrollHeight + 200 });
  }
};

let height = documentBody.clientHeight;
window.addEventListener('resize', event => {
  const difference = height - documentBody.clientHeight;
  if (documentBody.scrollHeight !== documentBody.scrollTop + documentBody.clientHeight) {
    window.scrollBy({ left: 0, top: difference });
  }
  height = documentBody.clientHeight;
});

const isMessageNode = (node: Element): boolean =>
  !!node && node.getAttribute && node.hasAttribute('data-msg-id');

const getStartAndEndNodes = (): { start: number, end: number } => {
  const startNode = getMessageNode(document.elementFromPoint(200, 20));
  const endNode = getMessageNode(document.elementFromPoint(200, window.innerHeight - 20));

  return {
    start: isMessageNode(startNode)
      ? startNode.getAttribute('data-msg-id')
      : Number.MAX_SAFE_INTEGER,
    end: isMessageNode(endNode) ? endNode.getAttribute('data-msg-id') : 0,
  };
};

let prevNodes = getStartAndEndNodes();

const sendScrollMessage = () => {
  const currentNodes = getStartAndEndNodes();

  sendMessage({
    type: 'scroll',
    // See MessageListEventScroll for the meanings of these properties.
    offsetHeight: documentBody.offsetHeight,
    innerHeight: window.innerHeight,
    scrollY: window.scrollY,
    startMessageId: Math.min(prevNodes.start, currentNodes.start),
    endMessageId: Math.max(prevNodes.end, currentNodes.end),
  });
  prevNodes = currentNodes;
};

// If the message list is too short to scroll, fake a scroll event
// in order to cause the messages to be marked as read.
const sendScrollMessageIfListShort = () => {
  if (documentBody.scrollHeight === documentBody.clientHeight) {
    sendScrollMessage();
  }
};

const handleScrollEvent = () => {
  lastTouchEventTimestamp = 0;
  if (scrollEventsDisabled) {
    return;
  }

  sendScrollMessage();

  const nearEnd = documentBody.offsetHeight - window.scrollY - window.innerHeight > 100;
  showHideElement('scroll-bottom', nearEnd);
};

window.addEventListener('scroll', handleScrollEvent);

type ScrollTarget =
  | { type: 'none' }
  | { type: 'bottom' }
  | { type: 'anchor', anchor: number }
  | { type: 'preserve', msgId: number, prevBoundTop: number };

// Try to identify a message on screen and its location, so we can
// scroll the corresponding message to the same place afterward.
const findPreserveTarget = (): ScrollTarget => {
  // TODO magic numbers
  const msgNode = getMessageNode(document.elementFromPoint(200, 50));
  if (!msgNode) {
    // TODO log this -- it's an error which the user will notice.
    // (We don't attempt this unless there are messages already,
    // which we really want to keep steady in view.)
    return { type: 'none' };
  }
  const msgId = getMessageIdFromNode(msgNode);
  const prevBoundRect = msgNode.getBoundingClientRect();
  return { type: 'preserve', msgId, prevBoundTop: prevBoundRect.top };
};

// Scroll the given message to the same height it was at before.
const scrollToPreserve = (msgId: number, prevBoundTop: number) => {
  const newElement = document.getElementById(`msg-${msgId}`);
  if (!newElement) {
    // TODO log this -- it's an error which the user will notice.
    return;
  }
  const newBoundRect = newElement.getBoundingClientRect();
  window.scrollBy(0, newBoundRect.top - prevBoundTop);
};

const handleMessageContent = (msg: MessageInputContent) => {
  let target: ScrollTarget;
  if (msg.updateStrategy === 'replace') {
    target = { type: 'none' };
  } else if (msg.updateStrategy === 'scroll-to-anchor') {
    target = { type: 'anchor', anchor: msg.anchor };
  } else if (
    msg.updateStrategy === 'scroll-to-bottom-if-near-bottom'
    && /* align */ isNearBottom()
  ) {
    target = { type: 'bottom' };
  } else {
    // including 'default' and 'preserve-position'
    target = findPreserveTarget();
  }

  documentBody.innerHTML = msg.content;

  if (target.type === 'bottom') {
    scrollToBottom();
  } else if (target.type === 'anchor') {
    scrollToAnchor(target.anchor);
  } else if (target.type === 'preserve') {
    scrollToPreserve(target.msgId, target.prevBoundTop);
  }

  sendScrollMessageIfListShort();
};

const handleMessageFetching = (msg: MessageInputFetching) => {
  showHideElement('message-loading', msg.showMessagePlaceholders);
  showHideElement('spinner-older', msg.fetchingOlder);
  showHideElement('spinner-newer', msg.fetchingNewer);
};

const handleMessageTyping = (msg: MessageInputTyping) => {
  const elementTyping = document.getElementById('typing');
  if (elementTyping) {
    elementTyping.innerHTML = msg.content;
    setTimeout(() => scrollToBottomIfNearEnd());
  }
};

const messageHandlers = {
  content: handleMessageContent,
  fetching: handleMessageFetching,
  typing: handleMessageTyping,
};

// $FlowFixMe
document.addEventListener('message', e => {
  scrollEventsDisabled = true;
  // $FlowFixMe
  const messages: WebviewInputMessage[] = JSON.parse(e.data);
  messages.forEach((msg: WebviewInputMessage) => {
    // $FlowFixMe
    messageHandlers[msg.type](msg);
  });
  scrollEventsDisabled = false;
});

documentBody.addEventListener('click', e => {
  e.preventDefault();
  lastTouchEventTimestamp = 0;

  if (e.target.matches('.scroll-bottom')) {
    scrollToBottom();
    return;
  }

  if (e.target.matches('.avatar-img')) {
    sendMessage({
      type: 'avatar',
      fromEmail: e.target.getAttribute('data-email'),
    });
    return;
  }

  if (e.target.matches('.header')) {
    sendMessage({
      type: 'narrow',
      narrow: e.target.getAttribute('data-narrow'),
      id: e.target.getAttribute('data-id'),
    });
    return;
  }

  if (e.target.matches('a[target="_blank"] > img')) {
    sendMessage({
      type: 'image',
      src: e.target.parentNode.getAttribute('href'),
      messageId: getMessageIdFromNode(e.target),
    });
    return;
  }

  if (e.target.matches('a')) {
    sendMessage({
      type: 'url',
      href: e.target.getAttribute('href'),
      messageId: getMessageIdFromNode(e.target),
    });
    return;
  }

  if (e.target.parentNode.matches('a')) {
    sendMessage({
      type: 'url',
      href: e.target.parentNode.getAttribute('href'),
      messageId: getMessageIdFromNode(e.target.parentNode),
    });
    return;
  }

  if (e.target.matches('.reaction')) {
    sendMessage({
      type: 'reaction',
      name: e.target.getAttribute('data-name'),
      code: e.target.getAttribute('data-code'),
      reactionType: e.target.getAttribute('data-type'),
      messageId: getMessageIdFromNode(e.target),
      voted: e.target.classList.contains('self-voted'),
    });
  }
});

const handleLongPress = e => {
  // The logic that defines a "long press" in terms of raw touch events
  // is pretty subtle.  The `lastTouchEventTimestamp` and surrounding logic
  // are an attempt to define a long press.
  //
  // TODO: The logic around this "timestamp" is a bit obscure; it's
  // sometimes a real timestamp, other times 0 as sort of a boolean flag.
  // It would be good to clean it up to be clearer.
  //
  // At the same time, the logic is believed not to cover all the cases it
  // should; for example, multitouch events.  Better would be to either find
  // a library we can use which strives to handle all that complexity, or
  // get long-press events from the platform.
  if (!lastTouchEventTimestamp || Date.now() - lastTouchEventTimestamp < 500) {
    return;
  }

  lastTouchEventTimestamp = 0;

  sendMessage({
    type: 'longPress',
    target: e.target.matches('.header') ? 'header' : 'message',
    messageId: getMessageIdFromNode(e.target),
  });
};

documentBody.addEventListener('touchstart', e => {
  if (e.changedTouches[0].pageX < 20) {
    return;
  }

  lastTouchPositionX = e.changedTouches[0].pageX;
  lastTouchPositionY = e.changedTouches[0].pageY;
  lastTouchEventTimestamp = Date.now();
  setTimeout(() => handleLongPress(e), 500);
});

documentBody.addEventListener('touchend', e => {
  if (
    isNearPositions(
      lastTouchPositionX,
      lastTouchPositionY,
      e.changedTouches[0].pageX,
      e.changedTouches[0].pageY,
    )
  ) {
    lastTouchEventTimestamp = Date.now();
  }
});

documentBody.addEventListener('touchmove', e => {
  lastTouchEventTimestamp = 0;
});

documentBody.addEventListener('drag', e => {
  lastTouchEventTimestamp = 0;
});

const waitForBridge = () => {
  if (window.postMessage.length === 1) {
    sendMessage({ type: 'ready' });
  } else {
    setTimeout(waitForBridge, 10);
  }
};
waitForBridge();
<commit_msg>eslint: Adjust a manual-formatting hack for the new rule.<commit_after>/* @flow */
import type {
  WebviewInputMessage,
  MessageInputContent,
  MessageInputFetching,
  MessageInputTyping,
} from '../webViewHandleUpdates';

// We pull out document.body in one place, and check it's not null, in order
// to provide that assertion to the type-checker.
const documentBody = document.body;
if (!documentBody) {
  throw new Error('No document.body element!');
}

const escapeHtml = (text: string): string => {
  const element = document.createElement('div');
  element.innerText = text;
  return element.innerHTML;
};

const sendMessage = (msg: Object) => {
  window.postMessage(JSON.stringify(msg), '*');
};

window.onerror = (message, source, line, column, error) => {
  if (window.enableWebViewErrorDisplay) {
    const elementJsError = document.getElementById('js-error');
    if (elementJsError) {
      elementJsError.innerHTML = [
        `Message: ${message}`,
        `Source: ${source}`,
        `Line: ${line}:${column}`,
        `Error: ${JSON.stringify(error)}`,
        '',
      ]
        .map(escapeHtml)
        .join('
');
    }
  }

  sendMessage({
    type: 'error',
    details: {
      message,
      source,
      line,
      column,
      error,
    },
  });

  return true;
};

let scrollEventsDisabled = true;
let lastTouchEventTimestamp = 0;
let lastTouchPositionX = -1;
let lastTouchPositionY = -1;

const showHideElement = (elementId: string, show: boolean) => {
  const element = document.getElementById(elementId);
  if (element) {
    element.classList.toggle('hidden', !show);
  }
};

const isNearPositions = (x1: number = 0, y1: number = 0, x2: number = 0, y2: number = 0): boolean =>
  Math.abs(x1 - x2) < 10 && Math.abs(y1 - y2) < 10;

const getMessageNode = (node: Element): Element => {
  let curNode = node;
  while (curNode && curNode.parentNode && curNode.parentNode !== documentBody) {
    curNode = curNode.parentNode;
  }
  return curNode;
};

const getMessageIdFromNode = (node: Node): number => {
  const msgNode = getMessageNode(node);
  return msgNode ? +msgNode.getAttribute('data-msg-id') : -1;
};

const scrollToBottom = () => {
  window.scroll({ left: 0, top: documentBody.scrollHeight, behavior: 'smooth' });
};

const isNearBottom = (): boolean =>
  documentBody.scrollHeight - 100 < documentBody.scrollTop + documentBody.clientHeight;

const scrollToBottomIfNearEnd = () => {
  if (isNearBottom()) {
    scrollToBottom();
  }
};

const scrollToAnchor = (anchor: number) => {
  const anchorNode = document.getElementById(`msg-${anchor}`);

  if (anchorNode) {
    anchorNode.scrollIntoView({ block: 'start' });
  } else {
    window.scroll({ left: 0, top: documentBody.scrollHeight + 200 });
  }
};

let height = documentBody.clientHeight;
window.addEventListener('resize', event => {
  const difference = height - documentBody.clientHeight;
  if (documentBody.scrollHeight !== documentBody.scrollTop + documentBody.clientHeight) {
    window.scrollBy({ left: 0, top: difference });
  }
  height = documentBody.clientHeight;
});

const isMessageNode = (node: Element): boolean =>
  !!node && node.getAttribute && node.hasAttribute('data-msg-id');

const getStartAndEndNodes = (): { start: number, end: number } => {
  const startNode = getMessageNode(document.elementFromPoint(200, 20));
  const endNode = getMessageNode(document.elementFromPoint(200, window.innerHeight - 20));

  return {
    start: isMessageNode(startNode)
      ? startNode.getAttribute('data-msg-id')
      : Number.MAX_SAFE_INTEGER,
    end: isMessageNode(endNode) ? endNode.getAttribute('data-msg-id') : 0,
  };
};

let prevNodes = getStartAndEndNodes();

const sendScrollMessage = () => {
  const currentNodes = getStartAndEndNodes();

  sendMessage({
    type: 'scroll',
    // See MessageListEventScroll for the meanings of these properties.
    offsetHeight: documentBody.offsetHeight,
    innerHeight: window.innerHeight,
    scrollY: window.scrollY,
    startMessageId: Math.min(prevNodes.start, currentNodes.start),
    endMessageId: Math.max(prevNodes.end, currentNodes.end),
  });
  prevNodes = currentNodes;
};

// If the message list is too short to scroll, fake a scroll event
// in order to cause the messages to be marked as read.
const sendScrollMessageIfListShort = () => {
  if (documentBody.scrollHeight === documentBody.clientHeight) {
    sendScrollMessage();
  }
};

const handleScrollEvent = () => {
  lastTouchEventTimestamp = 0;
  if (scrollEventsDisabled) {
    return;
  }

  sendScrollMessage();

  const nearEnd = documentBody.offsetHeight - window.scrollY - window.innerHeight > 100;
  showHideElement('scroll-bottom', nearEnd);
};

window.addEventListener('scroll', handleScrollEvent);

type ScrollTarget =
  | { type: 'none' }
  | { type: 'bottom' }
  | { type: 'anchor', anchor: number }
  | { type: 'preserve', msgId: number, prevBoundTop: number };

// Try to identify a message on screen and its location, so we can
// scroll the corresponding message to the same place afterward.
const findPreserveTarget = (): ScrollTarget => {
  // TODO magic numbers
  const msgNode = getMessageNode(document.elementFromPoint(200, 50));
  if (!msgNode) {
    // TODO log this -- it's an error which the user will notice.
    // (We don't attempt this unless there are messages already,
    // which we really want to keep steady in view.)
    return { type: 'none' };
  }
  const msgId = getMessageIdFromNode(msgNode);
  const prevBoundRect = msgNode.getBoundingClientRect();
  return { type: 'preserve', msgId, prevBoundTop: prevBoundRect.top };
};

// Scroll the given message to the same height it was at before.
const scrollToPreserve = (msgId: number, prevBoundTop: number) => {
  const newElement = document.getElementById(`msg-${msgId}`);
  if (!newElement) {
    // TODO log this -- it's an error which the user will notice.
    return;
  }
  const newBoundRect = newElement.getBoundingClientRect();
  window.scrollBy(0, newBoundRect.top - prevBoundTop);
};

const handleMessageContent = (msg: MessageInputContent) => {
  let target: ScrollTarget;
  if (msg.updateStrategy === 'replace') {
    target = { type: 'none' };
  } else if (msg.updateStrategy === 'scroll-to-anchor') {
    target = { type: 'anchor', anchor: msg.anchor };
  } else if (
    msg.updateStrategy === 'scroll-to-bottom-if-near-bottom'
    && isNearBottom() /* align */
  ) {
    target = { type: 'bottom' };
  } else {
    // including 'default' and 'preserve-position'
    target = findPreserveTarget();
  }

  documentBody.innerHTML = msg.content;

  if (target.type === 'bottom') {
    scrollToBottom();
  } else if (target.type === 'anchor') {
    scrollToAnchor(target.anchor);
  } else if (target.type === 'preserve') {
    scrollToPreserve(target.msgId, target.prevBoundTop);
  }

  sendScrollMessageIfListShort();
};

const handleMessageFetching = (msg: MessageInputFetching) => {
  showHideElement('message-loading', msg.showMessagePlaceholders);
  showHideElement('spinner-older', msg.fetchingOlder);
  showHideElement('spinner-newer', msg.fetchingNewer);
};

const handleMessageTyping = (msg: MessageInputTyping) => {
  const elementTyping = document.getElementById('typing');
  if (elementTyping) {
    elementTyping.innerHTML = msg.content;
    setTimeout(() => scrollToBottomIfNearEnd());
  }
};

const messageHandlers = {
  content: handleMessageContent,
  fetching: handleMessageFetching,
  typing: handleMessageTyping,
};

// $FlowFixMe
document.addEventListener('message', e => {
  scrollEventsDisabled = true;
  // $FlowFixMe
  const messages: WebviewInputMessage[] = JSON.parse(e.data);
  messages.forEach((msg: WebviewInputMessage) => {
    // $FlowFixMe
    messageHandlers[msg.type](msg);
  });
  scrollEventsDisabled = false;
});

documentBody.addEventListener('click', e => {
  e.preventDefault();
  lastTouchEventTimestamp = 0;

  if (e.target.matches('.scroll-bottom')) {
    scrollToBottom();
    return;
  }

  if (e.target.matches('.avatar-img')) {
    sendMessage({
      type: 'avatar',
      fromEmail: e.target.getAttribute('data-email'),
    });
    return;
  }

  if (e.target.matches('.header')) {
    sendMessage({
      type: 'narrow',
      narrow: e.target.getAttribute('data-narrow'),
      id: e.target.getAttribute('data-id'),
    });
    return;
  }

  if (e.target.matches('a[target="_blank"] > img')) {
    sendMessage({
      type: 'image',
      src: e.target.parentNode.getAttribute('href'),
      messageId: getMessageIdFromNode(e.target),
    });
    return;
  }

  if (e.target.matches('a')) {
    sendMessage({
      type: 'url',
      href: e.target.getAttribute('href'),
      messageId: getMessageIdFromNode(e.target),
    });
    return;
  }

  if (e.target.parentNode.matches('a')) {
    sendMessage({
      type: 'url',
      href: e.target.parentNode.getAttribute('href'),
      messageId: getMessageIdFromNode(e.target.parentNode),
    });
    return;
  }

  if (e.target.matches('.reaction')) {
    sendMessage({
      type: 'reaction',
      name: e.target.getAttribute('data-name'),
      code: e.target.getAttribute('data-code'),
      reactionType: e.target.getAttribute('data-type'),
      messageId: getMessageIdFromNode(e.target),
      voted: e.target.classList.contains('self-voted'),
    });
  }
});

const handleLongPress = e => {
  // The logic that defines a "long press" in terms of raw touch events
  // is pretty subtle.  The `lastTouchEventTimestamp` and surrounding logic
  // are an attempt to define a long press.
  //
  // TODO: The logic around this "timestamp" is a bit obscure; it's
  // sometimes a real timestamp, other times 0 as sort of a boolean flag.
  // It would be good to clean it up to be clearer.
  //
  // At the same time, the logic is believed not to cover all the cases it
  // should; for example, multitouch events.  Better would be to either find
  // a library we can use which strives to handle all that complexity, or
  // get long-press events from the platform.
  if (!lastTouchEventTimestamp || Date.now() - lastTouchEventTimestamp < 500) {
    return;
  }

  lastTouchEventTimestamp = 0;

  sendMessage({
    type: 'longPress',
    target: e.target.matches('.header') ? 'header' : 'message',
    messageId: getMessageIdFromNode(e.target),
  });
};

documentBody.addEventListener('touchstart', e => {
  if (e.changedTouches[0].pageX < 20) {
    return;
  }

  lastTouchPositionX = e.changedTouches[0].pageX;
  lastTouchPositionY = e.changedTouches[0].pageY;
  lastTouchEventTimestamp = Date.now();
  setTimeout(() => handleLongPress(e), 500);
});

documentBody.addEventListener('touchend', e => {
  if (
    isNearPositions(
      lastTouchPositionX,
      lastTouchPositionY,
      e.changedTouches[0].pageX,
      e.changedTouches[0].pageY,
    )
  ) {
    lastTouchEventTimestamp = Date.now();
  }
});

documentBody.addEventListener('touchmove', e => {
  lastTouchEventTimestamp = 0;
});

documentBody.addEventListener('drag', e => {
  lastTouchEventTimestamp = 0;
});

const waitForBridge = () => {
  if (window.postMessage.length === 1) {
    sendMessage({ type: 'ready' });
  } else {
    setTimeout(waitForBridge, 10);
  }
};
waitForBridge();
<|endoftext|>


<commit_before>(function( jStat, Math ) {

	// for quick reference
var isFunction = jStat.utils.isFunction,

	// ascending functions for sort
	ascNum = function( a, b ) { return a - b; };

jStat.extend({

	// sum of an array
	sum : function( arr ) {
		var sum = 0,
			i = arr.length,
			tmp;
		while ( --i >= 0 ) {
			sum += arr[i];
		}
		return sum;
	},

	// sum squared
	sumsqrd : function( arr ) {
		var sum = 0,
			i = arr.length;
		while ( --i >= 0 ) sum += arr[i] * arr[i];
		return sum;
	},

	// sum of squared errors of prediction (SSE)
	sumsqerr : function( arr ) {
		var mean = jStat.mean( arr ),
			sum = 0,
			i = arr.length,
			tmp;
		while ( --i >= 0 ) {
			tmp = arr[i] - mean;
			sum += tmp * tmp;
		}
		return sum;
	},

	// product of an array
	product : function( arr ) {
		var prod = 1,
			i = arr.length;
		while ( --i >= 0 ) prod *= arr[i];
		return prod;
	},

	// minimum value of an array
	min : function( arr ) {
		var low = arr[0],
			i = 0;
		while ( ++i < arr.length )
			if ( arr[i] < low ) low = arr[i];
		return low;
	},

	// maximum value of an array
	max : function( arr ) {
		var high = arr[0],
			i = 0;
		while ( ++i < arr.length )
			if ( arr[i] > high ) high = arr[i];
		return high;
	},

	// mean value of an array
	mean : function( arr ) {
		return jStat.sum( arr ) / arr.length;
	},

	// mean squared error (MSE)
	meansqerr : function( arr ) {
		return jStat.sumsqerr( arr ) / arr.length;
	},

	// geometric mean of an array
	geomean : function( arr ) {
		return Math.pow( jStat.product( arr ), 1 / arr.length );
	},

	// median of an array
	median : function( arr ) {
		var arrlen = arr.length,
			_arr = arr.slice().sort( ascNum );
		// check if array is even or odd, then return the appropriate
		return !( arrlen & 1 )
			? ( _arr[( arrlen / 2 ) - 1 ] + _arr[( arrlen / 2 )]) / 2
		: _arr[( arrlen / 2 ) | 0 ];
	},

	// cumulative sum of an array
	cumsum : function( arr ) {
		var len = arr.length,
			sums = new Array( len ),
			i = 1;
		sums[0] = arr[0];
		for ( ; i < len; i++ ) {
			sums[i] = sums[i - 1] + arr[i];
		}
		return sums;
	},

	// successive differences of a sequence
	diff : function( arr ) {
		var diffs = [],
			arrLen = arr.length,
			i = 1;
		for ( i = 1; i < arrLen; i++ ) {
			diffs.push( arr[i] - arr[i-1]);
		}
		return diffs;
	},

	// mode of an array
	// if there are multiple modes of an array, return all of them
	// is this the appropriate way of handling it?
	mode : function( arr ) {
		var arrLen = arr.length,
			_arr = arr.slice().sort( ascNum ),
			count = 1,
			maxCount = 0,
			numMaxCount = 0,
			i = 0,
			mode_arr = [];
		for ( ; i < arrLen; i++ ) {
			if ( _arr[ i ] === _arr[ i + 1 ] ) {
				count++;
			} else {
				if ( count > maxCount ) {
					mode_arr = [ _arr[i] ];
					maxCount = count;
					numMaxCount = 0;
				}
				// are there multiple max counts
				else if ( count === maxCount ) {
					mode_arr.push(_arr[i]);
					numMaxCount++;
				}

				// resetting count for new value in array
				count = 1;
			}
		}
		return ( numMaxCount === 0 ) ? mode_arr[0] : mode_arr;
	},

	// range of an array
	range : function( arr ) {
		return jStat.max( arr ) - jStat.min( arr );
	},

	// variance of an array
	// flag indicates population vs sample
	variance : function( arr, flag ) {
		return jStat.sumsqerr( arr ) / ( arr.length - ( flag ? 1 : 0 ));
	},

	// standard deviation of an array
	// flag indicates population vs sample
	stdev : function( arr, flag ) {
		return Math.sqrt( jStat.variance( arr, flag ));
	},

	// mean deviation (mean absolute deviation) of an array
	meandev : function( arr ) {
		var devSum = 0,
			mean = jStat.mean( arr ),
			i = arr.length - 1;
		for ( ; i >= 0; i-- ) {
			devSum += Math.abs( arr[i] - mean );
		}
		return devSum / arr.length;
	},

	// median deviation (median absolute deviation) of an array
	meddev : function( arr ) {
		var devSum = 0,
			median = jStat.median( arr ),
			i = arr.length - 1;
		for ( ; i >= 0; i-- ) {
			devSum += Math.abs( arr[i] - median );
		}
		return devSum / arr.length;
	},

	// coefficient of variation
	coeffvar : function( arr ) {
		return jStat.stdev( arr ) / jStat.mean( arr );
	},

	// quartiles of an array
	quartiles : function( arr ) {
		var arrlen = arr.length,
			_arr = arr.slice().sort( ascNum );
		return [
			_arr[ Math.round(( arrlen ) / 4 ) - 1 ],
			_arr[ Math.round(( arrlen ) / 2 ) - 1 ],
			_arr[ Math.round(( arrlen ) * 3 / 4 ) - 1 ]
		];
	},

	// covariance of two arrays
	covariance : function( arr1, arr2 ) {
		var u = jStat.mean( arr1 ),
			v = jStat.mean( arr2 ),
			arr1Len = arr1.length,
			sq_dev = new Array(arr1Len),
			i = 0;
		for ( ; i < arr1Len; i++ ) {
			sq_dev[i] = ( arr1[i] - u ) * ( arr2[i] - v );
		}
		return jStat.sum( sq_dev ) / ( arr1Len - 1 );
	},

	// population correlation coefficient
	corrcoeff : function( arr1, arr2 ) {
		return jStat.covariance( arr1, arr2 ) / jStat.stdev( arr1, 1 ) / jStat.stdev( arr2, 1 );
	}
});

// extend jStat.fn with methods which don't require arguments and work on columns
(function( funcs ) {
	for ( var i = 0; i < funcs.length; i++ ) (function( passfunc ) {
		// if a matrix is passed, automatically assume operation should be done on the columns
		jStat.fn[ passfunc ] = function( fullbool, func ) {
			var arr = [],
				i = 0,
				tmpthis = this;
			// assignment reassignation depending on how parameters were passed in
			if ( isFunction( fullbool )) {
				func = fullbool;
				fullbool = false;
			}
			// check if a callback was passed with the function
			if ( func ) {
				setTimeout( function() {
					func.call( tmpthis, jStat.fn[ passfunc ].call( tmpthis, fullbool ));
				}, 15 );
				return this;
			}
			// check if matrix and run calculations
			if ( this.length > 1 ) {
				tmpthis = fullbool === true ? this : this.transpose();
				for ( ; i < tmpthis.length; i++ )
					arr[i] = jStat[ passfunc ]( tmpthis[i] );

				return fullbool === true ? jStat[ passfunc ]( arr ) : arr;
			}
			// pass fullbool if only vector, not a matrix. for variance and stdev
			return jStat[ passfunc ]( this[0], fullbool );
		};
	})( funcs[i] );
})( 'sum sumsqrd sumsqerr product min max mean meansqerr geomean median diff mode range variance stdev meandev meddev coeffvar quartiles'.split( ' ' ));

// extend jStat.fn with method for calculating cumulative sums, as it does not run again in case of true
(function( funcs ) {

	// if a matrix is passed, automatically assume operation should be done on the columns
	jStat.fn.cumsum = function( fullbool, func ) {
		var arr = [],
			i = 0,
			tmpthis = this;
		// assignment reassignation depending on how parameters were passed in
		if ( isFunction( fullbool )) {
			func = fullbool;
			fullbool = false;
		}
		// check if a callback was passed with the function
		if ( func ) {
			setTimeout( function() {
				func.call( tmpthis, jStat.fn.cumsum.call( tmpthis, fullbool ));
			}, 15 );
			return this;
		}
		// check if matrix and run calculations
		if ( this.length > 1 ) {
			tmpthis = fullbool === true ? this : this.transpose();
			for ( ; i < tmpthis.length; i++ )
				arr[i] = jStat.cumsum( tmpthis[i] );
			return arr;
		}
	};
})('cumsum');

}( this.jStat, Math ));
<commit_msg>deleting general function statements<commit_after>(function( jStat, Math ) {

	// for quick reference
var isFunction = jStat.utils.isFunction,

	// ascending functions for sort
	ascNum = function( a, b ) { return a - b; };

jStat.extend({

	// sum of an array
	sum : function( arr ) {
		var sum = 0,
			i = arr.length,
			tmp;
		while ( --i >= 0 ) {
			sum += arr[i];
		}
		return sum;
	},

	// sum squared
	sumsqrd : function( arr ) {
		var sum = 0,
			i = arr.length;
		while ( --i >= 0 ) sum += arr[i] * arr[i];
		return sum;
	},

	// sum of squared errors of prediction (SSE)
	sumsqerr : function( arr ) {
		var mean = jStat.mean( arr ),
			sum = 0,
			i = arr.length,
			tmp;
		while ( --i >= 0 ) {
			tmp = arr[i] - mean;
			sum += tmp * tmp;
		}
		return sum;
	},

	// product of an array
	product : function( arr ) {
		var prod = 1,
			i = arr.length;
		while ( --i >= 0 ) prod *= arr[i];
		return prod;
	},

	// minimum value of an array
	min : function( arr ) {
		var low = arr[0],
			i = 0;
		while ( ++i < arr.length )
			if ( arr[i] < low ) low = arr[i];
		return low;
	},

	// maximum value of an array
	max : function( arr ) {
		var high = arr[0],
			i = 0;
		while ( ++i < arr.length )
			if ( arr[i] > high ) high = arr[i];
		return high;
	},

	// mean value of an array
	mean : function( arr ) {
		return jStat.sum( arr ) / arr.length;
	},

	// mean squared error (MSE)
	meansqerr : function( arr ) {
		return jStat.sumsqerr( arr ) / arr.length;
	},

	// geometric mean of an array
	geomean : function( arr ) {
		return Math.pow( jStat.product( arr ), 1 / arr.length );
	},

	// median of an array
	median : function( arr ) {
		var arrlen = arr.length,
			_arr = arr.slice().sort( ascNum );
		// check if array is even or odd, then return the appropriate
		return !( arrlen & 1 )
			? ( _arr[( arrlen / 2 ) - 1 ] + _arr[( arrlen / 2 )]) / 2
		: _arr[( arrlen / 2 ) | 0 ];
	},

	// cumulative sum of an array
	cumsum : function( arr ) {
		var len = arr.length,
			sums = new Array( len ),
			i = 1;
		sums[0] = arr[0];
		for ( ; i < len; i++ ) {
			sums[i] = sums[i - 1] + arr[i];
		}
		return sums;
	},

	// successive differences of a sequence
	diff : function( arr ) {
		var diffs = [],
			arrLen = arr.length,
			i = 1;
		for ( i = 1; i < arrLen; i++ ) {
			diffs.push( arr[i] - arr[i-1]);
		}
		return diffs;
	},

	// mode of an array
	// if there are multiple modes of an array, return all of them
	// is this the appropriate way of handling it?
	mode : function( arr ) {
		var arrLen = arr.length,
			_arr = arr.slice().sort( ascNum ),
			count = 1,
			maxCount = 0,
			numMaxCount = 0,
			i = 0,
			mode_arr = [];
		for ( ; i < arrLen; i++ ) {
			if ( _arr[ i ] === _arr[ i + 1 ] ) {
				count++;
			} else {
				if ( count > maxCount ) {
					mode_arr = [ _arr[i] ];
					maxCount = count;
					numMaxCount = 0;
				}
				// are there multiple max counts
				else if ( count === maxCount ) {
					mode_arr.push(_arr[i]);
					numMaxCount++;
				}

				// resetting count for new value in array
				count = 1;
			}
		}
		return ( numMaxCount === 0 ) ? mode_arr[0] : mode_arr;
	},

	// range of an array
	range : function( arr ) {
		return jStat.max( arr ) - jStat.min( arr );
	},

	// variance of an array
	// flag indicates population vs sample
	variance : function( arr, flag ) {
		return jStat.sumsqerr( arr ) / ( arr.length - ( flag ? 1 : 0 ));
	},

	// standard deviation of an array
	// flag indicates population vs sample
	stdev : function( arr, flag ) {
		return Math.sqrt( jStat.variance( arr, flag ));
	},

	// mean deviation (mean absolute deviation) of an array
	meandev : function( arr ) {
		var devSum = 0,
			mean = jStat.mean( arr ),
			i = arr.length - 1;
		for ( ; i >= 0; i-- ) {
			devSum += Math.abs( arr[i] - mean );
		}
		return devSum / arr.length;
	},

	// median deviation (median absolute deviation) of an array
	meddev : function( arr ) {
		var devSum = 0,
			median = jStat.median( arr ),
			i = arr.length - 1;
		for ( ; i >= 0; i-- ) {
			devSum += Math.abs( arr[i] - median );
		}
		return devSum / arr.length;
	},

	// coefficient of variation
	coeffvar : function( arr ) {
		return jStat.stdev( arr ) / jStat.mean( arr );
	},

	// quartiles of an array
	quartiles : function( arr ) {
		var arrlen = arr.length,
			_arr = arr.slice().sort( ascNum );
		return [
			_arr[ Math.round(( arrlen ) / 4 ) - 1 ],
			_arr[ Math.round(( arrlen ) / 2 ) - 1 ],
			_arr[ Math.round(( arrlen ) * 3 / 4 ) - 1 ]
		];
	},

	// covariance of two arrays
	covariance : function( arr1, arr2 ) {
		var u = jStat.mean( arr1 ),
			v = jStat.mean( arr2 ),
			arr1Len = arr1.length,
			sq_dev = new Array(arr1Len),
			i = 0;
		for ( ; i < arr1Len; i++ ) {
			sq_dev[i] = ( arr1[i] - u ) * ( arr2[i] - v );
		}
		return jStat.sum( sq_dev ) / ( arr1Len - 1 );
	},

	// population correlation coefficient
	corrcoeff : function( arr1, arr2 ) {
		return jStat.covariance( arr1, arr2 ) / jStat.stdev( arr1, 1 ) / jStat.stdev( arr2, 1 );
	}
});

// extend jStat.fn with methods which don't require arguments and work on columns
(function( funcs ) {
	for ( var i = 0; i < funcs.length; i++ ) (function( passfunc ) {
		// if a matrix is passed, automatically assume operation should be done on the columns
		jStat.fn[ passfunc ] = function( fullbool, func ) {
			var arr = [],
				i = 0,
				tmpthis = this;
			// assignment reassignation depending on how parameters were passed in
			if ( isFunction( fullbool )) {
				func = fullbool;
				fullbool = false;
			}
			// check if a callback was passed with the function
			if ( func ) {
				setTimeout( function() {
					func.call( tmpthis, jStat.fn[ passfunc ].call( tmpthis, fullbool ));
				}, 15 );
				return this;
			}
			// check if matrix and run calculations
			if ( this.length > 1 ) {
				tmpthis = fullbool === true ? this : this.transpose();
				for ( ; i < tmpthis.length; i++ )
					arr[i] = jStat[ passfunc ]( tmpthis[i] );

				return fullbool === true ? jStat[ passfunc ]( arr ) : arr;
			}
			// pass fullbool if only vector, not a matrix. for variance and stdev
			return jStat[ passfunc ]( this[0], fullbool );
		};
	})( funcs[i] );
})( 'sum sumsqrd sumsqerr product min max mean meansqerr geomean median diff mode range variance stdev meandev meddev coeffvar quartiles'.split( ' ' ));

// extend jStat.fn with method for calculating cumulative sums, as it does not run again in case of true
// if a matrix is passed, automatically assume operation should be done on the columns
jStat.fn.cumsum = function( fullbool, func ) {
	var arr = [],
		i = 0,
		tmpthis = this;
	// assignment reassignation depending on how parameters were passed in
	if ( isFunction( fullbool )) {
		func = fullbool;
		fullbool = false;
	}
	// check if a callback was passed with the function
	if ( func ) {
		setTimeout( function() {
			func.call( tmpthis, jStat.fn.cumsum.call( tmpthis, fullbool ));
		}, 15 );
		return this;
	}
	// check if matrix and run calculations
	if ( this.length > 1 ) {
		tmpthis = fullbool === true ? this : this.transpose();
		for ( ; i < tmpthis.length; i++ )
			arr[i] = jStat.cumsum( tmpthis[i] );
		return arr;
	}
};

}( this.jStat, Math ));
<|endoftext|>


<commit_before>function getInternetExplorerVersion()
    // Returns the version of Internet Explorer or a -1
    // (indicating the use of another browser).
{
    var rv = -1; // Return value assumes failure.
    if (navigator.appName == 'Microsoft Internet Explorer') {
        var ua = navigator.userAgent;
        var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
        if (re.exec(ua) != null)
            rv = parseFloat(RegExp.$1);
    }
    return rv;
}
;(function ($) {
    var overlay, holdselect, freeselect, loader, showloader, timer,
        isIE6 = (navigator.userAgent.toLowerCase().indexOf('msie 6') != -1) && getInternetExplorerVersion() < 7 && !window.XMLHttpRequest;
    $.Overlay = function (settings) {
        settings = jQuery.extend({
            canclose: true,
            showloader: false,
            loaderimg: './img/bar90.gif',
            overlay_opacity: 0.7,
            canTimeout: false,
            timeout: 10000,
            z_index:9000,
            tiemoutcallback: null
        }, settings);

        this.show = function () {
            overlay = $('

');
            showloader = settings.showloader;
            if (isIE6) {
                holdselect = $('');
                freeselect = $('');
            }
            overlay.css({
                'position': (isIE6 ? 'absolute' : 'fixed'),
                'left': 0,
                'top': 0,
                'width': $(window).width(),
                'height': (isIE6 ? $('body').height() : $(window).height()),
                'background': '#777777',
                'opacity': settings.overlay_opacity,
                'z-index': settings.z_index
            });

            overlay.appendTo('body');

            if (showloader) {
                loader = $('

loading……

');
                loader.css({
                    'position': (isIE6 ? 'absolute' : 'fixed'),
                    'left': ($(window).width() - loader.width()) / 2,
                    'top': ($(window).height() - loader.height()) / 2,
                    'z-index': settings.z_index + 10
                });

                loader.appendTo('body');
            }
            overlay.fadeIn('fast', function () {
                if (settings.showloader) {
                    loader.show();
                }
            });

            if (isIE6) {
                $('body').append(holdselect);
            }

            if (settings.canclose) {
                overlay.bind('click', function () {
                    $.Overlay.close();
                });
            }
        };

        this.show();

        if (settings.canTimeout) {
            timer = Timer(function () {
                $.Overlay.close();
                if (null == settings.timeoutcallback) {
                    alert('timeout!');
                } else {
                    settings.timeoutcallback();
                }
            }, settings.timeout);
        }

        var t;
    };
    $.Overlay.close = function () {
        if (null != timer) {
            timer.pause();
        }

        if (showloader) {
            loader.remove();
        }
        overlay.fadeOut('fast');
        overlay.unbind('click');
        overlay.remove();

        if (isIE6) {
            holdselect.remove();
            $('body').append(freeselect);
            freeselect.remove();
        }
    };
})(jQuery);
<commit_msg>fix bug<commit_after>function getInternetExplorerVersion()
    // Returns the version of Internet Explorer or a -1
    // (indicating the use of another browser).
{
    var rv = -1; // Return value assumes failure.
    if (navigator.appName == 'Microsoft Internet Explorer') {
        var ua = navigator.userAgent;
        var re = new RegExp("MSIE ([0-9]{1,}[\.0-9]{0,})");
        if (re.exec(ua) != null)
            rv = parseFloat(RegExp.$1);
    }
    return rv;
}
; (function ($) {
    var id = 'jquery_overlay_place_holder';
    var overlay, holdselect, freeselect, loader, showloader, timer,
        isIE6 = (navigator.userAgent.toLowerCase().indexOf('msie 6') != -1) && getInternetExplorerVersion() < 7 && !window.XMLHttpRequest;
    $.Overlay = function (settings) {
        settings = jQuery.extend({
            canclose: true,
            showloader: false,
            loaderimg: '/content/images/bar90.gif',
            overlay_opacity: 0.7,
            canTimeout: false,
            timeout: 10000,
            z_index:9000,
            tiemoutcallback: null
        }, settings);

        this.show = function () {
            if (isIE6) {
                holdselect = $('');
                freeselect = $('');
            }

            showloader = settings.showloader;

            if ($('#' + id).length == 0) {
                overlay = $('

');

                overlay.css({
                    'position': (isIE6 ? 'absolute' : 'fixed'),
                    'left': 0,
                    'top': 0,
                    'width': $(window).width(),
                    'height': (isIE6 ? $('body').height() : $(window).height()),
                    'background': '#777777',
                    'opacity': settings.overlay_opacity,
                    'z-index': settings.z_index
                });

                overlay.appendTo('body');

                if (showloader) {
                    loader = $('

数据加载中……

');
                    loader.css({
                        'position': (isIE6 ? 'absolute' : 'fixed'),
                        'left': ($(window).width() - loader.width()) / 2,
                        'top': ($(window).height() - loader.height()) / 2,
                        'z-index': settings.z_index + 10
                    });

                    loader.appendTo('body');
                }
            };

            overlay.fadeIn('fast', function () {
                if (settings.showloader) {
                    loader.show();
                }
            });

            if (isIE6) {
                $('body').append(holdselect);
            }

            if (settings.canclose) {
                overlay.bind('click', function () {
                    $.Overlay.close();
                });
            }
        };

        this.show();

        if (settings.canTimeout) {
            timer = Timer(function () {
                $.Overlay.close();
                if (null == settings.timeoutcallback) {
                    alert('操作超时!');
                } else {
                    settings.timeoutcallback();
                }
            }, settings.timeout);
        }

        var t;
    };

    $.Overlay.close = function () {
        if (null != timer) {
            timer.pause();
        }

        overlay.fadeOut('fast', function () {
            if (showloader) {
                loader.hide();
            }
        });

        overlay.unbind('click');

        overlay.hide();

        if (isIE6) {
            holdselect.remove();
            $('body').append(freeselect);
            freeselect.remove();
        }
    };
})(jQuery);
<|endoftext|>


<commit_before>var assert = require('assert')
var crypto = require('crypto')
var networks = require('./networks')

var Address = require('./address')
var HDNode = require('./hdnode')
var Transaction = require('./transaction')

function Wallet(seed, network) {
  network = network || networks.bitcoin

  // Stored in a closure to make accidental serialization less likely
  var masterkey = null
  var me = this
  var accountZero = null
  var internalAccount = null
  var externalAccount = null

  // Addresses
  this.addresses = []
  this.changeAddresses = []

  // Transaction output data
  this.outputs = {}

  // Make a new master key
  this.newMasterKey = function(seed) {
    seed = seed || crypto.randomBytes(32)
    masterkey = HDNode.fromSeedBuffer(seed, network)

    // HD first-level child derivation method should be hardened
    // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
    accountZero = masterkey.deriveHardened(0)
    externalAccount = accountZero.derive(0)
    internalAccount = accountZero.derive(1)

    me.addresses = []
    me.changeAddresses = []

    me.outputs = {}
  }

  this.newMasterKey(seed)

  this.generateAddress = function() {
    var key = externalAccount.derive(this.addresses.length)
    this.addresses.push(key.getAddress().toString())
    return this.addresses[this.addresses.length - 1]
  }

  this.generateChangeAddress = function() {
    var key = internalAccount.derive(this.changeAddresses.length)
    this.changeAddresses.push(key.getAddress().toString())
    return this.changeAddresses[this.changeAddresses.length - 1]
  }

  this.getBalance = function() {
    return this.getUnspentOutputs().reduce(function(memo, output){
      return memo + output.value
    }, 0)
  }

  this.getUnspentOutputs = function() {
    var utxo = []

    for(var key in this.outputs){
      var output = this.outputs[key]
      if(!output.to) utxo.push(outputToUnspentOutput(output))
    }

    return utxo
  }

  this.setUnspentOutputs = function(utxo) {
    var outputs = {}

    utxo.forEach(function(uo){
      validateUnspentOutput(uo)
      var o = unspentOutputToOutput(uo)
      outputs[o.from] = o
    })

    this.outputs = outputs
  }

  function outputToUnspentOutput(output){
    var hashAndIndex = output.from.split(":")

    return {
      hash: hashAndIndex[0],
      outputIndex: parseInt(hashAndIndex[1]),
      address: output.address,
      value: output.value,
      pending: output.pending
    }
  }

  function unspentOutputToOutput(o) {
    var hash = o.hash
    var key = hash + ":" + o.outputIndex
    return {
      from: key,
      address: o.address,
      value: o.value,
      pending: o.pending
    }
  }

  function validateUnspentOutput(uo) {
    var missingField

    if (isNullOrUndefined(uo.hash)) {
      missingField = "hash"
    }

    var requiredKeys = ['outputIndex', 'address', 'value']
    requiredKeys.forEach(function (key) {
      if (isNullOrUndefined(uo[key])){
        missingField = key
      }
    })

    if (missingField) {
      var message = [
        'Invalid unspent output: key', missingField, 'is missing.',
        'A valid unspent output must contain'
      ]
      message.push(requiredKeys.join(', '))
      message.push("and hash")
      throw new Error(message.join(' '))
    }
  }

  function isNullOrUndefined(value) {
    return value == undefined
  }

  this.processPendingTx = function(tx){
    processTx(tx, true)
  }

  this.processConfirmedTx = function(tx){
    processTx(tx, false)
  }

  function processTx(tx, isPending) {
    var txid = tx.getId()

    tx.outs.forEach(function(txOut, i) {
      var address

      try {
        address = Address.fromOutputScript(txOut.script, network).toString()
      } catch(e) {
        if (!(e.message.match(/has no matching Address/))) throw e
      }

      if (isMyAddress(address)) {
        var output = txid + ':' + i

        me.outputs[output] = {
          from: output,
          value: txOut.value,
          address: address,
          pending: isPending
        }
      }
    })

    tx.ins.forEach(function(txIn, i) {
      // copy and convert to big-endian hex
      var txinId = new Buffer(txIn.hash)
      Array.prototype.reverse.call(txinId)
      txinId = txinId.toString('hex')

      var output = txinId + ':' + txIn.index

      if (!(output in me.outputs)) return

      if (isPending) {
        me.outputs[output].to = txid + ':' + i
        me.outputs[output].pending = true
      } else {
        delete me.outputs[output]
      }
    })
  }

  this.createTx = function(to, value, fixedFee, changeAddress) {
    assert(value > network.dustThreshold, value + ' must be above dust threshold (' + network.dustThreshold + ' Satoshis)')

    var utxos = getCandidateOutputs(value)
    var accum = 0
    var subTotal = value
    var addresses = []

    var tx = new Transaction()
    tx.addOutput(to, value)

    for (var i = 0; i < utxos.length; ++i) {
      var utxo = utxos[i]
      addresses.push(utxo.address)

      var outpoint = utxo.from.split(':')
      tx.addInput(outpoint[0], parseInt(outpoint[1]))

      var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee

      accum += utxo.value
      subTotal = value + fee
      if (accum >= subTotal) {
        var change = accum - subTotal

        if (change > network.dustThreshold) {
          tx.addOutput(changeAddress || getChangeAddress(), change)
        }

        break
      }
    }

    assert(accum >= subTotal, 'Not enough funds (incl. fee): ' + accum + ' < ' + subTotal)

    this.signWith(tx, addresses)
    return tx
  }

  function getCandidateOutputs() {
    var unspent = []

    for (var key in me.outputs) {
      var output = me.outputs[key]
      if (!output.pending) unspent.push(output)
    }

    var sortByValueDesc = unspent.sort(function(o1, o2){
      return o2.value - o1.value
    })

    return sortByValueDesc
  }

  function estimateFeePadChangeOutput(tx) {
    var tmpTx = tx.clone()
    tmpTx.addOutput(getChangeAddress(), network.dustSoftThreshold || 0)

    return network.estimateFee(tmpTx)
  }

  function getChangeAddress() {
    if(me.changeAddresses.length === 0) me.generateChangeAddress();
    return me.changeAddresses[me.changeAddresses.length - 1]
  }

  this.signWith = function(tx, addresses) {
    assert.equal(tx.ins.length, addresses.length, 'Number of addresses must match number of transaction inputs')

    addresses.forEach(function(address, i) {
      var key = me.getPrivateKeyForAddress(address)

      tx.sign(i, key)
    })

    return tx
  }

  this.getMasterKey = function() { return masterkey }
  this.getAccountZero = function() { return accountZero }
  this.getInternalAccount = function() { return internalAccount }
  this.getExternalAccount = function() { return externalAccount }

  this.getPrivateKey = function(index) {
    return externalAccount.derive(index).privKey
  }

  this.getInternalPrivateKey = function(index) {
    return internalAccount.derive(index).privKey
  }

  this.getPrivateKeyForAddress = function(address) {
    var index
    if((index = this.addresses.indexOf(address)) > -1) {
      return this.getPrivateKey(index)
    } else if((index = this.changeAddresses.indexOf(address)) > -1) {
      return this.getInternalPrivateKey(index)
    } else {
      throw new Error('Unknown address. Make sure the address is from the keychain and has been generated.')
    }
  }

  function isReceiveAddress(address){
    return me.addresses.indexOf(address) > -1
  }

  function isChangeAddress(address){
    return me.changeAddresses.indexOf(address) > -1
  }

  function isMyAddress(address) {
    return isReceiveAddress(address) || isChangeAddress(address)
  }
}

module.exports = Wallet
<commit_msg>Wallet: deprecates newMasterKey<commit_after>var assert = require('assert')
var crypto = require('crypto')
var networks = require('./networks')

var Address = require('./address')
var HDNode = require('./hdnode')
var Transaction = require('./transaction')

function Wallet(seed, network) {
  seed = seed || crypto.randomBytes(32)
  network = network || networks.bitcoin

  // Stored in a closure to make accidental serialization less likely
  var masterkey = HDNode.fromSeedBuffer(seed, network)
  var me = this

  // HD first-level child derivation method should be hardened
  // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
  var accountZero = masterkey.deriveHardened(0)
  var externalAccount = accountZero.derive(0)
  var internalAccount = accountZero.derive(1)

  // Addresses
  this.addresses = []
  this.changeAddresses = []

  // Transaction output data
  this.outputs = {}

  this.generateAddress = function() {
    var key = externalAccount.derive(this.addresses.length)
    this.addresses.push(key.getAddress().toString())
    return this.addresses[this.addresses.length - 1]
  }

  this.generateChangeAddress = function() {
    var key = internalAccount.derive(this.changeAddresses.length)
    this.changeAddresses.push(key.getAddress().toString())
    return this.changeAddresses[this.changeAddresses.length - 1]
  }

  this.getBalance = function() {
    return this.getUnspentOutputs().reduce(function(memo, output){
      return memo + output.value
    }, 0)
  }

  this.getUnspentOutputs = function() {
    var utxo = []

    for(var key in this.outputs){
      var output = this.outputs[key]
      if(!output.to) utxo.push(outputToUnspentOutput(output))
    }

    return utxo
  }

  // FIXME: remove in 2.x.y
  this.newMasterKey = function(seed) {
    console.warn('newMasterKey is deprecated, please make a new Wallet instance instead')

    seed = seed || crypto.randomBytes(32)
    masterkey = HDNode.fromSeedBuffer(seed, network)

    // HD first-level child derivation method should be hardened
    // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
    accountZero = masterkey.deriveHardened(0)
    externalAccount = accountZero.derive(0)
    internalAccount = accountZero.derive(1)

    me.addresses = []
    me.changeAddresses = []

    me.outputs = {}
  }

  this.setUnspentOutputs = function(utxo) {
    var outputs = {}

    utxo.forEach(function(uo){
      validateUnspentOutput(uo)
      var o = unspentOutputToOutput(uo)
      outputs[o.from] = o
    })

    this.outputs = outputs
  }

  function outputToUnspentOutput(output){
    var hashAndIndex = output.from.split(":")

    return {
      hash: hashAndIndex[0],
      outputIndex: parseInt(hashAndIndex[1]),
      address: output.address,
      value: output.value,
      pending: output.pending
    }
  }

  function unspentOutputToOutput(o) {
    var hash = o.hash
    var key = hash + ":" + o.outputIndex
    return {
      from: key,
      address: o.address,
      value: o.value,
      pending: o.pending
    }
  }

  function validateUnspentOutput(uo) {
    var missingField

    if (isNullOrUndefined(uo.hash)) {
      missingField = "hash"
    }

    var requiredKeys = ['outputIndex', 'address', 'value']
    requiredKeys.forEach(function (key) {
      if (isNullOrUndefined(uo[key])){
        missingField = key
      }
    })

    if (missingField) {
      var message = [
        'Invalid unspent output: key', missingField, 'is missing.',
        'A valid unspent output must contain'
      ]
      message.push(requiredKeys.join(', '))
      message.push("and hash")
      throw new Error(message.join(' '))
    }
  }

  function isNullOrUndefined(value) {
    return value == undefined
  }

  this.processPendingTx = function(tx){
    processTx(tx, true)
  }

  this.processConfirmedTx = function(tx){
    processTx(tx, false)
  }

  function processTx(tx, isPending) {
    var txid = tx.getId()

    tx.outs.forEach(function(txOut, i) {
      var address

      try {
        address = Address.fromOutputScript(txOut.script, network).toString()
      } catch(e) {
        if (!(e.message.match(/has no matching Address/))) throw e
      }

      if (isMyAddress(address)) {
        var output = txid + ':' + i

        me.outputs[output] = {
          from: output,
          value: txOut.value,
          address: address,
          pending: isPending
        }
      }
    })

    tx.ins.forEach(function(txIn, i) {
      // copy and convert to big-endian hex
      var txinId = new Buffer(txIn.hash)
      Array.prototype.reverse.call(txinId)
      txinId = txinId.toString('hex')

      var output = txinId + ':' + txIn.index

      if (!(output in me.outputs)) return

      if (isPending) {
        me.outputs[output].to = txid + ':' + i
        me.outputs[output].pending = true
      } else {
        delete me.outputs[output]
      }
    })
  }

  this.createTx = function(to, value, fixedFee, changeAddress) {
    assert(value > network.dustThreshold, value + ' must be above dust threshold (' + network.dustThreshold + ' Satoshis)')

    var utxos = getCandidateOutputs(value)
    var accum = 0
    var subTotal = value
    var addresses = []

    var tx = new Transaction()
    tx.addOutput(to, value)

    for (var i = 0; i < utxos.length; ++i) {
      var utxo = utxos[i]
      addresses.push(utxo.address)

      var outpoint = utxo.from.split(':')
      tx.addInput(outpoint[0], parseInt(outpoint[1]))

      var fee = fixedFee == undefined ? estimateFeePadChangeOutput(tx) : fixedFee

      accum += utxo.value
      subTotal = value + fee
      if (accum >= subTotal) {
        var change = accum - subTotal

        if (change > network.dustThreshold) {
          tx.addOutput(changeAddress || getChangeAddress(), change)
        }

        break
      }
    }

    assert(accum >= subTotal, 'Not enough funds (incl. fee): ' + accum + ' < ' + subTotal)

    this.signWith(tx, addresses)
    return tx
  }

  function getCandidateOutputs() {
    var unspent = []

    for (var key in me.outputs) {
      var output = me.outputs[key]
      if (!output.pending) unspent.push(output)
    }

    var sortByValueDesc = unspent.sort(function(o1, o2){
      return o2.value - o1.value
    })

    return sortByValueDesc
  }

  function estimateFeePadChangeOutput(tx) {
    var tmpTx = tx.clone()
    tmpTx.addOutput(getChangeAddress(), network.dustSoftThreshold || 0)

    return network.estimateFee(tmpTx)
  }

  function getChangeAddress() {
    if(me.changeAddresses.length === 0) me.generateChangeAddress();
    return me.changeAddresses[me.changeAddresses.length - 1]
  }

  this.signWith = function(tx, addresses) {
    assert.equal(tx.ins.length, addresses.length, 'Number of addresses must match number of transaction inputs')

    addresses.forEach(function(address, i) {
      var key = me.getPrivateKeyForAddress(address)

      tx.sign(i, key)
    })

    return tx
  }

  this.getMasterKey = function() { return masterkey }
  this.getAccountZero = function() { return accountZero }
  this.getInternalAccount = function() { return internalAccount }
  this.getExternalAccount = function() { return externalAccount }

  this.getPrivateKey = function(index) {
    return externalAccount.derive(index).privKey
  }

  this.getInternalPrivateKey = function(index) {
    return internalAccount.derive(index).privKey
  }

  this.getPrivateKeyForAddress = function(address) {
    var index
    if((index = this.addresses.indexOf(address)) > -1) {
      return this.getPrivateKey(index)
    } else if((index = this.changeAddresses.indexOf(address)) > -1) {
      return this.getInternalPrivateKey(index)
    } else {
      throw new Error('Unknown address. Make sure the address is from the keychain and has been generated.')
    }
  }

  function isReceiveAddress(address){
    return me.addresses.indexOf(address) > -1
  }

  function isChangeAddress(address){
    return me.changeAddresses.indexOf(address) > -1
  }

  function isMyAddress(address) {
    return isReceiveAddress(address) || isChangeAddress(address)
  }
}

module.exports = Wallet
<|endoftext|>


<commit_before>if (!Meteor.settings.public) {
	Meteor.settings = {
		public: {}
	};
}<commit_msg>Fixed order of settings undefined<commit_after>if (!Meteor.settings) {
	Meteor.settings = {};
}
if (!Meteor.settings.public) {
	Meteor.settings.public = {};
}<|endoftext|>


<commit_before>var Script = require('./script');
var ECKey = require('./eckey').ECKey;
var convert = require('./convert');
var assert = require('assert');
var BigInteger = require('./jsbn/jsbn');
var Transaction = require('./transaction').Transaction;
var TransactionIn = require('./transaction').TransactionIn;
var TransactionOut = require('./transaction').TransactionOut;
var HDNode = require('./hdwallet.js')
var rng = require('secure-random');

var Wallet = function (seed, options) {
    if (!(this instanceof Wallet)) { return new Wallet(seed, options); }

    var options = options || {}
    var network = options.network || 'mainnet'

    // Stored in a closure to make accidental serialization less likely
    var masterkey = null;
    var me = this;
    var accountZero = null;
    var internalAccount = null;
    var externalAccount = null;

    // Addresses
    this.addresses = [];
    this.changeAddresses = [];

    // Transaction output data
    this.outputs = {};

    // Make a new master key
    this.newMasterKey = function(seed, network) {
        if (!seed) seed= rng(32, { array: true })
        masterkey = new HDNode(seed, network);

        // HD first-level child derivation method should be private
        // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
        accountZero = masterkey.derivePrivate(0)
        externalAccount = accountZero.derive(0)
        internalAccount = accountZero.derive(1)

        me.addresses = [];
        me.changeAddresses = [];

        me.outputs = {};
    }
    this.newMasterKey(seed, network)

    this.generateAddress = function() {
        var key = externalAccount.derive(this.addresses.length)
        this.addresses.push(key.getAddress().toString())
        return this.addresses[this.addresses.length - 1]
    }

    this.generateChangeAddress = function() {
        var key = internalAccount.derive(this.changeAddresses.length)
        this.changeAddresses.push(key.getAddress().toString())
        return this.changeAddresses[this.changeAddresses.length - 1]
    }

    this.getUnspentOutputs = function() {
      var utxo = []

      for(var key in this.outputs){
        utxo.push(outputToUnspentOutput(this.outputs[key]))
      }

      return utxo
    }

    this.setUnspentOutputs = function(utxo) {
      var outputs = {}

      utxo.forEach(function(uo){
        validateUnspentOutput(uo)
        var o = unspentOutputToOutput(uo)
        outputs[o.receive] = o
      })

      this.outputs = outputs
    }

    function outputToUnspentOutput(output){
      var hashAndIndex = output.receive.split(":")

      return {
        hash: hashAndIndex[0],
        hashLittleEndian: convert.reverseEndian(hashAndIndex[0]),
        outputIndex: parseInt(hashAndIndex[1]),
        scriptPubKey: output.scriptPubKey,
        address: output.address,
        value: output.value
      }
    }

    function unspentOutputToOutput(o) {
      var hash = o.hash || convert.reverseEndian(o.hashLittleEndian)
      var key = hash + ":" + o.outputIndex
      return {
        receive: key,
        scriptPubKey: o.scriptPubKey,
        address: o.address,
        value: o.value
      }
    }

    function validateUnspentOutput(uo) {
      var missingField;

      if(isNullOrUndefined(uo.hash) && isNullOrUndefined(uo.hashLittleEndian)){
        missingField = "hash(or hashLittleEndian)"
      }

      var requiredKeys = ['outputIndex', 'scriptPubKey', 'address', 'value']
      requiredKeys.forEach(function(key){
        if(isNullOrUndefined(uo[key])){
          missingField = key
        }
      })

      if(missingField) {
        var message = [
          'Invalid unspent output: key', field, 'is missing.',
          'A valid unspent output must contain'
        ]
        message.push(requiredKeys.join(', '))
        message.push("and hash(or hashLittleEndian)")
        throw new Error(message.join(' '))
      }
    }

    function isNullOrUndefined(value){
      return value == undefined
    }

    this.processTx = function(tx) {
        var txhash = convert.bytesToHex(tx.getHash())

        tx.outs.forEach(function(txOut, i){
            var address = txOut.address.toString()
            if (isMyAddress(address)) {
                var output = txhash+':'+i
                me.outputs[output] = {
                    receive: output,
                    value: txOut.value,
                    address: address,
                    scriptPubKey: convert.bytesToHex(txOut.script.buffer) //TODO: txOut.scriptPubKey()
                    // timestamp: new Date().getTime() / 1000,
                    // pending: true
                }
            }
        })

        tx.ins.forEach(function(txIn, i){
            var op = txIn.outpoint
            var o = me.outputs[op.hash+':'+op.index]
            if (o) {
                o.spend = txhash+':'+i
                // o.spendpending = true
                // o.timestamp = new Date().getTime() / 1000
            }
        })
    }

    // Processes an output from an external source of the form
    // { output: txhash:index, value: integer, address: address }
    // Excellent compatibility with SX and pybitcointools
    this.processOutput = function(o) {
        if (!this.outputs[o.output] || this.outputs[o.output].pending)
             this.outputs[o.output] = o;
    }

    this.processExistingOutputs = function() {
        var t = new Date().getTime() / 1000
        for (var o in this.outputs) {
            if (o.pending && t > o.timestamp + 1200)
                delete this.outputs[o]
            if (o.spendpending && t > o.timestamp + 1200) {
                o.spendpending = false
                o.spend = false
                delete o.timestamp
            }
        }
    }
    var peoInterval = setInterval(this.processExistingOutputs, 10000)

    this.getUtxoToPay = function(value) {
        var h = []
        for (var out in this.outputs) h.push(this.outputs[out])
        var utxo = h.filter(function(x) { return !x.spend });
        var valuecompare = function(a,b) { return a.value > b.value; }
        var high = utxo.filter(function(o) { return o.value >= value; })
                       .sort(valuecompare);
        if (high.length > 0) return [high[0]];
        utxo.sort(valuecompare);
        var totalval = 0;
        for (var i = 0; i < utxo.length; i++) {
            totalval += utxo[i].value;
            if (totalval >= value) return utxo.slice(0,i+1);
        }
        throw ("Not enough money to send funds including transaction fee. Have: "
                     + (totalval / 100000000) + ", needed: " + (value / 100000000));
    }

    this.mkSend = function(to, value, fee) {
        var utxo = this.getUtxoToPay(value + fee)
        var sum = utxo.reduce(function(t,o) { return t + o.value },0),
            remainder = sum - value - fee
        if (value < 5430) throw new Error("Amount below dust threshold!")
        var unspentOuts = 0;
        for (var o in this.outputs) {
            if (!this.outputs[o].spend) unspentOuts += 1
            if (unspentOuts >= 5) return
        }
        var change = this.addresses[this.addresses.length - 1]
        var toOut = { address: to, value: value },
            changeOut = { address: change, value: remainder }
            halfChangeOut = { address: change, value: Math.floor(remainder/2) };

        var outs =
              remainder < 5430  ? [toOut]
            : remainder < 10860 ? [toOut, changeOut]
            : unspentOuts == 5  ? [toOut, changeOut]
            :                     [toOut, halfChangeOut, halfChangeOut]

        var tx = new Bitcoin.Transaction({
            ins: utxo.map(function(x) { return x.receive }),
            outs: outs
        })
        this.sign(tx)
        return tx
    }

    this.mkSendToOutputs = function(outputs, changeIndex, fee) {
        var value = outputs.reduce(function(t,o) { return t + o.value },0),
            utxo = this.getUtxoToPay(value + fee),
            sum = utxo.reduce(function(t,p) { return t + o.value },0);
        utxo[changeIndex].value += sum - value - fee;
        var tx = new Bitcoin.Transaction({
            ins: utxo.map(function(x) { return x.receive }),
            outs: outputs
        })
        this.sign(tx)
        return tx
    }

    this.sign = function(tx) {
        tx.ins.map(function(inp,i) {
            var inp = inp.outpoint.hash+':'+inp.outpoint.index;
            if (me.outputs[inp]) {
                var address = me.outputs[inp].address
                tx.sign(i, me.getPrivateKeyForAddress(address))
            }
        })
        return tx;
    }

    this.getMasterKey = function() { return masterkey }
    this.getAccountZero = function() { return accountZero }
    this.getInternalAccount = function() { return internalAccount }
    this.getExternalAccount = function() { return externalAccount }

    this.getPrivateKey = function(index) {
        return externalAccount.derive(index).priv
    }

    this.getInternalPrivateKey = function(index) {
        return internalAccount.derive(index).priv
    }

    this.getPrivateKeyForAddress = function(address) {
      var index;
      if((index = this.addresses.indexOf(address)) > -1) {
        return this.getPrivateKey(index)
      } else if((index = this.changeAddresses.indexOf(address)) > -1) {
        return this.getInternalPrivateKey(index)
      } else {
        throw new Error('Unknown address. Make sure the address is from the keychain and has been generated.')
      }
    }

    function isReceiveAddress(address){
      return me.addresses.indexOf(address) > -1
    }

    function isChangeAddress(address){
      return me.changeAddresses.indexOf(address) > -1
    }

    function isMyAddress(address) {
      return isReceiveAddress(address) || isChangeAddress(address)
    }
};

module.exports = Wallet;
<commit_msg>remove processOutput and processExistingOutput<commit_after>var Script = require('./script');
var ECKey = require('./eckey').ECKey;
var convert = require('./convert');
var assert = require('assert');
var BigInteger = require('./jsbn/jsbn');
var Transaction = require('./transaction').Transaction;
var TransactionIn = require('./transaction').TransactionIn;
var TransactionOut = require('./transaction').TransactionOut;
var HDNode = require('./hdwallet.js')
var rng = require('secure-random');

var Wallet = function (seed, options) {
    if (!(this instanceof Wallet)) { return new Wallet(seed, options); }

    var options = options || {}
    var network = options.network || 'mainnet'

    // Stored in a closure to make accidental serialization less likely
    var masterkey = null;
    var me = this;
    var accountZero = null;
    var internalAccount = null;
    var externalAccount = null;

    // Addresses
    this.addresses = [];
    this.changeAddresses = [];

    // Transaction output data
    this.outputs = {};

    // Make a new master key
    this.newMasterKey = function(seed, network) {
        if (!seed) seed= rng(32, { array: true })
        masterkey = new HDNode(seed, network);

        // HD first-level child derivation method should be private
        // See https://bitcointalk.org/index.php?topic=405179.msg4415254#msg4415254
        accountZero = masterkey.derivePrivate(0)
        externalAccount = accountZero.derive(0)
        internalAccount = accountZero.derive(1)

        me.addresses = [];
        me.changeAddresses = [];

        me.outputs = {};
    }
    this.newMasterKey(seed, network)

    this.generateAddress = function() {
        var key = externalAccount.derive(this.addresses.length)
        this.addresses.push(key.getAddress().toString())
        return this.addresses[this.addresses.length - 1]
    }

    this.generateChangeAddress = function() {
        var key = internalAccount.derive(this.changeAddresses.length)
        this.changeAddresses.push(key.getAddress().toString())
        return this.changeAddresses[this.changeAddresses.length - 1]
    }

    this.getUnspentOutputs = function() {
      var utxo = []

      for(var key in this.outputs){
        utxo.push(outputToUnspentOutput(this.outputs[key]))
      }

      return utxo
    }

    this.setUnspentOutputs = function(utxo) {
      var outputs = {}

      utxo.forEach(function(uo){
        validateUnspentOutput(uo)
        var o = unspentOutputToOutput(uo)
        outputs[o.receive] = o
      })

      this.outputs = outputs
    }

    function outputToUnspentOutput(output){
      var hashAndIndex = output.receive.split(":")

      return {
        hash: hashAndIndex[0],
        hashLittleEndian: convert.reverseEndian(hashAndIndex[0]),
        outputIndex: parseInt(hashAndIndex[1]),
        scriptPubKey: output.scriptPubKey,
        address: output.address,
        value: output.value
      }
    }

    function unspentOutputToOutput(o) {
      var hash = o.hash || convert.reverseEndian(o.hashLittleEndian)
      var key = hash + ":" + o.outputIndex
      return {
        receive: key,
        scriptPubKey: o.scriptPubKey,
        address: o.address,
        value: o.value
      }
    }

    function validateUnspentOutput(uo) {
      var missingField;

      if(isNullOrUndefined(uo.hash) && isNullOrUndefined(uo.hashLittleEndian)){
        missingField = "hash(or hashLittleEndian)"
      }

      var requiredKeys = ['outputIndex', 'scriptPubKey', 'address', 'value']
      requiredKeys.forEach(function(key){
        if(isNullOrUndefined(uo[key])){
          missingField = key
        }
      })

      if(missingField) {
        var message = [
          'Invalid unspent output: key', field, 'is missing.',
          'A valid unspent output must contain'
        ]
        message.push(requiredKeys.join(', '))
        message.push("and hash(or hashLittleEndian)")
        throw new Error(message.join(' '))
      }
    }

    function isNullOrUndefined(value){
      return value == undefined
    }

    this.processTx = function(tx) {
        var txhash = convert.bytesToHex(tx.getHash())

        tx.outs.forEach(function(txOut, i){
            var address = txOut.address.toString()
            if (isMyAddress(address)) {
                var output = txhash+':'+i
                me.outputs[output] = {
                    receive: output,
                    value: txOut.value,
                    address: address,
                    scriptPubKey: convert.bytesToHex(txOut.script.buffer) //TODO: txOut.scriptPubKey()
                    // timestamp: new Date().getTime() / 1000,
                    // pending: true
                }
            }
        })

        tx.ins.forEach(function(txIn, i){
            var op = txIn.outpoint
            var o = me.outputs[op.hash+':'+op.index]
            if (o) {
                o.spend = txhash+':'+i
                // o.spendpending = true
                // o.timestamp = new Date().getTime() / 1000
            }
        })
    }

    this.getUtxoToPay = function(value) {
        var h = []
        for (var out in this.outputs) h.push(this.outputs[out])
        var utxo = h.filter(function(x) { return !x.spend });
        var valuecompare = function(a,b) { return a.value > b.value; }
        var high = utxo.filter(function(o) { return o.value >= value; })
                       .sort(valuecompare);
        if (high.length > 0) return [high[0]];
        utxo.sort(valuecompare);
        var totalval = 0;
        for (var i = 0; i < utxo.length; i++) {
            totalval += utxo[i].value;
            if (totalval >= value) return utxo.slice(0,i+1);
        }
        throw ("Not enough money to send funds including transaction fee. Have: "
                     + (totalval / 100000000) + ", needed: " + (value / 100000000));
    }

    this.mkSend = function(to, value, fee) {
        var utxo = this.getUtxoToPay(value + fee)
        var sum = utxo.reduce(function(t,o) { return t + o.value },0),
            remainder = sum - value - fee
        if (value < 5430) throw new Error("Amount below dust threshold!")
        var unspentOuts = 0;
        for (var o in this.outputs) {
            if (!this.outputs[o].spend) unspentOuts += 1
            if (unspentOuts >= 5) return
        }
        var change = this.addresses[this.addresses.length - 1]
        var toOut = { address: to, value: value },
            changeOut = { address: change, value: remainder }
            halfChangeOut = { address: change, value: Math.floor(remainder/2) };

        var outs =
              remainder < 5430  ? [toOut]
            : remainder < 10860 ? [toOut, changeOut]
            : unspentOuts == 5  ? [toOut, changeOut]
            :                     [toOut, halfChangeOut, halfChangeOut]

        var tx = new Bitcoin.Transaction({
            ins: utxo.map(function(x) { return x.receive }),
            outs: outs
        })
        this.sign(tx)
        return tx
    }

    this.mkSendToOutputs = function(outputs, changeIndex, fee) {
        var value = outputs.reduce(function(t,o) { return t + o.value },0),
            utxo = this.getUtxoToPay(value + fee),
            sum = utxo.reduce(function(t,p) { return t + o.value },0);
        utxo[changeIndex].value += sum - value - fee;
        var tx = new Bitcoin.Transaction({
            ins: utxo.map(function(x) { return x.receive }),
            outs: outputs
        })
        this.sign(tx)
        return tx
    }

    this.sign = function(tx) {
        tx.ins.map(function(inp,i) {
            var inp = inp.outpoint.hash+':'+inp.outpoint.index;
            if (me.outputs[inp]) {
                var address = me.outputs[inp].address
                tx.sign(i, me.getPrivateKeyForAddress(address))
            }
        })
        return tx;
    }

    this.getMasterKey = function() { return masterkey }
    this.getAccountZero = function() { return accountZero }
    this.getInternalAccount = function() { return internalAccount }
    this.getExternalAccount = function() { return externalAccount }

    this.getPrivateKey = function(index) {
        return externalAccount.derive(index).priv
    }

    this.getInternalPrivateKey = function(index) {
        return internalAccount.derive(index).priv
    }

    this.getPrivateKeyForAddress = function(address) {
      var index;
      if((index = this.addresses.indexOf(address)) > -1) {
        return this.getPrivateKey(index)
      } else if((index = this.changeAddresses.indexOf(address)) > -1) {
        return this.getInternalPrivateKey(index)
      } else {
        throw new Error('Unknown address. Make sure the address is from the keychain and has been generated.')
      }
    }

    function isReceiveAddress(address){
      return me.addresses.indexOf(address) > -1
    }

    function isChangeAddress(address){
      return me.changeAddresses.indexOf(address) > -1
    }

    function isMyAddress(address) {
      return isReceiveAddress(address) || isChangeAddress(address)
    }
};

module.exports = Wallet;
<|endoftext|>


<commit_before>'use strict'

const randomBytes = require('randombytes')
const {
  createCipheriv,
  createDecipheriv
} = require('browserify-cipher')
const secp256k1 = require('secp256k1')
const scrypt = require('scrypt-async')
const struct = require('varstruct')
const Bitcoin = require('./bitcoin.js')
const Ethereum = require('./ethereum.js')
const { sha3, ripemd160 } = require('./hash.js')
const { concat, byte } = require('./util.js')

const Wallet = struct([
  { name: 'encryptedSeed', type: struct.VarBuffer(struct.Byte) },
  { name: 'salt', type: struct.VarBuffer(struct.Byte) },
  { name: 'iv', type: struct.VarBuffer(struct.Byte) },
  { name: 'authTag', type: struct.VarBuffer(struct.Byte) }
])

function generateSeed () {
  return randomBytes(32)
}

function deriveWallet (seed, testnet = false) {
  let privateKeys = derivePrivateKeys(seed)
  let publicKeys = derivePublicKeys(privateKeys)
  let addresses = deriveAddresses(publicKeys, testnet)
  return { privateKeys, publicKeys, addresses }
}

function derivePrivateKeys (seed) {
  if (seed.length < 32) {
    throw Error('Seed must be at least 32 bytes')
  }
  let cosmos = sha3(concat(seed, byte(0)))
  let bitcoin = sha3(concat(seed, byte(1)))
  let ethereum = sha3(concat(seed, byte(2)))
  return { cosmos, bitcoin, ethereum }
}

function derivePublicKeys (priv) {
  let cosmos = secp256k1.publicKeyCreate(priv.cosmos)
  let bitcoin = secp256k1.publicKeyCreate(priv.bitcoin)
  let ethereum = secp256k1.publicKeyCreate(priv.ethereum, false)
  return { cosmos, bitcoin, ethereum }
}

function getCosmosAddress (pub) {
  return ripemd160(pub).toString('hex')
}

function deriveAddresses (pub, testnet = false) {
  let cosmos = getCosmosAddress(pub.cosmos)
  let bitcoin = Bitcoin.getAddress(pub.bitcoin, testnet)
  let ethereum = Ethereum.getAddress(pub.ethereum)
  return { cosmos, bitcoin, ethereum }
}

function deriveEncryptionKey (password, salt, cb) {
  scrypt(password, salt, { N: 32768, r: 10 }, (key) => {
    cb(Buffer(key))
  })
}

function encryptSeed (seed, password, cb) {
  let salt = randomBytes(32)
  deriveEncryptionKey(password, salt, (key) => {
    let iv = randomBytes(16)
    let cipher = createCipheriv('aes-256-gcm', key, iv)
    let encryptedSeed = concat(
      cipher.update(seed),
      cipher.final()
    )
    let authTag = cipher.getAuthTag()
    cb(null, { encryptedSeed, salt, iv, authTag })
  })
}

function decryptSeed ({ encryptedSeed, salt, iv, authTag }, password, cb) {
  deriveEncryptionKey(password, salt, (key) => {
    let decipher = createDecipheriv('aes-256-gcm', key, iv)
    decipher.setAuthTag(authTag)
    cb(null, concat(
      decipher.update(encryptedSeed),
      decipher.final()
    ))
  })
}

function encodeWallet (wallet) {
  return Wallet.encode(wallet)
}

function decodeWallet (bytes) {
  return Wallet.decode(bytes)
}

module.exports = {
  generateSeed,
  deriveWallet,
  encryptSeed,
  decryptSeed,
  encodeWallet,
  decodeWallet
}
<commit_msg>Return wallet decrypt errors to decryptWallet callback<commit_after>'use strict'

const randomBytes = require('randombytes')
const {
  createCipheriv,
  createDecipheriv
} = require('browserify-cipher')
const secp256k1 = require('secp256k1')
const scrypt = require('scrypt-async')
const struct = require('varstruct')
const Bitcoin = require('./bitcoin.js')
const Ethereum = require('./ethereum.js')
const { sha3, ripemd160 } = require('./hash.js')
const { concat, byte } = require('./util.js')

const Wallet = struct([
  { name: 'encryptedSeed', type: struct.VarBuffer(struct.Byte) },
  { name: 'salt', type: struct.VarBuffer(struct.Byte) },
  { name: 'iv', type: struct.VarBuffer(struct.Byte) },
  { name: 'authTag', type: struct.VarBuffer(struct.Byte) }
])

function generateSeed () {
  return randomBytes(32)
}

function deriveWallet (seed, testnet = false) {
  let privateKeys = derivePrivateKeys(seed)
  let publicKeys = derivePublicKeys(privateKeys)
  let addresses = deriveAddresses(publicKeys, testnet)
  return { privateKeys, publicKeys, addresses }
}

function derivePrivateKeys (seed) {
  if (seed.length < 32) {
    throw Error('Seed must be at least 32 bytes')
  }
  let cosmos = sha3(concat(seed, byte(0)))
  let bitcoin = sha3(concat(seed, byte(1)))
  let ethereum = sha3(concat(seed, byte(2)))
  return { cosmos, bitcoin, ethereum }
}

function derivePublicKeys (priv) {
  let cosmos = secp256k1.publicKeyCreate(priv.cosmos)
  let bitcoin = secp256k1.publicKeyCreate(priv.bitcoin)
  let ethereum = secp256k1.publicKeyCreate(priv.ethereum, false)
  return { cosmos, bitcoin, ethereum }
}

function getCosmosAddress (pub) {
  return ripemd160(pub).toString('hex')
}

function deriveAddresses (pub, testnet = false) {
  let cosmos = getCosmosAddress(pub.cosmos)
  let bitcoin = Bitcoin.getAddress(pub.bitcoin, testnet)
  let ethereum = Ethereum.getAddress(pub.ethereum)
  return { cosmos, bitcoin, ethereum }
}

function deriveEncryptionKey (password, salt, cb) {
  scrypt(password, salt, { N: 32768, r: 10 }, (key) => {
    cb(Buffer(key))
  })
}

function encryptSeed (seed, password, cb) {
  let salt = randomBytes(32)
  deriveEncryptionKey(password, salt, (key) => {
    let iv = randomBytes(16)
    let cipher = createCipheriv('aes-256-gcm', key, iv)
    let encryptedSeed = concat(
      cipher.update(seed),
      cipher.final()
    )
    let authTag = cipher.getAuthTag()
    cb(null, { encryptedSeed, salt, iv, authTag })
  })
}

function decryptSeed ({ encryptedSeed, salt, iv, authTag }, password, cb) {
  deriveEncryptionKey(password, salt, (key) => {
    let decipher = createDecipheriv('aes-256-gcm', key, iv)
    decipher.setAuthTag(authTag)
    try {
      let seed = concat(
        decipher.update(encryptedSeed),
        decipher.final()
      )
      cb(null, seed)
    } catch (err) {
      cb(err)
    }
  })
}

function encodeWallet (wallet) {
  return Wallet.encode(wallet)
}

function decodeWallet (bytes) {
  return Wallet.decode(bytes)
}

module.exports = {
  generateSeed,
  deriveWallet,
  encryptSeed,
  decryptSeed,
  encodeWallet,
  decodeWallet
}
<|endoftext|>


<commit_before>define(function (text, ko) {
	return {
		load: function (name, req, onLoad, config) {
			if (config.isBuild) {
				//don't do anything if this is a build, can't inline a web worker
				onLoad();
				return;
			}

			var url = req.toUrl(name);

			if (window.Worker) {
				onLoad(new Worker(url));
			} else {
				req(["worker-fake"], function () {
					onLoad(new Worker(url));
				});
			}
		}
	};
});<commit_msg>Including version in worker plugin.<commit_after>define(function (text, ko) {
	return {
		version: "1.0.0",
		load: function (name, req, onLoad, config) {
			if (config.isBuild) {
				//don't do anything if this is a build, can't inline a web worker
				onLoad();
				return;
			}

			var url = req.toUrl(name);

			if (window.Worker) {
				onLoad(new Worker(url));
			} else {
				req(["worker-fake"], function () {
					onLoad(new Worker(url));
				});
			}
		}
	};
});<|endoftext|>


<commit_before>Package.describe({
  name: 'canotto90:latte',
  version: '0.6.1',
  summary: 'Run mocha-like specs in Meteor, without Velocity\'s overhead.',
  git: 'https://github.com/taromero/latte.git',
  documentation: '../../README.md'
})

Package.onUse(function (api) {
  api.versionsFrom('1.1.0.1')
  api.use('mongo@1.1.0', 'server')
  api.use('nooitaf:colors@0.0.2')
  api.addFiles('latte.js', 'server')
  api.export('T', 'server')
  api.export('describe', 'server')
  api.export('context', 'server')
  api.export('it', 'server')
  api.export('iit', 'server')
  api.export('beforeAll', 'server')
  api.export('beforeEach', 'server')
  api.export('afterAll', 'server')
  api.export('afterEach', 'server')
  api.export('ddescribe', 'server')
  api.export('ccontext', 'server')
})
<commit_msg>Version bump<commit_after>Package.describe({
  name: 'canotto90:latte',
  version: '0.6.2',
  summary: 'Run mocha-like specs in Meteor, without Velocity\'s overhead.',
  git: 'https://github.com/taromero/latte.git',
  documentation: '../../README.md'
})

Package.onUse(function (api) {
  api.versionsFrom('1.1.0.1')
  api.use('mongo@1.1.0', 'server')
  api.use('nooitaf:colors@0.0.2')
  api.addFiles('latte.js', 'server')
  api.export('T', 'server')
  api.export('describe', 'server')
  api.export('context', 'server')
  api.export('it', 'server')
  api.export('iit', 'server')
  api.export('beforeAll', 'server')
  api.export('beforeEach', 'server')
  api.export('afterAll', 'server')
  api.export('afterEach', 'server')
  api.export('ddescribe', 'server')
  api.export('ccontext', 'server')
})
<|endoftext|>


<commit_before>import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Text, View } from 'react-native'
import * as d3Scale from 'd3-scale'
import * as array from 'd3-array'
import Svg, { Text as SVGText } from 'react-native-svg'

class XAxis extends PureComponent {

    state = {
        width: 0,
        height: 0,
    }

    _onLayout(event) {
        const { nativeEvent: { layout: { width, height } } } = event

        if (width !== this.state.width) {
            this.setState({ width, height })
        }
    }

    _getX(domain) {
        const {
            scale,
            spacingInner,
            spacingOuter,
            contentInset: {
                left  = 0,
                right = 0,
            },
            min,
            max,
        } = this.props

        const { width } = this.state

        const x = scale()
            .domain([ min || domain[ 0 ], max || domain[ 1 ] ])
            .range([ left, width - right ])

        if (scale === d3Scale.scaleBand) {

            x
                .paddingInner([ spacingInner ])
                .paddingOuter([ spacingOuter ])

            //add half a bar to center label
            return (value) => x(value) + (x.bandwidth() / 2)
        }

        return x
    }

    render() {

        const {
            style,
            scale,
            data,
            xAccessor,
            formatLabel,
            numberOfTicks,
            svg,
            children,
        } = this.props

        const { height, width } = this.state

        if (data.length === 0) {
            return <View style={ style }/>
        }

        const values = data.map((item, index) => xAccessor({ item, index }))
        const extent  = array.extent(values)
        const domain  = scale === d3Scale.scaleBand ? values : extent

        const x     = this._getX(domain)
        const ticks = numberOfTicks ? x.ticks(numberOfTicks) : values

        return (
            <View style={ style }>
                <View
                    style={{ flexGrow: 1 }}
                    onLayout={ event => this._onLayout(event) }
                >
                    {/*invisible text to allow for parent resizing*/}
                    <Text style={{ color: 'transparent', fontSize: svg.fontSize }}>
                        { formatLabel(ticks[0], 0) }
                    </Text>
                    {
                        height > 0 && width > 0 &&
                        <Svg style={{
                            position: 'absolute',
                            top: 0,
                            left: 0,
                            height,
                            width,
                        }}>
                            {children}
                            {
                                // don't render labels if width isn't measured yet,
                                // causes rendering issues
                                width > 0 &&
                                ticks.map((value, index) => {
                                    const { svg: valueSvg = {} } = data[ index ] || {}

                                    return (
                                        <SVGText
                                            textAnchor={ 'middle' }
                                            originX={ x(value) }
                                            alignmentBaseline={ 'hanging' }
                                            { ...svg }
                                            { ...valueSvg }
                                            key={ index }
                                            x={ x(value) }
                                        >
                                            {formatLabel(value, index)}
                                        </SVGText>
                                    )
                                })
                            }
                        </Svg>
                    }
                </View>
            </View>
        )
    }
}

XAxis.propTypes = {
    data: PropTypes.arrayOf(PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.object,
    ])).isRequired,
    spacingInner: PropTypes.number,
    spacingOuter: PropTypes.number,
    formatLabel: PropTypes.func,
    contentInset: PropTypes.shape({
        left: PropTypes.number,
        right: PropTypes.number,
    }),
    scale: PropTypes.oneOf([ d3Scale.scaleTime, d3Scale.scaleLinear, d3Scale.scaleBand ]),
    numberOfTicks: PropTypes.number,
    xAccessor: PropTypes.func,
    svg: PropTypes.object,
    min: PropTypes.any,
    max: PropTypes.any,
}

XAxis.defaultProps = {
    spacingInner: 0.05,
    spacingOuter: 0.05,
    contentInset: {},
    svg: {},
    xAccessor: ({ index }) => index,
    scale: d3Scale.scaleLinear,
    formatLabel: value => value,
}

export default XAxis
<commit_msg>fix xAxis with scale = scaleband<commit_after>import React, { PureComponent } from 'react'
import PropTypes from 'prop-types'
import { Text, View } from 'react-native'
import * as d3Scale from 'd3-scale'
import * as array from 'd3-array'
import Svg, { Text as SVGText } from 'react-native-svg'

class XAxis extends PureComponent {

    state = {
        width: 0,
        height: 0,
    }

    _onLayout(event) {
        const { nativeEvent: { layout: { width, height } } } = event

        if (width !== this.state.width) {
            this.setState({ width, height })
        }
    }

    _getX(domain) {
        const {
            scale,
            spacingInner,
            spacingOuter,
            contentInset: {
                left  = 0,
                right = 0,
            },
        } = this.props

        const { width } = this.state

        const x = scale()
            .domain(domain)
            .range([ left, width - right ])

        if (scale === d3Scale.scaleBand) {

            x
                .paddingInner([ spacingInner ])
                .paddingOuter([ spacingOuter ])

            //add half a bar to center label
            return (value) => x(value) + (x.bandwidth() / 2)
        }

        return x
    }

    render() {

        const {
            style,
            scale,
            data,
            xAccessor,
            formatLabel,
            numberOfTicks,
            svg,
            children,
            min,
            max,
        } = this.props

        const { height, width } = this.state

        if (data.length === 0) {
            return <View style={ style }/>
        }

        const values = data.map((item, index) => xAccessor({ item, index }))
        const extent  = array.extent(values)
        const domain = scale === d3Scale.scaleBand ?
            values :
            [ min || extent[ 0 ], max || extent[ 1 ] ]

        const x     = this._getX(domain)
        const ticks = numberOfTicks ? x.ticks(numberOfTicks) : values

        return (
            <View style={ style }>
                <View
                    style={{ flexGrow: 1 }}
                    onLayout={ event => this._onLayout(event) }
                >
                    {/*invisible text to allow for parent resizing*/}
                    <Text style={{ color: 'transparent', fontSize: svg.fontSize }}>
                        { formatLabel(ticks[0], 0) }
                    </Text>
                    {
                        height > 0 && width > 0 &&
                        <Svg style={{
                            position: 'absolute',
                            top: 0,
                            left: 0,
                            height,
                            width,
                        }}>
                            {children}
                            {
                                // don't render labels if width isn't measured yet,
                                // causes rendering issues
                                width > 0 &&
                                ticks.map((value, index) => {
                                    const { svg: valueSvg = {} } = data[ index ] || {}

                                    return (
                                        <SVGText
                                            textAnchor={ 'middle' }
                                            originX={ x(value) }
                                            alignmentBaseline={ 'hanging' }
                                            { ...svg }
                                            { ...valueSvg }
                                            key={ index }
                                            x={ x(value) }
                                        >
                                            {formatLabel(value, index)}
                                        </SVGText>
                                    )
                                })
                            }
                        </Svg>
                    }
                </View>
            </View>
        )
    }
}

XAxis.propTypes = {
    data: PropTypes.arrayOf(PropTypes.oneOfType([
        PropTypes.number,
        PropTypes.object,
    ])).isRequired,
    spacingInner: PropTypes.number,
    spacingOuter: PropTypes.number,
    formatLabel: PropTypes.func,
    contentInset: PropTypes.shape({
        left: PropTypes.number,
        right: PropTypes.number,
    }),
    scale: PropTypes.oneOf([ d3Scale.scaleTime, d3Scale.scaleLinear, d3Scale.scaleBand ]),
    numberOfTicks: PropTypes.number,
    xAccessor: PropTypes.func,
    svg: PropTypes.object,
    min: PropTypes.any,
    max: PropTypes.any,
}

XAxis.defaultProps = {
    spacingInner: 0.05,
    spacingOuter: 0.05,
    contentInset: {},
    svg: {},
    xAccessor: ({ index }) => index,
    scale: d3Scale.scaleLinear,
    formatLabel: value => value,
}

export default XAxis
<|endoftext|>