<commit_before>/** @jsx React.DOM */
define([
    'underscore', 'jquery', 'react', 'kendo',
    'wingspan-forms/util/debug',
    'wingspan-forms/ControlCommon',
    'wingspan-forms/ImmutableOptimizations'
], function (_, $, React, kendo, debug, ControlCommon, ImmutableOptimizations) {
    'use strict';

    // (AHG) This "turns off" behavior in the kendo.ui.Select widget that selects the first option when refreshing the
    // combo with new data. When the widget has no value set, this causes a value change without firing an event.
    // We don't want the combo selection to change unless the user makes the change
    kendo.ui.ComboBox.fn._options = function (data, optionLabel) {
        var selectedIndex = this.element[0].selectedIndex;

        kendo.ui.Select.fn._options.call(this, data, optionLabel);
        this.element[0].selectedIndex = selectedIndex;
    };

    function getDisplayValue(value, displayField) {
        return _.isObject(value) ? value[displayField] : '';
    }

    function setComboValue(comboWidget, props) {
        var value = props.value;

        // (AHG) If we have an object (with both display text and value), set both the text and the value. The value needs to be set
        // so the widget knows if the value changes and needs to fire the event. The text needs to be set in case the store has no
        // data.
        if (_.isObject(value)) {
            comboWidget.value(value[props.valueField]);
            comboWidget.text(value[props.displayField]);
        }
        else if (value !== undefined) {
            comboWidget.value(value);
        }
    }

    var KendoComboBox = React.createClass({
        mixins: [ImmutableOptimizations],

        fieldClass: 'formFieldCombobox',

        getDefaultProps: function () {
            return {
                value: undefined,
                onChange: $.noop,
                id: undefined,
                displayField: undefined,
                valueField: undefined,
                dataSource: undefined,
                template: undefined,
                filter: 'startswith',
                width: null, // use default width whatever that is...
                disabled: false,
                readonly: false,
                noControl: false,
                placeholderText: ''
            };
        },

        componentWillMount: function () {
            debug.verify(this.props.displayField);
            debug.verify(this.props.valueField);

            if (!this.props.noControl) {
                debug.verify(this.props.dataSource);
            }
        },

        /*jshint ignore:start */
        render: function () {
            return (this.props.noControl
                ? ({getDisplayValue(this.props.value, this.props.displayField)})
                : (<select id={this.props.id}/>));
        },
        /*jshint ignore:end */

        componentDidMount: function (rootNode) {
            var $el = $(rootNode),
                props = this.props;

            if (props.noControl) {
                // If the value is just an ID, we need to fetch data from the server to get the display value.
                if (!_.isObject(props.value)) {
                    // However, if the ID is the display value, we can use it as is.
                    if (props.valueField === props.displayField) {
                        $el.text(props.value);
                        return;
                    }
                    props.dataSource.fetch().then(function () {
                        $el.text(getDisplayValue(props.dataSource.get(props.value), props.displayField));
                    }).done();
                }
                return;
            }

            if (props.width) {
                $el.width(props.width);
            }
            $el.kendoComboBox({
                autoBind: false,
                filter: this.props.filter,
                highlightFirst: false,
                dataTextField: props.displayField,
                dataValueField: props.valueField,
                dataSource: props.dataSource,
                placeholder: props.placeholderText,
                template: props.template,
                change: this.onChange
            });

            setComboValue($el.data('kendoComboBox'), props);
            ControlCommon.setKendoDisabledReadonly($el.data('kendoComboBox'), props.disabled, props.readonly);
        },

        componentWillUnmount: function () {
            var kendoWidget = $(this.getDOMNode()).data('kendoComboBox');

            if (kendoWidget) {
                kendoWidget.destroy();
            }
        },

        componentWillReceiveProps: function (nextProps) {
            var cantChange = ['template', 'valueField', 'displayField', 'placeholderText', 'filter'];
            debug.verify(_.isEqual(_.pick(nextProps, cantChange), _.pick(this.props, cantChange)), 'these props cant change after mount');
        },

        componentDidUpdate: function (prevProps, prevState, rootNode) {
            debug.verify(!!rootNode);

            if (this.props.noControl) {
                // nothing left to do - it was all done in JSX
                return;
            }

            var kendoWidget = $(rootNode).data('kendoComboBox');

            if (prevProps.dataSource !== this.props.dataSource) {
                kendoWidget.setDataSource(this.props.dataSource);
                //AHG: Well, this is needed to workaround some odd behavior in the kendo refresh() method,
                // specifically the call to _selectItem(). That causes a surprise change in value without firing the change event.
                // It's important to refresh() the widget _before_ updating the value, because the _selectItem in refresh() changes the value.
                kendoWidget.refresh();
            }

            setComboValue(kendoWidget, this.props);
            ControlCommon.setKendoDisabledReadonly(kendoWidget, this.props.disabled, this.props.readonly);
        },

        onChange: function (event) {
            var model = event.sender.dataItem();

            // pass up the same structure as was originally passed down to us.
            if (_.isString(this.props.value) || _.isNumber(this.props.value)) {
                this.props.onChange((model ? model.get(this.props.valueField) : model));
            } else {
                // Do not return the internal kendo model objects, since they're an implementation detail of the combo/store.
                this.props.onChange((model instanceof kendo.data.Model) ? model.toJSON() : model);
            }
        }
/*
        readFieldFromSelectedValue: function(field) {
            if (!_.isEmpty(this.props.value)) {
                // Not sure where this came from, but you can specify a dataItem function as a value/display
                // property in Kendo, and we use this, at least we use it on the facet combos on the Document
                // Type Picker and Related Document Picker.
                //
                // This part of the code manages the value state of the kendo control in React, and it was running
                // afoul of this mechanism because it didn't know how to look up the value property correctly
                // when using this facility.  We manually implement a compatible lookup procedure to keep everyone
                // happy without writing any code outside of this class.
                if (_.str.endsWith(field, '()')) {
                    var self = this;
                    var functionName = field.substring(0, field.length - 2);
                    var item = _.find(this.props.dataSource.data(), function(item){ return _.isEqual(item.value.toJSON(), self.props.value.value)});
                    return !!item ? item[functionName]() : '';
                } else {
                    return this.props.value[field];
                }
            } else {
                return '';
            }
        }*/
    });

    void getDisplayValue;
    void KendoComboBox;

    return KendoComboBox;
});
<commit_msg>TMF-4739 work area - if documents across many studies, changing documents does not update the study<commit_after>/** @jsx React.DOM */
define([
    'underscore', 'jquery', 'react', 'kendo',
    'wingspan-forms/util/debug',
    'wingspan-forms/ControlCommon',
    'wingspan-forms/ImmutableOptimizations'
], function (_, $, React, kendo, debug, ControlCommon, ImmutableOptimizations) {
    'use strict';

    // (AHG) This "turns off" behavior in the kendo.ui.Select widget that selects the first option when refreshing the
    // combo with new data. When the widget has no value set, this causes a value change without firing an event.
    // We don't want the combo selection to change unless the user makes the change
    kendo.ui.ComboBox.fn._options = function (data, optionLabel) {
        var selectedIndex = this.element[0].selectedIndex;

        kendo.ui.Select.fn._options.call(this, data, optionLabel);
        this.element[0].selectedIndex = selectedIndex;
    };

    function getDisplayValue(value, displayField) {
        return _.isObject(value) ? value[displayField] : '';
    }

    function setComboValue(comboWidget, props) {
        var value = props.value;

        // (AHG) If we have an object (with both display text and value), set both the text and the value. The value needs to be set
        // so the widget knows if the value changes and needs to fire the event. The text needs to be set in case the store has no
        // data.
        if (_.isObject(value)) {
            comboWidget.value(value[props.valueField]);
            comboWidget.text(value[props.displayField]);
        }
        else if (value !== undefined) {
            comboWidget.value(value);
        }
    }

    var KendoComboBox = React.createClass({
        mixins: [ImmutableOptimizations],

        fieldClass: 'formFieldCombobox',

        getDefaultProps: function () {
            return {
                value: undefined,
                onChange: $.noop,
                id: undefined,
                displayField: undefined,
                valueField: undefined,
                dataSource: undefined,
                template: undefined,
                filter: 'startswith',
                width: null, // use default width whatever that is...
                disabled: false,
                readonly: false,
                noControl: false,
                placeholderText: ''
            };
        },

        componentWillMount: function () {
            debug.verify(this.props.displayField);
            debug.verify(this.props.valueField);

            if (!this.props.noControl) {
                debug.verify(this.props.dataSource);
            }
        },

        /*jshint ignore:start */
        render: function () {
            return (this.props.noControl
                ? ({getDisplayValue(this.props.value, this.props.displayField)})
                : (<select id={this.props.id}/>));
        },
        /*jshint ignore:end */

        componentDidMount: function (rootNode) {
            var $el = $(rootNode),
                props = this.props;

            if (props.noControl) {
                this.setNoControlValue($el);
            }
            else {
                if (props.width) {
                    $el.width(props.width);
                }
                $el.kendoComboBox({
                    autoBind: false,
                    filter: this.props.filter,
                    highlightFirst: false,
                    dataTextField: props.displayField,
                    dataValueField: props.valueField,
                    dataSource: props.dataSource,
                    placeholder: props.placeholderText,
                    template: props.template,
                    change: this.onChange
                });

                setComboValue($el.data('kendoComboBox'), props);
                ControlCommon.setKendoDisabledReadonly($el.data('kendoComboBox'), props.disabled, props.readonly);
            }
        },

        componentWillUnmount: function () {
            var kendoWidget = $(this.getDOMNode()).data('kendoComboBox');

            if (kendoWidget) {
                kendoWidget.destroy();
            }
        },

        componentWillReceiveProps: function (nextProps) {
            var cantChange = ['template', 'valueField', 'displayField', 'placeholderText', 'filter'];
            debug.verify(_.isEqual(_.pick(nextProps, cantChange), _.pick(this.props, cantChange)), 'these props cant change after mount');
        },

        componentDidUpdate: function (prevProps, prevState, rootNode) {
            debug.verify(!!rootNode);
            var $el = $(rootNode)

            if (this.props.noControl) {
                this.setNoControlValue($el);
            }
            else {
                var kendoWidget = $el.data('kendoComboBox');

                if (prevProps.dataSource !== this.props.dataSource) {
                    kendoWidget.setDataSource(this.props.dataSource);
                    //AHG: Well, this is needed to workaround some odd behavior in the kendo refresh() method,
                    // specifically the call to _selectItem(). That causes a surprise change in value without firing the change event.
                    // It's important to refresh() the widget _before_ updating the value, because the _selectItem in refresh() changes the value.
                    kendoWidget.refresh();
                }

                setComboValue(kendoWidget, this.props);
                ControlCommon.setKendoDisabledReadonly(kendoWidget, this.props.disabled, this.props.readonly);
            }
        },

        onChange: function (event) {
            var model = event.sender.dataItem();

            // pass up the same structure as was originally passed down to us.
            if (_.isString(this.props.value) || _.isNumber(this.props.value)) {
                this.props.onChange((model ? model.get(this.props.valueField) : model));
            } else {
                // Do not return the internal kendo model objects, since they're an implementation detail of the combo/store.
                this.props.onChange((model instanceof kendo.data.Model) ? model.toJSON() : model);
            }
        },

        setNoControlValue: function ($el) {
            // If the value is just an ID, we need to fetch data from the server to get the display value.
            if (!_.isObject(this.props.value)) {
                // However, if the ID is the display value, we can use it as is.
                if (this.props.valueField === this.props.displayField) {
                    $el.text(this.props.value);
                    return;
                }
                var self = this;
                this.props.dataSource.fetch().then(function () {
                    $el.text(getDisplayValue(self.props.dataSource.get(self.props.value), self.props.displayField));
                }).done();
            }
        }
/*
        readFieldFromSelectedValue: function(field) {
            if (!_.isEmpty(this.props.value)) {
                // Not sure where this came from, but you can specify a dataItem function as a value/display
                // property in Kendo, and we use this, at least we use it on the facet combos on the Document
                // Type Picker and Related Document Picker.
                //
                // This part of the code manages the value state of the kendo control in React, and it was running
                // afoul of this mechanism because it didn't know how to look up the value property correctly
                // when using this facility.  We manually implement a compatible lookup procedure to keep everyone
                // happy without writing any code outside of this class.
                if (_.str.endsWith(field, '()')) {
                    var self = this;
                    var functionName = field.substring(0, field.length - 2);
                    var item = _.find(this.props.dataSource.data(), function(item){ return _.isEqual(item.value.toJSON(), self.props.value.value)});
                    return !!item ? item[functionName]() : '';
                } else {
                    return this.props.value[field];
                }
            } else {
                return '';
            }
        }*/
    });

    void getDisplayValue;
    void KendoComboBox;

    return KendoComboBox;
});
<|endoftext|>


<commit_before>'use strict';

var React          = require('react');
var TestUtils      = require('react/lib/ReactTestUtils');
var CheckboxEditor = require('../CheckboxEditor');

describe('CheckboxEditor', () => {
 var component;
 var testColumn = {
   onRowSelect : function(){}
 };

 describe('Basic tests', () => {
   beforeEach(() => {
     spyOn(testColumn, 'onRowSelect');
     component = TestUtils.renderIntoDocument(<CheckboxEditor
       value={true}
       rowIdx={1}
       column={testColumn}/>);
     });

     it('should create a new CheckboxEditor instance', () => {
       expect(component).toBeDefined();
     });

     it('should be selected if value prop is true', () => {
       var Input = TestUtils.findRenderedDOMComponentWithTag(component, 'input');
       expect(Input.props.checked).toBe(true);
     });

     it('should call onRowSelect with correct RowIdx when checkbox is clicked', () => {
       var Input = TestUtils.findRenderedDOMComponentWithTag(component, 'input');
       TestUtils.Simulate.click(Input.getDOMNode());
       expect(testColumn.onRowSelect).toHaveBeenCalled();
       var fakeEvent = {stopPropagation : function(){}};
       expect(testColumn.onRowSelect.mostRecentCall.args[0]).toEqual(1, fakeEvent);
     });
 });

 describe('Unchecked tests', () => {
   beforeEach(() => {
     component = TestUtils.renderIntoDocument(<CheckboxEditor
       value={false}
       rowIdx={1}
       column={testColumn}/>);
     });

   it('should not be selected if value prop is false', () => {
     var Input = TestUtils.findRenderedDOMComponentWithTag(component, 'input');
     expect(Input.props.checked).toBe(false);
   });
 });

});
<commit_msg>use setProps instead of a new component<commit_after>'use strict';

var React          = require('react');
var TestUtils      = require('react/lib/ReactTestUtils');
var CheckboxEditor = require('../CheckboxEditor');

describe('CheckboxEditor', () => {
 var component;
 var testColumn = {
   onRowSelect : function(){}
 };

 describe('Basic tests', () => {
   beforeEach(() => {
     spyOn(testColumn, 'onRowSelect');
     component = TestUtils.renderIntoDocument(<CheckboxEditor
       value={true}
       rowIdx={1}
       column={testColumn}/>);
     });

     it('should create a new CheckboxEditor instance', () => {
       expect(component).toBeDefined();
     });

     it('should be selected if value prop is true', () => {
       var Input = TestUtils.findRenderedDOMComponentWithTag(component, 'input');
       expect(Input.props.checked).toBe(true);
     });

     it('should call onRowSelect with correct RowIdx when checkbox is clicked', () => {
       var Input = TestUtils.findRenderedDOMComponentWithTag(component, 'input');
       TestUtils.Simulate.click(Input.getDOMNode());
       expect(testColumn.onRowSelect).toHaveBeenCalled();
       var fakeEvent = {stopPropagation : function(){}};
       expect(testColumn.onRowSelect.mostRecentCall.args[0]).toEqual(1, fakeEvent);
     });

     it('should not be selected if value prop is false', () => {
       component.setProps({value: false});
       var Input = TestUtils.findRenderedDOMComponentWithTag(component, 'input');
       expect(Input.props.checked).toBe(false);
     });
 });

});
<|endoftext|>


<commit_before>var request = require('request');
var url = require('url');
var myanmar_tools = require('myanmar-tools');

var ContentService = require('../content-service');
var util = require('util');
var config = require('../../../config/secrets');
var crowdtangle_lists = require('../../../config/crowdtangle_list');

var request = require('request');
var dateFormat = require('dateformat');
var _ = require('underscore');

//options.lastReportDate is passed here through the content-service-factory and will be utilised in calling the api, but its actually only maintained by the parent content service, it is only utilised by the child
var CrowdTangleContentService = function(options) {
	this._baseUrl = config.get().crowdtangle.baseUrl;
	this._pathName = config.get().crowdtangle.pathName;
	this._count = config.get().crowdtangle.count;
	this._language = config.get().crowdtangle.language;
	this._sortParameter = config.get().crowdtangle.sortParam;
	this._apiToken = config.get().crowdtangle.apiToken;
	this._keywords = options.keywords;
	this._listIds = parseInt(options.tags);
	this.zawgyiConvertor = new myanmar_tools.ZawgyiConverter();
	this.zawgyiDetector = new myanmar_tools.ZawgyiDetector();
	this.fetchType = 'pull';
	ContentService.call(this, options); //associates the child service with its parent, which has the notion of lastreportdate
}

util.inherits(CrowdTangleContentService, ContentService);

//this method overwrites the _doFetch method in the content-service (which is the parent class)
//and then inside the content-service, we have a fetch method that is called by the pull-bot and is responsible for emitting (writing) the reports and updating the final value of lastReportDate
//options.maxCount (present but not used, used by the parent content service)
//what about types? -- discuss with Michael
CrowdTangleContentService.prototype._doFetch = function(options, callback) {
	var self = this;
 	//handle errors using process.nextTick
 	// this will depend mostly on the options that we expect to receive, and if there is issue with that

	// if (!this._url) {
	// 	process.nextTick(function() { self.emit('error', new Error('Missing URL')); });
	// 	return callback([]);
	// }

  	//now we need to submit the request
  	this._httpRequest( {url: this._completeUrl(options)}, function(err, res, body) {
	    if (err) {
			self.emit('error', new Error('HTTP error: ' + err.message));
			return callback([]);
		} else if (res.statusCode != 200) {
			self.emit('error', new Error.HTTP(res.statusCode));
			return callback([]);
	    }

	    //if no errors, parse the body..
	    var responses;
	    try {
			responses = JSON.parse(body).result.posts;
			if (!(responses instanceof Array)) {
				self.emit('error', new Error('Wrong data'));
				return callback([]);
			}
	    	// any other error handling wrt the structure of responses
	    } catch (e) {
			self.emit('error', new Error('Parse error: ' + e.message));
			return callback([]);
	    }
	    // the responses will be sorted by the content-service (parent method)
	    // Parse response data and return them.
	    var reportData = responses.map(function(x) { return self._parse(x); });
	    callback(reportData);
  	});
};

CrowdTangleContentService.prototype._httpRequest = function(params, callback) {
	request(params, callback);
};

CrowdTangleContentService.prototype._completeUrl = function() {
	return url.format({
		protocol: 'https',
		hostname: this._baseUrl,
		pathname: this._pathName,
		query: {
			token: this._apiToken,
	    	count: this._count,
	    	language: this._language,
	    	listIds: this._listIds,
	    	searchTerm: this._keywords,
	    	sortBy: this._sortParameter,
	    	startDate: this._lastReportDate,
	    	endDate: null
	  	}
	});
}

CrowdTangleContentService.prototype._parse = function(data) {

	var metadata = {
		sponsor: data.brandedContentSponsor	|| null,
		caption: data.caption || null,
		description: data.description || null,
		title: data.title || null,
		crowdtangleId: data.id || null,
		externalUrl: data.link || null,
		platform: data.platform || null,
		type: data.type || null,
		accountVerified: data.account ? data.account.verified : false,
		accountHandle: data.account ? data.account.handle : null,
		subscriberCount: data.account ? data.account.subscriberCount : 0,
		accountUrl: data.account ? data.account.url : null,
	};
	var text = data.message || data.description || data.title || data.caption || "[No Content]"; //??? need to revisit, what if there is no text? what about youtube case
	//check for encoding. TODO: verify if the current language is Burmese

	if(this.zawgyiDetector.getZawgyiProbability(text) >= config.get().crowdtangle.zawgyiProb){
		text = this.zawgyiConvertor.zawgyiToUnicode(text);
	}

	var author = data.account ? data.account.name || data.account.handle : null;

	if(author !== '' && this.zawgyiDetector.getZawgyiProbability(author) >= config.get().crowdtangle.zawgyiProb){
		author = this.zawgyiConvertor.zawgyiToUnicode(author);
	}

	// This code deals specifically with matching a crowdtangle list to a report's account id
	this.crowdtangle_lists = crowdtangle_lists.crowdtangle_list_account_pairs;
	// If the list is found and matched, then the ct_tag is the list name
	if (this.crowdtangle_lists[data.account.id]) {
		metadata.ct_tag = this.crowdtangle_lists[data.account.id];
	} else {
		// If the list is not found and not matched, make the ct_tag the account.id so we can identify it later.
		metadata.ct_tag = data.account.id;
	}

	return {
	    authoredAt: new Date(data.date) || new Date(),
	    fetchedAt: new Date(),
	    content: text,
	    author: author,
	    metadata: metadata,
	    url: data.postUrl,
	    //_sources: '', //need to get this info from somewhere
	    //_sourceNicknames: ''
  	};
}

CrowdTangleContentService.prototype.reloadSettings = function() {
	this._baseUrl = config.get().crowdtangle.baseUrl;
	this._pathName = config.get().crowdtangle.pathName;
	this._count = config.get().crowdtangle.count;
	this._language = config.get().crowdtangle.language;
};

module.exports = CrowdTangleContentService;<commit_msg>Store CT media url and statistics in database<commit_after>var request = require('request');
var url = require('url');
var myanmar_tools = require('myanmar-tools');

var ContentService = require('../content-service');
var util = require('util');
var config = require('../../../config/secrets');
var crowdtangle_lists = require('../../../config/crowdtangle_list');

var request = require('request');
var dateFormat = require('dateformat');
var _ = require('underscore');

//options.lastReportDate is passed here through the content-service-factory and will be utilised in calling the api, but its actually only maintained by the parent content service, it is only utilised by the child
var CrowdTangleContentService = function(options) {
	this._baseUrl = config.get().crowdtangle.baseUrl;
	this._pathName = config.get().crowdtangle.pathName;
	this._count = config.get().crowdtangle.count;
	this._language = config.get().crowdtangle.language;
	this._sortParameter = config.get().crowdtangle.sortParam;
	this._apiToken = config.get().crowdtangle.apiToken;
	this._keywords = options.keywords;
	this._listIds = parseInt(options.tags);
	this.zawgyiConvertor = new myanmar_tools.ZawgyiConverter();
	this.zawgyiDetector = new myanmar_tools.ZawgyiDetector();
	this.fetchType = 'pull';
	ContentService.call(this, options); //associates the child service with its parent, which has the notion of lastreportdate
}

util.inherits(CrowdTangleContentService, ContentService);

//this method overwrites the _doFetch method in the content-service (which is the parent class)
//and then inside the content-service, we have a fetch method that is called by the pull-bot and is responsible for emitting (writing) the reports and updating the final value of lastReportDate
//options.maxCount (present but not used, used by the parent content service)
//what about types? -- discuss with Michael
CrowdTangleContentService.prototype._doFetch = function(options, callback) {
	var self = this;
 	//handle errors using process.nextTick
 	// this will depend mostly on the options that we expect to receive, and if there is issue with that

	// if (!this._url) {
	// 	process.nextTick(function() { self.emit('error', new Error('Missing URL')); });
	// 	return callback([]);
	// }

  	//now we need to submit the request
  	this._httpRequest( {url: this._completeUrl(options)}, function(err, res, body) {
	    if (err) {
			self.emit('error', new Error('HTTP error: ' + err.message));
			return callback([]);
		} else if (res.statusCode != 200) {
			self.emit('error', new Error.HTTP(res.statusCode));
			return callback([]);
	    }

	    //if no errors, parse the body..
	    var responses;
	    try {
			responses = JSON.parse(body).result.posts;
			if (!(responses instanceof Array)) {
				self.emit('error', new Error('Wrong data'));
				return callback([]);
			}
	    	// any other error handling wrt the structure of responses
	    } catch (e) {
			self.emit('error', new Error('Parse error: ' + e.message));
			return callback([]);
	    }
	    // the responses will be sorted by the content-service (parent method)
	    // Parse response data and return them.
	    var reportData = responses.map(function(x) { return self._parse(x); });
	    callback(reportData);
  	});
};

CrowdTangleContentService.prototype._httpRequest = function(params, callback) {
	request(params, callback);
};

CrowdTangleContentService.prototype._completeUrl = function() {
	return url.format({
		protocol: 'https',
		hostname: this._baseUrl,
		pathname: this._pathName,
		query: {
			token: this._apiToken,
	    	count: this._count,
	    	language: this._language,
	    	listIds: this._listIds,
	    	searchTerm: this._keywords,
	    	sortBy: this._sortParameter,
	    	startDate: this._lastReportDate,
	    	endDate: null
	  	}
	});
}

CrowdTangleContentService.prototype._parse = function(data) {

	var metadata = {
		sponsor: data.brandedContentSponsor	|| null,
		caption: data.caption || null,
		description: data.description || null,
		title: data.title || null,
		crowdtangleId: data.id || null,
		externalUrl: data.link || null,
		platform: data.platform || null,
		type: data.type || null,
		accountVerified: data.account ? data.account.verified : false,
		accountHandle: data.account ? data.account.handle : null,
		subscriberCount: data.account ? data.account.subscriberCount : 0,
		accountUrl: data.account ? data.account.url : null,
		mediaUrl: data.media? data.media.map(function(medium) {
			return {type: medium.type, url: medium.url}
		}) : null,
		actualStatistics: data.statistics.actual,
		expectedStatistics: data.statistics.expected
	};
	var text = data.message || data.description || data.title || data.caption || "[No Content]"; //??? need to revisit, what if there is no text? what about youtube case
	//check for encoding. TODO: verify if the current language is Burmese

	if(this.zawgyiDetector.getZawgyiProbability(text) >= config.get().crowdtangle.zawgyiProb){
		text = this.zawgyiConvertor.zawgyiToUnicode(text);
	}

	var author = data.account ? data.account.name || data.account.handle : null;

	if(author !== '' && this.zawgyiDetector.getZawgyiProbability(author) >= config.get().crowdtangle.zawgyiProb){
		author = this.zawgyiConvertor.zawgyiToUnicode(author);
	}

	// This code deals specifically with matching a crowdtangle list to a report's account id
	this.crowdtangle_lists = crowdtangle_lists.crowdtangle_list_account_pairs;
	// If the list is found and matched, then the ct_tag is the list name
	if (this.crowdtangle_lists[data.account.id]) {
		metadata.ct_tag = this.crowdtangle_lists[data.account.id];
	} else {
		// If the list is not found and not matched, make the ct_tag the account.id so we can identify it later.
		metadata.ct_tag = data.account.id;
	}

	return {
	    authoredAt: new Date(data.date) || new Date(),
	    fetchedAt: new Date(),
	    content: text,
	    author: author,
	    metadata: metadata,
	    url: data.postUrl,
	    //_sources: '', //need to get this info from somewhere
	    //_sourceNicknames: ''
  	};
}

CrowdTangleContentService.prototype.reloadSettings = function() {
	this._baseUrl = config.get().crowdtangle.baseUrl;
	this._pathName = config.get().crowdtangle.pathName;
	this._count = config.get().crowdtangle.count;
	this._language = config.get().crowdtangle.language;
};

module.exports = CrowdTangleContentService;<|endoftext|>


<commit_before>/*jslint node: true */
const pathresolver = require('angular-filemanager-nodejs-bridge').pathresolver;
const path = require('path');

pathresolver.baseDir = function(req) {

  if(!req.user || !req.user.username || !Array.isArray(req.user.global_roles)) {
    throw new Error("No valid user!");
  }

  // Admin users can see all the directories
  if(req.user.global_roles.indexOf('admin') >= 0) {
    return process.env.DATA_DIR;
  }

  // Other users can only see their own directory
  return path.join(process.env.DATA_DIR, req.user.username);
};
<commit_msg>Create user directories if they do not exist<commit_after>/*jslint node: true */
const pathresolver = require('angular-filemanager-nodejs-bridge').pathresolver;
const path = require('path');
const fs = require('fs.extra');

pathresolver.baseDir = function(req) {

  if(!req.user || !req.user.username || !Array.isArray(req.user.global_roles)) {
    throw new Error("No valid user!");
  }

  // Admin users can see all the directories
  if(req.user.global_roles.indexOf('admin') >= 0) {
    return process.env.DATA_DIR;
  }

  // Other users can only see their own directory
  var baseDir = path.join(process.env.DATA_DIR, req.user.username);

  // Create the directory if it does not exist already
  fs.mkdirpSync(baseDir);

  return baseDir;
};
<|endoftext|>


<commit_before>export default function add(a, b) {
  let i = a.length - 1
  let j = b.length -1

  let carry = 0 // 进位
  let ret = ''

  while (i >= 0 || j >= 0) {
    let x = 0 // a 位数上的值
    let y = 0 // b 位数上的值
    let sum

    if (i >= 0) {
      x = a[i] - '0' // 将 string 转换成 number
      i --
    }

    if (j >= 0) {
      y = b[j] - '0'
      j --
    }

    sum = x + y + carry
    if (sum >= 10) {
      carry = 1
      sum -= 10
    } else {
      carry = 0
    }

    ret = sum + ret // 字符串拼接
  }

  if (carry) {
    ret = carry + ret
  }

  return ret
}<commit_msg>feat: add 骚版本<commit_after>/*
 * @Author: Ge.Yuanjun
 * @Date: 2020-04-07 01:21:44
 * @Last Modified by: Ge.Yuanjun
 * @Last Modified time: 2020-04-07 01:26:11
 */

// 简洁版
export default function add(a, b) {
  let i = a.length - 1
  let j = b.length -1

  let carry = 0 // 进位
  let ret = ''

  while (i >= 0 || j >= 0) {
    let x = 0 // a 位数上的值
    let y = 0 // b 位数上的值
    let sum

    if (i >= 0) {
      x = a[i] - '0' // 将 string 转换成 number
      i --
    }

    if (j >= 0) {
      y = b[j] - '0'
      j --
    }

    sum = x + y + carry
    if (sum >= 10) {
      carry = 1
      sum -= 10
    } else {
      carry = 0
    }

    ret = sum + ret // 字符串拼接
  }

  if (carry) {
    ret = carry + ret
  }

  return ret
}

/**
 * 骚操作版本
 * @param {*} a
 * @param {*} b
 */
export function largeNumSum (a, b) {
  let c = 0
  let res = ''
  a = a.split()
  b = b.split()

  while (a.length || b.length || c) {
    c += ~~a.pop() + ~~b.pop() // boolean 型变量c会存在类型转换（true -> 1, false -> 0）
    res += c % 10
    c = c > 9 // c 在此处重置未 boolean 型变量
  }
}<|endoftext|>


<commit_before>const Discord = require('discord.js');
const { ChannelProcessor } = require('./channelProcessor');
const {discordApiToken } = require('./secrets.js');
const client = new Discord.Client();

const channelProcessorMap = {};

client.on('ready', () => {
  console.log('Bot is ready');
});

client.on('message', (message) => {
  const channel = message.channel;
  const channelID = message.channel.id;

  if(channelProcessorMap[channelID] == null) {
    const newChannelProcessor =  new ChannelProcessor(channelID);
    newChannelProcessor.setOnHaikuFunction((haiku) => {
      channel.send(
      `${haiku.author} has created a beautiful Haiku!
      ${haiku.lines[0]}
      ${haiku.lines[1]}
      ${haiku.lines[2]}`);
    });
    channelProcessorMap[channelID] = newChannelProcessor;
  }

  channelProcessorMap[channelID].processMessage(message);
});

client.login(discordApiToken);
<commit_msg>Add console logging upon haiku trigger<commit_after>const Discord = require('discord.js');
const { ChannelProcessor } = require('./channelProcessor');
const {discordApiToken } = require('./secrets.js');
const client = new Discord.Client();

const channelProcessorMap = {};

client.on('ready', () => {
  console.log('Bot is ready');
});

client.on('message', (message) => {
  const channel = message.channel;
  const channelID = message.channel.id;

  if(channelProcessorMap[channelID] == null) {
    const newChannelProcessor =  new ChannelProcessor(channelID);
    newChannelProcessor.setOnHaikuFunction((haiku) => {
      console.log(
        `Haiku triggered:
        author: ${haiku.author}
        lines: ${haiku.lines}`);
      channel.send(
        `${haiku.author} has created a beautiful Haiku!
        ${haiku.lines[0]}
        ${haiku.lines[1]}
        ${haiku.lines[2]}`);
    });
    channelProcessorMap[channelID] = newChannelProcessor;
  }

  channelProcessorMap[channelID].processMessage(message);
});

client.login(discordApiToken);
<|endoftext|>


<commit_before><commit_msg>Grocery list<commit_after>// Pseudocode
// Create a new list
// Add an item with a quantity to the list
// Remove an item from the list
// Update quantities for items in your list
// Print the list

// Initial Solution
var groceryList = {};

function add_item(item, qty){
  if (groceryList[item] > 0)
    return groceryList[item] += qty;
  else
    return groceryList[item] = qty;
}

add_item('meat', 1);
add_item('milk', 2);
add_item('apple', 6);
console.log(groceryList);
add_item('meat', 1);
console.log(groceryList);

function remove_item(item, qty){
    if (groceryList[item] > 1)
        groceryList[item] -= qty;
    else if (groceryList[item] === 1)
        delete groceryList[item];
 }

remove_item('apple', 2);
remove_item('meat', 1);
console.log(groceryList);

function show_list(){
  for (var item in groceryList){
    console.log("Item: "+item+", Qty: "+groceryList[item]);
  }
}

show_list()

/* Reflection
1. What concepts did you solidify in working on this challenge? (reviewing the passing of information, objects, constructors, etc.)
Creating objects, checking if a key exists, maniputlating properties of objects, iterating over key-value pairs.
2. What was the most difficult part of this challenge?
I intended to implement soritng in alphabetic order but it appeared to be rather long.
3. Did an array or object make more sense to use and why?
I used an object because the item name is related to and paired with the quantity.
*/<|endoftext|>


<commit_before>"use strict";

const EOF                  = Symbol.for('EOF');
const WHITESPACE           = Symbol.for('WHITESPACE');
const COLON                = Symbol.for('COLON');
const SEMICOLON            = Symbol.for('SEMICOLON');
const COMMA                = Symbol.for('COMMA');
const OPEN_SQUARE_BRACKET  = Symbol.for('OPEN_SQUARE_BRACKET');
const CLOSE_SQUARE_BRACKET = Symbol.for('CLOSE_SQUARE_BRACKET');
const OPEN_CURLY_BRACKET   = Symbol.for('OPEN_CURLY_BRACKET');
const CLOSE_CURLY_BRACKET  = Symbol.for('CLOSE_CURLY_BRACKET');
const OPEN_PARENTHESIS     = Symbol.for('OPEN_PARENTHESIS');
const CLOSE_PARENTHESIS    = Symbol.for('CLOSE_PARENTHESIS');
const AT                   = Symbol.for('AT');
const EQUALS               = Symbol.for('EQUALS');
const DOLLAR               = Symbol.for('DOLLAR');
const PLUS                 = Symbol.for('PLUS');
const MINUS                = Symbol.for('MINUS');
const STOP                 = Symbol.for('STOP');
const LESS_THAN            = Symbol.for('LESS_THAN');
const GREATER_THAN         = Symbol.for('GREATER_THAN');
const ASTERISK             = Symbol.for('ASTERISK');
const VERTICAL_LINE        = Symbol.for('VERTICAL_LINE');
const TILDE                = Symbol.for('TILDE');
const COMMENT              = Symbol.for('COMMENT');
const NUMBER               = Symbol.for('NUMBER');
const PERCENTAGE           = Symbol.for('PERCENTAGE');
const STRING               = Symbol.for('STRING');
const HASH                 = Symbol.for('HASH');
const NAME                 = Symbol.for('NAME');
const EXCLAMATION          = Symbol.for('EXCLAMATION');
const AMPERSAND            = Symbol.for('AMPERSAND');
const SOLIDUS              = Symbol.for('SOLIDUS');
const CARET                = Symbol.for('CARET');
const UNKNOWN              = Symbol.for('UNKNOWN');

var specialChars = new Map();

specialChars.set(':', COLON);
specialChars.set(';', SEMICOLON);
specialChars.set(',', COMMA);
specialChars.set('[', OPEN_SQUARE_BRACKET);
specialChars.set(']', CLOSE_SQUARE_BRACKET);
specialChars.set('{', OPEN_CURLY_BRACKET);
specialChars.set('}', CLOSE_CURLY_BRACKET);
specialChars.set('(', OPEN_PARENTHESIS);
specialChars.set(')', CLOSE_PARENTHESIS);
specialChars.set('@', AT);
specialChars.set('=', EQUALS);
specialChars.set('$', DOLLAR);
specialChars.set('+', PLUS);
specialChars.set('-', MINUS);
specialChars.set('.', STOP);
specialChars.set('<', LESS_THAN);
specialChars.set('>', GREATER_THAN);
specialChars.set('*', ASTERISK);
specialChars.set('|', VERTICAL_LINE);
specialChars.set('~', TILDE);
specialChars.set('%', PERCENTAGE);
specialChars.set('!', EXCLAMATION);
specialChars.set('&', AMPERSAND);
specialChars.set('/', SOLIDUS);
specialChars.set('^', CARET);

var stylecow = require('./index');

stylecow.Tokens = class {
    constructor (code) {
        this.code = code;

        this.currChar;
        this.nextChar;

        this.pos = 0;
        this.col = 0;
        this.line = 1;

        this.next();
        this.next();
        this.col = 0;
    }

    next () {
        this.currChar = this.nextChar;
        this.nextChar = this.nextNextChar;
        this.nextNextChar = this.code.charAt(this.pos++);

        if (this.currChar === '\n') {
            ++this.line;
            this.col = 0;
        } else if (this.currChar === '') {
            return false;
        } else {
            ++this.col;
        }

        //handle breaklines
        if (this.nextNextChar === '\r') {
            if (this.code.charAt(this.pos + 1) === '\n') {
                ++this.pos;
            }

            this.nextNextChar = '\n';
        } else if (this.nextNextChar === '\f') {
            this.nextNextChar = '\n';
        }

        return true;
    }

    get () {
        this.next();

        return this.WHITESPACE()
            || this.HASH()
            || this.COMMENT()
            || this.STRING()
            || this.NUMBER()
            || this.NAME()
            || this.SPECIAL_CHAR()
            || this.END();
    }

    END () {
        if (this.currChar === '') {
            return [EOF, this.line, this.col];
        }

        return [UNKNOWN, this.line, this.col, this.currChar];
        console.error('Unespected character "' + this.currChar + '" (' + this.currChar.charCodeAt(0) + ') in line: ' + this.line + ' column: ' + this.col);
    }

    WHITESPACE () {
        if (this.currChar === ' ' || this.currChar === '\t' || this.currChar === '\n') {
            while (this.nextChar === ' ' || this.nextChar === '\t' || this.nextChar === '\n') {
                this.next();
            }

            return [WHITESPACE, this.line, this.col, ' '];
        }
    }

    SPECIAL_CHAR () {
        if (specialChars.has(this.currChar)) {
            return [specialChars.get(this.currChar), this.line, this.col, this.currChar];
        }
    }

    COMMENT () {
        if (this.currChar === '/' && this.nextChar === '*') {
            var token = [COMMENT, this.line, this.col, ''];

            this.next();

            while (this.next() && (this.currChar !== '*' || this.nextChar !== '/')) {
                token[3] += this.currChar;
            }

            this.next();

            return token;
        }
    }

    NUMBER () {
        if (
            isDigit(this.currChar)
             || ((this.currChar === '-') && ((this.nextChar === '.' && isDigit(this.nextNextChar)) || isDigit(this.nextChar)))
             || ((this.currChar === '.') && isDigit(this.nextChar))
        ) {
            var token = [NUMBER, this.line, this.col, this.currChar];

            while ((this.nextChar === '.' || isDigit(this.nextChar)) && this.next()) {
                token[3] += this.currChar;
            }

            //normalize "(+|-).n"
            if (token[3][0] === '.') {
                token[3] = '0' + token[3];
            } else if ((token[3][0] === '+' || token[3][0] === '-') && token[3][1] === '.') {
                token[3] = token[3][0] + '0' + token[3].substr(1);
            }

            //convert to float
            token[3] = parseFloat(token[3]);

            return token;
        }
    }

    STRING () {
        if (this.currChar === '"' || this.currChar === "'") {
            var quote = this.currChar;
            var token = [STRING, this.line, this.col, ''];

            while (this.next() && this.currChar !== quote) {
                if (this.currChar === '\\') {
                    if (this.nextChar === quote) {
                        this.next();
                    } if (this.nextChar === '\\') {
                        token[3] += this.currChar;
                        this.next();
                    }
                }

                token[3] += this.currChar;
            }

            return token;
        }
    }

    NAME () {
        if (isEscaped(this.currChar, this.nextChar) || (isName(this.currChar) && (this.currChar !== '-' || (isName(this.nextChar) && !isDigit(this.nextChar))))) {
            var token = [NAME, this.line, this.col, this.currChar];

            if (this.currChar === '\\') {
                this.next()
                token[3] += this.currChar;
            }

            while ((isEscaped(this.nextChar, this.nextNextChar) || isName(this.nextChar)) && this.next()) {
                token[3] += this.currChar;

                if (this.currChar === '\\') {
                    this.next()
                    token[3] += this.currChar;
                }
            }

            return token;
        }
    }

    HASH () {
        if (this.currChar === '#') {
            var token = [HASH, this.line, this.col, ''];

            while ((isEscaped(this.nextChar, this.nextNextChar) || isName(this.nextChar)) && this.next()) {
                token[3] += this.currChar;

                if (this.currChar === '\\') {
                    this.next()
                    token[3] += this.currChar;
                }
            }

            return token;
        }
    }
};

function isName(code) {
    var n = code.charCodeAt(0);

    return n === 0x2d                //-
        || n === 0x5f                //_
        || (n >= 0x30 && n <= 0x39)  //digit
        || (n >= 0x41 && n <= 0x5a)  //uppercase
        || (n >= 0x61 && n <= 0x7a)  //lowercase
        || (n >= 0x80); //nonascii
}

function isDigit(code) {
    var n = code.charCodeAt(0);

    return n >= 0x30 && n <= 0x39;
}

function isLetter(code) {
    var n = code.charCodeAt(0);

    //upper or lowercase
    return (n >= 0x41 && n <= 0x5a) || (n >= 0x61 && n <= 0x7a);
}

function isEscaped(code1, code2) {
    if (code1 !== '\\') {
        return false;
    }

    if (code2 === '\n') {
        return false;
    }

    return true;
}
<commit_msg>removed unused code<commit_after>"use strict";

const EOF                  = Symbol.for('EOF');
const WHITESPACE           = Symbol.for('WHITESPACE');
const COLON                = Symbol.for('COLON');
const SEMICOLON            = Symbol.for('SEMICOLON');
const COMMA                = Symbol.for('COMMA');
const OPEN_SQUARE_BRACKET  = Symbol.for('OPEN_SQUARE_BRACKET');
const CLOSE_SQUARE_BRACKET = Symbol.for('CLOSE_SQUARE_BRACKET');
const OPEN_CURLY_BRACKET   = Symbol.for('OPEN_CURLY_BRACKET');
const CLOSE_CURLY_BRACKET  = Symbol.for('CLOSE_CURLY_BRACKET');
const OPEN_PARENTHESIS     = Symbol.for('OPEN_PARENTHESIS');
const CLOSE_PARENTHESIS    = Symbol.for('CLOSE_PARENTHESIS');
const AT                   = Symbol.for('AT');
const EQUALS               = Symbol.for('EQUALS');
const DOLLAR               = Symbol.for('DOLLAR');
const PLUS                 = Symbol.for('PLUS');
const MINUS                = Symbol.for('MINUS');
const STOP                 = Symbol.for('STOP');
const LESS_THAN            = Symbol.for('LESS_THAN');
const GREATER_THAN         = Symbol.for('GREATER_THAN');
const ASTERISK             = Symbol.for('ASTERISK');
const VERTICAL_LINE        = Symbol.for('VERTICAL_LINE');
const TILDE                = Symbol.for('TILDE');
const COMMENT              = Symbol.for('COMMENT');
const NUMBER               = Symbol.for('NUMBER');
const PERCENTAGE           = Symbol.for('PERCENTAGE');
const STRING               = Symbol.for('STRING');
const HASH                 = Symbol.for('HASH');
const NAME                 = Symbol.for('NAME');
const EXCLAMATION          = Symbol.for('EXCLAMATION');
const AMPERSAND            = Symbol.for('AMPERSAND');
const SOLIDUS              = Symbol.for('SOLIDUS');
const CARET                = Symbol.for('CARET');
const UNKNOWN              = Symbol.for('UNKNOWN');

var specialChars = new Map();

specialChars.set(':', COLON);
specialChars.set(';', SEMICOLON);
specialChars.set(',', COMMA);
specialChars.set('[', OPEN_SQUARE_BRACKET);
specialChars.set(']', CLOSE_SQUARE_BRACKET);
specialChars.set('{', OPEN_CURLY_BRACKET);
specialChars.set('}', CLOSE_CURLY_BRACKET);
specialChars.set('(', OPEN_PARENTHESIS);
specialChars.set(')', CLOSE_PARENTHESIS);
specialChars.set('@', AT);
specialChars.set('=', EQUALS);
specialChars.set('$', DOLLAR);
specialChars.set('+', PLUS);
specialChars.set('-', MINUS);
specialChars.set('.', STOP);
specialChars.set('<', LESS_THAN);
specialChars.set('>', GREATER_THAN);
specialChars.set('*', ASTERISK);
specialChars.set('|', VERTICAL_LINE);
specialChars.set('~', TILDE);
specialChars.set('%', PERCENTAGE);
specialChars.set('!', EXCLAMATION);
specialChars.set('&', AMPERSAND);
specialChars.set('/', SOLIDUS);
specialChars.set('^', CARET);

var stylecow = require('./index');

stylecow.Tokens = class {
    constructor (code) {
        this.code = code;

        this.currChar;
        this.nextChar;

        this.pos = 0;
        this.col = 0;
        this.line = 1;

        this.next();
        this.next();
        this.col = 0;
    }

    next () {
        this.currChar = this.nextChar;
        this.nextChar = this.nextNextChar;
        this.nextNextChar = this.code.charAt(this.pos++);

        if (this.currChar === '\n') {
            ++this.line;
            this.col = 0;
        } else if (this.currChar === '') {
            return false;
        } else {
            ++this.col;
        }

        //handle breaklines
        if (this.nextNextChar === '\r') {
            if (this.code.charAt(this.pos + 1) === '\n') {
                ++this.pos;
            }

            this.nextNextChar = '\n';
        } else if (this.nextNextChar === '\f') {
            this.nextNextChar = '\n';
        }

        return true;
    }

    get () {
        this.next();

        return this.WHITESPACE()
            || this.HASH()
            || this.COMMENT()
            || this.STRING()
            || this.NUMBER()
            || this.NAME()
            || this.SPECIAL_CHAR()
            || this.END();
    }

    END () {
        if (this.currChar === '') {
            return [EOF, this.line, this.col];
        }

        return [UNKNOWN, this.line, this.col, this.currChar];
    }

    WHITESPACE () {
        if (this.currChar === ' ' || this.currChar === '\t' || this.currChar === '\n') {
            while (this.nextChar === ' ' || this.nextChar === '\t' || this.nextChar === '\n') {
                this.next();
            }

            return [WHITESPACE, this.line, this.col, ' '];
        }
    }

    SPECIAL_CHAR () {
        if (specialChars.has(this.currChar)) {
            return [specialChars.get(this.currChar), this.line, this.col, this.currChar];
        }
    }

    COMMENT () {
        if (this.currChar === '/' && this.nextChar === '*') {
            var token = [COMMENT, this.line, this.col, ''];

            this.next();

            while (this.next() && (this.currChar !== '*' || this.nextChar !== '/')) {
                token[3] += this.currChar;
            }

            this.next();

            return token;
        }
    }

    NUMBER () {
        if (
            isDigit(this.currChar)
             || ((this.currChar === '-') && ((this.nextChar === '.' && isDigit(this.nextNextChar)) || isDigit(this.nextChar)))
             || ((this.currChar === '.') && isDigit(this.nextChar))
        ) {
            var token = [NUMBER, this.line, this.col, this.currChar];

            while ((this.nextChar === '.' || isDigit(this.nextChar)) && this.next()) {
                token[3] += this.currChar;
            }

            //normalize "(+|-).n"
            if (token[3][0] === '.') {
                token[3] = '0' + token[3];
            } else if ((token[3][0] === '+' || token[3][0] === '-') && token[3][1] === '.') {
                token[3] = token[3][0] + '0' + token[3].substr(1);
            }

            //convert to float
            token[3] = parseFloat(token[3]);

            return token;
        }
    }

    STRING () {
        if (this.currChar === '"' || this.currChar === "'") {
            var quote = this.currChar;
            var token = [STRING, this.line, this.col, ''];

            while (this.next() && this.currChar !== quote) {
                if (this.currChar === '\\') {
                    if (this.nextChar === quote) {
                        this.next();
                    } if (this.nextChar === '\\') {
                        token[3] += this.currChar;
                        this.next();
                    }
                }

                token[3] += this.currChar;
            }

            return token;
        }
    }

    NAME () {
        if (isEscaped(this.currChar, this.nextChar) || (isName(this.currChar) && (this.currChar !== '-' || (isName(this.nextChar) && !isDigit(this.nextChar))))) {
            var token = [NAME, this.line, this.col, this.currChar];

            if (this.currChar === '\\') {
                this.next()
                token[3] += this.currChar;
            }

            while ((isEscaped(this.nextChar, this.nextNextChar) || isName(this.nextChar)) && this.next()) {
                token[3] += this.currChar;

                if (this.currChar === '\\') {
                    this.next()
                    token[3] += this.currChar;
                }
            }

            return token;
        }
    }

    HASH () {
        if (this.currChar === '#') {
            var token = [HASH, this.line, this.col, ''];

            while ((isEscaped(this.nextChar, this.nextNextChar) || isName(this.nextChar)) && this.next()) {
                token[3] += this.currChar;

                if (this.currChar === '\\') {
                    this.next()
                    token[3] += this.currChar;
                }
            }

            return token;
        }
    }
};

function isName(code) {
    var n = code.charCodeAt(0);

    return n === 0x2d                //-
        || n === 0x5f                //_
        || (n >= 0x30 && n <= 0x39)  //digit
        || (n >= 0x41 && n <= 0x5a)  //uppercase
        || (n >= 0x61 && n <= 0x7a)  //lowercase
        || (n >= 0x80); //nonascii
}

function isDigit(code) {
    var n = code.charCodeAt(0);

    return n >= 0x30 && n <= 0x39;
}

function isLetter(code) {
    var n = code.charCodeAt(0);

    //upper or lowercase
    return (n >= 0x41 && n <= 0x5a) || (n >= 0x61 && n <= 0x7a);
}

function isEscaped(code1, code2) {
    if (code1 !== '\\') {
        return false;
    }

    if (code2 === '\n') {
        return false;
    }

    return true;
}
<|endoftext|>


<commit_before>var util = require('util');
var _    = require('lodash-node');

function V(name, edges)
{
    return {
        name: name         // Name of this Vertice
        ,edges: edges       // adjacent edges
        ,vertices: []       // adjacent vertices
        ,findEdgeTo: function(nodeB)
        {
            for(var edge_idx in this.edges)
            {
                var edge = this.edges[edge_idx];
                if(edge.between[1] === nodeB)
                    return edge;
            }

            return false;
        }
    }
}

function E(weight)
{
    return {
        weight: weight     // weight of edge
        ,between: []        // 2 vertices (the ones this edge connects)
    }
}

function createNetwork(graph)
{
    var vertices = 0;

    // Autodetect number of nodes in our graph
    for(i in graph)
    {
        if(i != 0 && graph[i] == Infinity)
            break;

        if(graph[i] === Infinity) vertices = 0;
        else                vertices++
    }

    // Create those nodes
    var verticeArray = [];
    for(var i = 0; i < vertices; i++)
    {
        var edges = [];
        for(var j = 0; j < vertices; j++)
        {
            var edgeValue = graph.shift();
            edges.push(E(edgeValue));
        }
        verticeArray.push(V('Node' + (i + 1), edges));
    }

    // Wire the nodes
    for(var oV in verticeArray)
    {
        for(var iV in verticeArray)
        {
            verticeArray[oV].vertices.push(verticeArray[iV]);
        }
    }

    // wire edges
    for(var v in verticeArray)
    {
        for(e in verticeArray[v].edges)
        {
            verticeArray[v].edges[e].between = [
                verticeArray[v],
                verticeArray[e]
            ];
        }
    }

    // NOTE: weight Infinity indicates an edge to the vertice itself (loop in this "state")
    // which is, at least for TSM, is an invalid path, thats why its Infinitive...
    // so it will never be seen as possible way

    return verticeArray;
}

function calculatePathCosts(s)
{
    var cost = 0;
    for(var node_idx = 0; node_idx < s.length; node_idx++)
    {
        var node = s[node_idx];
        var nextNode = s[node_idx+1];

        if(nextNode !== undefined)
        {
            cost += node.findEdgeTo(nextNode).weight;
        }
    }
    return cost;
}

/**
 * Solve the TSM-Problem through an iterative backtracking.
 *
 * @param network
 * @param startKnoten
 */
function TSM(network, startKnoten)
{
    var currentNode = network[startKnoten];   // x = graph.length;
    var s = [];                             // current path                   ; dimension: x
    var v = [[]];                           // visited nodes for each layer   ; dimension: x * x
    var u = [];                             // unvisited nodes                ; dimension: x
    var d = 0;                              // current depth                  ; min: 0 ; max: x

    // Push our root path
    s.push(currentNode);

    while(s.length > 0)
    {
        //u = currentNode.edges - v;
        u = []; // empty unvisited nodes, as we generate them now
        _.forEach(currentNode.edges, function(edge){
            if(edge.weight === Infinity)
                return; // filter impossible edges (loop or cut)

            for(var visitedNode_idx in v[d])
                if(v[d][visitedNode_idx] === edge.between[1])
                    return; // it was already visited

            for(var currentPathNodes_idx in s)
                if(s[currentPathNodes_idx] === edge.between[1])
                    return; // they are our parents, so we visited them already

            u.push(edge.between[1]); // it is not visited
        });

        if(u.length > 0)
        { // traverse unvisited
            v[++d] = []; // reset visited nodes for comming layer

            // get the first unvisited and push onto our path
            currentNode = u.shift();
            s.push(currentNode);
        }
        else
        { // no traversal possible
            // Calculate Pathcosts if we are in a complete path
            if(s.length === network.length)
            {
                var pathCost = calculatePathCosts(s);
                if(pathCost < bestCosts.cost)
                {
                    bestCosts.cost = pathCost;
                    bestCosts.path = _.clone(s);
                }
            }

            if(d > 0)
            {
                v[--d].push(s.pop());
                currentNode = s[s.length-1];
            }else{ // End of tree - finish.
                s.pop(); // we pop our root node, so we are done.
            }
        }

        if(s.length === network.length)
        { // We got a complete path, print it.
            var printPath = [];
            _.forEach(s, function(node){
                printPath.push(node.name);
            });

            console.log('Found Path: ', printPath);
        }
    }
}

var bestCosts = {
    path: null,
    cost: Infinity
};

var G = createNetwork([
    Infinity, 906, 814, 30, 198,
    906, Infinity, 633, 225, 103,
    814, 633, Infinity, 462, 553,
    30, 225, 462, Infinity, 814,
    198, 103, 553, 814, Infinity
]);

// Init our TSM and solve it
TSM(G, 0);

// Print the solution
var printPath = [];
_.forEach(bestCosts.path, function(node){
    printPath.push(node.name);
});
console.log('bestCost: ', bestCosts);
console.log('bestPath: ', printPath);
<commit_msg>Refactored TSM Problem solver.<commit_after>var util = require('util');
var _    = require('lodash-node');

/**
 * Convert a graph representation to a Node-Edge-Node based Network.
 *
 * @param graph
 * @returns {{getNetwork: Function}}
 * @constructor
 */
function GraphNetwork(graph)
{
    /**
     * Representation of an Vertice/Node inside our graph.
     *
     * @param name name of the vertice
     * @param edges adjacent edges
     * @constructor
     */
    function V(name, edges)
    {
        var vertices = []; // adjacent vertices
        return {
            /**
             * Returns the name of the Vertice/Node.
             *
             * @returns {*}
             */
            getName: function(){ return name; }

            /**
             * Returns the requested edge.
             *
             * @param idx Index of the edge.
             * @returns {*}
             */
            ,getEdge: function(idx){ return edges[idx]; }

            /**
             * Returns all adjacent edges.
             *
             * @returns {*}
             */
            ,getEdges: function(){ return edges; }

            /**
             * Adds an adjacent Vertice/Node.
             *
             * Note: Should only be called if you know what you are doing.
             *       It's mainly used to generate the network.
             *       You will not be able to delete added Vertices/Nodes.
             *
             * @param vertice
             */
            ,addVertice: function(vertice){ vertices.push(vertice); }

            /**
             * Returns all adjacent Vertices/Nodes.
             *
             * @returns {Array}
             */
            ,getVertices: function(){ return vertices; }

            /**
             * Will return the edge to the requested Vertice/Node.
             *
             * If it do not find an corresponding edge it will return false
             *
             * @param destination
             * @returns {*}
             */
            ,findEdgeTo: function(destination)
            {
                for(var edge_idx in edges)
                {
                    var edge = edges[edge_idx];
                    if(edge.getDestination() === destination)
                        return edge;
                }

                return false;
            }
        }
    }

    /**
     * Representation of an edge between two Vertices/Nodes inside our graph
     *
     * @param weight
     * @returns {{createConnection: Function, getWeight: Function, getSource: Function, getDestination: Function}}
     * @constructor
     */
    function E(weight)
    {
        var between = [];

        return {
            /**
             * Creates the connection to the Vertices/Nodes for this edge.
             * @param source
             * @param destination
             */
            createConnection: function(source, destination) {
                between = [source, destination];
            }

            /**
             * Returns the weight of this edge
             * @returns {*}
             */
            ,getWeight: function(){ return weight; }

            /**
             * Returns the Source/From Vertice/Node of this Edge
             *
             * @returns {*}
             */
            , getSource: function(){ return between[0]; }

            /**
             * Returns the Destination/To Vertice/Node of this Edge
             * @returns {*}
             */
            , getDestination: function(){ return between[1]; }
        }
    }

    /**
     * Create the network as Vertices/Nodes with Edges between them form the given graph.
     *
     * Note: The first Node, of the returned network, will not have the name 0 but 1,
     *       the index is 0 based.
     *       (This is due to the fact that we'll never draw an 0-Node on paper)
     *
     * Example:
     *      network[0] => {name: '1', ...}
     *      network[1] => {name: '2', ...}
     *      ...
     *
     * @param graph
     * @returns {Array}
     */
    function createNetwork(graph)
    {
        var vertices = 0;

        // Autodetect number of nodes in our graph
        for(i in graph)
        {
            if(i != 0 && graph[i] == Infinity)
                break;

            if(graph[i] === Infinity) vertices = 0;
            else                vertices++
        }

        // Create those nodes
        var verticeArray = [];
        for(var i = 0; i < vertices; i++)
        {
            var edges = [];
            for(var j = 0; j < vertices; j++)
            {
                var edgeValue = graph.shift();
                edges.push(new E(edgeValue));
            }
            verticeArray.push(new V((i + 1), edges));
        }

        // Wire the nodes
        for(var oV in verticeArray)
        {
            for(var iV in verticeArray)
            {
                verticeArray[oV].addVertice(verticeArray[iV]);
            }
        }

        // wire edges
        for(var v in verticeArray)
        {
            for(e in verticeArray[v].getEdges())
            {
                verticeArray[v].getEdge(e).createConnection(verticeArray[v], verticeArray[e])
            }
        }

        // NOTE: A weight of Infinity in our Network indicates an edge to any not reachable Node.
        //       Therefore in TSM, for example, loops on the Node itself will be Infinity.
        //       You may also be able split a Network and set all Edges between them to Infinity.

        return verticeArray;
    }

    var network = createNetwork(graph);

    return {
        /**
         * Returns the network built from the given graph.
         *
         * @returns {*}
         */
        getNetwork: function(){
            return network;
        }
    }
}

/**
 * This class can solve the TSM Problem.
 *
 * @param {GraphNetwork} network description of the problem
 * @returns {{solve: Function, getBestPath: Function, getBestCost: Function}}
 * @constructor
 */
function TSM(network)
{
    /**
     * Will hold our best path state
     *
     * @type {{path: [], cost: Number}}
     */
    var bestCosts = {
        path: null
        ,cost: Infinity
    };

    var _debug = false;

    /**
     * Calculates the cost for the given path through the edge weights.
     *
     * @param s path
     *
     * @returns {number} cost of the path
     */
    function calculatePathCosts(s)
    {
        var cost = 0;
        for(var node_idx = 0; node_idx < s.length; node_idx++)
        {
            var node = s[node_idx];
            var nextNode = s[node_idx+1];

            if(nextNode !== undefined)
            {
                cost += node.findEdgeTo(nextNode).getWeight();
            }
        }
        return cost;
    }

    /**
     * Solve the TSM-Problem through an iterative backtracking.
     *
     * @param start the node inside the network where we want to start
     */
    function solve(start)
    {
        var currentNode = network[start];       // x = network.length;
        var s = [];                             // current path                   ; dimension: x
        var v = [[]];                           // visited nodes for each layer   ; dimension: x * x
        var u = [];                             // unvisited nodes                ; dimension: x
        var d = 0;                              // current depth                  ; min: 0 ; max: x

        // Push our root path
        s.push(currentNode);

        while(s.length > 0)
        {
            //u = currentNode.edges - v;
            u = []; // empty unvisited nodes, as we generate them now
            _.forEach(currentNode.getEdges(), function(edge){
                if(edge.getWeight() === Infinity)
                    return; // filter impossible edges (loop or cut)

                for(var visitedNode_idx in v[d])
                    if(v[d][visitedNode_idx] === edge.getDestination())
                        return; // it was already visited

                for(var currentPathNodes_idx in s)
                    if(s[currentPathNodes_idx] === edge.getDestination())
                        return; // they are our parents, so we visited them already

                u.push(edge.getDestination()); // it is not visited
            });

            if(u.length > 0)
            { // traverse unvisited
                v[++d] = []; // reset visited nodes for comming layer

                // get the first unvisited and push onto our path
                currentNode = u.shift();
                s.push(currentNode);
            }
            else
            { // no traversal possible
                // Calculate Pathcosts if we are in a complete path
                if(s.length === network.length)
                {
                    var pathCost = calculatePathCosts(s);
                    if(pathCost < bestCosts.cost)
                    {
                        bestCosts.cost = pathCost;
                        bestCosts.path = _.clone(s);
                    }
                }

                if(d > 0)
                {
                    v[--d].push(s.pop());
                    currentNode = s[s.length-1];
                }else{ // End of tree - finish.
                    s.pop(); // we pop our root node, so we are done.
                }
            }

            if(s.length === network.length)
            { // We got a complete path, print it.
                var printPath = [];
                _.forEach(s, function(node){
                    printPath.push(node.getName());
                });

                if(_debug)
                    console.log('Found Path: ', printPath, 'Cost: ', calculatePathCosts(s));
            }
        }
    }

    return {
        solve: solve
        /**
         * Returns the best path found.
         * @returns {*[]}
         */
        ,getBestPath: function(){return bestCosts.path;}
        /**
         * Returns the cost of the best path found.
         * @returns {Number}
         */
        ,getBestCost: function(){return bestCosts.cost;}
        /**
         * Will enable debugging output
         */
        ,toggleDebug: function(){_debug = !_debug;}
    };
}

var Problem = new GraphNetwork([
    Infinity, 906, 814, 30, 198,
    906, Infinity, 633, 225, 103,
    814, 633, Infinity, 462, 553,
    30, 225, 462, Infinity, 814,
    198, 103, 553, 814, Infinity
]);

// Init our TSM and solve it
var ProblemSolver = new TSM(Problem.getNetwork());
ProblemSolver.toggleDebug();
ProblemSolver.solve(0);

// Print the solution
var printPath = [];
_.forEach(ProblemSolver.getBestPath(), function(node){
    printPath.push(node.getName());
});
console.log('bestCost: ', ProblemSolver.getBestCost());
console.log('bestPath: ', printPath);
<|endoftext|>


<commit_before>function initMap() {
    // add the map layer
    map = new L.Map("localmap");
    var min = 14;
    var max = 17;
    var myLayer = new L.TileLayer('http://tiles3.buergerbautstadt.de/berlin/{z}/{x}/{y}.png', {
        minZoom: min,
        maxZoom: max,
        attribution: 'Map data © 2012 OpenStreetMap contributors',
        zIndex: 0,
        errorTileUrl: 'http://tiles3.buergerbautstadt.de/error.png',
        reuseTiles: true
    });
    map.addLayer(myLayer);

    // center map
    var center = new L.LatLng(lat, lon);
    map.setView(center, 15);

    var icon = L.icon({
        iconUrl: staticUrl + '/img/Baustellenschilder/klein/schild_gruen_klein.png',
        iconSize:     [26, 45], // size of the icon width,height
        iconAnchor:   [13, 45], // point of the icon which will correspond to marker's location
    });

    var polygonOptions = {
        weight: 3,
        color: '#de6a00',
        opacity: 1,
        fill: true,
        fillColor: '#de6a00',
        fillOpacity: 0.05
    }

    if (typeof(polygon) !== 'undefined') {
        if (polygontype === 'Polygon') {
            L.polygon(polygon).setStyle(polygonOptions).addTo(map);
        } else if (polygontype === 'MultiPolygon') {
            L.multiPolygon(polygon).setStyle(polygonOptions).addTo(map);
        }
    }
    L.marker([lat,lon],{icon: icon}).addTo(map);

}

$(document).ready(function() {
    setTimeout('initMap()',100);
});<commit_msg>Update localmap.js<commit_after>function initMap() {
    // add the map layer
    map = new L.Map("localmap");
    var min = 14;
    var max = 17;
    var myLayer = new L.TileLayer('http://tiles3.buergerbautstadt.de/berlin/{z}/{x}/{y}.png', {
        minZoom: min,
        maxZoom: max,
        attribution: 'Map data © 2012 OpenStreetMap contributors and Geoportal Berlin/Geltungsbereiche der Bebauungspläne',
        zIndex: 0,
        errorTileUrl: 'http://tiles3.buergerbautstadt.de/error.png',
        reuseTiles: true
    });
    map.addLayer(myLayer);

    // center map
    var center = new L.LatLng(lat, lon);
    map.setView(center, 15);

    var icon = L.icon({
        iconUrl: staticUrl + '/img/Baustellenschilder/klein/schild_gruen_klein.png',
        iconSize:     [26, 45], // size of the icon width,height
        iconAnchor:   [13, 45], // point of the icon which will correspond to marker's location
    });

    var polygonOptions = {
        weight: 3,
        color: '#de6a00',
        opacity: 1,
        fill: true,
        fillColor: '#de6a00',
        fillOpacity: 0.05
    }

    if (typeof(polygon) !== 'undefined') {
        if (polygontype === 'Polygon') {
            L.polygon(polygon).setStyle(polygonOptions).addTo(map);
        } else if (polygontype === 'MultiPolygon') {
            L.multiPolygon(polygon).setStyle(polygonOptions).addTo(map);
        }
    }
    L.marker([lat,lon],{icon: icon}).addTo(map);

}

$(document).ready(function() {
    setTimeout('initMap()',100);
});
<|endoftext|>


<commit_before>/**
 * Created by pozpl on 16.12.13.
 */

angular.module('GeoHashingApp').factory('AuthenticationService', [function () {
    var userIsAuthenticated = false;
    var lastUnauthenticatedState = null;
    var lastUnauthenticatedStateParams = null;
    return {
        setLoggedIn: function (value) {
            userIsAuthenticated = value;
        },

        isLoggedIn: function () {
            return userIsAuthenticated;
        },

        getLastUnauthenticatedState: function () {
            return lastUnauthenticatedState;
        },

        setLastUnauthenticatedState: function (value) {
            lastUnauthenticatedState = value;
        },

        setLastUnauthenticatedStateParams: function(value){
            lastUnauthenticatedStateParams = value;
        },

        getLastUnauthenticatedStateParams: function(){
            return lastUnauthenticatedStateParams;
        }
    };
}]
);
<commit_msg>master Now we can get and set user Id in authentication service<commit_after>/**
 * Created by pozpl on 16.12.13.
 */

angular.module('GeoHashingApp').factory('AuthenticationService', [function () {
    var userIsAuthenticated = false;
    var lastUnauthenticatedState = null;
    var lastUnauthenticatedStateParams = null;
    var userId = null;

    return {
        setLoggedIn: function (value) {
            userIsAuthenticated = value;
        },

        isLoggedIn: function () {
            return userIsAuthenticated;
        },

        getLastUnauthenticatedState: function () {
            return lastUnauthenticatedState;
        },

        setLastUnauthenticatedState: function (value) {
            lastUnauthenticatedState = value;
        },

        setLastUnauthenticatedStateParams: function(value){
            lastUnauthenticatedStateParams = value;
        },

        getLastUnauthenticatedStateParams: function(){
            return lastUnauthenticatedStateParams;
        },

        setUserId : function(userId){
            this.userId = userId;
        },

        getUserId : function(){
            return userId;
        }
    };
}]
);
<|endoftext|>


<commit_before>'use strict';

let User = rootRequire('app/users/model');
let Application = rootRequire('app/users/application/model');
let csv = require('to-csv');
let help = rootRequire('app/helpers/functions');
let path = require('path');
let zip = require('express-zip');

module.exports = {

  /**
  * Download a CSV of all attendees
  * GET /exports/attendees
  */
  attendees: (req, res) => {
    User
      .find()
      .select('created email application')
      .where({application: {$exists: true}})
      .sort('-created')
      .populate({
        path: 'application',
        match: req.query
      })
      .exec((err, users) => {
        if (err) return res.internalError();
        if (!users || !users.length) return res.clientError('No attendees match that query');
        let list = users.map((user) => {
          return {
            'Registered': new Date(user.created).toUTCString(),
            'Name': user.application.name || '',
            'Email': user.email || '',
            'Phone': user.application.phone || '',
            'School': user.application.school || '',
            'Major': user.application.major || '',
            'Year': user.application.year || '',
            'Age': user.application.age || '',
            'Gender': help.capitalizeFirstLetter(user.application.gender) || '',
            'Shirt Size': user.application.shirt || '',
            'RSVPd?': (user.application.going) ? 'Yes' : 'No',
            'Link': user.application.link || '',
            'Status': help.capitalizeFirstLetter(user.application.status) || '',
            'Checked In?': (user.application.checked) ? 'Yes' : 'No',
          };
        });
        res.setHeader('Content-disposition', 'attachment; filename=attendees.csv');
        res.setHeader('Content-type', 'text/csv');
        return res.status(200).send(csv(list));
      });
  },

  /**
  * Download a resume book
  * GET /exports/resumes
  */
  resumes: (req, res) => {
    Application
      .find(req.query)
      .select('resume name')
      .exec((err, applications) => {
        if (err) return res.internalError();

        let files = applications.filter((application) => {
          return application.resume && application.resume.length > 0 && application.resume.length <= 40;
        }).map((application) => {

          // normalize file names
          let newName = titleCase(application.name).replace(/\s/g, '');
          let newExt = path.extname(application.resume);
          let a = {
            path: path.join(__dirname, '../../resumes/', application.resume),
            name: newName + newExt
          };
		  console.log(a);
		  return a;

        });

        return res.zip(files);

      });
  }

};

function titleCase(str) {
  return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}
<commit_msg>added mlh to exports<commit_after>'use strict';

let User = rootRequire('app/users/model');
let Application = rootRequire('app/users/application/model');
let csv = require('to-csv');
let help = rootRequire('app/helpers/functions');
let path = require('path');
let zip = require('express-zip');

module.exports = {

  /**
  * Download a CSV of all attendees
  * GET /exports/attendees
  */
  attendees: (req, res) => {
    User
      .find()
      .select('created email application')
      .where({application: {$exists: true}})
      .sort('-created')
      .populate({
        path: 'application',
        match: req.query
      })
      .exec((err, users) => {
        if (err) return res.internalError();
        if (!users || !users.length) return res.clientError('No attendees match that query');
        let list = users.map((user) => {
          return {
            'Registered': new Date(user.created).toUTCString(),
            'Name': user.application.name || '',
            'Email': user.email || '',
            'Phone': user.application.phone || '',
            'School': user.application.school || '',
            'Major': user.application.major || '',
            'Year': user.application.year || '',
            'Age': user.application.age || '',
            'Gender': help.capitalizeFirstLetter(user.application.gender) || '',
            'Shirt Size': user.application.shirt || '',
            'RSVPd?': (user.application.going) ? 'Yes' : 'No',
            'Link': user.application.link || '',
            'Status': help.capitalizeFirstLetter(user.application.status) || '',
            'Checked In?': (user.application.checked) ? 'Yes' : 'No',
            'Conduct': user.application.conduct ? 'Yes' : 'No',
            'MLH Emails': user.application.mlh_emails ? 'Yes' : 'No'
          };
        });
        res.setHeader('Content-disposition', 'attachment; filename=attendees.csv');
        res.setHeader('Content-type', 'text/csv');
        return res.status(200).send(csv(list));
      });
  },

  /**
  * Download a resume book
  * GET /exports/resumes
  */
  resumes: (req, res) => {
    Application
      .find(req.query)
      .select('resume name')
      .exec((err, applications) => {
        if (err) return res.internalError();

        let files = applications.filter((application) => {
          return application.resume && application.resume.length > 0 && application.resume.length <= 40;
        }).map((application) => {

          // normalize file names
          let newName = titleCase(application.name).replace(/\s/g, '');
          let newExt = path.extname(application.resume);
          let a = {
            path: path.join(__dirname, '../../resumes/', application.resume),
            name: newName + newExt
          };
		  console.log(a);
		  return a;

        });

        return res.zip(files);

      });
  }

};

function titleCase(str) {
  return str.replace(/\w\S*/g, function(txt){return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();});
}
<|endoftext|>