<commit_before>var fs = require('fs');
var util = require('util');
var request = require('request-json');
var winston = require('winston');
var config = require('../config');
var manifest = require('./manifest');
var csCore = require('./csCore.js');
var sendEmail = require('./sendEmail');
var crypto = require('cryptofunc');
var selectUtils = require('selectUtils');

///////////////////////////////////////////////////////////////////////////////////////
// State

var otp_store = {};
var mail_timestamp = {};

var electionID = manifest.hash;
var listOfEligibleVoters = manifest.voters.map(function(k){ return k.email; });
var colSerSigKey = config.signing_key;
var cs = csCore.create(electionID, listOfEligibleVoters, colSerSigKey);

//  status and opening/closing time
var resultReady = fs.existsSync(config.RESULT_FILE);

// If there is no result yet, but there are some accepted
// ballots, we need to read these ballots to resume the operation:
if (!resultReady && fs.existsSync(config.ACCEPTED_BALLOTS_LOG_FILE)) {
    console.log('Resuming (reading already accepted ballots)');
    var log_data = fs.readFileSync(config.ACCEPTED_BALLOTS_LOG_FILE, {flags:'r', encoding:'utf8'});
    log_data.split('\n').forEach(function (entry) { // for each entry in the log file
        if (entry==='') return;
        entry = JSON.parse(entry);
        console.log('  - processing a ballot of', entry.email);
        cs.collectBallot(entry.email, entry.ballot);
    });
}

// Status
function Status() {
    var _open = false;
    var _closed = false;

    return {
        isOpen : function () {
            return _open;
        },
        isClosed : function () {
            return _closed;
        },
        isActive : function () {
            return _open && !_closed;
        },
        status : function () {
            if (_closed) return 'closed'
            else if (_open) return 'open'
            else return 'not open yet';
        },
        open : function () {
            if (!_closed) _open = true;
        },
        close : function () {
            _closed = true;
        }
    }
}
var status = Status();

// Time
var startTime = new Date(manifest.startTime);
var endTime = new Date(manifest.endTime);
var now = new Date();
var timeToOpen = startTime - now;
var timeToClose = endTime - now;
console.log('Time to open:  %s (%s)', selectUtils.timeDelta2String(timeToOpen), startTime);
console.log('Time to close: %s (%s)', selectUtils.timeDelta2String(timeToClose), endTime);

// DETERMINING THE INITIAL STATUS

// if the result is ready (saved in the file), set the status to
// closed (no further ballots are accepted; the result will not be
// sent)
if (resultReady) {
    console.log('Result already exits. Election status set to closed');
    status.close();
}
// if the result is not ready yet, but the time is over, close
// election (this will save and send the result, and set the
// status to closed):
else if (timeToClose <= 0) {
    closeElection();
}
// otherwise (the result is not ready, time is not over) set the
// timeouts for the opening and closing:
else {
    if (timeToClose > 2147483647) { // it is too much for node (v8); the event would fire immediately
        console.log('WARNING: the closing time is too far in the future.');
        console.log('         The election will not close automatically.');
        // FIXME This is clearly a temporary solution. We should handle this limitation.
    }
    else {
        setTimeout(closeElection, timeToClose);
    }

    if (timeToOpen > 2147483647) { // again, too much for v8
        console.log('WARNING: the opening time is too far in the future.');
        console.log('         The election will not open automatically.');
        // FIXME As above.
    }
    else {
        setTimeout(function() {
            status.open();
            winston.info('OPENING ELECTION.');
        }, timeToOpen); // fires right away (in the next click), if timeToOpen <= 0;
    }
}

var log = fs.createWriteStream(config.ACCEPTED_BALLOTS_LOG_FILE, {flags:'a', encoding:'utf8'});

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE otp
//

exports.otp = function otp(req, res)
{
    var email = req.body.email;

    if (!status.isActive()) { // if the status is not active, reject the request
        var descr = (status.isClosed() ? 'Election closed' : 'Election not opened yet');
        winston.info('OTP request (%s) ERROR: %s.', email, descr)
        res.send({ ok: false, descr: descr });
        return;
    }

    if (email) {
        if (!cs.eligibleVoters[email]) // not eligible voter
        {
            winston.info('OTP request (%s) ERROR: Voter not eligible', email);
            res.send({ ok: false, descr: 'Invalid voter identifier (e-mail)' });
        }
        else // voter is eligible; create a fresh OTP and send it
        {
            // Generate OTP (if not generated yet):
        	if (otp_store[email]==null) { // OTP not generated yet (for the given voter)
        		// generate store the OTP under the voter id (email)
        		otp_store[email] = crypto.nonce().slice(0,10); // an OTP will have 5 bytes
        		winston.info('OTP request (%s) accepted. Fresh OTP = %s', email, otp_store[email]);
        	}
        	else // OTP already generated
        		winston.info('OTP request (%s) accepted. Already stored OTP = %s', email, otp_store[email]);

            // Send email:
            if (config.sendEmail) {
                var now = new Date();
                var sentRecently = mail_timestamp[email] != null
                                   && (now - mail_timestamp[email]) < config.timespanEmail*60000; // timespan is specified in minutes (in the config file)
                if (!sentRecently) {
                    // Sent an e-mail with the OTP
                    winston.info('Sending an email to \'%s\' with OTP  ', email, otp_store[email]);
                    var emailContent = 'Election: ' + manifest.title + '\n\nOne time password: ' + otp_store[email] + '\n';
                    sendEmail(email, 'Your One Time Password for sElect', emailContent, function (err,info) {
                        if (err) {
                            winston.info(' ...Error:', err);
                            // TODO: what to do if we are here (the e-mail has not been sent)?
                            res.send({ ok: false, descr: 'Problems in sending the E-mail.' });
                        } else {
                            winston.info(' ...E-mail sent: ' + info.response);
                            mail_timestamp[email] = new Date(); // now
                            res.send({ ok: true });
                        }
                    });
                }
                else { // otp was sent recently
                    winston.info('E-mail to \'%s\' was sent recently (not sent this time).', email );
                    res.send({ ok: true });
                }
            }
            else { // ! config.sentEmail
                res.send({ ok: true });
            }
        }
    }
    else {
        res.send({ ok: false, descr: 'Empty e-mail address' });
    }
};

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE cast
//

exports.cast = function cast(req, res)
{
    var email = req.body.email;
    var otp = req.body.otp;
    var ballot = req.body.ballot;

    // make sure that we have all the pieces:
    if (!email || !otp || !ballot ) {
        winston.info('Cast request (%s) ERROR: Invalid request', email)
        res.send({ ok: false, descr: 'Invalid request' });
        return;
    }

    // is the server active?
    if (!status.isActive()) {
        var descr = (status.isClosed() ? 'Election closed' : 'Election not opened yet');
        winston.info('Cast request (%s) ERROR: %s.', email, descr)
        res.send({ ok: false, descr: descr });
        return;
    }

    // Check the OTP (and, implicitly, the identifier)
    if (otp_store[email] === otp) {
        // Cast the ballot:
    	var response = cs.collectBallot(email, ballot);
//    	if (err) {
//    		winston.info('Cast request (%s/%s) INTERNAL ERROR %s', email, otp, err.toString());
//    		res.send({ ok: false, descr: 'Internal error' });
//    	}
    	if (!response.ok) {
    		winston.info('Cast request (%s/%s) BALLOT REJECTED. Response = %s', email, otp, response.data);
    		res.send({ ok: false, descr: response.data });
    	}
    	else { // everything ok
    		winston.info('Cast request (%s/%s) accepted', email, otp);
    		res.send({ ok: true, receipt: response.data });
    		// log the accepted ballot
    		log.write(JSON.stringify({ email:email, ballot:ballot })+'\n', null,
    				function whenFlushed(e,r) {
                    winston.info('Ballot for %s logged', email);
    		});
    		// TODO: how to make sure that this stream is flushed right away
    	}
    }
    else // OTP not correct
    {
        winston.info('Cast request ERROR: Invalid OTP:', otp);
        res.send({ ok: false, descr: 'Invalid OTP (one time password)' });
    }
};

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE info
//
exports.info = function info(req, res)  {
    var now = new Date();
    var timeToOpen  = selectUtils.timeDelta2String(startTime - now);
    var timeToClose = selectUtils.timeDelta2String(endTime - now);
    var openingTime = util.format('Time to open:  %s (%s)', selectUtils.timeDelta2String(timeToOpen), startTime);
    var closingTime =     util.format('Time to close: %s (%s)', selectUtils.timeDelta2String(timeToClose), endTime);

    res.send({
        electionID : manifest.hash,
        status: status.status(),
        title: manifest.title,
        timeToOpen: timeToOpen,
        timeToClose: timeToClose,
    });
}

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE controlPanel
//
exports.controlPanel = function info(req, res)  {
    var now = new Date();
    var timeToOpen = startTime - now;
    var timeToClose = endTime - now;
    var openingTime = util.format('Time to open:  %s (%s)', selectUtils.timeDelta2String(timeToOpen), startTime);
    var closingTime =     util.format('Time to close: %s (%s)', selectUtils.timeDelta2String(timeToClose), endTime);
    res.render('info', {manifest:manifest,
                        status: status.status(),
                        openingTime: openingTime,
                        closingTime: closingTime,
                        active: status.isActive(),
                        resultReady:resultReady});
}

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE close
//
exports.close = function close(req, res)  {
    if (status.isClosed()) {
        res.send({ ok: false, info: "election already closed" });
    }
    else {
        res.send({ ok: true, info: "triggered to close the election" });
        closeElection();
    }
}

var mixserv_options = {};
if (config.ignore_fin_serv_cert)
    mixserv_options = {rejectUnauthorized: false};

function closeElection() {
    if (status.isClosed()) return;
    status.close();
    winston.info('CLOSING ELECTION.');
    // get the result, send it to the first mix server, and save it.
    var result = cs.getResult();
    // send the result to the first mixserver
    sendData(result, manifest.mixServers[0].URI, mixserv_options);
    saveData(result, config.RESULT_FILE);

    // FIXME TO BE DONE: send the voters' list to the Bulletin Board
}

// Send data to the server with that URI
// The last parameter is *optional*
function sendData(data, URI, destserv_options) {
	winston.info("Sending data to '%s'", URI);
	if(destserv_options)
		var destServ = request.newClient(URI, destserv_options);
	else
		var destServ = request.newClient(URI);
    var toBeSent = {data: data};
    // one could add something like {timeout:10000} to the request below, after 'toBeSent'
    destServ.post('data', toBeSent, function(err, otp_res, body) {
        if (err) {
            winston.info(" ...Error: Cannot send the result to '%s': ", URI, err);
        }
        else {
            winston.info(" ...Result sent to '%s'", URI);
            winston.info(" ...Response: ", body);
        }
    });
}

// Save data in a file
function saveData(data, file) {
    fs.writeFile(file, data, function (err) {
        if (err)
            winston.info('Problems with saving data', data);
        else {
            winston.info('Result saved in', data);
            resultReady = true;
        }
    });
}

///////////////////////////////////////////////////////////////////////////////////////
// Serve a particular static file
exports.serveFile = function serveFile(path) {
    return function (req, res) {
        fs.exists(path, function(exists) {
            if (exists) {
                fs.createReadStream(path).pipe(res);
            } else {
                res.status(404).send('404: Not found');
            }
        });
    }
}

<commit_msg>bug in cs fixed<commit_after>var fs = require('fs');
var util = require('util');
var request = require('request-json');
var winston = require('winston');
var config = require('../config');
var manifest = require('./manifest');
var csCore = require('./csCore.js');
var sendEmail = require('./sendEmail');
var crypto = require('cryptofunc');
var selectUtils = require('selectUtils');

///////////////////////////////////////////////////////////////////////////////////////
// State

var otp_store = {};
var mail_timestamp = {};

var electionID = manifest.hash;
var listOfEligibleVoters = manifest.voters.map(function(k){ return k.email; });
var colSerSigKey = config.signing_key;
var cs = csCore.create(electionID, listOfEligibleVoters, colSerSigKey);

//  status and opening/closing time
var resultReady = fs.existsSync(config.RESULT_FILE);

// If there is no result yet, but there are some accepted
// ballots, we need to read these ballots to resume the operation:
if (!resultReady && fs.existsSync(config.ACCEPTED_BALLOTS_LOG_FILE)) {
    console.log('Resuming (reading already accepted ballots)');
    var log_data = fs.readFileSync(config.ACCEPTED_BALLOTS_LOG_FILE, {flags:'r', encoding:'utf8'});
    log_data.split('\n').forEach(function (entry) { // for each entry in the log file
        if (entry==='') return;
        entry = JSON.parse(entry);
        console.log('  - processing a ballot of', entry.email);
        cs.collectBallot(entry.email, entry.ballot);
    });
}

// Status
function Status() {
    var _open = false;
    var _closed = false;

    return {
        isOpen : function () {
            return _open;
        },
        isClosed : function () {
            return _closed;
        },
        isActive : function () {
            return _open && !_closed;
        },
        status : function () {
            if (_closed) return 'closed'
            else if (_open) return 'open'
            else return 'not open yet';
        },
        open : function () {
            if (!_closed) _open = true;
        },
        close : function () {
            _closed = true;
        }
    }
}
var status = Status();

// Time
var startTime = new Date(manifest.startTime);
var endTime = new Date(manifest.endTime);
var now = new Date();
var timeToOpen = startTime - now;
var timeToClose = endTime - now;
console.log('Time to open:  %s (%s)', selectUtils.timeDelta2String(timeToOpen), startTime);
console.log('Time to close: %s (%s)', selectUtils.timeDelta2String(timeToClose), endTime);

// DETERMINING THE INITIAL STATUS

// if the result is ready (saved in the file), set the status to
// closed (no further ballots are accepted; the result will not be
// sent)
if (resultReady) {
    console.log('Result already exits. Election status set to closed');
    status.close();
}
// if the result is not ready yet, but the time is over, close
// election (this will save and send the result, and set the
// status to closed):
else if (timeToClose <= 0) {
    closeElection();
}
// otherwise (the result is not ready, time is not over) set the
// timeouts for the opening and closing:
else {
    if (timeToClose > 2147483647) { // it is too much for node (v8); the event would fire immediately
        console.log('WARNING: the closing time is too far in the future.');
        console.log('         The election will not close automatically.');
        // FIXME This is clearly a temporary solution. We should handle this limitation.
    }
    else {
        setTimeout(closeElection, timeToClose);
    }

    if (timeToOpen > 2147483647) { // again, too much for v8
        console.log('WARNING: the opening time is too far in the future.');
        console.log('         The election will not open automatically.');
        // FIXME As above.
    }
    else {
        setTimeout(function() {
            status.open();
            winston.info('OPENING ELECTION.');
        }, timeToOpen); // fires right away (in the next click), if timeToOpen <= 0;
    }
}

var log = fs.createWriteStream(config.ACCEPTED_BALLOTS_LOG_FILE, {flags:'a', encoding:'utf8'});

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE otp
//

exports.otp = function otp(req, res)
{
    var email = req.body.email;

    if (!status.isActive()) { // if the status is not active, reject the request
        var descr = (status.isClosed() ? 'Election closed' : 'Election not opened yet');
        winston.info('OTP request (%s) ERROR: %s.', email, descr)
        res.send({ ok: false, descr: descr });
        return;
    }

    if (email) {
        if (!cs.eligibleVoters[email]) // not eligible voter
        {
            winston.info('OTP request (%s) ERROR: Voter not eligible', email);
            res.send({ ok: false, descr: 'Invalid voter identifier (e-mail)' });
        }
        else // voter is eligible; create a fresh OTP and send it
        {
            // Generate OTP (if not generated yet):
        	if (otp_store[email]==null) { // OTP not generated yet (for the given voter)
        		// generate store the OTP under the voter id (email)
        		otp_store[email] = crypto.nonce().slice(0,10); // an OTP will have 5 bytes
        		winston.info('OTP request (%s) accepted. Fresh OTP = %s', email, otp_store[email]);
        	}
        	else // OTP already generated
        		winston.info('OTP request (%s) accepted. Already stored OTP = %s', email, otp_store[email]);

            // Send email:
            if (config.sendEmail) {
                var now = new Date();
                var sentRecently = mail_timestamp[email] != null
                                   && (now - mail_timestamp[email]) < config.timespanEmail*60000; // timespan is specified in minutes (in the config file)
                if (!sentRecently) {
                    // Sent an e-mail with the OTP
                    winston.info('Sending an email to \'%s\' with OTP  ', email, otp_store[email]);
                    var emailContent = 'Election: ' + manifest.title + '\n\nOne time password: ' + otp_store[email] + '\n';
                    sendEmail(email, 'Your One Time Password for sElect', emailContent, function (err,info) {
                        if (err) {
                            winston.info(' ...Error:', err);
                            // TODO: what to do if we are here (the e-mail has not been sent)?
                            res.send({ ok: false, descr: 'Problems in sending the E-mail.' });
                        } else {
                            winston.info(' ...E-mail sent: ' + info.response);
                            mail_timestamp[email] = new Date(); // now
                            res.send({ ok: true });
                        }
                    });
                }
                else { // otp was sent recently
                    winston.info('E-mail to \'%s\' was sent recently (not sent this time).', email );
                    res.send({ ok: true });
                }
            }
            else { // ! config.sentEmail
                res.send({ ok: true });
            }
        }
    }
    else {
        res.send({ ok: false, descr: 'Empty e-mail address' });
    }
};

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE cast
//

exports.cast = function cast(req, res)
{
    var email = req.body.email;
    var otp = req.body.otp;
    var ballot = req.body.ballot;

    // make sure that we have all the pieces:
    if (!email || !otp || !ballot ) {
        winston.info('Cast request (%s) ERROR: Invalid request', email)
        res.send({ ok: false, descr: 'Invalid request' });
        return;
    }

    // is the server active?
    if (!status.isActive()) {
        var descr = (status.isClosed() ? 'Election closed' : 'Election not opened yet');
        winston.info('Cast request (%s) ERROR: %s.', email, descr)
        res.send({ ok: false, descr: descr });
        return;
    }

    // Check the OTP (and, implicitly, the identifier)
    if (otp_store[email] === otp) {
        // Cast the ballot:
    	var response = cs.collectBallot(email, ballot);
//    	if (err) {
//    		winston.info('Cast request (%s/%s) INTERNAL ERROR %s', email, otp, err.toString());
//    		res.send({ ok: false, descr: 'Internal error' });
//    	}
    	if (!response.ok) {
    		winston.info('Cast request (%s/%s) BALLOT REJECTED. Response = %s', email, otp, response.data);
    		res.send({ ok: false, descr: response.data });
    	}
    	else { // everything ok
    		winston.info('Cast request (%s/%s) accepted', email, otp);
    		res.send({ ok: true, receipt: response.data });
    		// log the accepted ballot
    		log.write(JSON.stringify({ email:email, ballot:ballot })+'\n', null,
    				function whenFlushed(e,r) {
                    winston.info('Ballot for %s logged', email);
    		});
    		// TODO: how to make sure that this stream is flushed right away
    	}
    }
    else // OTP not correct
    {
        winston.info('Cast request ERROR: Invalid OTP:', otp);
        res.send({ ok: false, descr: 'Invalid OTP (one time password)' });
    }
};

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE info
//
exports.info = function info(req, res)  {
    var now = new Date();
    var timeToOpen  = selectUtils.timeDelta2String(startTime - now);
    var timeToClose = selectUtils.timeDelta2String(endTime - now);
    var openingTime = util.format('Time to open:  %s (%s)', selectUtils.timeDelta2String(timeToOpen), startTime);
    var closingTime =     util.format('Time to close: %s (%s)', selectUtils.timeDelta2String(timeToClose), endTime);

    res.send({
        electionID : manifest.hash,
        status: status.status(),
        title: manifest.title,
        timeToOpen: timeToOpen,
        timeToClose: timeToClose,
    });
}

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE controlPanel
//
exports.controlPanel = function info(req, res)  {
    var now = new Date();
    var timeToOpen = startTime - now;
    var timeToClose = endTime - now;
    var openingTime = util.format('Time to open:  %s (%s)', selectUtils.timeDelta2String(timeToOpen), startTime);
    var closingTime =     util.format('Time to close: %s (%s)', selectUtils.timeDelta2String(timeToClose), endTime);
    res.render('info', {manifest:manifest,
                        status: status.status(),
                        openingTime: openingTime,
                        closingTime: closingTime,
                        active: status.isActive(),
                        resultReady:resultReady});
}

///////////////////////////////////////////////////////////////////////////////////////
// ROUTE close
//
exports.close = function close(req, res)  {
    if (status.isClosed()) {
        res.send({ ok: false, info: "election already closed" });
    }
    else {
        res.send({ ok: true, info: "triggered to close the election" });
        closeElection();
    }
}

var mixserv_options = {};
if (config.ignore_fin_serv_cert)
    mixserv_options = {rejectUnauthorized: false};

function closeElection() {
    if (status.isClosed()) return;
    status.close();
    winston.info('CLOSING ELECTION.');
    // get the result, send it to the first mix server, and save it.
    var result = cs.getResult();
    // send the result to the first mixserver
    sendData(result, manifest.mixServers[0].URI, mixserv_options);
    saveData(result, config.RESULT_FILE);

    // FIXME TO BE DONE: send the voters' list to the Bulletin Board
}

// Send data to the server with that URI
// The last parameter is *optional*
function sendData(data, URI, destserv_options) {
	winston.info("Sending data to '%s'", URI);
	if(destserv_options)
		var destServ = request.newClient(URI, destserv_options);
	else
		var destServ = request.newClient(URI);
    var toBeSent = {data: data};
    // one could add something like {timeout:10000} to the request below, after 'toBeSent'
    destServ.post('data', toBeSent, function(err, otp_res, body) {
        if (err) {
            winston.info(" ...Error: Cannot send the result to '%s': ", URI, err);
        }
        else {
            winston.info(" ...Result sent to '%s'", URI);
            winston.info(" ...Response: ", body);
        }
    });
}

// Save data in a file
function saveData(data, file) {
    fs.writeFile(file, data, function (err) {
        if (err)
            winston.info('Problems with saving data', data);
        else {
            winston.info('Result saved in', file);
            resultReady = true;
        }
    });
}

///////////////////////////////////////////////////////////////////////////////////////
// Serve a particular static file
exports.serveFile = function serveFile(path) {
    return function (req, res) {
        fs.exists(path, function(exists) {
            if (exists) {
                fs.createReadStream(path).pipe(res);
            } else {
                res.status(404).send('404: Not found');
            }
        });
    }
}

<|endoftext|>


<commit_before>import deepFreeze from 'deep-freeze';
import projectReducer from './ProjectReducer.js';

describe("ProjectReducer", () => {
    it("should get the default state", () => {
        const stateBefore = undefined;
        const action = {};
        const stateAfter = {
            id: 0,
            people: [],
            pairingBoards: []
        };

        expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
    });

    describe("actions", () => {
        describe("LOAD_PROJECT", () => {
            it("should set the project to the passed in project object", () => {
                const stateBefore = {
                    pairingBoards: []
                };

                const action = {
                    type: "LOAD_PROJECT",
                    project: {
                        shoobadooba: "doobadoowa"
                    }
                };

                const stateAfter = {
                    shoobadooba: "doobadoowa"
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });
        });

        describe('RESET_PAIRING_BOARD', () => {
            it('moves all people from pairing boards to floating', () => {
                const stateBefore = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [
                                {name: "Hanzle"},
                                {name: "Gretel"}
                            ]
                        }
                    ]
                };

                const action = { type: 'RESET_PAIRING_BOARD' };

                const stateAfter = {
                    id: 7,
                    people: [
                        {name: "Bubba Gump"},
                        {name:"Charles Shaw"},
                        {name: "Hanzle"},
                        {name: "Gretel"}
                    ],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: []
                        },
                        {
                            name: "BOARD2",
                            people: []
                        }
                    ]
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });

            it('does not move people on exempt pairing boards to floating', () => {
                const stateBefore = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [
                                {name: "Hansel"},
                                {name: "Gretel"}
                            ]
                        },
                        {
                          name: "OOO",
                          exempt: true,
                          people: [{name: "Rip van Winkle"}]
                        }
                    ]
                };

                const action = { type: 'RESET_PAIRING_BOARD' };

                const stateAfter = {
                    id: 7,
                    people: [
                        {name: "Bubba Gump"},
                        {name:"Charles Shaw"},
                        {name: "Hansel"},
                        {name: "Gretel"}
                    ],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: []
                        },
                        {
                            name: "BOARD2",
                            people: []
                        },
                        {
                          name: "OOO",
                          exempt: true,
                          people: [{name: "Rip van Winkle"}]
                        }
                    ]
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });
        });

        describe('SMART_RESET_BOARD', () => {
            it('leaves a person per pair while moving th excess to floating', () => {
                const stateBefore = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [
                                {name: "Hanzle"},
                                {name: "Gretel"}
                            ]
                        },
                        {
                            name: "BOARD3",
                            people: [
                                {name: "Bob"},
                                {name: "Jim"},
                                {name: "Alice"}
                            ]
                        },
                        {
                            name: "BOARD4",
                            people: []
                        }
                    ]
                };

                const action = { type: 'SMART_RESET_BOARD' };

                const stateAfter = {
                    id: 7,
                    people: [
                        {name: "Bubba Gump"},
                        {name: "Gretel"},
                        {name: "Alice"},
                        {name: "Jim"}
                    ],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [
                                {name:"Charles Shaw"}
                            ]
                        },
                        {
                            name: "BOARD2",
                            people: [
                                {name: "Hanzle"}
                            ]
                        },
                        {
                            name: "BOARD3",
                            people: [
                                {name: "Bob"}
                            ]
                        },
                        {
                            name: "BOARD4",
                            people: []
                        }
                    ]
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });

            it('does not move people on exempt pairing boards to floating', () => {
                const stateBefore = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [{name: "Hansel"}]
                        },
                        {
                          name: "OOO",
                          exempt: true,
                          people: [{name: "Gretel"}, {name: "Rip van Winkle"}, {name: "Alice" }]
                        }
                    ]
                };

                const action = { type: 'SMART_RESET_BOARD' };

                const stateAfter = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [{name: "Hansel"}]
                        },
                        {
                          name: "OOO",
                          exempt: true,
                          people: [{name: "Gretel"}, {name: "Rip van Winkle"}, {name: "Alice" }]
                        }
                    ]
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });
        });
    });
});
<commit_msg>Minor update to project reducer spec<commit_after>import deepFreeze from 'deep-freeze';
import projectReducer from './ProjectReducer.js';

describe("ProjectReducer", () => {
    it("should get the default state", () => {
        const stateBefore = undefined;
        const action = {};
        const stateAfter = {
            id: 0,
            people: [],
            pairingBoards: []
        };

        expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
    });

    describe("actions", () => {
        describe("LOAD_PROJECT", () => {
            it("should set the project to the passed in project object", () => {
                const stateBefore = {
                    id: 77,
                    people: [{name: "Billy"}],
                    pairingBoards: []
                };

                const action = {
                    type: "LOAD_PROJECT",
                    project: {
                        shoobadooba: "doobadoowa"
                    }
                };

                const stateAfter = {
                    shoobadooba: "doobadoowa"
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });
        });

        describe('RESET_PAIRING_BOARD', () => {
            it('moves all people from pairing boards to floating', () => {
                const stateBefore = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [
                                {name: "Hanzle"},
                                {name: "Gretel"}
                            ]
                        }
                    ]
                };

                const action = { type: 'RESET_PAIRING_BOARD' };

                const stateAfter = {
                    id: 7,
                    people: [
                        {name: "Bubba Gump"},
                        {name:"Charles Shaw"},
                        {name: "Hanzle"},
                        {name: "Gretel"}
                    ],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: []
                        },
                        {
                            name: "BOARD2",
                            people: []
                        }
                    ]
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });

            it('does not move people on exempt pairing boards to floating', () => {
                const stateBefore = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [
                                {name: "Hansel"},
                                {name: "Gretel"}
                            ]
                        },
                        {
                          name: "OOO",
                          exempt: true,
                          people: [{name: "Rip van Winkle"}]
                        }
                    ]
                };

                const action = { type: 'RESET_PAIRING_BOARD' };

                const stateAfter = {
                    id: 7,
                    people: [
                        {name: "Bubba Gump"},
                        {name:"Charles Shaw"},
                        {name: "Hansel"},
                        {name: "Gretel"}
                    ],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: []
                        },
                        {
                            name: "BOARD2",
                            people: []
                        },
                        {
                          name: "OOO",
                          exempt: true,
                          people: [{name: "Rip van Winkle"}]
                        }
                    ]
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });
        });

        describe('SMART_RESET_BOARD', () => {
            it('leaves a person per pair while moving th excess to floating', () => {
                const stateBefore = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [
                                {name: "Hanzle"},
                                {name: "Gretel"}
                            ]
                        },
                        {
                            name: "BOARD3",
                            people: [
                                {name: "Bob"},
                                {name: "Jim"},
                                {name: "Alice"}
                            ]
                        },
                        {
                            name: "BOARD4",
                            people: []
                        }
                    ]
                };

                const action = { type: 'SMART_RESET_BOARD' };

                const stateAfter = {
                    id: 7,
                    people: [
                        {name: "Bubba Gump"},
                        {name: "Gretel"},
                        {name: "Alice"},
                        {name: "Jim"}
                    ],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [
                                {name:"Charles Shaw"}
                            ]
                        },
                        {
                            name: "BOARD2",
                            people: [
                                {name: "Hanzle"}
                            ]
                        },
                        {
                            name: "BOARD3",
                            people: [
                                {name: "Bob"}
                            ]
                        },
                        {
                            name: "BOARD4",
                            people: []
                        }
                    ]
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });

            it('does not move people on exempt pairing boards to floating', () => {
                const stateBefore = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [{name: "Hansel"}]
                        },
                        {
                          name: "OOO",
                          exempt: true,
                          people: [{name: "Gretel"}, {name: "Rip van Winkle"}, {name: "Alice" }]
                        }
                    ]
                };

                const action = { type: 'SMART_RESET_BOARD' };

                const stateAfter = {
                    id: 7,
                    people: [{name: "Bubba Gump"}],
                    pairingBoards: [
                        {
                            name: "BOARD1",
                            people: [{name:"Charles Shaw"}]
                        },
                        {
                            name: "BOARD2",
                            people: [{name: "Hansel"}]
                        },
                        {
                          name: "OOO",
                          exempt: true,
                          people: [{name: "Gretel"}, {name: "Rip van Winkle"}, {name: "Alice" }]
                        }
                    ]
                };

                deepFreeze(stateBefore);
                deepFreeze(action);

                expect(projectReducer(stateBefore, action)).toEqual(stateAfter);
            });
        });
    });
});
<|endoftext|>


<commit_before>var core = require('../../core'),
    BlurXFilter = require('./BlurXFilter'),
    BlurYFilter = require('./BlurYFilter');

/**
 * The BlurFilter applies a Gaussian blur to an object.
 * The strength of the blur can be set for x- and y-axis separately.
 *
 * @class
 * @extends PIXI.Filter
 * @memberof PIXI.filters
 */
function BlurFilter()
{
    core.Filter.call(this);

    this.blurXFilter = new BlurXFilter();
    this.blurYFilter = new BlurYFilter();
    this.resolution = 0.5;//0.25;//0.25//1//01.26;
    this.blurYFilter.passes = this.blurXFilter.passes = 1;
    this.blurYFilter.strength = this.blurXFilter.strength = 4;//4// 4

    this.padding = 40;
    this.passes = 2;
}

BlurFilter.prototype = Object.create(core.Filter.prototype);
BlurFilter.prototype.constructor = BlurFilter;
module.exports = BlurFilter;

BlurFilter.prototype.apply = function (filterManager, input, output)
{

    var renderTarget = filterManager.getRenderTarget(true);

    this.blurXFilter.apply(filterManager, input, renderTarget, true);
    this.blurYFilter.apply(filterManager, renderTarget, output, true);

    filterManager.returnRenderTarget(renderTarget);
};

Object.defineProperties(BlurFilter.prototype, {
    /**
     * Sets the strength of both the blurX and blurY properties simultaneously
     *
     * @member {number}
     * @memberOf PIXI.filters.BlurFilter#
     * @default 2
     */
    blur: {
        get: function ()
        {
            return this.blurXFilter.blur;
        },
        set: function (value)
        {
            this.padding = Math.max( Math.abs(this.blurYFilter.strength),  Math.abs(this.blurYFilter.strength)) * 2;
            this.blurXFilter.blur = this.blurYFilter.blur = value;
        }
    },

    /**
     * Sets the number of passes for blur. More passes means higher quaility bluring.
     *
     * @member {number}
     * @memberof PIXI.filters.BlurYFilter#
     * @default 1
     */
    passes: {
        get: function ()
        {
            return  this.blurXFilter.passes;
        },
        set: function (value)
        {

            this.blurXFilter.passes = this.blurYFilter.passes = value;
        }
    },

    /**
     * Sets the strength of the blurX property
     *
     * @member {number}
     * @memberOf PIXI.filters.BlurFilter#
     * @default 2
     */
    blurX: {
        get: function ()
        {
            return this.blurXFilter.blur;
        },
        set: function (value)
        {
            this.blurXFilter.blur = value;
            this.padding = Math.max( Math.abs(this.blurYFilter.strength),  Math.abs(this.blurYFilter.strength)) * 2;
        }
    },

    /**
     * Sets the strength of the blurY property
     *
     * @member {number}
     * @memberOf PIXI.filters.BlurFilter#
     * @default 2
     */
    blurY: {
        get: function ()
        {
            return this.blurYFilter.blur;
        },
        set: function (value)
        {
            this.blurYFilter.blur = value;
            this.padding = Math.max( Math.abs(this.blurYFilter.strength),  Math.abs(this.blurYFilter.strength)) * 2;
        }
    }
});
<commit_msg>fixed clearing at the end of a filter<commit_after>var core = require('../../core'),
    BlurXFilter = require('./BlurXFilter'),
    BlurYFilter = require('./BlurYFilter');

/**
 * The BlurFilter applies a Gaussian blur to an object.
 * The strength of the blur can be set for x- and y-axis separately.
 *
 * @class
 * @extends PIXI.Filter
 * @memberof PIXI.filters
 */
function BlurFilter()
{
    core.Filter.call(this);

    this.blurXFilter = new BlurXFilter();
    this.blurYFilter = new BlurYFilter();
    this.resolution = 0.5;//0.25;//0.25//1//01.26;
    this.blurYFilter.passes = this.blurXFilter.passes = 1;
    this.blurYFilter.strength = this.blurXFilter.strength = 4;//4// 4

    this.padding = 40;
    this.passes = 2;
}

BlurFilter.prototype = Object.create(core.Filter.prototype);
BlurFilter.prototype.constructor = BlurFilter;
module.exports = BlurFilter;

BlurFilter.prototype.apply = function (filterManager, input, output)
{

    var renderTarget = filterManager.getRenderTarget(true);

    this.blurXFilter.apply(filterManager, input, renderTarget, true);
    this.blurYFilter.apply(filterManager, renderTarget, output, false);

    filterManager.returnRenderTarget(renderTarget);
};

Object.defineProperties(BlurFilter.prototype, {
    /**
     * Sets the strength of both the blurX and blurY properties simultaneously
     *
     * @member {number}
     * @memberOf PIXI.filters.BlurFilter#
     * @default 2
     */
    blur: {
        get: function ()
        {
            return this.blurXFilter.blur;
        },
        set: function (value)
        {
            this.padding = Math.max( Math.abs(this.blurYFilter.strength),  Math.abs(this.blurYFilter.strength)) * 2;
            this.blurXFilter.blur = this.blurYFilter.blur = value;
        }
    },

    /**
     * Sets the number of passes for blur. More passes means higher quaility bluring.
     *
     * @member {number}
     * @memberof PIXI.filters.BlurYFilter#
     * @default 1
     */
    passes: {
        get: function ()
        {
            return  this.blurXFilter.passes;
        },
        set: function (value)
        {

            this.blurXFilter.passes = this.blurYFilter.passes = value;
        }
    },

    /**
     * Sets the strength of the blurX property
     *
     * @member {number}
     * @memberOf PIXI.filters.BlurFilter#
     * @default 2
     */
    blurX: {
        get: function ()
        {
            return this.blurXFilter.blur;
        },
        set: function (value)
        {
            this.blurXFilter.blur = value;
            this.padding = Math.max( Math.abs(this.blurYFilter.strength),  Math.abs(this.blurYFilter.strength)) * 2;
        }
    },

    /**
     * Sets the strength of the blurY property
     *
     * @member {number}
     * @memberOf PIXI.filters.BlurFilter#
     * @default 2
     */
    blurY: {
        get: function ()
        {
            return this.blurYFilter.blur;
        },
        set: function (value)
        {
            this.blurYFilter.blur = value;
            this.padding = Math.max( Math.abs(this.blurYFilter.strength),  Math.abs(this.blurYFilter.strength)) * 2;
        }
    }
});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.menu',
  name: 'DAOMenu',
  extends: 'foam.nanos.menu.AbstractMenu',

  properties: [
    {
      class: 'String',
      name: 'daoKey'
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'summaryView',
      javaType: 'foam.lib.json.UnknownFObject',
      javaInfoType: 'foam.core.AbstractFObjectPropertyInfo',
      javaJSONParser: 'new foam.lib.json.UnknownFObjectParser()',
      // TODO: remove next line when permanently fixed in ViewSpec
      fromJSON: function fromJSON(value, ctx, prop, json) { return value; }
    }
  ],

  methods: [
    function createView(X) {
      if ( ! X[this.daoKey] ) {
        throw new Error("No dao found for " + this.daoKey);
      }

      var view = { class: 'foam.comics.BrowserView', data: X[this.daoKey] };

      if ( this.summaryView ) view.summaryView = this.summaryView;
      return view;
    }
  ]
});
<commit_msg>Add documentation.<commit_after>/**
 * @license
 * Copyright 2017 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

foam.CLASS({
  package: 'foam.nanos.menu',
  name: 'DAOMenu',
  extends: 'foam.nanos.menu.AbstractMenu',

  documentation: 'Menu for displaying standard DAO controller.',

  properties: [
    {
      class: 'String',
      name: 'daoKey'
    },
    {
      class: 'foam.u2.ViewSpec',
      name: 'summaryView',
      javaType: 'foam.lib.json.UnknownFObject',
      javaInfoType: 'foam.core.AbstractFObjectPropertyInfo',
      javaJSONParser: 'new foam.lib.json.UnknownFObjectParser()',
      // TODO: remove next line when permanently fixed in ViewSpec
      fromJSON: function fromJSON(value, ctx, prop, json) { return value; }
    }
  ],

  methods: [
    function createView(X) {
      if ( ! X[this.daoKey] ) {
        throw new Error("No DAO found for " + this.daoKey);
      }

      var view = { class: 'foam.comics.BrowserView', data: X[this.daoKey] };

      if ( this.summaryView ) view.summaryView = this.summaryView;
      return view;
    }
  ]
});
<|endoftext|>


<commit_before>import translations from '../_texts/translations.js'
import '../Forms.vue'

export default {
  props: {
    value: {type: String},
    format: {default: 'MM/dd/yyyy'},
    disabledDaysOfWeek: {type: Array, default () { return [] }},
    width: {type: String},
    clearButton: {type: Boolean, default: false},
    lang: {type: String, default: navigator.language},
    placeholder: {type: String},
    iconsFont: {type: String, default: 'fa'}
  },
  data () {
    return {
      currDate: new Date(),
      dateRange: [],
      decadeRange: [],
      displayDayView: false,
      displayMonthView: false,
      displayYearView: false,
      val: this.value
    }
  },
  watch: {
    currDate () {
      this.getDateRange()
    },
    format () {
      this.val = this.stringify(this.currDate)
    },
    val (val) {
      this.$emit('input', val)
    },
    value (val) {
      if (this.val !== val) { this.val = val }
    }
  },
  computed: {
    text () {
      return translations(this.lang)
    },
    preBtnClasses () {
      return `datepicker-preBtn ${this.iconsFont} ${this.iconsFont}-chevron-left`
    },
    nextBtnClasses () {
      return `datepicker-nextBtn ${this.iconsFont} ${this.iconsFont}-chevron-right`
    },
    disabledDaysArray () {
      return this.disabledDaysOfWeek.map(d => parseInt(d, 10))
    }
  },
  methods: {
    close () {
      this.displayDayView = this.displayMonthView = this.displayYearView = false
    },
    inputClick () {
      this.currDate = this.parse(this.val) || this.parse(new Date())
      if (this.displayMonthView || this.displayYearView) {
        this.displayDayView = false
      } else {
        this.displayDayView = !this.displayDayView
      }
    },
    preNextDecadeClick (flag) {
      const year = this.currDate.getFullYear()
      const months = this.currDate.getMonth()
      const date = this.currDate.getDate()

      if (flag === 0) {
        this.currDate = new Date(year - 10, months, date)
      } else {
        this.currDate = new Date(year + 10, months, date)
      }
    },
    preNextMonthClick (flag) {
      const year = this.currDate.getFullYear()
      const month = this.currDate.getMonth()
      const date = this.currDate.getDate()

      if (flag === 0) {
        const preMonth = this.getYearMonth(year, month - 1)
        this.currDate = new Date(preMonth.year, preMonth.month, date)
      } else {
        const nextMonth = this.getYearMonth(year, month + 1)
        this.currDate = new Date(nextMonth.year, nextMonth.month, date)
      }
    },
    preNextYearClick (flag) {
      const year = this.currDate.getFullYear()
      const months = this.currDate.getMonth()
      const date = this.currDate.getDate()

      if (flag === 0) {
        this.currDate = new Date(year - 1, months, date)
      } else {
        this.currDate = new Date(year + 1, months, date)
      }
    },
    yearSelect (year) {
      this.displayYearView = false
      this.displayMonthView = true
      this.currDate = new Date(year, this.currDate.getMonth(), this.currDate.getDate())
    },
    daySelect (day) {
      if (day.sclass === 'datepicker-item-disable') {
        return false
      } else {
        this.currDate = day.date
        this.val = this.stringify(this.currDate)
        this.displayDayView = false
      }
    },
    switchMonthView () {
      this.displayDayView = false
      this.displayMonthView = true
    },
    switchDecadeView () {
      this.displayMonthView = false
      this.displayYearView = true
    },
    monthSelect (index) {
      this.displayMonthView = false
      this.displayDayView = true
      this.currDate = new Date(this.currDate.getFullYear(), index, this.currDate.getDate())
    },
    getYearMonth (year, month) {
      if (month > 11) {
        year++
        month = 0
      } else if (month < 0) {
        year--
        month = 11
      }
      return {year: year, month: month}
    },
    stringifyDecadeHeader (date) {
      const yearStr = date.getFullYear().toString()
      const firstYearOfDecade = yearStr.substring(0, yearStr.length - 1) + 0
      const lastYearOfDecade = parseInt(firstYearOfDecade, 10) + 10
      return firstYearOfDecade + '-' + lastYearOfDecade
    },
    stringifyDayHeader (date) {
      return this.text.months[date.getMonth()] + ' ' + date.getFullYear()
    },
    parseMonth (date) {
      return this.text.months[date.getMonth()]
    },
    stringifyYearHeader (date) {
      return date.getFullYear()
    },
    stringify (date, format = this.format) {
      if (!date) date = this.parse()
      if (!date) return ''
      const year = date.getFullYear()
      const month = date.getMonth() + 1
      const day = date.getDate()
      const monthName = this.parseMonth(date)
      return format
        .replace(/yyyy/g, year)
        .replace(/yy/g, year)
        .replace(/MMMM/g, monthName)
        .replace(/MMM/g, monthName.substring(0, 3))
        .replace(/MM/g, ('0' + month).slice(-2))
        .replace(/M(?!a)/g, month)
        .replace(/dd/g, ('0' + day).slice(-2))
        .replace(/d/g, day)
    },
    parse (str) {
      if (str === undefined || str === null) { str = this.val }
      let date = str.length === 10 && (this.format === 'dd-MM-yyyy' || this.format === 'dd/MM/yyyy')
        ? new Date(str.substring(6, 10), str.substring(3, 5) - 1, str.substring(0, 2))
        : new Date(str)
      return isNaN(date.getFullYear()) ? new Date() : date
    },
    getDayCount (year, month) {
      const dict = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
      if (month === 1) {
        if ((year % 400 === 0) || (year % 4 === 0 && year % 100 !== 0)) {
          return 29
        }
      }
      return dict[month]
    },
    getDateRange () {
      this.dateRange = []
      this.decadeRange = []
      const time = {
        year: this.currDate.getFullYear(),
        month: this.currDate.getMonth(),
        day: this.currDate.getDate()
      }
      const yearStr = time.year.toString()
      const firstYearOfDecade = (yearStr.substring(0, yearStr.length - 1) + 0) - 1
      for (let i = 0; i < 12; i++) {
        this.decadeRange.push({
          text: firstYearOfDecade + i
        })
      }

      const currMonthFirstDay = new Date(time.year, time.month, 1)
      let firstDayWeek = currMonthFirstDay.getDay() + 1
      if (firstDayWeek === 0) {
        firstDayWeek = 7
      }
      const dayCount = this.getDayCount(time.year, time.month)
      if (firstDayWeek > 1) {
        const preMonth = this.getYearMonth(time.year, time.month - 1)
        const prevMonthDayCount = this.getDayCount(preMonth.year, preMonth.month)
        for (let i = 1; i < firstDayWeek; i++) {
          const dayText = prevMonthDayCount - firstDayWeek + i + 1
          const date = new Date(preMonth.year, preMonth.month, dayText)
          let sclass = 'datepicker-item-gray'
          if (this.disabledDaysArray.indexOf(date.getDay()) > -1) {
            sclass = 'datepicker-item-disable'
          }
          this.dateRange.push({ text: dayText, date, sclass })
        }
      }

      for (let i = 1; i <= dayCount; i++) {
        const date = new Date(time.year, time.month, i)
        let sclass = ''
        if (this.disabledDaysArray.indexOf(date.getDay()) > -1) {
          sclass = 'datepicker-item-disable'
        }
        if (i === time.day && date.getFullYear() === time.year && date.getMonth() === time.month) {
          sclass = 'datepicker-dateRange-item-active'
        }
        this.dateRange.push({text: i, date, sclass})
      }

      if (this.dateRange.length < 42) {
        const nextMonthNeed = 42 - this.dateRange.length
        const nextMonth = this.getYearMonth(time.year, time.month + 1)

        for (let i = 1; i <= nextMonthNeed; i++) {
          const date = new Date(nextMonth.year, nextMonth.month, i)
          let sclass = 'datepicker-item-gray'
          if (this.disabledDaysArray.indexOf(date.getDay()) > -1) {
            sclass = 'datepicker-item-disable'
          }
          this.dateRange.push({text: i, date, sclass})
        }
      }
    }
  },
  mounted () {
    this.$emit('child-created', this)
    this.currDate = this.parse(this.val) || this.parse(new Date())
    this._blur = e => {
      if (!this.$el.contains(e.target)) {
        this.close()
      }
    }
    window.addEventListener('click', this._blur)
  },
  beforeDestroy () {
    window.removeEventListener('click', this._blur)
  }
}
<commit_msg>refactoring<commit_after>import translations from '../_texts/translations.js'
import '../Forms.vue'

export default {
  props: {
    value: {type: String},
    format: {default: 'MM/dd/yyyy'},
    disabledDaysOfWeek: {type: Array, default () { return [] }},
    width: {type: String},
    clearButton: {type: Boolean, default: false},
    lang: {type: String, default: navigator.language},
    placeholder: {type: String},
    iconsFont: {type: String, default: 'fa'}
  },
  data () {
    return {
      currDate: new Date(),
      dateRange: [],
      decadeRange: [],
      displayDayView: false,
      displayMonthView: false,
      displayYearView: false,
      val: this.value
    }
  },
  watch: {
    currDate () {
      this.getDateRange()
    },
    format () {
      this.val = this.stringify(this.currDate)
    },
    val (val) {
      this.$emit('input', val)
    },
    value (val) {
      if (this.val !== val) { this.val = val }
    }
  },
  computed: {
    text () {
      return translations(this.lang)
    },
    preBtnClasses () {
      return `datepicker-preBtn ${this.iconsFont} ${this.iconsFont}-chevron-left`
    },
    nextBtnClasses () {
      return `datepicker-nextBtn ${this.iconsFont} ${this.iconsFont}-chevron-right`
    },
    disabledDaysArray () {
      return this.disabledDaysOfWeek.map(d => parseInt(d, 10))
    },
    setupRange () {
      return {
        year: this.currDate.getFullYear(),
        month: this.currDate.getMonth(),
        day: this.currDate.getDate()
      }
    }
  },
  methods: {
    close () {
      this.displayDayView = this.displayMonthView = this.displayYearView = false
    },
    inputClick () {
      this.currDate = this.parse(this.val) || this.parse(new Date())
      if (this.displayMonthView || this.displayYearView) {
        this.displayDayView = false
      } else {
        this.displayDayView = !this.displayDayView
      }
    },
    preNextDecadeClick (flag) {
      const year = this.currDate.getFullYear()
      const months = this.currDate.getMonth()
      const date = this.currDate.getDate()

      if (flag === 0) {
        this.currDate = new Date(year - 10, months, date)
      } else {
        this.currDate = new Date(year + 10, months, date)
      }
    },
    preNextMonthClick (flag) {
      const year = this.currDate.getFullYear()
      const month = this.currDate.getMonth()
      const date = this.currDate.getDate()

      if (flag === 0) {
        const preMonth = this.getYearMonth(year, month - 1)
        this.currDate = new Date(preMonth.year, preMonth.month, date)
      } else {
        const nextMonth = this.getYearMonth(year, month + 1)
        this.currDate = new Date(nextMonth.year, nextMonth.month, date)
      }
    },
    preNextYearClick (flag) {
      const year = this.currDate.getFullYear()
      const months = this.currDate.getMonth()
      const date = this.currDate.getDate()

      if (flag === 0) {
        this.currDate = new Date(year - 1, months, date)
      } else {
        this.currDate = new Date(year + 1, months, date)
      }
    },
    yearSelect (year) {
      this.displayYearView = false
      this.displayMonthView = true
      this.currDate = new Date(year, this.currDate.getMonth(), this.currDate.getDate())
    },
    daySelect (day) {
      if (day.sclass === 'datepicker-item-disable') {
        return false
      } else {
        this.currDate = day.date
        this.val = this.stringify(this.currDate)
        this.displayDayView = false
      }
    },
    switchMonthView () {
      this.displayDayView = false
      this.displayMonthView = true
    },
    switchDecadeView () {
      this.displayMonthView = false
      this.displayYearView = true
    },
    monthSelect (index) {
      this.displayMonthView = false
      this.displayDayView = true
      this.currDate = new Date(this.currDate.getFullYear(), index, this.currDate.getDate())
    },
    getYearMonth (year, month) {
      if (month > 11) {
        year++
        month = 0
      } else if (month < 0) {
        year--
        month = 11
      }
      return {year: year, month: month}
    },
    stringifyDecadeHeader (date) {
      const yearStr = date.getFullYear().toString()
      const firstYearOfDecade = yearStr.substring(0, yearStr.length - 1) + 0
      const lastYearOfDecade = parseInt(firstYearOfDecade, 10) + 10
      return firstYearOfDecade + '-' + lastYearOfDecade
    },
    stringifyDayHeader (date) {
      return this.text.months[date.getMonth()] + ' ' + date.getFullYear()
    },
    parseMonth (date) {
      return this.text.months[date.getMonth()]
    },
    stringifyYearHeader (date) {
      return date.getFullYear()
    },
    stringify (date, format = this.format) {
      if (!date) date = this.parse()
      if (!date) return ''
      const year = date.getFullYear()
      const month = date.getMonth() + 1
      const day = date.getDate()
      const monthName = this.parseMonth(date)
      return format
        .replace(/yyyy/g, year)
        .replace(/yy/g, year)
        .replace(/MMMM/g, monthName)
        .replace(/MMM/g, monthName.substring(0, 3))
        .replace(/MM/g, ('0' + month).slice(-2))
        .replace(/M(?!a)/g, month)
        .replace(/dd/g, ('0' + day).slice(-2))
        .replace(/d/g, day)
    },
    parse (str) {
      if (str === undefined || str === null) { str = this.val }
      let date = str.length === 10 && (this.format === 'dd-MM-yyyy' || this.format === 'dd/MM/yyyy')
        ? new Date(str.substring(6, 10), str.substring(3, 5) - 1, str.substring(0, 2))
        : new Date(str)
      return isNaN(date.getFullYear()) ? new Date() : date
    },
    getDayCount (year, month) {
      const dict = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
      if (month === 1) {
        if ((year % 400 === 0) || (year % 4 === 0 && year % 100 !== 0)) {
          return 29
        }
      }
      return dict[month]
    },
    getDateRange () {
      this.dateRange = []
      this.decadeRange = []
      const yearStr = this.setupRange.year.toString()
      const firstYearOfDecade = (yearStr.substring(0, yearStr.length - 1) + 0) - 1
      for (let i = 0; i < 12; i++) {
        this.decadeRange.push({
          text: firstYearOfDecade + i
        })
      }

      this.rangeMajor1()
      this.mountCalendar()
      this.rangeLength42()
    },
    firstDay () {
      const currMonthFirstDay = new Date(this.setupRange.year, this.setupRange.month, 1)
      let firstDayWeek = currMonthFirstDay.getDay() + 1
      if (firstDayWeek === 0) {
        firstDayWeek = 7
      }
      return firstDayWeek
    },
    mountCalendar () {
      const dayCount = this.getDayCount(this.setupRange.year, this.setupRange.month)

      for (let i = 1; i <= dayCount; i++) {
        const date = new Date(this.setupRange.year, this.setupRange.month, i)
        let sclass = ''
        if (this.disabledDaysArray.indexOf(date.getDay()) > -1) {
          sclass = 'datepicker-item-disable'
        }
        if (i === this.setupRange.day && date.getFullYear() === this.setupRange.year && date.getMonth() === this.setupRange.month) {
          sclass = 'datepicker-dateRange-item-active'
        }
        this.dateRange.push({text: i, date, sclass})
      }
    },
    rangeMajor1 () {
      if (this.firstDay() > 1) {
        const preMonth = this.getYearMonth(this.setupRange.year, this.setupRange.month - 1)
        const prevMonthDayCount = this.getDayCount(preMonth.year, preMonth.month)
        for (let i = 1; i < this.firstDay(); i++) {
          const dayText = prevMonthDayCount - this.firstDay() + i + 1
          const date = new Date(preMonth.year, preMonth.month, dayText)
          let sclass = 'datepicker-item-gray'
          if (this.disabledDaysArray.indexOf(date.getDay()) > -1) {
            sclass = 'datepicker-item-disable'
          }
          this.dateRange.push({ text: dayText, date, sclass })
        }
      }
    },
    rangeLength42 () {
      if (this.dateRange.length < 42) {
        const nextMonthNeed = 42 - this.dateRange.length
        const nextMonth = this.getYearMonth(this.setupRange.year, this.setupRange.month + 1)

        for (let i = 1; i <= nextMonthNeed; i++) {
          const date = new Date(nextMonth.year, nextMonth.month, i)
          let sclass = 'datepicker-item-gray'
          if (this.disabledDaysArray.indexOf(date.getDay()) > -1) {
            sclass = 'datepicker-item-disable'
          }
          this.dateRange.push({text: i, date, sclass})
        }
      }
    }
  },
  mounted () {
    this.$emit('child-created', this)
    this.currDate = this.parse(this.val) || this.parse(new Date())
    this._blur = e => {
      if (!this.$el.contains(e.target)) {
        this.close()
      }
    }
    window.addEventListener('click', this._blur)
  },
  beforeDestroy () {
    window.removeEventListener('click', this._blur)
  }
}
<|endoftext|>


<commit_before>var getUrlVars = function() {
	var vars = {};
	var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
		vars[key] = value;
	});
	return vars;
}

var size = 256;
var serializeArray = function(arr) {
	var out = new Array();

	for (key in arr) {
		out.push(key + '=' + arr[key]);
	}

	return out.join('&');
};

function initialize(mapTypeName) {
	var customTypeOptions = {
		getTileUrl: function(coord, zoom) {
			options = {
				w: size,
				h: size,
				x: coord.x,
				y: coord.y,
				z: zoom,
				i: 10,
			}
			// Add any form elements to request
			$('form input').each(function(idx, e) {
				options[e.id] =  e.value;
			});
			return "/" + mapTypeName + "?" + serializeArray(options);
		},
		tileSize: new google.maps.Size(size, size),
		maxZoom: 1000,
		minZoom: 0,
		name: mapTypeName
	};
	var customMapType = new google.maps.ImageMapType(customTypeOptions);
	var myLatlng = new google.maps.LatLng(0, 0);
	var myOptions = {
		center: myLatlng,
		zoom: 0,
		streetViewControl: false,
		mapTypeControlOptions: {
			mapTypeIds: [mapTypeName]
		}
	};

	var map = new google.maps.Map(document.getElementById("maps"), myOptions);
	map.mapTypes.set(mapTypeName, customMapType);
	map.setMapTypeId(mapTypeName);
	return map;
}
<commit_msg>Center map on load.<commit_after>var getUrlVars = function() {
	var vars = {};
	var parts = window.location.href.replace(/[?&]+([^=&]+)=([^&]*)/gi, function(m,key,value) {
		vars[key] = value;
	});
	return vars;
}

var size = 256;
var serializeArray = function(arr) {
	var out = new Array();

	for (key in arr) {
		out.push(key + '=' + arr[key]);
	}

	return out.join('&');
};

function initialize(mapTypeName) {
	var customTypeOptions = {
		getTileUrl: function(coord, zoom) {
			options = {
				w: size,
				h: size,
				x: coord.x,
				y: coord.y,
				z: zoom,
				i: 10,
			}
			// Add any form elements to request
			$('form input').each(function(idx, e) {
				options[e.id] =  e.value;
			});
			return "/" + mapTypeName + "?" + serializeArray(options);
		},
		tileSize: new google.maps.Size(size, size),
		maxZoom: 1000,
		minZoom: 0,
		name: mapTypeName
	};
	var customMapType = new google.maps.ImageMapType(customTypeOptions);
	var myLatlng = new google.maps.LatLng(0, 0);
	var myOptions = {
		center: myLatlng,
		zoom: 0,
		streetViewControl: false,
		mapTypeControlOptions: {
			mapTypeIds: [mapTypeName]
		}
	};

	var map = new google.maps.Map(document.getElementById("maps"), myOptions);
	map.mapTypes.set(mapTypeName, customMapType);
	map.setMapTypeId(mapTypeName);
	map.panTo(myLatlng);
	return map;
}
<|endoftext|>


<commit_before>var PROPERTY_SS_ID = 'SPREADSHEET_ID';
var RULE_KEY = 'MAILMAN_PROP_RULES';
var MAILMAN_VERSION = 'pre0.14';
var APP_NAME = 'Mailman';
var LAST_AUTH_KEY = 'MAILMAN_AUTH_EMAIL';

var SPREADSHEET_ID = null;
<commit_msg>Incremented the version to pre0.16.<commit_after>var PROPERTY_SS_ID = 'SPREADSHEET_ID';
var RULE_KEY = 'MAILMAN_PROP_RULES';
var MAILMAN_VERSION = 'pre0.16';
var APP_NAME = 'Mailman';
var LAST_AUTH_KEY = 'MAILMAN_AUTH_EMAIL';

var SPREADSHEET_ID = null;
<|endoftext|>


<commit_before>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../utils/Class');
var List = require('../structs/List');
var PluginCache = require('../plugins/PluginCache');
var GameObjectEvents = require('./events');
var SceneEvents = require('../scene/events');
var StableSort = require('../utils/array/StableSort');

/**
 * @classdesc
 * The Display List plugin.
 *
 * Display Lists belong to a Scene and maintain the list of Game Objects to render every frame.
 *
 * Some of these Game Objects may also be part of the Scene's [Update List]{@link Phaser.GameObjects.UpdateList}, for updating.
 *
 * @class DisplayList
 * @extends Phaser.Structs.List.<Phaser.GameObjects.GameObject>
 * @memberof Phaser.GameObjects
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Scene} scene - The Scene that this Display List belongs to.
 */
var DisplayList = new Class({

    Extends: List,

    initialize:

    function DisplayList (scene)
    {
        List.call(this, scene);

        /**
         * The flag the determines whether Game Objects should be sorted when `depthSort()` is called.
         *
         * @name Phaser.GameObjects.DisplayList#sortChildrenFlag
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.sortChildrenFlag = false;

        /**
         * The Scene that this Display List belongs to.
         *
         * @name Phaser.GameObjects.DisplayList#scene
         * @type {Phaser.Scene}
         * @since 3.0.0
         */
        this.scene = scene;

        /**
         * The Scene's Systems.
         *
         * @name Phaser.GameObjects.DisplayList#systems
         * @type {Phaser.Scenes.Systems}
         * @since 3.0.0
         */
        this.systems = scene.sys;

        /**
         * The Scene's Event Emitter.
         *
         * @name Phaser.GameObjects.DisplayList#events
         * @type {Phaser.Events.EventEmitter}
         * @since 3.50.0
         */
        this.events = scene.sys.events;

        //  Set the List callbacks
        this.addCallback = this.addChildCallback;
        this.removeCallback = this.removeChildCallback;

        this.events.once(SceneEvents.BOOT, this.boot, this);
        this.events.on(SceneEvents.START, this.start, this);
    },

    /**
     * This method is called automatically, only once, when the Scene is first created.
     * Do not invoke it directly.
     *
     * @method Phaser.GameObjects.DisplayList#boot
     * @private
     * @since 3.5.1
     */
    boot: function ()
    {
        this.events.once(SceneEvents.DESTROY, this.destroy, this);
    },

    /**
     * Internal method called from `List.addCallback`.
     *
     * @method Phaser.GameObjects.DisplayList#addChildCallback
     * @private
     * @fires Phaser.Scenes.Events#ADDED_TO_SCENE
     * @fires Phaser.GameObjects.Events#ADDED_TO_SCENE
     * @since 3.50.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was added to the list.
     */
    addChildCallback: function (gameObject)
    {
        gameObject.emit(GameObjectEvents.ADDED_TO_SCENE, gameObject, this.scene);

        if (gameObject.displayList)
        {
            gameObject.displayList.remove(gameObject);
        }

        gameObject.displayList = this;

        this.queueDepthSort();

        this.events.emit(SceneEvents.ADDED_TO_SCENE, gameObject, this.scene);
    },

    /**
     * Internal method called from `List.removeCallback`.
     *
     * @method Phaser.GameObjects.DisplayList#removeChildCallback
     * @private
     * @fires Phaser.Scenes.Events#REMOVED_FROM_SCENE
     * @fires Phaser.GameObjects.Events#REMOVED_FROM_SCENE
     * @since 3.50.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was removed from the list.
     */
    removeChildCallback: function (gameObject)
    {
        gameObject.emit(GameObjectEvents.REMOVED_FROM_SCENE, gameObject, this.scene);

        gameObject.displayList = null;

        this.queueDepthSort();

        this.events.emit(SceneEvents.REMOVED_FROM_SCENE, gameObject, this.scene);
    },

    /**
     * This method is called automatically by the Scene when it is starting up.
     * It is responsible for creating local systems, properties and listening for Scene events.
     * Do not invoke it directly.
     *
     * @method Phaser.GameObjects.DisplayList#start
     * @private
     * @since 3.5.0
     */
    start: function ()
    {
        this.events.once(SceneEvents.SHUTDOWN, this.shutdown, this);
    },

    /**
     * Force a sort of the display list on the next call to depthSort.
     *
     * @method Phaser.GameObjects.DisplayList#queueDepthSort
     * @since 3.0.0
     */
    queueDepthSort: function ()
    {
        this.sortChildrenFlag = true;
    },

    /**
     * Immediately sorts the display list if the flag is set.
     *
     * @method Phaser.GameObjects.DisplayList#depthSort
     * @since 3.0.0
     */
    depthSort: function ()
    {
        if (this.sortChildrenFlag)
        {
            StableSort(this.list, this.sortByDepth);

            this.sortChildrenFlag = false;
        }
    },

    /**
     * Compare the depth of two Game Objects.
     *
     * @method Phaser.GameObjects.DisplayList#sortByDepth
     * @since 3.0.0
     *
     * @param {Phaser.GameObjects.GameObject} childA - The first Game Object.
     * @param {Phaser.GameObjects.GameObject} childB - The second Game Object.
     *
     * @return {number} The difference between the depths of each Game Object.
     */
    sortByDepth: function (childA, childB)
    {
        return childA._depth - childB._depth;
    },

    /**
     * Returns an array which contains all objects currently on the Display List.
     * This is a reference to the main list array, not a copy of it, so be careful not to modify it.
     *
     * @method Phaser.GameObjects.DisplayList#getChildren
     * @since 3.12.0
     *
     * @return {Phaser.GameObjects.GameObject[]} The group members.
     */
    getChildren: function ()
    {
        return this.list;
    },

    /**
     * The Scene that owns this plugin is shutting down.
     * We need to kill and reset all internal properties as well as stop listening to Scene events.
     *
     * @method Phaser.GameObjects.DisplayList#shutdown
     * @private
     * @since 3.0.0
     */
    shutdown: function ()
    {
        var list = this.list;

        var i = list.length;

        while (i--)
        {
            list[i].destroy(true);
        }

        list.length = 0;

        this.events.off(SceneEvents.SHUTDOWN, this.shutdown, this);
    },

    /**
     * The Scene that owns this plugin is being destroyed.
     * We need to shutdown and then kill off all external references.
     *
     * @method Phaser.GameObjects.DisplayList#destroy
     * @private
     * @since 3.0.0
     */
    destroy: function ()
    {
        this.shutdown();

        this.events.off(SceneEvents.START, this.start, this);

        this.scene = null;
        this.systems = null;
        this.events = null;
    }

});

PluginCache.register('DisplayList', DisplayList, 'displayList');

module.exports = DisplayList;
<commit_msg>`DisplayList.addChildCallback` will now use the new `addToDisplayList` and `removeFromDisplayList` Game Object methods.<commit_after>/**
 * @author       Richard Davey <rich@photonstorm.com>
 * @copyright    2020 Photon Storm Ltd.
 * @license      {@link https://opensource.org/licenses/MIT|MIT License}
 */

var Class = require('../utils/Class');
var List = require('../structs/List');
var PluginCache = require('../plugins/PluginCache');
var GameObjectEvents = require('./events');
var SceneEvents = require('../scene/events');
var StableSort = require('../utils/array/StableSort');

/**
 * @classdesc
 * The Display List plugin.
 *
 * Display Lists belong to a Scene and maintain the list of Game Objects to render every frame.
 *
 * Some of these Game Objects may also be part of the Scene's [Update List]{@link Phaser.GameObjects.UpdateList}, for updating.
 *
 * @class DisplayList
 * @extends Phaser.Structs.List.<Phaser.GameObjects.GameObject>
 * @memberof Phaser.GameObjects
 * @constructor
 * @since 3.0.0
 *
 * @param {Phaser.Scene} scene - The Scene that this Display List belongs to.
 */
var DisplayList = new Class({

    Extends: List,

    initialize:

    function DisplayList (scene)
    {
        List.call(this, scene);

        /**
         * The flag the determines whether Game Objects should be sorted when `depthSort()` is called.
         *
         * @name Phaser.GameObjects.DisplayList#sortChildrenFlag
         * @type {boolean}
         * @default false
         * @since 3.0.0
         */
        this.sortChildrenFlag = false;

        /**
         * The Scene that this Display List belongs to.
         *
         * @name Phaser.GameObjects.DisplayList#scene
         * @type {Phaser.Scene}
         * @since 3.0.0
         */
        this.scene = scene;

        /**
         * The Scene's Systems.
         *
         * @name Phaser.GameObjects.DisplayList#systems
         * @type {Phaser.Scenes.Systems}
         * @since 3.0.0
         */
        this.systems = scene.sys;

        /**
         * The Scene's Event Emitter.
         *
         * @name Phaser.GameObjects.DisplayList#events
         * @type {Phaser.Events.EventEmitter}
         * @since 3.50.0
         */
        this.events = scene.sys.events;

        //  Set the List callbacks
        this.addCallback = this.addChildCallback;
        this.removeCallback = this.removeChildCallback;

        this.events.once(SceneEvents.BOOT, this.boot, this);
        this.events.on(SceneEvents.START, this.start, this);
    },

    /**
     * This method is called automatically, only once, when the Scene is first created.
     * Do not invoke it directly.
     *
     * @method Phaser.GameObjects.DisplayList#boot
     * @private
     * @since 3.5.1
     */
    boot: function ()
    {
        this.events.once(SceneEvents.DESTROY, this.destroy, this);
    },

    /**
     * Internal method called from `List.addCallback`.
     *
     * @method Phaser.GameObjects.DisplayList#addChildCallback
     * @private
     * @fires Phaser.Scenes.Events#ADDED_TO_SCENE
     * @fires Phaser.GameObjects.Events#ADDED_TO_SCENE
     * @since 3.50.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was added to the list.
     */
    addChildCallback: function (gameObject)
    {
        if (gameObject.displayList && gameObject.displayList !== this)
        {
            gameObject.removeFromDisplayList();
        }

        if (!gameObject.displayList)
        {
            this.queueDepthSort();

            gameObject.displayList = this;

            gameObject.emit(GameObjectEvents.ADDED_TO_SCENE, gameObject, this.scene);

            this.events.emit(SceneEvents.ADDED_TO_SCENE, gameObject, this.scene);
        }
    },

    /**
     * Internal method called from `List.removeCallback`.
     *
     * @method Phaser.GameObjects.DisplayList#removeChildCallback
     * @private
     * @fires Phaser.Scenes.Events#REMOVED_FROM_SCENE
     * @fires Phaser.GameObjects.Events#REMOVED_FROM_SCENE
     * @since 3.50.0
     *
     * @param {Phaser.GameObjects.GameObject} gameObject - The Game Object that was removed from the list.
     */
    removeChildCallback: function (gameObject)
    {
        this.queueDepthSort();

        gameObject.displayList = null;

        gameObject.emit(GameObjectEvents.REMOVED_FROM_SCENE, gameObject, this.scene);

        this.events.emit(SceneEvents.REMOVED_FROM_SCENE, gameObject, this.scene);
    },

    /**
     * This method is called automatically by the Scene when it is starting up.
     * It is responsible for creating local systems, properties and listening for Scene events.
     * Do not invoke it directly.
     *
     * @method Phaser.GameObjects.DisplayList#start
     * @private
     * @since 3.5.0
     */
    start: function ()
    {
        this.events.once(SceneEvents.SHUTDOWN, this.shutdown, this);
    },

    /**
     * Force a sort of the display list on the next call to depthSort.
     *
     * @method Phaser.GameObjects.DisplayList#queueDepthSort
     * @since 3.0.0
     */
    queueDepthSort: function ()
    {
        this.sortChildrenFlag = true;
    },

    /**
     * Immediately sorts the display list if the flag is set.
     *
     * @method Phaser.GameObjects.DisplayList#depthSort
     * @since 3.0.0
     */
    depthSort: function ()
    {
        if (this.sortChildrenFlag)
        {
            StableSort(this.list, this.sortByDepth);

            this.sortChildrenFlag = false;
        }
    },

    /**
     * Compare the depth of two Game Objects.
     *
     * @method Phaser.GameObjects.DisplayList#sortByDepth
     * @since 3.0.0
     *
     * @param {Phaser.GameObjects.GameObject} childA - The first Game Object.
     * @param {Phaser.GameObjects.GameObject} childB - The second Game Object.
     *
     * @return {number} The difference between the depths of each Game Object.
     */
    sortByDepth: function (childA, childB)
    {
        return childA._depth - childB._depth;
    },

    /**
     * Returns an array which contains all objects currently on the Display List.
     * This is a reference to the main list array, not a copy of it, so be careful not to modify it.
     *
     * @method Phaser.GameObjects.DisplayList#getChildren
     * @since 3.12.0
     *
     * @return {Phaser.GameObjects.GameObject[]} The group members.
     */
    getChildren: function ()
    {
        return this.list;
    },

    /**
     * The Scene that owns this plugin is shutting down.
     *
     * We need to kill and reset all internal properties as well as stop listening to Scene events.
     *
     * @method Phaser.GameObjects.DisplayList#shutdown
     * @private
     * @since 3.0.0
     */
    shutdown: function ()
    {
        var list = this.list;

        var i = list.length;

        while (i--)
        {
            list[i].destroy(true);
        }

        list.length = 0;

        this.events.off(SceneEvents.SHUTDOWN, this.shutdown, this);
    },

    /**
     * The Scene that owns this plugin is being destroyed.
     * We need to shutdown and then kill off all external references.
     *
     * @method Phaser.GameObjects.DisplayList#destroy
     * @private
     * @since 3.0.0
     */
    destroy: function ()
    {
        this.shutdown();

        this.events.off(SceneEvents.START, this.start, this);

        this.scene = null;
        this.systems = null;
        this.events = null;
    }

});

PluginCache.register('DisplayList', DisplayList, 'displayList');

module.exports = DisplayList;
<|endoftext|>