<commit_before>/* jshint node:true */
/**
 * PUSH Notification server
 * (c) Telefonica Digital, 2012 - All rights reserved
 * License: GNU Affero V3 (see LICENSE file)
 * Fernando Rodríguez Sela <frsela@tid.es>
 * Guillermo Lopez Leal <gll@tid.es>
 */

'use strict';

var Log = require('../common/Logger'),
    urlparser = require('url'),
    config = require('../config.js'),
    consts = config.consts,
    fs = require('fs'),
    cluster = require('cluster'),
    MsgBroker = require('../common/MsgBroker'),
    DataStore = require('../common/DataStore'),
    errorcodes = require('../common/constants').errorcodes.GENERAL,
    errorcodesAS = require('../common/constants').errorcodes.AS,
    Pages = require('../common/Pages.js'),
    Maintenance = require('../common/Maintenance.js'),
    Helpers = require('../common/Helpers.js');

function NS_AS() {
    this.ip = '';
    this.port = 0;
    this.ssl = false;
    this.kSimplePushASFrontendVersionv1 = 'v1';
    this.server = null;
    this.closing = false;
    this.closingCorrectly = false;
    this.dataStoreReady = false;
    this.msgBrokerReady = false;
    this.readyTimeout = undefined;
}

NS_AS.prototype = {

    checkReady: function() {
        if (this.dataStoreReady && this.msgBrokerReady) {
            Log.debug(
                'NS_AS::checkReady --> We are ready. Clearing any readyTimeout'
            );
            clearTimeout(this.readyTimeout);
        } else {
            Log.debug('NS_AS::checkReady --> Not ready yet. dataStoreReady=' +
                this.dataStoreReady +
                ', msgBrokerReady=' + this.msgBrokerReady);
        }
        return this.dataStoreReady && this.msgBrokerReady;
    },

    start: function() {
        Log.info('NS_AS::start()');
        var conf = config.NS_AS.interface;
        this.ip = conf.ip;
        this.port = conf.port;
        this.ssl = conf.ssl;

        var closed = 0;
        var errored = false;
        var forked = 0;

        if (cluster.isMaster) {
            // Fork workers.
            for (var i = 0; i < config.NS_AS.numProcesses; i++) {
                cluster.fork();
                forked++;
            }

            cluster.on('exit', function(worker, code) {
                if (code !== 0) {
                    Log.error(Log.messages.ERROR_WORKERERROR, {
                        'id': worker.id,
                        'pid': worker.process.pid,
                        'code': code
                    });
                    if (forked > 20) {
                        Log.critical(
                            'NS_AS::start --> Please, check logs, there have been too much re-spawns'
                        );
                    } else {
                        Log.info('NS_AS::start --> Spawning a new worker…');
                        cluster.fork();
                        forked++;
                        --closed;
                        errored = true;
                    }
                } else {
                    Log.info('NS_AS::start -- wrk' + worker.id +
                        ' with PID ' + worker.process.pid +
                        ' exited correctly');
                }
                ++closed;
                if (closed === config.NS_AS.numProcesses) {
                    if (errored) {
                        Log.error(
                            'NS_AS::start() -- Closing INCORRECTLY. Check errors for a worker'
                        );
                        process.exit(1);
                    } else {
                        Log.info(
                            'NS_AS::start() -- Closing. That\'s all folks!'
                        );
                        process.exit(0);
                    }
                }
            });

        } else {
            var self = this;
            // Create a new HTTP(S) Server
            if (this.ssl) {
                var options = {
                    ca: Helpers.getCaChannel(),
                    key: fs.readFileSync(consts.key),
                    cert: fs.readFileSync(consts.cert),
                    requestCert: false,
                    rejectUnauthorized: false
                };
                this.server = require('https').createServer(options, this.onHTTPMessage
                    .bind(this));
            } else {
                this.server = require('http').createServer(this.onHTTPMessage.bind(
                    this));
            }
            this.server.listen(this.port, this.ip);
            Log.info('NS_AS::start --> HTTP' + (this.ssl ? 'S' : '') +
                ' push AS server starting on ' + this.ip + ':' + this.port);

            // Events from MsgBroker
            MsgBroker.once('ready', function() {
                Log.info('NS_AS::start --> MsgBroker ready and connected');
                self.msgBrokerReady = true;
                self.checkReady();
            });
            MsgBroker.on('closed', function() {
                if (self.closingCorrectly) {
                    Log.info('NS_AS::start --> Closed MsgBroker');
                    return;
                }
                Log.critical(Log.messages.CRITICAL_MBDISCONNECTED, {
                    'class': 'NS_AS',
                    'method': 'start'
                });
                self.msgBrokerReady = false;
                self.stop();
            });

            //Events from DataStore
            DataStore.once('ready', function() {
                Log.info('NS_AS::start --> DataStore ready and connected');
                self.dataStoreReady = true;
                self.checkReady();
            });
            DataStore.on('closed', function() {
                if (self.closingCorrectly) {
                    Log.info('NS_AS::start --> Closed DataStore');
                    return;
                }
                Log.critical(Log.messages.CRITICAL_DBDISCONNECTED, {
                    'class': 'NS_AS',
                    'method': 'start'
                });
                self.dataStoreReady = false;
                self.stop();
            });

            //Wait until we have setup our events listeners
            process.nextTick(function() {
                MsgBroker.start();
                DataStore.start();
            });

            //Check if we are alive
            this.readyTimeout = setTimeout(function() {
                Log.debug('NS_AS::start --> readyTimeout fired');
                if (!self.checkReady()) {
                    Log.critical(Log.messages.CRITICAL_NOTREADY);
                }
            }, 30 * 1000); //Wait 30 seconds
        }
    },

    stop: function(correctly) {
        Log.info('NS_AS::stop() called with correctly=' + correctly);
        if (cluster.isMaster) {
            var timeouts = [];
            Object.keys(cluster.workers).forEach(function(id) {
                timeouts[id] = setTimeout(function() {
                    Log.info('NS_AS::stop --> Killing worker ' + id);
                    cluster.workers[id].destroy();
                }, 4000);
                cluster.workers[id].on('disconnect', function() {
                    Log.info('NS_AS::stop --> Worker ' + id +
                        ' disconnected');
                    clearTimeout(timeouts[id]);
                });
            });
            setTimeout(function() {
                process.exit(1);
            }, 10000);

        } else {
            this.closingCorrectly = correctly;
            Log.info('NS_AS::stop --> Closing NS_AS server');
            clearTimeout(this.readyTimeout);

            //Closing connection with MsgBroker and DataStore
            DataStore.removeAllListeners();
            MsgBroker.removeAllListeners();
            MsgBroker.stop();
            DataStore.stop();

            //Closing connections from the server
            this.server.close();

            setTimeout(function() {
                Log.info('Suiciding worker with id=' + cluster.worker.id +
                    '. Bye!');
                cluster.worker.destroy();
            }, 3000);
        }
    },

    onHTTPMessage: function(request, response) {
        var self = this;

        response.res = function responseHTTP(errorCode) {
            Log.debug('NS_AS::responseHTTP: ', errorCode);
            this.statusCode = errorCode[0];
            this.setHeader('access-control-allow-origin', '*');
            if (consts.PREPRODUCTION_MODE) {
                this.setHeader('Content-Type', 'text/plain');
                if (this.statusCode === 200) {
                    this.write('{"status":"ACCEPTED"}');
                } else {
                    this.write('{"status":"ERROR", "reason":"' + errorCode[1] +
                        '"}');
                }
            }
            this.end();
        };

        if (!self.checkReady()) {
            Log.debug(
                'NS_AS::onHTTPMessage --> Message rejected, we are not ready yet'
            );
            response.res(errorcodes.NOT_READY);
            return;
        }

        Log.debug('NS_AS::onHTTPMessage --> Received request for ' + request.url);
        var url = urlparser.parse(request.url, true);
        var path = url.pathname.split('/');

        // CORS support
        if (request.method === 'OPTIONS') {
            Log.debug('NS_AS::onHTTPMessage --> Received an OPTIONS method');
            response.setHeader('Access-Control-Allow-Origin', '*');
            response.setHeader('Access-Control-Allow-Methods',
                'PUT, GET, OPTIONS');
            response.end();
            return;
        }

        // Frontend for the Mozilla SimplePush API
        if (request.method === 'PUT') {
            Log.debug('NS_AS::onHTTPMessage --> Received a PUT');
            var body = '';
            request.on('data', function(data) {
                body += data;
                // Max. payload: "version=9007199254740992" => lenght: 24
                if (body.length > 25) {
                    request.tooBig = true;
                    request.emit('end');
                    Log.debug(
                        'NS_AS::onHTTPMessage --> Message rejected, too long for this API'
                    );
                    response.res(errorcodesAS.BAD_MESSAGE_BODY_TOO_BIG);
                }
            });
            request.on('end', function() {
                if (request.tooBig) {
                    return;
                }
                self.simplepushRequest(request, body, response);
            });

        } else if (request.method === 'GET') {
            switch (path[1]) {
                case 'about':
                    if (consts.PREPRODUCTION_MODE) {
                        var text = '';
                        try {
                            var p = new Pages();
                            p.setTemplate('views/about.tmpl');
                            text = p.render(function(t) {
                                switch (t) {
                                    case '{{GIT_VERSION}}':
                                        return require('fs').readFileSync(
                                            'version.info');
                                    case '{{MODULE_NAME}}':
                                        return 'Application Server Frontend';
                                    default:
                                        return '';
                                }
                            });
                        } catch (e) {
                            text = 'No version.info file';
                        }
                        response.setHeader('Content-Type', 'text/html');
                        response.statusCode = 200;
                        response.write(text);
                        response.end();
                        return;
                    } else {
                        response.res(errorcodes.NOT_ALLOWED_ON_PRODUCTION_SYSTEM);
                        return;
                    }
                    break;

                case 'status':
                    // Return status mode to be used by load-balancers
                    response.setHeader('Content-Type', 'text/html');
                    if (Maintenance.getStatus()) {
                        response.statusCode = 503;
                        response.write('Under Maintenance');
                    } else {
                        response.statusCode = 200;
                        response.write('OK');
                    }
                    response.end();
                    break;

                default:
                    Log.debug('NS_AS::onHTTPMessage --> messageType "' + path[1] +
                        '" not recognized');
                    response.res(errorcodesAS.BAD_URL);
                    return;
            }

        } else {
            response.statusCode = 400;
            response.end();
        }
    },

    simplepushRequest: function(request, body, response) {
        /**
         * Check if we are closing. If so, send a 500 status
         * to the client. That means: "retry again later"
         */
        if (this.closing) {
            response.end(500);
            return;
        }

        /**
         * Check if the request is correct
         */
        var URI = request.url.split('/');
        var appToken = URI[3];
        var versions = String(body).split('=');
        var version = versions[1] || -1;

        if (URI.length < 3 ||
            URI[1] !== this.kSimplePushASFrontendVersionv1 ||
            URI[2] !== 'notify' || !appToken ||
            versions[0] !== 'version' || !Helpers.isVersion(version)) {
            response.statusCode = 400;
            response.end('{ reason: "Bad request"}');
            Log.info('NS_AS::simplepushRequest --> Bad request ' + body);
            return;
        }

        //Now, we are safe to start using the path and data
        var connectionIP = request.socket.remoteAddress;
        Log.notify(Log.messages.NOTIFY_APPTOKEN_VERSION, {
            appToken: appToken,
            version: version,
            ip: connectionIP || null
        });

        // Send the OK response always, this free some server resources
        response.statusCode = 200;
        //CORS support
        response.setHeader('Access-Control-Allow-Origin', '*');
        response.end('{}');

        //And now we proccess the notification.
        DataStore.getInfoForAppToken(appToken, function(error, appInfo) {
            if (!appInfo || !appInfo.ch || !Array.isArray(appInfo.no)) {
                return;
            }
            (appInfo.no).forEach(function(nodeId) {
                var msg = DataStore.newVersion(nodeId, appToken,
                    appInfo.ch, version);
                MsgBroker.push('newMessages', msg);
            });
        });
    }
};

// Exports
exports.NS_AS = NS_AS;
<commit_msg>JIRA/OWD-38012 Push server: Monitor push API to detect incorrect usage<commit_after>/* jshint node:true */
/**
 * PUSH Notification server
 * (c) Telefonica Digital, 2012 - All rights reserved
 * License: GNU Affero V3 (see LICENSE file)
 * Fernando Rodríguez Sela <frsela@tid.es>
 * Guillermo Lopez Leal <gll@tid.es>
 */

'use strict';

var Log = require('../common/Logger'),
    urlparser = require('url'),
    config = require('../config.js'),
    consts = config.consts,
    fs = require('fs'),
    cluster = require('cluster'),
    MsgBroker = require('../common/MsgBroker'),
    DataStore = require('../common/DataStore'),
    errorcodes = require('../common/constants').errorcodes.GENERAL,
    errorcodesAS = require('../common/constants').errorcodes.AS,
    Pages = require('../common/Pages.js'),
    Maintenance = require('../common/Maintenance.js'),
    Helpers = require('../common/Helpers.js');

function NS_AS() {
    this.ip = '';
    this.port = 0;
    this.ssl = false;
    this.kSimplePushASFrontendVersionv1 = 'v1';
    this.server = null;
    this.closing = false;
    this.closingCorrectly = false;
    this.dataStoreReady = false;
    this.msgBrokerReady = false;
    this.readyTimeout = undefined;
}

NS_AS.prototype = {

    checkReady: function() {
        if (this.dataStoreReady && this.msgBrokerReady) {
            Log.debug(
                'NS_AS::checkReady --> We are ready. Clearing any readyTimeout'
            );
            clearTimeout(this.readyTimeout);
        } else {
            Log.debug('NS_AS::checkReady --> Not ready yet. dataStoreReady=' +
                this.dataStoreReady +
                ', msgBrokerReady=' + this.msgBrokerReady);
        }
        return this.dataStoreReady && this.msgBrokerReady;
    },

    start: function() {
        Log.info('NS_AS::start()');
        var conf = config.NS_AS.interface;
        this.ip = conf.ip;
        this.port = conf.port;
        this.ssl = conf.ssl;

        var closed = 0;
        var errored = false;
        var forked = 0;

        if (cluster.isMaster) {
            // Fork workers.
            for (var i = 0; i < config.NS_AS.numProcesses; i++) {
                cluster.fork();
                forked++;
            }

            cluster.on('exit', function(worker, code) {
                if (code !== 0) {
                    Log.error(Log.messages.ERROR_WORKERERROR, {
                        'id': worker.id,
                        'pid': worker.process.pid,
                        'code': code
                    });
                    if (forked > 20) {
                        Log.critical(
                            'NS_AS::start --> Please, check logs, there have been too much re-spawns'
                        );
                    } else {
                        Log.info('NS_AS::start --> Spawning a new worker…');
                        cluster.fork();
                        forked++;
                        --closed;
                        errored = true;
                    }
                } else {
                    Log.info('NS_AS::start -- wrk' + worker.id +
                        ' with PID ' + worker.process.pid +
                        ' exited correctly');
                }
                ++closed;
                if (closed === config.NS_AS.numProcesses) {
                    if (errored) {
                        Log.error(
                            'NS_AS::start() -- Closing INCORRECTLY. Check errors for a worker'
                        );
                        process.exit(1);
                    } else {
                        Log.info(
                            'NS_AS::start() -- Closing. That\'s all folks!'
                        );
                        process.exit(0);
                    }
                }
            });

        } else {
            var self = this;
            // Create a new HTTP(S) Server
            if (this.ssl) {
                var options = {
                    ca: Helpers.getCaChannel(),
                    key: fs.readFileSync(consts.key),
                    cert: fs.readFileSync(consts.cert),
                    requestCert: false,
                    rejectUnauthorized: false
                };
                this.server = require('https').createServer(options, this.onHTTPMessage
                    .bind(this));
            } else {
                this.server = require('http').createServer(this.onHTTPMessage.bind(
                    this));
            }
            this.server.listen(this.port, this.ip);
            Log.info('NS_AS::start --> HTTP' + (this.ssl ? 'S' : '') +
                ' push AS server starting on ' + this.ip + ':' + this.port);

            // Events from MsgBroker
            MsgBroker.once('ready', function() {
                Log.info('NS_AS::start --> MsgBroker ready and connected');
                self.msgBrokerReady = true;
                self.checkReady();
            });
            MsgBroker.on('closed', function() {
                if (self.closingCorrectly) {
                    Log.info('NS_AS::start --> Closed MsgBroker');
                    return;
                }
                Log.critical(Log.messages.CRITICAL_MBDISCONNECTED, {
                    'class': 'NS_AS',
                    'method': 'start'
                });
                self.msgBrokerReady = false;
                self.stop();
            });

            //Events from DataStore
            DataStore.once('ready', function() {
                Log.info('NS_AS::start --> DataStore ready and connected');
                self.dataStoreReady = true;
                self.checkReady();
            });
            DataStore.on('closed', function() {
                if (self.closingCorrectly) {
                    Log.info('NS_AS::start --> Closed DataStore');
                    return;
                }
                Log.critical(Log.messages.CRITICAL_DBDISCONNECTED, {
                    'class': 'NS_AS',
                    'method': 'start'
                });
                self.dataStoreReady = false;
                self.stop();
            });

            //Wait until we have setup our events listeners
            process.nextTick(function() {
                MsgBroker.start();
                DataStore.start();
            });

            //Check if we are alive
            this.readyTimeout = setTimeout(function() {
                Log.debug('NS_AS::start --> readyTimeout fired');
                if (!self.checkReady()) {
                    Log.critical(Log.messages.CRITICAL_NOTREADY);
                }
            }, 30 * 1000); //Wait 30 seconds
        }
    },

    stop: function(correctly) {
        Log.info('NS_AS::stop() called with correctly=' + correctly);
        if (cluster.isMaster) {
            var timeouts = [];
            Object.keys(cluster.workers).forEach(function(id) {
                timeouts[id] = setTimeout(function() {
                    Log.info('NS_AS::stop --> Killing worker ' + id);
                    cluster.workers[id].destroy();
                }, 4000);
                cluster.workers[id].on('disconnect', function() {
                    Log.info('NS_AS::stop --> Worker ' + id +
                        ' disconnected');
                    clearTimeout(timeouts[id]);
                });
            });
            setTimeout(function() {
                process.exit(1);
            }, 10000);

        } else {
            this.closingCorrectly = correctly;
            Log.info('NS_AS::stop --> Closing NS_AS server');
            clearTimeout(this.readyTimeout);

            //Closing connection with MsgBroker and DataStore
            DataStore.removeAllListeners();
            MsgBroker.removeAllListeners();
            MsgBroker.stop();
            DataStore.stop();

            //Closing connections from the server
            this.server.close();

            setTimeout(function() {
                Log.info('Suiciding worker with id=' + cluster.worker.id +
                    '. Bye!');
                cluster.worker.destroy();
            }, 3000);
        }
    },

    // Get IP and port also if configured using UNIX sockets
    // The HTTP proxy shall inyect the header X-Forwarded-For with the port
    // proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for:$remote_port;
    setRemoteAddress: function(request) {
        if (!request.socket.remoteAddress && !request.socket.remotePort &&
            request.headers['x-forwarded-for']) {
            request.remoteAddress =
                request.headers['x-forwarded-for'].split(':')[0];
            request.remotePort =
                request.headers['x-forwarded-for'].split(':')[1];
        } else {
            request.remoteAddress = request.socket.remoteAddress;
            request.remotePort = request.socket.remotePort;
        }

        Log.info('Connection from ' + this.getRemoteAddress(request));
    },

    getRemoteAddress: function(request) {
        return request.remoteAddress + ':' + request.remotePort;
    },

    onHTTPMessage: function(request, response) {
        this.setRemoteAddress(request);

        var self = this;
        response.res = function responseHTTP(errorCode) {
            Log.debug('NS_AS::responseHTTP: ', errorCode);
            this.statusCode = errorCode[0];
            this.setHeader('access-control-allow-origin', '*');
            if (consts.PREPRODUCTION_MODE) {
                this.setHeader('Content-Type', 'text/plain');
                if (this.statusCode === 200) {
                    this.write('{"status":"ACCEPTED"}');
                } else {
                    this.write('{"status":"ERROR", "reason":"' + errorCode[1] +
                        '"}');
                }
            }
            this.end();
        };

        if (!self.checkReady()) {
            Log.debug(
                'NS_AS::onHTTPMessage --> Message rejected, we are not ready yet'
            );
            response.res(errorcodes.NOT_READY);
            return;
        }

        Log.debug('NS_AS::onHTTPMessage --> Received request for ' + request.url);
        var url = urlparser.parse(request.url, true);
        var path = url.pathname.split('/');

        // CORS support
        if (request.method === 'OPTIONS') {
            Log.debug('NS_AS::onHTTPMessage --> Received an OPTIONS method');
            response.setHeader('Access-Control-Allow-Origin', '*');
            response.setHeader('Access-Control-Allow-Methods',
                'PUT, GET, OPTIONS');
            response.end();
            return;
        }

        // Frontend for the Mozilla SimplePush API
        if (request.method === 'PUT') {
            Log.debug('NS_AS::onHTTPMessage --> Received a PUT');
            var body = '';
            request.on('data', function(data) {
                body += data;
                // Max. payload: "version=9007199254740992" => lenght: 24
                if (body.length > 25) {
                    request.tooBig = true;
                    request.emit('end');
                    Log.debug(
                        'NS_AS::onHTTPMessage --> Message rejected, too long for this API'
                    );
                    response.res(errorcodesAS.BAD_MESSAGE_BODY_TOO_BIG);
                }
            });
            request.on('end', function() {
                if (request.tooBig) {
                    return;
                }
                self.simplepushRequest(request, body, response);
            });

        } else if (request.method === 'GET') {
            switch (path[1]) {
                case 'about':
                    if (consts.PREPRODUCTION_MODE) {
                        var text = '';
                        try {
                            var p = new Pages();
                            p.setTemplate('views/about.tmpl');
                            text = p.render(function(t) {
                                switch (t) {
                                    case '{{GIT_VERSION}}':
                                        return require('fs').readFileSync(
                                            'version.info');
                                    case '{{MODULE_NAME}}':
                                        return 'Application Server Frontend';
                                    default:
                                        return '';
                                }
                            });
                        } catch (e) {
                            text = 'No version.info file';
                        }
                        response.setHeader('Content-Type', 'text/html');
                        response.statusCode = 200;
                        response.write(text);
                        response.end();
                        return;
                    } else {
                        response.res(errorcodes.NOT_ALLOWED_ON_PRODUCTION_SYSTEM);
                        return;
                    }
                    break;

                case 'status':
                    // Return status mode to be used by load-balancers
                    response.setHeader('Content-Type', 'text/html');
                    if (Maintenance.getStatus()) {
                        response.statusCode = 503;
                        response.write('Under Maintenance');
                    } else {
                        response.statusCode = 200;
                        response.write('OK');
                    }
                    response.end();
                    break;

                default:
                    Log.debug('NS_AS::onHTTPMessage --> messageType "' + path[1] +
                        '" not recognized');
                    response.res(errorcodesAS.BAD_URL);
                    return;
            }

        } else {
            response.statusCode = 400;
            response.end();
        }
    },

    simplepushRequest: function(request, body, response) {
        /**
         * Check if we are closing. If so, send a 500 status
         * to the client. That means: "retry again later"
         */
        if (this.closing) {
            response.end(500);
            return;
        }

        /**
         * Check if the request is correct
         */
        var URI = request.url.split('/');
        var appToken = URI[3];
        var versions = String(body).split('=');
        var version = versions[1] || -1;

        if (URI.length < 3 ||
            URI[1] !== this.kSimplePushASFrontendVersionv1 ||
            URI[2] !== 'notify' || !appToken ||
            versions[0] !== 'version' || !Helpers.isVersion(version)) {
            response.statusCode = 400;
            response.end('{ reason: "Bad request"}');
            Log.info('NS_AS::simplepushRequest --> Bad request from ' +
            this.getRemoteAddress(request) + ' with body "' + body + '"');
            return;
        }

        //Now, we are safe to start using the path and data
        var connectionIP = request.socket.remoteAddress;
        Log.notify(Log.messages.NOTIFY_APPTOKEN_VERSION, {
            appToken: appToken,
            version: version,
            ip: connectionIP || null
        });

        // Send the OK response always, this free some server resources
        response.statusCode = 200;
        //CORS support
        response.setHeader('Access-Control-Allow-Origin', '*');
        response.end('{}');

        //And now we proccess the notification.
        DataStore.getInfoForAppToken(appToken, function(error, appInfo) {
            if (!appInfo || !appInfo.ch || !Array.isArray(appInfo.no)) {
                return;
            }
            (appInfo.no).forEach(function(nodeId) {
                var msg = DataStore.newVersion(nodeId, appToken,
                    appInfo.ch, version);
                MsgBroker.push('newMessages', msg);
            });
        });
    }
};

// Exports
exports.NS_AS = NS_AS;
<|endoftext|>


<commit_before>var redis = require('redis');
var bluebird = require('bluebird');
var hash = require('string-hash');

// Activate Promises for redis
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);

var StorageHandler = module.exports = function () {
  this.storage = redis.createClient();
};

StorageHandler.prototype.getTeamPromise = function(teamName, region) {
  var self = this;
  var saveName = hash(region + "-" + teamName);
  return new Promise(function(resolve, reject){
    self.storage.getAsync("team:"+saveName).then(function(data){
      resolve(JSON.parse(data));
    }).catch(function(err){
      reject(err);
    });
  });
}

StorageHandler.prototype.getSummonerPromise = function(summName, region) {
  var self = this;
  var saveName = hash(region + "-" + summName);
  return new Promise(function(resolve, reject){
    self.storage.getAsync("summ:"+saveName).then(function(data){
      resolve(JSON.parse(data));
    }).catch(function(err){
      reject(err);
    });
  });
}

StorageHandler.prototype.addSummoner = function(summName, region, data) {
  var saveName = hash(region + "-" + summName);
  this.storage.set("summ:"+saveName, JSON.stringify(data));
}
<commit_msg>Added a routine that adds a team to the storage<commit_after>var redis = require('redis');
var bluebird = require('bluebird');
var hash = require('string-hash');

// Activate Promises for redis
bluebird.promisifyAll(redis.RedisClient.prototype);
bluebird.promisifyAll(redis.Multi.prototype);

var StorageHandler = module.exports = function () {
  this.storage = redis.createClient();
};

StorageHandler.prototype.getTeamPromise = function(teamName, region) {
  var self = this;
  var saveName = hash(region + "-" + teamName);
  return new Promise(function(resolve, reject){
    self.storage.getAsync("team:"+saveName).then(function(data){
      resolve(JSON.parse(data));
    }).catch(function(err){
      reject(err);
    });
  });
}

StorageHandler.prototype.getSummonerPromise = function(summName, region) {
  var self = this;
  var saveName = hash(region + "-" + summName);
  return new Promise(function(resolve, reject){
    self.storage.getAsync("summ:"+saveName).then(function(data){
      resolve(JSON.parse(data));
    }).catch(function(err){
      reject(err);
    });
  });
}

StorageHandler.prototype.addSummoner = function(summName, region, data) {
  var saveName = hash(region + "-" + summName);
  this.storage.set("summ:"+saveName, JSON.stringify(data));
}

StorageHandler.prototype.addTeam = function(teamName, region, data) {
  var saveName = hash(region + "-" + teamName);
  this.storage.set("team:"+saveName, JSON.stringify(data));
}
<|endoftext|>


<commit_before>var firebaseAuthToken = "";
var firebaseUid = "";
var intervalId = null;
var checker = function() {
  firebaseAuthToken = $('#firebaseAuthToken').text();
  firebaseUid = $('#firebaseUid').text();

  if(firebaseAuthToken != '') {
    console.log('setting uid ' + firebaseUid);
    console.log('setting authToken ' + firebaseAuthToken);
    chrome.storage.local.set({firebaseAuthToken: firebaseAuthToken, firebaseUid: firebaseUid});
    clearTimeout(intervalId);
    window.close();
  }
};

intervalId = setTimeout(checker, 1000);

<commit_msg>remove console log messages in auth.js<commit_after>var firebaseAuthToken = "";
var firebaseUid = "";
var intervalId = null;
var checker = function() {
  firebaseAuthToken = $('#firebaseAuthToken').text();
  firebaseUid = $('#firebaseUid').text();

  if(firebaseAuthToken != '') {
    chrome.storage.local.set({firebaseAuthToken: firebaseAuthToken, firebaseUid: firebaseUid});
    clearTimeout(intervalId);
    window.close();
  }
};

intervalId = setTimeout(checker, 1000);

<|endoftext|>


<commit_before>import { Map } from 'immutable'
import { createSelector } from 'reselect'

const replaceUndefined = (value) =>
  (typeof value === 'undefined') ? '' : value

const getRegionData = (state) => state.regionData

const makeExtremeValuesSelector = () =>
  createSelector([getRegionData], (regionData) => {
    const values = regionData.map((item) => item.get('value'))
    const filteredValues = values.filterNot((item) => item === '')
    return Map({
      min: replaceUndefined(filteredValues.min()),
      max: replaceUndefined(filteredValues.max())
    })
  }
)

export default makeExtremeValuesSelector;
<commit_msg>update extremeValues selector<commit_after>import { Map, List } from 'immutable'
import { createSelector } from 'reselect'

const replaceUndefined = (value) =>
  (typeof value === 'undefined') ? '' : value

const getRegionData = (state) => state.regionData
const domainStartValue = (state) => state.mapUi.getIn(['equidistant', 'domainStartValue'])
const domainEndValue = (state) => state.mapUi.getIn(['equidistant', 'domainEndValue'])

const makeExtremeValuesSelector = () =>
  createSelector(
  [getRegionData, domainStartValue, domainEndValue],
  (regionData, startValue, endValue) => {
    const values = regionData.map((item) => item.get('value'))
    const combinedValues = List([values, startValue, endValue]).flatten()
    const filteredValues = combinedValues.filterNot((item) => item === '')

    return Map({
      min: replaceUndefined(filteredValues.min()),
      max: replaceUndefined(filteredValues.max()),
    })
  }
)

export default makeExtremeValuesSelector;
<|endoftext|>


<commit_before>/**
 * @module ol/Feature
 */
import {assert} from './asserts.js';
import {listen, unlisten, unlistenByKey} from './events.js';
import EventType from './events/EventType.js';
import BaseObject, {getChangeEventType} from './Object.js';
import Geometry from './geom/Geometry.js';
import Style from './style/Style.js';

/**
 * @classdesc
 * A vector object for geographic features with a geometry and other
 * attribute properties, similar to the features in vector file formats like
 * GeoJSON.
 *
 * Features can be styled individually with `setStyle`; otherwise they use the
 * style of their vector layer.
 *
 * Note that attribute properties are set as {@link module:ol/Object} properties on
 * the feature object, so they are observable, and have get/set accessors.
 *
 * Typically, a feature has a single geometry property. You can set the
 * geometry using the `setGeometry` method and get it with `getGeometry`.
 * It is possible to store more than one geometry on a feature using attribute
 * properties. By default, the geometry used for rendering is identified by
 * the property name `geometry`. If you want to use another geometry property
 * for rendering, use the `setGeometryName` method to change the attribute
 * property associated with the geometry for the feature.  For example:
 *
 * ```js
 *
 * import Feature from 'ol/Feature';
 * import Polygon from 'ol/geom/Polygon';
 * import Point from 'ol/geom/Point';
 *
 * var feature = new Feature({
 *   geometry: new Polygon(polyCoords),
 *   labelPoint: new Point(labelCoords),
 *   name: 'My Polygon'
 * });
 *
 * // get the polygon geometry
 * var poly = feature.getGeometry();
 *
 * // Render the feature as a point using the coordinates from labelPoint
 * feature.setGeometryName('labelPoint');
 *
 * // get the point geometry
 * var point = feature.getGeometry();
 * ```
 *
 * @api
 */
class Feature extends BaseObject {
  /**
   * @param {Geometry|Object<string, *>=} opt_geometryOrProperties
   *     You may pass a Geometry object directly, or an object literal containing
   *     properties. If you pass an object literal, you may include a Geometry
   *     associated with a `geometry` key.
   */
  constructor(opt_geometryOrProperties) {

    super();

    /**
     * @private
     * @type {number|string|undefined}
     */
    this.id_ = undefined;

    /**
     * @type {string}
     * @private
     */
    this.geometryName_ = 'geometry';

    /**
     * User provided style.
     * @private
     * @type {import("./style/Style.js").default|Array<import("./style/Style.js").default>|import("./style/Style.js").StyleFunction}
     */
    this.style_ = null;

    /**
     * @private
     * @type {import("./style/Style.js").StyleFunction|undefined}
     */
    this.styleFunction_ = undefined;

    /**
     * @private
     * @type {?import("./events.js").EventsKey}
     */
    this.geometryChangeKey_ = null;

    listen(
      this, getChangeEventType(this.geometryName_),
      this.handleGeometryChanged_, this);

    if (opt_geometryOrProperties !== undefined) {
      if (opt_geometryOrProperties instanceof Geometry ||
          !opt_geometryOrProperties) {

        const geometry = /** @type {?Geometry} */ (opt_geometryOrProperties);
        this.setGeometry(geometry);
      } else {
        /** @type {Object<string, *>} */
        const properties = opt_geometryOrProperties;
        this.setProperties(properties);
      }
    }
  }

  /**
   * Clone this feature. If the original feature has a geometry it
   * is also cloned. The feature id is not set in the clone.
   * @return {Feature} The clone.
   * @api
   */
  clone() {
    const clone = new Feature(this.getProperties());
    clone.setGeometryName(this.getGeometryName());
    const geometry = this.getGeometry();
    if (geometry) {
      clone.setGeometry(geometry.clone());
    }
    const style = this.getStyle();
    if (style) {
      clone.setStyle(style);
    }
    return clone;
  }

  /**
   * Get the feature's default geometry.  A feature may have any number of named
   * geometries.  The "default" geometry (the one that is rendered by default) is
   * set when calling {@link module:ol/Feature~Feature#setGeometry}.
   * @return {Geometry|undefined} The default geometry for the feature.
   * @api
   * @observable
   */
  getGeometry() {
    return (
      /** @type {Geometry|undefined} */ (this.get(this.geometryName_))
    );
  }

  /**
   * Get the feature identifier.  This is a stable identifier for the feature and
   * is either set when reading data from a remote source or set explicitly by
   * calling {@link module:ol/Feature~Feature#setId}.
   * @return {number|string|undefined} Id.
   * @api
   */
  getId() {
    return this.id_;
  }

  /**
   * Get the name of the feature's default geometry.  By default, the default
   * geometry is named `geometry`.
   * @return {string} Get the property name associated with the default geometry
   *     for this feature.
   * @api
   */
  getGeometryName() {
    return this.geometryName_;
  }

  /**
   * Get the feature's style. Will return what was provided to the
   * {@link module:ol/Feature~Feature#setStyle} method.
   * @return {import("./style/Style.js").default|Array<import("./style/Style.js").default>|import("./style/Style.js").StyleFunction} The feature style.
   * @api
   */
  getStyle() {
    return this.style_;
  }

  /**
   * Get the feature's style function.
   * @return {import("./style/Style.js").StyleFunction|undefined} Return a function
   * representing the current style of this feature.
   * @api
   */
  getStyleFunction() {
    return this.styleFunction_;
  }

  /**
   * @private
   */
  handleGeometryChange_() {
    this.changed();
  }

  /**
   * @private
   */
  handleGeometryChanged_() {
    if (this.geometryChangeKey_) {
      unlistenByKey(this.geometryChangeKey_);
      this.geometryChangeKey_ = null;
    }
    const geometry = this.getGeometry();
    if (geometry) {
      this.geometryChangeKey_ = listen(geometry,
        EventType.CHANGE, this.handleGeometryChange_, this);
    }
    this.changed();
  }

  /**
   * Set the default geometry for the feature.  This will update the property
   * with the name returned by {@link module:ol/Feature~Feature#getGeometryName}.
   * @param {Geometry|undefined} geometry The new geometry.
   * @api
   * @observable
   */
  setGeometry(geometry) {
    this.set(this.geometryName_, geometry);
  }

  /**
   * Set the style for the feature.  This can be a single style object, an array
   * of styles, or a function that takes a resolution and returns an array of
   * styles. If it is `null` the feature has no style (a `null` style).
   * @param {import("./style/Style.js").default|Array<import("./style/Style.js").default>|import("./style/Style.js").StyleFunction} style Style for this feature.
   * @api
   * @fires module:ol/events/Event~Event#event:change
   */
  setStyle(style) {
    this.style_ = style;
    this.styleFunction_ = !style ? undefined : createStyleFunction(style);
    this.changed();
  }

  /**
   * Set the feature id.  The feature id is considered stable and may be used when
   * requesting features or comparing identifiers returned from a remote source.
   * The feature id can be used with the
   * {@link module:ol/source/Vector~VectorSource#getFeatureById} method.
   * @param {number|string|undefined} id The feature id.
   * @api
   * @fires module:ol/events/Event~Event#event:change
   */
  setId(id) {
    this.id_ = id;
    this.changed();
  }

  /**
   * Set the property name to be used when getting the feature's default geometry.
   * When calling {@link module:ol/Feature~Feature#getGeometry}, the value of the property with
   * this name will be returned.
   * @param {string} name The property name of the default geometry.
   * @api
   */
  setGeometryName(name) {
    unlisten(
      this, getChangeEventType(this.geometryName_),
      this.handleGeometryChanged_, this);
    this.geometryName_ = name;
    listen(
      this, getChangeEventType(this.geometryName_),
      this.handleGeometryChanged_, this);
    this.handleGeometryChanged_();
  }
}

/**
 * Convert the provided object into a feature style function.  Functions passed
 * through unchanged.  Arrays of import("./style/Style.js").default or single style objects wrapped
 * in a new feature style function.
 * @param {import("./style/Style.js").StyleFunction|!Array<import("./style/Style.js").default>|!import("./style/Style.js").default} obj
 *     A feature style function, a single style, or an array of styles.
 * @return {import("./style/Style.js").StyleFunction} A style function.
 */
export function createStyleFunction(obj) {
  if (typeof obj === 'function') {
    return obj;
  } else {
    /**
     * @type {Array<import("./style/Style.js").default>}
     */
    let styles;
    if (Array.isArray(obj)) {
      styles = obj;
    } else {
      assert(obj instanceof Style,
        41); // Expected an `import("./style/Style.js").Style` or an array of `import("./style/Style.js").Style`
      styles = [obj];
    }
    return function() {
      return styles;
    };
  }
}
export default Feature;
<commit_msg>Use Style instead of import(./style/Style.js).default<commit_after>/**
 * @module ol/Feature
 */
import {assert} from './asserts.js';
import {listen, unlisten, unlistenByKey} from './events.js';
import EventType from './events/EventType.js';
import BaseObject, {getChangeEventType} from './Object.js';
import Geometry from './geom/Geometry.js';
import Style from './style/Style.js';

/**
 * @classdesc
 * A vector object for geographic features with a geometry and other
 * attribute properties, similar to the features in vector file formats like
 * GeoJSON.
 *
 * Features can be styled individually with `setStyle`; otherwise they use the
 * style of their vector layer.
 *
 * Note that attribute properties are set as {@link module:ol/Object} properties on
 * the feature object, so they are observable, and have get/set accessors.
 *
 * Typically, a feature has a single geometry property. You can set the
 * geometry using the `setGeometry` method and get it with `getGeometry`.
 * It is possible to store more than one geometry on a feature using attribute
 * properties. By default, the geometry used for rendering is identified by
 * the property name `geometry`. If you want to use another geometry property
 * for rendering, use the `setGeometryName` method to change the attribute
 * property associated with the geometry for the feature.  For example:
 *
 * ```js
 *
 * import Feature from 'ol/Feature';
 * import Polygon from 'ol/geom/Polygon';
 * import Point from 'ol/geom/Point';
 *
 * var feature = new Feature({
 *   geometry: new Polygon(polyCoords),
 *   labelPoint: new Point(labelCoords),
 *   name: 'My Polygon'
 * });
 *
 * // get the polygon geometry
 * var poly = feature.getGeometry();
 *
 * // Render the feature as a point using the coordinates from labelPoint
 * feature.setGeometryName('labelPoint');
 *
 * // get the point geometry
 * var point = feature.getGeometry();
 * ```
 *
 * @api
 */
class Feature extends BaseObject {
  /**
   * @param {Geometry|Object<string, *>=} opt_geometryOrProperties
   *     You may pass a Geometry object directly, or an object literal containing
   *     properties. If you pass an object literal, you may include a Geometry
   *     associated with a `geometry` key.
   */
  constructor(opt_geometryOrProperties) {

    super();

    /**
     * @private
     * @type {number|string|undefined}
     */
    this.id_ = undefined;

    /**
     * @type {string}
     * @private
     */
    this.geometryName_ = 'geometry';

    /**
     * User provided style.
     * @private
     * @type {Style|Array<Style>|import("./style/Style.js").StyleFunction}
     */
    this.style_ = null;

    /**
     * @private
     * @type {import("./style/Style.js").StyleFunction|undefined}
     */
    this.styleFunction_ = undefined;

    /**
     * @private
     * @type {?import("./events.js").EventsKey}
     */
    this.geometryChangeKey_ = null;

    listen(
      this, getChangeEventType(this.geometryName_),
      this.handleGeometryChanged_, this);

    if (opt_geometryOrProperties !== undefined) {
      if (opt_geometryOrProperties instanceof Geometry ||
          !opt_geometryOrProperties) {

        const geometry = /** @type {?Geometry} */ (opt_geometryOrProperties);
        this.setGeometry(geometry);
      } else {
        /** @type {Object<string, *>} */
        const properties = opt_geometryOrProperties;
        this.setProperties(properties);
      }
    }
  }

  /**
   * Clone this feature. If the original feature has a geometry it
   * is also cloned. The feature id is not set in the clone.
   * @return {Feature} The clone.
   * @api
   */
  clone() {
    const clone = new Feature(this.getProperties());
    clone.setGeometryName(this.getGeometryName());
    const geometry = this.getGeometry();
    if (geometry) {
      clone.setGeometry(geometry.clone());
    }
    const style = this.getStyle();
    if (style) {
      clone.setStyle(style);
    }
    return clone;
  }

  /**
   * Get the feature's default geometry.  A feature may have any number of named
   * geometries.  The "default" geometry (the one that is rendered by default) is
   * set when calling {@link module:ol/Feature~Feature#setGeometry}.
   * @return {Geometry|undefined} The default geometry for the feature.
   * @api
   * @observable
   */
  getGeometry() {
    return (
      /** @type {Geometry|undefined} */ (this.get(this.geometryName_))
    );
  }

  /**
   * Get the feature identifier.  This is a stable identifier for the feature and
   * is either set when reading data from a remote source or set explicitly by
   * calling {@link module:ol/Feature~Feature#setId}.
   * @return {number|string|undefined} Id.
   * @api
   */
  getId() {
    return this.id_;
  }

  /**
   * Get the name of the feature's default geometry.  By default, the default
   * geometry is named `geometry`.
   * @return {string} Get the property name associated with the default geometry
   *     for this feature.
   * @api
   */
  getGeometryName() {
    return this.geometryName_;
  }

  /**
   * Get the feature's style. Will return what was provided to the
   * {@link module:ol/Feature~Feature#setStyle} method.
   * @return {Style|Array<Style>|import("./style/Style.js").StyleFunction} The feature style.
   * @api
   */
  getStyle() {
    return this.style_;
  }

  /**
   * Get the feature's style function.
   * @return {import("./style/Style.js").StyleFunction|undefined} Return a function
   * representing the current style of this feature.
   * @api
   */
  getStyleFunction() {
    return this.styleFunction_;
  }

  /**
   * @private
   */
  handleGeometryChange_() {
    this.changed();
  }

  /**
   * @private
   */
  handleGeometryChanged_() {
    if (this.geometryChangeKey_) {
      unlistenByKey(this.geometryChangeKey_);
      this.geometryChangeKey_ = null;
    }
    const geometry = this.getGeometry();
    if (geometry) {
      this.geometryChangeKey_ = listen(geometry,
        EventType.CHANGE, this.handleGeometryChange_, this);
    }
    this.changed();
  }

  /**
   * Set the default geometry for the feature.  This will update the property
   * with the name returned by {@link module:ol/Feature~Feature#getGeometryName}.
   * @param {Geometry|undefined} geometry The new geometry.
   * @api
   * @observable
   */
  setGeometry(geometry) {
    this.set(this.geometryName_, geometry);
  }

  /**
   * Set the style for the feature.  This can be a single style object, an array
   * of styles, or a function that takes a resolution and returns an array of
   * styles. If it is `null` the feature has no style (a `null` style).
   * @param {Style|Array<Style>|import("./style/Style.js").StyleFunction} style Style for this feature.
   * @api
   * @fires module:ol/events/Event~Event#event:change
   */
  setStyle(style) {
    this.style_ = style;
    this.styleFunction_ = !style ? undefined : createStyleFunction(style);
    this.changed();
  }

  /**
   * Set the feature id.  The feature id is considered stable and may be used when
   * requesting features or comparing identifiers returned from a remote source.
   * The feature id can be used with the
   * {@link module:ol/source/Vector~VectorSource#getFeatureById} method.
   * @param {number|string|undefined} id The feature id.
   * @api
   * @fires module:ol/events/Event~Event#event:change
   */
  setId(id) {
    this.id_ = id;
    this.changed();
  }

  /**
   * Set the property name to be used when getting the feature's default geometry.
   * When calling {@link module:ol/Feature~Feature#getGeometry}, the value of the property with
   * this name will be returned.
   * @param {string} name The property name of the default geometry.
   * @api
   */
  setGeometryName(name) {
    unlisten(
      this, getChangeEventType(this.geometryName_),
      this.handleGeometryChanged_, this);
    this.geometryName_ = name;
    listen(
      this, getChangeEventType(this.geometryName_),
      this.handleGeometryChanged_, this);
    this.handleGeometryChanged_();
  }
}

/**
 * Convert the provided object into a feature style function.  Functions passed
 * through unchanged.  Arrays of Style or single style objects wrapped
 * in a new feature style function.
 * @param {import("./style/Style.js").StyleFunction|!Array<Style>|!Style} obj
 *     A feature style function, a single style, or an array of styles.
 * @return {import("./style/Style.js").StyleFunction} A style function.
 */
export function createStyleFunction(obj) {
  if (typeof obj === 'function') {
    return obj;
  } else {
    /**
     * @type {Array<Style>}
     */
    let styles;
    if (Array.isArray(obj)) {
      styles = obj;
    } else {
      assert(obj instanceof Style,
        41); // Expected an `import("./style/Style.js").Style` or an array of `import("./style/Style.js").Style`
      styles = [obj];
    }
    return function() {
      return styles;
    };
  }
}
export default Feature;
<|endoftext|>


<commit_before>describe("CharacterCount", function(); {
  describe("onReadyCharacterCount", function(); {
    var noCKEditor, changeEditorCount;

    beforeEach(function(); {
      changeEditorCount = spyOn(window, 'changeEditorCount').and.callThrough();
      noCKEditor = spyOn(window, 'noCKEditor').and.callThrough();
    });

    describe('noCKEditor', function(); {
      describe('home page', function(); {
        beforeEach(function(); {
          $('body').addClass('pages home');
          loadFixtures('character_count.html');

          onReadyCharacterCount();
          $('#moment_why').keyup();
        });

        it("noCKEditor to be called", function(); {
          expect(noCKEditor).toHaveBeenCalled();
        });
      });

      describe('character limit', function(); {
        function charLimit(val); {
          loadFixtures('character_count.html');
          var editor = $('#moment_why');
          editor.val(val);
          noCKEditor(editor);
        }

        it("submit button should not be disabled", function(); {
          charLimit('aaaaaaa');
          var input = $('input[type="submit"]');

          expect(input.attr('disabled')).toBeTrue();
        });

        it("submit button should not be disabled", function(); {
          var overLimit = new Array(2002).join('a');
          charLimit(overLimit);
          var input = $('input[type="submit"]');

          expect(input.attr('disabled')).toBeTrue();
        });
      });
    });
  });
});
<commit_msg>fix syntax and expect tests to get CharacterCount specs to run and pass (#570)<commit_after>describe("CharacterCount", function() {
  describe("onReadyCharacterCount", function() {
    var noCKEditor, changeEditorCount;

    beforeEach(function() {
      changeEditorCount = spyOn(window, 'changeEditorCount').and.callThrough();
      noCKEditor = spyOn(window, 'noCKEditor').and.callThrough();
    });

    describe('noCKEditor', function() {
      describe('home page', function() {
        beforeEach(function() {
          $('body').addClass('pages home');
          loadFixtures('character_count.html');

          onReadyCharacterCount();
          $('#moment_why').keyup();
        });

        it("noCKEditor to be called", function() {
          expect(noCKEditor).toHaveBeenCalled();
        });
      });

      describe('character limit', function() {
        function charLimit(val) {
          loadFixtures('character_count.html');
          var editor = $('#moment_why');
          editor.val(val);
          noCKEditor(editor);
        }

        it("submit button should not be disabled", function() {
          charLimit('aaaaaaa');
          var input = $('input[type="submit"]');

          expect(input.prop('disabled')).toBe(false);
        });

        it("submit button should be disabled", function() {
          var overLimit = new Array(2002).join('a');
          charLimit(overLimit);
          var input = $('input[type="submit"]');

          expect(input.prop('disabled')).toBe(true);
        });
      });
    });
  });
});
<|endoftext|>


<commit_before>/*
# Community Almanac - A place for your stories.
# Copyright (C) 2009  Douglas Mayle, Robert Marianski,
# Andy Cochran, Chris Patterson

# This file is part of Community Almanac.

# Community Almanac is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.

# Community Almanac is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with Community Almanac.  If not, see <http://www.gnu.org/licenses/>.
*/
$(document).ready(function() {

  // add the title to the submit button form
  $('#submit-button-form').submit(function() {
      var title = $('#page-title').val();
      var newinput = $('<input type="hidden" name="name" value="' + title + '" />');
      newinput.appendTo($(this));
      return true;
  });

  // on pages, have the add a comment link unhide the form
  $("#comment-form").hide();
  $("#comment-bttn").click(function() {
      $("#comment-form").slideDown("fast");
      $(this).slideUp("normal");
      return false;
      });
  // and have the comment link submit the form itself
  $('#comment-submit a.comment-link').click(function() {
    $('#comment-form').submit();
    return false;
  });

  // clicking on page title erases text already there
  $('#page-title').focus(function() {
      if ($(this).val() == "Page Name") {
        $(this).val("");
      }
  }).blur(function() {
      if ($(this).val() == "") {
        $(this).val("Page Name");
      }
  });

  $('ul.page-media-items').sortable({
    update: function(event, ui) {
      ui.item.parent().children().each(function(index) {
        if (this == ui.item.get(0)) {
          var content = $(this).find('div.mediacontent').get(0);
          $.post('/sort', {id: content.id, index: index})
        }
      });
    },
    handle: 'div.media-tab'
  });
  var tools = [{
      link: $('#text-tool'),
      submitfn: submit_handler
    }, {
      link: $('#map-tool'),
      attach_form_behaviors: map_behaviors,
      submitfn: submit_handler
    }
  ];

  for (var i = 0; i < tools.length; i++) {
      var fn = function(i) {
        var tool = tools[i];
        var link = tool.link;
        var submitfn = tool.submitfn;
        var attach_form_behaviors = tool.attach_form_behaviors;

        // when somebody tries to add a particular media type, fetch the form from
        // the server, and attach the behavior to it
        link.click(function(e) {
          e.preventDefault();
          var url = $(this).attr('href');

          var formcontainer = $('#form-container');
          $.get(url, null, function(data) {
            formcontainer.empty();
            formcontainer.show();
            $(data).appendTo(formcontainer).hide().fadeIn('fast', function() {
              $(this).find('textarea').focus();
            });
            $('form.media-item a.media-cancel').click(function(e) {
              e.preventDefault();
              formcontainer.fadeOut('normal', function() { $(this).empty(); });
            });
            $('form.media-item').submit(submitfn);
            // attach custom behaviors if needed
            if (attach_form_behaviors) {
              attach_form_behaviors($(this));
            }
          });
        });
      }
      fn(i);
    }
});

function submit_handler(e) {
  e.preventDefault();
  var url = $(this).attr('action');
  var data = $('form.media-item').serialize();
  var formcontainer = $('#form-container');

  $.ajax({
    contentType: 'application/x-www-form-urlencoded',
    data: data,
    success: function(data, textStatus) {
      formcontainer.empty();
      var newLi = $('

');
      newLi.appendTo('ul.page-media-items');
      $('
' + data + '
').appendTo(newLi).hide().fadeIn('slow');
      },
    type: "POST",
    url: url
  });
};

function map_behaviors(formcontainer) {
  var featureLayer = new OpenLayers.Layer.Vector('feature');
  var onActivate = function() { featureLayer.destroyFeatures(); };
  var drawPoint = new OpenLayers.Control.DrawFeature(
    featureLayer, OpenLayers.Handler.Point,
    {'displayClass': 'olControlDrawFeaturePoint',
     'eventListeners': {'activate': onActivate}});
  var drawPath = new OpenLayers.Control.DrawFeature(
    featureLayer, OpenLayers.Handler.Path,
    {'displayClass': 'olControlDrawFeaturePath',
     'eventListeners': {'activate': onActivate}});
  var drawPolygon = new OpenLayers.Control.DrawFeature(
    featureLayer, OpenLayers.Handler.Polygon,
    {'displayClass': 'olControlDrawFeaturePolygon',
     'eventListeners': {'activate': onActivate}});
  var deactivateAllEditingControls = function() {
    drawPoint.deactivate();
    drawPath.deactivate();
    drawPolygon.deactivate();
  };
  var featureAdded = function(evt) {
    deactivateAllEditingControls();
    var formatter = new OpenLayers.Format.GeoJSON();
    var str = formatter.write(evt.feature.geometry);
    $('#feature-geometry').val(str);
  };
  featureLayer.events.on({featureadded: featureAdded});
  var panelControls = [
   new OpenLayers.Control.Navigation({zoomWheelEnabled: false}),
   new OpenLayers.Control.PanZoom(),
   drawPoint,
   drawPath,
   drawPolygon
  ];
  var toolbar = new OpenLayers.Control.Panel({
     displayClass: 'olControlEditingToolbar',
     defaultControl: panelControls[0]
  });
  toolbar.addControls(panelControls);

  map = new OpenLayers.Map('map', {
    projection: new OpenLayers.Projection('EPSG:900913'),
    displayProjection: new OpenLayers.Projection('EPSG:4326'),
    maxExtent: new OpenLayers.Bounds(-14323800, 2299000, -7376800, 7191400),
    });
  var baseLayer = new OpenLayers.Layer.Google('streets', {sphericalMercator: true, type: G_PHYSICAL_MAP});
  map.addLayer(baseLayer);
  map.addControl(toolbar);
  map.addLayer(featureLayer);
  var almanac_center_url = $('.almanac-center-url').attr('href');
  $.getJSON(almanac_center_url, {}, function(data) {
    var lng = data.lng;
    var lat = data.lat;
    var center = new OpenLayers.LonLat(lng, lat);
    center.transform(new OpenLayers.Projection('EPSG:4326'), map.getProjectionObject());
    map.setCenter(center, 12);
  });
}
<commit_msg>format javascript to use two spaces<commit_after>/*
# Community Almanac - A place for your stories.
# Copyright (C) 2009  Douglas Mayle, Robert Marianski,
# Andy Cochran, Chris Patterson

# This file is part of Community Almanac.

# Community Almanac is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.

# Community Almanac is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Affero General Public License for more details.

# You should have received a copy of the GNU Affero General Public License
# along with Community Almanac.  If not, see <http://www.gnu.org/licenses/>.
*/
$(document).ready(function() {

  // add the title to the submit button form
  $('#submit-button-form').submit(function() {
    var title = $('#page-title').val();
    var newinput = $('<input type="hidden" name="name" value="' + title + '" />');
    newinput.appendTo($(this));
    return true;
  });

  // on pages, have the add a comment link unhide the form
  $("#comment-form").hide();
  $("#comment-bttn").click(function() {
    $("#comment-form").slideDown("fast");
    $(this).slideUp("normal");
    return false;
    });
  // and have the comment link submit the form itself
  $('#comment-submit a.comment-link').click(function() {
    $('#comment-form').submit();
    return false;
  });

  // clicking on page title erases text already there
  $('#page-title').focus(function() {
    if ($(this).val() == "Page Name") {
      $(this).val("");
    }
  }).blur(function() {
    if ($(this).val() == "") {
      $(this).val("Page Name");
    }
  });

  $('ul.page-media-items').sortable({
    update: function(event, ui) {
      ui.item.parent().children().each(function(index) {
        if (this == ui.item.get(0)) {
          var content = $(this).find('div.mediacontent').get(0);
          $.post('/sort', {id: content.id, index: index})
        }
      });
    },
    handle: 'div.media-tab'
  });
  var tools = [{
      link: $('#text-tool'),
      submitfn: submit_handler
    }, {
      link: $('#map-tool'),
      attach_form_behaviors: map_behaviors,
      submitfn: submit_handler
    }
  ];

  for (var i = 0; i < tools.length; i++) {
    var fn = function(i) {
      var tool = tools[i];
      var link = tool.link;
      var submitfn = tool.submitfn;
      var attach_form_behaviors = tool.attach_form_behaviors;

      // when somebody tries to add a particular media type, fetch the form from
      // the server, and attach the behavior to it
      link.click(function(e) {
        e.preventDefault();
        var url = $(this).attr('href');

        var formcontainer = $('#form-container');
        $.get(url, null, function(data) {
          formcontainer.empty();
          formcontainer.show();
          $(data).appendTo(formcontainer).hide().fadeIn('fast', function() {
            $(this).find('textarea').focus();
          });
          $('form.media-item a.media-cancel').click(function(e) {
            e.preventDefault();
            formcontainer.fadeOut('normal', function() { $(this).empty(); });
          });
          $('form.media-item').submit(submitfn);
          // attach custom behaviors if needed
          if (attach_form_behaviors) {
            attach_form_behaviors($(this));
          }
        });
      });
    }
    fn(i);
    }
});

function submit_handler(e) {
  e.preventDefault();
  var url = $(this).attr('action');
  var data = $('form.media-item').serialize();
  var formcontainer = $('#form-container');

  $.ajax({
    contentType: 'application/x-www-form-urlencoded',
    data: data,
    success: function(data, textStatus) {
      formcontainer.empty();
      var newLi = $('

');
      newLi.appendTo('ul.page-media-items');
      $('
' + data + '
').appendTo(newLi).hide().fadeIn('slow');
      },
    type: "POST",
    url: url
  });
};

function map_behaviors(formcontainer) {
  var featureLayer = new OpenLayers.Layer.Vector('feature');
  var onActivate = function() { featureLayer.destroyFeatures(); };
  var drawPoint = new OpenLayers.Control.DrawFeature(
    featureLayer, OpenLayers.Handler.Point,
    {'displayClass': 'olControlDrawFeaturePoint',
     'eventListeners': {'activate': onActivate}});
  var drawPath = new OpenLayers.Control.DrawFeature(
    featureLayer, OpenLayers.Handler.Path,
    {'displayClass': 'olControlDrawFeaturePath',
     'eventListeners': {'activate': onActivate}});
  var drawPolygon = new OpenLayers.Control.DrawFeature(
    featureLayer, OpenLayers.Handler.Polygon,
    {'displayClass': 'olControlDrawFeaturePolygon',
     'eventListeners': {'activate': onActivate}});
  var deactivateAllEditingControls = function() {
    drawPoint.deactivate();
    drawPath.deactivate();
    drawPolygon.deactivate();
  };
  var featureAdded = function(evt) {
    deactivateAllEditingControls();
    var formatter = new OpenLayers.Format.GeoJSON();
    var str = formatter.write(evt.feature.geometry);
    $('#feature-geometry').val(str);
  };
  featureLayer.events.on({featureadded: featureAdded});
  var panelControls = [
   new OpenLayers.Control.Navigation({zoomWheelEnabled: false}),
   new OpenLayers.Control.PanZoom(),
   drawPoint,
   drawPath,
   drawPolygon
  ];
  var toolbar = new OpenLayers.Control.Panel({
     displayClass: 'olControlEditingToolbar',
     defaultControl: panelControls[0]
  });
  toolbar.addControls(panelControls);

  map = new OpenLayers.Map('map', {
    projection: new OpenLayers.Projection('EPSG:900913'),
    displayProjection: new OpenLayers.Projection('EPSG:4326'),
    maxExtent: new OpenLayers.Bounds(-14323800, 2299000, -7376800, 7191400),
    });
  var baseLayer = new OpenLayers.Layer.Google('streets', {sphericalMercator: true, type: G_PHYSICAL_MAP});
  map.addLayer(baseLayer);
  map.addControl(toolbar);
  map.addLayer(featureLayer);
  var almanac_center_url = $('.almanac-center-url').attr('href');
  $.getJSON(almanac_center_url, {}, function(data) {
    var lng = data.lng;
    var lat = data.lat;
    var center = new OpenLayers.LonLat(lng, lat);
    center.transform(new OpenLayers.Projection('EPSG:4326'), map.getProjectionObject());
    map.setCenter(center, 12);
  });
}
<|endoftext|>


<commit_before>var WebFontConfig = {
    google: {
      families: ['Sniglet','Bungee Inline', 'Orbitron', 'Monoton', 'Press Start 2P']
    }
};

var loadState = {
  preload: function() {
    game.load.script("webfont", "//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js");
    game.load.image('sky', 'assets/night-sky.png');
    game.load.image('mike', 'assets/hungry-mike-small.png');
    game.load.image('baddie', 'assets/bad-broccoli-small.png');
    game.load.image('bagel', 'assets/bagel-small.png');
    game.load.image('weapoon', 'assets/knife.png');
    game.load.image('baddieWeapoon', 'assets/flame.png');
    game.load.image('player', 'assets/rocket.png');
  },

  create: function() {
    game.state.start('start');
  }
}
<commit_msg>Update the players pic<commit_after>var WebFontConfig = {
    google: {
      families: ['Sniglet','Bungee Inline', 'Orbitron', 'Monoton', 'Press Start 2P']
    }
};

var loadState = {
  preload: function() {
    game.load.script("webfont", "//ajax.googleapis.com/ajax/libs/webfont/1.4.7/webfont.js");
    game.load.image('sky', 'assets/night-sky.png');
    game.load.image('mike', 'assets/hungry-mike-small.png');
    game.load.image('baddie', 'assets/bad-broccoli-small.png');
    game.load.image('bagel', 'assets/bagel-small.png');
    game.load.image('weapoon', 'assets/knife.png');
    game.load.image('baddieWeapoon', 'assets/flame.png');
    game.load.image('player', '../assets/marissa.png');
  },

  create: function() {
    game.state.start('start');
  }
}
<|endoftext|>


<commit_before>describe("Marker", function () {
	var map,
	    container,
	    icon1,
	    icon2;

	beforeEach(function () {
		container = container = createContainer();
		map = L.map(container);

		map.setView([0, 0], 0);
		icon1 = new L.Icon.Default();
		icon2 = new L.Icon.Default({
			iconUrl: icon1.options.iconUrl + '?2',
			shadowUrl: icon1.options.shadowUrl + '?2'
		});
	});

	afterEach(function () {
		removeMapContainer(map, container);
	});

	describe("#setIcon", function () {

		it("set the correct x and y size attributes", function () {
			var expectedX = 96;
			var expectedY = 100;
			var sizedIcon = new L.Icon.Default({
				iconUrl: icon1.options.iconUrl + '?3',
				iconSize: [expectedX, expectedY]
			});

			var marker = L.marker([0, 0], {icon: sizedIcon});
			map.addLayer(marker);

			var icon = marker._icon;

			expect(icon.style.width).to.be(expectedX + 'px');
			expect(icon.style.height).to.be(expectedY + 'px');
		});

		it("set the correct x and y size attributes passing only one value", function () {
			var expectedXY = 96;
			var sizedIcon = new L.Icon.Default({
				iconUrl: icon1.options.iconUrl + '?3',
				iconSize: expectedXY
			});

			var marker = L.marker([0, 0], {icon: sizedIcon});
			map.addLayer(marker);

			var icon = marker._icon;

			expect(icon.style.width).to.be(expectedXY + 'px');
			expect(icon.style.height).to.be(expectedXY + 'px');
		});

		it("set the correct x and y size attributes passing a L.Point instance", function () {
			var expectedXY = 96;
			var sizedIcon = new L.Icon.Default({
				iconUrl: icon1.options.iconUrl + '?3',
				iconSize: L.point(expectedXY, expectedXY)
			});

			var marker = L.marker([0, 0], {icon: sizedIcon});
			map.addLayer(marker);

			var icon = marker._icon;

			expect(icon.style.width).to.be(expectedXY + 'px');
			expect(icon.style.height).to.be(expectedXY + 'px');
		});

		it("changes the icon to another image while re-using the IMG element", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);

			var beforeIcon = marker._icon;
			marker.setIcon(icon2);
			var afterIcon = marker._icon;

			expect(beforeIcon).to.be(afterIcon); // Check that the  element is re-used
			expect(afterIcon.src).to.contain(icon2._getIconUrl('icon'));
		});

		it("preserves draggability", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);

			marker.dragging.disable();
			marker.setIcon(icon2);

			expect(marker.dragging.enabled()).to.be(false);

			marker.dragging.enable();

			marker.setIcon(icon1);

			expect(marker.dragging.enabled()).to.be(true);

			map.removeLayer(marker);
			map.addLayer(marker);

			expect(marker.dragging.enabled()).to.be(true);

			map.removeLayer(marker);
			// Dragging is still enabled, we should be able to disable it,
			// even if marker is off the map.
			expect(marker.dragging).to.be(undefined);
			marker.options.draggable = false;
			map.addLayer(marker);

			map.removeLayer(marker);

			// We should also be able to enable dragging while off the map
			expect(marker.dragging).to.be(undefined);
			marker.options.draggable = true;

			map.addLayer(marker);
			expect(marker.dragging.enabled()).to.be(true);
		});

		it("changes the DivIcon to another DivIcon, while re-using the DIV element", function () {
			var marker = L.marker([0, 0], {icon: L.divIcon({html: 'Inner1Text'})});
			map.addLayer(marker);

			var beforeIcon = marker._icon;
			marker.setIcon(L.divIcon({html: 'Inner2Text'}));
			var afterIcon = marker._icon;

			expect(beforeIcon).to.be(afterIcon); // Check that the
 element is re-used
			expect(afterIcon.innerHTML).to.contain('Inner2Text');
		});

		it("removes text when changing to a blank DivIcon", function () {
			var marker = L.marker([0, 0], {icon: L.divIcon({html: 'Inner1Text'})});
			map.addLayer(marker);

			marker.setIcon(L.divIcon());

			expect(marker._icon.innerHTML).to.not.contain('Inner1Text');
		});

		it("changes a DivIcon to an image", function () {
			var marker = L.marker([0, 0], {icon: L.divIcon({html: 'Inner1Text'})});
			map.addLayer(marker);
			var oldIcon = marker._icon;

			marker.setIcon(icon1);

			expect(oldIcon).to.not.be(marker._icon); // Check that the _icon is NOT re-used
			expect(oldIcon.parentNode).to.be(null);

			if (L.Browser.retina) {
				expect(marker._icon.src).to.contain('marker-icon-2x.png');
			} else {
				expect(marker._icon.src).to.contain('marker-icon.png');
			}
			expect(marker._icon.parentNode).to.be(map._panes.markerPane);
		});

		it("changes an image to a DivIcon", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);
			var oldIcon = marker._icon;

			marker.setIcon(L.divIcon({html: 'Inner1Text'}));

			expect(oldIcon).to.not.be(marker._icon); // Check that the _icon is NOT re-used
			expect(oldIcon.parentNode).to.be(null);

			expect(marker._icon.innerHTML).to.contain('Inner1Text');
			expect(marker._icon.parentNode).to.be(map._panes.markerPane);
		});

		it("reuses the icon/shadow when changing icon", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);
			var oldIcon = marker._icon;
			var oldShadow = marker._shadow;

			marker.setIcon(icon2);

			expect(oldIcon).to.be(marker._icon);
			expect(oldShadow).to.be(marker._shadow);

			expect(marker._icon.parentNode).to.be(map._panes.markerPane);
			expect(marker._shadow.parentNode).to.be(map._panes.shadowPane);
		});

		it("sets the alt attribute to a default value when no alt text is passed", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);
			var icon = marker._icon;
			expect(icon.hasAttribute('alt')).to.be(true);
			expect(icon.alt).to.be('Marker');
		});

		it("doesn't set the alt attribute for DivIcons", function () {
			var marker = L.marker([0, 0], {icon: L.divIcon(), alt: 'test'});
			map.addLayer(marker);
			var icon = marker._icon;
			expect(icon.hasAttribute('alt')).to.be(false);
		});

		it("pan map to focus marker", function (done) {
			var marker = L.marker([70, 0], {icon: L.divIcon()});
			map.addLayer(marker);

			setTimeout(function () {
				expect(function () {
					marker._icon.focus();
				}).to.not.throwException();

				done();
			}, 100);
		});

		it("pan map to focus marker with no iconSize", function (done) {
			var marker = L.marker([70, 0], {icon: L.divIcon({iconSize: null})});
			map.addLayer(marker);

			setTimeout(function () {
				expect(function () {
					marker._panOnFocus();
				}).to.not.throwException();

				done();
			}, 100);
		});
	});

	describe("#setLatLng", function () {
		it("fires a move event", function () {

			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);

			var beforeLatLng = marker._latlng;
			var afterLatLng = new L.LatLng(1, 2);

			var eventArgs = null;
			marker.on('move', function (e) {
				eventArgs = e;
			});

			marker.setLatLng(afterLatLng);

			expect(eventArgs).to.not.be(null);
			expect(eventArgs.oldLatLng).to.be(beforeLatLng);
			expect(eventArgs.latlng).to.be(afterLatLng);
			expect(marker.getLatLng()).to.be(afterLatLng);
		});
	});

	describe('events', function () {
		it('fires click event when clicked', function () {
			var spy = sinon.spy();

			var marker = L.marker([0, 0]).addTo(map);

			marker.on('click', spy);
			happen.click(marker._icon);

			expect(spy.called).to.be.ok();
		});

		it('fires click event when clicked with DivIcon', function () {
			var spy = sinon.spy();

			var marker = L.marker([0, 0], {icon: L.divIcon()}).addTo(map);

			marker.on('click', spy);
			happen.click(marker._icon);

			expect(spy.called).to.be.ok();
		});

		it('fires click event when clicked on DivIcon child element', function () {
			var spy = sinon.spy();

			var marker = L.marker([0, 0], {icon: L.divIcon({html: ''})}).addTo(map);

			marker.on('click', spy);

			happen.click(marker._icon);
			expect(spy.called).to.be.ok();

			happen.click(marker._icon.querySelector('img'));
			expect(spy.calledTwice).to.be.ok();
		});

		it('fires click event when clicked on DivIcon child element set using setIcon', function () {
			var spy = sinon.spy();

			var marker = L.marker([0, 0]).addTo(map);
			marker.setIcon(L.divIcon({html: ''}));

			marker.on('click', spy);

			happen.click(marker._icon);
			expect(spy.called).to.be.ok();

			happen.click(marker._icon.querySelector('img'));
			expect(spy.calledTwice).to.be.ok();
		});

		it("do not propagate click event", function () {
			var spy = sinon.spy();
			var spy2 = sinon.spy();
			var mapSpy = sinon.spy();
			var marker = L.marker([55.8, 37.6]);
			map.addLayer(marker);
			marker.on('click', spy);
			marker.on('click', spy2);
			map.on('click', mapSpy);
			happen.click(marker._icon);
			expect(spy.called).to.be.ok();
			expect(spy2.called).to.be.ok();
			expect(mapSpy.called).not.to.be.ok();
		});

		it("do not propagate dblclick event", function () {
			var spy = sinon.spy();
			var spy2 = sinon.spy();
			var mapSpy = sinon.spy();
			var marker = L.marker([55.8, 37.6]);
			map.addLayer(marker);
			marker.on('dblclick', spy);
			marker.on('dblclick', spy2);
			map.on('dblclick', mapSpy);
			happen.dblclick(marker._icon);
			expect(spy.called).to.be.ok();
			expect(spy2.called).to.be.ok();
			expect(mapSpy.called).not.to.be.ok();
		});

		it("do not catch event if it does not listen to it", function (done) {
			var marker = L.marker([55, 37]);
			map.addLayer(marker);
			marker.once('mousemove', function (e) {
				// It should be the marker coordinates
				expect(e.latlng.equals(marker.getLatLng())).to.be.equal(true);
			});
			happen.mousemove(marker._icon);

			map.once('mousemove', function (e) {
				// It should be the mouse coordinates, not the marker ones
				expect(e.latlng.equals(marker.getLatLng())).to.be.equal(false);
				done();
			});
			happen.mousemove(marker._icon);
		});
	});
});
<commit_msg>Speed up tests relating to focusing on Marker (#8545)<commit_after>describe("Marker", function () {
	var map,
	    container,
	    icon1,
	    icon2;

	beforeEach(function () {
		container = container = createContainer();
		map = L.map(container);

		map.setView([0, 0], 0);
		icon1 = new L.Icon.Default();
		icon2 = new L.Icon.Default({
			iconUrl: icon1.options.iconUrl + '?2',
			shadowUrl: icon1.options.shadowUrl + '?2'
		});
	});

	afterEach(function () {
		removeMapContainer(map, container);
	});

	describe("#setIcon", function () {

		it("set the correct x and y size attributes", function () {
			var expectedX = 96;
			var expectedY = 100;
			var sizedIcon = new L.Icon.Default({
				iconUrl: icon1.options.iconUrl + '?3',
				iconSize: [expectedX, expectedY]
			});

			var marker = L.marker([0, 0], {icon: sizedIcon});
			map.addLayer(marker);

			var icon = marker._icon;

			expect(icon.style.width).to.be(expectedX + 'px');
			expect(icon.style.height).to.be(expectedY + 'px');
		});

		it("set the correct x and y size attributes passing only one value", function () {
			var expectedXY = 96;
			var sizedIcon = new L.Icon.Default({
				iconUrl: icon1.options.iconUrl + '?3',
				iconSize: expectedXY
			});

			var marker = L.marker([0, 0], {icon: sizedIcon});
			map.addLayer(marker);

			var icon = marker._icon;

			expect(icon.style.width).to.be(expectedXY + 'px');
			expect(icon.style.height).to.be(expectedXY + 'px');
		});

		it("set the correct x and y size attributes passing a L.Point instance", function () {
			var expectedXY = 96;
			var sizedIcon = new L.Icon.Default({
				iconUrl: icon1.options.iconUrl + '?3',
				iconSize: L.point(expectedXY, expectedXY)
			});

			var marker = L.marker([0, 0], {icon: sizedIcon});
			map.addLayer(marker);

			var icon = marker._icon;

			expect(icon.style.width).to.be(expectedXY + 'px');
			expect(icon.style.height).to.be(expectedXY + 'px');
		});

		it("changes the icon to another image while re-using the IMG element", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);

			var beforeIcon = marker._icon;
			marker.setIcon(icon2);
			var afterIcon = marker._icon;

			expect(beforeIcon).to.be(afterIcon); // Check that the  element is re-used
			expect(afterIcon.src).to.contain(icon2._getIconUrl('icon'));
		});

		it("preserves draggability", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);

			marker.dragging.disable();
			marker.setIcon(icon2);

			expect(marker.dragging.enabled()).to.be(false);

			marker.dragging.enable();

			marker.setIcon(icon1);

			expect(marker.dragging.enabled()).to.be(true);

			map.removeLayer(marker);
			map.addLayer(marker);

			expect(marker.dragging.enabled()).to.be(true);

			map.removeLayer(marker);
			// Dragging is still enabled, we should be able to disable it,
			// even if marker is off the map.
			expect(marker.dragging).to.be(undefined);
			marker.options.draggable = false;
			map.addLayer(marker);

			map.removeLayer(marker);

			// We should also be able to enable dragging while off the map
			expect(marker.dragging).to.be(undefined);
			marker.options.draggable = true;

			map.addLayer(marker);
			expect(marker.dragging.enabled()).to.be(true);
		});

		it("changes the DivIcon to another DivIcon, while re-using the DIV element", function () {
			var marker = L.marker([0, 0], {icon: L.divIcon({html: 'Inner1Text'})});
			map.addLayer(marker);

			var beforeIcon = marker._icon;
			marker.setIcon(L.divIcon({html: 'Inner2Text'}));
			var afterIcon = marker._icon;

			expect(beforeIcon).to.be(afterIcon); // Check that the
 element is re-used
			expect(afterIcon.innerHTML).to.contain('Inner2Text');
		});

		it("removes text when changing to a blank DivIcon", function () {
			var marker = L.marker([0, 0], {icon: L.divIcon({html: 'Inner1Text'})});
			map.addLayer(marker);

			marker.setIcon(L.divIcon());

			expect(marker._icon.innerHTML).to.not.contain('Inner1Text');
		});

		it("changes a DivIcon to an image", function () {
			var marker = L.marker([0, 0], {icon: L.divIcon({html: 'Inner1Text'})});
			map.addLayer(marker);
			var oldIcon = marker._icon;

			marker.setIcon(icon1);

			expect(oldIcon).to.not.be(marker._icon); // Check that the _icon is NOT re-used
			expect(oldIcon.parentNode).to.be(null);

			if (L.Browser.retina) {
				expect(marker._icon.src).to.contain('marker-icon-2x.png');
			} else {
				expect(marker._icon.src).to.contain('marker-icon.png');
			}
			expect(marker._icon.parentNode).to.be(map._panes.markerPane);
		});

		it("changes an image to a DivIcon", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);
			var oldIcon = marker._icon;

			marker.setIcon(L.divIcon({html: 'Inner1Text'}));

			expect(oldIcon).to.not.be(marker._icon); // Check that the _icon is NOT re-used
			expect(oldIcon.parentNode).to.be(null);

			expect(marker._icon.innerHTML).to.contain('Inner1Text');
			expect(marker._icon.parentNode).to.be(map._panes.markerPane);
		});

		it("reuses the icon/shadow when changing icon", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);
			var oldIcon = marker._icon;
			var oldShadow = marker._shadow;

			marker.setIcon(icon2);

			expect(oldIcon).to.be(marker._icon);
			expect(oldShadow).to.be(marker._shadow);

			expect(marker._icon.parentNode).to.be(map._panes.markerPane);
			expect(marker._shadow.parentNode).to.be(map._panes.shadowPane);
		});

		it("sets the alt attribute to a default value when no alt text is passed", function () {
			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);
			var icon = marker._icon;
			expect(icon.hasAttribute('alt')).to.be(true);
			expect(icon.alt).to.be('Marker');
		});

		it("doesn't set the alt attribute for DivIcons", function () {
			var marker = L.marker([0, 0], {icon: L.divIcon(), alt: 'test'});
			map.addLayer(marker);
			var icon = marker._icon;
			expect(icon.hasAttribute('alt')).to.be(false);
		});

		it("pan map to focus marker", function () {
			var marker = L.marker([70, 0], {icon: L.divIcon()});
			map.addLayer(marker);

			expect(function () {
				marker._icon.focus();
			}).to.not.throwException();
		});

		it("pan map to focus marker with no iconSize", function () {
			var marker = L.marker([70, 0], {icon: L.divIcon({iconSize: null})});
			map.addLayer(marker);

			expect(function () {
				marker._panOnFocus();
			}).to.not.throwException();
		});
	});

	describe("#setLatLng", function () {
		it("fires a move event", function () {

			var marker = L.marker([0, 0], {icon: icon1});
			map.addLayer(marker);

			var beforeLatLng = marker._latlng;
			var afterLatLng = new L.LatLng(1, 2);

			var eventArgs = null;
			marker.on('move', function (e) {
				eventArgs = e;
			});

			marker.setLatLng(afterLatLng);

			expect(eventArgs).to.not.be(null);
			expect(eventArgs.oldLatLng).to.be(beforeLatLng);
			expect(eventArgs.latlng).to.be(afterLatLng);
			expect(marker.getLatLng()).to.be(afterLatLng);
		});
	});

	describe('events', function () {
		it('fires click event when clicked', function () {
			var spy = sinon.spy();

			var marker = L.marker([0, 0]).addTo(map);

			marker.on('click', spy);
			happen.click(marker._icon);

			expect(spy.called).to.be.ok();
		});

		it('fires click event when clicked with DivIcon', function () {
			var spy = sinon.spy();

			var marker = L.marker([0, 0], {icon: L.divIcon()}).addTo(map);

			marker.on('click', spy);
			happen.click(marker._icon);

			expect(spy.called).to.be.ok();
		});

		it('fires click event when clicked on DivIcon child element', function () {
			var spy = sinon.spy();

			var marker = L.marker([0, 0], {icon: L.divIcon({html: ''})}).addTo(map);

			marker.on('click', spy);

			happen.click(marker._icon);
			expect(spy.called).to.be.ok();

			happen.click(marker._icon.querySelector('img'));
			expect(spy.calledTwice).to.be.ok();
		});

		it('fires click event when clicked on DivIcon child element set using setIcon', function () {
			var spy = sinon.spy();

			var marker = L.marker([0, 0]).addTo(map);
			marker.setIcon(L.divIcon({html: ''}));

			marker.on('click', spy);

			happen.click(marker._icon);
			expect(spy.called).to.be.ok();

			happen.click(marker._icon.querySelector('img'));
			expect(spy.calledTwice).to.be.ok();
		});

		it("do not propagate click event", function () {
			var spy = sinon.spy();
			var spy2 = sinon.spy();
			var mapSpy = sinon.spy();
			var marker = L.marker([55.8, 37.6]);
			map.addLayer(marker);
			marker.on('click', spy);
			marker.on('click', spy2);
			map.on('click', mapSpy);
			happen.click(marker._icon);
			expect(spy.called).to.be.ok();
			expect(spy2.called).to.be.ok();
			expect(mapSpy.called).not.to.be.ok();
		});

		it("do not propagate dblclick event", function () {
			var spy = sinon.spy();
			var spy2 = sinon.spy();
			var mapSpy = sinon.spy();
			var marker = L.marker([55.8, 37.6]);
			map.addLayer(marker);
			marker.on('dblclick', spy);
			marker.on('dblclick', spy2);
			map.on('dblclick', mapSpy);
			happen.dblclick(marker._icon);
			expect(spy.called).to.be.ok();
			expect(spy2.called).to.be.ok();
			expect(mapSpy.called).not.to.be.ok();
		});

		it("do not catch event if it does not listen to it", function (done) {
			var marker = L.marker([55, 37]);
			map.addLayer(marker);
			marker.once('mousemove', function (e) {
				// It should be the marker coordinates
				expect(e.latlng.equals(marker.getLatLng())).to.be.equal(true);
			});
			happen.mousemove(marker._icon);

			map.once('mousemove', function (e) {
				// It should be the mouse coordinates, not the marker ones
				expect(e.latlng.equals(marker.getLatLng())).to.be.equal(false);
				done();
			});
			happen.mousemove(marker._icon);
		});
	});
});
<|endoftext|>


<commit_before>var assert = require('assert');

function isExpired(ticket) {
  var ungraceperiod = 60000;
  var compareDate = new Date();
  compareDate.setTime(compareDate.getTime() + ungraceperiod);
  return (new Date(ticket.refreshTokenExpiration)) < compareDate;
}

function generateCacheKey(claimtype, context) {
  var cmps;
  if (!process.env.mozuHosted) {
    assert(context.appKey, "No application key in context!");
    cmps = [context.appKey];
  } else {
    cmps = ['mozuHosted'];
  }
  switch(claimtype) {
    case "developer":
      assert(context.developerAccountId, "No developer account id in context!");
      assert(context.developerAccount && context.developerAccount.emailAddress, "No developer account email address in context!");
      cmps.push(context.developerAccountId, context.developerAccount.emailAddress);
      break;
    case "admin-user":
      assert(context.tenant, "No tenant in context!");
      assert(context.adminUser && context.adminUser.emailAddress, "No admin user email address in context!");
      cmps.push(context.tenant, context.adminUser.emailAddress);
      break;
    default:
      break;
  }
  return cmps.join();
}

var InMemoryAuthCache = module.exports = function InMemoryAuthCache() {
  var claimsCaches = {
    application: {},
    developer: {},
    'admin-user': {}
  };

  return {
    get: function(claimtype, context, callback) {
      var ticket = claimsCaches[claimtype][generateCacheKey(claimtype, context)];
      setImmediate(function() {
        callback(null, ticket && !isExpired(ticket) && ticket || undefined);
      });
    },
    set: function(claimtype, context, ticket, callback) {
      claimsCaches[claimtype][generateCacheKey(claimtype, context)] = ticket;
      setImmediate(callback);
    },
    constructor: InMemoryAuthCache
  }
};<commit_msg>removing developer account id check in auth cache<commit_after>var assert = require('assert');

function isExpired(ticket) {
  var ungraceperiod = 60000;
  var compareDate = new Date();
  compareDate.setTime(compareDate.getTime() + ungraceperiod);
  return (new Date(ticket.refreshTokenExpiration)) < compareDate;
}

function generateCacheKey(claimtype, context) {
  var cmps;
  if (!process.env.mozuHosted) {
    assert(context.appKey, "No application key in context!");
    cmps = [context.appKey];
  } else {
    cmps = ['mozuHosted'];
  }
  switch(claimtype) {
    case "developer":
      assert(context.developerAccount && context.developerAccount.emailAddress, "No developer account email address in context!");
      cmps.push(context.developerAccount.emailAddress, context.developerAccountId);
      break;
    case "admin-user":
      assert(context.tenant, "No tenant in context!");
      assert(context.adminUser && context.adminUser.emailAddress, "No admin user email address in context!");
      cmps.push(context.tenant, context.adminUser.emailAddress);
      break;
    default:
      break;
  }
  return cmps.join();
}

var InMemoryAuthCache = module.exports = function InMemoryAuthCache() {
  var claimsCaches = {
    application: {},
    developer: {},
    'admin-user': {}
  };

  return {
    get: function(claimtype, context, callback) {
      var ticket = claimsCaches[claimtype][generateCacheKey(claimtype, context)];
      setImmediate(function() {
        callback(null, ticket && !isExpired(ticket) && ticket || undefined);
      });
    },
    set: function(claimtype, context, ticket, callback) {
      claimsCaches[claimtype][generateCacheKey(claimtype, context)] = ticket;
      setImmediate(callback);
    },
    constructor: InMemoryAuthCache
  }
};<|endoftext|>