<commit_before>console.log("Starting...")

var config = require("../config.json");

var Queue     = new require("./Queue"),
    Player    = new require("./Player"),
    express   = require("express"), app = express(),
    http      = require("http"),
    server    = http.Server(app),
    io        = require("socket.io")(server),
    Promise   = require('promise'),
    spotify   = require('node-spotify')({
        appkeyFile: config.spotify.apiKeyLocation
    }),
    loudness  = require('loudness'),
    request   = require("request"),
    async     = require("async"),
    fs        = require("fs"),
    Datastore = require("nedb"),
    exec      = require('child_process').exec;

var db = {
    history: new Datastore({filename: 'data/history.db', autoload: true}),
    votes: new Datastore({filename: 'data/votes.db', autoload: true})
};

console.log("Loaded modules")

var queue, player, normalVolume, phoneVolume = 75, isPhone = false, lastTime, accessToken, accessTokenTimeout, accessTokenExpireTime = 0;

var lastUsed = new Date;

spotify.on({
    ready: function () {
        console.log("Logged in!");

        queue = new Queue(spotify);
        player = new Player(spotify.player, queue);

        spotify.player.on({
            endOfTrack: function () {
                queue.next();
            }
        });

        server.listen(config.http.port, config.http.ip);
        console.log("Started on port %s:%s", config.http.ip, config.http.port);

        app.get("/templates.json", function (req, res) {
            async.map([__dirname + "/public/templates", __dirname + "/public/templates/partials"],
                function (path, cb) {
                    fs.readdir(path, function (err, files) {
                        cb(null, files.filter(function (val) {
                            return val != "." && val != ".."
                        }).map(function (val) {
                            var arr = val.split(".");
                            arr.pop(); //Remove extension
                            return arr.join(".");
                        }).filter(function (val) {
                            return val != "";
                        }))
                    })
                }, function (err, data) {
                    res.json({
                        templates: data[0],
                        partials: data[1]
                    });
                });
        });
        app.get("/pages.json", function (req, res) {
            fs.readdir(__dirname + "/public/js/pages", function (err, files) {
                res.json(files.filter(function (val) {
                    return val != "." && val != ".."
                }));
            })
        });

        app.use(express.static(__dirname + "/public"));
        io.set('transports', ['websocket']);
        io.on('connection', function (socket) {
            var ip = socket.request.connection.remoteAddress;
            updateAccessToken();
            console.log("Connection from: " + ip)

            socket.on("disconnect", function () {
                console.log(ip + " disconnected: " + JSON.stringify(arguments));
            })
            socket.on("pause", function () {
                console.log("pause")
                lastUsed = new Date;
                player.pause();
            });
            socket.on("play", function () {
                console.log("play")
                lastUsed = new Date;
                player.play();
            });
            socket.on("stop", function () {
                console.log("stop");
                lastUsed = new Date;
                player.stop();
            })
            socket.on("skip", function () {
                console.log("skip")
                lastUsed = new Date;
                queue.next();
            });
            socket.on("skipBack", function () {
                console.log("skipBack")
                lastUsed = new Date;
                queue.previous();
            });
            socket.on("addTrack", function (data) {
                console.log("addTrack", data)
                queue.add([data.uri], data.source);
                lastUsed = new Date;
            })
            socket.on("add", function (tracks) {
                console.log("add", tracks)
                queue.add(tracks);
                lastUsed = new Date;
            })
            socket.on("addPlaylist", function (playlistURI) {
                console.log("addPlaylist", playlistURI)
                var playlist = spotify.createFromLink(playlistURI);
                queue.loadItem(playlist).then(function () {
                    queue.add(playlist.getTracks(), playlistURI);
                })
                lastUsed = new Date;
            });
            socket.on("addAlbum", function (albumURI) {
                console.log("addAlbum", albumURI)
                spotify.createFromLink(albumURI).browse(function (err, album) {
                    queue.add(album.tracks, albumURI);
                });
                lastUsed = new Date;
            });
            socket.on("clearQueue", function () {
                console.log("clearQueue")
                queue.clear();
                lastUsed = new Date;
            })
            socket.on("save", function () {
                queue.save();
            })
            socket.on("load", function () {
                queue.load();
            })
            socket.on("getQueue", function () {
                socket.emit("playQueue", queue.getJson()) //No validation needed
            })
            socket.on("toggleShuffle", function (on) {
                queue.toggleShuffle(on); //Validation done in toggleShuffle
                lastUsed = new Date;
            })
            socket.on("setVolume", function (volume) {
                volume = volume;
                loudness.setVolume(parseInt(volume), function (err) {
                    if (isPhone) {
                        phoneVolume = volume;
                    } else {
                        normalVolume = volume;
                    }
                    io.emit("volume", volume);
                });
                lastUsed = new Date;
            })
            socket.on("seek", function (seconds) {
                spotify.player.seek(parseInt(seconds));
                lastUsed = new Date;
            })
            socket.on("phoneOn", function () {
                isPhone = true;
                io.emit("isPhone", true)
                io.emit("volume", phoneVolume)
                loudness.setVolume(phoneVolume, function () {

                });
                lastUsed = new Date;
            })
            socket.on("phoneOff", function () {
                isPhone = false;
                io.emit("isPhone", false)
                io.emit("volume", normalVolume)
                loudness.setVolume(normalVolume, function () {

                });
                lastUsed = new Date;
            })
            socket.on("vote", function (data) {
                console.log("vote", data, ip);
                queue.vote(data.uuid, data.up, ip);
                lastUsed = new Date;
            })
            socket.on("getHistory", function () {
                db.history.find({}).sort({time: -1}).exec(function (err, docs) {
                    if (err) {
                        console.log(err);
                        return;
                    }
                    socket.emit("history", docs);
                })
            });
            socket.on("reboot", function () {
                exec("sudo reboot");
            });
            socket.on("getHistory", function () {
                db.history.find({}, function (err, docs) {
                    if (err) {
                        console.error(err);
                        return;
                    }
                    socket.emit("history", docs);
                })
            });

            socket.emit("ip", ip);
            socket.emit("playState", player.isPlaying)
            socket.emit("accessToken", accessToken);
            socket.emit("changedCurrent", player.getCurrentTrackJson())
            socket.emit("seek", lastTime);
            socket.emit("volume", normalVolume);
            socket.emit("isPhone", isPhone)
        });

        queue.on("change", function () {
            io.emit("playQueue", queue.getJson());
        })
        queue.on("vote", function (e) {
            db.votes.update({uri: e.track.link, ip: e.ip}, {
                uri: e.track.link,
                ip: e.ip,
                up: e.up,
                time: (new Date).getTime()
            }, {upsert: true});
        })
        player.on("changedCurrent", function (track) {
            io.emit("changedCurrent", track);
            if (!track) return;
            db.history.insert({
                uri: track.link,
                time: (new Date).getTime()
            })
        })
        player.on("playState", function (isPlaying) {
            io.emit("playState", isPlaying);
        })
        player.on("playError", function (error) {
            console.error(error);
            io.emit("error", "Can't play '" + error.track.name + "'")
        })
        queue.on("error", function (error) {
            console.error(error);
            io.emit("error", "Can't add '" + error.track.name + "' " + error.message);
        })

        loudness.getVolume(function (err, volume) {
            normalVolume = volume;
        })

        setInterval(function () {
            if (spotify.player.currentSecond != lastTime) {
                lastTime = spotify.player.currentSecond;
                io.emit("seek", lastTime);
            }
        }, 1000);

        setInterval(function () {
            queue.save();
            if ((new Date).getTime() - lastUsed.getTime() > 3 * 60 * 60 * 1000) {
                //3 Hours since the last action was taken, unload the track
                queue.stop();
            }
        }, 300000); // 5 Minutes
    },
    playTokenLost: function () {
        io.emit("error", "This spotify account is being used elsewhere. Pausing playback...");
    }
});
/**
 * Get a new access token from spotify for the clients to use
 */
function updateAccessToken() {
    if (accessTokenTimeout) clearTimeout(accessTokenTimeout);
    if (io.sockets.sockets.length == 0)  return;
    if (accessTokenExpireTime > (new Date).getTime() + 60000) {
        //More than 60 secs in future
        accessTokenTimeout = setTimeout(updateAccessToken, accessTokenExpireTime - (new Date).getTime() - 30000);
        return;
    }

    request({
        url: "https://accounts.spotify.com/api/token",
        method: "POST",
        headers: {
            Authorization: "Basic " + (new Buffer(config.spotify.clientId + ":" + config.spotify.clientSecret)).toString("base64") //Get base64 encoded string of the API key
        },
        form: {
            grant_type: "client_credentials"
        }
    }, function (err, message, response) {
        response = JSON.parse(response);
        //Notify the clients of the updated access token
        io.emit("accessToken", response.access_token);
        accessToken = response.access_token;
        //Update 60 seconds before this one expires (about 59 minutes)
        accessTokenTimeout = setTimeout(updateAccessToken, (response.expires_in - 60) * 1000);
        accessTokenExpireTime = (new Date).getTime() + (response.expires_in * 1000);
        console.log("Got access token %s", accessToken);
    });
}

console.log("Logging into spotify")

spotify.login(config.spotify.username, config.spotify.password)
updateAccessToken();<commit_msg>Emit pause on token loss to update play button<commit_after>console.log("Starting...")

var config = require("../config.json");

var Queue     = new require("./Queue"),
    Player    = new require("./Player"),
    express   = require("express"), app = express(),
    http      = require("http"),
    server    = http.Server(app),
    io        = require("socket.io")(server),
    Promise   = require('promise'),
    spotify   = require('node-spotify')({
        appkeyFile: config.spotify.apiKeyLocation
    }),
    loudness  = require('loudness'),
    request   = require("request"),
    async     = require("async"),
    fs        = require("fs"),
    Datastore = require("nedb"),
    exec      = require('child_process').exec;

var db = {
    history: new Datastore({filename: 'data/history.db', autoload: true}),
    votes: new Datastore({filename: 'data/votes.db', autoload: true})
};

console.log("Loaded modules")

var queue, player, normalVolume, phoneVolume = 75, isPhone = false, lastTime, accessToken, accessTokenTimeout, accessTokenExpireTime = 0;

var lastUsed = new Date;

spotify.on({
    ready: function () {
        console.log("Logged in!");

        queue = new Queue(spotify);
        player = new Player(spotify.player, queue);

        spotify.player.on({
            endOfTrack: function () {
                queue.next();
            }
        });

        server.listen(config.http.port, config.http.ip);
        console.log("Started on port %s:%s", config.http.ip, config.http.port);

        app.get("/templates.json", function (req, res) {
            async.map([__dirname + "/public/templates", __dirname + "/public/templates/partials"],
                function (path, cb) {
                    fs.readdir(path, function (err, files) {
                        cb(null, files.filter(function (val) {
                            return val != "." && val != ".."
                        }).map(function (val) {
                            var arr = val.split(".");
                            arr.pop(); //Remove extension
                            return arr.join(".");
                        }).filter(function (val) {
                            return val != "";
                        }))
                    })
                }, function (err, data) {
                    res.json({
                        templates: data[0],
                        partials: data[1]
                    });
                });
        });
        app.get("/pages.json", function (req, res) {
            fs.readdir(__dirname + "/public/js/pages", function (err, files) {
                res.json(files.filter(function (val) {
                    return val != "." && val != ".."
                }));
            })
        });

        app.use(express.static(__dirname + "/public"));
        io.set('transports', ['websocket']);
        io.on('connection', function (socket) {
            var ip = socket.request.connection.remoteAddress;
            updateAccessToken();
            console.log("Connection from: " + ip)

            socket.on("disconnect", function () {
                console.log(ip + " disconnected: " + JSON.stringify(arguments));
            })
            socket.on("pause", function () {
                console.log("pause")
                lastUsed = new Date;
                player.pause();
            });
            socket.on("play", function () {
                console.log("play")
                lastUsed = new Date;
                player.play();
            });
            socket.on("stop", function () {
                console.log("stop");
                lastUsed = new Date;
                player.stop();
            })
            socket.on("skip", function () {
                console.log("skip")
                lastUsed = new Date;
                queue.next();
            });
            socket.on("skipBack", function () {
                console.log("skipBack")
                lastUsed = new Date;
                queue.previous();
            });
            socket.on("addTrack", function (data) {
                console.log("addTrack", data)
                queue.add([data.uri], data.source);
                lastUsed = new Date;
            })
            socket.on("add", function (tracks) {
                console.log("add", tracks)
                queue.add(tracks);
                lastUsed = new Date;
            })
            socket.on("addPlaylist", function (playlistURI) {
                console.log("addPlaylist", playlistURI)
                var playlist = spotify.createFromLink(playlistURI);
                queue.loadItem(playlist).then(function () {
                    queue.add(playlist.getTracks(), playlistURI);
                })
                lastUsed = new Date;
            });
            socket.on("addAlbum", function (albumURI) {
                console.log("addAlbum", albumURI)
                spotify.createFromLink(albumURI).browse(function (err, album) {
                    queue.add(album.tracks, albumURI);
                });
                lastUsed = new Date;
            });
            socket.on("clearQueue", function () {
                console.log("clearQueue")
                queue.clear();
                lastUsed = new Date;
            })
            socket.on("save", function () {
                queue.save();
            })
            socket.on("load", function () {
                queue.load();
            })
            socket.on("getQueue", function () {
                socket.emit("playQueue", queue.getJson()) //No validation needed
            })
            socket.on("toggleShuffle", function (on) {
                queue.toggleShuffle(on); //Validation done in toggleShuffle
                lastUsed = new Date;
            })
            socket.on("setVolume", function (volume) {
                volume = volume;
                loudness.setVolume(parseInt(volume), function (err) {
                    if (isPhone) {
                        phoneVolume = volume;
                    } else {
                        normalVolume = volume;
                    }
                    io.emit("volume", volume);
                });
                lastUsed = new Date;
            })
            socket.on("seek", function (seconds) {
                spotify.player.seek(parseInt(seconds));
                lastUsed = new Date;
            })
            socket.on("phoneOn", function () {
                isPhone = true;
                io.emit("isPhone", true)
                io.emit("volume", phoneVolume)
                loudness.setVolume(phoneVolume, function () {

                });
                lastUsed = new Date;
            })
            socket.on("phoneOff", function () {
                isPhone = false;
                io.emit("isPhone", false)
                io.emit("volume", normalVolume)
                loudness.setVolume(normalVolume, function () {

                });
                lastUsed = new Date;
            })
            socket.on("vote", function (data) {
                console.log("vote", data, ip);
                queue.vote(data.uuid, data.up, ip);
                lastUsed = new Date;
            })
            socket.on("getHistory", function () {
                db.history.find({}).sort({time: -1}).exec(function (err, docs) {
                    if (err) {
                        console.log(err);
                        return;
                    }
                    socket.emit("history", docs);
                })
            });
            socket.on("reboot", function () {
                exec("sudo reboot");
            });
            socket.on("getHistory", function () {
                db.history.find({}, function (err, docs) {
                    if (err) {
                        console.error(err);
                        return;
                    }
                    socket.emit("history", docs);
                })
            });

            socket.emit("ip", ip);
            socket.emit("playState", player.isPlaying)
            socket.emit("accessToken", accessToken);
            socket.emit("changedCurrent", player.getCurrentTrackJson())
            socket.emit("seek", lastTime);
            socket.emit("volume", normalVolume);
            socket.emit("isPhone", isPhone)
        });

        queue.on("change", function () {
            io.emit("playQueue", queue.getJson());
        })
        queue.on("vote", function (e) {
            db.votes.update({uri: e.track.link, ip: e.ip}, {
                uri: e.track.link,
                ip: e.ip,
                up: e.up,
                time: (new Date).getTime()
            }, {upsert: true});
        })
        player.on("changedCurrent", function (track) {
            io.emit("changedCurrent", track);
            if (!track) return;
            db.history.insert({
                uri: track.link,
                time: (new Date).getTime()
            })
        })
        player.on("playState", function (isPlaying) {
            io.emit("playState", isPlaying);
        })
        player.on("playError", function (error) {
            console.error(error);
            io.emit("error", "Can't play '" + error.track.name + "'")
        })
        queue.on("error", function (error) {
            console.error(error);
            io.emit("error", "Can't add '" + error.track.name + "' " + error.message);
        })

        loudness.getVolume(function (err, volume) {
            normalVolume = volume;
        })

        setInterval(function () {
            if (spotify.player.currentSecond != lastTime) {
                lastTime = spotify.player.currentSecond;
                io.emit("seek", lastTime);
            }
        }, 1000);

        setInterval(function () {
            queue.save();
            if ((new Date).getTime() - lastUsed.getTime() > 3 * 60 * 60 * 1000) {
                //3 Hours since the last action was taken, unload the track
                queue.stop();
            }
        }, 300000); // 5 Minutes
    },
    playTokenLost: function () {
        io.emit("error", "This spotify account is being used elsewhere. Pausing playback...");
        player.pause();
    }
});
/**
 * Get a new access token from spotify for the clients to use
 */
function updateAccessToken() {
    if (accessTokenTimeout) clearTimeout(accessTokenTimeout);
    if (io.sockets.sockets.length == 0)  return;
    if (accessTokenExpireTime > (new Date).getTime() + 60000) {
        //More than 60 secs in future
        accessTokenTimeout = setTimeout(updateAccessToken, accessTokenExpireTime - (new Date).getTime() - 30000);
        return;
    }

    request({
        url: "https://accounts.spotify.com/api/token",
        method: "POST",
        headers: {
            Authorization: "Basic " + (new Buffer(config.spotify.clientId + ":" + config.spotify.clientSecret)).toString("base64") //Get base64 encoded string of the API key
        },
        form: {
            grant_type: "client_credentials"
        }
    }, function (err, message, response) {
        response = JSON.parse(response);
        //Notify the clients of the updated access token
        io.emit("accessToken", response.access_token);
        accessToken = response.access_token;
        //Update 60 seconds before this one expires (about 59 minutes)
        accessTokenTimeout = setTimeout(updateAccessToken, (response.expires_in - 60) * 1000);
        accessTokenExpireTime = (new Date).getTime() + (response.expires_in * 1000);
        console.log("Got access token %s", accessToken);
    });
}

console.log("Logging into spotify")

spotify.login(config.spotify.username, config.spotify.password)
updateAccessToken();<|endoftext|>


<commit_before>var fs = require('fs');
var myRestifyApi = require('my-restify-api');
var UnauthorizedError = myRestifyApi.error.UnauthorizedError;
//var oauth = myRestifyApi.plugin.oauth;

var oauth = function oauth(req) {

  var context = {};
  context.user = function () {
  };

  context.client = function (client) {
    return context;
  };

  context.scope = function (scope) {
    return context;
  };

  return context;
};

var festivalsController = require('./controller/festivals');
var categoriesController = require('./controller/categories');
var eventsController = require('./controller/events');
var placesController = require('./controller/places');

var logger = require('./logger/logger').logger;

var startServer = function startServer(callback) {
  fs.readFile('config/public.key', function (err, data) {
    if (err) {
      logger.debug('config/public.key read error: ', err);
      throw err;
    }

    var options = {
      appName: 'festivals',
      swagger: {
        enabled: true,
        apiDocsDir: __dirname + '/../public/'
      },
      authorization: {
        authHeaderPrefix: 'x-auth-',
        key: data,
        noVerify: false
      },
      bodyParser: {
        enabled: true,
        options: {
          maxBodySize: 1e6,
          mapParams: true,
          overrideParams: false
        }
      },
      acceptable: [
        'application/vnd.festivals.v1+json',
        'application/vnd.festivals.v1+xml'
      ]
    };

    var errorHandlers = {
      EventNotFound: {
        className: 'NotFoundError'
      },
      EventPlaceNotFound: {
        className: 'NotFoundError'
      },
      FestivalNotFound: {
        className: 'NotFoundError'
      },
      ServiceUnavailable: {
        className: 'ServiceUnavailableError'
      }
    };

    var publicCacheHandler = function (req, res, next) {
      res.cache('public', {maxAge: 600});
      res.header('Vary', 'Accept-Language, Accept-Encoding, Accept, Content-Type');
      res.charSet('utf-8');
//    res.header('Last-Modified', new Date());
      return next();
    };

    var noCacheHandler = function (req, res, next) {
      res.cache('private');
      res.charSet('utf-8');
      return next();
    };

    var routes = {
      get: [],
      post: [],
      put: [],
      delete: []
    };

    routes.get.push({
      options: {
        path: '/api/festivals', version: '1.0.0'
      },
      authMethod: function readFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: festivalsController.getFestivalsV1
    });

    routes.post.push({
      options: {
        path: '/api/festivals', version: '1.0.0'
      },
      authMethod: function createFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:create')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: festivalsController.createFestivalV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id', version: '1.0.0'
      },
      authMethod: function readFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: festivalsController.getFestivalV1
    });

    routes.put.push({
      options: {
        path: '/api/festivals/:id', version: '1.0.0'
      },
      authMethod: function updateFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:update')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: festivalsController.updateFestivalV1
    });

    routes.delete.push({
      options: {
        path: '/api/festivals/:id', version: '1.0.0'
      },
      authMethod: function deleteFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:delete')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: festivalsController.deleteFestivalV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/categories', version: '1.0.0'
      },
      authMethod: function readCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: categoriesController.getFestivalCategoriesV1
    });

    routes.post.push({
      options: {
        path: '/api/festivals/:id/categories', version: '1.0.0'
      },
      authMethod: function createCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:create')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: categoriesController.createFestivalCategoryV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/categories/:category.id', version: '1.0.0'
      },
      authMethod: function readCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: categoriesController.getFestivalCategoryV1
    });

    routes.put.push({
      options: {
        path: '/api/festivals/:id/categories/:category.id', version: '1.0.0'
      },
      authMethod: function updateCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:update')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: categoriesController.updateFestivalCategoryV1
    });

    routes.delete.push({
      options: {
        path: '/api/festivals/:id/categories/:category.id', version: '1.0.0'
      },
      authMethod: function deleteCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:delete')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: categoriesController.deleteFestivalCategoryV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/events', version: '1.0.0'
      },
      authMethod: function readEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: eventsController.getFestivalEventsV1
    });

    routes.post.push({
      options: {
        path: '/api/festivals/:id/events', version: '1.0.0'
      },
      authMethod: function createEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:create')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: eventsController.createFestivalEventV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/events/:event.id', version: '1.0.0'
      },
      authMethod: function readEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: eventsController.getFestivalEventV1
    });

    routes.put.push({
      options: {
        path: '/api/festivals/:id/events/:event.id', version: '1.0.0'
      },
      authMethod: function updateEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:update')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: eventsController.updateFestivalEventV1
    });

    routes.delete.push({
      options: {
        path: '/api/festivals/:id/events/:event.id', version: '1.0.0'
      },
      authMethod: function deleteEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:delete')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: eventsController.deleteFestivalEventV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/places', version: '1.0.0'
      },
      authMethod: function readPlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: placesController.getFestivalPlacesV1
    });

    routes.post.push({
      options: {
        path: '/api/festivals/:id/places', version: '1.0.0'
      },
      authMethod: function createPlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:create')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: placesController.createFestivalPlaceV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/places/:place.id', version: '1.0.0'
      },
      authMethod: function readPlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: placesController.getFestivalPlaceV1
    });

    routes.put.push({
      options: {
        path: '/api/festivals/:id/places/:place.id', version: '1.0.0'
      },
      authMethod: function updatePlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:update')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: placesController.updateFestivalPlaceV1
    });

    routes.delete.push({
      options: {
        path: '/api/festivals/:id/places/:place.id', version: '1.0.0'
      },
      authMethod: function deletePlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:delete')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: placesController.deleteFestivalPlaceV1
    });

    var server = myRestifyApi.createServer(routes, errorHandlers, options);

    myRestifyApi.runServer(server, options, function (err, port) {
      logger.debug('myRestifyApi running on port: %d', port);
      return callback(err, port);
    });
  });
};

module.exports = {
  startServer: startServer
};
<commit_msg>Added options support<commit_after>var fs = require('fs');
var myRestifyApi = require('my-restify-api');
var UnauthorizedError = myRestifyApi.error.UnauthorizedError;
//var oauth = myRestifyApi.plugin.oauth;

var oauth = function oauth(req) {

  var context = {};
  context.user = function () {
  };

  context.client = function (client) {
    return context;
  };

  context.scope = function (scope) {
    return context;
  };

  return context;
};

var festivalsController = require('./controller/festivals');
var categoriesController = require('./controller/categories');
var eventsController = require('./controller/events');
var placesController = require('./controller/places');

var logger = require('./logger/logger').logger;

var startServer = function startServer(callback) {
  fs.readFile('config/public.key', function (err, data) {
    if (err) {
      logger.debug('config/public.key read error: ', err);
      throw err;
    }

    var options = {
      appName: 'festivals',
      swagger: {
        enabled: true,
        apiDocsDir: __dirname + '/../public/'
      },
      authorization: {
        authHeaderPrefix: 'x-auth-',
        key: data,
        noVerify: false
      },
      bodyParser: {
        enabled: true,
        options: {
          maxBodySize: 1e6,
          mapParams: true,
          overrideParams: false
        }
      },
      acceptable: [
        'application/vnd.festivals.v1+json',
        'application/vnd.festivals.v1+xml'
      ]
    };

    var errorHandlers = {
      EventNotFound: {
        className: 'NotFoundError'
      },
      EventPlaceNotFound: {
        className: 'NotFoundError'
      },
      FestivalNotFound: {
        className: 'NotFoundError'
      },
      ServiceUnavailable: {
        className: 'ServiceUnavailableError'
      }
    };

    var publicCacheHandler = function (req, res, next) {
      res.cache('public', {maxAge: 600});
      res.header('Vary', 'Accept-Language, Accept-Encoding, Accept, Content-Type');
      res.charSet('utf-8');
//    res.header('Last-Modified', new Date());
      return next();
    };

    var noCacheHandler = function (req, res, next) {
      res.cache('private');
      res.charSet('utf-8');
      return next();
    };

    var routes = {
      get: [],
      post: [],
      put: [],
      delete: []
    };

    routes.get.push({
      options: {
        path: '/api/festivals', version: '1.0.0'
      },
      authMethod: function readFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: festivalsController.getFestivalsV1
    });

    routes.post.push({
      options: {
        path: '/api/festivals', version: '1.0.0'
      },
      authMethod: function createFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:create')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: festivalsController.createFestivalV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id', version: '1.0.0'
      },
      authMethod: function readFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: festivalsController.getFestivalV1
    });

    routes.put.push({
      options: {
        path: '/api/festivals/:id', version: '1.0.0'
      },
      authMethod: function updateFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:update')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: festivalsController.updateFestivalV1
    });

    routes.delete.push({
      options: {
        path: '/api/festivals/:id', version: '1.0.0'
      },
      authMethod: function deleteFestivalsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('festivals:delete')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: festivalsController.deleteFestivalV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/categories', version: '1.0.0'
      },
      authMethod: function readCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: categoriesController.getFestivalCategoriesV1
    });

    routes.post.push({
      options: {
        path: '/api/festivals/:id/categories', version: '1.0.0'
      },
      authMethod: function createCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:create')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: categoriesController.createFestivalCategoryV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/categories/:category.id', version: '1.0.0'
      },
      authMethod: function readCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: categoriesController.getFestivalCategoryV1
    });

    routes.put.push({
      options: {
        path: '/api/festivals/:id/categories/:category.id', version: '1.0.0'
      },
      authMethod: function updateCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:update')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: categoriesController.updateFestivalCategoryV1
    });

    routes.delete.push({
      options: {
        path: '/api/festivals/:id/categories/:category.id', version: '1.0.0'
      },
      authMethod: function deleteCategoriesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('categories:delete')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: categoriesController.deleteFestivalCategoryV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/events', version: '1.0.0'
      },
      authMethod: function readEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: eventsController.getFestivalEventsV1
    });

    routes.post.push({
      options: {
        path: '/api/festivals/:id/events', version: '1.0.0'
      },
      authMethod: function createEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:create')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: eventsController.createFestivalEventV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/events/:event.id', version: '1.0.0'
      },
      authMethod: function readEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: eventsController.getFestivalEventV1
    });

    routes.put.push({
      options: {
        path: '/api/festivals/:id/events/:event.id', version: '1.0.0'
      },
      authMethod: function updateEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:update')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: eventsController.updateFestivalEventV1
    });

    routes.delete.push({
      options: {
        path: '/api/festivals/:id/events/:event.id', version: '1.0.0'
      },
      authMethod: function deleteEventsAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('events:delete')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: eventsController.deleteFestivalEventV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/places', version: '1.0.0'
      },
      authMethod: function readPlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: placesController.getFestivalPlacesV1
    });

    routes.post.push({
      options: {
        path: '/api/festivals/:id/places', version: '1.0.0'
      },
      authMethod: function createPlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:create')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: placesController.createFestivalPlaceV1
    });

    routes.get.push({
      options: {
        path: '/api/festivals/:id/places/:place.id', version: '1.0.0'
      },
      authMethod: function readPlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:get')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: publicCacheHandler,
      controllerMethod: placesController.getFestivalPlaceV1
    });

    routes.put.push({
      options: {
        path: '/api/festivals/:id/places/:place.id', version: '1.0.0'
      },
      authMethod: function updatePlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:update')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: placesController.updateFestivalPlaceV1
    });

    routes.delete.push({
      options: {
        path: '/api/festivals/:id/places/:place.id', version: '1.0.0'
      },
      authMethod: function deletePlacesAuthHandler(req, res, next) {

        try {
          oauth(req)
            .scope('places:delete')
            .client('client')
            .user();
        }
        catch (e) {
          return next(new UnauthorizedError('Unauthorized error: ' + e.message));
        }

        return next();
      },
      cache: noCacheHandler,
      controllerMethod: placesController.deleteFestivalPlaceV1
    });

    var server = myRestifyApi.createServer(routes, errorHandlers, options);

    server.opts(/.*/, function (req, res, next) {
      res.header("Access-Control-Allow-Origin", "*");
      res.header("Access-Control-Allow-Methods", req.header("Access-Control-Request-Method"));
      res.header("Access-Control-Allow-Headers", req.header("Access-Control-Request-Headers"));
      res.send(200);
      return next();
    });

    myRestifyApi.runServer(server, options, function (err, port) {
      logger.debug('myRestifyApi running on port: %d', port);
      return callback(err, port);
    });
  });
};

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


<commit_before>function fetchQuestion( button ) {

	// UI elements
	var buttonElement = button;

	var randomRange = 6;

	// List of the questions and answers ; [Question ; Answer ; Source link ; Source text]
	// Source link is optional
	// Source text is optional

	var questionList = [
		['Navigator, NavigatorIOS or Navigation Experimental in RN?',
		'Navigator is the current common choice. However, Facebook is dropping support for it and focusing on Navigation Experimental which includes the Redux logic which is commonly admitted as best practice today. You could still use NavigatorIOS since it is a native component, but you won\'t be able to personalize it.',
		'https://github.com/ericvicenti/navigation-rfc/blob/master/Docs/NavigationOverview.md',
		'Navigation Comparison'],
		['Question 2', 'Answer 2', ''],
		['Question 3', 'Answer 3'],
		['Question 4', 'Answer 4', 'Source 4'],
		['Question 5', 'Answer 5', 'Source 5'],
		['Question 6', 'Answer 6', 'Source 6'],
		['Question 7', 'Answer 7', 'Source 7'],
		['Question 8', 'Answer 8', 'Source 8'],
	];

	var questions = null;

	// Prepares and binds the button
		var init = function() {

		button.onclick = onButtonClick;

		questions = questionList.slice(0);

		// If the Browser supports html5 storage
		if ( supportsHtmlStorage() === true ) {

			// Check for past data
			if ( localStorage[ 'questions' ] !== undefined ) {
				loadQuestions();
			}
		}

		var question = selectQuestion();

		document.getElementById('question').innerHTML = question[0];
		document.getElementById('answer').innerHTML = question[1];
		if (question[2] == null || question[2] == '' || question[2] == undefined) {
			document.getElementById('source').innerHTML = '';
			document.getElementById('sourceLink').innerHTML = '';
		} else {
			document.getElementById('source').innerHTML = 'Source: ';
			if (question[3] == null || question[3] == '' || question[3] == undefined)
				document.getElementById('sourceLink').innerHTML = question[2];
			else
				document.getElementById('sourceLink').innerHTML = question[3];
			document.getElementById('sourceLink').setAttribute('href', question[2]);
		}
	};

	// Selects and removes the next question from the list
	var selectQuestion = function() {
		var question, range, index;

		range = randomRange > questions.length ? questions.length : randomRange;
		index = Math.floor(Math.random() * range);

		question = questions[index];
		questions.splice(index, 1);

		// When all questions have been seen, refresh the list
		if ( questions.length == 0 ) {
			questions = questionList.slice(0);
    	}
    	storeQuestions();

		return question;
	};

	var onButtonClick = function() {

		var question = selectQuestion();

		document.getElementById('question').innerHTML = question[0];
		document.getElementById('answer').innerHTML = question[1];
		if (question[2] == null || question[2] == '' || question[2] == undefined) {
			document.getElementById('source').innerHTML = '';
			document.getElementById('sourceLink').innerHTML = '';
		} else {
			document.getElementById('source').innerHTML = 'Source: ';
			if (question[3] == null || question[3] == '' || question[3] == undefined)
				document.getElementById('sourceLink').innerHTML = question[2];
			else
				document.getElementById('sourceLink').innerHTML = question[3];
			document.getElementById('sourceLink').setAttribute('href', question[2]);
		}
	};

	// Save the current list of questions for the new user.
	var storeQuestions = function() {
		localStorage[ 'questions' ] = JSON.stringify( questions );
	}

	// Load the list of questions
	var loadQuestions = function() {
		questions = JSON.parse(localStorage['questions']);
	};

	init();
}
<commit_msg>First big push of content<commit_after>function fetchQuestion( button ) {

	// UI elements
	var buttonElement = button;

	var randomRange = 6;

	// List of the questions and answers ; [Question ; Answer ; Source link ; Source text]
	// Source link is optional
	// Source text is optional

	var questionList = [
	// ReactJS relative questions
	// ---------------------------

		['Who is in charge of the development of React JS?',
		'React JS was originally built by Facebook. Today, a large community contribute to it',
		'https://github.com/facebook/react',
		'React JS Github'],
		['Why is React JS so fast?',
		'React JS uses what\'s called a virtual DOM that allows to only update the minimum necessary in what\'s displayed',
		'https://facebook.github.io/react/docs/advanced-performance.html',
		'React Advanced Performance'],
		['What is the logic of React?',
		'React is all about building reusable components. In fact, with React the only thing you do is build components. Since they\'re so encapsulated, components make code reuse, testing, and separation of concerns easy.',
		'https://facebook.github.io/react/docs/why-react.html',
		'Why React?'],
		['What back-end should I use React with?',
		'React can work with any regular back-end. However, many people prefer to use Node.js to have a javascript full stack. Also, many people use webpack which is quite convenient',
		'',
		''],
		['Why aren\'t the styles in an external common file?',
		'Styles are local because of the component logic. Basically, if you want a style to be shared by several components, it\'s the same component, maybe nested in another component',
		'',
		''],

	// React Native relative questions
	// -------------------------------

		['Navigator, NavigatorIOS or Navigation Experimental in RN?',
		'Navigator is the current common choice. However, Facebook is dropping support for it and focusing on Navigation Experimental which includes the Redux logic which is commonly admitted as best practice today. You could still use NavigatorIOS since it is a native component, but you won\'t be able to personalize it.',
		'https://github.com/ericvicenti/navigation-rfc/blob/master/Docs/NavigationOverview.md',
		'Navigation Comparison'],
		['Who is in charge of the development of React Native?',
		'React JS was originally built by Facebook. Today, a large community contribute to it',
		'https://github.com/facebook/react-native',
		'React Native GitHub'],
		['Is React Native stable?',
		'There is a minor update of React Native approximatively every 2 weeks. However, none of them should break your app',
		'',
		''],
		['How to create animations in React Native?',
		'There is a library dedicated to it called Animated',
		'https://facebook.github.io/react-native/docs/animations.html',
		'Animated Library'],
		['Are React JS and React Native the same language?',
		'The logic and the syntax are the same ; the only difference is the HTML tags replaced by mobile components tags',
		'https://facebook.github.io/react-native/',
		'React Native Homepage'],
		['Is React Native really native?',
		'The UI is exclusively native, while the logic part of the app is executed in its own separated thread by Javascript-engine',
		'https://medium.com/@talkol/performance-limitations-of-react-native-and-how-to-overcome-them-947630d7f440#.7a5xt7z54',
		'Performance limitations'],
		['Is React Native hybrid?',
		'Since the logic part of the app is executed in a separate thread by Javascript-engine, yes',
		'https://medium.com/@talkol/performance-limitations-of-react-native-and-how-to-overcome-them-947630d7f440#.7a5xt7z54',
		'Performance limitations'],
		['Can I use one code for iOS and Android?',
		'That depends on the components you are using ; some are exclusive to iOS or Android, some work on both. However, you should consider users are used to different UI and UX depending on the platform',
		'',
		''],
		['How can I contribute to React Native?',
		'You can create your own components or make a pull request for any existing component on GitHub!',
		'',
		''],
		['Why can\'t I develop with React Native iOS without a Mac?',
		'For now, you need XCode to compile an iOS app, which is exclusively on OSX (soon to be Mac OS).',
		'',
		''],
		['Are there any best practices for React Native? Where to find them?',
		'The best pratices are still in dispute. The closest to them would be the official F8 sample app you can find on GitHub',
		'https://github.com/fbsamples/f8app',
		'F8 on GitHub'],

	// Redux relative question
	// -----------------------

		['Who is in charge of the development of Redux?',
		'Redux was originally developed by Dan Abramov, who was since hired at Facebook',
		'http://redux.js.org/',
		'redux.js.org'],
		['What is Flux?',
		'Flux is a logic architecture to handle the data flow of your app and any state in it. It eases the way you can get your data shared between several components',
		'',
		''],
		['What is Redux?',
		'Redux is an implementation of Flux. It comes with many functions you will use all the time',
		'http://redux.js.org/',
		'redux.js.org'],
		['What are actions and reducers?',
		'Actions and reducers are Redux notions. Actions are payloads of information that describe “what happens” and reducers update the state according to those actions',
		'http://redux.js.org/docs/basics/Store.html',
		'Store in Redux'],
		['What are mapStateToProps and mapDispatchToProps?',
		'These are common functions used in a Flux environment. Since the states are supposed to be immutable in Redux, you need to pass them to your props and manipulate as such. mapDispatchToProps will then update your main state',
		'',
		''],
		['Is Redux or any other implementation of Flux required when working with React?',
		'Flux is not required but highly recommended if you do not want to avoid struggling with states and props being inherited in a messy project',
		'',
		''],
		// ['Question 8',
		// 'Answer 8',
		// '',
		// ''],
	];

	var questions = null;

	// Prepares and binds the button
		var init = function() {

		button.onclick = onButtonClick;

		questions = questionList.slice(0);

		// If the Browser supports html5 storage
		if ( supportsHtmlStorage() === true ) {

			// Check for past data
			if ( localStorage[ 'questions' ] !== undefined ) {
				loadQuestions();
			}
		}

		var question = selectQuestion();

		document.getElementById('question').innerHTML = question[0];
		document.getElementById('answer').innerHTML = question[1];
		if (question[2] == null || question[2] == '' || question[2] == undefined) {
			document.getElementById('source').innerHTML = '';
			document.getElementById('sourceLink').innerHTML = '';
		} else {
			document.getElementById('source').innerHTML = 'Source: ';
			if (question[3] == null || question[3] == '' || question[3] == undefined)
				document.getElementById('sourceLink').innerHTML = question[2];
			else
				document.getElementById('sourceLink').innerHTML = question[3];
			document.getElementById('sourceLink').setAttribute('href', question[2]);
		}
	};

	// Selects and removes the next question from the list
	var selectQuestion = function() {
		var question, range, index;

		range = randomRange > questions.length ? questions.length : randomRange;
		index = Math.floor(Math.random() * range);

		question = questions[index];
		questions.splice(index, 1);

		// When all questions have been seen, refresh the list
		if ( questions.length == 0 ) {
			questions = questionList.slice(0);
    	}
    	storeQuestions();

		return question;
	};

	var onButtonClick = function() {

		var question = selectQuestion();

		document.getElementById('question').innerHTML = question[0];
		document.getElementById('answer').innerHTML = question[1];
		if (question[2] == null || question[2] == '' || question[2] == undefined) {
			document.getElementById('source').innerHTML = '';
			document.getElementById('sourceLink').innerHTML = '';
		} else {
			document.getElementById('source').innerHTML = 'Source: ';
			if (question[3] == null || question[3] == '' || question[3] == undefined)
				document.getElementById('sourceLink').innerHTML = question[2];
			else
				document.getElementById('sourceLink').innerHTML = question[3];
			document.getElementById('sourceLink').setAttribute('href', question[2]);
		}
	};

	// Save the current list of questions for the new user.
	var storeQuestions = function() {
		localStorage[ 'questions' ] = JSON.stringify( questions );
	}

	// Load the list of questions
	var loadQuestions = function() {
		questions = JSON.parse(localStorage['questions']);
	};

	init();
}
<|endoftext|>


<commit_before>/*jshint node:true, strict:false */
var express = require('express'),
  url = require('url'),
  request = require('request'),
  u = require('underscore'),
  JSON3 = require('json3');

var app = express();

app.use(express.logger());
app.use(express['static'](__dirname + '/..'));

app.configure('production', function(){
  var airbrakeKey = process.env.JSONP_AIRBRAKE_KEY;
  if (airbrakeKey){
    var airbrake = require('airbrake').createClient(airbrakeKey);
    airbrake.handleExceptions();
    console.log('Aibrake enabled');
  }
});

// returns a copy of the object without the specified properties
function except(obj /*, properties */){
  var result = u.extend({}, obj),
    properties = u.rest(arguments);

  properties.forEach(function(prop){
    delete result[prop];
  });

  return result;
}

function isValidJson(str) {
  try {
    JSON3.parse(str);
  } catch (e) {
    return false;
  }
  return true;
}

app.get('/', function(req, res) {
  var params = url.parse(req.url, true).query,
    apiUrl = params.url || params.src;

  if (!apiUrl){
    // serve landing page
    res.render('index.ejs', {
      layout: false,
      host: req.headers.host,
      nodeVersion: process.version
    });

  } else {
    // do proxy

    // copy headers from the external request, but remove those that node should generate
    var externalReqHeaders = except(req.headers, 'accept-encoding', 'connection', 'cookie', 'host', 'user-agent');
    externalReqHeaders.accept = 'application/json';

    request({
      uri: apiUrl,
      strictSSL: false, // node(jitsu?) has some SSL problems
      headers: externalReqHeaders
    }, function(error, response, body){
      var callbackName = params.callback || params.jsonp,
        raw = params.raw, // undocumented, for now
        status;

      if (error){
        status = 502; // bad gateway
        body = JSON3.stringify({
          error: error.message || body
        });

      } else if (!raw && !isValidJson(body)){
        // invalid JSON
        status = 502; // bad gateway
        body = JSON3.stringify({ error: body });

      } else {
        // proxy successful
        status = response.statusCode;
        // copy headers from the external response, but remove those that node should generate
        res.set(except(response.headers, 'content-length', 'connection', 'server', 'x-frame-options'));
      }

      // enable cross-domain-ness
      res.set('access-control-allow-origin', '*'); // CORS

      if (!callbackName){
        // treat as an AJAX request (CORS)
        if (raw){
          res.set('content-type', 'text/plain');
        } else {
          res.set('content-type', 'application/json');
        }
      } else {
        // JSONP
        res.set('content-type', 'text/javascript'); // use instead of 'application/javascript' for IE < 8 compatibility

        if (raw){
          // escape and pass via JSON
          body = JSON3.stringify({data: body});
        }

        // escape closing tags - see http://stackoverflow.com/a/9249932/358804
        body = body.replace(/<\//g, '<\\/');
        // wrap in callback
        body = callbackName + '(' + body + ');';
      }

      res.send(status, body);
    });
  }
});

module.exports = app;
<commit_msg>rearrange code around header setting<commit_after>/*jshint node:true, strict:false */
var express = require('express'),
  url = require('url'),
  request = require('request'),
  u = require('underscore'),
  JSON3 = require('json3');

var app = express();

app.use(express.logger());
app.use(express['static'](__dirname + '/..'));

app.configure('production', function(){
  var airbrakeKey = process.env.JSONP_AIRBRAKE_KEY;
  if (airbrakeKey){
    var airbrake = require('airbrake').createClient(airbrakeKey);
    airbrake.handleExceptions();
    console.log('Aibrake enabled');
  }
});

// returns a copy of the object without the specified properties
function except(obj /*, properties */){
  var result = u.extend({}, obj),
    properties = u.rest(arguments);

  properties.forEach(function(prop){
    delete result[prop];
  });

  return result;
}

function isValidJson(str) {
  try {
    JSON3.parse(str);
  } catch (e) {
    return false;
  }
  return true;
}

app.get('/', function(req, res) {
  var params = url.parse(req.url, true).query,
    apiUrl = params.url || params.src;

  if (!apiUrl){
    // serve landing page
    res.render('index.ejs', {
      layout: false,
      host: req.headers.host,
      nodeVersion: process.version
    });

  } else {
    // do proxy

    // copy headers from the external request, but remove those that node should generate
    var externalReqHeaders = except(req.headers, 'accept-encoding', 'connection', 'cookie', 'host', 'user-agent');
    externalReqHeaders.accept = 'application/json';

    request({
      uri: apiUrl,
      strictSSL: false, // node(jitsu?) has some SSL problems
      headers: externalReqHeaders
    }, function(error, response, body){
      var callbackName = params.callback || params.jsonp,
        raw = params.raw, // undocumented, for now
        status;

      if (error){
        status = 502; // bad gateway
        body = JSON3.stringify({
          error: error.message || body
        });

      } else if (!raw && !isValidJson(body)){
        // invalid JSON
        status = 502; // bad gateway
        body = JSON3.stringify({ error: body });

      } else {
        // proxy successful
        status = response.statusCode;
        // copy headers from the external response, but remove those that node should generate
        res.set(except(response.headers, 'content-length', 'connection', 'server', 'x-frame-options'));
      }

      // enable cross-domain-ness
      res.set('access-control-allow-origin', '*'); // CORS

      if (callbackName){
        // JSONP
        res.set('content-type', 'text/javascript'); // use instead of 'application/javascript' for IE < 8 compatibility

        if (raw){
          // escape and pass via JSON
          body = JSON3.stringify({data: body});
        }

        // escape closing tags - see http://stackoverflow.com/a/9249932/358804
        body = body.replace(/<\//g, '<\\/');
        // wrap in callback
        body = callbackName + '(' + body + ');';

      } else {
        // treat as an AJAX request (CORS)
        if (raw){
          res.set('content-type', 'text/plain');
        } else {
          res.set('content-type', 'application/json');
        }
      }

      res.send(status, body);
    });
  }
});

module.exports = app;
<|endoftext|>


<commit_before>// is short
// What ARE you. Fake crying, and don't tell your dad
// Weave it

function Start_Dinner_4(){

	n(". . .");
	m("It's because your dad's almost never home, isn't it?");
	m("Without a strong male role model, you become confused...");

	Choose({
		"Yeah, coz Dad's SUCH a great role model.": function(message){
			n(message);
			m("Nick, no matter what, he's your father. You should love him.");
			My_Fault();
		},
		"That's not how it works. I'd be bi anyway.": function(message){
			n(message);
			m("How do you know?! Are you an expert in psychology?!");
			My_Fault();
		},
		"You know what? Maybe you're right.": function(message){
			n(message);
			m("I know...");
			My_Fault();
		}
	});

}

function My_Fault(){

	Show("clock_time","clock_1930");

	n(". . .");
	m("This is all my fault...");
	m("I told you to be careful around those kinds of people, but I told you too late...");

	Show("mom","mom_cry");

	m("[sob]");
	m("Oh Nick! My poor baby!");

	Show("nicky","dinner_nicky_sit");

	Choose({
		"Mom... please don't cry...": Cry_1,
		"Quit your fake crying.": Cry_2,
		"[cry]": Cry_3
	});
}

function Cry_1(message){

	$.crying = "sympathy";

	n(message);
	m("huu... huu... huu...");
	n("I'm sorry. About Jack, the lies, everything.");
	m("owww... owww...");
	n("I take it all back.");
	m("sniff...");
	n("...please...");
	What_Are_You();
}

function Cry_2(message){

	$.crying = "anger";
	Show("nicky","dinner_nicky_defiant");

	n(message);
	m("huu... huu... huu...");
	n("Seriously, it is SO fake.");
	m("owww... owww...");
	n("Will you shut up?!");
	m("sniff...");
	n("SHUT. UP.");
	What_Are_You();

}

function Cry_3(message){

	$.crying = "mocking";
	Show("nicky","dinner_nicky_outrage");

	n("BAWWWWW");
	m("huu... huu... huu...");
	n("WAH WAH WAH WAH WAHHH");
	m("owww... owww...");
	n("BRRrrRR-BRR-BRbR BWAH BWAHRR rrrRRR-WaahHH WO WO WO RaaahhH");
	m("sniff...");

	Show("nicky","dinner_nicky_defiant");
	n("Okay, we done?");
	What_Are_You();

}

function What_Are_You(){

	m(". . .");
	m("Nick... what are you?");
	n("Excuse me?");

	Show("nicky","dinner_nicky_sit");

	Show("mom","mom_sit");
	m("What are you?");

	Choose({
		"I'm bisexual.": function(message){

			$.what_are_you = "bisexual";

			n(message);
			if($.admit_bisexuality){
				m("...and you said that means...");
			}
			n("Sexually attracted to both men and women.");
			m("You can't be both.")
			m("You have to pick one.");
			n("That's... not how it works. At all.");
			Have_You_Had_Sex();

		},
		"I'm just confused.": function(message){

			$.what_are_you = "confused";

			n(message);
			m("...I know.");
			m("I'm sorry Jack confused you.");
			m("You're just going through a phase, it's okay.");
			n(". . .");
			m("It's okay. It's okay...");
			Have_You_Had_Sex();

		},
		"I'm your son, dammit.": function(message){

			$.what_are_you = "son";

			n(message);
			n(". . .");
			n("Isn't that enough?");
			Have_You_Had_Sex();

		}
	});
}

function Have_You_Had_Sex(){
	m(". . .");
	m("Did you have sex with Jack.");
	Choose({
		"Yes.": function(message){
			n(message);
			m("[DRY HEAVE]");
			Have_You_Had_Sex_2();
		},
		"No.": function(message){
			n(message);
			m("Please stop lying... I saw your texts...");
			n("We were just sexting, we didn't actually--");
			m("...and your photos...");
			Have_You_Had_Sex_2();
		},
		"I'm not saying.": function(message){
			n(message);
			m("oh my god... you did.");
			Have_You_Had_Sex_2();
		}
	});
}

function Have_You_Had_Sex_2(){

	n(". . .");
	m("Which... one of you is the woman?");

	Show("nicky","dinner_nicky_outrage");

	n("OH COME ON!");
	n("That's like asking which chopstick is the spoo--");
	m("Which one of you?...");

	Show("nicky","dinner_nicky_defiant");

	Choose({
		"I'm usually the bottom.":function(message){
			$.top_or_bottom = "bottom";

			n(message);
			Throw_Up();
		},
		"Jack is, mostly.":function(message){
			$.top_or_bottom = "top";

			n(message);
			m("Th-that... means you could still be straight! R-right?...");
			m("If... you know... you're the one who puts your...");
			m("your...");
			Throw_Up();
		},
		"We take turns.":function(message){
			$.top_or_bottom = "versatile";

			n(message);
			Throw_Up();
		}
	});
}

function Throw_Up(){

	PlaySound("sfx","dinner_vomit");

	Show("clock_time","clock_1940");
	Show("mom","mom_vomit");
	Show("table","dinner_table_2");
	Wait(1000);

	Choose({
		"what.": Father_Soon,
		"whaaat.": Father_Soon,
		"whaaaaaaaaaaaaaaat.": Father_Soon
	});

}

function Father_Soon(message){

	n(message);

	Show("mom","mom_sit");

	m(". . .");
	m("Your father will be back soon.");
	n("The food's cold. Well, except for the spot you just uh, reversed, on.");
	m("Your dad's late. Must have been a stressful day at work.");
	m("So... please... when he's back...");
	m("Promise me you'll keep all this secret?");
	n(". . .");

	m("Don't tell him about Jack.");

	switch($.what_are_you){
		case "bisexual":
			m("Don't tell him you think you're bisexual.");
			break;
		case "confused":
			m("Don't tell him you're confused about your sexuality.");
			break;
		case "son":
			m("Don't tell him you lied to us so you could... do things with Jack.");
			break;
	}

	switch($.top_or_bottom){
		case "top":
			m("Don't tell him you make Jack a woman.");
			break;
		case "bottom":
			m("Don't tell him you act like a woman with Jack.");
			break;
		case "versatile":
			m("Don't tell him you and Jack both act like women.");
			break;
	}

	m("Okay?...");

	Choose({
		"Okay.": function(message){
			$.promise_silence = "yes";

			n(message);
			m("Okay.");
			m(". . .");
			m("Your father's here.");
			Father_Soon_2();
		},
		"No. Not okay.": function(message){
			$.promise_silence = "no";

			n(message);
			m("Nick, no no no, please--");
			m("Oh no. Your father's here.");
			Father_Soon_2();
		},
		"As long as you don't tell him, either.": function(message){
			$.promise_silence = "tit for tat";

			n(message);
			m("I won't.");
			n("Promise me you won't.");
			m("I pr--");
			m("Shhh. Your father's here.");
			Father_Soon_2();
		}
	});

}

function Father_Soon_2(){
	Show("nicky","dinner_nicky_sit");
	Start_Dinner_5();
}
<commit_msg>Translation of dinner_4 has been started.<commit_after>// is short
// What ARE you. Fake crying, and don't tell your dad
// Weave it

function Start_Dinner_4(){

	n(". . .");
	m("お父さんがほとんど家にいないのがいけないのね?");
	m("お手本になるような男性が近くにいないせいで、男とは何か、あなたは分からなくなっているんだわ...");

	Choose({
		"父さんはいいお手本だよホント。": function(message){
			n(message);
			m("ニッキー、何をしようとあの人はあなたのお父さんなのよ。お父さんを愛しなさい。");
			My_Fault();
		},
		"そんなことは関係ないよ。That's not how it works. I'd be bi anyway.": function(message){
			n("そんなことは関係ないよ。どっちにしろ僕はバイになってたよ。");
			m("そんな事あなたに分かんないでしょ! あなた心理学の専門家でもないでしょう?!");
			My_Fault();
		},
		"あの...そうかもね。": function(message){
			n(message);
			m("そうよ...");
			My_Fault();
		}
	});

}

function My_Fault(){

	Show("clock_time","clock_1930");

	n(". . .");
	m("私がいけないんだわ...");
	m("ああいうタイプの人に気をつけなさいと、もっと早く言ってあげるべきだったわ...");

	Show("mom","mom_cry");

	m("[すすり泣く]");
	m("ニッキー! かわいそうに!");

	Show("nicky","dinner_nicky_sit");

	Choose({
		"母さん...泣かないで...": Cry_1,
		"嘘泣きはやめろよ。": Cry_2,
		"[泣く]": Cry_3
	});
}

function Cry_1(message){

	$.crying = "sympathy";

	n(message);
	m("huu... huu... huu...");
	n("I'm sorry. About Jack, the lies, everything.");
	m("owww... owww...");
	n("I take it all back.");
	m("sniff...");
	n("...please...");
	What_Are_You();
}

function Cry_2(message){

	$.crying = "anger";
	Show("nicky","dinner_nicky_defiant");

	n(message);
	m("huu... huu... huu...");
	n("Seriously, it is SO fake.");
	m("owww... owww...");
	n("Will you shut up?!");
	m("sniff...");
	n("SHUT. UP.");
	What_Are_You();

}

function Cry_3(message){

	$.crying = "mocking";
	Show("nicky","dinner_nicky_outrage");

	n("BAWWWWW");
	m("huu... huu... huu...");
	n("WAH WAH WAH WAH WAHHH");
	m("owww... owww...");
	n("BRRrrRR-BRR-BRbR BWAH BWAHRR rrrRRR-WaahHH WO WO WO RaaahhH");
	m("sniff...");

	Show("nicky","dinner_nicky_defiant");
	n("Okay, we done?");
	What_Are_You();

}

function What_Are_You(){

	m(". . .");
	m("Nick... what are you?");
	n("Excuse me?");

	Show("nicky","dinner_nicky_sit");

	Show("mom","mom_sit");
	m("What are you?");

	Choose({
		"I'm bisexual.": function(message){

			$.what_are_you = "bisexual";

			n(message);
			if($.admit_bisexuality){
				m("...and you said that means...");
			}
			n("Sexually attracted to both men and women.");
			m("You can't be both.")
			m("You have to pick one.");
			n("That's... not how it works. At all.");
			Have_You_Had_Sex();

		},
		"I'm just confused.": function(message){

			$.what_are_you = "confused";

			n(message);
			m("...I know.");
			m("I'm sorry Jack confused you.");
			m("You're just going through a phase, it's okay.");
			n(". . .");
			m("It's okay. It's okay...");
			Have_You_Had_Sex();

		},
		"I'm your son, dammit.": function(message){

			$.what_are_you = "son";

			n(message);
			n(". . .");
			n("Isn't that enough?");
			Have_You_Had_Sex();

		}
	});
}

function Have_You_Had_Sex(){
	m(". . .");
	m("Did you have sex with Jack.");
	Choose({
		"Yes.": function(message){
			n(message);
			m("[DRY HEAVE]");
			Have_You_Had_Sex_2();
		},
		"No.": function(message){
			n(message);
			m("Please stop lying... I saw your texts...");
			n("We were just sexting, we didn't actually--");
			m("...and your photos...");
			Have_You_Had_Sex_2();
		},
		"I'm not saying.": function(message){
			n(message);
			m("oh my god... you did.");
			Have_You_Had_Sex_2();
		}
	});
}

function Have_You_Had_Sex_2(){

	n(". . .");
	m("Which... one of you is the woman?");

	Show("nicky","dinner_nicky_outrage");

	n("OH COME ON!");
	n("That's like asking which chopstick is the spoo--");
	m("Which one of you?...");

	Show("nicky","dinner_nicky_defiant");

	Choose({
		"I'm usually the bottom.":function(message){
			$.top_or_bottom = "bottom";

			n(message);
			Throw_Up();
		},
		"Jack is, mostly.":function(message){
			$.top_or_bottom = "top";

			n(message);
			m("Th-that... means you could still be straight! R-right?...");
			m("If... you know... you're the one who puts your...");
			m("your...");
			Throw_Up();
		},
		"We take turns.":function(message){
			$.top_or_bottom = "versatile";

			n(message);
			Throw_Up();
		}
	});
}

function Throw_Up(){

	PlaySound("sfx","dinner_vomit");

	Show("clock_time","clock_1940");
	Show("mom","mom_vomit");
	Show("table","dinner_table_2");
	Wait(1000);

	Choose({
		"what.": Father_Soon,
		"whaaat.": Father_Soon,
		"whaaaaaaaaaaaaaaat.": Father_Soon
	});

}

function Father_Soon(message){

	n(message);

	Show("mom","mom_sit");

	m(". . .");
	m("Your father will be back soon.");
	n("The food's cold. Well, except for the spot you just uh, reversed, on.");
	m("Your dad's late. Must have been a stressful day at work.");
	m("So... please... when he's back...");
	m("Promise me you'll keep all this secret?");
	n(". . .");

	m("Don't tell him about Jack.");

	switch($.what_are_you){
		case "bisexual":
			m("Don't tell him you think you're bisexual.");
			break;
		case "confused":
			m("Don't tell him you're confused about your sexuality.");
			break;
		case "son":
			m("Don't tell him you lied to us so you could... do things with Jack.");
			break;
	}

	switch($.top_or_bottom){
		case "top":
			m("Don't tell him you make Jack a woman.");
			break;
		case "bottom":
			m("Don't tell him you act like a woman with Jack.");
			break;
		case "versatile":
			m("Don't tell him you and Jack both act like women.");
			break;
	}

	m("Okay?...");

	Choose({
		"Okay.": function(message){
			$.promise_silence = "yes";

			n(message);
			m("Okay.");
			m(". . .");
			m("Your father's here.");
			Father_Soon_2();
		},
		"No. Not okay.": function(message){
			$.promise_silence = "no";

			n(message);
			m("Nick, no no no, please--");
			m("Oh no. Your father's here.");
			Father_Soon_2();
		},
		"As long as you don't tell him, either.": function(message){
			$.promise_silence = "tit for tat";

			n(message);
			m("I won't.");
			n("Promise me you won't.");
			m("I pr--");
			m("Shhh. Your father's here.");
			Father_Soon_2();
		}
	});

}

function Father_Soon_2(){
	Show("nicky","dinner_nicky_sit");
	Start_Dinner_5();
}
<|endoftext|>