<commit_before>/**
 * UI and function to add project
 */
'use strict';

require('css/add-project-plugin.css');
var $ = require('jquery');
var m = require('mithril');
var $osf = require('js/osfHelpers');

// XHR configuration to get apiserver connection to work
var xhrconfig = function (xhr) {
    xhr.withCredentials = true;
};

var AddProject = {
    controller : function (options) {
        var self = this;
        self.defaults = {
            buttonTemplate : m('.btn.btn-primary[data-toggle="modal"][data-target="#addProjectModal"]', 'Add new Project'),
            parent : null
        };
        self.viewState = m.prop('form'); // 'processing', 'success', 'error';
        self.options = $.extend(self.defaults, options);
        self.init = function _init () {

        };
        self.newProjectName = m.prop('');
        self.newProjectDesc = m.prop('');
        self.categoryList = [
            { value: 'project', label: 'Project'},
            { value: 'hypothesis', label : 'Hypothesis' },
            { value: 'methods and measures', label :  'Methods and Measures'},
            { value: 'procedure', label:  'Procedure'},
            { value: 'instrumentation', label: 'Instrumentation'},
            { value: 'data', label:  'Data'},
            { value: 'analysis', label: 'Analysis'},
            { value: 'communication',label: 'Communication'},
            { value: 'other', label: 'Other'}
        ];
        self.newProjectCategory = m.prop(self.categoryList[0].value);
        self.goToProjectLink = m.prop('');
        self.errorMessageType = m.prop('unknown');
        self.errorMessage = {
            'unknown' : 'There was an unknown error. Please try again later.'
        };
        self.chooseCategory = function(event){
            self.newProjectCategory = $(this).val();
        };
        self.add = function _add () {
            self.viewState('processing');
            var url = $osf.apiV2Url('nodes/', { query : {}});
            var data = {
                    'data' : {
                        'type': 'nodes',
                        'attributes': {
                            'title': self.newProjectName(),
                            'category': self.newProjectCategory,
                            'description' : self.newProjectDesc()
                        }
                    }
                };
            var success = function _success (result) {
                self.viewState('success');
                console.log('success', result);
                self.goToProjectLink(result.data.links.html);
            };
            var error = function _error (result) {
                self.viewState('error');
                console.log('error', result);
            };
            m.request({method : 'POST', url : url, data : data, config : xhrconfig})
                .then(success, error);
            self.newProjectName('');
            //m.redraw(true);

        };
        self.reset = function _reset(){
            self.newProjectName('');
            self.viewState('form');
            $('.modal').modal('hide');
        };
    },
    view : function (ctrl, options) {
        var templates = {
            form : m('.modal-content', [
                m('.modal-header', [
                    m('button.close[data-dismiss="modal"][aria-label="Close"]',{ onclick : ctrl.reset}, [
                        m('span[aria-hidden="true"]','×'),
                    ]),
                    m('h3.modal-title', 'Add New Project')
                ]),
                m('.modal-body', [
                    m('', [
                        m('.form-group.m-v-sm', [
                            m('label[for="projectName].f-w-lg.text-bigger', 'Project Name'),
                            m('input[type="text"].form-control', {
                                onkeyup: function(ev){
                                    if (ev.which === 13) {
                                         ctrl.add();
                                    }
                                    ctrl.newProjectName($(this).val());
                                },
                                value : ctrl.newProjectName()
                            })
                        ]),
                        m('.form-group.m-v-sm', [
                            m('label[for="projectDesc].f-w-lg.text-bigger', 'Project Description'),
                            m('textarea.form-control', {
                                onkeyup: m.withAttr('value', ctrl.newProjectDesc),
                                value : ctrl.newProjectDesc()
                            })
                        ]),
                        m('.f-w-lg.text-bigger','Category'),
                        m('.category-radio.p-h-md', [
                            ctrl.categoryList.map(function(cat){
                                return m('.radio', m('label', [  m('input', {
                                    type: 'radio',
                                    name: 'projectCategory',
                                    value: cat.value,
                                    checked: ctrl.newProjectCategory() === cat.value,
                                    onchange : m.withAttr('value', ctrl.newProjectCategory)
                                }), cat.label ]));
                            })
                        ])
                    ])
                ]),
                m('.modal-footer', [
                    m('button[type="button"].btn.btn-default[data-dismiss="modal"]', { onclick : ctrl.reset},  'Cancel'),
                    m('button[type="button"].btn.btn-success', { onclick : ctrl.add },'Add')
                ])
            ]),
            processing : m('.modal-content',
                m('.modal-content',
                    m('.modal-header', [
                        m('button.close[data-dismiss="modal"][aria-label="Close"]',{ onclick : ctrl.reset}, [
                            m('span[aria-hidden="true"]','×'),
                        ]),
                    ]),
                    m('.modal-body', [
                            m('.add-project-processing', 'Saving your project...')
                        ]
                    )
                )
            ),
            success : m('.modal-content', [
                m('.modal-content',
                    m('.modal-body', [
                            m('button.close[data-dismiss="modal"][aria-label="Close"]',{ onclick : ctrl.reset}, [
                                m('span[aria-hidden="true"]','×'),
                            ]),
                            m('h4.p-md.add-project-success.text-success', 'Project created successfully!')
                        ]
                    ),
                    m('.modal-footer', [
                        m('button[type="button"].btn.btn-default[data-dismiss="modal"]', { onclick : ctrl.reset },  'Keep Working Here'),
                        m('a.btn.btn-success', { href : ctrl.goToProjectLink() },'Go to New Project')
                    ])
                )
            ]),
            error : m('.modal-content', [
                m('.modal-content',
                    m('.modal-body', [
                            m('button.close[data-dismiss="modal"][aria-label="Close"]',{ onclick : ctrl.reset}, [
                                m('span[aria-hidden="true"]','×'),
                            ]),
                            m('h4.p-md.add-project-error.text-danger', 'Couldn\'t create your project'),
                            m('p', ctrl.errorMessage[ctrl.errorMessageType()])
                        ]
                    )
                )
            ])
        };

        return  m('', [
            ctrl.options.buttonTemplate,
            m('#addProjectModal.modal.fade[tabindex=-1][role="dialog"][aria-labelledby="addProject"][aria-hidden="true"]',
                m('.modal-dialog',
                    templates[ctrl.viewState()]
                )
            )
        ]);
    }
};

module.exports = AddProject;<commit_msg>Fix resetting all sections in add project form  [skip ci]<commit_after>/**
 * UI and function to add project
 */
'use strict';

require('css/add-project-plugin.css');
var $ = require('jquery');
var m = require('mithril');
var $osf = require('js/osfHelpers');

// XHR configuration to get apiserver connection to work
var xhrconfig = function (xhr) {
    xhr.withCredentials = true;
};

var AddProject = {
    controller : function (options) {
        var self = this;
        self.defaults = {
            buttonTemplate : m('.btn.btn-primary[data-toggle="modal"][data-target="#addProjectModal"]', 'Add new Project'),
            parent : null
        };
        self.viewState = m.prop('form'); // 'processing', 'success', 'error';
        self.options = $.extend(self.defaults, options);
        self.init = function _init () {

        };
        self.newProjectName = m.prop('');
        self.newProjectDesc = m.prop('');
        self.categoryList = [
            { value: 'project', label: 'Project'},
            { value: 'hypothesis', label : 'Hypothesis' },
            { value: 'methods and measures', label :  'Methods and Measures'},
            { value: 'procedure', label:  'Procedure'},
            { value: 'instrumentation', label: 'Instrumentation'},
            { value: 'data', label:  'Data'},
            { value: 'analysis', label: 'Analysis'},
            { value: 'communication',label: 'Communication'},
            { value: 'other', label: 'Other'}
        ];
        self.newProjectCategory = m.prop(self.categoryList[0].value);
        self.goToProjectLink = m.prop('');
        self.errorMessageType = m.prop('unknown');
        self.errorMessage = {
            'unknown' : 'There was an unknown error. Please try again later.'
        };
        self.chooseCategory = function(event){
            self.newProjectCategory = $(this).val();
        };
        self.add = function _add () {
            self.viewState('processing');
            var url = $osf.apiV2Url('nodes/', { query : {}});
            var data = {
                    'data' : {
                        'type': 'nodes',
                        'attributes': {
                            'title': self.newProjectName(),
                            'category': self.newProjectCategory,
                            'description' : self.newProjectDesc()
                        }
                    }
                };
            var success = function _success (result) {
                self.viewState('success');
                console.log('success', result);
                self.goToProjectLink(result.data.links.html);
            };
            var error = function _error (result) {
                self.viewState('error');
                console.log('error', result);
            };
            m.request({method : 'POST', url : url, data : data, config : xhrconfig})
                .then(success, error);
            self.newProjectName('');
            //m.redraw(true);

        };
        self.reset = function _reset(){
            self.newProjectName('');
            self.viewState('form');
            self.newProjectDesc('');
            self.newProjectCategory(self.categoryList[0].value);
            $('.modal').modal('hide');
        };
    },
    view : function (ctrl, options) {
        var templates = {
            form : m('.modal-content', [
                m('.modal-header', [
                    m('button.close[data-dismiss="modal"][aria-label="Close"]',{ onclick : ctrl.reset}, [
                        m('span[aria-hidden="true"]','×'),
                    ]),
                    m('h3.modal-title', 'Add New Project')
                ]),
                m('.modal-body', [
                    m('', [
                        m('.form-group.m-v-sm', [
                            m('label[for="projectName].f-w-lg.text-bigger', 'Project Name'),
                            m('input[type="text"].form-control', {
                                onkeyup: function(ev){
                                    if (ev.which === 13) {
                                         ctrl.add();
                                    }
                                    ctrl.newProjectName($(this).val());
                                },
                                value : ctrl.newProjectName()
                            })
                        ]),
                        m('.form-group.m-v-sm', [
                            m('label[for="projectDesc].f-w-lg.text-bigger', 'Project Description'),
                            m('textarea.form-control', {
                                onkeyup: m.withAttr('value', ctrl.newProjectDesc),
                                value : ctrl.newProjectDesc()
                            })
                        ]),
                        m('.f-w-lg.text-bigger','Category'),
                        m('.category-radio.p-h-md', [
                            ctrl.categoryList.map(function(cat){
                                return m('.radio', m('label', [  m('input', {
                                    type: 'radio',
                                    name: 'projectCategory',
                                    value: cat.value,
                                    checked: ctrl.newProjectCategory() === cat.value,
                                    onchange : m.withAttr('value', ctrl.newProjectCategory)
                                }), cat.label ]));
                            })
                        ])
                    ])
                ]),
                m('.modal-footer', [
                    m('button[type="button"].btn.btn-default[data-dismiss="modal"]', { onclick : ctrl.reset},  'Cancel'),
                    m('button[type="button"].btn.btn-success', { onclick : ctrl.add },'Add')
                ])
            ]),
            processing : m('.modal-content',
                m('.modal-content',
                    m('.modal-header', [
                        m('button.close[data-dismiss="modal"][aria-label="Close"]',{ onclick : ctrl.reset}, [
                            m('span[aria-hidden="true"]','×'),
                        ]),
                    ]),
                    m('.modal-body', [
                            m('.add-project-processing', 'Saving your project...')
                        ]
                    )
                )
            ),
            success : m('.modal-content', [
                m('.modal-content',
                    m('.modal-body', [
                            m('button.close[data-dismiss="modal"][aria-label="Close"]',{ onclick : ctrl.reset}, [
                                m('span[aria-hidden="true"]','×'),
                            ]),
                            m('h4.p-md.add-project-success.text-success', 'Project created successfully!')
                        ]
                    ),
                    m('.modal-footer', [
                        m('button[type="button"].btn.btn-default[data-dismiss="modal"]', { onclick : ctrl.reset },  'Keep Working Here'),
                        m('a.btn.btn-success', { href : ctrl.goToProjectLink() },'Go to New Project')
                    ])
                )
            ]),
            error : m('.modal-content', [
                m('.modal-content',
                    m('.modal-body', [
                            m('button.close[data-dismiss="modal"][aria-label="Close"]',{ onclick : ctrl.reset}, [
                                m('span[aria-hidden="true"]','×'),
                            ]),
                            m('h4.p-md.add-project-error.text-danger', 'Couldn\'t create your project'),
                            m('p', ctrl.errorMessage[ctrl.errorMessageType()])
                        ]
                    )
                )
            ])
        };

        return  m('', [
            ctrl.options.buttonTemplate,
            m('#addProjectModal.modal.fade[tabindex=-1][role="dialog"][aria-labelledby="addProject"][aria-hidden="true"]',
                m('.modal-dialog',
                    templates[ctrl.viewState()]
                )
            )
        ]);
    }
};

module.exports = AddProject;<|endoftext|>


<commit_before>/* eslint no-shadow: ["error", { "allow": ["state", "routes"] }]*/
/* eslint no-param-reassign: ["off"] */
import routes from '../../api/routes';
import * as types from '../mutation-types';

// initial state
const state = {
  all: [],
};

// getters
const getters = {
  allRoutes: state => state.all,
};

// actions
const actions = {
  getAllRoutes({ commit }) {
    routes.getRoutes((routes) => {
      commit(types.RECEIVE_POSTS, { routes });
    });
  },
};

// mutations
const mutations = {
  [types.RECEIVE_ROUTES](state, { routes }) {
    state.all = routes;
  },
};

export default {
  state,
  getters,
  actions,
  mutations,
};
<commit_msg>eslint fix<commit_after>/* eslint no-shadow: ["error", { "allow": ["state", "routes"] }] */
/* eslint no-param-reassign: ["off"] */
import routes from '../../api/routes';
import * as types from '../mutation-types';

// initial state
const state = {
  all: [],
};

// getters
const getters = {
  allRoutes: state => state.all,
};

// actions
const actions = {
  getAllRoutes({ commit }) {
    routes.getRoutes((routes) => {
      commit(types.RECEIVE_POSTS, { routes });
    });
  },
};

// mutations
const mutations = {
  [types.RECEIVE_ROUTES](state, { routes }) {
    state.all = routes;
  },
};

export default {
  state,
  getters,
  actions,
  mutations,
};
<|endoftext|>


<commit_before>import AppDispatcher from '../dispatchers/AppDispatcher';
import events from 'events';
import fetch from '../core/fetch';
import VehicleActions from '../actions/VehicleActions';
import { apiBase, apiKey } from '../config';

const EventEmitter = events.EventEmitter;
const KEY = apiKey;

class VehicleStore extends EventEmitter {
	constructor() {
		super();
		this.state = {
			vehicle: {
				year: '',
				make: '',
				model: '',
			},
			showStyle: false,
			catStyleParts: null,
			activeCategory: null,
			error: null,
			partToAdd: null,
			partToRemove: null,
			showIconMediaVehicle: true,
		};
		this.bindActions(VehicleActions);
	}

	setActiveCategory(category) {
		let style = null;
		if (this.checkStyle(category)) {
			style = this.checkStyle(category);
		}
		this.setState({ activeCategory: category, style });
	}

	async getCategoryStyles() {
		if (this.state.vehicle.year === '' || this.state.vehicle.make === '' || this.state.vehicle.model === '') {
			return;
		}
		const params = '&year=' + this.state.vehicle.year + '&make=' + this.state.vehicle.make + '&model=' + encodeURIComponent(this.state.vehicle.model) + '&envision=true';
		Promise.all([
			await fetch(`${apiBase}/vehicle/mongo/categoryStyleParts?key=${KEY}` + params, {
				method: 'post',
				headers: {
					'Content-Type': 'application/x-www-form-urlencoded',
					'Accept': 'application/json',
				},
			}),
			await fetch(`${apiBase}/category/320/parts?key=${KEY}`),
		]).then((resps) => {
			return Promise.all([resps[0].json(), resps[1].json()]);
		}).then((resps) => {
			let catStyleParts = [];
			if (Array.isArray(resps[0])) {
				catStyleParts = resps[0];
			}
			if (!Array.isArray(resps[1].parts) && !Array.isArray(resps[0])) {
				this.setState({ error: 'no parts returned.' });
				return;
			}
			if (!Array.isArray(resps[0])) {
				this.setState({ error: resps[0].message });
			}
			try {
				catStyleParts.push({ 'name': 'Seat Defenders', 'styles': [{ 'name': 'all', 'parts': resps[1].parts }] });
				let activeCategory = this.state.activeCategory;
				if (!activeCategory) {
					activeCategory = catStyleParts[0];
				}
				let style = this.state.style;
				if (this.checkStyle(activeCategory)) {
					style = this.checkStyle(activeCategory);
				}

				this.setState({ catStyleParts, activeCategory, showStyle: false, style });
			} catch (e) {
				this.setState({ error: e });
			}
		});
	}

	checkStyle(activeCategory) {
		// cat has one style called 'all'
		if (activeCategory.styles.length === 1 && activeCategory.styles[0].name === 'all') {
			return activeCategory.styles[0];
		}
		// cat has style with same name as current style
		for (const i in activeCategory.styles) {
			if (this.state.style && activeCategory.styles[i].name === this.state.style.name) {
				return activeCategory.styles[i];
			}
		}
		return null;
	}

	async set(vehicle) {
		let showStyle = true;
		if (vehicle.style && vehicle.style !== '') {
			showStyle = false;
		}
		this.setState({ vehicle, showStyle });
		await this.getCategoryStyles();
	}

	setShowStyleState(showStyle) {
		this.setState({ showStyle });
	}
	setShowIconMediaVehicleState(showIconMediaVehicle) {
		this.setState({ showIconMediaVehicle });
	}

	setIconMediaVehicle(iconMediaVehicle) {
		this.setState({ iconMediaVehicle, showIconMediaVehicle: false });
	}

	setStyle(style) {
		const vehicle = this.state.vehicle;
		vehicle.style = style.name;
		this.setState({ showStyle: false, style, vehicle });
	}

	addPartToVehicle(part) {
		// must have iconLayer - TODO is this true?
		if (part.iconLayer === '') {
			return;
		}
		const vehicle = this.state.vehicle;
		if (!vehicle.parts) {
			vehicle.parts = [];
		}
		// remove part with same iconLayer
		let partToRemove = null;
		for (const i in vehicle.parts) {
			if (vehicle.parts[i].iconLayer === part.iconLayer) {
				partToRemove = vehicle.parts[i];
				vehicle.parts.splice(i);
			}
		}
		vehicle.parts.push(part);
		this.setState({ vehicle, partToAdd: part, partToRemove });
	}

	removePartFromVehicle(part) {
		const vehicle = this.state.vehicle;
		for (const i in vehicle.parts) {
			if (vehicle.parts[i].id === part.id) {
				vehicle.parts.splice(i, 1);
			}
		}
		this.setState({ vehicle, partToRemove: part });
	}
}

VehicleStore.dispatchToken = null;

export default AppDispatcher.createStore(VehicleStore);
<commit_msg>prevents duplicate parts on envision vehicle<commit_after>import AppDispatcher from '../dispatchers/AppDispatcher';
import events from 'events';
import fetch from '../core/fetch';
import VehicleActions from '../actions/VehicleActions';
import { apiBase, apiKey } from '../config';

const EventEmitter = events.EventEmitter;
const KEY = apiKey;

class VehicleStore extends EventEmitter {
	constructor() {
		super();
		this.state = {
			vehicle: {
				year: '',
				make: '',
				model: '',
			},
			showStyle: false,
			catStyleParts: null,
			activeCategory: null,
			error: null,
			partToAdd: null,
			partToRemove: null,
			showIconMediaVehicle: true,
		};
		this.bindActions(VehicleActions);
	}

	setActiveCategory(category) {
		let style = null;
		if (this.checkStyle(category)) {
			style = this.checkStyle(category);
		}
		this.setState({ activeCategory: category, style });
	}

	async getCategoryStyles() {
		if (this.state.vehicle.year === '' || this.state.vehicle.make === '' || this.state.vehicle.model === '') {
			return;
		}
		const params = '&year=' + this.state.vehicle.year + '&make=' + this.state.vehicle.make + '&model=' + encodeURIComponent(this.state.vehicle.model) + '&envision=true';
		Promise.all([
			await fetch(`${apiBase}/vehicle/mongo/categoryStyleParts?key=${KEY}` + params, {
				method: 'post',
				headers: {
					'Content-Type': 'application/x-www-form-urlencoded',
					'Accept': 'application/json',
				},
			}),
			await fetch(`${apiBase}/category/320/parts?key=${KEY}`),
		]).then((resps) => {
			return Promise.all([resps[0].json(), resps[1].json()]);
		}).then((resps) => {
			let catStyleParts = [];
			if (Array.isArray(resps[0])) {
				catStyleParts = resps[0];
			}
			if (!Array.isArray(resps[1].parts) && !Array.isArray(resps[0])) {
				this.setState({ error: 'no parts returned.' });
				return;
			}
			if (!Array.isArray(resps[0])) {
				this.setState({ error: resps[0].message });
			}
			try {
				catStyleParts.push({ 'name': 'Seat Defenders', 'styles': [{ 'name': 'all', 'parts': resps[1].parts }] });
				let activeCategory = this.state.activeCategory;
				if (!activeCategory) {
					activeCategory = catStyleParts[0];
				}
				let style = this.state.style;
				if (this.checkStyle(activeCategory)) {
					style = this.checkStyle(activeCategory);
				}

				this.setState({ catStyleParts, activeCategory, showStyle: false, style });
			} catch (e) {
				this.setState({ error: e });
			}
		});
	}

	checkStyle(activeCategory) {
		// cat has one style called 'all'
		if (activeCategory.styles.length === 1 && activeCategory.styles[0].name === 'all') {
			return activeCategory.styles[0];
		}
		// cat has style with same name as current style
		for (const i in activeCategory.styles) {
			if (this.state.style && activeCategory.styles[i].name === this.state.style.name) {
				return activeCategory.styles[i];
			}
		}
		return null;
	}

	async set(vehicle) {
		let showStyle = true;
		if (vehicle.style && vehicle.style !== '') {
			showStyle = false;
		}
		this.setState({ vehicle, showStyle });
		await this.getCategoryStyles();
	}

	setShowStyleState(showStyle) {
		this.setState({ showStyle });
	}
	setShowIconMediaVehicleState(showIconMediaVehicle) {
		this.setState({ showIconMediaVehicle });
	}

	setIconMediaVehicle(iconMediaVehicle) {
		this.setState({ iconMediaVehicle, showIconMediaVehicle: false });
	}

	setStyle(style) {
		const vehicle = this.state.vehicle;
		vehicle.style = style.name;
		this.setState({ showStyle: false, style, vehicle });
	}

	// adds part to state.vehicle.parts; removes part of same layer
	addPartToVehicle(part) {
		// must have iconLayer - TODO is this true?
		if (part.iconLayer === '') {
			return;
		}
		const vehicle = this.state.vehicle;
		if (!vehicle.parts) {
			vehicle.parts = [];
		}
		// remove part with same iconLayer
		let partToRemove = null;
		for (const i in vehicle.parts) {
			if (vehicle.parts[i].iconLayer === part.iconLayer && !this.partIsOnVehicle(part)) {
				partToRemove = vehicle.parts[i];
				vehicle.parts.splice(i);
			}
		}
		if (!this.partIsOnVehicle(part)) {
			vehicle.parts.push(part);
		}
		this.setState({ vehicle, partToAdd: part, partToRemove });
	}

	// removes part from state.vehicle.parts
	removePartFromVehicle(part) {
		const vehicle = this.state.vehicle;
		for (const i in vehicle.parts) {
			if (vehicle.parts[i].id === part.id) {
				vehicle.parts.splice(i, 1);
			}
		}
		this.setState({ vehicle, partToRemove: part });
	}

	// returns true if part is already in state.vehicle.parts; otherwise false
	partIsOnVehicle(part) {
		for (const i in this.state.vehicle.parts) {
			if (part.id === this.state.vehicle.parts[i].id) {
				return true;
			}
		}
		return false;
	}
}

VehicleStore.dispatchToken = null;

export default AppDispatcher.createStore(VehicleStore);
<|endoftext|>


<commit_before>import React from 'react';
import Helmet from 'react-helmet';
import BackIcon from 'react-icons/lib/fa/chevron-left';
import ForwardIcon from 'react-icons/lib/fa/chevron-right';

import Link from '../components/Link';
import Tags from '../components/Tags';

import '../css/basics.scss';
import '../css/blog-post.scss';
import '../css/lists.scss';

export default function Template({ data, pathContext }) {
  const { markdownRemark: post } = data;
  const { next, prev } = pathContext;
  return (

      <Helmet title={`${post.frontmatter.title}`} />

          {post.frontmatter.date}

          {post.frontmatter.title}

        <Tags className="no-underline" list={post.frontmatter.tags || []} />

          {prev &&
            <Link className="link prev" to={prev.frontmatter.path}>
              <BackIcon
                style={{
                  height: '0.75rem',
                  position: 'relative',
                  top: '0.3rem',
                }}
              />

                {prev.frontmatter.title}

            </Link>}
          {next &&
            <Link className="link next" to={next.frontmatter.path}>

                {next.frontmatter.title}

              <ForwardIcon
                style={{
                  height: '0.75rem',
                  position: 'relative',
                  top: '0.3rem',
                }}
              />
            </Link>}

  );
}

export const pageQuery = graphql`
  query BlogPostByPath($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        path
        tags
        title
      }
    }
  }
`;<commit_msg>edit arrow position for blog pagination<commit_after>import React from 'react';
import Helmet from 'react-helmet';
import BackIcon from 'react-icons/lib/fa/chevron-left';
import ForwardIcon from 'react-icons/lib/fa/chevron-right';

import Link from '../components/Link';
import Tags from '../components/Tags';

import '../css/basics.scss';
import '../css/blog-post.scss';
import '../css/lists.scss';

export default function Template({ data, pathContext }) {
  const { markdownRemark: post } = data;
  const { next, prev } = pathContext;
  return (

      <Helmet title={`${post.frontmatter.title}`} />

          {post.frontmatter.date}

          {post.frontmatter.title}

        <Tags className="no-underline" list={post.frontmatter.tags || []} />

          {prev &&
            <Link className="link prev" to={prev.frontmatter.path}>
              <BackIcon
                style={{
                  height: '0.75rem',
                  position: 'relative',
                  top: '0.15rem',
                }}
              />

                {prev.frontmatter.title}

            </Link>}
          {next &&
            <Link className="link next" to={next.frontmatter.path}>

                {next.frontmatter.title}

              <ForwardIcon
                style={{
                  height: '0.75rem',
                  position: 'relative',
                  top: '0.15rem',
                }}
              />
            </Link>}

  );
}

export const pageQuery = graphql`
  query BlogPostByPath($path: String!) {
    markdownRemark(frontmatter: { path: { eq: $path } }) {
      html
      frontmatter {
        date(formatString: "MMMM DD, YYYY")
        path
        tags
        title
      }
    }
  }
`;<|endoftext|>


<commit_before>var assert = require('assert')
var bufferutils = require('./bufferutils')
var ops = require('./opcodes')
var scripts = require('./scripts')

var Address = require('./address')
var ECPair = require('./ecpair')
var ECSignature = require('./ecsignature')
var Script = require('./script')
var Transaction = require('./transaction')

function extractInput (txIn) {
  var redeemScript
  var scriptSig = txIn.script
  var prevOutScript
  var prevOutType = scripts.classifyInput(scriptSig, true)
  var scriptType

  // Re-classify if scriptHash
  if (prevOutType === 'scripthash') {
    redeemScript = Script.fromBuffer(scriptSig.chunks.slice(-1)[0])
    prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())

    scriptSig = Script.fromChunks(scriptSig.chunks.slice(0, -1))
    scriptType = scripts.classifyInput(scriptSig, true)
  } else {
    scriptType = prevOutType
  }

  // Extract hashType, pubKeys and signatures
  var hashType, parsed, pubKeys, signatures

  switch (scriptType) {
    case 'pubkeyhash': {
      parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
      hashType = parsed.hashType
      pubKeys = scriptSig.chunks.slice(1)
      signatures = [parsed.signature]
      prevOutScript = ECPair.fromPublicKeyBuffer(pubKeys[0]).getAddress().toOutputScript()

      break
    }

    case 'pubkey': {
      parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
      hashType = parsed.hashType
      signatures = [parsed.signature]

      if (redeemScript) {
        pubKeys = redeemScript.chunks.slice(0, 1)
      }

      break
    }

    case 'multisig': {
      signatures = scriptSig.chunks.slice(1).map(function (chunk) {
        if (chunk === ops.OP_0) return chunk

        var parsed = ECSignature.parseScriptSignature(chunk)
        hashType = parsed.hashType

        return parsed.signature
      })

      if (redeemScript) {
        pubKeys = redeemScript.chunks.slice(1, -2)
      }

      break
    }
  }

  return {
    hashType: hashType,
    prevOutScript: prevOutScript,
    prevOutType: prevOutType,
    pubKeys: pubKeys,
    redeemScript: redeemScript,
    scriptType: scriptType,
    signatures: signatures
  }
}

function TransactionBuilder () {
  this.prevTxMap = {}
  this.prevOutScripts = {}
  this.prevOutTypes = {}

  this.inputs = []
  this.tx = new Transaction()
}

TransactionBuilder.fromTransaction = function (transaction) {
  var txb = new TransactionBuilder()

  // Copy other transaction fields
  txb.tx.version = transaction.version
  txb.tx.locktime = transaction.locktime

  // Extract/add inputs
  transaction.ins.forEach(function (txIn) {
    txb.addInput(txIn.hash, txIn.index, txIn.sequence)
  })

  // Extract/add outputs
  transaction.outs.forEach(function (txOut) {
    txb.addOutput(txOut.script, txOut.value)
  })

  // Extract/add signatures
  txb.inputs = transaction.ins.map(function (txIn) {
    // TODO: remove me after testcase added
    assert(!Transaction.isCoinbaseHash(txIn.hash), 'coinbase inputs not supported')

    // Ignore empty scripts
    if (txIn.script.buffer.length === 0) return {}

    return extractInput(txIn)
  })

  return txb
}

TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) {
  // is it a txId?
  if (typeof txHash === 'string') {
    // a txId is big-endian hex, we want a little-endian Buffer
    txHash = new Buffer(txHash, 'hex')
    Array.prototype.reverse.call(txHash)

  // is it a Transaction?
  } else if (txHash instanceof Transaction) {
    prevOutScript = txHash.outs[vout].script
    txHash = txHash.getHash()
  }

  var input = {}
  if (prevOutScript) {
    var prevOutType = scripts.classifyOutput(prevOutScript)

    // if we can, extract pubKey information
    switch (prevOutType) {
      case 'multisig': {
        input.pubKeys = prevOutScript.chunks.slice(1, -2)
        break
      }

      case 'pubkey': {
        input.pubKeys = prevOutScript.chunks.slice(0, 1)
        break
      }
    }

    if (prevOutType !== 'scripthash') {
      input.scriptType = prevOutType
    }

    input.prevOutScript = prevOutScript
    input.prevOutType = prevOutType
  }

  assert(this.inputs.every(function (input2) {
    if (input2.hashType === undefined) return true

    return input2.hashType & Transaction.SIGHASH_ANYONECANPAY
  }), 'No, this would invalidate signatures')

  var prevOut = txHash.toString('hex') + ':' + vout
  assert(!(prevOut in this.prevTxMap), 'Transaction is already an input')

  var vin = this.tx.addInput(txHash, vout, sequence)
  this.inputs[vin] = input
  this.prevTxMap[prevOut] = vin

  return vin
}

TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) {
  assert(this.inputs.every(function (input) {
    if (input.hashType === undefined) return true

    return (input.hashType & 0x1f) === Transaction.SIGHASH_SINGLE
  }), 'No, this would invalidate signatures')

  // Attempt to get a valid address if it's a base58 address string
  if (typeof scriptPubKey === 'string') {
    scriptPubKey = Address.fromBase58Check(scriptPubKey)
  }

  // Attempt to get a valid script if it's an Address object
  if (scriptPubKey instanceof Address) {
    scriptPubKey = scriptPubKey.toOutputScript()
  }

  return this.tx.addOutput(scriptPubKey, value)
}

TransactionBuilder.prototype.build = function () {
  return this.__build(false)
}
TransactionBuilder.prototype.buildIncomplete = function () {
  return this.__build(true)
}

var canSignTypes = {
  'pubkeyhash': true,
  'multisig': true,
  'pubkey': true
}

TransactionBuilder.prototype.__build = function (allowIncomplete) {
  if (!allowIncomplete) {
    assert(this.tx.ins.length > 0, 'Transaction has no inputs')
    assert(this.tx.outs.length > 0, 'Transaction has no outputs')
  }

  var tx = this.tx.clone()

  // Create script signatures from signature meta-data
  this.inputs.forEach(function (input, index) {
    var scriptType = input.scriptType
    var scriptSig

    if (!allowIncomplete) {
      assert(!!scriptType, 'Transaction is not complete')
      assert(scriptType in canSignTypes, scriptType + ' not supported')
      assert(input.signatures, 'Transaction is missing signatures')
    }

    if (input.signatures) {
      switch (scriptType) {
        case 'pubkeyhash': {
          var pkhSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = scripts.pubKeyHashInput(pkhSignature, input.pubKeys[0])
          break
        }

        case 'multisig': {
          // Array.prototype.map is sparse-compatible
          var msSignatures = input.signatures.map(function (signature) {
            return signature && signature.toScriptSignature(input.hashType)
          })

          // fill in blanks with OP_0
          if (allowIncomplete) {
            for (var i = 0; i < msSignatures.length; ++i) {
              if (msSignatures[i]) continue

              msSignatures[i] = ops.OP_0
            }
          } else {
            // Array.prototype.filter returns non-sparse array
            msSignatures = msSignatures.filter(function (x) { return x })
          }

          var redeemScript = allowIncomplete ? undefined : input.redeemScript
          scriptSig = scripts.multisigInput(msSignatures, redeemScript)
          break
        }

        case 'pubkey': {
          var pkSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = scripts.pubKeyInput(pkSignature)
          break
        }
      }
    }

    // did we build a scriptSig?
    if (scriptSig) {
      // wrap as scriptHash if necessary
      if (input.prevOutType === 'scripthash') {
        scriptSig = scripts.scriptHashInput(scriptSig, input.redeemScript)
      }

      tx.setInputScript(index, scriptSig)
    }
  })

  return tx
}

TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hashType) {
  assert(index in this.inputs, 'No input at index: ' + index)
  hashType = hashType || Transaction.SIGHASH_ALL

  var input = this.inputs[index]
  var canSign = input.hashType &&
    input.prevOutScript &&
    input.prevOutType &&
    input.pubKeys &&
    input.scriptType &&
    input.signatures

  var kpPubKey = keyPair.getPublicKeyBuffer()

  // are we almost ready to sign?
  if (canSign) {
    // if redeemScript was provided, enforce consistency
    if (redeemScript) {
      assert.deepEqual(input.redeemScript, redeemScript, 'Inconsistent redeemScript')
    }

    assert.equal(input.hashType, hashType, 'Inconsistent hashType')

  // no? prepare
  } else {
    // must be pay-to-scriptHash?
    if (redeemScript) {
      // if we have a prevOutScript, enforce scriptHash equality to the redeemScript
      if (input.prevOutScript) {
        assert.equal(input.prevOutType, 'scripthash', 'PrevOutScript must be P2SH')

        var scriptHash = input.prevOutScript.chunks[1]
        assert.deepEqual(scriptHash, redeemScript.getHash(), 'RedeemScript does not match ' + scriptHash.toString('hex'))
      }

      var scriptType = scripts.classifyOutput(redeemScript)
      assert(scriptType in canSignTypes, 'RedeemScript not supported (' + scriptType + ')')

      var pubKeys = []
      switch (scriptType) {
        case 'multisig': {
          pubKeys = redeemScript.chunks.slice(1, -2)
          break
        }

        case 'pubkeyhash': {
          var pkh1 = redeemScript.chunks[2]
          var pkh2 = keyPair.getAddress().hash

          assert.deepEqual(pkh1, pkh2, 'privateKey cannot sign for this input')
          pubKeys = [kpPubKey]
          break
        }

        case 'pubkey': {
          pubKeys = redeemScript.chunks.slice(0, 1)
          break
        }
      }

      if (!input.prevOutScript) {
        input.prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())
        input.prevOutType = 'scripthash'
      }

      input.pubKeys = pubKeys
      input.redeemScript = redeemScript
      input.scriptType = scriptType

    // cannot be pay-to-scriptHash
    } else {
      assert.notEqual(input.prevOutType, 'scripthash', 'PrevOutScript is P2SH, missing redeemScript')

      // can we otherwise sign this?
      if (input.scriptType) {
        assert(input.pubKeys, input.scriptType + ' not supported')

      // we know nothin' Jon Snow, assume pubKeyHash
      } else {
        input.prevOutScript = keyPair.getAddress().toOutputScript()
        input.prevOutType = 'pubkeyhash'
        input.pubKeys = [kpPubKey]
        input.scriptType = input.prevOutType
      }
    }

    input.hashType = hashType
    input.signatures = input.signatures || []
  }

  var signatureScript = input.redeemScript || input.prevOutScript
  var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)

  // enforce signature order matches public keys
  if (input.scriptType === 'multisig' && input.redeemScript && input.signatures.length !== input.pubKeys.length) {
    // maintain a local copy of unmatched signatures
    var unmatched = input.signatures.slice()

    input.signatures = input.pubKeys.map(function (pubKey) {
      var match
      var keyPair2 = ECPair.fromPublicKeyBuffer(pubKey)

      // check for any matching signatures
      unmatched.some(function (signature, i) {
        if (!signature || !keyPair2.verify(signatureHash, signature)) return false
        match = signature

        // remove matched signature from unmatched
        unmatched.splice(i, 1)

        return true
      })

      return match || undefined
    })
  }

  // enforce in order signing of public keys
  assert(input.pubKeys.some(function (pubKey, i) {
    if (!bufferutils.equal(kpPubKey, pubKey)) return false

    assert(!input.signatures[i], 'Signature already exists')

    var signature = keyPair.sign(signatureHash)
    input.signatures[i] = signature

    return true
  }), 'key pair cannot sign for this input')
}

module.exports = TransactionBuilder
<commit_msg>TxBuilder: clarify comment about coin base script support<commit_after>var assert = require('assert')
var bufferutils = require('./bufferutils')
var ops = require('./opcodes')
var scripts = require('./scripts')

var Address = require('./address')
var ECPair = require('./ecpair')
var ECSignature = require('./ecsignature')
var Script = require('./script')
var Transaction = require('./transaction')

function extractInput (txIn) {
  var redeemScript
  var scriptSig = txIn.script
  var prevOutScript
  var prevOutType = scripts.classifyInput(scriptSig, true)
  var scriptType

  // Re-classify if scriptHash
  if (prevOutType === 'scripthash') {
    redeemScript = Script.fromBuffer(scriptSig.chunks.slice(-1)[0])
    prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())

    scriptSig = Script.fromChunks(scriptSig.chunks.slice(0, -1))
    scriptType = scripts.classifyInput(scriptSig, true)
  } else {
    scriptType = prevOutType
  }

  // Extract hashType, pubKeys and signatures
  var hashType, parsed, pubKeys, signatures

  switch (scriptType) {
    case 'pubkeyhash': {
      parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
      hashType = parsed.hashType
      pubKeys = scriptSig.chunks.slice(1)
      signatures = [parsed.signature]
      prevOutScript = ECPair.fromPublicKeyBuffer(pubKeys[0]).getAddress().toOutputScript()

      break
    }

    case 'pubkey': {
      parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
      hashType = parsed.hashType
      signatures = [parsed.signature]

      if (redeemScript) {
        pubKeys = redeemScript.chunks.slice(0, 1)
      }

      break
    }

    case 'multisig': {
      signatures = scriptSig.chunks.slice(1).map(function (chunk) {
        if (chunk === ops.OP_0) return chunk

        var parsed = ECSignature.parseScriptSignature(chunk)
        hashType = parsed.hashType

        return parsed.signature
      })

      if (redeemScript) {
        pubKeys = redeemScript.chunks.slice(1, -2)
      }

      break
    }
  }

  return {
    hashType: hashType,
    prevOutScript: prevOutScript,
    prevOutType: prevOutType,
    pubKeys: pubKeys,
    redeemScript: redeemScript,
    scriptType: scriptType,
    signatures: signatures
  }
}

function TransactionBuilder () {
  this.prevTxMap = {}
  this.prevOutScripts = {}
  this.prevOutTypes = {}

  this.inputs = []
  this.tx = new Transaction()
}

TransactionBuilder.fromTransaction = function (transaction) {
  var txb = new TransactionBuilder()

  // Copy other transaction fields
  txb.tx.version = transaction.version
  txb.tx.locktime = transaction.locktime

  // Extract/add inputs
  transaction.ins.forEach(function (txIn) {
    txb.addInput(txIn.hash, txIn.index, txIn.sequence)
  })

  // Extract/add outputs
  transaction.outs.forEach(function (txOut) {
    txb.addOutput(txOut.script, txOut.value)
  })

  // Extract/add signatures
  txb.inputs = transaction.ins.map(function (txIn) {
    // TODO: verify whether extractInput is sane with coinbase scripts
    if (Transaction.isCoinbaseHash(txIn.hash)) {
      throw new Error('coinbase inputs not supported')
    }

    // Ignore empty scripts
    if (txIn.script.buffer.length === 0) return {}

    return extractInput(txIn)
  })

  return txb
}

TransactionBuilder.prototype.addInput = function (txHash, vout, sequence, prevOutScript) {
  // is it a txId?
  if (typeof txHash === 'string') {
    // a txId is big-endian hex, we want a little-endian Buffer
    txHash = new Buffer(txHash, 'hex')
    Array.prototype.reverse.call(txHash)

  // is it a Transaction?
  } else if (txHash instanceof Transaction) {
    prevOutScript = txHash.outs[vout].script
    txHash = txHash.getHash()
  }

  var input = {}
  if (prevOutScript) {
    var prevOutType = scripts.classifyOutput(prevOutScript)

    // if we can, extract pubKey information
    switch (prevOutType) {
      case 'multisig': {
        input.pubKeys = prevOutScript.chunks.slice(1, -2)
        break
      }

      case 'pubkey': {
        input.pubKeys = prevOutScript.chunks.slice(0, 1)
        break
      }
    }

    if (prevOutType !== 'scripthash') {
      input.scriptType = prevOutType
    }

    input.prevOutScript = prevOutScript
    input.prevOutType = prevOutType
  }

  assert(this.inputs.every(function (input2) {
    if (input2.hashType === undefined) return true

    return input2.hashType & Transaction.SIGHASH_ANYONECANPAY
  }), 'No, this would invalidate signatures')

  var prevOut = txHash.toString('hex') + ':' + vout
  assert(!(prevOut in this.prevTxMap), 'Transaction is already an input')

  var vin = this.tx.addInput(txHash, vout, sequence)
  this.inputs[vin] = input
  this.prevTxMap[prevOut] = vin

  return vin
}

TransactionBuilder.prototype.addOutput = function (scriptPubKey, value) {
  assert(this.inputs.every(function (input) {
    if (input.hashType === undefined) return true

    return (input.hashType & 0x1f) === Transaction.SIGHASH_SINGLE
  }), 'No, this would invalidate signatures')

  // Attempt to get a valid address if it's a base58 address string
  if (typeof scriptPubKey === 'string') {
    scriptPubKey = Address.fromBase58Check(scriptPubKey)
  }

  // Attempt to get a valid script if it's an Address object
  if (scriptPubKey instanceof Address) {
    scriptPubKey = scriptPubKey.toOutputScript()
  }

  return this.tx.addOutput(scriptPubKey, value)
}

TransactionBuilder.prototype.build = function () {
  return this.__build(false)
}
TransactionBuilder.prototype.buildIncomplete = function () {
  return this.__build(true)
}

var canSignTypes = {
  'pubkeyhash': true,
  'multisig': true,
  'pubkey': true
}

TransactionBuilder.prototype.__build = function (allowIncomplete) {
  if (!allowIncomplete) {
    assert(this.tx.ins.length > 0, 'Transaction has no inputs')
    assert(this.tx.outs.length > 0, 'Transaction has no outputs')
  }

  var tx = this.tx.clone()

  // Create script signatures from signature meta-data
  this.inputs.forEach(function (input, index) {
    var scriptType = input.scriptType
    var scriptSig

    if (!allowIncomplete) {
      assert(!!scriptType, 'Transaction is not complete')
      assert(scriptType in canSignTypes, scriptType + ' not supported')
      assert(input.signatures, 'Transaction is missing signatures')
    }

    if (input.signatures) {
      switch (scriptType) {
        case 'pubkeyhash': {
          var pkhSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = scripts.pubKeyHashInput(pkhSignature, input.pubKeys[0])
          break
        }

        case 'multisig': {
          // Array.prototype.map is sparse-compatible
          var msSignatures = input.signatures.map(function (signature) {
            return signature && signature.toScriptSignature(input.hashType)
          })

          // fill in blanks with OP_0
          if (allowIncomplete) {
            for (var i = 0; i < msSignatures.length; ++i) {
              if (msSignatures[i]) continue

              msSignatures[i] = ops.OP_0
            }
          } else {
            // Array.prototype.filter returns non-sparse array
            msSignatures = msSignatures.filter(function (x) { return x })
          }

          var redeemScript = allowIncomplete ? undefined : input.redeemScript
          scriptSig = scripts.multisigInput(msSignatures, redeemScript)
          break
        }

        case 'pubkey': {
          var pkSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = scripts.pubKeyInput(pkSignature)
          break
        }
      }
    }

    // did we build a scriptSig?
    if (scriptSig) {
      // wrap as scriptHash if necessary
      if (input.prevOutType === 'scripthash') {
        scriptSig = scripts.scriptHashInput(scriptSig, input.redeemScript)
      }

      tx.setInputScript(index, scriptSig)
    }
  })

  return tx
}

TransactionBuilder.prototype.sign = function (index, keyPair, redeemScript, hashType) {
  assert(index in this.inputs, 'No input at index: ' + index)
  hashType = hashType || Transaction.SIGHASH_ALL

  var input = this.inputs[index]
  var canSign = input.hashType &&
    input.prevOutScript &&
    input.prevOutType &&
    input.pubKeys &&
    input.scriptType &&
    input.signatures

  var kpPubKey = keyPair.getPublicKeyBuffer()

  // are we almost ready to sign?
  if (canSign) {
    // if redeemScript was provided, enforce consistency
    if (redeemScript) {
      assert.deepEqual(input.redeemScript, redeemScript, 'Inconsistent redeemScript')
    }

    assert.equal(input.hashType, hashType, 'Inconsistent hashType')

  // no? prepare
  } else {
    // must be pay-to-scriptHash?
    if (redeemScript) {
      // if we have a prevOutScript, enforce scriptHash equality to the redeemScript
      if (input.prevOutScript) {
        assert.equal(input.prevOutType, 'scripthash', 'PrevOutScript must be P2SH')

        var scriptHash = input.prevOutScript.chunks[1]
        assert.deepEqual(scriptHash, redeemScript.getHash(), 'RedeemScript does not match ' + scriptHash.toString('hex'))
      }

      var scriptType = scripts.classifyOutput(redeemScript)
      assert(scriptType in canSignTypes, 'RedeemScript not supported (' + scriptType + ')')

      var pubKeys = []
      switch (scriptType) {
        case 'multisig': {
          pubKeys = redeemScript.chunks.slice(1, -2)
          break
        }

        case 'pubkeyhash': {
          var pkh1 = redeemScript.chunks[2]
          var pkh2 = keyPair.getAddress().hash

          assert.deepEqual(pkh1, pkh2, 'privateKey cannot sign for this input')
          pubKeys = [kpPubKey]
          break
        }

        case 'pubkey': {
          pubKeys = redeemScript.chunks.slice(0, 1)
          break
        }
      }

      if (!input.prevOutScript) {
        input.prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())
        input.prevOutType = 'scripthash'
      }

      input.pubKeys = pubKeys
      input.redeemScript = redeemScript
      input.scriptType = scriptType

    // cannot be pay-to-scriptHash
    } else {
      assert.notEqual(input.prevOutType, 'scripthash', 'PrevOutScript is P2SH, missing redeemScript')

      // can we otherwise sign this?
      if (input.scriptType) {
        assert(input.pubKeys, input.scriptType + ' not supported')

      // we know nothin' Jon Snow, assume pubKeyHash
      } else {
        input.prevOutScript = keyPair.getAddress().toOutputScript()
        input.prevOutType = 'pubkeyhash'
        input.pubKeys = [kpPubKey]
        input.scriptType = input.prevOutType
      }
    }

    input.hashType = hashType
    input.signatures = input.signatures || []
  }

  var signatureScript = input.redeemScript || input.prevOutScript
  var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)

  // enforce signature order matches public keys
  if (input.scriptType === 'multisig' && input.redeemScript && input.signatures.length !== input.pubKeys.length) {
    // maintain a local copy of unmatched signatures
    var unmatched = input.signatures.slice()

    input.signatures = input.pubKeys.map(function (pubKey) {
      var match
      var keyPair2 = ECPair.fromPublicKeyBuffer(pubKey)

      // check for any matching signatures
      unmatched.some(function (signature, i) {
        if (!signature || !keyPair2.verify(signatureHash, signature)) return false
        match = signature

        // remove matched signature from unmatched
        unmatched.splice(i, 1)

        return true
      })

      return match || undefined
    })
  }

  // enforce in order signing of public keys
  assert(input.pubKeys.some(function (pubKey, i) {
    if (!bufferutils.equal(kpPubKey, pubKey)) return false

    assert(!input.signatures[i], 'Signature already exists')

    var signature = keyPair.sign(signatureHash)
    input.signatures[i] = signature

    return true
  }), 'key pair cannot sign for this input')
}

module.exports = TransactionBuilder
<|endoftext|>


<commit_before>var assert = require('assert')
var ops = require('./opcodes')
var scripts = require('./scripts')

var ECPubKey = require('./ecpubkey')
var ECSignature = require('./ecsignature')
var Script = require('./script')
var Transaction = require('./transaction')

function isCoinbase(txHash) {
  return Array.prototype.every.call(txHash, function(x) {
    return x === 0
  })
}

function extractInput(txIn) {
  var redeemScript
  var scriptSig = txIn.script
  var prevOutScript
  var prevOutType = scripts.classifyInput(scriptSig, true)
  var scriptType

  // Re-classify if scriptHash
  if (prevOutType === 'scripthash') {
    redeemScript = Script.fromBuffer(scriptSig.chunks.slice(-1)[0])
    prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())

    scriptSig = Script.fromChunks(scriptSig.chunks.slice(0, -1))
    scriptType = scripts.classifyInput(scriptSig, true)

  } else {
    scriptType = prevOutType
  }

  // Extract hashType, pubKeys and signatures
  var hashType, parsed, pubKeys, signatures

  switch (scriptType) {
    case 'pubkeyhash': {
      parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
      hashType = parsed.hashType
      pubKeys = [ECPubKey.fromBuffer(scriptSig.chunks[1])]
      signatures = [parsed.signature]
      prevOutScript = pubKeys[0].getAddress().toOutputScript()

      break
    }

    case 'pubkey': {
      parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
      hashType = parsed.hashType
      signatures = [parsed.signature]

      if (redeemScript) {
        pubKeys = [ECPubKey.fromBuffer(redeemScript.chunks[0])]
      }

      break
    }

    case 'multisig': {
      signatures = scriptSig.chunks.slice(1).map(function(chunk) {
        if (chunk === ops.OP_0) return chunk

        var parsed = ECSignature.parseScriptSignature(chunk)
        hashType = parsed.hashType

        return parsed.signature
      })

      if (redeemScript) {
        pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer)
      }

      break
    }
  }

  return {
    hashType: hashType,
    prevOutScript: prevOutScript,
    prevOutType: prevOutType,
    pubKeys: pubKeys,
    redeemScript: redeemScript,
    scriptType: scriptType,
    signatures: signatures
  }
}

function TransactionBuilder() {
  this.prevTxMap = {}
  this.prevOutScripts = {}
  this.prevOutTypes = {}

  this.inputs = []
  this.tx = new Transaction()
}

TransactionBuilder.fromTransaction = function(transaction) {
  var txb = new TransactionBuilder()

  // Copy other transaction fields
  txb.tx.version = transaction.version
  txb.tx.locktime = transaction.locktime

  // Extract/add inputs
  transaction.ins.forEach(function(txIn) {
    txb.addInput(txIn.hash, txIn.index, txIn.sequence)
  })

  // Extract/add outputs
  transaction.outs.forEach(function(txOut) {
    txb.addOutput(txOut.script, txOut.value)
  })

  // Extract/add signatures
  txb.inputs = transaction.ins.map(function(txIn) {
    // TODO: remove me after testcase added
    assert(!isCoinbase(txIn.hash), 'coinbase inputs not supported')

    // Ignore empty scripts
    if (txIn.script.buffer.length === 0) return

    return extractInput(txIn)
  })

  return txb
}

TransactionBuilder.prototype.addInput = function(prevTx, index, sequence, prevOutScript) {
  var prevOutHash

  if (typeof prevTx === 'string') {
    prevOutHash = new Buffer(prevTx, 'hex')

    // TxId hex is big-endian, we want little-endian hash
    Array.prototype.reverse.call(prevOutHash)

  } else if (prevTx instanceof Transaction) {
    prevOutHash = prevTx.getHash()
    prevOutScript = prevTx.outs[index].script

  } else {
    prevOutHash = prevTx

  }

  var input = {}
  if (prevOutScript) {
    var prevOutType = scripts.classifyOutput(prevOutScript)

    // if we can, extract pubKey information
    switch (prevOutType) {
      case 'multisig':
        input.pubKeys = prevOutScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer)
        break

      case 'pubkey':
        input.pubKeys = prevOutScript.chunks.slice(0, 1).map(ECPubKey.fromBuffer)
        break
    }

    if (prevOutType !== 'scripthash') {
      input.scriptType = prevOutType
    }

    input.prevOutScript = prevOutScript
    input.prevOutType = prevOutType
  }

  assert(this.inputs.every(function(input2) {
    if (input2.hashType === undefined) return true

    return input2.hashType & Transaction.SIGHASH_ANYONECANPAY
  }), 'No, this would invalidate signatures')

  var prevOut = prevOutHash.toString('hex') + ':' + index
  assert(!(prevOut in this.prevTxMap), 'Transaction is already an input')

  var vin = this.tx.addInput(prevOutHash, index, sequence)
  this.inputs[vin] = input
  this.prevTxMap[prevOut] = vin

  return vin
}

TransactionBuilder.prototype.addOutput = function(scriptPubKey, value) {
  assert(this.inputs.every(function(input) {
    if (input.hashType === undefined) return true

    return (input.hashType & 0x1f) === Transaction.SIGHASH_SINGLE
  }), 'No, this would invalidate signatures')

  return this.tx.addOutput(scriptPubKey, value)
}

TransactionBuilder.prototype.build = function() { return this.__build(false) }
TransactionBuilder.prototype.buildIncomplete = function() { return this.__build(true) }

var canSignTypes = { 'pubkeyhash': true, 'multisig': true, 'pubkey': true }

TransactionBuilder.prototype.__build = function(allowIncomplete) {
  if (!allowIncomplete) {
    assert(this.tx.ins.length > 0, 'Transaction has no inputs')
    assert(this.tx.outs.length > 0, 'Transaction has no outputs')
  }

  var tx = this.tx.clone()

  // Create script signatures from signature meta-data
  this.inputs.forEach(function(input, index) {
    var scriptType = input.scriptType
    var scriptSig

    if (!allowIncomplete) {
      assert(!!scriptType, 'Transaction is not complete')
      assert(scriptType in canSignTypes, scriptType + ' not supported')
      assert(input.signatures, 'Transaction is missing signatures')
    }

    if (input.signatures) {
      switch (scriptType) {
        case 'pubkeyhash':
          var pkhSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = scripts.pubKeyHashInput(pkhSignature, input.pubKeys[0])
          break

        case 'multisig':
          // Array.prototype.map is sparse-compatible
          var msSignatures = input.signatures.map(function(signature) {
            return signature.toScriptSignature(input.hashType)
          })

          // fill in blanks with OP_0
          for (var i = 0; i < msSignatures.length; ++i) {
            if (msSignatures[i]) continue

            msSignatures[i] = ops.OP_0
          }

          var redeemScript = allowIncomplete ? undefined : input.redeemScript
          scriptSig = scripts.multisigInput(msSignatures, redeemScript)
          break

        case 'pubkey':
          var pkSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = scripts.pubKeyInput(pkSignature)
          break
      }
    }

    // did we build a scriptSig?
    if (scriptSig) {
      // wrap as scriptHash if necessary
      if (input.prevOutType === 'scripthash') {
        scriptSig = scripts.scriptHashInput(scriptSig, input.redeemScript)
      }

      tx.setInputScript(index, scriptSig)
    }
  })

  return tx
}

TransactionBuilder.prototype.sign = function(index, privKey, redeemScript, hashType) {
  assert(index in this.inputs, 'No input at index: ' + index)
  hashType = hashType || Transaction.SIGHASH_ALL

  var input = this.inputs[index]

  if (input.hashType !== undefined) {
    assert.equal(input.hashType, hashType, 'Inconsistent hashType')
  }

  var initialized = input.prevOutScript &&
                    input.prevOutType &&
                    input.hashType &&
                    input.pubKeys &&
                    input.signatures

  // are we already initialized?
  if (initialized) {
    // redeemScript only needed to initialize, but if provided again, enforce consistency
    if (redeemScript) {
      assert.deepEqual(input.redeemScript, redeemScript, 'Inconsistent redeemScript')
    }

  // initialize it
  } else {
    if (redeemScript) {
      // if we have a prevOutScript, enforce scriptHash equality to the redeemScript
      if (input.prevOutScript) {
        assert.equal(input.prevOutType, 'scripthash', 'PrevOutScript must be P2SH')

        var scriptHash = input.prevOutScript.chunks[1]
        assert.deepEqual(scriptHash, redeemScript.getHash(), 'RedeemScript does not match ' + scriptHash.toString('hex'))
      }

      var scriptType = scripts.classifyOutput(redeemScript)
      assert(scriptType in canSignTypes, 'RedeemScript not supported (' + scriptType + ')')

      var pubKeys = []
      switch (scriptType) {
        case 'multisig':
          pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer)
          break

        case 'pubkeyhash':
          var pkh1 = redeemScript.chunks[2]
          var pkh2 = privKey.pub.getAddress().hash

          assert.deepEqual(pkh1, pkh2, 'privateKey cannot sign for this input')
          pubKeys = [privKey.pub]
          break

        case 'pubkey':
          pubKeys = redeemScript.chunks.slice(0, 1).map(ECPubKey.fromBuffer)
          break
      }

      if (!input.prevOutScript) {
        input.prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())
        input.prevOutType = 'scripthash'
      }

      input.pubKeys = pubKeys
      input.redeemScript = redeemScript
      input.scriptType = scriptType

    } else {
      assert.notEqual(input.prevOutType, 'scripthash', 'PrevOutScript is P2SH, missing redeemScript')

      // if nothing known, assume pubKeyHash
      if (!input.scriptType) {
        input.prevOutScript = privKey.pub.getAddress().toOutputScript()
        input.prevOutType = 'pubkeyhash'
        input.pubKeys = [privKey.pub]
        input.scriptType = input.prevOutType
      }
    }

    input.hashType = hashType
    input.signatures = input.signatures || []
  }

  // do we know how to sign this?
  assert(input.scriptType in canSignTypes, input.scriptType + ' not supported')

  // enforce in order signing of public keys
  assert(input.pubKeys.some(function(pubKey, i) {
    if (!privKey.pub.Q.equals(pubKey.Q)) return false

    assert(!input.signatures[i], 'Signature already exists')
    var signatureScript = input.redeemScript || input.prevOutScript
    var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)
    var signature = privKey.sign(signatureHash)
    input.signatures[i] = signature

    return true
  }, this), 'privateKey cannot sign for this input')
}

module.exports = TransactionBuilder
<commit_msg>TxBuilder: defer mutation further, but still catch non-standards<commit_after>var assert = require('assert')
var ops = require('./opcodes')
var scripts = require('./scripts')

var ECPubKey = require('./ecpubkey')
var ECSignature = require('./ecsignature')
var Script = require('./script')
var Transaction = require('./transaction')

function isCoinbase(txHash) {
  return Array.prototype.every.call(txHash, function(x) {
    return x === 0
  })
}

function extractInput(txIn) {
  var redeemScript
  var scriptSig = txIn.script
  var prevOutScript
  var prevOutType = scripts.classifyInput(scriptSig, true)
  var scriptType

  // Re-classify if scriptHash
  if (prevOutType === 'scripthash') {
    redeemScript = Script.fromBuffer(scriptSig.chunks.slice(-1)[0])
    prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())

    scriptSig = Script.fromChunks(scriptSig.chunks.slice(0, -1))
    scriptType = scripts.classifyInput(scriptSig, true)

  } else {
    scriptType = prevOutType
  }

  // Extract hashType, pubKeys and signatures
  var hashType, parsed, pubKeys, signatures

  switch (scriptType) {
    case 'pubkeyhash': {
      parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
      hashType = parsed.hashType
      pubKeys = [ECPubKey.fromBuffer(scriptSig.chunks[1])]
      signatures = [parsed.signature]
      prevOutScript = pubKeys[0].getAddress().toOutputScript()

      break
    }

    case 'pubkey': {
      parsed = ECSignature.parseScriptSignature(scriptSig.chunks[0])
      hashType = parsed.hashType
      signatures = [parsed.signature]

      if (redeemScript) {
        pubKeys = [ECPubKey.fromBuffer(redeemScript.chunks[0])]
      }

      break
    }

    case 'multisig': {
      signatures = scriptSig.chunks.slice(1).map(function(chunk) {
        if (chunk === ops.OP_0) return chunk

        var parsed = ECSignature.parseScriptSignature(chunk)
        hashType = parsed.hashType

        return parsed.signature
      })

      if (redeemScript) {
        pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer)
      }

      break
    }
  }

  return {
    hashType: hashType,
    prevOutScript: prevOutScript,
    prevOutType: prevOutType,
    pubKeys: pubKeys,
    redeemScript: redeemScript,
    scriptType: scriptType,
    signatures: signatures
  }
}

function TransactionBuilder() {
  this.prevTxMap = {}
  this.prevOutScripts = {}
  this.prevOutTypes = {}

  this.inputs = []
  this.tx = new Transaction()
}

TransactionBuilder.fromTransaction = function(transaction) {
  var txb = new TransactionBuilder()

  // Copy other transaction fields
  txb.tx.version = transaction.version
  txb.tx.locktime = transaction.locktime

  // Extract/add inputs
  transaction.ins.forEach(function(txIn) {
    txb.addInput(txIn.hash, txIn.index, txIn.sequence)
  })

  // Extract/add outputs
  transaction.outs.forEach(function(txOut) {
    txb.addOutput(txOut.script, txOut.value)
  })

  // Extract/add signatures
  txb.inputs = transaction.ins.map(function(txIn) {
    // TODO: remove me after testcase added
    assert(!isCoinbase(txIn.hash), 'coinbase inputs not supported')

    // Ignore empty scripts
    if (txIn.script.buffer.length === 0) return

    return extractInput(txIn)
  })

  return txb
}

TransactionBuilder.prototype.addInput = function(prevTx, index, sequence, prevOutScript) {
  var prevOutHash

  if (typeof prevTx === 'string') {
    prevOutHash = new Buffer(prevTx, 'hex')

    // TxId hex is big-endian, we want little-endian hash
    Array.prototype.reverse.call(prevOutHash)

  } else if (prevTx instanceof Transaction) {
    prevOutHash = prevTx.getHash()
    prevOutScript = prevTx.outs[index].script

  } else {
    prevOutHash = prevTx

  }

  var input = {}
  if (prevOutScript) {
    var prevOutType = scripts.classifyOutput(prevOutScript)

    // if we can, extract pubKey information
    switch (prevOutType) {
      case 'multisig':
        input.pubKeys = prevOutScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer)
        break

      case 'pubkey':
        input.pubKeys = prevOutScript.chunks.slice(0, 1).map(ECPubKey.fromBuffer)
        break
    }

    if (prevOutType !== 'scripthash') {
      input.scriptType = prevOutType
    }

    input.prevOutScript = prevOutScript
    input.prevOutType = prevOutType
  }

  assert(this.inputs.every(function(input2) {
    if (input2.hashType === undefined) return true

    return input2.hashType & Transaction.SIGHASH_ANYONECANPAY
  }), 'No, this would invalidate signatures')

  var prevOut = prevOutHash.toString('hex') + ':' + index
  assert(!(prevOut in this.prevTxMap), 'Transaction is already an input')

  var vin = this.tx.addInput(prevOutHash, index, sequence)
  this.inputs[vin] = input
  this.prevTxMap[prevOut] = vin

  return vin
}

TransactionBuilder.prototype.addOutput = function(scriptPubKey, value) {
  assert(this.inputs.every(function(input) {
    if (input.hashType === undefined) return true

    return (input.hashType & 0x1f) === Transaction.SIGHASH_SINGLE
  }), 'No, this would invalidate signatures')

  return this.tx.addOutput(scriptPubKey, value)
}

TransactionBuilder.prototype.build = function() { return this.__build(false) }
TransactionBuilder.prototype.buildIncomplete = function() { return this.__build(true) }

var canSignTypes = { 'pubkeyhash': true, 'multisig': true, 'pubkey': true }

TransactionBuilder.prototype.__build = function(allowIncomplete) {
  if (!allowIncomplete) {
    assert(this.tx.ins.length > 0, 'Transaction has no inputs')
    assert(this.tx.outs.length > 0, 'Transaction has no outputs')
  }

  var tx = this.tx.clone()

  // Create script signatures from signature meta-data
  this.inputs.forEach(function(input, index) {
    var scriptType = input.scriptType
    var scriptSig

    if (!allowIncomplete) {
      assert(!!scriptType, 'Transaction is not complete')
      assert(scriptType in canSignTypes, scriptType + ' not supported')
      assert(input.signatures, 'Transaction is missing signatures')
    }

    if (input.signatures) {
      switch (scriptType) {
        case 'pubkeyhash':
          var pkhSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = scripts.pubKeyHashInput(pkhSignature, input.pubKeys[0])
          break

        case 'multisig':
          // Array.prototype.map is sparse-compatible
          var msSignatures = input.signatures.map(function(signature) {
            return signature.toScriptSignature(input.hashType)
          })

          // fill in blanks with OP_0
          for (var i = 0; i < msSignatures.length; ++i) {
            if (msSignatures[i]) continue

            msSignatures[i] = ops.OP_0
          }

          var redeemScript = allowIncomplete ? undefined : input.redeemScript
          scriptSig = scripts.multisigInput(msSignatures, redeemScript)
          break

        case 'pubkey':
          var pkSignature = input.signatures[0].toScriptSignature(input.hashType)
          scriptSig = scripts.pubKeyInput(pkSignature)
          break
      }
    }

    // did we build a scriptSig?
    if (scriptSig) {
      // wrap as scriptHash if necessary
      if (input.prevOutType === 'scripthash') {
        scriptSig = scripts.scriptHashInput(scriptSig, input.redeemScript)
      }

      tx.setInputScript(index, scriptSig)
    }
  })

  return tx
}

TransactionBuilder.prototype.sign = function(index, privKey, redeemScript, hashType) {
  assert(index in this.inputs, 'No input at index: ' + index)
  hashType = hashType || Transaction.SIGHASH_ALL

  var input = this.inputs[index]

  if (input.hashType !== undefined) {
    assert.equal(input.hashType, hashType, 'Inconsistent hashType')
  }

  var initialized = input.hashType &&
                    input.prevOutScript &&
                    input.prevOutType &&
                    input.pubKeys &&
                    input.scriptType &&
                    input.signatures

  // are we already initialized?
  if (initialized) {
    // redeemScript only needed to initialize, but if provided again, enforce consistency
    if (redeemScript) {
      assert.deepEqual(input.redeemScript, redeemScript, 'Inconsistent redeemScript')
    }

  // initialize it
  } else {
    if (redeemScript) {
      // if we have a prevOutScript, enforce scriptHash equality to the redeemScript
      if (input.prevOutScript) {
        assert.equal(input.prevOutType, 'scripthash', 'PrevOutScript must be P2SH')

        var scriptHash = input.prevOutScript.chunks[1]
        assert.deepEqual(scriptHash, redeemScript.getHash(), 'RedeemScript does not match ' + scriptHash.toString('hex'))
      }

      var scriptType = scripts.classifyOutput(redeemScript)
      assert(scriptType in canSignTypes, 'RedeemScript not supported (' + scriptType + ')')

      var pubKeys = []
      switch (scriptType) {
        case 'multisig':
          pubKeys = redeemScript.chunks.slice(1, -2).map(ECPubKey.fromBuffer)
          break

        case 'pubkeyhash':
          var pkh1 = redeemScript.chunks[2]
          var pkh2 = privKey.pub.getAddress().hash

          assert.deepEqual(pkh1, pkh2, 'privateKey cannot sign for this input')
          pubKeys = [privKey.pub]
          break

        case 'pubkey':
          pubKeys = redeemScript.chunks.slice(0, 1).map(ECPubKey.fromBuffer)
          break
      }

      if (!input.prevOutScript) {
        input.prevOutScript = scripts.scriptHashOutput(redeemScript.getHash())
        input.prevOutType = 'scripthash'
      }

      input.pubKeys = pubKeys
      input.redeemScript = redeemScript
      input.scriptType = scriptType

    } else {
      assert.notEqual(input.prevOutType, 'scripthash', 'PrevOutScript is P2SH, missing redeemScript')

      // can we sign this?
      if (input.scriptType) {
        assert(input.pubKeys, input.scriptType + ' not supported')

      // we know nothin' Jon Snow, assume pubKeyHash
      } else {
        input.prevOutScript = privKey.pub.getAddress().toOutputScript()
        input.prevOutType = 'pubkeyhash'
        input.pubKeys = [privKey.pub]
        input.scriptType = input.prevOutType

      }
    }

    input.hashType = hashType
    input.signatures = input.signatures || []
  }

  // enforce in order signing of public keys
  assert(input.pubKeys.some(function(pubKey, i) {
    if (!privKey.pub.Q.equals(pubKey.Q)) return false

    assert(!input.signatures[i], 'Signature already exists')
    var signatureScript = input.redeemScript || input.prevOutScript
    var signatureHash = this.tx.hashForSignature(index, signatureScript, hashType)
    var signature = privKey.sign(signatureHash)
    input.signatures[i] = signature

    return true
  }, this), 'privateKey cannot sign for this input')
}

module.exports = TransactionBuilder
<|endoftext|>


<commit_before>/*
 * typeahead.js
 * https://github.com/twitter/typeahead.js
 * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
 */

var Typeahead = (function() {
  var attrsKey = 'ttAttrs';

  // constructor
  // -----------

  // THOUGHT: what if datasets could dynamically be added/removed?
  function Typeahead(o) {
    var $menu, $input, $hint, datasets;

    o = o || {};

    if (!o.input) {
      $.error('missing input');
    }

    this.autoselect = !!o.autoselect;
    this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
    this.$node = buildDomStructure(o.input, o.withHint);

    $menu = this.$node.find('.tt-dropdown-menu');
    $input = this.$node.find('.tt-input');
    $hint = this.$node.find('.tt-hint');

    this.eventBus = o.eventBus || new EventBus({ el: $input });

    this.dropdown = new Dropdown({ menu: $menu, datasets: o.datasets })
    .onSync('suggestionClicked', this._onSuggestionClicked, this)
    .onSync('cursorMoved', this._onCursorMoved, this)
    .onSync('cursorRemoved', this._onCursorRemoved, this)
    .onSync('opened', this._onOpened, this)
    .onSync('closed', this._onClosed, this)
    .onAsync('datasetRendered', this._onDatasetRendered, this);

    this.input = new Input({ input: $input, hint: $hint })
    .onSync('focused', this._onFocused, this)
    .onSync('blurred', this._onBlurred, this)
    .onSync('enterKeyed', this._onEnterKeyed, this)
    .onSync('tabKeyed', this._onTabKeyed, this)
    .onSync('escKeyed', this._onEscKeyed, this)
    .onSync('upKeyed', this._onUpKeyed, this)
    .onSync('downKeyed', this._onDownKeyed, this)
    .onSync('leftKeyed', this._onLeftKeyed, this)
    .onSync('rightKeyed', this._onRightKeyed, this)
    .onSync('queryChanged', this._onQueryChanged, this)
    .onSync('whitespaceChanged', this._onWhitespaceChanged, this);

    // HACK: prevents input blur on menu click
    // https://github.com/twitter/typeahead.js/pull/351
    $menu.on('mousedown.tt', function($e) {
      if (_.isMsie() && _.isMsie() < 9) {
        $input[0].onbeforedeactivate = function() {
          window.event.returnValue = false;
          $input[0].onbeforedeactivate = null;
        };
      }

      // ie 9+ and other browsers
      $e.preventDefault();
    });
  }

  // instance methods
  // ----------------

  _.mixin(Typeahead.prototype, {

    // ### private

    _onSuggestionClicked: function onSuggestionClicked(type, $el) {
      var datum;

      if (datum = this.dropdown.getDatumForSuggestion($el)) {
        this._select(datum);
      }
    },

    _onCursorMoved: function onCursorMoved() {
      var datum = this.dropdown.getDatumForCursor();

      this.input.clearHint();
      this.input.setInputValue(datum.value, true);

      this.eventBus.trigger('cursorchanged', datum.raw, datum.datasetName);
    },

    _onCursorRemoved: function onCursorRemoved() {
      this.input.resetInputValue();
      this._updateHint();
    },

    _onDatasetRendered: function onDatasetRendered() {
      this._updateHint();
    },

    _onOpened: function onOpened() {
      this._updateHint();

      this.eventBus.trigger('opened');
    },

    _onClosed: function onClosed() {
      this.input.clearHint();

      this.eventBus.trigger('closed');
    },

    _onFocused: function onFocused() {
      this.dropdown.open();
    },

    _onBlurred: function onBlurred() {
      this.dropdown.close();
    },

    _onEnterKeyed: function onEnterKeyed(type, $e) {
      var cursorDatum, topSuggestionDatum;

      cursorDatum = this.dropdown.getDatumForCursor();
      topSuggestionDatum = this.dropdown.getDatumForTopSuggestion();

      if (cursorDatum) {
        this._select(cursorDatum);
        $e.preventDefault();
      }

      else if (this.autoselect && topSuggestionDatum) {
        this._select(topSuggestionDatum);
        $e.preventDefault();
      }
    },

    _onTabKeyed: function onTabKeyed(type, $e) {
      var datum;

      if (datum = this.dropdown.getDatumForCursor()) {
        this._select(datum);
        $e.preventDefault();
      }

      else {
        this._autocomplete();
      }
    },

    _onEscKeyed: function onEscKeyed() {
      this.dropdown.close();
      this.input.resetInputValue();
    },

    _onUpKeyed: function onUpKeyed() {
      var query = this.input.getQuery();

      if(!this.dropdown.isOpen && query.length >= this.minLength) {
        this.dropdown.update(query);
      }

      this.dropdown.open();
      this.dropdown.moveCursorUp();
    },

    _onDownKeyed: function onDownKeyed() {
      var query = this.input.getQuery();

      if( !this.dropdown.isOpen && query.length >= this.minLength) {
        this.dropdown.update(query);
      }

      this.dropdown.open();
      this.dropdown.moveCursorDown();
    },

    _onLeftKeyed: function onLeftKeyed() {
      this.dir === 'rtl' && this._autocomplete();
    },

    _onRightKeyed: function onRightKeyed() {
      this.dir === 'ltr' && this._autocomplete();
    },

    _onQueryChanged: function onQueryChanged(e, query) {
      this.input.clearHint();
      this.dropdown.empty();
      query.length >= this.minLength && this.dropdown.update(query);
      this.dropdown.open();
      this._setLanguageDirection();
    },

    _onWhitespaceChanged: function onWhitespaceChanged() {
      this._updateHint();
      this.dropdown.open();
    },

    _setLanguageDirection: function setLanguageDirection() {
      var dir;

      if (this.dir !== (dir = this.input.getLanguageDirection())) {
        this.dir = dir;
        this.$node.css('direction', dir);
        this.dropdown.setLanguageDirection(dir);
      }
    },

    _updateHint: function updateHint() {
      var datum, inputValue, query, escapedQuery, frontMatchRegEx, match;

      datum = this.dropdown.getDatumForTopSuggestion();

      if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) {
        inputValue = this.input.getInputValue();
        query = Input.normalizeQuery(inputValue);
        escapedQuery = _.escapeRegExChars(query);

        frontMatchRegEx = new RegExp('^(?:' + escapedQuery + ')(.*$)', 'i');
        match = frontMatchRegEx.exec(datum.value);

        this.input.setHintValue(inputValue + (match ? match[1] : ''));
      }
    },

    _autocomplete: function autocomplete() {
      var hint, query, datum;

      hint = this.input.getHintValue();
      query = this.input.getQuery();

      if (hint && query !== hint && this.input.isCursorAtEnd()) {
        datum = this.dropdown.getDatumForTopSuggestion();
        datum && this.input.setInputValue(datum.value);

        this.eventBus.trigger('autocompleted', datum.raw, datum.datasetName);
      }
    },

    _select: function select(datum) {
      this.input.clearHint();
      this.input.setQuery(datum.value);
      this.input.setInputValue(datum.value, true);
      this.dropdown.empty();

      this._setLanguageDirection();

      // in ie, focus is not a synchronous event, so when a selection
      // is triggered by a click within the dropdown menu, we need to
      // defer the closing of the dropdown otherwise it'll stay open
      _.defer(_.bind(this.dropdown.close, this.dropdown));

      this.eventBus.trigger('selected', datum.raw, datum.datasetName);
    },

    // ### public

    open: function open() {
      this.dropdown.open();
    },

    close: function close() {
      this.dropdown.close();
    },

    getQuery: function getQuery() {
      return this.input.getQuery();
    },

    setQuery: function setQuery(val) {
      this.input.setInputValue(val);
    },

    destroy: function destroy() {
      this.input.destroy();
      this.dropdown.destroy();

      destroyDomStructure(this.$node);

      this.$node = null;
    }
  });

  return Typeahead;

  function buildDomStructure(input, withHint) {
    var $input, $wrapper, $dropdown, $hint;

    $input = $(input);
    $wrapper = $(html.wrapper).css(css.wrapper);
    $dropdown = $(html.dropdown).css(css.dropdown);
    $hint = $input.clone().css(css.hint).css(getBackgroundStyles($input));

    $hint
    .removeData()
    .addClass('tt-hint')
    .removeAttr('id name placeholder')
    .prop('disabled', true)
    .attr({ autocomplete: 'off', spellcheck: 'false' });

    // store the original values of the attrs that get modified
    // so modifications can be reverted on destroy
    $input.data(attrsKey, {
      dir: $input.attr('dir'),
      autocomplete: $input.attr('autocomplete'),
      spellcheck: $input.attr('spellcheck'),
      style: $input.attr('style')
    });

    $input
    .addClass('tt-input')
    .attr({ autocomplete: 'off', spellcheck: false })
    .css(withHint ? css.input : css.inputWithNoHint);

    // ie7 does not like it when dir is set to auto
    try { !$input.attr('dir') && $input.attr('dir', 'auto'); } catch (e) {}

    return $input
    .wrap($wrapper)
    .parent()
    .prepend(withHint ? $hint : null)
    .append($dropdown);
  }

  function getBackgroundStyles($el) {
    return {
      backgroundAttachment: $el.css('background-attachment'),
      backgroundClip: $el.css('background-clip'),
      backgroundColor: $el.css('background-color'),
      backgroundImage: $el.css('background-image'),
      backgroundOrigin: $el.css('background-origin'),
      backgroundPosition: $el.css('background-position'),
      backgroundRepeat: $el.css('background-repeat'),
      backgroundSize: $el.css('background-size')
    };
  }

  function destroyDomStructure($node) {
    var $input = $node.find('.tt-input');

    // need to remove attrs that weren't previously defined and
    // revert attrs that originally had a value
    _.each($input.data(attrsKey), function(val, key) {
      _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
    });

    $input
    .detach()
    .removeData(attrsKey)
    .removeClass('tt-input')
    .insertAfter($node);

    $node.remove();
  }
})();
<commit_msg>Allow event delegation of events on .tt-suggestion. Fixes #118.<commit_after>/*
 * typeahead.js
 * https://github.com/twitter/typeahead.js
 * Copyright 2013 Twitter, Inc. and other contributors; Licensed MIT
 */

var Typeahead = (function() {
  var attrsKey = 'ttAttrs';

  // constructor
  // -----------

  // THOUGHT: what if datasets could dynamically be added/removed?
  function Typeahead(o) {
    var $menu, $input, $hint, datasets;

    o = o || {};

    if (!o.input) {
      $.error('missing input');
    }

    this.autoselect = !!o.autoselect;
    this.minLength = _.isNumber(o.minLength) ? o.minLength : 1;
    this.$node = buildDomStructure(o.input, o.withHint);

    $menu = this.$node.find('.tt-dropdown-menu');
    $input = this.$node.find('.tt-input');
    $hint = this.$node.find('.tt-hint');

    this.eventBus = o.eventBus || new EventBus({ el: $input });

    this.dropdown = new Dropdown({ menu: $menu, datasets: o.datasets })
    .onSync('suggestionClicked', this._onSuggestionClicked, this)
    .onSync('cursorMoved', this._onCursorMoved, this)
    .onSync('cursorRemoved', this._onCursorRemoved, this)
    .onSync('opened', this._onOpened, this)
    .onSync('closed', this._onClosed, this)
    .onAsync('datasetRendered', this._onDatasetRendered, this);

    this.input = new Input({ input: $input, hint: $hint })
    .onSync('focused', this._onFocused, this)
    .onSync('blurred', this._onBlurred, this)
    .onSync('enterKeyed', this._onEnterKeyed, this)
    .onSync('tabKeyed', this._onTabKeyed, this)
    .onSync('escKeyed', this._onEscKeyed, this)
    .onSync('upKeyed', this._onUpKeyed, this)
    .onSync('downKeyed', this._onDownKeyed, this)
    .onSync('leftKeyed', this._onLeftKeyed, this)
    .onSync('rightKeyed', this._onRightKeyed, this)
    .onSync('queryChanged', this._onQueryChanged, this)
    .onSync('whitespaceChanged', this._onWhitespaceChanged, this);

    // #351: prevents input blur on menu click
    $menu.on('mousedown.tt', function($e) {
      if (_.isMsie() && _.isMsie() < 9) {
        $input[0].onbeforedeactivate = function() {
          window.event.returnValue = false;
          $input[0].onbeforedeactivate = null;
        };
      }

      // ie 9+ and other browsers
      $e.preventDefault();
    });
  }

  // instance methods
  // ----------------

  _.mixin(Typeahead.prototype, {

    // ### private

    _onSuggestionClicked: function onSuggestionClicked(type, $el) {
      var datum;

      if (datum = this.dropdown.getDatumForSuggestion($el)) {
        this._select(datum);
      }
    },

    _onCursorMoved: function onCursorMoved() {
      var datum = this.dropdown.getDatumForCursor();

      this.input.clearHint();
      this.input.setInputValue(datum.value, true);

      this.eventBus.trigger('cursorchanged', datum.raw, datum.datasetName);
    },

    _onCursorRemoved: function onCursorRemoved() {
      this.input.resetInputValue();
      this._updateHint();
    },

    _onDatasetRendered: function onDatasetRendered() {
      this._updateHint();
    },

    _onOpened: function onOpened() {
      this._updateHint();

      this.eventBus.trigger('opened');
    },

    _onClosed: function onClosed() {
      this.input.clearHint();

      this.eventBus.trigger('closed');
    },

    _onFocused: function onFocused() {
      this.dropdown.open();
    },

    _onBlurred: function onBlurred() {
      this.dropdown.close();
    },

    _onEnterKeyed: function onEnterKeyed(type, $e) {
      var cursorDatum, topSuggestionDatum;

      cursorDatum = this.dropdown.getDatumForCursor();
      topSuggestionDatum = this.dropdown.getDatumForTopSuggestion();

      if (cursorDatum) {
        this._select(cursorDatum);
        $e.preventDefault();
      }

      else if (this.autoselect && topSuggestionDatum) {
        this._select(topSuggestionDatum);
        $e.preventDefault();
      }
    },

    _onTabKeyed: function onTabKeyed(type, $e) {
      var datum;

      if (datum = this.dropdown.getDatumForCursor()) {
        this._select(datum);
        $e.preventDefault();
      }

      else {
        this._autocomplete();
      }
    },

    _onEscKeyed: function onEscKeyed() {
      this.dropdown.close();
      this.input.resetInputValue();
    },

    _onUpKeyed: function onUpKeyed() {
      var query = this.input.getQuery();

      if(!this.dropdown.isOpen && query.length >= this.minLength) {
        this.dropdown.update(query);
      }

      this.dropdown.open();
      this.dropdown.moveCursorUp();
    },

    _onDownKeyed: function onDownKeyed() {
      var query = this.input.getQuery();

      if( !this.dropdown.isOpen && query.length >= this.minLength) {
        this.dropdown.update(query);
      }

      this.dropdown.open();
      this.dropdown.moveCursorDown();
    },

    _onLeftKeyed: function onLeftKeyed() {
      this.dir === 'rtl' && this._autocomplete();
    },

    _onRightKeyed: function onRightKeyed() {
      this.dir === 'ltr' && this._autocomplete();
    },

    _onQueryChanged: function onQueryChanged(e, query) {
      this.input.clearHint();
      this.dropdown.empty();
      query.length >= this.minLength && this.dropdown.update(query);
      this.dropdown.open();
      this._setLanguageDirection();
    },

    _onWhitespaceChanged: function onWhitespaceChanged() {
      this._updateHint();
      this.dropdown.open();
    },

    _setLanguageDirection: function setLanguageDirection() {
      var dir;

      if (this.dir !== (dir = this.input.getLanguageDirection())) {
        this.dir = dir;
        this.$node.css('direction', dir);
        this.dropdown.setLanguageDirection(dir);
      }
    },

    _updateHint: function updateHint() {
      var datum, inputValue, query, escapedQuery, frontMatchRegEx, match;

      datum = this.dropdown.getDatumForTopSuggestion();

      if (datum && this.dropdown.isVisible() && !this.input.hasOverflow()) {
        inputValue = this.input.getInputValue();
        query = Input.normalizeQuery(inputValue);
        escapedQuery = _.escapeRegExChars(query);

        frontMatchRegEx = new RegExp('^(?:' + escapedQuery + ')(.*$)', 'i');
        match = frontMatchRegEx.exec(datum.value);

        this.input.setHintValue(inputValue + (match ? match[1] : ''));
      }
    },

    _autocomplete: function autocomplete() {
      var hint, query, datum;

      hint = this.input.getHintValue();
      query = this.input.getQuery();

      if (hint && query !== hint && this.input.isCursorAtEnd()) {
        datum = this.dropdown.getDatumForTopSuggestion();
        datum && this.input.setInputValue(datum.value);

        this.eventBus.trigger('autocompleted', datum.raw, datum.datasetName);
      }
    },

    _select: function select(datum) {
      this.input.clearHint();
      this.input.setQuery(datum.value);
      this.input.setInputValue(datum.value, true);
      this.dropdown.close();

      this._setLanguageDirection();

      // #118: allow click event to bubble up to the body before removing
      // the suggestions otherwise we break event delegation
      _.defer(_.bind(this.dropdown.empty, this.dropdown));

      this.eventBus.trigger('selected', datum.raw, datum.datasetName);
    },

    // ### public

    open: function open() {
      this.dropdown.open();
    },

    close: function close() {
      this.dropdown.close();
    },

    getQuery: function getQuery() {
      return this.input.getQuery();
    },

    setQuery: function setQuery(val) {
      this.input.setInputValue(val);
    },

    destroy: function destroy() {
      this.input.destroy();
      this.dropdown.destroy();

      destroyDomStructure(this.$node);

      this.$node = null;
    }
  });

  return Typeahead;

  function buildDomStructure(input, withHint) {
    var $input, $wrapper, $dropdown, $hint;

    $input = $(input);
    $wrapper = $(html.wrapper).css(css.wrapper);
    $dropdown = $(html.dropdown).css(css.dropdown);
    $hint = $input.clone().css(css.hint).css(getBackgroundStyles($input));

    $hint
    .removeData()
    .addClass('tt-hint')
    .removeAttr('id name placeholder')
    .prop('disabled', true)
    .attr({ autocomplete: 'off', spellcheck: 'false' });

    // store the original values of the attrs that get modified
    // so modifications can be reverted on destroy
    $input.data(attrsKey, {
      dir: $input.attr('dir'),
      autocomplete: $input.attr('autocomplete'),
      spellcheck: $input.attr('spellcheck'),
      style: $input.attr('style')
    });

    $input
    .addClass('tt-input')
    .attr({ autocomplete: 'off', spellcheck: false })
    .css(withHint ? css.input : css.inputWithNoHint);

    // ie7 does not like it when dir is set to auto
    try { !$input.attr('dir') && $input.attr('dir', 'auto'); } catch (e) {}

    return $input
    .wrap($wrapper)
    .parent()
    .prepend(withHint ? $hint : null)
    .append($dropdown);
  }

  function getBackgroundStyles($el) {
    return {
      backgroundAttachment: $el.css('background-attachment'),
      backgroundClip: $el.css('background-clip'),
      backgroundColor: $el.css('background-color'),
      backgroundImage: $el.css('background-image'),
      backgroundOrigin: $el.css('background-origin'),
      backgroundPosition: $el.css('background-position'),
      backgroundRepeat: $el.css('background-repeat'),
      backgroundSize: $el.css('background-size')
    };
  }

  function destroyDomStructure($node) {
    var $input = $node.find('.tt-input');

    // need to remove attrs that weren't previously defined and
    // revert attrs that originally had a value
    _.each($input.data(attrsKey), function(val, key) {
      _.isUndefined(val) ? $input.removeAttr(key) : $input.attr(key, val);
    });

    $input
    .detach()
    .removeData(attrsKey)
    .removeClass('tt-input')
    .insertAfter($node);

    $node.remove();
  }
})();
<|endoftext|>


<commit_before>/* eslint-env jest */

import niceTime from './nice-time'

describe('niceTime', () => {
    test('should return now for timeperiod of less than 90 seconds', () => {
        const date = new Date()
        expect(niceTime(date)).toBe('now')
    })

    test('should return minutes for timeperiod of less than 600 seconds', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 7, 2, 14, 45)
        expect(niceTime(date, { now })).toBe('20 minutes ago')
    })

    test('should return timeperiod stamp for timeperiod less than 24 hours', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 7, 2, 18, 55)
        expect(niceTime(date, { now })).toBe('14:25')
    })

    test('should return the timeperiod stamp and the day for timeperiod less than 24 hours but not on the same day', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 7, 3, 10, 55)
        expect(niceTime(date, { now })).toBe('Yesterday 14:25')
    })

    test('should return the day and timestamp for timeperiod less than 3 days', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 7, 4, 18, 55)
        expect(niceTime(date, { now })).toBe('Tue 14:25')
    })

    test('should return the date, the month and the time for timeperiod in the same year', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 9, 2, 18, 55)
        expect(niceTime(date, { now })).toBe('2 Aug 14:25')
    })

    test('should return the date, month, year and the time for timeperiod not in the same year', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2017, 7, 2, 18, 55)
        expect(niceTime(date, { now })).toBe('2 Aug 2016 14:25')
    })

    test('should return the placeholder for invalid date', () => {
        const date = new Date(2016, 7, 3, 14, 25)
        const now = new Date(2016, 7, 2, 18, 55)
        expect(niceTime(date, { now })).toBe('soon?!')
    })
})
<commit_msg>fixed failed test<commit_after>/* eslint-env jest */

import niceTime from './nice-time'

describe('niceTime', () => {
    test('should return now for timeperiod of less than 90 seconds', () => {
        const date = new Date()
        expect(niceTime(date)).toBe('now')
    })

    test('should return minutes for timeperiod of less than 600 seconds', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 7, 2, 14, 45)
        expect(niceTime(date, { now })).toBe('20 minutes ago')
    })

    test('should return timeperiod stamp for timeperiod less than 24 hours', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 7, 2, 18, 55)
        expect(niceTime(date, { now })).toBe('Today 14:25')
    })

    test('should return the timeperiod stamp and the day for timeperiod less than 24 hours but not on the same day', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 7, 3, 10, 55)
        expect(niceTime(date, { now })).toBe('Yesterday 14:25')
    })

    test('should return the day and timestamp for timeperiod less than 3 days', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 7, 4, 18, 55)
        expect(niceTime(date, { now })).toBe('Tue 14:25')
    })

    test('should return the date, the month and the time for timeperiod in the same year', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2016, 9, 2, 18, 55)
        expect(niceTime(date, { now })).toBe('2 Aug 14:25')
    })

    test('should return the date, month, year and the time for timeperiod not in the same year', () => {
        const date = new Date(2016, 7, 2, 14, 25)
        const now = new Date(2017, 7, 2, 18, 55)
        expect(niceTime(date, { now })).toBe('2 Aug 2016 14:25')
    })

    test('should return the placeholder for invalid date', () => {
        const date = new Date(2016, 7, 3, 14, 25)
        const now = new Date(2016, 7, 2, 18, 55)
        expect(niceTime(date, { now })).toBe('soon?!')
    })
})
<|endoftext|>