<commit_before>var alexa = require('alexa-app');
var request = require('request-promise');
var express = require('express');
var nodecache = require('node-cache');

// Create instance of express
var express_app = express();
// Create a 1 hour cache for storing user devices
var cache = new nodecache({ stdTTL: 3600, checkperiod: 120 });
// Create instance of alexa-app
var app = new alexa.app('connect');
// Bind alexa-app to express instance
app.express({ expressApp: express_app });

// Run every time the skill is accessed
app.pre = function (req, res, type) {
    // Error if the application ID of the request is not for this skill
    if (req.applicationId != "amzn1.ask.skill.33d79728-0f5a-44e7-ae22-ccf0b0c0e9e0" &&
        req.getSession().details.application.applicationId != "amzn1.ask.skill.33d79728-0f5a-44e7-ae22-ccf0b0c0e9e0") {
        throw "Invalid applicationId";
    }
    // Check that the user has an access token, if they have linked their account
    if (!req.getSession().details.user.accessToken) {
        res.say("You have not linked your Spotify account, check your Alexa app to link the account");
        res.linkAccount();
    }
};

// Run after every request
app.post = function (req, res, type, exception) {
    if (exception) {
        return res.clear().say("An error occured: " + exception).send();
    }
};

// Function for when skill is invoked without intent
app.launch(function (req, res) {
    res.say("I can control your Spotify Connect devices, to start, ask me to list your devices");
    res.reprompt("To start, ask me to list your devices");
    // Keep session open
    res.shouldEndSession(false);
});

// Handle default Amazon help intent
// No slots or utterances required
app.intent("AMAZON.HelpIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    res.say("You can ask me to list your connect devices and then control them. ")
    res.say("For example, tell me to play on a device number after listing devices");
    res.reprompt("What would you like to do?");
    // Keep session open
    res.shouldEndSession(false);
    return;
});

// Handle default Amazon stop intent
// No slots or utterances required
app.intent("AMAZON.StopIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    return;
});

// Handle default Amazon cancel intent
// No slots or utterances required
app.intent("AMAZON.CancelIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    return;
});

// Handle play intent
// No slots required
app.intent('PlayIntent', {
    "utterances": [
        "play",
        "resume",
        "continue"
    ]
},
    function (req, res) {
        // PUT to Spotify REST API
        var statusCode;
        return request.put("https://api.spotify.com/v1/me/player/play", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle pause intent
// No slots required
app.intent('PauseIntent', {
    "utterances": [
        "pause"
    ]
},
    function (req, res) {
        // PUT to Spotify REST API
        return request.put("https://api.spotify.com/v1/me/player/pause", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle skip next intent
// No slots required
app.intent('SkipNextIntent', {
    "utterances": [
        "skip",
        "next",
        "forwards"
    ]
},
    function (req, res) {
        // POST to Spotify REST API
        return request.post("https://api.spotify.com/v1/me/player/next", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle skip previous intent
// No slots required
app.intent('SkipPreviousIntent', {
    "utterances": [
        "previous",
        "last",
        "back",
        "backwards"
    ]
},
    function (req, res) {
        // POST to Spotify REST API
        return request.post("https://api.spotify.com/v1/me/player/previous", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle volume level intent
// Slot for new volume
app.intent('VolumeLevelIntent', {
    "slots": {
        "VOLUMELEVEL": "AMAZON.NUMBER"
    },
    "utterances": [
        "{set the|set|} volume {level|} {to|} {-|VOLUMELEVEL}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("VOLUMELEVEL")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("VOLUMELEVEL"))) {
                    var volumeLevel = req.slot("VOLUMELEVEL");
                    // Check that the volume is valid
                    if (volumeLevel >= 0 && volumeLevel <= 10) {
                        // PUT to Spotify REST API
                        request.put({
                            // Send new volume * 10 (convert to percentage)
                            url: "https://api.spotify.com/v1/me/player/volume?volume_percent=" + 10 * volumeLevel,
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            // Handle sending as JSON
                            json: true
                        });
                    }
                    else {
                        // If not valid volume
                        res.say("You can only set the volume between 0 and 10");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("Try setting a volume between 0 and 10");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out the volume to use.")
                res.say("Try setting a volume between 0 and 10");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle get devices intent
// No slots required
app.intent('GetDevicesIntent', {
    "utterances": [
        "devices",
        "list",
        "search",
        "find"
    ]
},
    function (req, res) {
        // GET from Spotify REST API
        return request.get({
            url: "https://api.spotify.com/v1/me/player/devices",
            // Send access token as bearer auth
            auth: {
                "bearer": req.getSession().details.user.accessToken
            },
            // Parse results as JSON
            json: true
        })
            .then(function (body) {
                var devices = body.devices || [];
                var deviceNames = [];
                for (var i = 0; i < devices.length; i++) {
                    // Number each device
                    deviceNames.push((i + 1) + ". " + devices[i].name);
                    // Add the device number to JSON
                    devices[i].number = (i + 1);
                }
                req.getSession().set("devices", devices);
                cache.set(req.getSession().details.user.userId + ":devices", devices);
                // Check if user has devices
                if (devices.length > 0) {
                    // Comma separated list of device names
                    res.say("I found these connect devices: ");
                    res.say([deviceNames.slice(0, -1).join(', '), deviceNames.slice(-1)[0]].join(deviceNames.length < 2 ? '' : ', and ') + ". ");
                    res.say("What would you like to do with these devices?").reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
                else {
                    // No devices found
                    res.say("I couldn't find any connect devices, check your Alexa app for information on connecting a device");
                    res.card({
                        type: "Simple",
                        title: "Connecting to a device using Spotify Connect",
                        content: "To add a device to Spotify Connect,"
                            + " log in to your Spotify account on a supported device"
                            + " such as an Echo, phone, or computer"
                            + "\nhttps://support.spotify.com/uk/article/spotify-connect/"
                    });
                }
            })
            // Handle errors
            .catch(function (err) {
                req.getSession().set("statusCode", err.statusCode);
            });
    }
);

// Handle device play intent
// Slot for device number
app.intent('DevicePlayIntent', {
    "slots": {
        "DEVICENUMBER": "AMAZON.NUMBER"
    },
    "utterances": [
        "play on {number|device|device number|} {-|DEVICENUMBER}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("DEVICENUMBER")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("DEVICENUMBER"))) {
                    var deviceNumber = req.slot("DEVICENUMBER");
                    // Check if session is new
                    if (req.getSession().isNew()) {
                        // If new session try to use cache
                        var devices = cache.get(req.getSession().details.user.userId + ":devices") || [];
                    }
                    else {
                        // If existing session use session data
                        var devices = req.getSession().get("devices") || [];
                    }
                    var deviceId, deviceName;
                    // Iterate through devices to find ID and name by number
                    for (var i = 0; i < devices.length; i++) {
                        if (devices[i].number == deviceNumber) {
                            deviceId = devices[i].id;
                            deviceName = devices[i].name;
                        }
                    }
                    // Check that the device for the number was found
                    if (deviceId) {
                        // PUT to Spotify REST API
                        request.put({
                            url: "https://api.spotify.com/v1/me/player",
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            body: {
                                // Send device ID
                                "device_ids": [
                                    deviceId
                                ],
                                // Make sure that music plays
                                "play": true
                            },
                            // Handle sending as JSON
                            json: true
                        });
                        res.say("Playing on device " + deviceNumber + ": " + deviceName);
                    }
                    else {
                        // If device for number not found
                        res.say("I couldn't find device " + deviceNumber + ". ");
                        res.say("Try asking me to list devices first");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("I couldn't work out which device to play on, make sure you refer to the device by number.");
                    res.say("Try asking me to play on a device number");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out which device number to play on.");
                res.say("Try asking me to play on a device number");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle device transfer intent
// Slot for device number
app.intent('DeviceTransferIntent', {
    "slots": {
        "DEVICENUMBER": "AMAZON.NUMBER"
    },
    "utterances": [
        "transfer to {number|device|device number|} {-|DEVICENUMBER}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("DEVICENUMBER")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("DEVICENUMBER"))) {
                    var deviceNumber = req.slot("DEVICENUMBER");
                    // Check if session is new
                    if (req.getSession().isNew()) {
                        // If new session try to use cache
                        var devices = cache.get(req.getSession().details.user.userId + ":devices") || [];
                    }
                    else {
                        // If existing session use session data
                        var devices = req.getSession().get("devices") || [];
                    }
                    var deviceId, deviceName;
                    // Iterate through devices to find ID and name by number
                    for (var i = 0; i < devices.length; i++) {
                        if (devices[i].number == deviceNumber) {
                            deviceId = devices[i].id;
                            deviceName = devices[i].name;
                        }
                    }
                    // Check that the device for the number was found
                    if (deviceId) {
                        // PUT to Spotify REST API
                        request.put({
                            url: "https://api.spotify.com/v1/me/player",
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            body: {
                                // Send device ID
                                "device_ids": [
                                    deviceId
                                ]
                            },
                            // Handle sending as JSON
                            json: true
                        });
                        res.say("Transferring to device " + deviceNumber + ": " + deviceName);
                    }
                    else {
                        // If device for number not found
                        res.say("I couldn't find device " + deviceNumber + ". ");
                        res.say("Try asking me to list devices first");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("I couldn't work out which device to transfer to, make sure you refer to the device by number.");
                    res.say("Try asking me to transfer a device number");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out which device number to transfer to.");
                res.say("Try asking me to transfer to a device number");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle get track intent
// No slots required
app.intent('GetTrackIntent', {
    "utterances": [
        "{what is|what's} {playing|this song}",
        "what {song|track|} is this"
    ]
},
    function (req, res) {
        // GET from Spotify REST API
        return request.get({
            url: "https://api.spotify.com/v1/me/player/currently-playing",
            // Send access token as bearer auth
            auth: {
                "bearer": req.getSession().details.user.accessToken
            },
            // Parse results as JSON
            json: true
        })
            .then(function (body) {
                if (body.is_playing) {
                    res.say("This is " + body.item.name + " by " + body.item.artists[0].name);
                }
                else {
                    if (body.item.name) {
                        // If not playing but last track known
                        res.say("That was " + body.item.name + " by " + body.item.artists[0].name);
                    }
                    else {
                        // If unknown
                        res.say("Nothing is playing");
                    }
                }
            })
            // Handle errors
            .catch(function (err) {
                req.getSession().set("statusCode", err.statusCode);
            });
    }
);

// Set up redirect to project page
express_app.use(express.static(__dirname));
/* istanbul ignore next */
express_app.get('/', function (req, res) {
    res.redirect('https://github.com/thorpelawrence/alexa-spotify-connect');
});

/* istanbul ignore if */
// Only listen if run directly, not if required as a module
if (require.main === module) {
    var port = process.env.PORT || 8888;
    console.log("Listening on port " + port);
    express_app.listen(port);
}

// Export alexa-app instance for skill.js
module.exports = app;
<commit_msg>Cleanup<commit_after>var alexa = require('alexa-app');
var request = require('request-promise');
var express = require('express');
var nodecache = require('node-cache');

// Create instance of express
var express_app = express();
// Create a 1 hour cache for storing user devices
var cache = new nodecache({ stdTTL: 3600, checkperiod: 120 });
// Create instance of alexa-app
var app = new alexa.app('connect');
// Bind alexa-app to express instance
app.express({ expressApp: express_app });

// Run every time the skill is accessed
app.pre = function (req, res, type) {
    // Error if the application ID of the request is not for this skill
    if (req.applicationId != "amzn1.ask.skill.33d79728-0f5a-44e7-ae22-ccf0b0c0e9e0" &&
        req.getSession().details.application.applicationId != "amzn1.ask.skill.33d79728-0f5a-44e7-ae22-ccf0b0c0e9e0") {
        throw "Invalid applicationId";
    }
    // Check that the user has an access token, if they have linked their account
    if (!req.getSession().details.user.accessToken) {
        res.say("You have not linked your Spotify account, check your Alexa app to link the account");
        res.linkAccount();
    }
};

// Run after every request
app.post = function (req, res, type, exception) {
    if (exception) {
        return res.clear().say("An error occured: " + exception).send();
    }
};

// Function for when skill is invoked without intent
app.launch(function (req, res) {
    res.say("I can control your Spotify Connect devices, to start, ask me to list your devices");
    res.reprompt("To start, ask me to list your devices");
    // Keep session open
    res.shouldEndSession(false);
});

// Handle default Amazon help intent
// No slots or utterances required
app.intent("AMAZON.HelpIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    res.say("You can ask me to list your connect devices and then control them. ")
    res.say("For example, tell me to play on a device number after listing devices");
    res.reprompt("What would you like to do?");
    // Keep session open
    res.shouldEndSession(false);
});

// Handle default Amazon stop intent
// No slots or utterances required
app.intent("AMAZON.StopIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    return;
});

// Handle default Amazon cancel intent
// No slots or utterances required
app.intent("AMAZON.CancelIntent", {
    "slots": {},
    "utterances": []
}, function (req, res) {
    return;
});

// Handle play intent
// No slots required
app.intent('PlayIntent', {
    "utterances": [
        "play",
        "resume",
        "continue"
    ]
},
    function (req, res) {
        // PUT to Spotify REST API
        var statusCode;
        return request.put("https://api.spotify.com/v1/me/player/play", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle pause intent
// No slots required
app.intent('PauseIntent', {
    "utterances": [
        "pause"
    ]
},
    function (req, res) {
        // PUT to Spotify REST API
        return request.put("https://api.spotify.com/v1/me/player/pause", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle skip next intent
// No slots required
app.intent('SkipNextIntent', {
    "utterances": [
        "skip",
        "next",
        "forwards"
    ]
},
    function (req, res) {
        // POST to Spotify REST API
        return request.post("https://api.spotify.com/v1/me/player/next", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle skip previous intent
// No slots required
app.intent('SkipPreviousIntent', {
    "utterances": [
        "previous",
        "last",
        "back",
        "backwards"
    ]
},
    function (req, res) {
        // POST to Spotify REST API
        return request.post("https://api.spotify.com/v1/me/player/previous", {
            resolveWithFullResponse: true
        }).auth(null, null, true, req.getSession().details.user.accessToken)
            .then((r) => {
                req.getSession().set("statusCode", r.statusCode);
            });
    }
);

// Handle volume level intent
// Slot for new volume
app.intent('VolumeLevelIntent', {
    "slots": {
        "VOLUMELEVEL": "AMAZON.NUMBER"
    },
    "utterances": [
        "{set the|set|} volume {level|} {to|} {-|VOLUMELEVEL}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("VOLUMELEVEL")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("VOLUMELEVEL"))) {
                    var volumeLevel = req.slot("VOLUMELEVEL");
                    // Check that the volume is valid
                    if (volumeLevel >= 0 && volumeLevel <= 10) {
                        // PUT to Spotify REST API
                        request.put({
                            // Send new volume * 10 (convert to percentage)
                            url: "https://api.spotify.com/v1/me/player/volume?volume_percent=" + 10 * volumeLevel,
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            // Handle sending as JSON
                            json: true
                        });
                    }
                    else {
                        // If not valid volume
                        res.say("You can only set the volume between 0 and 10");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("Try setting a volume between 0 and 10");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out the volume to use.")
                res.say("Try setting a volume between 0 and 10");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle get devices intent
// No slots required
app.intent('GetDevicesIntent', {
    "utterances": [
        "devices",
        "list",
        "search",
        "find"
    ]
},
    function (req, res) {
        // GET from Spotify REST API
        return request.get({
            url: "https://api.spotify.com/v1/me/player/devices",
            // Send access token as bearer auth
            auth: {
                "bearer": req.getSession().details.user.accessToken
            },
            // Parse results as JSON
            json: true
        })
            .then(function (body) {
                var devices = body.devices || [];
                var deviceNames = [];
                for (var i = 0; i < devices.length; i++) {
                    // Number each device
                    deviceNames.push((i + 1) + ". " + devices[i].name);
                    // Add the device number to JSON
                    devices[i].number = (i + 1);
                }
                req.getSession().set("devices", devices);
                cache.set(req.getSession().details.user.userId + ":devices", devices);
                // Check if user has devices
                if (devices.length > 0) {
                    // Comma separated list of device names
                    res.say("I found these connect devices: ");
                    res.say([deviceNames.slice(0, -1).join(', '), deviceNames.slice(-1)[0]].join(deviceNames.length < 2 ? '' : ', and ') + ". ");
                    res.say("What would you like to do with these devices?").reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
                else {
                    // No devices found
                    res.say("I couldn't find any connect devices, check your Alexa app for information on connecting a device");
                    res.card({
                        type: "Simple",
                        title: "Connecting to a device using Spotify Connect",
                        content: "To add a device to Spotify Connect,"
                            + " log in to your Spotify account on a supported device"
                            + " such as an Echo, phone, or computer"
                            + "\nhttps://support.spotify.com/uk/article/spotify-connect/"
                    });
                }
            })
            // Handle errors
            .catch(function (err) {
                req.getSession().set("statusCode", err.statusCode);
            });
    }
);

// Handle device play intent
// Slot for device number
app.intent('DevicePlayIntent', {
    "slots": {
        "DEVICENUMBER": "AMAZON.NUMBER"
    },
    "utterances": [
        "play on {number|device|device number|} {-|DEVICENUMBER}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("DEVICENUMBER")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("DEVICENUMBER"))) {
                    var deviceNumber = req.slot("DEVICENUMBER");
                    // Check if session is new
                    if (req.getSession().isNew()) {
                        // If new session try to use cache
                        var devices = cache.get(req.getSession().details.user.userId + ":devices") || [];
                    }
                    else {
                        // If existing session use session data
                        var devices = req.getSession().get("devices") || [];
                    }
                    var deviceId, deviceName;
                    // Iterate through devices to find ID and name by number
                    for (var i = 0; i < devices.length; i++) {
                        if (devices[i].number == deviceNumber) {
                            deviceId = devices[i].id;
                            deviceName = devices[i].name;
                        }
                    }
                    // Check that the device for the number was found
                    if (deviceId) {
                        // PUT to Spotify REST API
                        request.put({
                            url: "https://api.spotify.com/v1/me/player",
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            body: {
                                // Send device ID
                                "device_ids": [
                                    deviceId
                                ],
                                // Make sure that music plays
                                "play": true
                            },
                            // Handle sending as JSON
                            json: true
                        });
                        res.say("Playing on device " + deviceNumber + ": " + deviceName);
                    }
                    else {
                        // If device for number not found
                        res.say("I couldn't find device " + deviceNumber + ". ");
                        res.say("Try asking me to list devices first");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("I couldn't work out which device to play on, make sure you refer to the device by number.");
                    res.say("Try asking me to play on a device number");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out which device number to play on.");
                res.say("Try asking me to play on a device number");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle device transfer intent
// Slot for device number
app.intent('DeviceTransferIntent', {
    "slots": {
        "DEVICENUMBER": "AMAZON.NUMBER"
    },
    "utterances": [
        "transfer to {number|device|device number|} {-|DEVICENUMBER}"
    ]
},
    function (req, res) {
        // Check that request contains session
        if (req.hasSession()) {
            // Check that the slot has a value
            if (req.slot("DEVICENUMBER")) {
                // Check if the slot is a number
                if (!isNaN(req.slot("DEVICENUMBER"))) {
                    var deviceNumber = req.slot("DEVICENUMBER");
                    // Check if session is new
                    if (req.getSession().isNew()) {
                        // If new session try to use cache
                        var devices = cache.get(req.getSession().details.user.userId + ":devices") || [];
                    }
                    else {
                        // If existing session use session data
                        var devices = req.getSession().get("devices") || [];
                    }
                    var deviceId, deviceName;
                    // Iterate through devices to find ID and name by number
                    for (var i = 0; i < devices.length; i++) {
                        if (devices[i].number == deviceNumber) {
                            deviceId = devices[i].id;
                            deviceName = devices[i].name;
                        }
                    }
                    // Check that the device for the number was found
                    if (deviceId) {
                        // PUT to Spotify REST API
                        request.put({
                            url: "https://api.spotify.com/v1/me/player",
                            // Send access token as bearer auth
                            auth: {
                                "bearer": req.getSession().details.user.accessToken
                            },
                            body: {
                                // Send device ID
                                "device_ids": [
                                    deviceId
                                ]
                            },
                            // Handle sending as JSON
                            json: true
                        });
                        res.say("Transferring to device " + deviceNumber + ": " + deviceName);
                    }
                    else {
                        // If device for number not found
                        res.say("I couldn't find device " + deviceNumber + ". ");
                        res.say("Try asking me to list devices first");
                        // Keep session open
                        res.shouldEndSession(false);
                    }
                }
                else {
                    // Not a number
                    res.say("I couldn't work out which device to transfer to, make sure you refer to the device by number.");
                    res.say("Try asking me to transfer a device number");
                    res.reprompt("What would you like to do?");
                    // Keep session open
                    res.shouldEndSession(false);
                }
            }
            else {
                // No slot value
                res.say("I couldn't work out which device number to transfer to.");
                res.say("Try asking me to transfer to a device number");
                res.reprompt("What would you like to do?");
                // Keep session open
                res.shouldEndSession(false);
            }
        }
    }
);

// Handle get track intent
// No slots required
app.intent('GetTrackIntent', {
    "utterances": [
        "{what is|what's} {playing|this song}",
        "what {song|track|} is this"
    ]
},
    function (req, res) {
        // GET from Spotify REST API
        return request.get({
            url: "https://api.spotify.com/v1/me/player/currently-playing",
            // Send access token as bearer auth
            auth: {
                "bearer": req.getSession().details.user.accessToken
            },
            // Parse results as JSON
            json: true
        })
            .then(function (body) {
                if (body.is_playing) {
                    res.say("This is " + body.item.name + " by " + body.item.artists[0].name);
                }
                else {
                    if (body.item.name) {
                        // If not playing but last track known
                        res.say("That was " + body.item.name + " by " + body.item.artists[0].name);
                    }
                    else {
                        // If unknown
                        res.say("Nothing is playing");
                    }
                }
            })
            // Handle errors
            .catch(function (err) {
                req.getSession().set("statusCode", err.statusCode);
            });
    }
);

// Set up redirect to project page
express_app.use(express.static(__dirname));
/* istanbul ignore next */
express_app.get('/', function (req, res) {
    res.redirect('https://github.com/thorpelawrence/alexa-spotify-connect');
});

/* istanbul ignore if */
// Only listen if run directly, not if required as a module
if (require.main === module) {
    var port = process.env.PORT || 8888;
    console.log("Listening on port " + port);
    express_app.listen(port);
}

// Export alexa-app instance for skill.js
module.exports = app;
<|endoftext|>


<commit_before>(function () {

	if (!console) window.console = {};

	var _log = console.log;
	var _info = console.info;
	var _error = console.error;
	var _warn = console.warn;
	var _clear = console.clear;
	var wrapper = document.createElement("div");
	var div = document.createElement("div");
	var style = document.createElement("style");
	var maxEntries = 50;

	wrapper.className = "as-console-wrapper";
	div.className = "as-console";

	document.body.appendChild(wrapper).appendChild(div);

	style.type = "text/css";
	style.textContent = [
    ".as-console-wrapper { position: fixed; bottom: 0; left: 0; right: 0; max-height: 150px; overflow-y: scroll; overflow-x: hidden; border-top: 1px solid #000; display: none; }",
    ".as-console { background: #e9e9e9; border: 1px solid #ccc; display: table; width: 100%; border-collapse: collapse; }",
    ".as-console-row { display: table-row; font-family: monospace; font-size: 13px; }",
    ".as-console-row:after { display: table-cell; padding: 3px 6px; color: rgba(0,0,0,.35); border: 1px solid #ccc; content: attr(data-date); vertical-align: top; }",
    ".as-console-row + .as-console-row > * { border: 1px solid #ccc; }",
    ".as-console-row-code { width: 100%; white-space: pre-wrap; padding: 3px 5px; display: table-cell; font-family: monospace; font-size: 13px; vertical-align: middle; }",
    ".as-console-error:before { content: 'Error: '; color: #f00; }",
    ".as-console-info:before { content: 'Info: '; color: #00f; }",
    ".as-console-warning:before { content: 'Warning: '; color: #e90 }",
    "@-webkit-keyframes flash { 0% { background: rgba(255,240,0,.25); } 100% { background: none; } }",
    "@-moz-keyframes flash { 0% { background: rgba(255,240,0,.25); } 100% { background: none; } }",
    "@-ms-keyframes flash { 0% { background: rgba(255,240,0,.25); } 100% { background: none; } }",
    "@keyframes flash { 0% { background: rgba(255,240,0,.25); } 100% { background: none; } }",
    ".as-console-row-code, .as-console-row:after { -webkit-animation: flash 1s; -moz-animation: flash 1s; -ms-animation: flash 1s; animation: flash 1s; }"].join("\n");

	document.head.appendChild(style);

	var getString = (function () {

	    var rx_one = /^[\],:{}\s]*$/,
            rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
            rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
            rx_four = /(?:^|:|,)(?:\s*\[)+/g,
            rx_escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
            rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

	    function f(n) {
	        return n < 10
                ? '0' + n
                : n;
	    }

	    function this_value() {
	        return this.valueOf();
	    }

	    var gap,
            indent,
            meta = {
                '\b': '\\b',
                '\t': '\\t',
                '\n': '\\n',
                '\f': '\\f',
                '\r': '\\r',
                '"': '\\"',
                '\\': '\\\\'
            },
            map,
            id;

	    function quote(string) {
	        rx_escapable.lastIndex = 0;
	        return rx_escapable.test(string)
                ? '"' + string.replace(rx_escapable, function (a) {
                    var c = meta[a];
                    return typeof c === 'string'
                        ? c
                        : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                }) + '"'
                : '"' + string + '"';
	    }

	    function getProps(obj) {
	        var props = [];

	        do {
	            for (var prop in obj) {
	                if (!props.includes(prop)) {
	                    props.push(prop);
	                }
	            }
	        }
	        while (obj = obj.__proto__);

	        return props;
	    }

	    function str(key, holder) {

	        var i,
                k,
                v,
                length,
                mind = gap,
                partial,
                value = holder[key],
                anchor;

	        if (value && typeof value === 'object' && typeof value.toJSON === 'function') {
	            value = value.toJSON(key);
	        }

	        if (value instanceof HTMLElement) {
	            return value.outerHTML;
	        }

	        if (value instanceof RegExp) {
	            return String(value);
	        }

	        if (value instanceof MimeType || value instanceof Plugin) {
	            return Object.prototype.toString.call(value);
	        }

	        switch (typeof value) {
	            case 'string':

	                return quote(value);

	            case 'boolean':
	            case 'function':
	            case 'null':
	            case 'number':
	            case 'undefined':

	                return String(value);

	            case 'object':

	                if (!value) {
	                    return 'null';
	                }

	                var _id = map.get(value);

	                if (_id) {
	                    return "/*ref:" + _id.toString(16) + "*/";
	                } else {
	                    _id = ++id;
	                    anchor = "/*id:" + _id.toString(16) + "*/";
	                    map.set(value, _id);
	                }

	                gap += indent;
	                partial = [];

	                if (Object.prototype.toString.apply(value) === '[object Array]') {

	                    length = value.length;
	                    for (i = 0; i < length; i += 1) {
	                        partial[i] = str(i, value) || 'null';
	                    }

	                    v = partial.length === 0
                            ? '[]'
                            : '[\n' + gap + anchor + "\n" + gap + partial.join(',\n' + gap) + '\n' + mind + ']';
	                    gap = mind;
	                    return v;
	                }

	                getProps(value).forEach(function (k) {
	                    v = str(k, value);
	                    if (v) {
	                        partial.push(quote(k) + ': ' + v);
	                    }
	                });

	                v = partial.length === 0
                        ? '{}'
                        : '{\n' + gap + anchor + "\n" + gap + partial.join(',\n' + gap) + '\n' + mind + '}';
	                gap = mind;

	                return v;
	        }
	    }

	    return function (value) {
	        gap = '';
	        indent = '  ';
	        map = new WeakMap();
	        id = 0;
	        var returnVal = str('', { '': value });

	        while (id) {
	            if (!new RegExp("/\\*ref:" + id.toString(16) + "\\*/").test(returnVal)) {
	                returnVal = returnVal.replace(new RegExp("[\r\n\t ]*/\\*id:" + id.toString(16) + "\\*/", "g"), "");
	            }
	            id--;
	        }

	        map = null;

	        return returnVal;
	    };
	})();

	function formatDate(d) {
		d = new Date(d.valueOf() - d.getTimezoneOffset() * 60000);
		return d.toISOString().replace("Z", "").replace("T", " ");
	}

	function format() {
		var i = 0,
            val,
            args = arguments;

		return args[0].replace(/(%?%[sdifoO])/g, function (c) {

			if (c.length === 3) return c;

			val = args[++i];

			if (val == null) {
				return "" + val;
			}

			switch (c.charAt(1)) {
				case "s":
					return val;
				case "d":
				case "i":
					return typeof val === "number" ? Math.floor(val) : "NaN";
				case "f":
					return typeof val === "number" ? val : "NaN";
				default:
					return getString(val);
			}
		});
	}

	function truncateEntries() {
		while (div.childNodes.length > maxEntries) {
			div.removeChild(div.firstChild);
		}
	}

	function createLogEntry() {

		var args = arguments;

		var row = document.createElement("div");
		row.className = "as-console-row";

		row.setAttribute("data-date", formatDate(new Date()));

		var code = row.appendChild(document.createElement("code"));
		code.className = "as-console-row-code";

		if (typeof args[0] === "string" && args.length > 1 && /((^|[^%])%[sdifoO])/.test(args[0])) {
			code.textContent = format.apply(null, args);
		} else {
			code.textContent = [].map.call(args, getString).join(" ");
		}

		div.appendChild(row);

		truncateEntries();

		wrapper.scrollTop = row.offsetTop;

		return row;
	}

	function showConsole(show) {
		wrapper.style.display = show ? "block" : "none";
	}

	console.log = function () {

		var args = arguments;

		_log && _log.apply(console, args);

		createLogEntry.apply(null, args);

		showConsole(1);

	};

	console.warn = function () {

		var args = arguments;

		_warn && _warn.apply(console, args);

		createLogEntry.apply(null, args)
            .children[0].classList.add("as-console-warning");

		showConsole(1);

	};

	console.info = function () {

	    var args = arguments;

	    _info && _info.apply(console, args);

	    createLogEntry.apply(null, args)
            .children[0].classList.add("as-console-info");

	    showConsole(1);

	};

	console.error = function () {

		var args = arguments;

		_error && _error.apply(console, args);

		var entry;
		var e = args[0];

		if (e instanceof Error) {
			entry = createLogEntry({
				message: e.message,
				filename: e.filename,
				lineno: e.lineno,
				colno: e.colno
			});
		} else {
			entry = createLogEntry.apply(null, args)
		}

		entry.children[0].classList.add("as-console-error");

		showConsole(1);

	};

	console.clear = function () {

		while (div.lastChild) {
			div.removeChild(div.lastChild);
		}

		_clear && _clear.apply(console, arguments);

		showConsole(0);

	};

	console.config = function (settings) {

		if (typeof settings === "object") {

			if (settings.maxEntries > 0) {
				maxEntries = settings.maxEntries;
				truncateEntries();
			}
		}

		console.log({
			maxEntries: maxEntries
		});
	}

	window.addEventListener("error", function (e) {
		createLogEntry({
			message: e.message,
			filename: e.filename,
			lineno: e.lineno,
			colno: e.colno
		}).children[0].classList.add("as-console-error");

		showConsole(1);
	});

})();<commit_msg>Bug-fix<commit_after>(function () {

	if (!console) window.console = {};

	var _log = console.log;
	var _info = console.info;
	var _error = console.error;
	var _warn = console.warn;
	var _clear = console.clear;
	var wrapper = document.createElement("div");
	var div = document.createElement("div");
	var style = document.createElement("style");
	var maxEntries = 50;

	wrapper.className = "as-console-wrapper";
	div.className = "as-console";

	document.body.appendChild(wrapper).appendChild(div);

	style.type = "text/css";
	style.textContent = [
    ".as-console-wrapper { position: fixed; bottom: 0; left: 0; right: 0; max-height: 150px; overflow-y: scroll; overflow-x: hidden; border-top: 1px solid #000; display: none; }",
    ".as-console { background: #e9e9e9; border: 1px solid #ccc; display: table; width: 100%; border-collapse: collapse; }",
    ".as-console-row { display: table-row; font-family: monospace; font-size: 13px; }",
    ".as-console-row:after { display: table-cell; padding: 3px 6px; color: rgba(0,0,0,.35); border: 1px solid #ccc; content: attr(data-date); vertical-align: top; }",
    ".as-console-row + .as-console-row > * { border: 1px solid #ccc; }",
    ".as-console-row-code { width: 100%; white-space: pre-wrap; padding: 3px 5px; display: table-cell; font-family: monospace; font-size: 13px; vertical-align: middle; }",
    ".as-console-error:before { content: 'Error: '; color: #f00; }",
    ".as-console-info:before { content: 'Info: '; color: #00f; }",
    ".as-console-warning:before { content: 'Warning: '; color: #e90 }",
    "@-webkit-keyframes flash { 0% { background: rgba(255,240,0,.25); } 100% { background: none; } }",
    "@-moz-keyframes flash { 0% { background: rgba(255,240,0,.25); } 100% { background: none; } }",
    "@-ms-keyframes flash { 0% { background: rgba(255,240,0,.25); } 100% { background: none; } }",
    "@keyframes flash { 0% { background: rgba(255,240,0,.25); } 100% { background: none; } }",
    ".as-console-row-code, .as-console-row:after { -webkit-animation: flash 1s; -moz-animation: flash 1s; -ms-animation: flash 1s; animation: flash 1s; }"].join("\n");

	document.head.appendChild(style);

	var getString = (function () {

	    var rx_one = /^[\],:{}\s]*$/,
            rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,
            rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,
            rx_four = /(?:^|:|,)(?:\s*\[)+/g,
            rx_escapable = /[\\\"\u0000-\u001f\u007f-\u009f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g,
            rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;

	    function f(n) {
	        return n < 10
                ? '0' + n
                : n;
	    }

	    function this_value() {
	        return this.valueOf();
	    }

	    var gap,
            indent,
            meta = {
                '\b': '\\b',
                '\t': '\\t',
                '\n': '\\n',
                '\f': '\\f',
                '\r': '\\r',
                '"': '\\"',
                '\\': '\\\\'
            },
            map,
            id;

	    function quote(string) {
	        rx_escapable.lastIndex = 0;
	        return rx_escapable.test(string)
                ? '"' + string.replace(rx_escapable, function (a) {
                    var c = meta[a];
                    return typeof c === 'string'
                        ? c
                        : '\\u' + ('0000' + a.charCodeAt(0).toString(16)).slice(-4);
                }) + '"'
                : '"' + string + '"';
	    }

	    function getProps(obj) {
	        var props = [];

	        do {
	            for (var prop in obj) {
	                if (!props.includes(prop)) {
	                    props.push(prop);
	                }
	            }
	        }
	        while (obj = obj.__proto__);

	        return props;
	    }

	    function str(key, holder) {

	        var i,
                k,
                v,
                length,
                mind = gap,
                partial,
                value,
                anchor;

	        try {
	            value = holder[key];
	        } catch (err) {
	            return "/* error accessing property */";
	        }

	        if (value && typeof value === 'object' && typeof value.toJSON === 'function') {
	            value = value.toJSON(key);
	        }

	        if (value instanceof HTMLElement) {
	            return value.outerHTML;
	        }

	        if (value instanceof RegExp) {
	            return String(value);
	        }

	        if (value instanceof MimeType || value instanceof Plugin) {
	            return Object.prototype.toString.call(value);
	        }

	        switch (typeof value) {
	            case 'string':

	                return quote(value);

	            case 'boolean':
	            case 'function':
	            case 'null':
	            case 'number':
	            case 'undefined':

	                return String(value);

	            case 'object':

	                if (!value) {
	                    return 'null';
	                }

	                var _id = map.get(value);

	                if (_id) {
	                    return "/*ref:" + _id.toString(16) + "*/";
	                } else {
	                    _id = ++id;
	                    anchor = "/*id:" + _id.toString(16) + "*/";
	                    map.set(value, _id);
	                }

	                gap += indent;
	                partial = [];

	                if (Object.prototype.toString.apply(value) === '[object Array]') {

	                    length = value.length;
	                    for (i = 0; i < length; i += 1) {
	                        partial[i] = str(i, value) || 'null';
	                    }

	                    v = partial.length === 0
                            ? '[]'
                            : '[\n' + gap + anchor + "\n" + gap + partial.join(',\n' + gap) + '\n' + mind + ']';
	                    gap = mind;
	                    return v;
	                }

	                getProps(value).forEach(function (k) {
	                    v = str(k, value);
	                    if (v) {
	                        partial.push(quote(k) + ': ' + v);
	                    }
	                });

	                v = partial.length === 0
                        ? '{}'
                        : '{\n' + gap + anchor + "\n" + gap + partial.join(',\n' + gap) + '\n' + mind + '}';
	                gap = mind;

	                return v;
	        }
	    }

	    return function (value) {
	        gap = '';
	        indent = '  ';
	        map = new WeakMap();
	        id = 0;
	        var returnVal = str('', { '': value });

	        while (id) {
	            if (!new RegExp("/\\*ref:" + id.toString(16) + "\\*/").test(returnVal)) {
	                returnVal = returnVal.replace(new RegExp("[\r\n\t ]*/\\*id:" + id.toString(16) + "\\*/", "g"), "");
	            }
	            id--;
	        }

	        map = null;

	        return returnVal;
	    };
	})();

	function formatDate(d) {
		d = new Date(d.valueOf() - d.getTimezoneOffset() * 60000);
		return d.toISOString().replace("Z", "").replace("T", " ");
	}

	function format() {
		var i = 0,
            val,
            args = arguments;

		return args[0].replace(/(%?%[sdifoO])/g, function (c) {

			if (c.length === 3) return c;

			val = args[++i];

			if (val == null) {
				return "" + val;
			}

			switch (c.charAt(1)) {
				case "s":
					return val;
				case "d":
				case "i":
					return typeof val === "number" ? Math.floor(val) : "NaN";
				case "f":
					return typeof val === "number" ? val : "NaN";
				default:
					return getString(val);
			}
		});
	}

	function truncateEntries() {
		while (div.childNodes.length > maxEntries) {
			div.removeChild(div.firstChild);
		}
	}

	function createLogEntry() {

		var args = arguments;

		var row = document.createElement("div");
		row.className = "as-console-row";

		row.setAttribute("data-date", formatDate(new Date()));

		var code = row.appendChild(document.createElement("code"));
		code.className = "as-console-row-code";

		if (typeof args[0] === "string" && args.length > 1 && /((^|[^%])%[sdifoO])/.test(args[0])) {
			code.textContent = format.apply(null, args);
		} else {
			code.textContent = [].map.call(args, getString).join(" ");
		}

		div.appendChild(row);

		truncateEntries();

		wrapper.scrollTop = row.offsetTop;

		return row;
	}

	function showConsole(show) {
		wrapper.style.display = show ? "block" : "none";
	}

	console.log = function () {

		var args = arguments;

		_log && _log.apply(console, args);

		createLogEntry.apply(null, args);

		showConsole(1);

	};

	console.warn = function () {

		var args = arguments;

		_warn && _warn.apply(console, args);

		createLogEntry.apply(null, args)
            .children[0].classList.add("as-console-warning");

		showConsole(1);

	};

	console.info = function () {

	    var args = arguments;

	    _info && _info.apply(console, args);

	    createLogEntry.apply(null, args)
            .children[0].classList.add("as-console-info");

	    showConsole(1);

	};

	console.error = function () {

		var args = arguments;

		_error && _error.apply(console, args);

		var entry;
		var e = args[0];

		if (e instanceof Error) {
			entry = createLogEntry({
				message: e.message,
				filename: e.filename,
				lineno: e.lineno,
				colno: e.colno
			});
		} else {
			entry = createLogEntry.apply(null, args)
		}

		entry.children[0].classList.add("as-console-error");

		showConsole(1);

	};

	console.clear = function () {

		while (div.lastChild) {
			div.removeChild(div.lastChild);
		}

		_clear && _clear.apply(console, arguments);

		showConsole(0);

	};

	console.config = function (settings) {

		if (typeof settings === "object") {

			if (settings.maxEntries > 0) {
				maxEntries = settings.maxEntries;
				truncateEntries();
			}
		}

		console.log({
			maxEntries: maxEntries
		});
	}

	window.addEventListener("error", function (e) {
		createLogEntry({
			message: e.message,
			filename: e.filename,
			lineno: e.lineno,
			colno: e.colno
		}).children[0].classList.add("as-console-error");

		showConsole(1);
	});

})();<|endoftext|>


<commit_before>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Excluding ie8 and 7 browsers due to lack of support for addEventListener function
({

   /*
    * Testing to make sure that when pullToRefresh and PullToShowMore are not present,
    * that there isn't a spacer div. This appears when the amount of data on the screen
    * does not fill out the scrollable area
    */
   testNoPullToRefreshNoDivSpace: {
       browsers: ["-IE7","-IE8"],
        test: function(cmp) {
            var scrlrDiv = cmp.find("scroller3").getElements()[1];
            var divChildren = scrlrDiv.children[0].children.length;
            $A.test.assertFalse(divChildren > 1, "There should not be children relating to a spacer");
        }
    },
    /*
     * Making sure that the spacer in the scrollable areas' size is greater than zero since the
     * amount of data does not take up all of the space in the scrollable area
     */
    testDivSpacerSizeForFewItems: {
	browsers: ["-IE7","-IE8"],
        test: function(cmp) {
            var scrlrDiv = cmp.find("scroller2").getElements()[1];
            var cssText = scrlrDiv.children[0].children[2].style.cssText;
            var height = cssText.match( /\d+/g )[0];
            $A.test.assertTrue(height > 0, "height should be greater than zero for spacing, since there is not enough information");
        }
    },
    /*
     * Making sure that the div padding in the scrollable area, when the amount of items takes
     * up all of the space, is zero. i.e. if you can scroll you shouldn't have padding
     */
    testDivSpacerSizeForMaxItems: {
	browsers: ["-IE7","-IE8"],
        test: function(cmp) {
            var scrlrDiv = cmp.find("scroller1").getElements()[1];
            var cssText = scrlrDiv.children[0].children[2].style.cssText;
            var height = cssText.match( /\d+/g )[0];
            $A.test.assertTrue(height == 0, "height should be equal to zero, since there is more than enough information");
        }
    }
})<commit_msg>make testDivSpacerSizeForMaxItems work with async client creation @rev ctatlah @bug W-1796232<commit_after>/*
 * Copyright (C) 2013 salesforce.com, inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Excluding ie8 and 7 browsers due to lack of support for addEventListener function
({

   /*
    * Testing to make sure that when pullToRefresh and PullToShowMore are not present,
    * that there isn't a spacer div. This appears when the amount of data on the screen
    * does not fill out the scrollable area
    */
   testNoPullToRefreshNoDivSpace: {
       browsers: ["-IE7","-IE8"],
        test: function(cmp) {
            var scrlrDiv = cmp.find("scroller3").getElements()[1];
            var divChildren = scrlrDiv.children[0].children.length;
            $A.test.assertFalse(divChildren > 1, "User does not want to pull to refresh, there should not be a div for a spacer here");
        }
    },
    /*
     * Making sure that the spacer in the scrollable areas' size is greater than zero since the
     * amount of data does not take up all of the space in the scrollable area
     */
    testDivSpacerSizeForFewItems: {
	browsers: ["-IE7","-IE8"],
        test: function(cmp) {
            var scrlrDiv = cmp.find("scroller2").getElements()[1];
            var cssText = scrlrDiv.children[0].children[2].style.cssText;
            var height = cssText.match( /\d+/g )[0];
            $A.test.assertTrue(height > 0, "height should be greater than zero for spacing, since there not enough list element");
        }
    },
    /*
     * Making sure that the div padding in the scrollable area, when the amount of items takes
     * up all of the space, is zero. i.e. if you can scroll you shouldn't have padding
     */
    testDivSpacerSizeForMaxItems: {
	browsers: ["-IE7","-IE8"],
        test: function(cmp) {
            var scrlrDiv = cmp.find("scroller1").getElements()[1];
            var childrenWithStyle =  scrlrDiv.children[0].children[2];
            $A.test.addWaitForWithFailureMessage("0",
        	    function(){return childrenWithStyle.style.cssText.match( /\d+/g )[0];},
        	    "height of the space was not zero. List elements go past scrollable area, and should not need a spacer.");
        }
    }
})<|endoftext|>


<commit_before>(() => {
  const $title = document.querySelector('.js-issue-title');
  if (!$title) {
    return;
  }

  chrome.storage.local.get('inlineLinks', (options) => {
    let title = $title.innerHTML.replace(/(<a[^>]+>|⬆|<\/a>)/g, '');
    // TODO: Bring back multiple matches for the new format
    // TODO: Format settings in options page? (Also, domain option?)
    let matches = title.match(/^\s*(\w+-\d+):?/);

    if (matches) {
      let tag = matches[1];
      const url = `https://buildout.atlassian.net/browse/${tag}`;
      const attrs = `href="${url}" target="_blank"`;

      const replacement = options.inlineLinks === false ?
        `${tag}<a ${attrs}>⬆</a>` :
        `<a ${attrs}>${tag}</a>`;

      title = title.replace(tag, replacement);
    }

    $title.innerHTML = title;
  });
})();
<commit_msg>Match multiple (specific) tags<commit_after>(() => {
  const $title = document.querySelector('.js-issue-title');
  if (!$title) {
    return;
  }

  chrome.storage.local.get('inlineLinks', (options) => {
    let title = $title.innerHTML.replace(/(<a[^>]+>|⬆|<\/a>)/g, '');
    // TODO: Format settings in options page? (Also, domain option?)
    title.match(/BSD-\d+/g).forEach((tag) => {
      const url = `https://buildout.atlassian.net/browse/${tag}`;
      const attrs = `href="${url}" target="_blank"`;

      const replacement = options.inlineLinks === false ?
        `${tag}<a ${attrs}>⬆</a>` :
        `<a ${attrs}>${tag}</a>`;

      title = title.replace(tag, replacement);
    })

    $title.innerHTML = title;
  });
})();
<|endoftext|>


<commit_before>var shouldDebug = false

var debug = function(message) {
	if(shouldDebug) {
		console.log(message);
	}
};

function hashHandler() {
    this.oldHash = window.location.hash;
    this.Check;

    var that = this;
    var detect = function(){
        if(that.oldHash!=window.location.hash){
            window.setTimeout(initialize, 100);
            that.oldHash = window.location.hash;
        }
    };
    this.Check = setInterval(function() { detect(); }, 100);
}
var hashDetection = new hashHandler();

function removeStringFromBegining(text, start) {
	var len = start.length
	if (text.substring(0, len) === start) {
	    text = text.substring(len, text.length);
	}
	return text
}

function standarizedTitle(title) {
	var aux = title;
	aux = removeStringFromBegining(aux, "Re: ");
	aux = removeStringFromBegining(aux, "Fwd: ");
	return encodeURIComponent(aux);
}

var initialize = function() {
	if(window.location.hash.indexOf("#inbox") !== -1) {
		debug('gmail-issues add is running');

		var title = document.querySelectorAll(".hP")[0];
		if (title != undefined) {
			var aTag = document.createElement('a');
    		aTag.setAttribute('href', 'https://issues.genexus.com/displaysearchissuesresults.aspx?' + standarizedTitle(title.innerText));
    		aTag.setAttribute('style', 'font-size: small;');
    		aTag.innerHTML = "Search Issue";
	    	debug(title.innerText);

    		title.parentNode.appendChild(aTag);
		}
	}
}

initialize();
<commit_msg>fixed: don't show link in non-issue emails<commit_after>var shouldDebug = false;

var debug = function(message) {
	if(shouldDebug) {
		console.log(message);
	}
};

function hashHandler() {
    this.oldHash = window.location.hash;
    this.Check;

    var that = this;
    var detect = function(){
        if(that.oldHash!=window.location.hash){
            window.setTimeout(initialize, 100);
            that.oldHash = window.location.hash;
        }
    };
    this.Check = setInterval(function() { detect(); }, 100);
}
var hashDetection = new hashHandler();

function removeStringFromBegining(text, start) {
	var len = start.length
	if (text.substring(0, len) === start) {
	    text = text.substring(len, text.length);
	}
	return text
}

function standarizedTitle(title) {
	var aux = title;
	aux = removeStringFromBegining(aux, "Re: ");
	aux = removeStringFromBegining(aux, "Fwd: ");
	return encodeURIComponent(aux);
}

var initialize = function() {
	if(window.location.hash.indexOf("#inbox") !== -1) {
		debug('gmail-issues add is running');

		var title = document.querySelectorAll(".hP")[0];
		if (title != undefined) {

			var found = false;
			var emailsList = document.querySelectorAll("span.g2");
			var i;
			var emailDir;
			for (i = 0; i <	emailsList.length; i++) {
			   	emailDir = emailsList[i].getAttribute("email");
				debug("Found email address: " + emailDir);
			   	if (emailDir == "rochaissues@genexus.com") {
			   		found = true;
			   		break;
			   	}
			}

			if (found) {
				var aTag = document.createElement('a');
    			aTag.setAttribute('href', 'https://issues.genexus.com/displaysearchissuesresults.aspx?' + standarizedTitle(title.innerText));
    			aTag.setAttribute('style', 'font-size: small;');
    			aTag.innerHTML = "Search Issue";
		    	debug(title.innerText);

    			title.parentNode.appendChild(aTag);
    		}
		}
	}
};

initialize();
<|endoftext|>


<commit_before>/**
 * This event handler is called when sendMessage gets called on the tab where this content script is running
 */
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
  if (request.action === 'getDom') {

    var reEval = $("a:contains('re-evaluate')");
    var reAttempt = $("a:contains('re-attempt')");
    var reBuild = $("a:contains('rebuild')");
    var succeeded = $(".vc-pullrequest-view-details-item-status span:contains('Build succeeded'):visible").length > 0;
    var noConflicts = $(".vc-pullrequest-view-details-item-status span:contains('No merge conflicts'):visible").length > 0;
    var mergeConflicts = $(".vc-pullrequest-view-details-item-status span:contains('Merge conflicts'):visible").length > 0;
    var approvedWithSuggestions = $(".vc-pullrequest-view-details-item-status span:contains('Reviewers approved with suggestions'):visible").length > 0;
    var approved = $(".vc-pullrequest-view-details-item-status span:contains('Reviewers approved'):visible").length > 0;
    var itemsNotLinked = $(".vc-pullrequest-view-details-item-status span:contains('No work items linked'):visible").length > 0;

    if (mergeConflicts) {
      sendResponse("finished");
      alert("Merge conflicts, please fix and then restart this tool");
      return;
    }

    if (itemsNotLinked) {
      sendResponse("finished");
      alert("Please link work items before proceeding");
      return;
    }

    if (reEval.length > 0 && reEval.css('display') !== 'none') {
      reEval[0].click();
      sendResponse("timeout");
    } else if (reAttempt.length > 0 && reAttempt.css('display') !== 'none') {
      sendResponse("finished");
      alert("Re attempting to check for conflicts -- this usually doesn't work so we stopped the program.");
    } else if (reBuild.length > 0 && reBuild.css('display') !== 'none') {
      reBuild[0].click();
    } else if (succeeded && noConflicts && (approved || approvedWithSuggestions)) {
      var button = $("button:contains('Complete pull request')")[0];
      button.click();

      // Wait for complete pull request popup to appear
      setTimeout(function completeMerge() {
        var mergePopup = $('.ui-dialog');
        var okButton = mergePopup.find('.ui-dialog-buttonpane #ok');
        okButton.click();
        sendResponse("finished");
        alert('committed PR');
      }, 200);
    }
  }
});<commit_msg>fixing the bug with multiple popups<commit_after>/**
 * This event handler is called when sendMessage gets called on the tab where this content script is running
 */
chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
  if (request.action === 'getDom') {

    var reEval = $("a:contains('re-evaluate')");
    var reAttempt = $("a:contains('re-attempt')");
    var reBuild = $("a:contains('rebuild')");
    var succeeded = $(".vc-pullrequest-view-details-item-status span:contains('Build succeeded'):visible").length > 0;
    var noConflicts = $(".vc-pullrequest-view-details-item-status span:contains('No merge conflicts'):visible").length > 0;
    var mergeConflicts = $(".vc-pullrequest-view-details-item-status span:contains('Merge conflicts'):visible").length > 0;
    var approvedWithSuggestions = $(".vc-pullrequest-view-details-item-status span:contains('Reviewers approved with suggestions'):visible").length > 0;
    var approved = $(".vc-pullrequest-view-details-item-status span:contains('Reviewers approved'):visible").length > 0;
    var itemsNotLinked = $(".vc-pullrequest-view-details-item-status span:contains('No work items linked'):visible").length > 0;

    if (mergeConflicts) {
      sendResponse("finished");
      alert("Merge conflicts, please fix and then restart this tool");
      return;
    }

    if (itemsNotLinked) {
      sendResponse("finished");
      alert("Please link work items before proceeding");
      return;
    }

    if (reEval.length > 0 && reEval.css('display') !== 'none') {
      reEval[0].click();
      sendResponse("timeout");
    } else if (reAttempt.length > 0 && reAttempt.css('display') !== 'none') {
      sendResponse("finished");
      alert("Re attempting to check for conflicts -- this usually doesn't work so we stopped the program.");
    } else if (reBuild.length > 0 && reBuild.css('display') !== 'none') {
      reBuild[0].click();
    } else if (succeeded && noConflicts && (approved || approvedWithSuggestions)) {
      var button = $("button:contains('Complete pull request')")[0];
      button.click();

      // Send the response immediately to make sure that no future getDom requests are queued
      sendResponse("finished");

      // Wait for complete pull request popup to appear
      setTimeout(function completeMerge() {
        var mergePopup = $('.ui-dialog');
        var okButton = mergePopup.find('.ui-dialog-buttonpane #ok');
        okButton.click();

        alert('committed PR');
      }, 200);
    }
  }
});<|endoftext|>


<commit_before>
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  console.log("Message recieved");
  console.log(open);
  if(request.message === "open_sidebar" && open == false){
    console.log("Selection event. Looking up: " + request.content);
    window.url = request.url;
    showPane();
    startQuery(request.content);
    return true;
  }
  else if(request.message === "close_sidebar" && open == true){
    console.log("Closing tab");
    clearQuery();
    endQuery();
    return true;
  }
  else if(request.message === "open_sidebar" && open == true){
    console.log("Selection. Clearing then looking up: " + request.content);
    clearQuery();

    var loading = document.createElement('div');
    loading.id = "loading-spinner";
    loading.innerHTML = "

Loading...Please Wait
";
    document.getElementById("descrips").appendChild(loading);

    startQuery(request.content);
    return true;
  }});

var open = false;
var listen = false;
var url = "";

function showPane()
{
  if (document.getElementById('panelright'))
  {
    var div = document.getElementById('panelright');
    div.style.display = 'block';
  }
  else
  {
    var div = document.createElement('div');
    div.id = 'panelright';
  }

  var loading = document.createElement('div');
  loading.id = "loading-spinner";
  loading.innerHTML = "

Loading...Please Wait
";
  div.appendChild(loading);

  var ul = document.createElement('ul');
  ul.id = 'descrips';
  ul.listStyleType = "none";
  document.body.appendChild(div);
  div.appendChild(ul);
  open = true;
  if (!listen)
  {
    listenclick();
    listen = true;
  }
}

function startQuery(quer){
  var searchUrl = 'https://api.wolframalpha.com/v2/query' + '?appid=' + encodeURIComponent(appID) +
  '&input=' + quer + '&format=image,plaintext&output=JSON';
  var x = new XMLHttpRequest();
  //  x.responseType = 'json';
  x.onload = function(){
    parseResponse(x.response, quer);
  }
  x.onerror = function(err) {
    console.log(err);
  };
  x.open('GET', searchUrl, true);
  x.send();
}

function parseResponse(respond, quer){
  var response = JSON.parse(respond);
  console.log(response);
  if (!response || !response["queryresult"]["success"] || response["queryresult"]["error"] ){
    console.log('Invalid search to Wolfram');
    return;
  }
  var importantTitle = {"input interpretation":null, "definition":null, "definitions":null, "synonym":null, "synonyms":null, "antonym":null, "antonyms":null,
                        "pronunciation":null, "image":null, "basic movie information":null, "cast":null, "wikipedia summary":null,
                        "basic series information":null, "latest trade":null, "chemical names and formulas":null,
                        "administrative regions": null, "current weather":null, "unit conversions":null, "basic information":null,
                        "notable facts":null, "location and owner":null, "basic properties":null};
  var importantIDs = {"definition:worddata":null, "observancedate (country)":null, "notableeventfordate":null};

  var pods = response["queryresult"]["pods"];
  console.log(pods);
  for(i = 0; i < pods.length; i++) {
    lowercase = pods[i].title.toLowerCase();
    if(lowercase === "input interpretation")
    {
      importantTitle[lowercase] = pods[i].subpods[0].plaintext;
    }
    else if(lowercase === "wikipedia summary"){
      importantTitle[lowercase] = true;
    }
    else if(lowercase === "image"){
      importantTitle[lowercase] = pods[i].subpods[0].img.src;
    }
    else if(lowercase in importantTitle){
      importantTitle[lowercase] = pods[i].subpods[0].plaintext;
    }
    else if(lowercase in importantIDs){
      importantIDs[lowercase] = pods[i].subpods[0].plaintext;
    }
  };

  var ul = document.getElementById("descrips");

  document.getElementById("loading-spinner").remove();
  for(var key in importantTitle){
    var li = document.createElement("li");
    if(importantTitle.hasOwnProperty(key) && importantTitle[key] !== null){
      if (key.toLowerCase() === "input interpretation")
      {
        li.innerHTML = "
" + importantTitle[key] + "
";
        ul.appendChild(li);
      }
      else if(key.toLowerCase() === "wikipedia summary"){
        getWikipediaSummary(pods[0].subpods[0].plaintext, function (htmlstr) {
          if (htmlstr)
          {
            console.log("wiki response");
            console.log(htmlstr);
            li.innerHTML = "
Wikipedia Summary

" + htmlstr + "
";
            li.setAttribute("id", "wikipedia-li");
            ul.appendChild(li);
          }
          else {
            li.remove();
          }
        });
      }
      else if(key.toLowerCase() === "image"){
        li.innerHTML = "
Image

";
        ul.appendChild(li);
        continue;
      }
      else if(key.toLowerCase() == "definition" || key.toLowerCase() == "definitions"){
        li.innerHTML = "
" + toTitleCase(key) + "

" + toTable(importantTitle[key]) + "

" + getDictLink(quer) + "
";
        ul.appendChild(li);
        continue;
      }
      else{
      li.innerHTML = "
" + toTitleCase(key) + "

" + toTable(importantTitle[key]);
      ul.appendChild(li);
      }
    }
  }
  for(var key in importantIDs){
    if(importantIDs.hasOwnProperty(key) && importantIDs[key] !== null){
      var li = document.createElement("li");
      li.innerHTML = /*"
" + toTitleCase(key) + "
*/"
" + importantIDs[key] + "
";
      ul.appendChild(li);
    }
  }
  if (ul.childNodes.length < 2)
  {
    var li = document.createElement("li");
    getWikipediaSummary(quer, function(htmlstr)  {
      if (htmlstr)
      {
        console.log("wiki response");
        console.log(htmlstr);
        li.innerHTML = "
Wikipedia Summary

" + htmlstr + "
";
        li.setAttribute("id", "wikipedia-li");
        ul.appendChild(li);
      }
      else
      {
        console.log("going dict");
        getDictionaryDef(quer, li, function(){});
      }
    });
  }
  else if("assumptions" in response["queryresult"]){
    var template = "Assuming \"" + response.queryresult.assumptions.word + "\" is " + response.queryresult.assumptions.values[0].desc + ". Use as ";
    template += "" +
      response.queryresult.assumptions.values[1].desc + " ";
    for(var k = 2; k < response.queryresult.assumptions.count; k++){
      template += "or " +
        response.queryresult.assumptions.values[k].desc + " ";
    }
    template += "instead.";
    var alternates = document.createElement("div");
    alternates.innerHTML = template;
    alternates.id = "chrome-extension-sidebar-alternates";
    document.getElementById("descrips").appendChild(alternates);
    for(var k = 1; k < response.queryresult.assumptions.count; k++){
      document.getElementById("chrome-extension-sidebar-alternates-"+ k).addEventListener("click", reload, false);
    }
  }
}

function getDictionaryDef(term, nodeEle){
  var url = "https://www.dictionaryapi.com/api/v1/references/collegiate/xml/" + encodeURIComponent(term) + "?key=" + encodeURIComponent(dictID);
  var x = new XMLHttpRequest();
  x.onload = function(){
  //  callback("
Wikipedia Summary

" + response.query.pages[pageid].extract + "
");
    nodeEle.appendChild(document.createTextNode(x.response));
  }
  x.onerror = function(err) {
    console.log(err);
  };
  x.open('GET', url, true);
  x.setRequestHeader( 'Api-User-Agent', 'MHacks9 Research Agent/1.0; github.com/3447/MHacks9' );
  x.send();
}

function getDictLink(quer){
  var linkText = "";
  linkText += "Dictionary.com entry for " + decodeURIComponent(quer) + "";
  return linkText;
}

function getWikipediaSummary(term, callback){
  var url = "https://en.wikipedia.org/w/api.php?format=json&action=query&exsentences=3&prop=extracts&exintro=&explaintext=&titles=" + encodeURIComponent(term);
  var x = new XMLHttpRequest();
  x.onload = function(){
    var response = JSON.parse(x.response);
    console.log(response);
    if("missing" in response.query){
      callback("");
      return;
    }
    var pageid = Object.keys(response.query.pages)[0];
    if(response.query.pages[pageid].extract == ""){
      callback("");
      return;
/*    try{
      if("missing" in response.query){
        document.getElementById("wikipedia-li").remove();
        return;
      }
      var pageid = Object.keys(response.query.pages)[0];
      if(response.query.pages[pageid].extract == ""){
          document.getElementById("wikipedia-li").remove();
        return;
      }
    } catch(err) {
      document.getElementById("wikipedia-li").remove();*/
    }
    callback(response.query.pages[pageid].extract);
    return;
  }
  x.onerror = function(err) {
    console.log(err);
  };
  x.open('GET', url, true);
  x.setRequestHeader( 'Api-User-Agent', 'MHacks9 Research Agent/1.0; github.com/3447/MHacks9' );
  x.send();
}

function clearQuery()
{
  var clear = document.getElementById('descrips');
  while(clear.firstChild){
    clear.removeChild(clear.firstChild);
  }
}

function endQuery()
{
  var ex = document.getElementById('panelright');
  ex.style.display = 'none';
  open = false;
}

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

function listenclick(){
  console.log("loaded dom");
    var sp = document.getElementById('panelright');
    // onClick's logic below:
    document.body.addEventListener('click', function(event) {
      console.log('clicked');
        if (sp.style.display == 'none')
          return;
        else {
          var t = event.target;
          console.log(event.target);
          var out = true;
          if( t === document.body )
          {
            clearQuery();
            endQuery();
          }
          while(t.parentNode != document.body){
            if(t === sp)
            {
              out = false;
              break;
            }
            t = t.parentNode;
          }
          if(out)
          {
            clearQuery();
            endQuery();
          }
        }
    });
}

function reload(zEvent){
  var input = this.getAttribute("new-script");
  var base = input.substring(3, input.indexOf("-_*"));
  var modifier = input.substring(input.indexOf("-_*")+3, input.length - 1);

  clearQuery();
  var loading = document.createElement('div');
  loading.id = "loading-spinner";
  loading.innerHTML = "

Loading...Please Wait
";
  document.getElementById("descrips").appendChild(loading);

  startQuery(base + "%20" + modifier);
}

function toTable(input){
  var rows = input.split("\n");
  var cols = {};
  for(var i = 0; i < rows.length; i++){
    cols[i] = rows[i].split("|");
  }
  if(rows.length == 1 && cols[0].length == 1)
    return input;
  var toReturn = "
";
  for(var i = 0; i < rows.length; i++){
    toReturn += "
";
    for(var j = 0; j < cols[i].length; j++){
      toReturn += "" + cols[i][j] + "	";
    }
    toReturn += "
";
  }
  toReturn += "
";
  return toReturn;
}
<commit_msg>Code cleanup<commit_after>
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
  console.log("Message recieved");
  console.log(open);
  if(request.message === "open_sidebar" && open == false){
    console.log("Selection event. Looking up: " + request.content);
    window.url = request.url;
    showPane();
    startQuery(request.content);
    return true;
  }
  else if(request.message === "close_sidebar" && open == true){
    console.log("Closing tab");
    clearQuery();
    endQuery();
    return true;
  }
  else if(request.message === "open_sidebar" && open == true){
    console.log("Selection. Clearing then looking up: " + request.content);
    clearQuery();

    var loading = document.createElement('div');
    loading.id = "loading-spinner";
    loading.innerHTML = "

Loading...Please Wait
";
    document.getElementById("descrips").appendChild(loading);

    startQuery(request.content);
    return true;
  }});

var open = false;
var listen = false;
var url = "";

function showPane()
{
  if (document.getElementById('panelright'))
  {
    var div = document.getElementById('panelright');
    div.style.display = 'block';
  }
  else
  {
    var div = document.createElement('div');
    div.id = 'panelright';
  }

  var loading = document.createElement('div');
  loading.id = "loading-spinner";
  loading.innerHTML = "

Loading...Please Wait
";
  div.appendChild(loading);

  var ul = document.createElement('ul');
  ul.id = 'descrips';
  ul.listStyleType = "none";
  document.body.appendChild(div);
  div.appendChild(ul);
  open = true;
  if (!listen)
  {
    listenclick();
    listen = true;
  }
}

function startQuery(quer){
  var searchUrl = 'https://api.wolframalpha.com/v2/query' + '?appid=' + encodeURIComponent(appID) +
  '&input=' + quer + '&format=image,plaintext&output=JSON';
  var x = new XMLHttpRequest();
  //  x.responseType = 'json';
  x.onload = function(){
    parseResponse(x.response, quer);
  }
  x.onerror = function(err) {
    console.log(err);
  };
  x.open('GET', searchUrl, true);
  x.send();
}

function parseResponse(respond, quer){
  var response = JSON.parse(respond);
  console.log(response);
  if (!response || !response["queryresult"]["success"] || response["queryresult"]["error"] ){
    console.log('Invalid search to Wolfram');
    return;
  }
  var importantTitle = {"input interpretation":null, "definition":null, "definitions":null, "synonym":null, "synonyms":null, "antonym":null, "antonyms":null,
                        "pronunciation":null, "image":null, "basic movie information":null, "cast":null, "wikipedia summary":null,
                        "basic series information":null, "latest trade":null, "chemical names and formulas":null,
                        "administrative regions": null, "current weather":null, "unit conversions":null, "basic information":null,
                        "notable facts":null, "location and owner":null, "basic properties":null};
  var importantIDs = {"definition:worddata":null, "observancedate (country)":null, "notableeventfordate":null};

  var pods = response["queryresult"]["pods"];
  console.log(pods);
  for(i = 0; i < pods.length; i++) {
    lowercase = pods[i].title.toLowerCase();
    if(lowercase === "input interpretation")
    {
      importantTitle[lowercase] = pods[i].subpods[0].plaintext;
    }
    else if(lowercase === "wikipedia summary"){
      importantTitle[lowercase] = true;
    }
    else if(lowercase === "image"){
      importantTitle[lowercase] = pods[i].subpods[0].img.src;
    }
    else if(lowercase in importantTitle){
      importantTitle[lowercase] = pods[i].subpods[0].plaintext;
    }
    else if(lowercase in importantIDs){
      importantIDs[lowercase] = pods[i].subpods[0].plaintext;
    }
  };

  var ul = document.getElementById("descrips");

  document.getElementById("loading-spinner").remove();
  for(var key in importantTitle){
    var li = document.createElement("li");
    if(importantTitle.hasOwnProperty(key) && importantTitle[key] !== null){
      if (key.toLowerCase() === "input interpretation")
      {
        li.innerHTML = "
" + importantTitle[key] + "
";
        ul.appendChild(li);
      }
      else if(key.toLowerCase() === "wikipedia summary"){
        getWikipediaSummary(pods[0].subpods[0].plaintext, function (htmlstr) {
          if (htmlstr)
          {
            console.log("wiki response");
            console.log(htmlstr);
            li.innerHTML = "
Wikipedia Summary

" + htmlstr + "
";
            li.setAttribute("id", "wikipedia-li");
            ul.appendChild(li);
          }
          else {
            li.remove();
          }
        });
      }
      else if(key.toLowerCase() === "image"){
        li.innerHTML = "
Image

";
        ul.appendChild(li);
        continue;
      }
      else if(key.toLowerCase() == "definition" || key.toLowerCase() == "definitions"){
        li.innerHTML = "
" + toTitleCase(key) + "

" + toTable(importantTitle[key]) + "

" + getDictLink(quer) + "
";
        ul.appendChild(li);
        continue;
      }
      else{
      li.innerHTML = "
" + toTitleCase(key) + "

" + toTable(importantTitle[key]);
      ul.appendChild(li);
      }
    }
  }
  for(var key in importantIDs){
    if(importantIDs.hasOwnProperty(key) && importantIDs[key] !== null){
      var li = document.createElement("li");
      li.innerHTML = /*"
" + toTitleCase(key) + "
*/"
" + importantIDs[key] + "
";
      ul.appendChild(li);
    }
  }
  if (ul.childNodes.length < 2)
  {
    var li = document.createElement("li");
    getWikipediaSummary(quer, function(htmlstr)  {
      if (htmlstr)
      {
        console.log("wiki response");
        console.log(htmlstr);
        li.innerHTML = "
Wikipedia Summary

" + htmlstr + "
";
        li.setAttribute("id", "wikipedia-li");
        ul.appendChild(li);
      }
      else
      {
        console.log("going dict");
        getDictionaryDef(quer, li, function(){});
      }
    });
  }
  else if("assumptions" in response["queryresult"]){
    var template = "Assuming \"" + response.queryresult.assumptions.word + "\" is " + response.queryresult.assumptions.values[0].desc + ". Use as ";
    template += "" +
      response.queryresult.assumptions.values[1].desc + " ";
    for(var k = 2; k < response.queryresult.assumptions.count; k++){
      template += "or " +
        response.queryresult.assumptions.values[k].desc + " ";
    }
    template += "instead.";
    var alternates = document.createElement("div");
    alternates.innerHTML = template;
    alternates.id = "chrome-extension-sidebar-alternates";
    document.getElementById("descrips").appendChild(alternates);
    for(var k = 1; k < response.queryresult.assumptions.count; k++){
      document.getElementById("chrome-extension-sidebar-alternates-"+ k).addEventListener("click", reload, false);
    }
  }
}

function getDictionaryDef(term, nodeEle){
  var url = "https://www.dictionaryapi.com/api/v1/references/collegiate/xml/" + encodeURIComponent(term) + "?key=" + encodeURIComponent(dictID);
  var x = new XMLHttpRequest();
  x.onload = function(){
  //  callback("
Wikipedia Summary

" + response.query.pages[pageid].extract + "
");
    nodeEle.appendChild(document.createTextNode(x.response));
  }
  x.onerror = function(err) {
    console.log(err);
  };
  x.open('GET', url, true);
  x.setRequestHeader( 'Api-User-Agent', 'MHacks9 Research Agent/1.0; github.com/3447/MHacks9' );
  x.send();
}

function getDictLink(quer){
  var linkText = "";
  linkText += "Dictionary.com entry for " + decodeURIComponent(quer) + "";
  return linkText;
}

function getWikipediaSummary(term, callback){
  var url = "https://en.wikipedia.org/w/api.php?format=json&action=query&exsentences=3&prop=extracts&exintro=&explaintext=&titles=" + encodeURIComponent(term);
  var x = new XMLHttpRequest();
  x.onload = function(){
    var response = JSON.parse(x.response);
    console.log(response);
    if("missing" in response.query){
      callback("");
      return;
    }
    var pageid = Object.keys(response.query.pages)[0];
    if(response.query.pages[pageid].extract == ""){
      callback("");
      return;
    }
    callback(response.query.pages[pageid].extract);
    return;
  }
  x.onerror = function(err) {
    console.log(err);
  };
  x.open('GET', url, true);
  x.setRequestHeader( 'Api-User-Agent', 'MHacks9 Research Agent/1.0; github.com/3447/MHacks9' );
  x.send();
}

function clearQuery()
{
  var clear = document.getElementById('descrips');
  while(clear.firstChild){
    clear.removeChild(clear.firstChild);
  }
}

function endQuery()
{
  var ex = document.getElementById('panelright');
  ex.style.display = 'none';
  open = false;
}

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

function listenclick(){
  console.log("loaded dom");
    var sp = document.getElementById('panelright');
    // onClick's logic below:
    document.body.addEventListener('click', function(event) {
      console.log('clicked');
        if (sp.style.display == 'none')
          return;
        else {
          var t = event.target;
          console.log(event.target);
          var out = true;
          if( t === document.body )
          {
            clearQuery();
            endQuery();
          }
          while(t.parentNode != document.body){
            if(t === sp)
            {
              out = false;
              break;
            }
            t = t.parentNode;
          }
          if(out)
          {
            clearQuery();
            endQuery();
          }
        }
    });
}

function reload(zEvent){
  var input = this.getAttribute("new-script");
  var base = input.substring(3, input.indexOf("-_*"));
  var modifier = input.substring(input.indexOf("-_*")+3, input.length - 1);

  clearQuery();
  var loading = document.createElement('div');
  loading.id = "loading-spinner";
  loading.innerHTML = "

Loading...Please Wait
";
  document.getElementById("descrips").appendChild(loading);

  startQuery(base + "%20" + modifier);
}

function toTable(input){
  var rows = input.split("\n");
  var cols = {};
  for(var i = 0; i < rows.length; i++){
    cols[i] = rows[i].split("|");
  }
  if(rows.length == 1 && cols[0].length == 1)
    return input;
  var toReturn = "
";
  for(var i = 0; i < rows.length; i++){
    toReturn += "
";
    for(var j = 0; j < cols[i].length; j++){
      toReturn += "" + cols[i][j] + "	";
    }
    toReturn += "
";
  }
  toReturn += "
";
  return toReturn;
}
<|endoftext|>