<commit_before>/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */
// Leave the above lines for propper jshinting

/**
 * SparkFun Inventor's Kit for Edison
 * Experiment 10 - Part 1: Keyboard
 * This sketch was written by SparkFun Electronics
 * November 18, 2015
 * https://github.com/sparkfun
 *
 * Capture keystrokes from a USB-connected keyboard and display them on a
 * character LCD.
 *
 * Released under the MIT License(http://opensource.org/licenses/MIT)
 */

// Import the filesystem module and our keymap table
var fs = require('fs');
var keymap = require('./libs/keymap.js');

// We'll also need johny-five and its Edison wrapper
var five = require('johnny-five');
var Edison = require('edison-io');
var board = new five.Board({
    io: new Edison()
});

// Global variables
var lcd;
var cursorPos;

// Johnny Five initialization
board.on('ready', function() {
    // Create our LCD object and define the pins
    // LCD pin name:    RS  EN DB4 DB5 DB6 DB7
    // Edison GP:       44  45  46  47  48  49
    lcd = new five.LCD({
        pins: ["GP44", "GP45", "GP46", "GP47", "GP48", "GP49"],
        backlight: 6,
        rows: 2,
        cols: 16
    });

    // Reset LCD
    lcd.off();
    lcd.on();

    // Show a cursor and set initial position
    lcd.clear();
    lcd.home();
    lcd.blink();
    cursorPos = 0;
});

// Create a stream that emits events on every key stroke
var readableStream = fs.createReadStream('/dev/input/event2');
console.log("Start typing!");

// Callback for a key event
readableStream.on('data', function(buf) {

    // Check for key down event and determine key pressed
    if ((buf[24] == 1) && (buf[28] == 1)) {
        var keyCode = ((buf[27] & 0xff) << 8) | (buf[26] & 0xff);
        var keyChar = keymap.keys[keyCode];

        // Make the character appear on the LCD
        if (lcd !== undefined) {

            // If it is a backspace, delete the previous character
            if (keyChar === 'bksp') {
                cursorPos--;
                if (cursorPos <= 0) {
                    cursorPos = 0;
                }
                lcd.print(" ");
                lcd.cursor(
                    Math.floor(cursorPos / lcd.cols),
                    (cursorPos % lcd.cols)
                );
                lcd.print(" ");
                lcd.cursor(
                    Math.floor(cursorPos / lcd.cols),
                    (cursorPos % lcd.cols)
                );

            // If it is a return character, clear the LCD
            } else if (keyChar == 'enter') {
                lcd.clear();
                cursorPos = 0;

            // Otherwise, print the character to the LCD
            } else if ((keyChar !== null) && (keyChar !== undefined)) {
                lcd.print(keyChar);
                cursorPos++;
            }

            // Stop the cursor at the end of the LCD
            if (cursorPos >= (lcd.rows * lcd.cols)) {
                cursorPos = (lcd.rows * lcd.cols) - 1;
            }

            // Update the cursor position (wrap to second line if needed)
            lcd.cursor(
                Math.floor(cursorPos / lcd.cols),
                (cursorPos % lcd.cols)
            );
        }
    }
});<commit_msg>Fixed LCD issues by moving removing pins 48 and 49 - use 14 and 15 instead.<commit_after>/*jslint node:true, vars:true, bitwise:true, unparam:true */
/*jshint unused:true */
// Leave the above lines for propper jshinting

/**
 * SparkFun Inventor's Kit for Edison
 * Experiment 10 - Part 1: Keyboard
 * This sketch was written by SparkFun Electronics
 * November 18, 2015
 * https://github.com/sparkfun
 *
 * Capture keystrokes from a USB-connected keyboard and display them on a
 * character LCD.
 *
 * Released under the MIT License(http://opensource.org/licenses/MIT)
 */

// Import the filesystem module and our keymap table
var fs = require('fs');
var keymap = require('./libs/keymap.js');

// We'll also need johny-five and its Edison wrapper
var five = require('johnny-five');
var Edison = require('edison-io');
var board = new five.Board({
    io: new Edison()
});

// Global variables
var lcd;
var cursorPos;

// Johnny Five initialization
board.on('ready', function() {

    // Create our LCD object and define the pins
    // LCD pin name:    RS  EN DB4 DB5 DB6 DB7
    // Edison GPIO:     14  15  44  45  46  47
    lcd = new five.LCD({
        pins: ["GP14", "GP15", "GP44", "GP45", "GP46", "GP47"],
        backlight: 6,
        rows: 2,
        cols: 16
    });

    // Turn on LCD, clear it, and set cursor to home
    lcd.on();
    lcd.clear();
    lcd.home();
    lcd.blink();
    cursorPos = 0;
    console.log("Start typing!");
});

// Create a stream that emits events on every key stroke
var readableStream = fs.createReadStream('/dev/input/event2');

// Callback for a key event
readableStream.on('data', function(buf) {

    // Check for key down event and determine key pressed
    if ((buf[24] == 1) && (buf[28] == 1)) {
        var keyCode = ((buf[27] & 0xff) << 8) | (buf[26] & 0xff);
        var keyChar = keymap.keys[keyCode];

        // Make the character appear on the LCD
        if (lcd !== undefined) {

            // If it is a backspace, delete the previous character
            if (keyChar === 'bksp') {
                cursorPos--;
                if (cursorPos <= 0) {
                    cursorPos = 0;
                }
                lcd.print(" ");
                lcd.cursor(
                    Math.floor(cursorPos / lcd.cols),
                    (cursorPos % lcd.cols)
                );
                lcd.print(" ");
                lcd.cursor(
                    Math.floor(cursorPos / lcd.cols),
                    (cursorPos % lcd.cols)
                );

            // If it is a return character, clear the LCD
            } else if (keyChar == 'enter') {
                lcd.clear();
                cursorPos = 0;

            // Otherwise, print the character to the LCD
            } else if ((keyChar !== null) && (keyChar !== undefined)) {
                lcd.print(keyChar);
                cursorPos++;
            }

            // Stop the cursor at the end of the LCD
            if (cursorPos >= (lcd.rows * lcd.cols)) {
                cursorPos = (lcd.rows * lcd.cols) - 1;
            }

            // Update the cursor position (wrap to second line if needed)
            lcd.cursor(
                Math.floor(cursorPos / lcd.cols),
                (cursorPos % lcd.cols)
            );
        }
    }
});<|endoftext|>


<commit_before>var alias = (function(){
  'use strict';
  var alias = {};
  var aliases = {
    "xn--dk8hms.ws": "🎉💯.ws"
  };

  alias.getAlias = function (host) {
    return aliases.host ? aliases.host : host;
  };

  return alias;
}(window));
<commit_msg>bugfixes<commit_after>var alias = (function(){
  'use strict';
  var alias = {};
  var aliases = [{
    host: "xn--dk8hms.ws",
    alias: "🎉💯.ws"
  }];

  alias.getAlias = function (host) {
    var count;
    for (count = 0; count < aliases.length; count++) {
      if (aliases[count].host === host) {
        return aliases[count].alias;
      }
    }
    return host;
  };

  return alias;
}(window));
<|endoftext|>


<commit_before>import React, { Component } from "react";
import { Picker } from "native-base";
import { connect } from "react-redux";

import {
	addComponent,
	removeComponent,
	watchComponent,
	updateQuery,
	setQueryOptions
} from "@appbaseio/reactivecore/lib/actions";
import { isEqual, checkValueChange } from "@appbaseio/reactivecore/lib/utils/helper";

import types from "@appbaseio/reactivecore/lib/utils/types";

const Item = Picker.Item;

class SingleDropdownRange extends Component {
	constructor(props) {
		super(props);

		this.state = {
			currentValue: null
		};
		this.type = "range";
	}

	componentDidMount() {
		this.props.addComponent(this.props.componentId);
		this.setReact(this.props);
		if (this.props.defaultSelected) {
			this.setValue(this.props.defaultSelected, true);
		}
	}

	componentWillReceiveProps(nextProps) {
		if (!isEqual(nextProps.react, this.props.react)) {
			this.setReact(nextProps);
		}

		if (this.props.defaultSelected !== nextProps.defaultSelected) {
			this.setValue(nextProps.defaultSelected, true);
		}
	}

	componentWillUnmount() {
		this.props.removeComponent(this.props.componentId);
	}

	setReact(props) {
		if (props.react) {
			props.watchComponent(props.componentId, props.react);
		}
	}

	defaultQuery = (value) => {
		if (value) {
			return {
				range: {
					[this.props.dataField]: {
						gte: value.start,
						lte: value.end,
						boost: 2.0
					}
				}
			};
		}
		return null;
	}

	setValue = (value, isDefaultValue = false) => {
		const currentValue = isDefaultValue ?
			this.props.data.find(item => item.label === value) :
			value;
		const performUpdate = () => {
			this.setState({
				currentValue
			});
			this.updateQuery(currentValue);
		}
		checkValueChange(
			this.props.componentId,
			currentValue,
			this.props.beforeValueChange,
			this.props.onValueChange,
			performUpdate
		);
	}

	updateQuery = (value) => {
		const query = this.props.customQuery || this.defaultQuery;
		let callback = null;
		if (this.props.onQueryChange) {
			callback = this.props.onQueryChange;
		}
		this.props.updateQuery(this.props.componentId, query(value), callback);
	}

	render() {
		return (
			<Picker
				iosHeader="Select one"
				mode="dropdown"
				placeholder={this.props.placeholder}
				selectedValue={this.state.currentValue}
				onValueChange={(item) => this.setValue(item)}
			>
				{
					this.props.data.map(item => (
						<Picker.Item key={item.label} label={item.label} value={item} />
					))
				}
			</Picker>
		);
	}
}

SingleDropdownRange.propTypes = {
	addComponent: types.addComponent,
	componentId: types.componentId,
	defaultSelected: types.string,
	react: types.react,
	removeComponent: types.removeComponent,
	dataField: types.dataField,
	data: types.data,
	beforeValueChange: types.beforeValueChange,
	onValueChange: types.onValueChange,
	customQuery: types.customQuery,
	onQueryChange: types.onQueryChange,
	updateQuery: types.updateQuery,
	placeholder: types.placeholder
}

SingleDropdownRange.defaultProps = {
	placeholder: "Select a value"
}

const mapDispatchtoProps = dispatch => ({
	addComponent: component => dispatch(addComponent(component)),
	removeComponent: component => dispatch(removeComponent(component)),
	watchComponent: (component, react) => dispatch(watchComponent(component, react)),
	updateQuery: (component, query, customQuery) => dispatch(updateQuery(component, query, customQuery))
});

export default connect(null, mapDispatchtoProps)(SingleDropdownRange);
<commit_msg>Add dynamic props for SingleDropdownRange #13<commit_after>import React, { Component } from "react";
import { Picker } from "native-base";
import { connect } from "react-redux";

import {
	addComponent,
	removeComponent,
	watchComponent,
	updateQuery,
	setQueryOptions
} from "@appbaseio/reactivecore/lib/actions";
import { isEqual, checkValueChange, checkPropChange } from "@appbaseio/reactivecore/lib/utils/helper";

import types from "@appbaseio/reactivecore/lib/utils/types";

const Item = Picker.Item;

class SingleDropdownRange extends Component {
	constructor(props) {
		super(props);

		this.state = {
			currentValue: null
		};
		this.type = "range";
	}

	componentDidMount() {
		this.props.addComponent(this.props.componentId);
		this.setReact(this.props);
		if (this.props.defaultSelected) {
			this.setValue(this.props.defaultSelected, true);
		}
	}

	componentWillReceiveProps(nextProps) {
		checkPropChange(
			this.props.react,
			nextProps.react,
			() => this.setReact(nextProps)
		);
		checkPropChange(
			this.props.defaultSelected,
			nextProps.defaultSelected,
			() => this.setValue(nextProps.defaultSelected, true, nextProps)
		);
	}

	componentWillUnmount() {
		this.props.removeComponent(this.props.componentId);
	}

	setReact(props) {
		if (props.react) {
			props.watchComponent(props.componentId, props.react);
		}
	}

	defaultQuery = (value, props) => {
		if (value) {
			return {
				range: {
					[props.dataField]: {
						gte: value.start,
						lte: value.end,
						boost: 2.0
					}
				}
			};
		}
		return null;
	}

	setValue = (value, isDefaultValue = false, props = this.props) => {
		const currentValue = isDefaultValue ?
			props.data.find(item => item.label === value) :
			value;
		const performUpdate = () => {
			this.setState({
				currentValue
			});
			this.updateQuery(currentValue, props);
		}
		checkValueChange(
			props.componentId,
			currentValue,
			props.beforeValueChange,
			props.onValueChange,
			performUpdate
		);
	}

	updateQuery = (value, props) => {
		const query = props.customQuery || this.defaultQuery;
		let callback = null;
		if (props.onQueryChange) {
			callback = props.onQueryChange;
		}
		props.updateQuery(props.componentId, query(value, props), value, props.filterLabel, callback);
	}

	render() {
		return (
			<Picker
				iosHeader="Select one"
				mode="dropdown"
				placeholder={this.props.placeholder}
				selectedValue={this.state.currentValue}
				onValueChange={(item) => this.setValue(item)}
			>
				{
					this.props.data.map(item => (
						<Picker.Item key={item.label} label={item.label} value={item} />
					))
				}
			</Picker>
		);
	}
}

SingleDropdownRange.propTypes = {
	addComponent: types.addComponent,
	componentId: types.componentId,
	defaultSelected: types.string,
	react: types.react,
	removeComponent: types.removeComponent,
	dataField: types.dataField,
	data: types.data,
	beforeValueChange: types.beforeValueChange,
	onValueChange: types.onValueChange,
	customQuery: types.customQuery,
	onQueryChange: types.onQueryChange,
	updateQuery: types.updateQuery,
	placeholder: types.placeholder
}

SingleDropdownRange.defaultProps = {
	placeholder: "Select a value"
}

const mapDispatchtoProps = dispatch => ({
	addComponent: component => dispatch(addComponent(component)),
	removeComponent: component => dispatch(removeComponent(component)),
	watchComponent: (component, react) => dispatch(watchComponent(component, react)),
	updateQuery: (component, query, value, filterLabel, customQuery) => dispatch(
		updateQuery(component, query, value, filterLabel, customQuery)
	)
});

export default connect(null, mapDispatchtoProps)(SingleDropdownRange);
<|endoftext|>


<commit_before>'use strict';

var Substance = require('substance');
var $$ = React.createElement;
var Surface = Substance.Surface;
var _ = require('substance/helpers');
var TitleEditor = require("./title_editor");
var Brackets = require('../../shared/components/brackets');
var UnsupportedNode = require('../../shared/components/unsupported_node');
var ContainerEditor = Surface.ContainerEditor;
var ContainerComponent = require('substance-ui/container_component');

var ENABLED_TOOLS = ["strong", "emphasis", "timecode", "remark", "entity_reference", "subject_reference"];

class ContentEditor extends React.Component {

  getChildContext() {
    return {
      surface: this.state.surface
    };
  }

  computeStateFromProps(props) {
    var doc = props.doc;
    var editor = new ContainerEditor('content');
    var surface = new Surface(this.context.surfaceManager, doc, editor, { name: 'content' });

    return {
      editor: editor,
      surface: surface
    };
  }

  initializeComponent() {
    // We may have already initialized the stuff
    var surfaceManager = this.context.surfaceManager;
    var surface = this.state.surface;
    var contentContainerEl = React.findDOMNode(this.refs.contentContainer);
    var compEl = React.findDOMNode(this);

    this.context.app.registerSurface(surface, {
      enabledTools: ENABLED_TOOLS
    });

    surface.attach(contentContainerEl);

    this.brackets = new Brackets({
      doc: this.props.doc,
      contentContainerEl: contentContainerEl,
      onBracketToggled: this.onBracketToggled.bind(this)
    });
    $(compEl).append(this.brackets.render().$el);
  }

  onBracketToggled(subjectReferenceId) {
    var app = this.context.app;
    var state = app.state;

    if (state.contextId === "editSubjectReference" && state.subjectReferenceId === subjectReferenceId) {
      app.replaceState({
        contextId: "subjects"
      });
    } else {
      app.replaceState({
        contextId: "editSubjectReference",
        subjectReferenceId: subjectReferenceId,
        noScroll: true
      });
    }
  }

  // Lifecycle
  // -------------

  componentWillMount() {
    this.setState(this.computeStateFromProps(this.props));
  }

  componentDidMount() {
    this.initializeComponent();
    // Custom event
    this.props.doc.emit('document:rendered');
  }

  componentDidUpdate() {
    this.brackets.render();
    this.brackets.updateBrackets();
  }

  componentWillUnmount() {
    this.dispose();
  }

  dispose() {
    var surface = this.state.surface;
    this.context.surfaceManager.unregisterSurface(surface);
    this.brackets.dispose();
    if (surface) {
      surface.disconnect(this);
      surface.dispose();
    }
  }

  render() {
    return $$('div', {className: 'content-editor-component panel-content-inner'},
      $$(TitleEditor, {doc: this.props.doc}),
      $$(ContainerComponent, {
        ref: 'contentContainer',
        containerId: 'content',
        doc: this.props.doc
      })
      // <Brackets> brackets go here
    );
  }
}

ContentEditor.contextTypes = {
  app: React.PropTypes.object.isRequired,
  componentRegistry: React.PropTypes.object.isRequired,
  notifications: React.PropTypes.object.isRequired,
  surfaceManager: React.PropTypes.object.isRequired
};

ContentEditor.childContextTypes = {
  // provided to editor components so that they know in which context they are
  surface: React.PropTypes.object,
};

ContentEditor.displayName = "ContentEditor";

module.exports = ContentEditor;
<commit_msg>Pass panel as a prop.<commit_after>'use strict';

var Substance = require('substance');
var $$ = React.createElement;
var Surface = Substance.Surface;
var _ = require('substance/helpers');
var TitleEditor = require("./title_editor");
var Brackets = require('../../shared/components/brackets');
var UnsupportedNode = require('../../shared/components/unsupported_node');
var ContainerEditor = Surface.ContainerEditor;
var ContainerComponent = require('substance-ui/container_component');

var ENABLED_TOOLS = ["strong", "emphasis", "timecode", "remark", "entity_reference", "subject_reference"];

class ContentEditor extends React.Component {

  getChildContext() {
    return {
      surface: this.state.surface
    };
  }

  computeStateFromProps(props) {
    var doc = props.doc;
    var editor = new ContainerEditor('content');
    var surface = new Surface(this.context.surfaceManager, doc, editor, { name: 'content' });

    return {
      editor: editor,
      surface: surface
    };
  }

  initializeComponent() {
    // We may have already initialized the stuff
    var surfaceManager = this.context.surfaceManager;
    var surface = this.state.surface;
    var contentContainerEl = React.findDOMNode(this.refs.contentContainer);
    var compEl = React.findDOMNode(this);

    this.context.app.registerSurface(surface, {
      enabledTools: ENABLED_TOOLS
    });

    surface.attach(contentContainerEl);

    this.brackets = new Brackets({
      doc: this.props.doc,
      panel: this.props.panel,
      contentContainerEl: contentContainerEl,
      onBracketToggled: this.onBracketToggled.bind(this)
    });
    $(compEl).append(this.brackets.render().$el);
  }

  onBracketToggled(subjectReferenceId) {
    var app = this.context.app;
    var state = app.state;

    if (state.contextId === "editSubjectReference" && state.subjectReferenceId === subjectReferenceId) {
      app.replaceState({
        contextId: "subjects"
      });
    } else {
      app.replaceState({
        contextId: "editSubjectReference",
        subjectReferenceId: subjectReferenceId,
        noScroll: true
      });
    }
  }

  // Lifecycle
  // -------------

  componentWillMount() {
    this.setState(this.computeStateFromProps(this.props));
  }

  componentDidMount() {
    this.initializeComponent();
    // Custom event
    this.props.doc.emit('document:rendered');
  }

  componentDidUpdate() {
    this.brackets.render();
    this.brackets.updateBrackets();
  }

  componentWillUnmount() {
    this.dispose();
  }

  dispose() {
    var surface = this.state.surface;
    this.context.surfaceManager.unregisterSurface(surface);
    this.brackets.dispose();
    if (surface) {
      surface.disconnect(this);
      surface.dispose();
    }
  }

  render() {
    return $$('div', {className: 'content-editor-component panel-content-inner'},
      $$(TitleEditor, {doc: this.props.doc}),
      $$(ContainerComponent, {
        ref: 'contentContainer',
        containerId: 'content',
        doc: this.props.doc
      })
      // <Brackets> brackets go here
    );
  }
}

ContentEditor.contextTypes = {
  app: React.PropTypes.object.isRequired,
  componentRegistry: React.PropTypes.object.isRequired,
  notifications: React.PropTypes.object.isRequired,
  surfaceManager: React.PropTypes.object.isRequired
};

ContentEditor.childContextTypes = {
  // provided to editor components so that they know in which context they are
  surface: React.PropTypes.object,
};

ContentEditor.displayName = "ContentEditor";

module.exports = ContentEditor;
<|endoftext|>


<commit_before>'use strict'

const request = require('request');
const phoneFormatter = require('phone-formatter');

const colors = ['#1c4fff', '#e84778', '#ffc229', '#1ae827', '#5389ff'];

module.exports = function (req, res, next) {
  var botPayload = {};
  botPayload.username = 'Gethuman Bot';
  botPayload.channel = req.body.channel_id;

  //handling text input
  var textInput = (req.body.text) ? req.body.text : '';
  if (textInput) {
    //this breaks ...
      summonQuestionResponse(textInput, botPayload, res);
  } else {
    //this works...
      botPayload.text = "Tell me your customer service issue.";
      botPayload.icon_emoji = ':question:';
      send(botPayload, function (error, status, body) {
        if (error) {
          return next(error);
        } else if (status !== 200) {
          return next(new Error('Incoming WebHook: ' + status + ' ' + body));
        } else {
          return res.status(200).end();
      }
      });
  };
}

function send (payload, callback) {
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services/' + path;

  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload)
  }, function (error, response, body) {
    if (error) {
      return callback(error);
    }
    callback(null, response.statusCode, body);
  });
}

function summonQuestionResponse(textInput, botPayload, res) {
    var questions = [];
    var companyIDs = [];
    var guideIDs = [];
    var companyObjects = [];
    var companyTable = {};
    var guideObjects = [];
    var guideTable = {};

    let filters = {
        type: 'question',
        isGuide: true
    };
    let limit = 5;
    console.log("checking in before first request");

    request('http://api.gethuman.co/v3/posts/search?match='
            + encodeURIComponent(textInput)
            + '&limit='
            + limit
            + '&filterBy='
            + encodeURIComponent(JSON.stringify(filters))
            , function (error, response, body) {
        if (!error && response.statusCode == 200) {
            console.log("first request is successful win");

            questions = JSON.parse(body);
            if (questions && questions.length) {
                for (let i = 0; i < questions.length; i++) {
                    companyIDs.push(questions[i].companyId);
                    guideIDs.push(questions[i].guideId);
                };
                // console.log("Company ID's: " + companyIDs);
                // console.log("Guide ID's: " + guideIDs);
                request('http://api.gethuman.co/v3/companies?where='
                    + encodeURIComponent(JSON.stringify({ _id: { $in: companyIDs }}))
                    , function (error, response, body) {
                    if (!error && response.statusCode == 200) {
                        companyObjects = JSON.parse(body);
                        for (let i = 0; i < companyObjects.length; i++) {
                            companyTable[companyObjects[i]._id] = companyObjects[i]
                        };
                        // console.log("All company Objects returned from API: " + JSON.stringify(companyTable));
                        request('http://api.gethuman.co/v3/guides?where='
                            + encodeURIComponent(JSON.stringify({ _id: { $in: guideIDs }}))
                            , function (error, response, body) {
                            if (!error && response.statusCode == 200) {
                                guideObjects = JSON.parse(body);
                                for (let i = 0; i < guideObjects.length; i++) {
                                    guideTable[guideObjects[i]._id] = guideObjects[i]
                                };
                                // console.log("All guide Objects returned from API: " + JSON.stringify(guideTable));
                                // attach Companies and Guides to Questions
                                for (var i = 0; i < questions.length; i++) {
                                    let cID = questions[i].companyId;
                                    questions[i].company = companyTable[cID];
                                    let gID = questions[i].guideId;
                                    questions[i].guide = guideTable[gID];
                                };
                                prepareQuestionsPayload(questions, botPayload, res);
                            } else if (error) {
                            console.log(error);
                          }
                        });
                    } else if (error) {
                    console.log(error);
                  }
                });
            } else {
                // console.log("Received no results from Questions API for input: " + textInput);
                summonCompanyResponse(textInput, botPayload, res);
            };
        } else if (error) {
            console.log("breaking at first request");
            // should it send a basic message back if the GH API is broken?
            console.log(error);
        }
    })
};

function summonCompanyResponse(textInput, botPayload, res) {
    var companies = [];

    request('http://api.gethuman.co/v3/companies/search?limit=5&match=' + encodeURIComponent(textInput), function (error, response, body) {
        if (!error && response.statusCode == 200) {
            companies = JSON.parse(body);
            // console.log("Full API response: " + body);
            if (companies && companies.length) {
                prepareCompaniesPayload(companies, botPayload, res);
            } else {
                // send back a plain text response, prompt for usable input
                botPayload.text = "We could not find anything matching your input to our database. Could you try rephrasing your concern, and be sure to spell the company name correctly?";
                botPayload.icon_emoji = ':stuck_out_tongue:';
                console.log("Received no results from Companies API for input: " + textInput);
                send(botPayload, function (error, status, body) {
                    if (error) {
                      return next(error);
                    } else if (status !== 200) {
                      return next(new Error('Incoming WebHook: ' + status + ' ' + body));
                    } else {
                      return res.status(200).end();
                    }
                });
            };
        } else if (error) {
          console.log(error);
        }
    })
};

function prepareQuestionsPayload(questions, botPayload, res) {
    botPayload.text = "Here are some issues potentially matching your input, and links for how to resolve them:";
    botPayload.icon_emoji = ':tada:';
    botPayload.attachments = [];

    for (let i = 0; i < questions.length; i++) {
        // console.log("Question # " + i + ": " + JSON.stringify(questions[i]));
        let name = questions[i].companyName || '';
        console.log("Company name found: " + name);
        let color = colors[i];
        let urlId = questions[i].urlId || '';
        let phone = (questions[i].company) ? questions[i].company.callback.phone : '';
        //format phone# for international format
        // let phoneIntl = (phone) ? phoneFormatter.format(phone, "+1NNNNNNNNNN") : '';
        let title = questions[i].title || '';
        // check if company name is in title already, add to front if not
        if (title.indexOf(name) < 0) {
            title = name + ": " + title;
        };
        let email = '';
        // filter GH array to find contactInfo
        // does this turn up any email at all? Investigate later.
        let emailContactMethods = questions[i].company.contactMethods.filter(function ( method ) {
            return method.type === "email";
        });
        if (emailContactMethods && emailContactMethods.length) {
            email = emailContactMethods[0].target;
        };

        let textField = '';
        if (phone && email) {
            textField = phone + " | " + email;
        } else if (phone) {
            textField = phone;
        } else if (email) {
            textField = email;
        };
        // if (questions[i].guide.steps) {
        //     console.log("Solutions for Question #" + i + ": " + JSON.stringify(questions[i].guide.steps));
        // } else {
        //     console.log("No solutions found for Question #" + i);
        // };
        // let solution = questions[i].guide.steps[0].details || 'No solution found for this issue.';
        // experimental solution to strip Html
        // solution = stripHtml(solution);
        let singleAttachment = {
            "fallback": "Solution guide for " + name,
            "title": title,
            "color": color,
            // redundant link to one in the Fields - add this if removing the Field
            // "title_link": "https://answers.gethuman.co/_" + encodeURIComponent(urlId),
            "text": textField,
            "fields": [
                {
                    // "title": "*****************************************",
                    "value": "------------------------------------------------------",
                    "short": false
                },
                {
                    // "title": "More info",
                    "value": "<http://answers.gethuman.co/_" + encodeURIComponent(urlId) + "|Step by Step Guide>",
                    "short": true
                },
                {
                    // "title": "Let us do it for you",
                    "value": "<http://gethuman.com?company=" + encodeURIComponent(name) + "|Solve for me - $20>",
                    "short": true
                }
            ]
        };
        // if (phoneIntl) {
        //     singleAttachment.fields.push({
        //         // "title": "Talk with " + name,
        //         "value": "<tel:" + phoneIntl + "|Call " + name + ">",
        //         "short": true
        //     })
        // };
        botPayload.attachments.push(singleAttachment);
    };
    // attach buttons to receive feedback
    // buttons not currently functional, until Bot status acheived
    // botPayload.attachments.push({
    //     "fallback": "Are you happy with these answers?",
    //     "title": "Are you happy with these answers?",
    //     "callback_id": "questions_feedback",
    //     "color": "#ff0000",
    //     "attachment_type": "default",
    //     "actions": [
    //         {
    //             "name": "yes",
    //             "text": "Yes",
    //             "type": "button",
    //             "value": "Yes"
    //         },
    //         {
    //             "name": "no",
    //             "text": "No",
    //             "type": "button",
    //             "value": "No"
    //         }
    //     ]
    // });

    // console.log("About to send the Questions payload. Godspeed!");
    send(botPayload, function (error, status, body) {
      if (error) {
        return next(error);
      } else if (status !== 200) {
        // inform user that our Incoming WebHook failed
        // console.log("Oh the humanity! Questions payload has crashed and burned.");
        // console.log("Let's have a look at the payload: " + JSON.stringify(botPayload));
        return next(new Error('Incoming WebHook: ' + status + ' ' + body));
      } else {
        // console.log("Questions payload sent on for much win.");
        return res.status(200).end();
      }
    });
};

function prepareCompaniesPayload(companies, botPayload, res) {
    botPayload.text = "We could not find any specific questions matching your input, but here is the contact information for some companies that could help you resolve your issue:";
    botPayload.icon_emoji = ':flashlight:';
    botPayload.attachments = [];

    for (let i=0; i < companies.length; i++) {
        let name = companies[i].name || '';
        console.log("Company name found: " + name);
        let color = colors[i];
        let phone = companies[i].callback.phone || '';
        // not needed! Slack will auto-detect phone number!
        // let phoneIntl = (phone) ? phoneFormatter.format(phone, "+1NNNNNNNNNN") : '';
        let email = '';
        // filter GH array to find contactInfo
        let emailContactMethods = companies[i].contactMethods.filter(function ( method ) {
            return method.type === "email";
        });
        if (emailContactMethods && emailContactMethods.length) {
            email = emailContactMethods[0].target;
        };
        let singleAttachment = {
            "fallback": "Company info for " + name,
            "title": name,
            "color": color,
            "text": email + " | " + phone,
            "fields": [
                {
                    // "title": "Solve - $20",
                    "value": "<http://gethuman.com?company=" + encodeURIComponent(name) + "|Hire GetHuman to Solve - $20>",
                    "short": true
                }
            ]
        };
        // No need for this - Slack auto-interprets phone!
        // if (phoneIntl) {
        //     singleAttachment.fields.unshift({
        //         "title": "Want to talk to " + name + " ?",
        //         "value": "<tel:" + phoneIntl + "|Call them now>",
        //         "short": true
        //     })
        // };
        // if (email) {
        //     singleAttachment.fields.unshift({
        //             // "title": "Email " + name,
        //             "value": "<mailto:" + email + "|Email " + name + ">",
        //             "short": true
        //     })
        // };
        botPayload.attachments.push(singleAttachment);
    };
    // payload ready, send it on!
    // console.log("About to send the Questions payload. Godspeed!");
    send(botPayload, function (error, status, body) {
      if (error) {
        return next(error);
      } else if (status !== 200) {
        // inform user that our Incoming WebHook failed
        // console.log("Oh the humanity! Companies payload has crashed and burned.");
        // console.log("Let's have a look at the payload: " + JSON.stringify(botPayload));
        return next(new Error('Incoming WebHook: ' + status + ' ' + body));
      } else {
        // console.log("Companies payload sent on for much win.");
        return res.status(200).end();
      }
    });
}

// string of regex's to remove HTML tags from string
// not needed if not displaying solutions text
// function stripHtml(string) {
//     return string.replace(/<\s*br\/*>/gi, "\n")
//       .replace(/<\s*a.*href="(.*?)".*>(.*?)<\/a>/gi, " $2 (Link->$1) ")
//       .replace(/<\s*\/*.+?>/ig, "\n")
//       .replace(/ {2,}/gi, " ")
//       .replace(/\n+\s*/gi, "\n\n");
// }<commit_msg>Abstract out functions<commit_after>'use strict'

const request = require('request');
const phoneFormatter = require('phone-formatter');

const colors = ['#1c4fff', '#e84778', '#ffc229', '#1ae827', '#5389ff'];

module.exports = function (req, res, next) {
  var botPayload = {};
  botPayload.username = 'Gethuman Bot';
  botPayload.channel = req.body.channel_id;

  //handling text input
  var textInput = (req.body.text) ? req.body.text : '';
  if (textInput) {
      summonQuestionResponse(textInput, botPayload, res);
  } else {
    prepareUserInputPrompt(botPayload, res);
  };
}

function send (payload, callback) {
  var path = process.env.INCOMING_WEBHOOK_PATH;
  var uri = 'https://hooks.slack.com/services/' + path;

  request({
    uri: uri,
    method: 'POST',
    body: JSON.stringify(payload)
  }, function (error, response, body) {
    if (error) {
      return callback(error);
    }
    callback(null, response.statusCode, body);
  });
}

function summonQuestionResponse(textInput, botPayload, res) {
    var questions = [];
    var companyIDs = [];
    var guideIDs = [];
    var companyObjects = [];
    var companyTable = {};
    var guideObjects = [];
    var guideTable = {};

    let filters = {
        type: 'question',
        isGuide: true
    };
    let limit = 5;

    request('http://api.gethuman.co/v3/posts/search?match='
            + encodeURIComponent(textInput)
            + '&limit='
            + limit
            + '&filterBy='
            + encodeURIComponent(JSON.stringify(filters))
            , function (error, response, body) {
        if (!error && response.statusCode == 200) {

            questions = JSON.parse(body);
            if (questions && questions.length) {
                for (let i = 0; i < questions.length; i++) {
                    companyIDs.push(questions[i].companyId);
                    guideIDs.push(questions[i].guideId);
                };
                request('http://api.gethuman.co/v3/companies?where='
                    + encodeURIComponent(JSON.stringify({ _id: { $in: companyIDs }}))
                    , function (error, response, body) {
                    if (!error && response.statusCode == 200) {
                        companyObjects = JSON.parse(body);
                        for (let i = 0; i < companyObjects.length; i++) {
                            companyTable[companyObjects[i]._id] = companyObjects[i]
                        };
                        request('http://api.gethuman.co/v3/guides?where='
                            + encodeURIComponent(JSON.stringify({ _id: { $in: guideIDs }}))
                            , function (error, response, body) {
                            if (!error && response.statusCode == 200) {
                                guideObjects = JSON.parse(body);
                                for (let i = 0; i < guideObjects.length; i++) {
                                    guideTable[guideObjects[i]._id] = guideObjects[i]
                                };
                                // attach Companies and Guides to Questions
                                for (var i = 0; i < questions.length; i++) {
                                    let cID = questions[i].companyId;
                                    questions[i].company = companyTable[cID];
                                    let gID = questions[i].guideId;
                                    questions[i].guide = guideTable[gID];
                                };
                                prepareQuestionsPayload(questions, botPayload, res);
                            } else if (error) {
                                prepareApiFailPayload(botPayload, res);
                                console.log(error);
                            }
                        });
                    } else if (error) {
                        prepareApiFailPayload(botPayload, res);
                        console.log(error);
                    }
                });
            } else {
                // console.log("Received no results from Questions API for input: " + textInput);
                summonCompanyResponse(textInput, botPayload, res);
            };
        } else if (error) {
            // should it send a basic message back if the GH API is broken?
            prepareApiFailPayload(botPayload, res);
            console.log(error);
        }
    })
};

function summonCompanyResponse(textInput, botPayload, res) {
    var companies = [];

    request('http://api.gethuman.co/v3/companies/search?limit=5&match=' + encodeURIComponent(textInput), function (error, response, body) {
        if (!error && response.statusCode == 200) {
            companies = JSON.parse(body);
            // console.log("Full API response: " + body);
            if (companies && companies.length) {
                prepareCompaniesPayload(companies, botPayload, res);
            } else {
                prepareNothingFoundPayload(botPayload, res);
            };
        } else if (error) {
          console.log(error);
        }
    })
};

function prepareQuestionsPayload(questions, botPayload, res) {
    botPayload.text = "Here are some issues potentially matching your input, and links for how to resolve them:";
    botPayload.icon_emoji = ':tada:';
    botPayload.attachments = [];

    for (let i = 0; i < questions.length; i++) {
        let name = questions[i].companyName || '';
        console.log("Company name found: " + name);
        let color = colors[i];
        let urlId = questions[i].urlId || '';
        let phone = (questions[i].company) ? questions[i].company.callback.phone : '';
        let title = questions[i].title || '';
        if (title.indexOf(name) < 0) {
            title = name + ": " + title;
        };
        let email = '';
        // filter GH array to find contactInfo
        // does this turn up any email at all? Investigating....
        console.log("Contact Methods for " + name + ": " + questions[i].company.contactMethods);
        let emailContactMethods = questions[i].company.contactMethods.filter(function ( method ) {
            return method.type === "email";
        });
        if (emailContactMethods && emailContactMethods.length) {
            email = emailContactMethods[0].target;
        };

        let textField = '';
        if (phone && email) {
            textField = phone + " | " + email;
        } else if (phone) {
            textField = phone;
        } else if (email) {
            textField = email;
        };
        let singleAttachment = {
            "fallback": "Solution guide for " + name,
            "title": title,
            "color": color,
            "text": textField,
            "fields": [
                {
                    "value": "------------------------------------------------------",
                    "short": false
                },
                {
                    "value": "<http://answers.gethuman.co/_" + encodeURIComponent(urlId) + "|Step by Step Guide>",
                    "short": true
                },
                {
                    "value": "<http://gethuman.com?company=" + encodeURIComponent(name) + "|Solve for me - $20>",
                    "short": true
                }
            ]
        };
        botPayload.attachments.push(singleAttachment);
    };

    // attach buttons to receive feedback
    // (buttons not currently functional, until Bot status acheived)
    // botPayload.attachments.push({
    //     "fallback": "Are you happy with these answers?",
    //     "title": "Are you happy with these answers?",
    //     "callback_id": "questions_feedback",
    //     "color": "#ff0000",
    //     "attachment_type": "default",
    //     "actions": [
    //         {
    //             "name": "yes",
    //             "text": "Yes",
    //             "type": "button",
    //             "value": "Yes"
    //         },
    //         {
    //             "name": "no",
    //             "text": "No",
    //             "type": "button",
    //             "value": "No"
    //         }
    //     ]
    // });

    send(botPayload, function (error, status, body) {
      if (error) {
        return next(error);
      } else if (status !== 200) {
        return next(new Error('Incoming WebHook: ' + status + ' ' + body));
      } else {
        return res.status(200).end();
      }
    });
};

function prepareCompaniesPayload(companies, botPayload, res) {
    botPayload.text = "We could not find any specific questions matching your input, but here is the contact information for some companies that could help you resolve your issue:";
    botPayload.icon_emoji = ':flashlight:';
    botPayload.attachments = [];

    for (let i=0; i < companies.length; i++) {
        let name = companies[i].name || '';
        console.log("Company name found: " + name);
        let color = colors[i];
        let phone = companies[i].callback.phone || '';
        let email = '';
        let emailContactMethods = companies[i].contactMethods.filter(function ( method ) {
            return method.type === "email";
        });
        if (emailContactMethods && emailContactMethods.length) {
            email = emailContactMethods[0].target;
        };
        let singleAttachment = {
            "fallback": "Company info for " + name,
            "title": name,
            "color": color,
            "text": email + " | " + phone,
            "fields": [
                {
                    // "title": "Solve - $20",
                    "value": "<http://gethuman.com?company=" + encodeURIComponent(name) + "|Hire GetHuman to Solve - $20>",
                    "short": true
                }
            ]
        };
        botPayload.attachments.push(singleAttachment);
    };
    send(botPayload, function (error, status, body) {
      if (error) {
        return next(error);
      } else if (status !== 200) {
        return next(new Error('Incoming WebHook: ' + status + ' ' + body));
      } else {
        return res.status(200).end();
      }
    });
}

// string of regex's to remove HTML tags from string
// not needed if not displaying solutions text
// function stripHtml(string) {
//     return string.replace(/<\s*br\/*>/gi, "\n")
//       .replace(/<\s*a.*href="(.*?)".*>(.*?)<\/a>/gi, " $2 (Link->$1) ")
//       .replace(/<\s*\/*.+?>/ig, "\n")
//       .replace(/ {2,}/gi, " ")
//       .replace(/\n+\s*/gi, "\n\n");
// }

function prepareUserInputPrompt(botPayload, res) {
    botPayload.text = "Tell me your customer service issue.";
    botPayload.icon_emoji = ':question:';
    send(botPayload, function (error, status, body) {
    if (error) {
      return next(error);
    } else if (status !== 200) {
      return next(new Error('Incoming WebHook: ' + status + ' ' + body));
    } else {
      return res.status(200).end();
    }
    });
};

function prepareNothingFoundPayload(botPayload, res) {
    botPayload.text = "We could not find anything matching your input to our database. Could you try rephrasing your concern, and be sure to spell the company name correctly?";
    botPayload.icon_emoji = ':stuck_out_tongue:';
    console.log("Received no results from Companies API for input: " + textInput);
    send(botPayload, function (error, status, body) {
        if (error) {
          return next(error);
        } else if (status !== 200) {
          return next(new Error('Incoming WebHook: ' + status + ' ' + body));
        } else {
          return res.status(200).end();
        }
    });
}

function prepareApiFailPayload(botPayload, res) {
    botPayload.text = "The GetHuman database just borked out. Sorry, try again later!";
    botPayload.icon_emoji = ':stuck_out_tongue:';
    console.log("GetHuman API failed.");
    send(botPayload, function (error, status, body) {
        if (error) {
          return next(error);
        } else if (status !== 200) {
          return next(new Error('Incoming WebHook: ' + status + ' ' + body));
        } else {
          return res.status(200).end();
        }
    });
}<|endoftext|>


<commit_before>var btn = {
    delete: function () {
        return $('<button type="button" class="nf_listfield_delete"></button>')
            .click(function (e) {
                e.preventDefault();
                $($(this).parent()).slideUp(400, function () {
                    $(this).remove();
                });
            });
    },
    drag: function () {
        return $('

');
    },
    add: function() {
        return $('<button type="button" class="nf_add btn btn-warning"> Add Item</button>');
    }
};

var fieldset = function (ctx) {
    if (ctx == document.body) {
        $('.nf_fieldset').addClass('closed');
        $('.error')
            .parents('.nf_fieldset > div').show()
            .parents('.nf_fieldset').removeClass('closed');
    }

    $('.nf_fieldset', ctx).each(function() {
        if ($(this).data('nf_fieldset'))
            return;
        $(this).data('nf_fieldset', true);

        var t = $(this),
            h2 = $('> h2', t),
            div = $('> div', t),
            i = $('').prependTo(t);

        t.css('min-height', h2.height());

        t.off('click').click(function(e) {
            if (!(t.is('.closed') || $(e.target).is(h2) || $(e.target).is(i)))
                return;

            i.toggleClass('icon-chevron-right icon-chevron-down');

            t.toggleClass('closed');
            div.stop(1,1).slideToggle('fast');
        });
    });
};

var ListField = function(el) {
    var self = this;
    self.el = $(el);

    self.init = function() {
        if (self.el.data('processed') == 'true')
            return;
        self.el.data('processed','true');

        var container = self.el.closest('.field').addClass('nf_listfield_container');

        self.name = self.el.attr('name');

        var tpl = $('> .nf_hidden_template', el);
        self.template = tpl.html();
        tpl.remove();

        self.list = $('> ul', el)
            .after(btn.add().click(self.add));

        self.length = $('> li', self.list)
            .append(btn.drag())
            .append(btn.delete())
            .length;

        self.list.sortable({
            items: 'li:not(.new_li)',
            handle: '.nf_listfield_drag',
            update: function() {
                var i = 0;

                $('> li', this).each(function(){
                    var li = this;
                    $('[name]', li).each(function() {
                        var input = $(this);
                        input.attr('name', input.attr('name').replace(new RegExp(self.name + '_li[0-9]+_'), self.name + '_li' + i + '_'));
                    });
                    i++;
                });
            }
        });

        widgets(this);
    };

    self.add = function(e) {
        e.preventDefault();

        var li = $('
').hide()
            .append(self.template)
            .append(btn.delete())
            .append(btn.drag())
            .insertBefore(this)
            .slideDown(function() {
                $('input:first', li).focus();
            });

        $('[name]', li).each(function() {
            var input = $(this),
                name = input.attr('name').replace(self.name + '_tmpl_', self.name + '_li' + self.length + '_');

            console.log('name', input.attr('name'), name);
            input.attr('name', name);
        });

        self.length++;

        // load nested widgets
        widgets(li);
    };

    self.init();
};

var widgets = function(ctx) {
    ctx = ctx || document.body;

    $('.nf_listfield', ctx).each(function() {
        $(this).data('listfield', new ListField(this));
    });

    fieldset(ctx);

    if ($.fn.select2)
        $('select', ctx).select2();

    if ($.fn.datepicker)
        $('.nf_datepicker', ctx).datepicker({
            format: 'dd/mm/yyyy'
        });

    if ($.fn.timepicker)
        $('.nf_timepicker', ctx).timepicker();
};

$(function(){
    $('.optional_label').each(function() {
        $('label[for="' + this.id  + '"]').addClass('optional_label');
    });

    widgets();

    $('form#document').submit(function(e) {
        $('p.submit button').prop('disabled', true);
        var btn = $('#saveButton');
//        btn.text(btn.data('saving-text'));
    });
});<commit_msg>reformat date string to be more generic<commit_after>var btn = {
    delete: function () {
        return $('<button type="button" class="nf_listfield_delete"></button>')
            .click(function (e) {
                e.preventDefault();
                $($(this).parent()).slideUp(400, function () {
                    $(this).remove();
                });
            });
    },
    drag: function () {
        return $('

');
    },
    add: function() {
        return $('<button type="button" class="nf_add btn btn-warning"> Add Item</button>');
    }
};

var fieldset = function (ctx) {
    if (ctx == document.body) {
        $('.nf_fieldset').addClass('closed');
        $('.error')
            .parents('.nf_fieldset > div').show()
            .parents('.nf_fieldset').removeClass('closed');
    }

    $('.nf_fieldset', ctx).each(function() {
        if ($(this).data('nf_fieldset'))
            return;
        $(this).data('nf_fieldset', true);

        var t = $(this),
            h2 = $('> h2', t),
            div = $('> div', t),
            i = $('').prependTo(t);

        t.css('min-height', h2.height());

        t.off('click').click(function(e) {
            if (!(t.is('.closed') || $(e.target).is(h2) || $(e.target).is(i)))
                return;

            i.toggleClass('icon-chevron-right icon-chevron-down');

            t.toggleClass('closed');
            div.stop(1,1).slideToggle('fast');
        });
    });
};

var ListField = function(el) {
    var self = this;
    self.el = $(el);

    self.init = function() {
        if (self.el.data('processed') == 'true')
            return;
        self.el.data('processed','true');

        var container = self.el.closest('.field').addClass('nf_listfield_container');

        self.name = self.el.attr('name');

        var tpl = $('> .nf_hidden_template', el);
        self.template = tpl.html();
        tpl.remove();

        self.list = $('> ul', el)
            .after(btn.add().click(self.add));

        self.length = $('> li', self.list)
            .append(btn.drag())
            .append(btn.delete())
            .length;

        self.list.sortable({
            items: 'li:not(.new_li)',
            handle: '.nf_listfield_drag',
            update: function() {
                var i = 0;

                $('> li', this).each(function(){
                    var li = this;
                    $('[name]', li).each(function() {
                        var input = $(this);
                        input.attr('name', input.attr('name').replace(new RegExp(self.name + '_li[0-9]+_'), self.name + '_li' + i + '_'));
                    });
                    i++;
                });
            }
        });

        widgets(this);
    };

    self.add = function(e) {
        e.preventDefault();

        var li = $('
').hide()
            .append(self.template)
            .append(btn.delete())
            .append(btn.drag())
            .insertBefore(this)
            .slideDown(function() {
                $('input:first', li).focus();
            });

        $('[name]', li).each(function() {
            var input = $(this),
                name = input.attr('name').replace(self.name + '_tmpl_', self.name + '_li' + self.length + '_');

            console.log('name', input.attr('name'), name);
            input.attr('name', name);
        });

        self.length++;

        // load nested widgets
        widgets(li);
    };

    self.init();
};

var widgets = function(ctx) {
    ctx = ctx || document.body;

    $('.nf_listfield', ctx).each(function() {
        $(this).data('listfield', new ListField(this));
    });

    fieldset(ctx);

    if ($.fn.select2)
        $('select', ctx).select2();

    if ($.fn.datepicker)
        $('.nf_datepicker', ctx).datepicker({
            format: 'yyyy/mm/dd'
        });

    if ($.fn.timepicker)
        $('.nf_timepicker', ctx).timepicker();
};

$(function(){
    $('.optional_label').each(function() {
        $('label[for="' + this.id  + '"]').addClass('optional_label');
    });

    widgets();

    $('form#document').submit(function(e) {
        $('p.submit button').prop('disabled', true);
        var btn = $('#saveButton');
//        btn.text(btn.data('saving-text'));
    });
});
<|endoftext|>


<commit_before>'use strict';

var extend = require('extend');

var log = require('./logging.js').getLogger('accountingController.js');
var QueryFilter = require('./QueryFilter.js');
var universalDaoModule = require('./UniversalDao.js');
var dateUtils = require('./DateUtils.js').DateUtils;
var async = require('async');

var DEFAULT_CFG ={};

function VirtualAccount(){
		this.dates=[];
		this.trans={};
		var self=this;

		this.addDate=function(date){
			this.dates.push(date);
			this.dates=this.dates.sort(function(a,b){
				if (a < b)
     				return -1;
  				if (a > b)
    				return 1;
  				return 0;});
		};

		this.getTrans=function(revdate){

			if (!this.trans[revdate]){
				this.trans[revdate]={credits:[],debits:[],balance:0,date:revdate};
				this.addDate(revdate);
			}

			return this.trans[revdate];
		};

		this.credit=function(revdate,value,entity){
			if (!revdate) return;
			var trs=this.getTrans(revdate);
			trs.credits.push({value:value,entity:entity});
		};

		this.debit=function(revdate,value,entity){
			if (!revdate) return;
			var trs=this.getTrans(revdate);
			trs.debits.push({value:value,entity:entity});
		};

		this.history=function(){
			return this.dates.map(function (date){
				return self.trans[date];
			});
		};

		this.recount = function(){
			var toPay=[];
			var paid=[];

			var toSpend=[];

			var balance =0;

			this.history().forEach(function(tr){
				tr.credits.map(function(credit){
					balance+=credit.value;
					credit.toSpend=credit.value;
					toSpend.push(credit);

				});

				tr.debits.map(function(debit){
					toPay.push(debit);
					debit.toPay=debit.value;
					debit.paid=0;
					debit.dateOfPayment=null;
				});

				if (balance>0){
					var payedCount=0;

					var toSpendUsed=null;
					toPay.forEach(function(debit){
						if (balance>0){
							if (balance>=debit.toPay){
							// full fee payed
								debit.paid=debit.entity.baseData.membershipFee;
								payedCount++;
								debit.dateOfPayment=tr.date;
								balance-=debit.toPay;
								toSpendUsed=spend(toSpend,debit.toPay);
								debit.toPay=0;
								toSpend=toSpendUsed.toSpend;
								debit.used=toSpendUsed.used;
								debit.fullyPayed=true;
								paid.push(debit);
							} else {
								// partially paid fee
								debit.paid+=balance;
								debit.toPay-=balance;
								debit.entity.baseData.membershipFeePaid=debit.entity.baseData.membershipFee-debit.toPay;
								spend(toSpend,balance);
								toSpendUsed=spend(toSpend,balance);
								balance=0;
								debit.used=toSpendUsed.used;
								toSpend=toSpendUsed.toSpend;
							}
						}
					});
					toPay.splice(0,payedCount);
				}
			});
			return{paid:paid,toPay:toPay,balance:balance};
		};
		function spend(credits,amount){

			var required=amount;
			var used=[];
			var toSpend=[];

			var useMore=true;
			credits.forEach(function(credit){

				if (useMore){
					if (required==credit.toSpend){
						required-=credit.toSpend;
						used.push(credit);
						useMore=false;
					}else if (required<credit.toSpend){
						used.push(credit);

						credit.toSpend-=required;
						toSpend.push(credit);
						useMore=false;
					} else if (required>credit.toSpend){
						used.push(credit);
						required-=credit.toSpend;
					}
				}else {
					toSpend.push(credit);
				}
			});
			return	{toSpend:toSpend,used:used};
		}
	}

var AccountingController = function(mongoDriver, schemaRegistry, options) {

	var cfg = extend(true, {}, DEFAULT_CFG, options);

	var peopleDao = new universalDaoModule.UniversalDao(
				mongoDriver,
				{collectionName: 'people'}
			);
	var feesDao = new universalDaoModule.UniversalDao(
		mongoDriver,
		{collectionName: 'fees'}
	);

	var paymentsDao = new universalDaoModule.UniversalDao(
		mongoDriver,
		{collectionName: 'payments'}
	);

	var self=this;

	this.getUserInfo = function(req, res,next) {

		if (!req.params.userId){
			log.error("userId not present");
			next("userId not present");
			return;
		}

		//verify authorization

		self.recountPerson(req.params.userId,function(err,data){if (err){next(err);log.error(err);return;} res.send(data)})
	};

	this.getClubInfo = function(req, res,next) {

		if (!req.params.clubId){
			log.error("clubId not present");
			next("clubId not present");
			return;
		}

		//get users fees and payments
		var qf=QueryFilter.create();
		qf.addCriterium("hockeyPlayerInfo.clubName.oid","eq",req.params.clubId)

		peopleDao.find(qf,function(error,players){

			var tocall=players.map(function(player){
				return function(callback){
					self.recountPerson(player.id,function(err,data){if(err){callback(err);return; }
					data.entity=player;
					callback(null,data);});
				}
			});
			async.parallelLimit(tocall, 10,function(err,data){
				self.aggregateResults(data,function(err,data){
					res.send(data);
				})
			});
		});

		//verify authorization

	};

	function countToPay(arr){
		var result=0;
		arr.forEach(function(item){
			result+=Number(item.toPay);
		})
		return result;
	}
	this.aggregateResults=function(data,callback){

		var overal={feesOverdue:0,
					feesOverdueValue:0,
					feesToPay:0,
					feesToPayValue:0,
					};

		var peoples=data.map(function(personInfo){
			var credit=0;
			if (personInfo.counted.balance>0){
				credit=personInfo.counted.balance;
			}
			var personLine={
				name:personInfo.entity.baseData.name.v,
				surName:personInfo.entity.baseData.surName.v,
				fee:personInfo.entity.membershipFeeInfo.membershipFee,
				paidValue: -1,
			 	feesToPay:personInfo.counted.toPay.length,
			 	feesToPayValue:countToPay(personInfo.counted.toPay),
				feesOverdue: personInfo.overDue.count,
				feesOverdueValue:personInfo.overDue.sum,
				credit: credit
			};

			overal.feesToPay+=personLine.feesToPay;
			overal.feesToPayValue+=personLine.feesToPayValue;
			overal.feesOverdue+=personLine.feesOverdue;

			overal.feesOverdueValue+=personLine.feesOverdueValue;

			return personLine;
		});

		// var data={people:peoples,overal:overal,raw:data};
		var data={people:peoples,overal:overal};

		callback(null,data);
	};

	this.recountPerson=function(peopleId,callback){

			//get users fees and payments
			var qf=QueryFilter.create();
			qf.addCriterium("baseData.member.oid","eq",peopleId);
			// qf.addSort('baseData.accountingDate','asc');

			var va =  new VirtualAccount();

			var result={};

			paymentsDao.find(qf,function(err,payments){

				if (err){
					log.error(err);
					return;
				}

				result.payments=payments;

				var qf=QueryFilter.create();
				qf.addCriterium("baseData.member.oid","eq",peopleId);

				payments.forEach(function(payment){
					va.credit(payment.baseData.accountingDate,payment.baseData.amount,payment);
				});

				feesDao.find(qf,function (err,fees){

					if (err){
						log.error(err);
						return;
					}

					fees.forEach(function(fee){
						va.debit(fee.baseData.setupDate,fee.baseData.membershipFee,fee);
					});

					var counted=va.recount();

					result.counted=counted;

					var overDue={new:[],old:[],sum:0,count:0};
					result.overDue=overDue;
					counted.toPay.forEach(function(topay){
						topay.entity.baseData.membershipFeePaid=topay.paid;
						if (topay.entity.baseData.feePaymentStatus=='created' ){
							if (topay.entity.baseData.dueDate<dateUtils.nowToReverse()){
								overDue.new.push(topay);
								overDue.sum+=(topay.entity.baseData.membershipFee-topay.entity.baseData.membershipFeePaid);
								overDue.count++;
								topay.entity.baseData.feePaymentStatus='overdue';

							}
							topay.entity.baseData.dateOfPayment=topay.dateOfPayment;
							topay.entity.baseData.membershipFeePaid=topay.paid;
							if (topay.used){
								topay.entity.listOfPayments={payments:topay.used.map(function(used){
									return {registry:'payments',oid:used.entity.id};
								})};
							}

						} else if (topay.entity.baseData.feePaymentStatus=='overdue' ||topay.entity.baseData.feePaymentStatus=='refunded'){
							topay.entity.baseData.feePaymentStatus='overdue';
							if (topay.entity.baseData.dueDate>dateUtils.nowToReverse()){
								topay.entity.baseData.feePaymentStatus='created';
							} else {
								overDue.old.push(topay);
								overDue.sum+=(topay.entity.baseData.membershipFee-topay.entity.baseData.membershipFeePaid);
								overDue.count++;
							}
							if (topay.used){
								topay.entity.listOfPayments={payments:topay.used.map(function(used){
									return {registry:'payments',oid:used.entity.id};
								})};
							}
							topay.entity.baseData.dateOfPayment=topay.dateOfPayment;
							topay.entity.baseData.membershipFeePaid=topay.paid;
						}
					});
					callback(null,result);
				});
			});
		};

};

module.exports = {
	AccountingController : AccountingController
};
<commit_msg>Fix: Only players with defined fee shown in club report.      Filter for active player.<commit_after>'use strict';

var extend = require('extend');

var log = require('./logging.js').getLogger('accountingController.js');
var QueryFilter = require('./QueryFilter.js');
var universalDaoModule = require('./UniversalDao.js');
var dateUtils = require('./DateUtils.js').DateUtils;
var async = require('async');

var DEFAULT_CFG ={};

function VirtualAccount(){
		this.dates=[];
		this.trans={};
		var self=this;

		this.addDate=function(date){
			this.dates.push(date);
			this.dates=this.dates.sort(function(a,b){
				if (a < b)
     				return -1;
  				if (a > b)
    				return 1;
  				return 0;});
		};

		this.getTrans=function(revdate){

			if (!this.trans[revdate]){
				this.trans[revdate]={credits:[],debits:[],balance:0,date:revdate};
				this.addDate(revdate);
			}

			return this.trans[revdate];
		};

		this.credit=function(revdate,value,entity){
			if (!revdate) return;
			var trs=this.getTrans(revdate);
			trs.credits.push({value:value,entity:entity});
		};

		this.debit=function(revdate,value,entity){
			if (!revdate) return;
			var trs=this.getTrans(revdate);
			trs.debits.push({value:value,entity:entity});
		};

		this.history=function(){
			return this.dates.map(function (date){
				return self.trans[date];
			});
		};

		this.recount = function(){
			var toPay=[];
			var paid=[];

			var toSpend=[];

			var balance =0;

			this.history().forEach(function(tr){
				tr.credits.map(function(credit){
					balance+=credit.value;
					credit.toSpend=credit.value;
					toSpend.push(credit);

				});

				tr.debits.map(function(debit){
					toPay.push(debit);
					debit.toPay=debit.value;
					debit.paid=0;
					debit.dateOfPayment=null;
				});

				if (balance>0){
					var payedCount=0;

					var toSpendUsed=null;
					toPay.forEach(function(debit){
						if (balance>0){
							if (balance>=debit.toPay){
							// full fee payed
								debit.paid=debit.entity.baseData.membershipFee;
								payedCount++;
								debit.dateOfPayment=tr.date;
								balance-=debit.toPay;
								toSpendUsed=spend(toSpend,debit.toPay);
								debit.toPay=0;
								toSpend=toSpendUsed.toSpend;
								debit.used=toSpendUsed.used;
								debit.fullyPayed=true;
								paid.push(debit);
							} else {
								// partially paid fee
								debit.paid+=balance;
								debit.toPay-=balance;
								debit.entity.baseData.membershipFeePaid=debit.entity.baseData.membershipFee-debit.toPay;
								spend(toSpend,balance);
								toSpendUsed=spend(toSpend,balance);
								balance=0;
								debit.used=toSpendUsed.used;
								toSpend=toSpendUsed.toSpend;
							}
						}
					});
					toPay.splice(0,payedCount);
				}
			});
			return{paid:paid,toPay:toPay,balance:balance};
		};
		function spend(credits,amount){

			var required=amount;
			var used=[];
			var toSpend=[];

			var useMore=true;
			credits.forEach(function(credit){

				if (useMore){
					if (required==credit.toSpend){
						required-=credit.toSpend;
						used.push(credit);
						useMore=false;
					}else if (required<credit.toSpend){
						used.push(credit);

						credit.toSpend-=required;
						toSpend.push(credit);
						useMore=false;
					} else if (required>credit.toSpend){
						used.push(credit);
						required-=credit.toSpend;
					}
				}else {
					toSpend.push(credit);
				}
			});
			return	{toSpend:toSpend,used:used};
		}
	}

var AccountingController = function(mongoDriver, schemaRegistry, options) {

	var cfg = extend(true, {}, DEFAULT_CFG, options);

	var peopleDao = new universalDaoModule.UniversalDao(
				mongoDriver,
				{collectionName: 'people'}
			);
	var feesDao = new universalDaoModule.UniversalDao(
		mongoDriver,
		{collectionName: 'fees'}
	);

	var paymentsDao = new universalDaoModule.UniversalDao(
		mongoDriver,
		{collectionName: 'payments'}
	);

	var self=this;

	this.getUserInfo = function(req, res,next) {

		if (!req.params.userId){
			log.error("userId not present");
			next("userId not present");
			return;
		}

		//verify authorization

		self.recountPerson(req.params.userId,function(err,data){if (err){next(err);log.error(err);return;} res.send(data)})
	};

	this.getClubInfo = function(req, res,next) {

		if (!req.params.clubId){
			log.error("clubId not present");
			next("clubId not present");
			return;
		}

		//get users fees and payments
		var qf=QueryFilter.create();
		qf.addCriterium("hockeyPlayerInfo.clubName.oid","eq",req.params.clubId)
		qf.addCriterium("membershipFeeInfo.membershipFee","ex",null);
		qf.addCriterium("hockeyPlayerInfo.isActivePlayer","ex",true);

		peopleDao.find(qf,function(error,players){

			var tocall=players.map(function(player){
				return function(callback){
					self.recountPerson(player.id,function(err,data){if(err){callback(err);return; }
					data.entity=player;
					callback(null,data);});
				}
			});
			async.parallelLimit(tocall, 10,function(err,data){
				self.aggregateResults(data,function(err,data){
					res.send(data);
				})
			});
		});

		//verify authorization

	};

	function countToPay(arr){
		var result=0;
		arr.forEach(function(item){
			result+=Number(item.toPay);
		})
		return result;
	}
	this.aggregateResults=function(data,callback){

		var overal={feesOverdue:0,
					feesOverdueValue:0,
					feesToPay:0,
					feesToPayValue:0,
					};

		var peoples=data.map(function(personInfo){
			var credit=0;
			if (personInfo.counted.balance>0){
				credit=personInfo.counted.balance;
			}
			var personLine={
				name:personInfo.entity.baseData.name.v,
				surName:personInfo.entity.baseData.surName.v,
				ee:personInfo.entity.membershipFeeInfo.membershipFee,
				paidValue: -1,
			 	feesToPay:personInfo.counted.toPay.length,
			 	feesToPayValue:countToPay(personInfo.counted.toPay),
				feesOverdue: personInfo.overDue.count,
				feesOverdueValue:personInfo.overDue.sum,
				credit: credit
			};

			overal.feesToPay+=personLine.feesToPay;
			overal.feesToPayValue+=personLine.feesToPayValue;
			overal.feesOverdue+=personLine.feesOverdue;

			overal.feesOverdueValue+=personLine.feesOverdueValue;

			return personLine;
		});

		// var data={people:peoples,overal:overal,raw:data};
		var data={people:peoples,overal:overal};

		callback(null,data);
	};

	this.recountPerson=function(peopleId,callback){

			//get users fees and payments
			var qf=QueryFilter.create();
			qf.addCriterium("baseData.member.oid","eq",peopleId);
			// qf.addSort('baseData.accountingDate','asc');

			var va =  new VirtualAccount();

			var result={};

			paymentsDao.find(qf,function(err,payments){

				if (err){
					log.error(err);
					return;
				}

				result.payments=payments;

				var qf=QueryFilter.create();
				qf.addCriterium("baseData.member.oid","eq",peopleId);

				payments.forEach(function(payment){
					va.credit(payment.baseData.accountingDate,payment.baseData.amount,payment);
				});

				feesDao.find(qf,function (err,fees){

					if (err){
						log.error(err);
						return;
					}

					fees.forEach(function(fee){
						va.debit(fee.baseData.setupDate,fee.baseData.membershipFee,fee);
					});

					var counted=va.recount();

					result.counted=counted;

					var overDue={new:[],old:[],sum:0,count:0};
					result.overDue=overDue;
					counted.toPay.forEach(function(topay){
						topay.entity.baseData.membershipFeePaid=topay.paid;
						if (topay.entity.baseData.feePaymentStatus=='created' ){
							if (topay.entity.baseData.dueDate<dateUtils.nowToReverse()){
								overDue.new.push(topay);
								overDue.sum+=(topay.entity.baseData.membershipFee-topay.entity.baseData.membershipFeePaid);
								overDue.count++;
								topay.entity.baseData.feePaymentStatus='overdue';

							}
							topay.entity.baseData.dateOfPayment=topay.dateOfPayment;
							topay.entity.baseData.membershipFeePaid=topay.paid;
							if (topay.used){
								topay.entity.listOfPayments={payments:topay.used.map(function(used){
									return {registry:'payments',oid:used.entity.id};
								})};
							}

						} else if (topay.entity.baseData.feePaymentStatus=='overdue' ||topay.entity.baseData.feePaymentStatus=='refunded'){
							topay.entity.baseData.feePaymentStatus='overdue';
							if (topay.entity.baseData.dueDate>dateUtils.nowToReverse()){
								topay.entity.baseData.feePaymentStatus='created';
							} else {
								overDue.old.push(topay);
								overDue.sum+=(topay.entity.baseData.membershipFee-topay.entity.baseData.membershipFeePaid);
								overDue.count++;
							}
							if (topay.used){
								topay.entity.listOfPayments={payments:topay.used.map(function(used){
									return {registry:'payments',oid:used.entity.id};
								})};
							}
							topay.entity.baseData.dateOfPayment=topay.dateOfPayment;
							topay.entity.baseData.membershipFeePaid=topay.paid;
						}
					});
					callback(null,result);
				});
			});
		};

};

module.exports = {
	AccountingController : AccountingController
};
<|endoftext|>


<commit_before>
Util.Form.customLabelStyle["inject"] = function(iN) {
	// u.bug("customLabelStyle:inject", iN);

	// some inputs cannot have labels injected
	// textarea has no type
	if(!iN.type || !iN.type.match(/file|radio|checkbox/)) {

		// store default value
		iN.default_value = u.text(iN.label);

		// add default handlers to focus and blur events
		u.e.addEvent(iN, "focus", u.f._changed_state);
		u.e.addEvent(iN, "blur", u.f._changed_state);

		// Create psydo label for inputs that can't easily show label value
		// Did experiments with with field replacement, but required too much work
		// replacing event and references (this seems to provide sufficient backup)
		if(iN.type.match(/number|integer|password|datetime|date/)) {

			iN.pseudolabel = u.ae(iN.parentNode, "span", {"class":"pseudolabel", "html":iN.default_value});
			iN.pseudolabel.iN = iN;

			// position on top of input
			u.as(iN.pseudolabel, "top", iN.offsetTop+"px");
			u.as(iN.pseudolabel, "left", iN.offsetLeft+"px");
			// create event to remove pseudolabel
			u.ce(iN.pseudolabel)
			iN.pseudolabel.inputStarted = function(event) {
				u.e.kill(event);
				this.iN.focus();
			}

		}

		u.f.updateDefaultState(iN);

	}

}

// internal focus/blur handler for default value controller - attatched to inputs
u.f._changed_state = function() {
	// u.bug("this._default_value_focus:", this);

	u.f.updateDefaultState(this);
}

// update default state on input
u.f.updateDefaultState = function(iN) {
	// u.bug("updateDefaultState for:", iN, iN.is_focused);

	// is input focused
	if(iN.is_focused || iN.val() !== "") {

		// leave default state
		u.rc(iN, "default");

		// remove default value if field does not have value
		if(iN.val() === "") {
			iN.val("");
		}

	}
	// input does not have focus - consider dafault value
	else {

		// only set default value if input is empty
		if(iN.val() === "") {

			// add class to indicate default value
			u.ac(iN, "default");
			iN.val(iN.default_value);

		}
	}
}
<commit_msg>Support for virtual inputs in labelstyle inject<commit_after>
Util.Form.customLabelStyle["inject"] = function(iN) {
	// u.bug("customLabelStyle:inject", iN);

	// some inputs cannot have labels injected
	// textarea has no type
	if(!iN.type || !iN.type.match(/file|radio|checkbox/)) {

		// store default value
		iN.default_value = u.text(iN.label);

		// add default handlers to focus and blur events
		u.e.addEvent(iN, "focus", u.f._changed_state);
		u.e.addEvent(iN, "blur", u.f._changed_state);

		// Create psydo label for inputs that can't easily show label value
		// Did experiments with with field replacement, but required too much work
		// replacing event and references (this seems to provide sufficient backup)
		if(iN.type.match(/number|integer|password|datetime|date/)) {

			iN.pseudolabel = u.ae(iN.parentNode, "span", {"class":"pseudolabel", "html":iN.default_value});
			iN.pseudolabel.iN = iN;

			// position on top of input
			u.as(iN.pseudolabel, "top", iN.offsetTop+"px");
			u.as(iN.pseudolabel, "left", iN.offsetLeft+"px");
			// create event to remove pseudolabel
			u.ce(iN.pseudolabel)
			iN.pseudolabel.inputStarted = function(event) {
				u.e.kill(event);
				this.iN.focus();
			}

		}

		u.f.updateDefaultState(iN);

	}

}

// internal focus/blur handler for default value controller - attatched to inputs
u.f._changed_state = function() {
	// u.bug("this._default_value_focus:", this);

	u.f.updateDefaultState(this);
}

// update default state on input
u.f.updateDefaultState = function(iN) {
	// u.bug("updateDefaultState for:", iN, iN.is_focused);

	// is input focused
	if(iN.is_focused || iN.val() !== "") {

		// leave default state
		u.rc(iN, "default");
		if(obj(iN.field.virtual_input)) {
			u.rc(iN.field.virtual_input, "default");
		}

		// remove default value if field does not have value
		if(iN.val() === "") {
			iN.val("");
		}

	}
	// input does not have focus - consider dafault value
	else {

		// only set default value if input is empty
		if(iN.val() === "") {

			// add class to indicate default value
			u.ac(iN, "default");
			if(obj(iN.field.virtual_input)) {
				u.ac(iN.field.virtual_input, "default");
			}
			iN.val(iN.default_value);

		}
	}
}
<|endoftext|>


<commit_before>'use strict';

// Libs
let spawn = require('child_process').spawn;
let exec = require('child_process').exec;

/**
 * The helper class for system updates
 */
class UpdateHelper {
    /**
     * Check for updates
     *
     * @returns {Promise} Status info
     */
    static check() {
        return new Promise((resolve, reject) => {
            let resolveObj = {
                behind: false,
                amount: 0,
                branch: 'stable',
                comment: ''
            };

            debug.log('Checking for updates...', this);

            function checkOutput(data) {
                let behindMatches = data.match(/Your branch is behind '(.+)' by (\d+) commit/);
                let upToDateMatches = data.match(/Your branch is up-to-date with '(.+)'/);
                let messageMatches = data.match(/[a-z0-9]{40} (.+)/);

                if(behindMatches && behindMatches.length > 1) {
                    resolveObj.behind = true;
                    resolveObj.branch = behindMatches[1].replace('origin/', '');
                    resolveObj.amount = behindMatches[2];
                }

                if(upToDateMatches && upToDateMatches.length > 1) {
                    resolveObj.branch = upToDateMatches[1].replace('origin/', '');
                }

                if(messageMatches && messageMatches.length > 1) {
                    resolveObj.comment = messageMatches[1];
                }
            }

            let git = exec('git fetch && git status && git log origin -1 --format=oneline', {
                cwd: appRoot
            });

            git.stdout.on('data', (data) => {
                checkOutput(data);
                debug.log(data, this, 3);
            });

            git.stderr.on('data', (data) => {
                checkOutput(data);
                debug.log(data, this, 3);
            });

            git.on('exit', (code) => {
                if(code == 0 || code == '0') {
                    debug.log('Done checking for updates', this);
                    resolve(resolveObj);
                } else {
                    debug.log('Checking for updates failed', this);
                    reject(new Error('git exited with status code ' + code));
                }
            });
        });
    }

    /**
     * Perform update
     *
     * @returns {Promise} Status info
     */
    static update() {
        return new Promise((resolve, reject) => {
            debug.log('Updating HashBrown...', this);

            let git = exec('git pull', {
                cwd: appRoot
            });

            git.stdout.on('data', (data) => {
                debug.log(data, this, 3);
            });

            git.stderr.on('data', (data) => {
                debug.log(data, this, 3);
            });

            git.on('exit', (code) => {
                if(code == 0 || code == '0') {
                    debug.log('Update successful', this);
                    resolve();
                } else {
                    debug.log('Update failed', this);
                    reject(new Error('git exited with status code ' + code));
                }
            });
        })
        .then(() => {
            return UpdateHelper.installDependencies();
        });
    }

    /**
     * Install dependencies
     *
     * @returns {Promise} Status info
     */
    installDependencies() {
        return new Promise((resolve, reject) => {
            debug.log('Installing dependencies...', this);

            let npm = exec('npm install', {
                cwd: appRoot
            });

            npm.stdout.on('data', (data) => {
                debug.log(data, this, 3);
            });

            npm.stderr.on('data', (data) => {
                debug.log(data, this, 3);
            });

            npm.on('exit', (code) => {
                if(code == 0 || code == '0') {
                    debug.log('Install successful', this);
                    resolve();
                } else {
                    debug.log('Update failed', this);
                    reject(new Error('npm exited with status code ' + code));
                }
            });
        });
    }
}

module.exports = UpdateHelper;
<commit_msg>Fixed UpdateHelper dependency installer<commit_after>'use strict';

// Libs
let spawn = require('child_process').spawn;
let exec = require('child_process').exec;

/**
 * The helper class for system updates
 */
class UpdateHelper {
    /**
     * Check for updates
     *
     * @returns {Promise} Status info
     */
    static check() {
        return new Promise((resolve, reject) => {
            let resolveObj = {
                behind: false,
                amount: 0,
                branch: 'stable',
                comment: ''
            };

            debug.log('Checking for updates...', this);

            function checkOutput(data) {
                let behindMatches = data.match(/Your branch is behind '(.+)' by (\d+) commit/);
                let upToDateMatches = data.match(/Your branch is up-to-date with '(.+)'/);
                let messageMatches = data.match(/[a-z0-9]{40} (.+)/);

                if(behindMatches && behindMatches.length > 1) {
                    resolveObj.behind = true;
                    resolveObj.branch = behindMatches[1].replace('origin/', '');
                    resolveObj.amount = behindMatches[2];
                }

                if(upToDateMatches && upToDateMatches.length > 1) {
                    resolveObj.branch = upToDateMatches[1].replace('origin/', '');
                }

                if(messageMatches && messageMatches.length > 1) {
                    resolveObj.comment = messageMatches[1];
                }
            }

            let git = exec('git fetch && git status && git log origin -1 --format=oneline', {
                cwd: appRoot
            });

            git.stdout.on('data', (data) => {
                checkOutput(data);
                debug.log(data, this, 3);
            });

            git.stderr.on('data', (data) => {
                checkOutput(data);
                debug.log(data, this, 3);
            });

            git.on('exit', (code) => {
                if(code == 0 || code == '0') {
                    debug.log('Done checking for updates', this);
                    resolve(resolveObj);
                } else {
                    debug.log('Checking for updates failed', this);
                    reject(new Error('git exited with status code ' + code));
                }
            });
        });
    }

    /**
     * Perform update
     *
     * @returns {Promise} Status info
     */
    static update() {
        return new Promise((resolve, reject) => {
            debug.log('Updating HashBrown...', this);

            let git = exec('git pull', {
                cwd: appRoot
            });

            git.stdout.on('data', (data) => {
                debug.log(data, this, 3);
            });

            git.stderr.on('data', (data) => {
                debug.log(data, this, 3);
            });

            git.on('exit', (code) => {
                if(code == 0 || code == '0') {
                    debug.log('Update successful', this);
                    resolve();
                } else {
                    debug.log('Update failed', this);
                    reject(new Error('git exited with status code ' + code));
                }
            });
        })
        .then(() => {
            return UpdateHelper.installDependencies();
        });
    }

    /**
     * Install dependencies
     *
     * @returns {Promise} Status info
     */
    static installDependencies() {
        return new Promise((resolve, reject) => {
            debug.log('Installing dependencies...', this);

            let npm = exec('npm install', {
                cwd: appRoot
            });

            npm.stdout.on('data', (data) => {
                debug.log(data, this, 3);
            });

            npm.stderr.on('data', (data) => {
                debug.log(data, this, 3);
            });

            npm.on('exit', (code) => {
                if(code == 0 || code == '0') {
                    debug.log('Install successful', this);
                    resolve();
                } else {
                    debug.log('Update failed', this);
                    reject(new Error('npm exited with status code ' + code));
                }
            });
        });
    }
}

module.exports = UpdateHelper;
<|endoftext|>


<commit_before>
Util.Form.customLabelStyle["inject"] = function(iN) {
	// u.bug("customLabelStyle:inject", iN);

	// some inputs cannot have labels injected
	// textarea has no type
	if(!iN.type || !iN.type.match(/file|radio|checkbox/)) {

		// store default value
		iN.default_value = u.text(iN.label);

		// add default handlers to focus and blur events
		u.e.addEvent(iN, "focus", u.f._changed_state);
		u.e.addEvent(iN, "blur", u.f._changed_state);

		// Create psydo label for inputs that can't easily show label value
		// Did experiments with with field replacement, but required too much work
		// replacing event and references (this seems to provide sufficient backup)
		if(iN.type.match(/number|integer|password|datetime|date/)) {

			iN.pseudolabel = u.ae(iN.parentNode, "span", {"class":"pseudolabel", "html":iN.default_value});
			iN.pseudolabel.iN = iN;

			// position on top of input
			u.as(iN.pseudolabel, "top", iN.offsetTop+"px");
			u.as(iN.pseudolabel, "left", iN.offsetLeft+"px");
			// create event to remove pseudolabel
			u.ce(iN.pseudolabel)
			iN.pseudolabel.inputStarted = function(event) {
				u.e.kill(event);
				this.iN.focus();
			}

		}

		u.f.updateDefaultState(iN);

	}

}

// internal focus/blur handler for default value controller - attatched to inputs
u.f._changed_state = function() {
	// u.bug("this._default_value_focus:", this);

	u.f.updateDefaultState(this);
}

// update default state on input
u.f.updateDefaultState = function(iN) {
	// u.bug("updateDefaultState for:", iN, iN.is_focused);

	// is input focused
	if(iN.is_focused || iN.val() !== "") {

		// leave default state
		u.rc(iN, "default");
		if(obj(iN.field.virtual_input)) {
			u.rc(iN.field.virtual_input, "default");
		}

		// remove default value if field does not have value
		if(iN.val() === "") {
			iN.val("");
		}

	}
	// input does not have focus - consider dafault value
	else {

		// only set default value if input is empty
		if(iN.val() === "") {

			// add class to indicate default value
			u.ac(iN, "default");
			if(obj(iN.field.virtual_input)) {
				u.ac(iN.field.virtual_input, "default");
			}
			iN.val(iN.default_value);

		}
	}
}
<commit_msg>Add virtual input support<commit_after>
Util.Form.customLabelStyle["inject"] = function(iN) {
	// u.bug("customLabelStyle:inject", iN, iN.type);

	// some inputs cannot have labels injected
	// textarea has no type
	if(!iN.type || !iN.type.match(/file|radio|checkbox/)) {

		// store default value
		iN.default_value = u.text(iN.label);

		// add default handlers to focus and blur events
		u.e.addEvent(iN, "focus", u.f._changed_state);
		u.e.addEvent(iN, "blur", u.f._changed_state);
		u.e.addEvent(iN, "change", u.f._changed_state);

		// Create psydo label for inputs that can't easily show label value
		// Did experiments with with field replacement, but required too much work
		// replacing event and references (this seems to provide sufficient backup)
		if(iN.type.match(/number|integer|password|datetime|date/)) {

			iN.pseudolabel = u.ae(iN.parentNode, "span", {"class":"pseudolabel", "html":iN.default_value});
			iN.pseudolabel.iN = iN;

			// position on top of input
			u.as(iN.pseudolabel, "top", iN.offsetTop+"px");
			u.as(iN.pseudolabel, "left", iN.offsetLeft+"px");
			// create event to remove pseudolabel
			u.ce(iN.pseudolabel)
			iN.pseudolabel.inputStarted = function(event) {
				u.e.kill(event);
				this.iN.focus();
			}

		}

		u.f.updateDefaultState(iN);

	}

}

// internal focus/blur handler for default value controller - attatched to inputs
u.f._changed_state = function() {
	// u.bug("this._default_value_focus:", this);

	u.f.updateDefaultState(this);
}

// update default state on input
u.f.updateDefaultState = function(iN) {
	// u.bug("updateDefaultState for:", iN, iN.is_focused);

	// is input focused
	if(iN.is_focused || iN.val() !== "") {

		// leave default state
		u.rc(iN, "default");
		if(iN.field.virtual_input) {
			u.rc(iN.field.virtual_input, "default");
		}

		// remove default value if field does not have value
		if(iN.val() === "") {
			iN.val("");
		}

	}
	// input does not have focus - consider dafault value
	else {

		// only set default value if input is empty
		if(iN.val() === "") {

			// add class to indicate default value
			u.ac(iN, "default");
			if(obj(iN.field.virtual_input)) {
				u.ac(iN.field.virtual_input, "default");
			}
			iN.val(iN.default_value);

		}
	}
}
<|endoftext|>