<commit_before>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('node-uuid');
const request = require('request');
const JSONbig = require('json-bigint');
const async = require('async');

const REST_PORT = (process.env.PORT || 5000);
const APIAI_ACCESS_TOKEN = process.env.APIAI_ACCESS_TOKEN;
const APIAI_LANG = process.env.APIAI_LANG || 'en';
const FB_VERIFY_TOKEN = process.env.FB_VERIFY_TOKEN;
const FB_PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN;
const FS_CLIENT_SECRET = process.env.FS_CLIENT_SECRET;
const FS_CLIENT_ID = process.env.FS_CLIENT_ID;

const apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
const sessionIds = new Map();

const foursquareVersion = '20160108';
const foursquareCategories = {
    food: 'food',
    drinks: 'drinks',
    coffee: 'coffee',
    shops: 'shops',
    arts: 'arts',
    topPicks: 'topPicks',
};
var suggestionLimit = 3;
var closestFirst = 0;
var venueType = 'topPicks';
var location = null;

const actionFindVenue = 'findVenue';
const intentFindVenue = 'FindVenue';

function processEvent(event) {
    var sender = event.sender.id.toString();

    if ((event.message && event.message.text) || (event.postback && event.postback.payload)) {
        var text = event.message ? event.message.text : event.postback.payload;
        // Handle a text message from this sender

        if (!sessionIds.has(sender)) {
            sessionIds.set(sender, uuid.v1());
        }

        console.log("Text:", text);

        let apiaiRequest = apiAiService.textRequest(text,
            {
                sessionId: sessionIds.get(sender)
            });

        apiaiRequest.on('response', (response) => {
            if (isDefined(response.result)) {
                let responseText = response.result.fulfillment.speech;
                let responseData = response.result.fulfillment.data;
                var action = response.result.action;
                var intentName = response.result.metadata.intentName;
                var parameters = response.result.parameters;

                console.log(action);

                if (isDefined(responseData) && isDefined(responseData.facebook)) {
                    if (!Array.isArray(responseData.facebook)) {
                        try {
                            console.log('Response as formatted message');
                            sendFBMessage(sender, responseData.facebook);
                        } catch (err) {
                            sendFBMessage(sender, {text: err.message});
                        }
                    } else {
                        async.eachSeries(responseData.facebook, (facebookMessage, callback) => {
                            try {
                                if (facebookMessage.sender_action) {
                                    console.log('Response as sender action');
                                    sendFBSenderAction(sender, facebookMessage.sender_action, callback);
                                }
                                else {
                                    console.log('Response as formatted message');
                                    sendFBMessage(sender, facebookMessage, callback);
                                }
                            } catch (err) {
                                sendFBMessage(sender, {text: err.message}, callback);
                            }
                        });
                    }
                } else if (isDefined(responseText)) {
                    textResponse(responseText, sender);
                } else if (isDefined(action) && isDefined(intentName)) {

                    if (action === actionFindVenue && intentName == intentFindVenue) {
                        if (isDefined(parameters)) {

                            console.log('Sender: ', sender);

                            findVenue(parameters, (foursquareResponse) => {
                                if (isDefined(foursquareResponse)) {
                                    console.log('Response is defined');
                                    sendFBCardMessage(sender, formatVenueData(foursquareResponse));
                                }
                            });
                        }
                    }
                }

            }
        });

        apiaiRequest.on('error', (error) => console.error(error));
        apiaiRequest.end();
    } /*else if (event.message && event.message.attachments) { //handle fb location sharing
        if (isDefined(event.message.attachments[0].payload) && isDefined(event.message.attachments[0].payload.coordinates)) {
            if (isDefined(event.message.attachments[0].payload.coordinates.lat) && isDefined(event.message.attachments[0].payload.coordinates.long)) {
                passCoordinates(sender, event.message.attachments[0].payload.coordinates);
            }
        }
    }*/
}

function passCoordinates(sender, coordinates) {
    console.log('Location made through!');
    parameters = {};
    parameters.location = {};
    parameters.location.coordinates = coordinates;
    console.log(isDefined(parameters.location.coordinates));
    console.log(parameters.location.coordinates.lat);
    console.log(parameters.location.coordinates.long);
}

function textResponse(str, sender) {
    console.log('Response as text message');
        // facebook API limit for text length is 320,
        // so we must split message if needed
    var splittedText = splitResponse(str);

    async.eachSeries(splittedText, (textPart, callback) => {
        sendFBMessage(sender, {text: textPart}, callback);
    });
}

function splitResponse(str) {
    if (str.length <= 320) {
        return [str];
    }

    return chunkString(str, 300);
}

function chunkString(s, len) {
    var curr = len, prev = 0;

    var output = [];

    while (s[curr]) {
        if (s[curr++] == ' ') {
            output.push(s.substring(prev, curr));
            prev = curr;
            curr += len;
        }
        else {
            var currReverse = curr;
            do {
                if (s.substring(currReverse - 1, currReverse) == ' ') {
                    output.push(s.substring(prev, currReverse));
                    prev = currReverse;
                    curr = currReverse + len;
                    break;
                }
                currReverse--;
            } while (currReverse > prev)
        }
    }
    output.push(s.substr(prev));
    return output;
}

function sendFBMessage(sender, messageData, callback) {
    request({
        url: 'https://graph.facebook.com/v2.6/me/messages',
        qs: {access_token: FB_PAGE_ACCESS_TOKEN},
        method: 'POST',
        json: {
            recipient: {id: sender},
            message: messageData
        }
    }, (error, response, body) => {
        if (error) {
            console.log('Error sending message: ', error);
        } else if (response.body.error) {
            console.log('Error: ', response.body.error);
        }

        if (callback) {
            callback();
        }
    });
}

function sendFBCardMessage (sender, messageData, callback) {
    console.log('Sending card message: ');
    console.log(messageData);

    var cardOptions = {
        url: 'https://graph.facebook.com/v2.6/me/messages',
        qs: {access_token: FB_PAGE_ACCESS_TOKEN},
        method: 'POST',
        json: {
            recipient: {id: sender},
            message: {
                'attachment': {
                    'type':'template',
                    'payload': {
                      'template_type':'generic',
                      'elements':[]
                    }
                }
            }
        }
    };

    for (let i = 0; i < suggestionLimit; i++) {
        cardOptions.json.message.attachment.payload.elements.push(messageData[i]);
    }

    request(cardOptions, (error, response, body) => {
        if (error) {
            console.log('Error sending card: ', error);
        } else if (response.body.error) {
            console.log('Error: ', response.body.error);
        }

        if (callback) {
            callback();
        }
    });
}

function sendFBSenderAction(sender, action, callback) {
    setTimeout(() => {
        request({
            url: 'https://graph.facebook.com/v2.6/me/messages',
            qs: {access_token: FB_PAGE_ACCESS_TOKEN},
            method: 'POST',
            json: {
                recipient: {id: sender},
                sender_action: action
            }
        }, (error, response, body) => {
            if (error) {
                console.log('Error sending action: ', error);
            } else if (response.body.error) {
                console.log('Error: ', response.body.error);
            }
            if (callback) {
                callback();
            }
        });
    }, 1000);
}

function doSubscribeRequest() {
    request({
            method: 'POST',
            uri: "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=" + FB_PAGE_ACCESS_TOKEN
        },
        (error, response, body) => {
            if (error) {
                console.error('Error while subscription: ', error);
            } else {
                console.log('Subscription result: ', response.body);
            }
        });
}

function isDefined(obj) {
    if (typeof obj == 'undefined') {
        return false;
    }

    if (!obj) {
        return false;
    }

    return obj != null;
}

const app = express();

app.use(bodyParser.text({type: 'application/json'}));

app.get('/webhook/', (req, res) => {
    if (req.query['hub.verify_token'] == FB_VERIFY_TOKEN) {
        res.send(req.query['hub.challenge']);

        setTimeout(() => {
            doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    try {
        var data = JSONbig.parse(req.body);

        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                        if (event.message && !event.message.is_echo ||
                            event.postback && event.postback.payload) {
                            processEvent(event);
                        }
                    });
                }
            });
        }

        return res.status(200).json({
            status: "ok"
        });
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }

});

app.listen(REST_PORT, () => {
    console.log('Rest service ready on port ' + REST_PORT);
});

doSubscribeRequest();

function formatVenueData(raw) {
    var items = raw.response.groups[0].items;
    var venues = [];
    var j = 0;

    if (isDefined(items)) {
        for (let i = 0; i < suggestionLimit; i++) {
            var venue = items[i].venue;
            var url = venue.url;

            var formatted = {};
            formatted.title = venue.name;

            if (isDefined(venue.hours) && isDefined(venue.hours.status)) {
                formatted.subtitle = venue.hours.status;
            } else {
                formatted.subtitle = '';
            }

            formatted.buttons = [];
            j = 0;

            formatted.buttons[j] = {
                type: 'web_url',
                title: 'View Website',
            };

            if (isDefined(url)) {
                formatted.buttons[j].url = url;
                j++;
            } else {
                formatted.buttons[j].url = 'http://foursquare.com/v/'.concat(venue.id);
                j++;
            }

            formatted.buttons[j] = {
                type: 'web_url',
            };
            if (isDefined(venue.location) && isDefined(venue.location.formattedAddress && venue.location.formattedAddress.length > 1)) {
                formatted.buttons[j].title = venue.location.formattedAddress[0].concat(', ', venue.location.formattedAddress[1]);
            } else {
                formatted.buttons[j].title = venue.location.city;
            }
            formatted.buttons[j].url = 'http://maps.google.com/?q='.concat(formatted.buttons[j].title);

            venues.push(formatted);
        }
    }
    return venues;
}

function formatGETOptions(parameters) {

    if (!isDefined(parameters.venueType)) {
        venueType = parameters.venueType;
    }

    var options = {
        method: 'GET',
        url: 'http://api.foursquare.com/v2/venues/explore',
        qs: {
            client_id: FS_CLIENT_ID,
            client_secret: FS_CLIENT_SECRET,
            v: foursquareVersion,
            m: 'foursquare',
            section: venueType,
            limit: suggestionLimit,
            sortByDistance: closestFirst,
        },
        venuePhotos: 1,
        json: true,
    };

    console.log('Venue: ', options.qs.section);

    if (!isDefined(parameters.location)) {
        if (isDefined(parameters.location.location)) { //location as address
            location = parameters.location.location;
            options.qs.near = location;
        }
        else if (isDefined(parameters.location.coordinates)) { //location as coordinates
            location = parameters.location.coordinates.lat.concat(',', parameters.location.coordinates.long);
            options.qs.ll = location;
        }
    }

    if (!isDefined(location)) {
        return null;
    }

    console.log('Location: ', location);

    return options;
}

function findVenue(parameters, callback) {

    var options = formatGETOptions(parameters);

    if (isDefined(options)) {
        request(options, (error, res, body) => {
            if (error) {
                console.error('GET Error: ', error);
            } else {
                callback(body);
            }
        });
    }
}<commit_msg>no message<commit_after>'use strict';

const apiai = require('apiai');
const express = require('express');
const bodyParser = require('body-parser');
const uuid = require('node-uuid');
const request = require('request');
const JSONbig = require('json-bigint');
const async = require('async');

const REST_PORT = (process.env.PORT || 5000);
const APIAI_ACCESS_TOKEN = process.env.APIAI_ACCESS_TOKEN;
const APIAI_LANG = process.env.APIAI_LANG || 'en';
const FB_VERIFY_TOKEN = process.env.FB_VERIFY_TOKEN;
const FB_PAGE_ACCESS_TOKEN = process.env.FB_PAGE_ACCESS_TOKEN;
const FS_CLIENT_SECRET = process.env.FS_CLIENT_SECRET;
const FS_CLIENT_ID = process.env.FS_CLIENT_ID;

const apiAiService = apiai(APIAI_ACCESS_TOKEN, {language: APIAI_LANG, requestSource: "fb"});
const sessionIds = new Map();

const foursquareVersion = '20160108';
const foursquareCategories = {
    food: 'food',
    drinks: 'drinks',
    coffee: 'coffee',
    shops: 'shops',
    arts: 'arts',
    topPicks: 'topPicks',
};
var suggestionLimit = 3;
var closestFirst = 0;
var venueType = 'topPicks';
var location = null;

const actionFindVenue = 'findVenue';
const intentFindVenue = 'FindVenue';

function processEvent(event) {
    var sender = event.sender.id.toString();

    if ((event.message && event.message.text) || (event.postback && event.postback.payload)) {
        var text = event.message ? event.message.text : event.postback.payload;
        // Handle a text message from this sender

        if (!sessionIds.has(sender)) {
            sessionIds.set(sender, uuid.v1());
        }

        console.log("Text:", text);

        let apiaiRequest = apiAiService.textRequest(text,
            {
                sessionId: sessionIds.get(sender)
            });

        apiaiRequest.on('response', (response) => {
            if (isDefined(response.result)) {
                let responseText = response.result.fulfillment.speech;
                let responseData = response.result.fulfillment.data;
                var action = response.result.action;
                var intentName = response.result.metadata.intentName;
                var parameters = response.result.parameters;

                console.log(action);

                if (isDefined(responseData) && isDefined(responseData.facebook)) {
                    if (!Array.isArray(responseData.facebook)) {
                        try {
                            console.log('Response as formatted message');
                            sendFBMessage(sender, responseData.facebook);
                        } catch (err) {
                            sendFBMessage(sender, {text: err.message});
                        }
                    } else {
                        async.eachSeries(responseData.facebook, (facebookMessage, callback) => {
                            try {
                                if (facebookMessage.sender_action) {
                                    console.log('Response as sender action');
                                    sendFBSenderAction(sender, facebookMessage.sender_action, callback);
                                }
                                else {
                                    console.log('Response as formatted message');
                                    sendFBMessage(sender, facebookMessage, callback);
                                }
                            } catch (err) {
                                sendFBMessage(sender, {text: err.message}, callback);
                            }
                        });
                    }
                } else if (isDefined(responseText)) {
                    textResponse(responseText, sender);
                } else if (isDefined(action) && isDefined(intentName)) {

                    if (action === actionFindVenue && intentName == intentFindVenue) {
                        if (isDefined(parameters)) {
                            findVenue(parameters, (foursquareResponse) => {
                                if (isDefined(foursquareResponse)) {
                                    console.log('Response is defined');
                                    sendFBCardMessage(sender, formatVenueData(foursquareResponse));
                                }
                            });
                        }
                    }
                }

            }
        });

        apiaiRequest.on('error', (error) => console.error(error));
        apiaiRequest.end();
    } /*else if (event.message && event.message.attachments) { //handle fb location sharing
        if (isDefined(event.message.attachments[0].payload) && isDefined(event.message.attachments[0].payload.coordinates)) {
            if (isDefined(event.message.attachments[0].payload.coordinates.lat) && isDefined(event.message.attachments[0].payload.coordinates.long)) {
                passCoordinates(sender, event.message.attachments[0].payload.coordinates);
            }
        }
    }*/
}

function passCoordinates(sender, coordinates) {
    console.log('Location made through!');
    parameters = {};
    parameters.location = {};
    parameters.location.coordinates = coordinates;
    console.log(isDefined(parameters.location.coordinates));
    console.log(parameters.location.coordinates.lat);
    console.log(parameters.location.coordinates.long);
}

function textResponse(str, sender) {
    console.log('Response as text message');
        // facebook API limit for text length is 320,
        // so we must split message if needed
    var splittedText = splitResponse(str);

    async.eachSeries(splittedText, (textPart, callback) => {
        sendFBMessage(sender, {text: textPart}, callback);
    });
}

function splitResponse(str) {
    if (str.length <= 320) {
        return [str];
    }

    return chunkString(str, 300);
}

function chunkString(s, len) {
    var curr = len, prev = 0;

    var output = [];

    while (s[curr]) {
        if (s[curr++] == ' ') {
            output.push(s.substring(prev, curr));
            prev = curr;
            curr += len;
        }
        else {
            var currReverse = curr;
            do {
                if (s.substring(currReverse - 1, currReverse) == ' ') {
                    output.push(s.substring(prev, currReverse));
                    prev = currReverse;
                    curr = currReverse + len;
                    break;
                }
                currReverse--;
            } while (currReverse > prev)
        }
    }
    output.push(s.substr(prev));
    return output;
}

function sendFBMessage(sender, messageData, callback) {
    request({
        url: 'https://graph.facebook.com/v2.6/me/messages',
        qs: {access_token: FB_PAGE_ACCESS_TOKEN},
        method: 'POST',
        json: {
            recipient: {id: sender},
            message: messageData
        }
    }, (error, response, body) => {
        if (error) {
            console.log('Error sending message: ', error);
        } else if (response.body.error) {
            console.log('Error: ', response.body.error);
        }

        if (callback) {
            callback();
        }
    });
}

function sendFBCardMessage (sender, messageData, callback) {
    console.log('Sending card message: ');
    console.log(messageData);

    var cardOptions = {
        url: 'https://graph.facebook.com/v2.6/me/messages',
        qs: {access_token: FB_PAGE_ACCESS_TOKEN},
        method: 'POST',
        json: {
            recipient: {id: sender},
            message: {
                'attachment': {
                    'type':'template',
                    'payload': {
                      'template_type':'generic',
                      'elements':[]
                    }
                }
            }
        }
    };

    for (let i = 0; i < suggestionLimit; i++) {
        cardOptions.json.message.attachment.payload.elements.push(messageData[i]);
    }

    request(cardOptions, (error, response, body) => {
        if (error) {
            console.log('Error sending card: ', error);
        } else if (response.body.error) {
            console.log('Error: ', response.body.error);
        }

        if (callback) {
            callback();
        }
    });
}

function sendFBSenderAction(sender, action, callback) {
    setTimeout(() => {
        request({
            url: 'https://graph.facebook.com/v2.6/me/messages',
            qs: {access_token: FB_PAGE_ACCESS_TOKEN},
            method: 'POST',
            json: {
                recipient: {id: sender},
                sender_action: action
            }
        }, (error, response, body) => {
            if (error) {
                console.log('Error sending action: ', error);
            } else if (response.body.error) {
                console.log('Error: ', response.body.error);
            }
            if (callback) {
                callback();
            }
        });
    }, 1000);
}

function doSubscribeRequest() {
    request({
            method: 'POST',
            uri: "https://graph.facebook.com/v2.6/me/subscribed_apps?access_token=" + FB_PAGE_ACCESS_TOKEN
        },
        (error, response, body) => {
            if (error) {
                console.error('Error while subscription: ', error);
            } else {
                console.log('Subscription result: ', response.body);
            }
        });
}

function isDefined(obj) {
    if (typeof obj == 'undefined') {
        return false;
    }

    if (!obj) {
        return false;
    }

    return obj != null;
}

const app = express();

app.use(bodyParser.text({type: 'application/json'}));

app.get('/webhook/', (req, res) => {
    if (req.query['hub.verify_token'] == FB_VERIFY_TOKEN) {
        res.send(req.query['hub.challenge']);

        setTimeout(() => {
            doSubscribeRequest();
        }, 3000);
    } else {
        res.send('Error, wrong validation token');
    }
});

app.post('/webhook/', (req, res) => {
    try {
        var data = JSONbig.parse(req.body);

        if (data.entry) {
            let entries = data.entry;
            entries.forEach((entry) => {
                let messaging_events = entry.messaging;
                if (messaging_events) {
                    messaging_events.forEach((event) => {
                        if (event.message && !event.message.is_echo ||
                            event.postback && event.postback.payload) {
                            processEvent(event);
                        }
                    });
                }
            });
        }

        return res.status(200).json({
            status: "ok"
        });
    } catch (err) {
        return res.status(400).json({
            status: "error",
            error: err
        });
    }

});

app.listen(REST_PORT, () => {
    console.log('Rest service ready on port ' + REST_PORT);
});

doSubscribeRequest();

function formatVenueData(raw) {
    var items = raw.response.groups[0].items;
    var venues = [];
    var j = 0;

    if (isDefined(items)) {
        for (let i = 0; i < suggestionLimit; i++) {
            var venue = items[i].venue;
            var url = venue.url;

            var formatted = {};
            formatted.title = venue.name;

            if (isDefined(venue.hours) && isDefined(venue.hours.status)) {
                formatted.subtitle = venue.hours.status;
            } else {
                formatted.subtitle = '';
            }

            formatted.buttons = [];
            j = 0;

            formatted.buttons[j] = {
                type: 'web_url',
                title: 'View Website',
            };

            if (isDefined(url)) {
                formatted.buttons[j].url = url;
                j++;
            } else {
                formatted.buttons[j].url = 'http://foursquare.com/v/'.concat(venue.id);
                j++;
            }

            formatted.buttons[j] = {
                type: 'web_url',
            };
            if (isDefined(venue.location) && isDefined(venue.location.formattedAddress && venue.location.formattedAddress.length > 1)) {
                formatted.buttons[j].title = venue.location.formattedAddress[0].concat(', ', venue.location.formattedAddress[1]);
            } else {
                formatted.buttons[j].title = venue.location.city;
            }
            formatted.buttons[j].url = 'http://maps.google.com/?q='.concat(formatted.buttons[j].title);

            venues.push(formatted);
        }
    }
    return venues;
}

function formatGETOptions(parameters) {

    if (!isDefined(parameters.venueType)) {
        venueType = parameters.venueType;
    }

    console.log('Address: ', isDefined(parameters.location));
    console.log('Coordinates: ', isDefined(parameters.coordinates));
    console.log('Venue: ', isDefined(parameters.venueType));

    var options = {
        method: 'GET',
        url: 'http://api.foursquare.com/v2/venues/explore',
        qs: {
            client_id: FS_CLIENT_ID,
            client_secret: FS_CLIENT_SECRET,
            v: foursquareVersion,
            m: 'foursquare',
            section: venueType,
            limit: suggestionLimit,
            sortByDistance: closestFirst,
        },
        venuePhotos: 1,
        json: true,
    };

    console.log('Venue: ', options.qs.section);

    if (!isDefined(parameters.location)) {
        if (isDefined(parameters.location.location)) { //location as address
            location = parameters.location.location;
            options.qs.near = location;
        }
        else if (isDefined(parameters.coordinates)) { //location as coordinates
            location = parameters.location.coordinates.lat.concat(',', parameters.location.coordinates.long);
            options.qs.ll = location;
        }
    }

    if (!isDefined(location)) {
        return null;
    }

    console.log('Location: ', location);

    return options;
}

function findVenue(parameters, callback) {

    var options = formatGETOptions(parameters);

    if (isDefined(options)) {
        request(options, (error, res, body) => {
            if (error) {
                console.error('GET Error: ', error);
            } else {
                callback(body);
            }
        });
    }
}<|endoftext|>


<commit_before>import fs from 'fs';
import path from 'path';
import shell from 'shelljs';
import findConfig from 'find-config';

const CONFIG_FILENAME = '.ghkrc';

function initialize(gitroot) {
    let templates = path.join(__dirname, 'templates');
    let hooks = path.join(gitroot, '.git', 'hooks');
    fs.readdir(templates, (error, files) => {
        files.forEach(file => {
            shell.cp(path.join(templates, file), hooks);
            fs.chmod(path.join(hooks, file), '755');
        });
    });
}

function activate(hook) {
    let hookPackages = getConfig(hook);
    for (let packageName of Object.keys(hookPackages)) {
        let githook = require(`ghk-${packageName}`);
        let githookConfig = hookPackages[packageName];
        let result = githook[hook](githookConfig);
        if (result !== true) {
            console.log(`${hook} hook failed: ${packageName}: ${result}`);
            process.exit(1);
        }
    }
}

function readConfigFile() {
    let configfile = findConfig(CONFIG_FILENAME);
    if (configfile) {
        let config = fs.readFileSync(configfile);
        return JSON.parse(config);
    }
    return {};
}

function getConfig(hook) {
    let config = readConfigFile();
    return hook in config ? config[hook] : [];
}

exports.initialize = initialize;
exports.activate = activate;
<commit_msg>run local hook scripts instead of copying them into hooks dir<commit_after>import fs from 'fs';
import path from 'path';
import findConfig from 'find-config';

const CONFIG_FILENAME = '.ghkrc';

function initialize(gitroot) {
    let ghkhooks = path.join(__dirname, 'hooks');
    let githooks = path.join(gitroot, '.git', 'hooks');
    fs.readdir(ghkhooks, (error, files) => {
        files.forEach(file => {
            let githook = path.join(githooks, file);
            let ghkhook = path.join(ghkhooks, file);
            createHookIfNotExists(githook);
            if (!isHookAlreadyAdded(githook, ghkhook)) {
                fs.appendFile(githook,
                    `\n# run ghk "${file}" hook\n${ghkhook}\n`);
                console.log(`Added ghk ${file} hook to this repo.`);
            }
        });
    });
}

function activate(hook) {
    let hookPackages = getConfig(hook);
    for (let packageName of Object.keys(hookPackages)) {
        let githook = require(`ghk-${packageName}`);
        let githookConfig = hookPackages[packageName];
        let result = githook[hook](githookConfig);
        if (result !== true) {
            console.log(`${hook} hook failed: ${packageName}: ${result}`);
            process.exit(1);
        }
    }
}

function createHookIfNotExists(hook) {
    try {
        fs.accessSync(hook, fs.W_OK);
    } catch (e) {
        if (e.code === 'ENOENT') {
            fs.writeFileSync(hook, '');
            fs.chmodSync(hook, '755');
        }
    }
}

function isHookAlreadyAdded(githook, ghkhook) {
    let pattern = new RegExp(ghkhook);
    return pattern.test(fs.readFileSync(githook));
}

function readConfigFile() {
    let configfile = findConfig(CONFIG_FILENAME);
    if (configfile) {
        let config = fs.readFileSync(configfile);
        return JSON.parse(config);
    }
    return {};
}

function getConfig(hook) {
    let config = readConfigFile();
    return hook in config ? config[hook] : [];
}

exports.initialize = initialize;
exports.activate = activate;
<|endoftext|>


<commit_before>/**
 * Put tasks defined in ~extend.js appended files within the more general tasks
 * listed below.
 */
'use strict';

const gulp = require('gulp');

gulp.task('contrib:data', [
]);

gulp.task('contrib:frontend-copy', [
// Uncomment if you wish to enable this.
//  'stylus:frontend-copy'
]);

gulp.task('contrib:once', [
// Uncomment if you wish to enable this.
//  'stylus:once'
]);

gulp.task('contrib:static', [
]);

gulp.task('contrib:syncback', [
]);

gulp.task('contrib:tcp-ip', [
]);

gulp.task('contrib:template', [
]);

gulp.task('contrib:watch', [
// Uncomment if you wish to enable this.
//  'stylus:watch'
]);
<commit_msg>stylus enabled by default<commit_after>/**
 * Put tasks defined in ~extend.js appended files within the more general tasks
 * listed below.
 */
'use strict';

const gulp = require('gulp');

gulp.task('contrib:data', [
]);

gulp.task('contrib:frontend-copy', [
// Comment or delete if you wish to disable this.
  'stylus:frontend-copy'
]);

gulp.task('contrib:once', [
// Comment or delete if you wish to disable this.
  'stylus:once'
]);

gulp.task('contrib:static', [
]);

gulp.task('contrib:syncback', [
]);

gulp.task('contrib:tcp-ip', [
]);

gulp.task('contrib:template', [
]);

gulp.task('contrib:watch', [
// Comment or delete if you wish to disable this.
  'stylus:watch'
]);
<|endoftext|>


<commit_before>function Multiband(name, topleft, nBands, wh, color_range) {
  if( arguments.length ) {
      this.getready(name, topleft, nBands, wh, color_range);
  }
}

//inherit from the Element prototype
Multiband.prototype = new Element();
//put the correct constructor reference back (not essential)
Multiband.prototype.constructor = Multiband;

Multiband.prototype.getready = function (name, topleft, nBands, wh, color_range) {

    //reference the getready method from the parent class
    this.tempReady = Element.prototype.getready;
    //and run it as if it were part of this object
    this.tempReady(name, topleft);

    this.nBands = nBands;
    this.sideBands = new Array (this.nBands);

    // The values. Every band has its starting point, height, width, hight and color.
    // TODO they should be Nan.
    this.values = {};

    for (var i = 0; i < nBands; i++) {
        var valueName = i + "sp";
        this.values[valueName] = 0;
        valueName = i + "ep";
        this.values[valueName] = 0;
        valueName = i + "height";
        this.values[valueName] = 0;
        valueName = i + "height";
        this.values[valueName] = 0;
        valueName = i + "color";
        this.values[valueName] = 0;
    }

    this.values["color_range"] = color_range;

    // The multiband display has a fixed width and height.
    this.width = wh[0];
    this.height = wh[1];

    this.drawClass = undefined;

    //By default, a Multiband always draws itself when value is set.
    this.drawItself = true;

    this.completed = true;

}

Multiband.prototype.calculateSidebands = function () {

    for (var i = 0; i < this.nBands; i++) {

        var startValue = this.values[i + "sp"];
        var endValue = this.values[i + "ep"];
        var heightValue = this.values[i + "height"];

        var startXPoint = this.xOrigin + (this.width * startValue);
        var endXPoint = this.xOrigin + (this.width * endValue);
        var yPoint = this.yOrigin + (1 - this.height * heightValue);

        this.sideBands[i] = {"startXPoint":startXPoint, "endXPoint":endXPoint, "yPoint":yPoint};
    }

}

Multiband.prototype.IsInROI = function(x, y) {
    /* DOES NOT WORK */
    if ((x > this.xOrigin) && (y > this.yOrigin)) {
                if ((x < (this.xOrigin + this.width)) && (y < (this.yOrigin + this.height))) {
                    //console.log(this.name, " ROI Handler: ", x, y, " is in band bounding box ", this.xOrigin, this.yOrigin, this.xOrigin + this.width, this.yOrigin + this.height);

                    // It's in the multiband's real estate on the screen. Now we got to find if it's on the proximity of a sideband.
                    // TODO set this somewhere else
                    var proximity = 3;
                    this.calculateSidebands();
                    for (var i = 0; i < this.nBands; i++) {

                        var curSB = this.sideBands[i];

                        if (y < curSB["yPoint"]) {
                            //Too high!
                            //console.log("Too high! (", y, " vs ", curSB["yPoint"]);
                            continue;
                        }
                        // Check for the start side band
                        if ((x > curSB["startXPoint"] - proximity) && (x < curSB["startXPoint"] + proximity)) {
                            //console.log(this.name, " ROI Handler: ", x, y, " is in ROI for starting point of sideband ", i);
                            // We got it!
                            this.sideBand = [0, i];
                            return true;
                        }
                        if ((x > curSB["endXPoint"] - proximity) && (x < curSB["endXPoint"] + proximity)) {
                            // We got it!
                            //console.log(this.name, " ROI Handler: ", x, y, " is in ROI for ending point of sideband ", i);
                            this.sideBand = [1, i];
                            return true;
                        }
                    }
                }
    }
    //console.log(this.name, " ROI Handler: ", x, y, " is NOT in sideband ROI ");
    //console.log ("Returning false");
    return false;
}

Multiband.prototype.onROI = function (start_x, start_y, curr_x, curr_y) {
    var deltaX = 0;
    deltaX = curr_x - start_x;

    if (this.sideBand[0] == 0) {
        //Moving the start sideband
        if (this.sideBand[1] == 0) {
            //Moving the first starting sideband: this is affected by the endband.
            var temp_value = this.values["band0sp"];

            var to_set = temp_value - deltaX / 2000;

            if (to_set >= this.values["0ep"]) {
                to_set = this.values["0ep"];
            }
            if (to_set < 0) {
                to_set = 0;
            }

            var ret = {"slot" : "0sp", "value" : to_set};

            return ret;
        }
        // Moving a middle start sideband; this is affected by the endband.
        var startSlot = this.sideBand[1] + "sp";
        var endSlot = this.sideBand[1] + "ep";
        var prevEndSlot =(this.sideBand[1] - 1) + "ep";

        temp_value = this.values[startSlot];

        to_set = temp_value - deltaX / 2000;

        if (to_set >= this.values[endSlot]) {
                to_set = this.values[endSlot];
            }

        if (to_set <= this.values[prevEndSlot]) {
                to_set = this.values[prevEndSlot];
            }

        ret = {"slot" : startSlot, "value" : to_set};
        return ret;

    }

    else if (this.sideBand[0] == 1) {
        //Moving the end sideband
        if (this.sideBand[1] == 0) {
            //Moving the last ending sideband: This is affected only by the hard limit.
            startSlot = this.sideBand[this.nBands] + "sp";
            endSlot = this.sideBand[this.nBands] + "ep";

            temp_value = this.values[endSlot];

            to_set = temp_value - deltaX / 2000;

            if (to_set < this.values[startSlot]) {
                to_set = this.values[startSlot];
            }
            if (to_set > 1) {
                to_set = 1;
            }

            ret = {"slot" : endSlot, "value" : to_set};

            return ret;
        }

        // Moving a middle end sideband; this is affected by other bands
        startSlot = this.sideBand[1] + "sp";
        endSlot = this.sideBand[1] + "ep";
        var nextStartSlot = (this.sideBand[1] + 1) + "sp";

        temp_value = this.values[endSlot];

        to_set = temp_value - deltaX / 2000;

        if (to_set < this.values[startSlot]) {
                to_set = this.values[startSlot];
            }

        if (to_set > this.values[nextStartSlot]) {
                to_set = this.values[nextStartSlot];
            }

        ret = {"slot" : endSlot, "value" : to_set};
        return ret;

    }
    else {
        //Shouldn't be here.
        throw new Error ("Error: sideband is neither start not end.");
    }

}

Multiband.prototype.setValue = function(slot, value) {

    var bandn = parseInt(slot, 10);
    var bandtype = slot.substring(slot.length - 2);

    // Bad hack. It catches the end or start points. TODO write it better.
    if ((bandtype == "sp") || (bandtype == "ep")) {

        // If it's a start or end point, don't make them overlap.
        var previous = undefined;
        var next = undefined;

        if ((bandtype == "sp") && (bandn == 0)) {
            if (value > this.values["0ep"]) {
                value = this.values["0ep"];
            }
        }

        else if ((bandtype == "ep") && (bandn == this.nBands - 1)) {
            previous = bandn + "sp";
            if (value < this.values[previous]) {
                value = this.values[previous];
            }
        }

        else {

            if (bandtype == "sp") {
                previous = (bandn - 1) + "ep";
                next = bandn + "ep";
            }
            else if (bandtype == "ep") {
                previous = bandn + "sp";
                next = (bandn + 1) + "sp";
            }

            if (value < this.values[previous]) {
                value = this.values[previous];
            }

            if (value > this.values[next]) {
                value = this.values[next];
            }

        }
    }

    //Can't call the superclass; we need to clear the old band, so we need a
    //special behaviour. TODO this is no more needed.

    if (this.values[slot] == undefined) {
        throw new Error ("Slot " + slot + " not present or value undefined");
    }

    if (value == this.values[slot]) {
        // Nothing to do.
        return;
    }

    //Must refresh / clear here. TODO we MUST insert this behaviour in the
    //canvasDraw class and call the superclass.

    if (this.drawItself == true) {
        // Refreshes the background
        this.refresh(true);
    }

    this.values[slot] = value;

    if (this.drawItself == true) {
        // Paints the bands
        this.refresh();
    }

}

Multiband.prototype.refresh = function (clear) {

        if (this.drawClass == undefined) {
            throw new Error ("Error: drawClass is undefined!");
        }
        // Draw yourself! This is per-class behaviour.
        //console.log (this.name, "'s drawClass is drawing itself!");

        //If we clear, we clear the whole background.
        if (clear == true) {

                this.drawClass.clear (this.xOrigin,
                                      this.yOrigin,
                                      this.width,
                                      this.height);
                                return;
            }

        // Here we do the math and draw ourselves
        this.calculateSidebands();

        for (var i = 0; i < this.nBands; i++) {

            var height = (1 - this.values[i + "height"]) * this.height;
            var range = this.values["color_range"];
            var colValue = this.values[i + "color"];
            var color_shade = (colValue - 0.5) * range;
            this.drawClass.draw(this.sideBands[i]["startXPoint"],
                            this.yOrigin + height,
                            this.sideBands[i]["endXPoint"] - this.sideBands[i]["startXPoint"],
                            this.height - height,
                            color_shade);
                        }
                    }

<commit_msg>JSLinted Multiband.js<commit_after>function Multiband(name, topleft, nBands, wh, color_range) {
    if (arguments.length) {
        this.getready(name, topleft, nBands, wh, color_range);
    }
}

//inherit from the Element prototype
Multiband.prototype = new Element();
//put the correct constructor reference back (not essential)
Multiband.prototype.constructor = Multiband;

Multiband.prototype.getready = function (name, topleft, nBands, wh, color_range) {

    var valueName,
        i;

    //reference the getready method from the parent class
    this.tempReady = Element.prototype.getready;
    //and run it as if it were part of this object
    this.tempReady(name, topleft);

    this.nBands = nBands;
    this.sideBands = new Array (this.nBands);

    // The values. Every band has its starting point, height, width, hight and color.
    // TODO they should be Nan.
    this.values = {};

    for (i = 0; i < nBands; i += 1) {
        valueName = i + "sp";
        this.values[valueName] = 0;
        valueName = i + "ep";
        this.values[valueName] = 0;
        valueName = i + "height";
        this.values[valueName] = 0;
        valueName = i + "height";
        this.values[valueName] = 0;
        valueName = i + "color";
        this.values[valueName] = 0;
    }

    this.values.color_range = color_range;

    // The multiband display has a fixed width and height.
    this.width = wh[0];
    this.height = wh[1];

    this.drawClass = undefined;

    //By default, a Multiband always draws itself when value is set.
    this.drawItself = true;

    this.completed = true;

};

Multiband.prototype.calculateSidebands = function () {

    var startValue,
        endValue,
        heightValue,
        startXPoint,
        endXPoint,
        yPoint,
        i;

    for (i = 0; i < this.nBands; i += 1) {

        startValue = this.values[i + "sp"];
        endValue = this.values[i + "ep"];
        heightValue = this.values[i + "height"];

        startXPoint = this.xOrigin + (this.width * startValue);
        endXPoint = this.xOrigin + (this.width * endValue);
        yPoint = this.yOrigin + (1 - this.height * heightValue);

        this.sideBands[i] = {"startXPoint": startXPoint, "endXPoint": endXPoint, "yPoint": yPoint};
    }

};

Multiband.prototype.IsInROI = function (x, y) {
    var proximity,
        i,
        curSB;

    /* DOES NOT WORK */
    if ((x > this.xOrigin) && (y > this.yOrigin)) {
        if ((x < (this.xOrigin + this.width)) && (y < (this.yOrigin + this.height))) {

            //console.log(this.name, " ROI Handler: ", x, y, " is in band bounding box ", this.xOrigin, this.yOrigin, this.xOrigin + this.width, this.yOrigin + this.height);

            // It's in the multiband's real estate on the screen. Now we got to find if it's on the proximity of a sideband.
            // TODO set this somewhere else
            proximity = 3;
            this.calculateSidebands();
            for (i = 0; i < this.nBands; i += 1) {

                curSB = this.sideBands[i];

                if (y < curSB.yPoint) {
                    //Too high!
                    //console.log("Too high! (", y, " vs ", curSB["yPoint"]);
                    continue;
                }
                // Check for the start side band
                if ((x > curSB.startXPoint - proximity) && (x < curSB.startXPoint + proximity)) {
                    //console.log(this.name, " ROI Handler: ", x, y, " is in ROI for starting point of sideband ", i);
                    // We got it!
                    this.sideBand = [0, i];
                    return true;
                }
                if ((x > curSB.endXPoint - proximity) && (x < curSB.endXPoint + proximity)) {
                    // We got it!
                    //console.log(this.name, " ROI Handler: ", x, y, " is in ROI for ending point of sideband ", i);
                    this.sideBand = [1, i];
                    return true;
                }
            }
        }
    }
    //console.log(this.name, " ROI Handler: ", x, y, " is NOT in sideband ROI ");
    //console.log ("Returning false");
    return false;
};

Multiband.prototype.onROI = function (start_x, start_y, curr_x, curr_y) {

    var temp_value,
            to_set,
            ret,
            startSlot,
            endSlot,
            prevEndSlot,
            nextStartSlot,
            deltaX;

    deltaX = curr_x - start_x;

    if (this.sideBand[0] === 0) {

        //Moving the start sideband
        if (this.sideBand[1] === 0) {
            //Moving the first starting sideband: this is affected by the endband.
            temp_value = this.values.band0sp;

            to_set = temp_value - deltaX / 2000;

            if (to_set >= this.values["0ep"]) {
                to_set = this.values["0ep"];
            }
            if (to_set < 0) {
                to_set = 0;
            }

            ret = {"slot" : "0sp", "value" : to_set};

            return ret;
        }
        // Moving a middle start sideband; this is affected by the endband.
        startSlot = this.sideBand[1] + "sp";
        endSlot = this.sideBand[1] + "ep";
        prevEndSlot = (this.sideBand[1] - 1) + "ep";

        temp_value = this.values[startSlot];

        to_set = temp_value - deltaX / 2000;

        if (to_set >= this.values[endSlot]) {
            to_set = this.values[endSlot];
        }

        if (to_set <= this.values[prevEndSlot]) {
            to_set = this.values[prevEndSlot];
        }

        ret = {"slot" : startSlot, "value" : to_set};
        return ret;

    }

    else if (this.sideBand[0] === 1) {
        //Moving the end sideband
        if (this.sideBand[1] === 0) {
            //Moving the last ending sideband: This is affected only by the hard limit.
            startSlot = this.sideBand[this.nBands] + "sp";
            endSlot = this.sideBand[this.nBands] + "ep";

            temp_value = this.values[endSlot];

            to_set = temp_value - deltaX / 2000;

            if (to_set < this.values[startSlot]) {
                to_set = this.values[startSlot];
            }
            if (to_set > 1) {
                to_set = 1;
            }

            ret = {"slot" : endSlot, "value" : to_set};

            return ret;
        }

        // Moving a middle end sideband; this is affected by other bands
        startSlot = this.sideBand[1] + "sp";
        endSlot = this.sideBand[1] + "ep";
        nextStartSlot = (this.sideBand[1] + 1) + "sp";

        temp_value = this.values[endSlot];

        to_set = temp_value - deltaX / 2000;

        if (to_set < this.values[startSlot]) {
            to_set = this.values[startSlot];
        }

        if (to_set > this.values[nextStartSlot]) {
            to_set = this.values[nextStartSlot];
        }

        ret = {"slot" : endSlot, "value" : to_set};
        return ret;

    }
    else {
        //Shouldn't be here.
        throw new Error("Error: sideband is neither start not end.");
    }

};

Multiband.prototype.setValue = function (slot, value) {

    var bandn,
        bandtype,
        previous,
        next;

    bandn = parseInt(slot, 10);
    bandtype = slot.substring(slot.length - 2);

    // Bad hack. It catches the end or start points. TODO write it better.
    if ((bandtype === "sp") || (bandtype === "ep")) {

        // If it's a start or end point, don't make them overlap.
        previous = undefined;
        next = undefined;

        if ((bandtype === "sp") && (bandn === 0)) {
            if (value > this.values["0ep"]) {
                value = this.values["0ep"];
            }
        }

        else if ((bandtype === "ep") && (bandn === this.nBands - 1)) {
            previous = bandn + "sp";
            if (value < this.values[previous]) {
                value = this.values[previous];
            }
        }

        else {

            if (bandtype === "sp") {
                previous = (bandn - 1) + "ep";
                next = bandn + "ep";
            }
            else if (bandtype === "ep") {
                previous = bandn + "sp";
                next = (bandn + 1) + "sp";
            }

            if (value < this.values[previous]) {
                value = this.values[previous];
            }

            if (value > this.values[next]) {
                value = this.values[next];
            }

        }
    }

    //Can't call the superclass; we need to clear the old band, so we need a
    //special behaviour. TODO this is no more needed.

    if (this.values[slot] === undefined) {
        throw new Error("Slot " + slot + " not present or value undefined");
    }

    if (value === this.values[slot]) {
        // Nothing to do.
        return;
    }

    //Must refresh / clear here. TODO we MUST insert this behaviour in the
    //canvasDraw class and call the superclass.
    if (this.drawItself === true) {
        // Refreshes the background
        this.refresh(true);
    }

    this.values[slot] = value;

    if (this.drawItself === true) {
        // Paints the bands
        this.refresh();
    }

};

Multiband.prototype.refresh = function (clear) {

    var height,
        range,
        colValue,
        color_shade,
        i;

    if (this.drawClass === undefined) {
        throw new Error("Error: drawClass is undefined!");
    }
    // Draw yourself! This is per-class behaviour.
    //console.log (this.name, "'s drawClass is drawing itself!");

    //If we clear, we clear the whole background.
    if (clear === true) {
        this.drawClass.clear(this.xOrigin,
                             this.yOrigin,
                             this.width,
                             this.height);
        return;
    }

    // Here we do the math and draw ourselves
    this.calculateSidebands();

    for (i = 0; i < this.nBands; i += 1) {

        height = (1 - this.values[i + "height"]) * this.height;
        range = this.values.color_range;
        colValue = this.values[i + "color"];
        color_shade = (colValue - 0.5) * range;
        this.drawClass.draw(this.sideBands[i].startXPoint,
                        this.yOrigin + height,
                        this.sideBands[i].endXPoint - this.sideBands[i].startXPoint,
                        this.height - height,
                        color_shade);
    }
};

<|endoftext|>


<commit_before>/*
# mixin

Adds a set of new client-side development configuration methods to the GruntConfig class
provided by `thehelp-project`.
*/

'use strict';

var fs = require('fs');
var path = require('path');
var _ = require('lodash');

module.exports = function mixin(GruntConfig) {

  var previous =
    GruntConfig.prototype.previousSetup = GruntConfig.prototype.previousSetup || [];

  var parent = GruntConfig.prototype.standardSetup;
  previous.push(parent);

  GruntConfig.prototype.standardSetup = function(options) {
    options = options || {};

    parent.call(this, options);

    this.registerConnect(options.connect);
  };

  /*
  `registerConnect` sets up two targets for the `grunt-contrib-connect`
  task, which runs a basic file server. Two different targets are provided:

  + test: On port 3001, this server will stop as soon as the grunt run stops,
  which means that it is only useful for grunt-based testing.
  + keepalive: A server on 3000 that runs until explicitly stopped - good
  for active development.

  */
  GruntConfig.prototype.registerConnect = function(options) {
    this.loadLocalNpm('grunt-contrib-connect', __dirname);

    this.grunt.config('connect', options || {
      test: {
        options: {
          base: '.',
          port: 3001
        }
      },
      keepalive: {
        options: {
          base: '.',
          port: 3000,
          keepalive: true
        }
      }
    });
  };

  // `registerMocha` pulls in `grunt-mocha` which uses `phantomjs`
  // and a custom bridge to run in-browser tests on the command line. You're
  // just responsible for the collection of `urls` to hit. You might consider using
  // the port 3001 urls available with the `connect` task above.
  GruntConfig.prototype.registerMocha = function(options) {
    options = options || {};

    if (!options.urls) {
      throw new Error('Need to provide array of urls to registerMocha()!');
    }

    options.reporter = options.reporter || 'Spec';

    if (typeof options.run === 'undefined') {
      //default to allowing tests to run when they are ready
      options.run = false;
    }

    if (typeof options.log === 'undefined') {
      //default to piping browser console output back to the command line
      options.log = true;
    }

    this.loadLocalNpm('grunt-mocha', __dirname);

    this.grunt.config('mocha', {
      default: {
        options: options
      }
    });
  };

  // `registerOptimizeLibrary` uses `grunt-requirejs` to produce one concatenated file,
  // by default optimized as well.
  GruntConfig.prototype.registerOptimize = function(options) {
    /*jshint maxcomplexity: 11 */

    if (!this.requirejsRegistered) {
      this.loadLocalNpm('grunt-requirejs', __dirname);
      this.requirejsRegistered = true;
    }

    options = options || {};
    options = _.cloneDeep(options);
    var config = options.config;

    if (!config) {
      throw new Error('Need to provide options.config with requirejs options!');
    }

    if (!options.name) {
      throw new Error('Need to provide options.name!');
    }

    if (options.standalone) {
      config.almond = true;
    }

    options.outName = options.outName || options.name;
    config.name = options.name;
    config.out = config.out || options.outName;

    //turning on source maps unless explictly configured
    if (typeof config.generateSourceMaps === 'undefined') {
      config.optimize = 'uglify2';
      config.generateSourceMaps = true;
      config.preserveLicenseComments = false;
    }

    if (options.empty) {
      _.forEach(options.empty, function(module) {
        config.paths[module] = 'empty:';
      });
    }

    var taskName = 'requirejs.' + options.outName + (options.postfix || '') + '.options';
    this.grunt.config(taskName, config);
  };

  /*
  `registerOptimizeLibrary` uses `grunt-requirejs` to produce both optimized
  and unoptimized versions of a given library using the r.js optimizer.
  If specified, `standalone` versions of that library can be produced as well
  (using almond), resulting in six total files:

  + library.js
  + library.min.js
  + library.min.js.map
  + standalone/library.js
  + standalone/library.min.js
  + standaline/library.min.js.map

  */
  GruntConfig.prototype.registerOptimizeLibrary = function(options) {

    options = options || {};
    options = _.cloneDeep(options);
    var config = options.config || {};
    var basePath = options.basePath || 'dist';
    var out = options.outName || options.name;

    //not minified, needs requirejs
    config.optimize = 'none';
    config.generateSourceMaps = false;
    config.preserveLicenseComments = true;

    config.out = path.join(basePath, out + '.js');
    this.registerOptimize(options);

    //minified, needs requirejs
    delete config.generateSourceMaps;

    options.postfix = '-min';
    config.out = path.join(basePath, out + '.min.js');
    this.registerOptimize(options);

    if (options.standalone) {

      delete options.postfix;

      //not minified, standalone with almond.js
      config.optimize = 'none';
      config.generateSourceMaps = false;
      config.preserveLicenseComments = true;

      options.postFix = '-standalone';
      config.out = path.join(basePath, 'standalone', out + '.js');
      this.registerOptimize(options);

      //minified, standalone with almond.js
      delete config.optimize;
      delete config.generateSourceMaps;

      options.almond = true;
      options.postFix = '-standalone-min';
      config.out = path.join(basePath, 'standalone', out + '.min.js');
      this.registerOptimize(options);
    }
  };

  // `registerCopyFromDist` uses `grunt-contrib-copy` to copy all files under the
  // 'dist/' folders of a list of specified npm-installed modules.
  GruntConfig.prototype.registerCopyFromDist = function(options) {
    options = options || {};

    this.registerCopy();

    options.target = options.target || 'lib/vendor';

    if (!options.modules) {
      throw new Error('Need to provide list of modules!');
    }

    var files = [];
    _.forEach(options.modules, function(module) {
      files.push({
        expand: true,
        cwd: path.join('node_modules', module, 'dist'),
        src: ['**/*'],
        dest: options.target
      });
    });

    if (files.length) {
      this.grunt.config('copy.from-dist', {
        files: files
      });
    }
  };

  // `registerCopyFromBower` uses `grunt-contrib-copy` to copy all files installed
  // by bower of the form 'bower_components/[module]/[module].js' to 'lib/vendor'.
  // You can use the `bowerSpecialCases` hash to override the default expected file
  // location.
  GruntConfig.prototype.bowerSpecialCases = {
    async: 'lib/async.js',
    hoverintent: 'jquery.hoverIntent.js',
    jquery: 'dist/jquery.js',
    labjs: 'LAB.min.js',
    lodash: 'dist/lodash.compat.js',
    requirejs: 'require.js',
    'spin.js': 'spin.js',
    tipsy: 'src/jquery.tipsy.js'
  };

  GruntConfig.prototype.fs = fs;

  GruntConfig.prototype.registerCopyFromBower = function(options) {
    options = options || {};

    var _this = this;
    var files = {};
    var dirs;

    options.target = options.target || 'lib/vendor';
    options.source = options.source || 'bower_components';

    this.registerCopy();

    try {
      dirs = this.fs.readdirSync(options.source);
    }
    catch (err) {
      this.grunt.log.warn(
        'registerCopyFromBower: Couldn\'t load installed bower components.'
      );
      return;
    }

    _.forEach(dirs, function(dir) {
      var filename = _this.bowerSpecialCases[dir] || dir + '.js';

      var file = path.join(options.source, dir, filename);

      try {
        if (_this.fs.statSync(file).isFile()) {
          files[path.join(options.target, path.basename(filename))] = file;
        }
      }
      catch (err) {
        _this.grunt.log.verbose.writeln('\n' + 'Warning: '.red + dir +
         ' bower directory didn\'t contain \'' + filename + '\'\n');
      }
    });

    if (_.keys(files).length) {
      _this.grunt.config('copy.from-bower', {
        files: files
      });
    }
  };

};
<commit_msg>optimizeLibrary - no longer generate source maps<commit_after>/*
# mixin

Adds a set of new client-side development configuration methods to the GruntConfig class
provided by `thehelp-project`.
*/

'use strict';

var fs = require('fs');
var path = require('path');
var _ = require('lodash');

module.exports = function mixin(GruntConfig) {

  var previous =
    GruntConfig.prototype.previousSetup = GruntConfig.prototype.previousSetup || [];

  var parent = GruntConfig.prototype.standardSetup;
  previous.push(parent);

  GruntConfig.prototype.standardSetup = function(options) {
    options = options || {};

    parent.call(this, options);

    this.registerConnect(options.connect);
  };

  /*
  `registerConnect` sets up two targets for the `grunt-contrib-connect`
  task, which runs a basic file server. Two different targets are provided:

  + test: On port 3001, this server will stop as soon as the grunt run stops,
  which means that it is only useful for grunt-based testing.
  + keepalive: A server on 3000 that runs until explicitly stopped - good
  for active development.

  */
  GruntConfig.prototype.registerConnect = function(options) {
    this.loadLocalNpm('grunt-contrib-connect', __dirname);

    this.grunt.config('connect', options || {
      test: {
        options: {
          base: '.',
          port: 3001
        }
      },
      keepalive: {
        options: {
          base: '.',
          port: 3000,
          keepalive: true
        }
      }
    });
  };

  // `registerMocha` pulls in `grunt-mocha` which uses `phantomjs`
  // and a custom bridge to run in-browser tests on the command line. You're
  // just responsible for the collection of `urls` to hit. You might consider using
  // the port 3001 urls available with the `connect` task above.
  GruntConfig.prototype.registerMocha = function(options) {
    options = options || {};

    if (!options.urls) {
      throw new Error('Need to provide array of urls to registerMocha()!');
    }

    options.reporter = options.reporter || 'Spec';

    if (typeof options.run === 'undefined') {
      //default to allowing tests to run when they are ready
      options.run = false;
    }

    if (typeof options.log === 'undefined') {
      //default to piping browser console output back to the command line
      options.log = true;
    }

    this.loadLocalNpm('grunt-mocha', __dirname);

    this.grunt.config('mocha', {
      default: {
        options: options
      }
    });
  };

  // `registerOptimizeLibrary` uses `grunt-requirejs` to produce one concatenated file,
  // by default optimized as well.
  GruntConfig.prototype.registerOptimize = function(options) {
    /*jshint maxcomplexity: 11 */

    if (!this.requirejsRegistered) {
      this.loadLocalNpm('grunt-requirejs', __dirname);
      this.requirejsRegistered = true;
    }

    options = options || {};
    options = _.cloneDeep(options);
    var config = options.config;

    if (!config) {
      throw new Error('Need to provide options.config with requirejs options!');
    }

    if (!options.name) {
      throw new Error('Need to provide options.name!');
    }

    if (options.standalone) {
      config.almond = true;
    }

    options.outName = options.outName || options.name;
    config.name = options.name;
    config.out = config.out || options.outName;

    //turning on source maps unless explictly configured
    if (typeof config.generateSourceMaps === 'undefined') {
      config.optimize = 'uglify2';
      config.generateSourceMaps = true;
      config.preserveLicenseComments = false;
    }

    if (options.empty) {
      _.forEach(options.empty, function(module) {
        config.paths[module] = 'empty:';
      });
    }

    var taskName = 'requirejs.' + options.outName + (options.postfix || '') + '.options';
    this.grunt.config(taskName, config);
  };

  /*
  `registerOptimizeLibrary` uses `grunt-requirejs` to produce both optimized
  and unoptimized versions of a given library using the r.js optimizer.
  If specified, `standalone` versions of that library can be produced as well
  (using almond), resulting in four total files:

  + library.js
  + library.min.js
  + standalone/library.js
  + standalone/library.min.js

  */
  GruntConfig.prototype.registerOptimizeLibrary = function(options) {

    options = options || {};
    options = _.cloneDeep(options);
    var config = options.config || {};
    var basePath = options.basePath || 'dist';
    var out = options.outName || options.name;

    //we never want source maps for libraries
    config.generateSourceMaps = false;
    config.preserveLicenseComments = true;

    //not minified, needs requirejs
    config.optimize = 'none';
    config.out = path.join(basePath, out + '.js');
    this.registerOptimize(options);

    //minified, needs requirejs
    delete config.optimize;
    options.postfix = '-min';
    config.out = path.join(basePath, out + '.min.js');
    this.registerOptimize(options);

    if (options.standalone) {

      //not minified, standalone with almond.js
      config.optimize = 'none';
      options.postfix = '-standalone';
      config.out = path.join(basePath, 'standalone', out + '.js');
      this.registerOptimize(options);

      //minified, standalone with almond.js
      delete config.optimize;
      options.postfix = '-standalone-min';
      config.out = path.join(basePath, 'standalone', out + '.min.js');
      this.registerOptimize(options);
    }
  };

  // `registerCopyFromDist` uses `grunt-contrib-copy` to copy all files under the
  // 'dist/' folders of a list of specified npm-installed modules.
  GruntConfig.prototype.registerCopyFromDist = function(options) {
    options = options || {};

    this.registerCopy();

    options.target = options.target || 'lib/vendor';

    if (!options.modules) {
      throw new Error('Need to provide list of modules!');
    }

    var files = [];
    _.forEach(options.modules, function(module) {
      files.push({
        expand: true,
        cwd: path.join('node_modules', module, 'dist'),
        src: ['**/*'],
        dest: options.target
      });
    });

    if (files.length) {
      this.grunt.config('copy.from-dist', {
        files: files
      });
    }
  };

  // `registerCopyFromBower` uses `grunt-contrib-copy` to copy all files installed
  // by bower of the form 'bower_components/[module]/[module].js' to 'lib/vendor'.
  // You can use the `bowerSpecialCases` hash to override the default expected file
  // location.
  GruntConfig.prototype.bowerSpecialCases = {
    async: 'lib/async.js',
    hoverintent: 'jquery.hoverIntent.js',
    jquery: 'dist/jquery.js',
    labjs: 'LAB.min.js',
    lodash: 'dist/lodash.compat.js',
    requirejs: 'require.js',
    'spin.js': 'spin.js',
    tipsy: 'src/jquery.tipsy.js'
  };

  GruntConfig.prototype.fs = fs;

  GruntConfig.prototype.registerCopyFromBower = function(options) {
    options = options || {};

    var _this = this;
    var files = {};
    var dirs;

    options.target = options.target || 'lib/vendor';
    options.source = options.source || 'bower_components';

    this.registerCopy();

    try {
      dirs = this.fs.readdirSync(options.source);
    }
    catch (err) {
      this.grunt.log.warn(
        'registerCopyFromBower: Couldn\'t load installed bower components.'
      );
      return;
    }

    _.forEach(dirs, function(dir) {
      var filename = _this.bowerSpecialCases[dir] || dir + '.js';

      var file = path.join(options.source, dir, filename);

      try {
        if (_this.fs.statSync(file).isFile()) {
          files[path.join(options.target, path.basename(filename))] = file;
        }
      }
      catch (err) {
        _this.grunt.log.verbose.writeln('\n' + 'Warning: '.red + dir +
         ' bower directory didn\'t contain \'' + filename + '\'\n');
      }
    });

    if (_.keys(files).length) {
      _this.grunt.config('copy.from-bower', {
        files: files
      });
    }
  };

};
<|endoftext|>


<commit_before>import React, { Component } from "react";
import moment from "moment";
import classNames from "classnames";
import { debounce } from 'lodash/function';

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

    this.state = {
      editing: false,
      text: "",
    };

    this.saveTodo = this.saveTodo.bind(this);
    this.onFocus = this.onFocus.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.onChange = this.onChange.bind(this);
    this.onKeyDown = this.onKeyDown.bind(this);

    this.saveTodoHandler = debounce(function () {
      this.saveTodo()
    }, 500);
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      text: nextProps.text
    });
  }

  saveTodo() {
    this.props.saveTodo(this.props.firebaseKey, this.props.day.valueOf(), this.state.text);
  }

  onFocus() {
    this.setState({editing: true});
    this.props.focusDay(this.props.day);
  }

  onBlur() {
    this.setState({
      editing: false
    });
    this.props.focusDay(null);
    this.saveTodo();
  }

  onChange(event) {
    this.setState({text: event.target.value})
    this.saveTodoHandler();
  }

  onKeyDown(event) {
    if (event.keyCode === 27 /*esc*/) {
      this.textarea.blur();
    }
  }

  render() {
    const dayClasses = classNames({
      "day flex vertical padding-0-5 padding-top overflow-hidden min-day-width": true,
      ["bg-" + this.props.colorNumber]: true,
      ["color-" + (this.props.colorNumber+2)]: true,
      "focused-day color-5": this.props.isFocusedDay,
      "focused-weekday": this.props.isFocusedWeekDay,
      "unfocused-day": this.props.aDayIsFocused && !this.props.isFocusedDay,
    });

    let monthLabel;

    if (
        (this.props.isFirstWeek && this.props.day.isSame(moment(this.props.day).startOf("isoweek"))) ||
        this.props.day.isSame(moment(this.props.day).startOf("month"))
    ) {
      monthLabel = this.props.day.format("MMM");
    }

    return (

            {this.props.day.format("ddd DD")} {monthLabel}

        <textarea
          id={this.props.day.valueOf()}
          ref={(c) => this.textarea = c}
          className={classNames({
            "padding-0-25 grow width-100": true,
            // "nowrap": !this.state.editing,
          })}
          onFocus={this.onFocus}
          onBlur={this.onBlur}
          onChange={this.onChange}
          value={this.state.text}
          onKeyDown={this.onKeyDown}
        />

    );
  }
}

export default Day;
<commit_msg>Improves date labels<commit_after>import React, { Component } from "react";
import moment from "moment";
import classNames from "classnames";
import { debounce } from 'lodash/function';

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

    this.state = {
      editing: false,
      text: "",
    };

    this.saveTodo = this.saveTodo.bind(this);
    this.onFocus = this.onFocus.bind(this);
    this.onBlur = this.onBlur.bind(this);
    this.onChange = this.onChange.bind(this);
    this.onKeyDown = this.onKeyDown.bind(this);

    this.saveTodoHandler = debounce(function () {
      this.saveTodo()
    }, 500);
  }

  componentWillReceiveProps(nextProps) {
    this.setState({
      text: nextProps.text
    });
  }

  saveTodo() {
    this.props.saveTodo(this.props.firebaseKey, this.props.day.valueOf(), this.state.text);
  }

  onFocus() {
    this.setState({editing: true});
    this.props.focusDay(this.props.day);
  }

  onBlur() {
    this.setState({
      editing: false
    });
    this.props.focusDay(null);
    this.saveTodo();
  }

  onChange(event) {
    this.setState({text: event.target.value})
    this.saveTodoHandler();
  }

  onKeyDown(event) {
    if (event.keyCode === 27 /*esc*/) {
      this.textarea.blur();
    }
  }

  render() {
    const dayClasses = classNames({
      "day flex vertical padding-0-5 padding-top overflow-hidden min-day-width": true,
      ["bg-" + this.props.colorNumber]: true,
      ["color-" + (this.props.colorNumber+2)]: true,
      "focused-day color-5": this.props.isFocusedDay,
      "focused-weekday": this.props.isFocusedWeekDay,
      "unfocused-day": this.props.aDayIsFocused && !this.props.isFocusedDay,
    });

    let dayLabel, monthLabel;

    if (this.props.isToday) {
      dayLabel = "Today " + this.props.day.format("DD")
    }
    else {
      dayLabel = this.props.day.format("ddd DD")
    }

    if (
        (this.props.isFirstWeek && this.props.day.isSame(moment(this.props.day).startOf("isoweek"))) ||
        this.props.day.isSame(moment(this.props.day).startOf("month"))
    ) {
      monthLabel = ` ${this.props.day.format("MMM YYYY")}`;
    }

    return (

            {dayLabel}{monthLabel}

        <textarea
          id={this.props.day.valueOf()}
          ref={(c) => this.textarea = c}
          className={classNames({
            "padding-0-25 grow width-100": true,
            // "nowrap": !this.state.editing,
          })}
          onFocus={this.onFocus}
          onBlur={this.onBlur}
          onChange={this.onChange}
          value={this.state.text}
          onKeyDown={this.onKeyDown}
        />

    );
  }
}

export default Day;
<|endoftext|>


<commit_before>describe( 'ExterminateGlobals.JS', function() {

	beforeEach( function () {
		ExterminateGlobals.resetConvenienceFunctions();
	} );

	it( 'has all the methods', function () {
		expect( window.ExterminateGlobals ).toBeDefined();
		expect( window.ExterminateGlobals.GlobalsCollector ).toBeDefined();
		expect( window.ExterminateGlobals.start ).toBeDefined();
		expect( window.ExterminateGlobals.stop ).toBeDefined();
		expect( window.ExterminateGlobals.collectMembers ).toBeUndefined();
	} );

	describe( 'GlobalsCollector', function () {
		describe( 'constructor', function () {
			it( 'adds "ExterminateGlobals" to the list of ignored keys', function () {
				var gc = new ExterminateGlobals.GlobalsCollector();

				expect( gc._ignoreKeys ).toEqual( [ 'ExterminateGlobals'] );
			} );

			it( 'creates a copy of ignored globals', function () {
				var ignoreKeys = [ 'ExterminateGlobals', 'foo' ];
				var gc = new ExterminateGlobals.GlobalsCollector( ignoreKeys );

				expect( gc._ignoreKeys ).not.toBe( ignoreKeys );
			} );
		} );

		describe( 'Getters', function () {
			it( 'getMonitoredObject() gets the correct monitored object', function () {
				var monitoredObject = { foo: 'bar' };
				var gc = new ExterminateGlobals.GlobalsCollector( [], monitoredObject );

				expect( gc.getMonitoredObject() ).toBe( monitoredObject );
			} );

			describe( 'getIgnoredKeys()', function () {
				it( 'gets the correct ignored keys', function () {
					var gc = new ExterminateGlobals.GlobalsCollector( [ 'foo', 'bar' ] );

					expect( gc.getIgnoredKeys() ).toEqual( [ 'foo', 'bar', 'ExterminateGlobals' ] );
				} );

				it( 'returns a copy of the list', function () {
					var dummyIgnored = [ 'foo', 'bar' ];
					var gc = new ExterminateGlobals.GlobalsCollector();
					gc._ignoreKeys = dummyIgnored;

					var returnedIgnored = gc.getIgnoredKeys();
					returnedIgnored.push( 'baz' );

					expect( gc.getIgnoredKeys() ).not.toBe( dummyIgnored );
				} );
			} );

			describe( 'getUnwantedGlobals()', function () {
				it( 'gets an empty array before collecting', function () {
					var gc = new ExterminateGlobals.GlobalsCollector();

					expect( gc.getUnwantedGlobals() ).toEqual( [] );
				} );

				it( 'returns a copy of the list', function () {
					var dummyUnwanted = [ 'foo', 'bar' ];
					var gc = new ExterminateGlobals.GlobalsCollector();
					gc._unwantedGlobals = dummyUnwanted;

					var returnedGlobals = gc.getUnwantedGlobals();
					returnedGlobals.push( 'baz' );

					expect( gc.getUnwantedGlobals() ).not.toBe( dummyUnwanted );
				} );
			} );
		} );

		describe( 'startCollecting()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );

		describe( 'collect()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );

		describe( 'print()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );
	} );

	describe( 'convenience functions', function () {
		describe( 'start()', function () {
			it( 'creates a new instance of GlobalsCollector', function () {
				ExterminateGlobals.start();
				expect( ExterminateGlobals.getHelperGlobalCollector() instanceof ExterminateGlobals.GlobalsCollector ).toBe( true );
			} );

			it( 'throws Error if called at inappropriate time', function () {
				ExterminateGlobals.start();
				expect( ExterminateGlobals.start ).toThrow();
			} );
		} );

		describe( 'stop()', function () {
			it( 'resets helper', function () {
				ExterminateGlobals.start();
				ExterminateGlobals.stop();
				expect( ExterminateGlobals.getHelperGlobalCollector() ).toBeNull();
			} );

			it( 'throws Error if called at inappropriate time', function () {
				expect( ExterminateGlobals.stop ).toThrow();
			} );
		} );
	} );

} );<commit_msg>Some more tests<commit_after>describe( 'ExterminateGlobals.JS', function() {

	beforeEach( function () {
		ExterminateGlobals.resetConvenienceFunctions();
	} );

	it( 'has all the methods', function () {
		expect( window.ExterminateGlobals ).toBeDefined();
		expect( window.ExterminateGlobals.GlobalsCollector ).toBeDefined();
		expect( window.ExterminateGlobals.start ).toBeDefined();
		expect( window.ExterminateGlobals.stop ).toBeDefined();
		expect( window.ExterminateGlobals.collectMembers ).toBeUndefined();
	} );

	describe( 'GlobalsCollector', function () {
		describe( 'constructor', function () {
			it( 'adds "ExterminateGlobals" to the list of ignored keys', function () {
				var gc = new ExterminateGlobals.GlobalsCollector();

				expect( gc._ignoreKeys ).toEqual( [ 'ExterminateGlobals'] );
			} );

			it( 'creates a copy of ignored globals', function () {
				var ignoreKeys = [ 'ExterminateGlobals', 'foo' ];
				var gc = new ExterminateGlobals.GlobalsCollector( ignoreKeys );

				expect( gc._ignoreKeys ).not.toBe( ignoreKeys );
			} );
		} );

		describe( 'Getters', function () {
			it( 'getMonitoredObject() gets the correct monitored object', function () {
				var monitoredObject = { foo: 'bar' };
				var gc = new ExterminateGlobals.GlobalsCollector( [], monitoredObject );

				expect( gc.getMonitoredObject() ).toBe( monitoredObject );
			} );

			describe( 'getIgnoredKeys()', function () {
				it( 'gets the correct ignored keys', function () {
					var gc = new ExterminateGlobals.GlobalsCollector( [ 'foo', 'bar' ] );

					expect( gc.getIgnoredKeys() ).toEqual( [ 'foo', 'bar', 'ExterminateGlobals' ] );
				} );

				it( 'returns a copy of the list', function () {
					var dummyIgnored = [ 'foo', 'bar' ];
					var gc = new ExterminateGlobals.GlobalsCollector();
					gc._ignoreKeys = dummyIgnored;

					var returnedIgnored = gc.getIgnoredKeys();
					returnedIgnored.push( 'baz' );

					expect( gc.getIgnoredKeys() ).not.toBe( dummyIgnored );
				} );
			} );

			describe( 'getUnwantedGlobals()', function () {
				it( 'gets an empty array before collecting', function () {
					var gc = new ExterminateGlobals.GlobalsCollector();

					expect( gc.getUnwantedGlobals() ).toEqual( [] );
				} );

				it( 'returns a copy of the list', function () {
					var dummyUnwanted = [ 'foo', 'bar' ];
					var gc = new ExterminateGlobals.GlobalsCollector();
					gc._unwantedGlobals = dummyUnwanted;

					var returnedGlobals = gc.getUnwantedGlobals();
					returnedGlobals.push( 'baz' );

					expect( gc.getUnwantedGlobals() ).not.toBe( dummyUnwanted );
				} );
			} );
		} );

		describe( 'startCollecting() & collect()', function () {
			it( 'reports the globals correctly', function () {
				var target = { foo: 'bar' };
				var gc = new ExterminateGlobals.GlobalsCollector( [], target );
				gc.startCollecting();

				target.bla = 'fla';

				var collected = gc.collect();

				expect( collected ).toEqual( [ 'bla'] );

			} );
		} );

		describe( 'print()', function () {
			it( 'todo', function () {
				expect( true ).toBe( true );
			} );
		} );
	} );

	describe( 'convenience functions', function () {
		describe( 'start()', function () {
			it( 'creates a new instance of GlobalsCollector', function () {
				ExterminateGlobals.start();
				expect( ExterminateGlobals.getHelperGlobalCollector() instanceof ExterminateGlobals.GlobalsCollector ).toBe( true );
			} );

			it( 'throws Error if called at inappropriate time', function () {
				ExterminateGlobals.start();
				expect( ExterminateGlobals.start ).toThrow();
			} );
		} );

		describe( 'stop()', function () {
			it( 'resets helper', function () {
				ExterminateGlobals.start();
				ExterminateGlobals.stop();
				expect( ExterminateGlobals.getHelperGlobalCollector() ).toBeNull();
			} );

			it( 'throws Error if called at inappropriate time', function () {
				expect( ExterminateGlobals.stop ).toThrow();
			} );
		} );
	} );

} );<|endoftext|>


<commit_before>/**
 * Broadcast updates to client when the model changes
 */

'use strict';

var Q = require('q');
var Wallet = require('./../wallet/wallet.model');
var BankAccount = require('../bankaccount/bankaccount.model');
var Utils = require('./../../utils/utils');

function makeTransfer(fromEmail, toEmail, amount) {
    var promiseFindSenderWallet = Wallet.findByOwnerEmail(fromEmail);
    var promiseFindRecvWallet = Wallet.findByOwnerEmail(toEmail);
    var promiseFindSenderBankAccount = BankAccount.findOneQ({ email: fromEmail }); // TODO what about when the bank is the receiver? should this happen?

    return Q.allSettled([promiseFindSenderWallet, promiseFindRecvWallet, promiseFindSenderBankAccount])
            .spread(function (senderWalletPromise, recvWalletPromise, senderBankAccount) {

        var deferred = Q.defer();

        var senderWallet = senderWalletPromise.value;
        var recvWallet = recvWalletPromise.value;
        var senderBank = senderBankAccount.value;

        function buildMissingError() {
            var result = {
                fromEmail: fromEmail,
                toEmail: toEmail,
                amount: amount,
                status: 'error',
                message: 'missing account'
            };

            Utils.getEventEmitter().emit('post:make_transfer', result);
            deferred.resolve(result);
        }

        // If the sender was a bank admin, get its wallet from bankaccounts
        if (!senderWallet && senderBank && recvWallet) {
            senderWallet = senderBank.hotWallet;
        }

        if (!senderWallet && !recvWallet) {
            // No wallets, it's a bust
            buildMissingError();
            return deferred.promise;
        }

        Utils.getNewConnectedRemote(senderWallet.address, senderWallet.secret).then(function (remote) {
            var transaction = remote.createTransaction('Payment', {
                account: senderWallet.address,
                destination: recvWallet.address,
                amount: amount + '/EUR/' + Utils.ROOT_RIPPLE_ACCOUNT.address
            });

            transaction.submit(function (err, res) {
                if (err) {
                    console.log(err);
                    Utils.getEventEmitter().emit('post:make_transfer', {
                        fromEmail: fromEmail,
                        toEmail: toEmail,
                        amount: amount,
                        message: 'Ripple error',
                        status: 'ripple error'
                    });
                    var stupidJavascriptError;
                    try {
                        stupidJavascriptError = new Error('ripple error')
                    } catch (e) {
                        deferred.reject(e);
                    }
                }
                if (res) {
                    Utils.getEventEmitter().emit('post:make_transfer', {
                        fromEmail: fromEmail,
                        toEmail: toEmail,
                        amount: amount,
                        status: 'success'
                    });
                    deferred.resolve({ status: 'success', transaction: transaction });
                }

            });
        });
        return deferred.promise;

    });
}

exports.makeTransfer = makeTransfer;

exports.register = function (socket) {
    Utils.getEventEmitter().on('post:make_transfer', function (data) {
        socket.emit('post:make_transfer', data);
    });

    Utils.getEventEmitter().on('make_transfer', function (data) {
        makeTransfer(data.fromEmail, data.toEmail, data.amount);
    });

    socket.on('make_transfer', function (data) {
        makeTransfer(data.fromEmail, data.toEmail, data.amount);
    });
};
<commit_msg>Re-add the aray checking code<commit_after>/**
 * Broadcast updates to client when the model changes
 */

'use strict';

var Q = require('q');
var Wallet = require('./../wallet/wallet.model');
var BankAccount = require('../bankaccount/bankaccount.model');
var Utils = require('./../../utils/utils');

function makeTransfer(fromEmail, toEmail, amount) {
    var promiseFindSenderWallet = Wallet.findByOwnerEmail(fromEmail);
    var promiseFindRecvWallet = Wallet.findByOwnerEmail(toEmail);
    var promiseFindSenderBankAccount = BankAccount.findOneQ({ email: fromEmail }); // TODO what about when the bank is the receiver? should this happen?

    return Q.allSettled([promiseFindSenderWallet, promiseFindRecvWallet, promiseFindSenderBankAccount])
            .spread(function (senderWalletPromise, recvWalletPromise, senderBankAccount) {

        var deferred = Q.defer();

        var senderWallet = senderWalletPromise.value;
        var recvWallet = recvWalletPromise.value;
        var senderBank = senderBankAccount.value;

        function buildMissingError() {
            var result = {
                fromEmail: fromEmail,
                toEmail: toEmail,
                amount: amount,
                status: 'error',
                message: 'missing account'
            };

            Utils.getEventEmitter().emit('post:make_transfer', result);
            deferred.resolve(result);
        }

        // If the sender was a bank admin, get its wallet from bankaccounts
        if (!senderWallet && senderBank && recvWallet) {
            senderWallet = senderBank.hotWallet;
        }

        if (!senderWallet && !recvWallet) {
            // No wallets, it's a bust
            buildMissingError();
            return deferred.promise;
        }

        // Not sure why these are arrays sometimes
        if (senderWallet.constructor === Array) {
            senderWallet = senderWallet[0];
        }
        if (recvWallet.constructor === Array) {
            recvWallet = recvWallet[0];
        }

        Utils.getNewConnectedRemote(senderWallet.address, senderWallet.secret).then(function (remote) {
            var transaction = remote.createTransaction('Payment', {
                account: senderWallet.address,
                destination: recvWallet.address,
                amount: amount + '/EUR/' + Utils.ROOT_RIPPLE_ACCOUNT.address
            });

            transaction.submit(function (err, res) {
                if (err) {
                    console.log(err);
                    Utils.getEventEmitter().emit('post:make_transfer', {
                        fromEmail: fromEmail,
                        toEmail: toEmail,
                        amount: amount,
                        message: 'Ripple error',
                        status: 'ripple error'
                    });
                    var stupidJavascriptError;
                    try {
                        stupidJavascriptError = new Error('ripple error')
                    } catch (e) {
                        deferred.reject(e);
                    }
                }
                if (res) {
                    Utils.getEventEmitter().emit('post:make_transfer', {
                        fromEmail: fromEmail,
                        toEmail: toEmail,
                        amount: amount,
                        status: 'success'
                    });
                    deferred.resolve({ status: 'success', transaction: transaction });
                }

            });
        });
        return deferred.promise;

    });
}

exports.makeTransfer = makeTransfer;

exports.register = function (socket) {
    Utils.getEventEmitter().on('post:make_transfer', function (data) {
        socket.emit('post:make_transfer', data);
    });

    Utils.getEventEmitter().on('make_transfer', function (data) {
        makeTransfer(data.fromEmail, data.toEmail, data.amount);
    });

    socket.on('make_transfer', function (data) {
        makeTransfer(data.fromEmail, data.toEmail, data.amount);
    });
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ImageSource
 * @flow
 */
'use strict';

export type ImageSource = {
  uri?: string,
} | number;
<commit_msg>Make flow types update to date with proptypes with ImageSource<commit_after>/**
 * Copyright (c) 2015-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @providesModule ImageSource
 * @flow
 */
'use strict';

// This is to sync with ImageSourcePropTypes.js.

type ImageURISource = {
  uri?: string,
  bundle?: string,
  method?: string,
  headers?: Object,
  body?: string,
  cache?: 'default' | 'reload' | 'force-cache' | 'only-if-cached',
  width?: number,
  height?: number,
  scale?: number,
};

export type ImageSource = ImageURISource | number | Array<ImageURISource>;
<|endoftext|>