<commit_before>/*globals requirejs, expect*/
/* jshint browser: true, mocha: true*/
/**
 * @author pmeijer / https://github.com/pmeijer
 */

describe('branch status', function () {
    'use strict';
    var client,
        storage,
        project,
        core,
        logger,
        gmeConfig,
        originalCommitHash,
        currentBranchName,
        currentBranchHash,
        projectName = 'branchStatus',
        projectId;

    before(function (done) {
        requirejs([
            'js/client',
            'js/logger',
            'common/storage/browserstorage',
            'common/core/core',
            'text!gmeConfig.json'
        ], function (Client, Logger, Storage, Core, gmeConfigJSON) {
            gmeConfig = JSON.parse(gmeConfigJSON);
            logger = Logger.create('test:branchStatus', gmeConfig.client.log);
            client = new Client(gmeConfig);
            projectId = gmeConfig.authentication.guestAccount + client.CONSTANTS.STORAGE.PROJECT_ID_SEP +
                    projectName;
            client.connectToDatabase(function (err) {
                expect(err).to.equal(null);
                client.selectProject(projectId, null, function (err) {
                    expect(err).to.equal(null);

                    storage = Storage.getStorage(logger, gmeConfig, true);
                    storage.open(function (status) {
                        logger.debug('storage is open');
                        expect(status).to.equal(client.CONSTANTS.STORAGE.CONNECTED);

                        storage.openProject(projectId, function (err, project_, branches) {
                            expect(err).to.equal(null);

                            project = project_;
                            core = new Core(project, {
                                globConf: gmeConfig,
                                logger: logger.fork('core')
                            });

                            originalCommitHash = branches.master;

                            done();
                        });
                    });
                });
            });
        });
    });

    after(function (done) {
        storage.close(function (err) {
            client.disconnectFromDatabase(function (err2) {
                done(err || err2);
            });
        });
    });

    afterEach(function (done) {
        client.selectBranch('master', null, function (err) {
            client.deleteBranch(projectId, currentBranchName, currentBranchHash, function (err2) {
                done(err || err2);
            });
        });
    });

    function createSelectBranch(branchName, callback) {
        client.createBranch(projectId, branchName, originalCommitHash, function (err) {
            expect(err).to.equal(null);
            client.selectBranch(branchName, null, callback);
        });
    }

    function changeRootNodeName(newName, commitHash, msg, callback) {
        // 1. Load commit-hash
        project.loadObject(commitHash, function (err, commitObject) {
            expect(err).to.equal(null);
            expect(typeof commitObject.root).to.equal('string');
            expect(commitObject.root.indexOf('#')).to.equal(0);
            // 2. Load root-node
            core.loadRoot(commitObject.root, function (err, rootNode) {
                var persisted;
                expect(err).to.equal(null);

                // 3. Modify the name and persist
                core.setAttribute(rootNode, 'name', newName);
                persisted = core.persist(rootNode);

                // 4. Make the commit w/o setting the branch-hash.
                project.makeCommit(null, [commitHash], persisted.rootHash, persisted.objects, msg,
                    function (err, commitResult) {
                        expect(err).to.equal(null);
                        expect(typeof commitResult.hash).to.equal('string');
                        expect(commitResult.hash.indexOf('#')).to.equal(0);

                        // 5. Return the new commit-hash.
                        callback(null, commitResult.hash);
                    }
                );
            });
        });
    }

    it('should go from SYNC to AHEAD_SYNC to SYNC when making changes.', function (done) {
        var branchName = 'sync_aheadSync_sync',
            prevStatus;

        currentBranchName = branchName;

        function removeHandler() {
            client.removeEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);
        }

        function eventHandler(__client, eventData) {
            if (prevStatus === client.CONSTANTS.BRANCH_STATUS.SYNC) {
                expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC);
                prevStatus = eventData.status;
            } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC) {
                expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);
                removeHandler();
                currentBranchHash = client.getActiveCommitHash();
                done();
            } else {
                removeHandler();
                done(new Error('Unexpected BranchStatus ' + eventData.status));
            }
        }

        createSelectBranch(branchName, function (err) {
            expect(err).to.equal(null);

            prevStatus = client.getBranchStatus();
            expect(prevStatus).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);

            client.addEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);

            var loaded = false,
                userGuid;

            function nodeEventHandler(events) {
                if (loaded) {
                    done(new Error('More than one event'));
                } else {
                    loaded = true;
                    expect(events.length).to.equal(2);
                    client.removeUI(userGuid);

                    client.setAttributes('', 'name', 'newRootName',
                        'should go from SYNC to AHEAD_SYNC to SYNC when making changes.');
                }
            }

            userGuid = client.addUI({}, nodeEventHandler);
            client.updateTerritory(userGuid, {'': {children: 0}});
        });
    });

    it('should go from SYNC to PULLING to SYNC when external changes are made.', function (done) {
        var branchName = 'sync_pull_sync',
            prevStatus;

        currentBranchName = branchName;

        function removeHandler() {
            client.removeEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);
        }

        function eventHandler(__client, eventData) {
            if (prevStatus === client.CONSTANTS.BRANCH_STATUS.SYNC) {
                expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.PULLING);
                prevStatus = eventData.status;
            } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.PULLING) {
                expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);
                removeHandler();
                currentBranchHash = client.getActiveCommitHash();
                done();
            } else {
                removeHandler();
                done(new Error('Unexpected BranchStatus' + eventData.status));
            }
        }

        createSelectBranch(branchName, function (err) {
            expect(err).to.equal(null);

            var currentHash = client.getActiveCommitHash(),
                msg = 'should go from SYNC to PULLING to SYNC when external changes are made.';

            prevStatus = client.getBranchStatus();
            expect(prevStatus).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);
            client.addEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);

            changeRootNodeName('newRootName', currentHash, msg, function (err, newHash) {
                expect(err).to.equal(null);
                project.setBranchHash(branchName, newHash, currentHash,
                    function (err, result) {
                        expect(err).to.equal(null);
                        expect(result.status).to.equal(client.CONSTANTS.STORAGE.SYNCED);
                    }
                );
            });
        });
    });

    it('should go from SYNC to AHEAD_SYNC to AHEAD_NOT_SYNC when making changes during external changes.',
        function (done) {
            var branchName = 'sync_aheadSync_aheadNotSync',
                currentHash,
                prevStatus;

            currentBranchName = branchName;

            function removeHandler() {
                client.removeEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);
            }

            function eventHandler(__client, eventData) {
                if (prevStatus === client.CONSTANTS.BRANCH_STATUS.SYNC) {
                    // 1) Here it starts pulling the external changes..
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.PULLING);
                    prevStatus = eventData.status;
                } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.PULLING) {
                    // 2) It starts with its own commit and disregards the pulling
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC);
                    prevStatus = eventData.status;
                } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC) {
                    // 3) When the commit returns its forked and the client is not in sync.
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_NOT_SYNC);
                    removeHandler();
                    currentBranchHash = client.getActiveCommitHash();
                    done();
                } else {
                    removeHandler();
                    done(new Error('Unexpected BranchStatus ' + eventData.status));
                }
            }

            createSelectBranch(branchName, function (err) {
                expect(err).to.equal(null);

                prevStatus = client.getBranchStatus();
                expect(prevStatus).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);

                currentHash = client.getActiveCommitHash();

                client.addEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);

                var loaded = false,
                    userGuid;

                function nodeEventHandler(events) {
                    if (loaded) {
                        done(new Error('More than one event'));
                    } else {
                        loaded = true;
                        expect(events.length).to.equal(2);
                        client.removeUI(userGuid);
                        changeRootNodeName('newConflictRootName', currentHash, 'externalChange',
                            function (err, newHash) {
                                expect(err).to.equal(null);
                                client.startTransaction('sync_aheadSync_aheadNotSync__start');
                                project.setBranchHash(branchName, newHash, currentHash,
                                    function (err, result) {
                                        expect(err).to.equal(null);
                                        expect(result.status).to.equal(client.CONSTANTS.STORAGE.SYNCED);
                                        client.setAttributes('', 'name', 'newRootyName', 'conflicting change');
                                        client.completeTransaction('sync_aheadSync_aheadNotSync__stop');
                                    }
                                );

                            }
                        );
                    }
                }

                userGuid = client.addUI({}, nodeEventHandler);
                client.updateTerritory(userGuid, {'': {children: 0}});
            });
        }
    );

    it('should go from AHEAD_NOT_SYNC to AHEAD_NOT_SYNC and increase commitQueue when making more changes.',
        function (done) {
            var branchName = 'sync_aheadSync_aheadNotSync_aheadNotSync',
                currentHash,
                prevStatus;

            currentBranchName = branchName;

            function removeHandler() {
                client.removeEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);
            }

            function eventHandler(__client, eventData) {
                if (prevStatus === client.CONSTANTS.BRANCH_STATUS.SYNC) {
                    prevStatus = eventData.status;
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC);
                } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC) {
                    prevStatus = eventData.status;
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_NOT_SYNC);
                    expect(eventData.details.length).to.equal(1);
                    client.setAttributes('', 'name', 'newRootyName2Test2', 'change when ahead not sync');
                } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.AHEAD_NOT_SYNC) {
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_NOT_SYNC);
                    expect(eventData.details.length).to.equal(2);
                    removeHandler();
                    currentBranchHash = client.getActiveCommitHash();
                    done();
                } else {
                    removeHandler();
                    done(new Error('Unexpected BranchStatus ' + eventData.status));
                }
            }

            createSelectBranch(branchName, function (err) {
                expect(err).to.equal(null);

                prevStatus = client.getBranchStatus();
                expect(prevStatus).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);

                currentHash = client.getActiveCommitHash();

                client.addEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);

                var loaded = false,
                    userGuid;

                function nodeEventHandler(events) {
                    if (loaded) {
                        done(new Error('More than one event'));
                    } else {
                        loaded = true;
                        expect(events.length).to.equal(2);
                        client.removeUI(userGuid);
                        changeRootNodeName('newConflictRootName', currentHash, 'externalChange',
                            function (err, newHash) {
                                expect(err).to.equal(null);
                                client.startTransaction('sync_aheadSync_aheadNotSync_aheadNotSync__start');
                                project.setBranchHash(branchName, newHash, currentHash,
                                    function (err, result) {
                                        expect(err).to.equal(null);
                                        expect(result.status).to.equal(client.CONSTANTS.STORAGE.SYNCED);
                                        client.setAttributes('', 'name', 'newRootyNameTest2', 'conflicting change');
                                        client.completeTransaction('sync_aheadSync_aheadNotSync_aheadNotSync__stop');
                                    }
                                );

                            }
                        );
                    }
                }

                userGuid = client.addUI({}, nodeEventHandler);
                client.updateTerritory(userGuid, {'': {children: 0}});
            });
        }
    );
});<commit_msg>#461 branchstatus.spec.js works<commit_after>/*globals requirejs, expect*/
/* jshint browser: true, mocha: true*/
/**
 * @author pmeijer / https://github.com/pmeijer
 */

describe('branch status', function () {
    'use strict';
    var client,
        storage,
        project,
        core,
        logger,
        gmeConfig,
        originalCommitHash,
        currentBranchName,
        currentBranchHash,
        projectName = 'branchStatus',
        projectId;

    before(function (done) {
        requirejs([
            'js/client',
            'js/logger',
            'common/storage/browserstorage',
            'common/core/core',
            'text!gmeConfig.json'
        ], function (Client, Logger, Storage, Core, gmeConfigJSON) {
            gmeConfig = JSON.parse(gmeConfigJSON);
            logger = Logger.create('test:branchStatus', gmeConfig.client.log);
            client = new Client(gmeConfig);
            projectId = gmeConfig.authentication.guestAccount + client.CONSTANTS.STORAGE.PROJECT_ID_SEP +
                    projectName;
            client.connectToDatabase(function (err) {
                expect(err).to.equal(null);
                client.selectProject(projectId, null, function (err) {
                    expect(err).to.equal(null);

                    storage = Storage.getStorage(logger, gmeConfig, true);
                    storage.open(function (status) {
                        logger.debug('storage is open');
                        expect(status).to.equal(client.CONSTANTS.STORAGE.CONNECTED);

                        storage.openProject(projectId, function (err, project_, branches) {
                            expect(err).to.equal(null);

                            project = project_;
                            core = new Core(project, {
                                globConf: gmeConfig,
                                logger: logger.fork('core')
                            });

                            originalCommitHash = branches.master;

                            done();
                        });
                    });
                });
            });
        });
    });

    after(function (done) {
        storage.close(function (err) {
            client.disconnectFromDatabase(function (err2) {
                done(err || err2);
            });
        });
    });

    afterEach(function (done) {
        client.selectBranch('master', null, function (err) {
            client.deleteBranch(projectId, currentBranchName, currentBranchHash, function (err2) {
                done(err || err2);
            });
        });
    });

    function createSelectBranch(branchName, callback) {
        client.createBranch(projectId, branchName, originalCommitHash, function (err) {
            expect(err).to.equal(null);
            client.selectBranch(branchName, null, callback);
        });
    }

    function changeRootNodeName(newName, commitHash, msg, callback) {
        // 1. Load commit-hash
        project.loadObject(commitHash, function (err, commitObject) {
            expect(err).to.equal(null);
            expect(typeof commitObject.root).to.equal('string');
            expect(commitObject.root.indexOf('#')).to.equal(0);
            // 2. Load root-node
            core.loadRoot(commitObject.root, function (err, rootNode) {
                var persisted;
                expect(err).to.equal(null);

                // 3. Modify the name and persist
                core.setAttribute(rootNode, 'name', newName);
                persisted = core.persist(rootNode);

                // 4. Make the commit w/o setting the branch-hash.
                project.makeCommit(null, [commitHash], persisted.rootHash, persisted.objects, msg,
                    function (err, commitResult) {
                        expect(err).to.equal(null);
                        expect(typeof commitResult.hash).to.equal('string');
                        expect(commitResult.hash.indexOf('#')).to.equal(0);

                        // 5. Return the new commit-hash.
                        callback(null, commitResult.hash);
                    }
                );
            });
        });
    }

    it('should go from SYNC to AHEAD_SYNC to SYNC when making changes.', function (done) {
        var branchName = 'sync_aheadSync_sync',
            prevStatus;

        currentBranchName = branchName;

        function removeHandler() {
            client.removeEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);
        }

        function eventHandler(__client, eventData) {
            if (prevStatus === client.CONSTANTS.BRANCH_STATUS.SYNC) {
                expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC);
                prevStatus = eventData.status;
            } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC) {
                expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);
                removeHandler();
                currentBranchHash = client.getActiveCommitHash();
                done();
            } else {
                removeHandler();
                done(new Error('Unexpected BranchStatus ' + eventData.status));
            }
        }

        createSelectBranch(branchName, function (err) {
            expect(err).to.equal(null);

            prevStatus = client.getBranchStatus();
            expect(prevStatus).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);

            client.addEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);

            var loaded = false,
                userGuid;

            function nodeEventHandler(events) {
                if (loaded) {
                    done(new Error('More than one event'));
                } else {
                    loaded = true;
                    expect(events.length).to.equal(2);
                    client.removeUI(userGuid);

                    client.setAttributes('', 'name', 'newRootName',
                        'should go from SYNC to AHEAD_SYNC to SYNC when making changes.');
                }
            }

            userGuid = client.addUI({}, nodeEventHandler);
            client.updateTerritory(userGuid, {'': {children: 0}});
        });
    });

    it('should go from SYNC to PULLING to SYNC when external changes are made.', function (done) {
        var branchName = 'sync_pull_sync',
            prevStatus;

        currentBranchName = branchName;

        function removeHandler() {
            client.removeEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);
        }

        function eventHandler(__client, eventData) {
            if (prevStatus === client.CONSTANTS.BRANCH_STATUS.SYNC) {
                expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.PULLING);
                prevStatus = eventData.status;
            } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.PULLING) {
                expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);
                removeHandler();
                currentBranchHash = client.getActiveCommitHash();
                done();
            } else {
                removeHandler();
                done(new Error('Unexpected BranchStatus' + eventData.status));
            }
        }

        createSelectBranch(branchName, function (err) {
            expect(err).to.equal(null);

            var currentHash = client.getActiveCommitHash(),
                msg = 'should go from SYNC to PULLING to SYNC when external changes are made.';

            prevStatus = client.getBranchStatus();
            expect(prevStatus).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);
            client.addEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);

            changeRootNodeName('newRootName', currentHash, msg, function (err, newHash) {
                expect(err).to.equal(null);
                project.setBranchHash(branchName, newHash, currentHash,
                    function (err, result) {
                        expect(err).to.equal(null);
                        expect(result.status).to.equal(client.CONSTANTS.STORAGE.SYNCED);
                    }
                );
            });
        });
    });

    it('should go from SYNC to AHEAD_SYNC to AHEAD_NOT_SYNC when making changes during external changes.',
        function (done) {
            var branchName = 'sync_aheadSync_aheadNotSync',
                currentHash,
                prevStatus;

            currentBranchName = branchName;

            function removeHandler() {
                client.removeEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);
            }

            function eventHandler(__client, eventData) {
                if (prevStatus === client.CONSTANTS.BRANCH_STATUS.SYNC) {
                    // 1) Here it starts pulling the external changes..
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.PULLING);
                    prevStatus = eventData.status;
                } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.PULLING) {
                    // 2) It starts with its own commit and disregards the pulling
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC);
                    prevStatus = eventData.status;
                } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC) {
                    // 3) When the commit returns its forked and the client is not in sync.
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_NOT_SYNC);
                    removeHandler();
                    currentBranchHash = client.getActiveCommitHash();
                    done();
                } else {
                    removeHandler();
                    done(new Error('Unexpected BranchStatus ' + eventData.status));
                }
            }

            createSelectBranch(branchName, function (err) {
                expect(err).to.equal(null);

                prevStatus = client.getBranchStatus();
                expect(prevStatus).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);

                currentHash = client.getActiveCommitHash();

                client.addEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);

                var loaded = false,
                    userGuid;

                function nodeEventHandler(events) {
                    if (loaded) {
                        done(new Error('More than one event'));
                    } else {
                        loaded = true;
                        expect(events.length).to.equal(2);
                        client.removeUI(userGuid);
                        changeRootNodeName('newConflictRootName', currentHash, 'externalChange',
                            function (err, newHash) {
                                expect(err).to.equal(null);
                                client.startTransaction('sync_aheadSync_aheadNotSync__start');
                                project.setBranchHash(branchName, newHash, currentHash,
                                    function (err, result) {
                                        expect(err).to.equal(null);
                                        expect(result.status).to.equal(client.CONSTANTS.STORAGE.SYNCED);
                                        client.setAttributes('', 'name', 'newRootyName', 'conflicting change');
                                        client.completeTransaction('sync_aheadSync_aheadNotSync__stop');
                                    }
                                );

                            }
                        );
                    }
                }

                userGuid = client.addUI({}, nodeEventHandler);
                client.updateTerritory(userGuid, {'': {children: 0}});
            });
        }
    );

    it('should go from AHEAD_NOT_SYNC to AHEAD_NOT_SYNC and increase commitQueue when making more changes.',
        function (done) {
            var branchName = 'sync_aheadSync_aheadNotSync_aheadNotSync',
                currentHash,
                prevStatus;

            currentBranchName = branchName;

            function removeHandler() {
                client.removeEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);
            }

            function eventHandler(__client, eventData) {
                if (prevStatus === client.CONSTANTS.BRANCH_STATUS.SYNC) {
                    prevStatus = eventData.status;
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.PULLING);
                } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.PULLING) {
                    prevStatus = eventData.status;
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC);
                } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.AHEAD_SYNC) {
                    prevStatus = eventData.status;
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_NOT_SYNC);
                    expect(eventData.commitQueue.length).to.equal(1);
                    client.setAttributes('', 'name', 'newRootyName2Test2', 'change when ahead not sync');
                } else if (prevStatus === client.CONSTANTS.BRANCH_STATUS.AHEAD_NOT_SYNC) {
                    expect(eventData.status).to.equal(client.CONSTANTS.BRANCH_STATUS.AHEAD_NOT_SYNC);
                    expect(eventData.commitQueue.length).to.equal(2);
                    removeHandler();
                    currentBranchHash = client.getActiveCommitHash();
                    done();
                } else {
                    removeHandler();
                    done(new Error('Unexpected BranchStatus ' + eventData.status));
                }
            }

            createSelectBranch(branchName, function (err) {
                expect(err).to.equal(null);

                prevStatus = client.getBranchStatus();
                expect(prevStatus).to.equal(client.CONSTANTS.BRANCH_STATUS.SYNC);

                currentHash = client.getActiveCommitHash();

                client.addEventListener(client.CONSTANTS.BRANCH_STATUS_CHANGED, eventHandler);

                var loaded = false,
                    userGuid;

                function nodeEventHandler(events) {
                    if (loaded) {
                        done(new Error('More than one event'));
                    } else {
                        loaded = true;
                        expect(events.length).to.equal(2);
                        client.removeUI(userGuid);
                        changeRootNodeName('newConflictRootName', currentHash, 'externalChange',
                            function (err, newHash) {
                                expect(err).to.equal(null);
                                client.startTransaction('sync_aheadSync_aheadNotSync_aheadNotSync__start');
                                project.setBranchHash(branchName, newHash, currentHash,
                                    function (err, result) {
                                        expect(err).to.equal(null);
                                        expect(result.status).to.equal(client.CONSTANTS.STORAGE.SYNCED);
                                        client.setAttributes('', 'name', 'newRootyNameTest2', 'conflicting change');
                                        client.completeTransaction('sync_aheadSync_aheadNotSync_aheadNotSync__stop');
                                    }
                                );

                            }
                        );
                    }
                }

                userGuid = client.addUI({}, nodeEventHandler);
                client.updateTerritory(userGuid, {'': {children: 0}});
            });
        }
    );
});<|endoftext|>


<commit_before>var cdb = require('cartodb.js');
var DatasetsItem = require('new_dashboard/datasets/datasets_item');
var MapsItem = require('new_dashboard/maps/maps_item');
var RemoteDatasetsItem = require('new_dashboard/datasets/remote_datasets_item');

/**
 * View representing the list of items
 */
module.exports = cdb.core.View.extend({

  tagName: 'ul',

  events: {},

  _ITEMS: {
    'remotes':  RemoteDatasetsItem,
    'datasets': DatasetsItem,
    'maps':     MapsItem
  },

  initialize: function() {
    this.router = this.options.router;
    this.user = this.options.user;
    this.collection.bind('remove', this.render, this);

    this._initBinds();
  },

  render: function() {
    this.clearSubViews();
    this.$el.attr('class', this.router.model.get('content_type') === 'datasets' ? 'DatasetsList' : 'MapsList');
    this.collection.each(this._addItem, this);
    return this;
  },

  _addItem: function(m, i) {
    var type = (this.router.model.get('library') && 'remotes') || this.router.model.get('content_type');
    var item = new this._ITEMS[type]({
      model:  m,
      router: this.router,
      user:   this.user
    });
    item.bind('remoteSelected', function(d){
      this.trigger('remoteSelected', d, this);
    }, this)

    this.addView(item);
    this.$el.append(item.render().el);
  },

  _initBinds: function() {
    this.collection.on('reset', this.render, this);
    this.add_related_model(this.collection);
  },

  show: function() {
    this.$el.removeClass('is-hidden');
  },

  hide: function() {
    this.$el.addClass('is-hidden');
  }

});
<commit_msg>Moved bind to _initBinds method<commit_after>var cdb = require('cartodb.js');
var DatasetsItem = require('new_dashboard/datasets/datasets_item');
var MapsItem = require('new_dashboard/maps/maps_item');
var RemoteDatasetsItem = require('new_dashboard/datasets/remote_datasets_item');

/**
 * View representing the list of items
 */
module.exports = cdb.core.View.extend({

  tagName: 'ul',

  events: {},

  _ITEMS: {
    'remotes':  RemoteDatasetsItem,
    'datasets': DatasetsItem,
    'maps':     MapsItem
  },

  initialize: function() {
    this.router = this.options.router;
    this.user = this.options.user;
    this._initBinds();
  },

  render: function() {
    this.clearSubViews();
    this.$el.attr('class', this.router.model.get('content_type') === 'datasets' ? 'DatasetsList' : 'MapsList');
    this.collection.each(this._addItem, this);
    return this;
  },

  _addItem: function(m, i) {
    var type = (this.router.model.get('library') && 'remotes') || this.router.model.get('content_type');
    var item = new this._ITEMS[type]({
      model:  m,
      router: this.router,
      user:   this.user
    });
    item.bind('remoteSelected', function(d){
      this.trigger('remoteSelected', d, this);
    }, this)

    this.addView(item);
    this.$el.append(item.render().el);
  },

  _initBinds: function() {
    this.collection.bind('reset remove', this.render, this);
    this.add_related_model(this.collection);
  },

  show: function() {
    this.$el.removeClass('is-hidden');
  },

  hide: function() {
    this.$el.addClass('is-hidden');
  }

});
<|endoftext|>


<commit_before>// Only use native node.js API's and references to ./lib here, this file is not transpiled!
//
// Note that if this tree-shaking test fails, there is probably
// new functionality which forces Webpack to turn off tree shaking.
//
// Typical solutions to get tree-shaking working again are:
//
// - move code into a separate file to isolate it
// - add /* #__PURE__ */ when creating a variable

const path = require('path')
const cp = require('child_process')
const assert = require('assert')
const del = require('del')
const webpack = require('webpack')

describe('tree shaking', function () {
  const appName = 'treeShakingApp.js'
  const bundleName = 'treeShakingApp.bundle.js'

  before(() => {
    cleanup()
  })

  after(() => {
    cleanup()
  })

  function cleanup () {
    del.sync(path.join(__dirname, bundleName))
  }

  it('should apply tree-shaking when bundling', function (done) {
    // This test takes a few seconds
    this.timeout(10000)

    const webpackConfig = {
      entry: path.join(__dirname, appName),
      mode: 'production',
      output: {
        filename: bundleName,
        path: __dirname
      }
    }

    webpack(webpackConfig).run(function (err, stats) {
      if (err) {
        console.error(err)
        done(err)
        return
      }

      const info = stats.toJson()
      if (stats.hasErrors()) {
        console.error('Webpack errors:\n' + info.errors.join('\n'))
        done(new Error('Compile failed'))
        return
      }

      // Test whether the size is small enough
      // At this moment, the full library size is 559137 bytes (unzipped),
      // and the size of this tree-shaken bundle is 98494 bytes (unzipped)
      // this may grow or shrink in the future
      assert.strictEqual(info.assets[0].name, bundleName)
      const size = info.assets[0].size
      const maxSize = 100000
      assert(size < maxSize,
        'bundled size must be small enough ' +
        '(actual size: ' + size + ' bytes, max size: ' + maxSize + ' bytes)')

      // Execute the bundle to test whether it actually works
      cp.exec('node ' + path.join(__dirname, bundleName), function (err, result) {
        if (err) {
          done(err)
          return
        }

        assert.strictEqual(result.replace(/\s/g, ''), '3')

        done()
      })
    })
  })
})
<commit_msg>Larger timeout for tree-shaking unit test<commit_after>// Only use native node.js API's and references to ./lib here, this file is not transpiled!
//
// Note that if this tree-shaking test fails, there is probably
// new functionality which forces Webpack to turn off tree shaking.
//
// Typical solutions to get tree-shaking working again are:
//
// - move code into a separate file to isolate it
// - add /* #__PURE__ */ when creating a variable

const path = require('path')
const cp = require('child_process')
const assert = require('assert')
const del = require('del')
const webpack = require('webpack')

describe('tree shaking', function () {
  const appName = 'treeShakingApp.js'
  const bundleName = 'treeShakingApp.bundle.js'

  before(() => {
    cleanup()
  })

  after(() => {
    cleanup()
  })

  function cleanup () {
    del.sync(path.join(__dirname, bundleName))
  }

  it('should apply tree-shaking when bundling', function (done) {
    // This test takes a few seconds
    this.timeout(60 * 1000)

    const webpackConfig = {
      entry: path.join(__dirname, appName),
      mode: 'production',
      output: {
        filename: bundleName,
        path: __dirname
      }
    }

    webpack(webpackConfig).run(function (err, stats) {
      if (err) {
        console.error(err)
        done(err)
        return
      }

      const info = stats.toJson()
      if (stats.hasErrors()) {
        console.error('Webpack errors:\n' + info.errors.join('\n'))
        done(new Error('Compile failed'))
        return
      }

      // Test whether the size is small enough
      // At this moment, the full library size is 559137 bytes (unzipped),
      // and the size of this tree-shaken bundle is 98494 bytes (unzipped)
      // this may grow or shrink in the future
      assert.strictEqual(info.assets[0].name, bundleName)
      const size = info.assets[0].size
      const maxSize = 100000
      assert(size < maxSize,
        'bundled size must be small enough ' +
        '(actual size: ' + size + ' bytes, max size: ' + maxSize + ' bytes)')

      // Execute the bundle to test whether it actually works
      cp.exec('node ' + path.join(__dirname, bundleName), function (err, result) {
        if (err) {
          done(err)
          return
        }

        assert.strictEqual(result.replace(/\s/g, ''), '3')

        done()
      })
    })
  })
})
<|endoftext|>


<commit_before>
describe("table-tags", function() {
  var tags
    , tables
    , user
    , tagsView;

  beforeEach(function() {
    tables = new cdb.admin.Tables();
    user = new cdb.admin.User({ id : "1" });
    tags = new cdb.admin.Tags();

    tagsView = new cdb.admin.dashboard.TagsView({
      tables: tables,
      model: tags
    });

    tagsView.model.fetch = function() { }

    this.server = sinon.fakeServer.create();
    var tableArray = [];
    for(var i = 0; i < _TABLES_PER_PAGE; i++) {
      tableArray.push({id: i, name: 'test'+i, privacy: 'PRIVATE', rows_counted: 1, updated_at: new Date(), tags: 'a', table_size: 100, description: 'test'});
    }

    this.server.respondWith("GET", "irrelevant.json",
                                [200, { "Content-Type": "application/json" },
                                 '{"total_entries":'+(_TABLES_PER_PAGE+2)+', "tables": '+JSON.stringify(tableArray)+'}']);

  });

  it("should update table tags when tables model is updated from server", function() {
    spyOn(tagsView.model, 'fetch');
    tables.trigger('reset')
    expect(tagsView.model.fetch).toHaveBeenCalled();
  });

  it("should update user tags when a table is removed", function() {
    spyOn(tagsView.model, 'fetch');
    tables.trigger('remove')

    expect(tagsView.model.fetch).toHaveBeenCalled();
  });
});
<commit_msg>added missing taglist tests<commit_after>
describe("table-tags", function() {
  var tags
    , tables
    , user
    , tagsView;

  beforeEach(function() {
    tables = new cdb.admin.Tables();
    user = new cdb.admin.User({ id : "1" });
    tags = new cdb.admin.Tags();

    tagsView = new cdb.admin.dashboard.TagsView({
      tables: tables,
      model: tags
    });

    tagsView.model.fetch = function() { }

    this.server = sinon.fakeServer.create();
    var tableArray = [];
    for(var i = 0; i < _TABLES_PER_PAGE; i++) {
      tableArray.push({id: i, name: 'test'+i, privacy: 'PRIVATE', rows_counted: 1, updated_at: new Date(), tags: 'a', table_size: 100, description: 'test'});
    }

    this.server.respondWith("GET", "irrelevant.json",
                                [200, { "Content-Type": "application/json" },
                                 '{"total_entries":'+(_TABLES_PER_PAGE+2)+', "tables": '+JSON.stringify(tableArray)+'}']);

  });

  it("should update table tags when tables model is updated from server", function() {
    spyOn(tagsView.model, 'fetch');
    tables.trigger('reset')
    expect(tagsView.model.fetch).toHaveBeenCalled();
  });

  it("should update user tags when a table is removed", function() {
    spyOn(tagsView.model, 'fetch');
    tables.trigger('remove')

    expect(tagsView.model.fetch).toHaveBeenCalled();
  });

  it("should render", function() {
    var container = $('

');
    container.appendTo($("body"));
    tagsView.el = container;
    tagsView.$el = $(container);
    tagsView.render();
    expect(container.html()).toBeTruthy();
    container.remove();
  });
  it("should not render 'view_all' button if there aren't enough tags", function() {
    var container = $('

');
    container.appendTo($("body"));
    tagsView.el = container;
    tagsView.$el = $(container);

    tagsView.render();

    expect(container.find('li.view_all a').length).toBeFalsy()
    container.remove();
  })

});
<|endoftext|>


<commit_before>var config = require('../../../config/secrets').facebook;
var ContentService = require('../content-service');
var graph = require('fbgraph');
var util = require('util');
var _ = require('underscore');

/*
 * Facebook Content Service constructor
 * @param options - (lastReportDate - Timestamp(optional),  url:String(required))
 */
var FacebookContentService = function(options) {
  graph.setAccessToken(config.accessToken);
  this.url = options.url;
  this.lastReportDate = Math.floor(options.lastReportDate  / 1000) || null;
  this.sourceType = 'facebook';
  this.botType = 'pull';
  ContentService.call(this, options);
};

util.inherits(FacebookContentService, ContentService);

// Fetch from the Facebook Content Service
FacebookContentService.prototype.fetch = function(callback) {
  callback = callback || function() {};
  var self = this;

  if (!this.url) {
    process.nextTick(function() {
      self._handleErrors(new Error('Missing Facebook URL'));
    });
    return;
  }

  var queries = {};

  // Determine Source ID of object
  queries.source = 'SELECT id, site, type, url FROM object_url WHERE url = "' + this.url + '"';

  // Fetch posts and comments
  queries.posts = 'SELECT source_id, post_id, updated_time, created_time, actor_id, permalink, message, comments.comment_list';
  queries.posts += ' FROM stream';
  queries.posts += ' WHERE source_id IN (SELECT id FROM #source)';
  // New comments trigger a fresh updated_time in its parent post
  if (this.lastReportDate) queries.posts += ' AND updated_time > ' + this.lastReportDate;
  queries.posts += ' ORDER BY updated_time';

  // Get author names
  queries.author = 'SELECT id, name FROM profile WHERE id IN (SELECT actor_id FROM #posts) OR id IN (SELECT comments.comment_list.fromid FROM #posts)';

  graph.fql(queries, function(err, res) {
    if (err) self._handleErrors(err);
    if (res.data && res.data.length) {
      res.data.forEach(function(data) {
        switch (data.name) {
          case 'source':
            if (data.fql_result_set.length === 0) self._handleErrors(new Error('Invalid Facebook URL'));
            break;
          case 'posts':
            var authors = _.findWhere(res.data, {name: 'author'}).fql_result_set;
            if (data.fql_result_set.length) self._handleResults(data.fql_result_set, authors);
            callback(null, self.lastReportDate * 1000);
            break;
        }
      });
    }
  });
};

// Handle errors
FacebookContentService.prototype._handleErrors = function(err) {
  if (err.exception) {
    // FBGraph module error
    this.emit('error', err.exception);
  } else if (err.error && (err.error.type === 0 || err.error.type === 1)) {
    // 1 API Unknown | 2 API Service warnings
    this.emit('warning', new Error('Facebook error: #{' + err.error.code + '} - #{' + err.error.type + '}: #{' + err.error.message + '}. Will try again.'));
  } else if (err.error) {
    // Catch any other Facebook API errors
    this.emit('error', new Error('Facebook error: #{' + err.error.code + '} (#{' + err.error.error_subcode +
      '}) - #{' + err.error.type + '}: #{' + err.error.message + '}'));
  } else {
    // Catch any other type of error
    this.emit('error', err);
  }
};

// Handle data returned by the FQL query
FacebookContentService.prototype._handleResults = function(data, authors) {
  var self = this;
  var lastReportDate = 0;
  var reports = [];
  // Handle each post
  data.forEach(function(post) {
    // `updated_time` only gets refreshed when posting a new comment. Use these
    // reports only when they are new or from a newly added Source.
    if (!self.lastReportDate || post.created_time === post.updated_time) {
      lastReportDate = Math.max(lastReportDate, post.updated_time);
      var report_data = self._parse(post, {type: 'post', authors: authors});
      reports.push(report_data);
    }
    if (post.comments.comment_list.length) {
      // Handle each comment
      post.comments.comment_list.forEach(function(comment) {
        // Skip old comments
        if (comment.time > self.lastReportDate) {
          lastReportDate = Math.max(lastReportDate, comment.time);
          var report_data = self._parse(comment, {type: 'comment', post: post, authors: authors});
          reports.push(report_data);
        }
      });
    }
  });
  // Comments are loaded as children of posts, sort after the fact to ensure proper chronology
  reports = _.sortBy(reports, 'authoredAt').forEach(function(report_data) {;
    self.emit('report', report_data);
  });
  this.lastReportDate = lastReportDate;
};

// Parse each fql result into our data format
FacebookContentService.prototype._parse = function(data, options) {
  switch (options.type) {
    case 'post':
      return {
        authoredAt: data.updated_time * 1000,
        fetchedAt: Date.now(),
        content: data.message,
        author: this._findAuthor(data.actor_id, options.authors),
        url: data.permalink
      };
    case 'comment':
      return {
        authoredAt: data.time * 1000,
        fetchedAt: Date.now(),
        content: data.text,
        author: this._findAuthor(data.fromid, options.authors),
        url: this._buildCommentUrl(data.id, options.post)
      };
  }
};

// Determine the author of a report
FacebookContentService.prototype._findAuthor = function(id, authors) {
  var author = _.findWhere(authors, {id: id});
  if (author) return author.name;
  return id;
};

// Builder function for making a comment url for posts
FacebookContentService.prototype._buildCommentUrl = function(commentId, post) {
  var id = commentId.split('_');
  if (/\?/.test(post.permalink)) {
    return post.permalink + '&comment_id=' + id[2];
  } else {
    return post.permalink + '?comment_id=' + id[2];
  }
};

module.exports = FacebookContentService;
<commit_msg>Verify that variable exists before looping<commit_after>var config = require('../../../config/secrets').facebook;
var ContentService = require('../content-service');
var graph = require('fbgraph');
var util = require('util');
var _ = require('underscore');

/*
 * Facebook Content Service constructor
 * @param options - (lastReportDate - Timestamp(optional),  url:String(required))
 */
var FacebookContentService = function(options) {
  graph.setAccessToken(config.accessToken);
  this.url = options.url;
  this.lastReportDate = Math.floor(options.lastReportDate  / 1000) || null;
  this.sourceType = 'facebook';
  this.botType = 'pull';
  ContentService.call(this, options);
};

util.inherits(FacebookContentService, ContentService);

// Fetch from the Facebook Content Service
FacebookContentService.prototype.fetch = function(callback) {
  callback = callback || function() {};
  var self = this;

  if (!this.url) {
    process.nextTick(function() {
      self._handleErrors(new Error('Missing Facebook URL'));
    });
    return;
  }

  var queries = {};

  // Determine Source ID of object
  queries.source = 'SELECT id, site, type, url FROM object_url WHERE url = "' + this.url + '"';

  // Fetch posts and comments
  queries.posts = 'SELECT source_id, post_id, updated_time, created_time, actor_id, permalink, message, comments.comment_list';
  queries.posts += ' FROM stream';
  queries.posts += ' WHERE source_id IN (SELECT id FROM #source)';
  // New comments trigger a fresh updated_time in its parent post
  if (this.lastReportDate) queries.posts += ' AND updated_time > ' + this.lastReportDate;
  queries.posts += ' ORDER BY updated_time';

  // Get author names
  queries.author = 'SELECT id, name FROM profile WHERE id IN (SELECT actor_id FROM #posts) OR id IN (SELECT comments.comment_list.fromid FROM #posts)';

  graph.fql(queries, function(err, res) {
    if (err) self._handleErrors(err);
    if (res && res.data && res.data.length) {
      res.data.forEach(function(data) {
        switch (data.name) {
          case 'source':
            if (data.fql_result_set.length === 0) self._handleErrors(new Error('Invalid Facebook URL'));
            break;
          case 'posts':
            var authors = _.findWhere(res.data, {name: 'author'}).fql_result_set;
            if (data.fql_result_set.length) self._handleResults(data.fql_result_set, authors);
            callback(null, self.lastReportDate * 1000);
            break;
        }
      });
    }
  });
};

// Handle errors
FacebookContentService.prototype._handleErrors = function(err) {
  if (err.exception) {
    // FBGraph module error
    this.emit('error', err.exception);
  } else if (err.error && (err.error.type === 0 || err.error.type === 1)) {
    // 1 API Unknown | 2 API Service warnings
    this.emit('warning', new Error('Facebook error: #{' + err.error.code + '} - #{' + err.error.type + '}: #{' + err.error.message + '}. Will try again.'));
  } else if (err.error) {
    // Catch any other Facebook API errors
    this.emit('error', new Error('Facebook error: #{' + err.error.code + '} (#{' + err.error.error_subcode +
      '}) - #{' + err.error.type + '}: #{' + err.error.message + '}'));
  } else {
    // Catch any other type of error
    this.emit('error', err);
  }
};

// Handle data returned by the FQL query
FacebookContentService.prototype._handleResults = function(data, authors) {
  var self = this;
  var lastReportDate = 0;
  var reports = [];
  // Handle each post
  data.forEach(function(post) {
    // `updated_time` only gets refreshed when posting a new comment. Use these
    // reports only when they are new or from a newly added Source.
    if (!self.lastReportDate || post.created_time === post.updated_time) {
      lastReportDate = Math.max(lastReportDate, post.updated_time);
      var report_data = self._parse(post, {type: 'post', authors: authors});
      reports.push(report_data);
    }
    if (post.comments.comment_list.length) {
      // Handle each comment
      post.comments.comment_list.forEach(function(comment) {
        // Skip old comments
        if (comment.time > self.lastReportDate) {
          lastReportDate = Math.max(lastReportDate, comment.time);
          var report_data = self._parse(comment, {type: 'comment', post: post, authors: authors});
          reports.push(report_data);
        }
      });
    }
  });
  // Comments are loaded as children of posts, sort after the fact to ensure proper chronology
  reports = _.sortBy(reports, 'authoredAt').forEach(function(report_data) {;
    self.emit('report', report_data);
  });
  this.lastReportDate = lastReportDate;
};

// Parse each fql result into our data format
FacebookContentService.prototype._parse = function(data, options) {
  switch (options.type) {
    case 'post':
      return {
        authoredAt: data.updated_time * 1000,
        fetchedAt: Date.now(),
        content: data.message,
        author: this._findAuthor(data.actor_id, options.authors),
        url: data.permalink
      };
    case 'comment':
      return {
        authoredAt: data.time * 1000,
        fetchedAt: Date.now(),
        content: data.text,
        author: this._findAuthor(data.fromid, options.authors),
        url: this._buildCommentUrl(data.id, options.post)
      };
  }
};

// Determine the author of a report
FacebookContentService.prototype._findAuthor = function(id, authors) {
  var author = _.findWhere(authors, {id: id});
  if (author) return author.name;
  return id;
};

// Builder function for making a comment url for posts
FacebookContentService.prototype._buildCommentUrl = function(commentId, post) {
  var id = commentId.split('_');
  if (/\?/.test(post.permalink)) {
    return post.permalink + '&comment_id=' + id[2];
  } else {
    return post.permalink + '?comment_id=' + id[2];
  }
};

module.exports = FacebookContentService;
<|endoftext|>


<commit_before>// Fetches posts and comments efficiently from a Facebook page or group using multiquery Facebook Graph API v2.2.

var config = require('../../../config/secrets').get().facebook;
var ContentService = require('../content-service');
var graph = require('fbgraph');
var util = require('util');
var _ = require('underscore');

// options.url - The url of the Facebook resource
// options.lastReportDate - The time of the last report already fetched (optional)
var FacebookContentService = function(options) {
  graph.setAccessToken(config.accessToken);
  this.fetchType = 'pull';
  this._url = options.url;
  this.posts = {};
  ContentService.call(this, options);
};

util.inherits(FacebookContentService, ContentService);

// Fetch from the Facebook Content Service
// options.maxCount - Max number of reports that will be accepted. (required)
// callback(reportData) - Callback via which to return fetched report data.
// We don't actually care about maxCount in this function, we just rely on Facebook's API limits to limit data per fetch.
// If we return too many items, that is ok, the parent class will handle it.
FacebookContentService.prototype._doFetch = function(options, callback) {
  var self = this;

  if (!this._url) {
    process.nextTick(function () {
      self.emit('error', new Error('Missing Facebook URL'));
    });
    callback([]);
    return;
  }

  var source = this._url.trim();

  // Parse out source name from url
  if (source.indexOf('?') === -1) {
    source = source.split('?')[0];
  }

  if (source.lastIndexOf('/') === source.length - 1) {
    source = source.substring(0, source.length - 1);
  }

  source = source.substring(source.lastIndexOf('/'), source.length);

  this._doRequest(source, function(err, data) {
    if (err) {
      self.emit('error', new Error(err.message));
      return callback([]);
    }
    callback(self._handleResults(data, source));
  })
};

FacebookContentService.prototype._doRequest = function (source, callback) {
  var self = this;
  var data = [];
  var params = {};
  if (this._lastReportDate) {
    params.since = this._lastReportDate.getTime() / 1000;
  }
  // Grab posts off of sources feed
  self._doGet('/v2.2/' + source + '/feed', params, data, callback);

  //If no new posts, grab comments off of most recent post
  if((!data || data.length == 0) && this.posts[source] != null) {
    self._doGet('/v2.2/' + this.posts[source].id + '/comments?order=reverse_chronological', {}, data, callback);
  }
};

FacebookContentService.prototype._doGet = function(url, params, data, callback) {
  var self = this;

  graph.get(url , params, function(err, res) {
    if (res.data && res.data.length > 0) {
      data = data.concat(res.data);
    }
    if (res.paging && res.paging.next) {
      self._doGet(res.paging.next, params, data, callback);
    } else {
      callback(err, data);
    }
  });
};

FacebookContentService.prototype._handleResults = function(data, source) {
  var self = this;
  var oldPost = this.posts[source];
  var reportData = [];

  if (!data) {
    return reportData;
  }

  data.forEach(function(object) {
    //If object has a type then it is a post, else it is a comment
    if (object.type) {
      // Handle each post and any comment already attached to it
      reportData.push(self._parse(object, {type: 'post'}));
      if (object.comments && object.comments.data && object.comments.data.length > 0 ) {
        object.comments.data.forEach(function(comment) {
          reportData.push(self._parse(comment, {type: 'comment', url: object.link}));
        });
      }
    } else {
      if (oldPost == undefined){
        reportData.push(self._parse(object, {type: 'comment', url: object.link}));
      } else {
        reportData.push(self._parse(object, {type: 'comment', url: oldPost.link }));
      }
    }
  });

  //Store most recent post id to retrieve post comments
  if (data[0] && data[0].type && data[0].id) {
    this.posts[source] = {
      id: data[0].id,
      link: data[0].link
    };
  }

  //Filter out old data if any
  if (self._lastReportDate)
    reportData = _.filter(reportData, function(rd) { return rd.authoredAt > self._lastReportDate });

  return reportData;
};

FacebookContentService.prototype._parse = function(data, options) {

  switch(options.type) {
    case 'post':
      return {
        authoredAt: new Date(data.created_time),
        fetchedAt: new Date(),
        content: data.message,
        author: data.from.name,
        url: data.link
      };
    case 'comment':
      return {
        authoredAt: new Date(data.created_time),
        fetchedAt: new Date(),
        content: data.message,
        author: data.from.name,
        url: options.url
      };
    default:
      return {
        authoredAt: new Date(data.created_time),
        fetchedAt: new Date(),
        content: data.message,
        author: data.from.name,
        url: options.url
      }
  }
};

module.exports = FacebookContentService;
<commit_msg>Fix problem with infinite paging until first post<commit_after>// Fetches posts and comments efficiently from a Facebook page or group using multiquery Facebook Graph API v2.2.

var config = require('../../../config/secrets').get().facebook;
var ContentService = require('../content-service');
var graph = require('fbgraph');
var util = require('util');
var _ = require('underscore');

// options.url - The url of the Facebook resource
// options.lastReportDate - The time of the last report already fetched (optional)
var FacebookContentService = function(options) {
  graph.setAccessToken(config.accessToken);
  this.fetchType = 'pull';
  this._url = options.url;
  this.posts = {};
  ContentService.call(this, options);
};

util.inherits(FacebookContentService, ContentService);

// Fetch from the Facebook Content Service
// options.maxCount - Max number of reports that will be accepted. (required)
// callback(reportData) - Callback via which to return fetched report data.
// We don't actually care about maxCount in this function, we just rely on Facebook's API limits to limit data per fetch.
// If we return too many items, that is ok, the parent class will handle it.
FacebookContentService.prototype._doFetch = function(options, callback) {
  var self = this;

  if (!this._url) {
    process.nextTick(function () {
      self.emit('error', new Error('Missing Facebook URL'));
    });
    callback([]);
    return;
  }

  var source = this._url.trim();

  // Parse out source name from url
  if (source.indexOf('?') === -1) {
    source = source.split('?')[0];
  }

  if (source.lastIndexOf('/') === source.length - 1) {
    source = source.substring(0, source.length - 1);
  }

  source = source.substring(source.lastIndexOf('/') + 1, source.length);

  this._doRequest(source, function(err, data) {
    if (err) {
      self.emit('error', new Error(err.message));
      return callback([]);
    }
    callback(self._handleResults(data, source));
  })
};

FacebookContentService.prototype._doRequest = function (source, callback) {
  var self = this;
  var data = [];
  var params = {};

  if (this._lastReportDate) {
    params.since = this._lastReportDate.getTime() / 1000;
  }
  // Grab posts off of sources feed
  self._doGet('/v2.2/' + source + '/feed', params, data, callback);

  //If no new posts, grab comments off of most recent post
  if((!data || data.length == 0) && this.posts[source] != null) {
    self._doGet('/v2.2/' + this.posts[source].id + '/comments?order=reverse_chronological', params, data, callback);
  }
};

FacebookContentService.prototype._doGet = function(url, params, data, callback) {
  var self = this;

  graph.get(url , params, function(err, res) {
    if (res.data && res.data.length > 0) {
      data = data.concat(res.data);
    }
    if (res.paging && res.paging.next && params.since) {
      self._doGet(res.paging.next, params, data, callback);
    } else {
      callback(err, data);
    }
  });
};

FacebookContentService.prototype._handleResults = function(data, source) {
  var self = this;
  var oldPost = this.posts[source];
  var reportData = [];

  if (!data) {
    return reportData;
  }

  data.forEach(function(object) {
    //If object has a type then it is a post, else it is a comment
    if (object.type) {
      // Handle each post and any comment already attached to it
      reportData.push(self._parse(object, {type: 'post'}));
      if (object.comments && object.comments.data && object.comments.data.length > 0 ) {
        object.comments.data.forEach(function(comment) {
          reportData.push(self._parse(comment, {type: 'comment', url: object.link}));
        });
      }
    } else {
      if (oldPost == undefined){
        reportData.push(self._parse(object, {type: 'comment', url: object.link}));
      } else {
        reportData.push(self._parse(object, {type: 'comment', url: oldPost.link }));
      }
    }
  });

  //Store most recent post id to retrieve post comments
  if (data[0] && data[0].type && data[0].id) {
    this.posts[source] = {
      id: data[0].id,
      link: data[0].link
    };
  }

  //Filter out old data if any
  if (self._lastReportDate)
    reportData = _.filter(reportData, function(rd) { return rd.authoredAt > self._lastReportDate });

  return reportData;
};

FacebookContentService.prototype._parse = function(data, options) {

  switch(options.type) {
    case 'post':
      return {
        authoredAt: new Date(data.created_time),
        fetchedAt: new Date(),
        content: data.message,
        author: data.from.name,
        url: data.link
      };
    case 'comment':
      return {
        authoredAt: new Date(data.created_time),
        fetchedAt: new Date(),
        content: data.message,
        author: data.from.name,
        url: options.url
      };
    default:
      return {
        authoredAt: new Date(data.created_time),
        fetchedAt: new Date(),
        content: data.message,
        author: data.from.name,
        url: options.url
      }
  }
};

module.exports = FacebookContentService;
<|endoftext|>


<commit_before>'use strict';

// MODULES //

var isnan = require( '@stdlib/math/base/assert/is-nan' );
var PINF = require( '@stdlib/math/constants/float64-pinf' );
var ibetaDerivative = require( './ibeta_derivative.js' );

// MAIN //

/**
* Evaluates the probability density function (PDF) for a F distribution with numerator degrees of freedom `d1` and denominator degrees of freedom `d2` at a value `x`.
*
* @param {number} x - input value
* @param {PositiveNumber} d1 - numerator degrees of freedom
* @param {PositiveNumber} d2 - denominator degrees of freedom
* @returns {number} evaluated PDF
*
* @example
* var y = pdf( 2.0, 0.5, 1.0 );
* // returns ~0.057
* @example
* var y = pdf( 0.1, 1.0, 1.0 );
* // returns ~0.915
* @example
* var y = pdf( -1.0, 4.0, 2.0 );
* // returns 0.0
* @example
* var y = pdf( NaN, 1.0, 1.0 );
* // returns NaN
* @example
* var y = pdf( 0.0, NaN, 1.0 );
* // returns NaN
* @example
* var y = pdf( 0.0, 1.0, NaN );
* // returns NaN
* @example
* var y = pdf( 2.0, 1.0, -1.0 );
* // returns NaN
* @example
* var y = pdf( 2.0, -1.0, 1.0 );
* // returns NaN
*/
function pdf( x, d1, d2 ) {
	var v1x;
	var y;
	var z;
	if (
		isnan( x ) ||
		isnan( d1 ) ||
		isnan( d2 )
	) {
		return NaN;
	}
	if ( d1 <= 0.0 || d2 <= 0.0 ) {
		return NaN;
	}
	if ( x < 0.0 || x === PINF ) {
		return 0.0;
	}
	if ( x === 0.0 ) {
		if ( d1 < 2.0 ) {
			return PINF;
		}
		else if ( d1 === 2.0 ) {
			return 1.0;
		}
		return 0.0;
	}
	v1x = d1 * x;
	if ( v1x > d2 ) {
		y = ( d2 * d1 ) / ( ( d2 + v1x ) * ( d2 + v1x ) );
		return y * ibetaDerivative( d2 / ( d2+v1x ), d2/2.0, d1/2.0 );
	}
	z = d2 + v1x;
	y = ((z * d1) - (x * d1 * d1)) / ( z * z );
	return y * ibetaDerivative( v1x / ( d2+v1x ), d1/2.0, d2/2.0 );
} // end FUNCTION pdf()

// EXPORTS //

module.exports = pdf;
<commit_msg>Add empty lines and consolidate conditionals<commit_after>'use strict';

// MODULES //

var isnan = require( '@stdlib/math/base/assert/is-nan' );
var PINF = require( '@stdlib/math/constants/float64-pinf' );
var ibetaDerivative = require( './ibeta_derivative.js' );

// MAIN //

/**
* Evaluates the probability density function (PDF) for a F distribution with numerator degrees of freedom `d1` and denominator degrees of freedom `d2` at a value `x`.
*
* @param {number} x - input value
* @param {PositiveNumber} d1 - numerator degrees of freedom
* @param {PositiveNumber} d2 - denominator degrees of freedom
* @returns {number} evaluated PDF
*
* @example
* var y = pdf( 2.0, 0.5, 1.0 );
* // returns ~0.057
*
* @example
* var y = pdf( 0.1, 1.0, 1.0 );
* // returns ~0.915
*
* @example
* var y = pdf( -1.0, 4.0, 2.0 );
* // returns 0.0
*
* @example
* var y = pdf( NaN, 1.0, 1.0 );
* // returns NaN
*
* @example
* var y = pdf( 0.0, NaN, 1.0 );
* // returns NaN
*
* @example
* var y = pdf( 0.0, 1.0, NaN );
* // returns NaN
*
* @example
* var y = pdf( 2.0, 1.0, -1.0 );
* // returns NaN
*
* @example
* var y = pdf( 2.0, -1.0, 1.0 );
* // returns NaN
*/
function pdf( x, d1, d2 ) {
	var v1x;
	var y;
	var z;
	if (
		isnan( x ) ||
		isnan( d1 ) ||
		isnan( d2 ) ||
		d1 <= 0.0 ||
		d2 <= 0.0
	) {
		return NaN;
	}
	if ( x < 0.0 || x === PINF ) {
		return 0.0;
	}
	if ( x === 0.0 ) {
		if ( d1 < 2.0 ) {
			return PINF;
		}
		else if ( d1 === 2.0 ) {
			return 1.0;
		}
		return 0.0;
	}
	v1x = d1 * x;
	if ( v1x > d2 ) {
		y = ( d2 * d1 ) / ( ( d2 + v1x ) * ( d2 + v1x ) );
		return y * ibetaDerivative( d2 / ( d2+v1x ), d2/2.0, d1/2.0 );
	}
	z = d2 + v1x;
	y = ((z * d1) - (x * d1 * d1)) / ( z * z );
	return y * ibetaDerivative( v1x / ( d2+v1x ), d1/2.0, d2/2.0 );
} // end FUNCTION pdf()

// EXPORTS //

module.exports = pdf;
<|endoftext|>


<commit_before>var rawData = [
  {
    title:  'micCheck',
    projectUrl: 'https://github.com/simonszc/starving_artists',
    publishedOn: '2015-1-29',
    projectDescription: '
A prototype for an open-sourced live performance database/listing app.
'
  },
  {
    title: 'busMall',
    projectUrl: 'https://github.com/simonszc/bus-mall',
    publishedOn: '2015-1-21',
    projectDescription: '
Single page app that displays images for the user to choose between, then displays their relative popularity
'
  },
  {
    title: 'campfireCoffee',
    projectUrl: 'https://github.com/simonszc/campfire-espresso',
    publishedOn: '2015-1-14',
    projectDescription: '
A theoretical site for a theoretical coffee company that also has a backpage with functionality to display randomized datasets for several kiosks using a set of properties for each.'
  }
];
<commit_msg>removed blogProjects.js. serves no purpose with PortfolioProjects.json here<commit_after><|endoftext|>


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

'use strict';

// MODULES //

var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive;
var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive;
var format = require( '@stdlib/string/format' );

// VARIABLES //

// Factors for converting individual surrogates
var Ox10000 = 0x10000|0; // 65536
var Ox400 = 0x400|0; // 1024

// Range for a high surrogate
var OxD800 = 0xD800|0; // 55296
var OxDBFF = 0xDBFF|0; // 56319

// Range for a low surrogate
var OxDC00 = 0xDC00|0; // 56320
var OxDFFF = 0xDFFF|0; // 57343

// MAIN //

/**
* Returns a Unicode code point from a string at a specified position.
*
* ## Notes
*
* -   UTF-16 encoding uses one 16-bit unit for non-surrogates (U+0000 to U+D7FF and U+E000 to U+FFFF).
* -   UTF-16 encoding uses two 16-bit units (surrogate pairs) for U+10000 to U+10FFFF and encodes U+10000-U+10FFFF by subtracting 0x10000 from the code point, expressing the result as a 20-bit binary, and splitting the 20 bits of 0x0-0xFFFFF as upper and lower 10-bits. The respective 10-bits are stored in two 16-bit words: a high and a low surrogate.
*
*
* @param {string} str - input string
* @param {integer} idx - position
* @param {boolean} [backward=false] - backward iteration for low surrogates
* @throws {TypeError} first argument must be a string
* @throws {TypeError} second argument must be an integer
* @throws {TypeError} third argument must be a boolean
* @throws {RangeError} position must be a valid index in string
* @returns {NonNegativeInteger} code point
*
* @example
* var out = codePointAt( 'last man standing', 4 );
* // returns 32
*
* @example
* var out = codePointAt( 'presidential election', 8, true );
* // returns 116
*
* @example
* var out = codePointAt( 'अनुच्छेद', 2 );
* // returns 2369
*
* @example
* var out = codePointAt( '🌷', 1, true );
* // returns 127799
*/
function codePointAt( str, idx, backward ) {
	var code;
	var FLG;
	var low;
	var hi;

	if ( !isString( str ) ) {
		throw new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', str ) );
	}
	if ( !isInteger( idx ) ) {
		throw new TypeError( format( 'invalid argument. Second argument must be an integer. Value: `%s`.', idx ) );
	}
	if ( idx < 0 ) {
		idx += str.length;
	}
	if ( idx < 0 || idx >= str.length ) {
		throw new RangeError( format( 'invalid argument. Second argument must be a valid position (i.e., be within string bounds). Value: `%d`.', idx ) );
	}
	if ( arguments.length > 2 ) {
		if ( !isBoolean( backward ) ) {
			throw new TypeError( format( 'invalid argument. Third argument must be a boolean. Value: `%s`.', backward ) );
		}
		FLG = backward;
	} else {
		FLG = false;
	}
	code = str.charCodeAt( idx );

	// High surrogate
	if ( code >= OxD800 && code <= OxDBFF && idx < str.length - 1 ) {
		hi = code;
		low = str.charCodeAt( idx+1 );
		if ( OxDC00 <= low && low <= OxDFFF ) {
			return ( ( hi - OxD800 ) * Ox400 ) + ( low - OxDC00 ) + Ox10000;
		}
		return hi;
	}
	// Low surrogate - support only if backward iteration is desired
	if ( FLG ) {
		if ( code >= OxDC00 && code <= OxDFFF && idx >= 1 ) {
			hi = str.charCodeAt( idx-1 );
			low = code;
			if ( OxD800 <= hi && hi <= OxDBFF ) {
				return ( ( hi - OxD800 ) * Ox400 ) + ( low - OxDC00 ) + Ox10000;
			}
			return low;
		}
	}
	return code;
}

// EXPORTS //

module.exports = codePointAt;
<commit_msg>Use base package<commit_after>/**
* @license Apache-2.0
*
* Copyright (c) 2020 The Stdlib Authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

'use strict';

// MODULES //

var isBoolean = require( '@stdlib/assert/is-boolean' ).isPrimitive;
var isString = require( '@stdlib/assert/is-string' ).isPrimitive;
var isInteger = require( '@stdlib/assert/is-integer' ).isPrimitive;
var format = require( '@stdlib/string/format' );
var base = require( '@stdlib/string/base/code-point-at' );

// MAIN //

/**
* Returns a Unicode code point from a string at a specified position.
*
* @param {string} str - input string
* @param {integer} idx - position
* @param {boolean} [backward=false] - backward iteration for low surrogates
* @throws {TypeError} first argument must be a string
* @throws {TypeError} second argument must be an integer
* @throws {TypeError} third argument must be a boolean
* @throws {RangeError} position must be a valid index in string
* @returns {NonNegativeInteger} code point
*
* @example
* var out = codePointAt( 'last man standing', 4 );
* // returns 32
*
* @example
* var out = codePointAt( 'presidential election', 8, true );
* // returns 116
*
* @example
* var out = codePointAt( 'अनुच्छेद', 2 );
* // returns 2369
*
* @example
* var out = codePointAt( '🌷', 1, true );
* // returns 127799
*/
function codePointAt( str, idx, backward ) {
	var FLG;

	if ( !isString( str ) ) {
		throw new TypeError( format( 'invalid argument. First argument must be a string. Value: `%s`.', str ) );
	}
	if ( !isInteger( idx ) ) {
		throw new TypeError( format( 'invalid argument. Second argument must be an integer. Value: `%s`.', idx ) );
	}
	if ( idx < 0 ) {
		idx += str.length;
	}
	if ( idx < 0 || idx >= str.length ) {
		throw new RangeError( format( 'invalid argument. Second argument must be a valid position (i.e., be within string bounds). Value: `%d`.', idx ) );
	}
	if ( arguments.length > 2 ) {
		if ( !isBoolean( backward ) ) {
			throw new TypeError( format( 'invalid argument. Third argument must be a boolean. Value: `%s`.', backward ) );
		}
		FLG = backward;
	} else {
		FLG = false;
	}
	return base( str, idx, FLG );
}

// EXPORTS //

module.exports = codePointAt;
<|endoftext|>


<commit_before>var through2      = require('through2')
    , hyperquest    = require('hyperquest')
    , bl            = require('bl')
    , exercise      = require('workshopper-exercise')()
    , filecheck     = require('workshopper-exercise/filecheck')
    , execute       = require('workshopper-exercise/execute')
    , comparestdout = require('workshopper-exercise/comparestdout')

    , date          = new Date(Date.now() - 100000)

// the output will be long lines so make the comparison take that into account
exercise.longCompareOutput = true

// checks that the submission file actually exists
exercise = filecheck(exercise)

// execute the solution and submission in parallel with spawn()
exercise = execute(exercise)

function rndport() {

    return 1024 + Math.floor(Math.random() * 64511);
}

// set up the data file to be passed to the submission
exercise.addSetup(function (mode, callback) {
    this.submissionPort = rndport();
    this.solutionPort   = this.submissionPort + 1

    this.submissionArgs = [ this.submissionPort ]
    this.solutionArgs   = [ this.solutionPort ]

    process.nextTick(callback)
})

// add a processor for both run and verify calls, added *before*
// the comparestdout processor so we can mess with the stdouts
exercise.addProcessor(function (mode, callback) {
    this.submissionStdout.pipe(process.stdout)

    // replace stdout with our own streams
    this.submissionStdout = through2()
    if (mode == 'verify')
        this.solutionStdout = through2()

    setTimeout(query.bind(this, mode), 500)

    process.nextTick(function () {
        callback(null, true)
    })
})

// compare stdout of solution and submission
exercise = comparestdout(exercise)

// delayed for 500ms to wait for servers to start so we can start
// playing with them
function query (mode) {
    var exercise = this

    function verify (port, stream) {

        function error (err) {

            exercise.emit(
                'fail'
                , 'Error connecting to http://localhost:' + port + ': ' + err.code
            )
        }

        hyperquest.get('http://localhost:' + port + '/').on('error', error).pipe(
            bl(function (err, data) {

                if (err)
                    return stream.emit('error', err)

                stream.write(data.toString() + '\n');
                stream.end();
            }));
    }

    verify(this.submissionPort, this.submissionStdout)

    if (mode == 'verify')
        verify(this.solutionPort, this.solutionStdout)
}

module.exports = exercise
<commit_msg>Coding guidelines changes<commit_after>var through2 = require('through2');
var hyperquest = require('hyperquest');
var bl = require('bl');
var exercise = require('workshopper-exercise')();
var filecheck = require('workshopper-exercise/filecheck');
var execute = require('workshopper-exercise/execute');
var comparestdout = require('workshopper-exercise/comparestdout');

// the output will be long lines so make the comparison take that into account
exercise.longCompareOutput = true;

// checks that the submission file actually exists
exercise = filecheck(exercise);

// execute the solution and submission in parallel with spawn()
exercise = execute(exercise);

function rndport() {

    return 1024 + Math.floor(Math.random() * 64511);
}

// set up the data file to be passed to the submission
exercise.addSetup(function (mode, callback) {

    this.submissionPort = rndport();
    this.solutionPort = this.submissionPort + 1;

    this.submissionArgs = [this.submissionPort];
    this.solutionArgs = [this.solutionPort];

    process.nextTick(callback);
});

// add a processor for both run and verify calls, added *before*
// the comparestdout processor so we can mess with the stdouts
exercise.addProcessor(function (mode, callback) {

    this.submissionStdout.pipe(process.stdout);

    // replace stdout with our own streams
    this.submissionStdout = through2();
    if (mode == 'verify') {
        this.solutionStdout = through2();
    }

    setTimeout(query.bind(this, mode), 500);

    process.nextTick(function () {
        callback(null, true)
    });
});

// compare stdout of solution and submission
exercise = comparestdout(exercise)

// delayed for 500ms to wait for servers to start so we can start
// playing with them
function query (mode) {
    var exercise = this

    function verify (port, stream) {

        function error (err) {

            exercise.emit('fail', 'Error connecting to http://localhost:' + port + ': ' + err.code)
        }

        hyperquest.get('http://localhost:' + port + '/')
            .on('error', error)
            .pipe(bl(function (err, data) {

                if (err)
                    return stream.emit('error', err)

                stream.write(data.toString() + '\n');
                stream.end();
            }));
    }

    verify(this.submissionPort, this.submissionStdout)

    if (mode == 'verify') {
        verify(this.solutionPort, this.solutionStdout);
    }
}

module.exports = exercise;<|endoftext|>


<commit_before>$(window).ready(function() {
    if($("#locust_count").length > 0) {
        $("#locust_count").focus().select();
    }
});

$("#box_stop a").click(function(event) {
    event.preventDefault();
    $.get($(this).attr("href"));
    $("body").attr("class", "stopped");
    $(".box_stop").hide();
    $("a.new_test").show();
    $("a.edit_test").hide();
    $(".user_count").hide();
});

$("#box_reset a").click(function(event) {
    event.preventDefault();
    $.get($(this).attr("href"));
});

$("#new_test").click(function(event) {
    event.preventDefault();
    $("#start").show();
    $("#locust_count").focus().select();
});

$(".edit_test").click(function(event) {
    event.preventDefault();
    $("#edit").show();
    $("#new_locust_count").focus().select();
});

$(".close_link").click(function(event) {
    event.preventDefault();
    $(this).parent().parent().hide();
});

var alternate = false;

$("ul.tabs").tabs("div.panes > div");

var stats_tpl = $('#stats-template');
var errors_tpl = $('#errors-template');
var exceptions_tpl = $('#exceptions-template');

$('#swarm_form').submit(function(event) {
    event.preventDefault();
    $.post($(this).attr("action"), $(this).serialize(),
        function(response) {
            if (response.success) {
                $("body").attr("class", "hatching");
                $("#start").fadeOut();
                $("#status").fadeIn();
                $(".box_running").fadeIn();
                $("a.new_test").fadeOut();
                $("a.edit_test").fadeIn();
                $(".user_count").fadeIn();
            }
        }
    );
});

$('#edit_form').submit(function(event) {
    event.preventDefault();
    $.post($(this).attr("action"), $(this).serialize(),
        function(response) {
            if (response.success) {
                $("body").attr("class", "hatching");
                $("#edit").fadeOut();
            }
        }
    );
});

var sortBy = function(field, reverse, primer){
    reverse = (reverse) ? -1 : 1;
    return function(a,b){
        a = a[field];
        b = b[field];
       if (typeof(primer) != 'undefined'){
           a = primer(a);
           b = primer(b);
       }
       if (ab) return reverse * 1;
       return 0;
    }
}

// Sorting by column
var sortAttribute = "name";
var desc = false;
var report;
$(".stats_label").click(function(event) {
    event.preventDefault();
    sortAttribute = $(this).attr("data-sortkey");
    desc = !desc;

    $('#stats tbody').empty();
    $('#errors tbody').empty();
    alternate = false;
    totalRow = report.stats.pop()
    sortedStats = (report.stats).sort(sortBy(sortAttribute, desc))
    sortedStats.push(totalRow)
    $('#stats tbody').jqoteapp(stats_tpl, sortedStats);
    alternate = false;
    $('#errors tbody').jqoteapp(errors_tpl, (report.errors).sort(sortBy(sortAttribute, desc)));
});

var charts = {}

function initChart(stat) {
    var width = $(".main").width();
    var rps_element = $('

').css("width", width);
    var avt_element = $('

').css("width", width);
    rps_element.appendTo("#charts .charts-container");
    avt_element.appendTo("#charts .charts-container");

    var date = [];
    var rps_data = [];
    var avt_data = [];

    now = new Date().toLocaleTimeString();
    date.push(now);
    rps_data.push({
        "name": now,
        "value": Math.round(stat.current_rps*100)/100
    });

    avt_data.push({
        "name": now,
        "value": Math.round(stat.avg_response_time)
    });

    var gridSettings = {x:60, y:70, x2:40, y2:40};

    var rps_chart = echarts.init(rps_element.get(0), 'vintage');
    rps_chart.setOption({
        title: {
            text: 'RPS',
            x: 10,
            y: 10,
        },
        tooltip: {
            trigger: 'axis',
            formatter: function (params) {
                param = params[0];
                return param.name + ': ' + param.value + ' reqs/s';
            },
            axisPointer: {
                animation: true
            }
        },
        xAxis: {
            type: 'category',
            splitLine: {
                show: false
            },
            data: date
        },
        yAxis: {
            type: 'value',
            boundaryGap: [0, '100%'],
            splitLine: {
                show: false
            }
        },
        series: [{
            name: 'RPS',
            type: 'line',
            showSymbol: false,
            hoverAnimation: false,
            data: rps_data
        }],
        grid: gridSettings,
    });

    var avt_chart = echarts.init(avt_element.get(0), 'vintage');
    avt_chart.setOption({
        title: {
            text: 'Average Response Time',
            x: 10,
            y: 10,
        },
        tooltip: {
            trigger: 'axis',
            formatter: function (params) {
                param = params[0];
                return param.name + ': ' + param.value + 'ms';
            },
            axisPointer: {
                animation: true
            }
        },
        xAxis: {
            type: 'category',
            splitLine: {
                show: false
            },
            data: date
        },
        yAxis: {
            type: 'value',
            boundaryGap: [0, '100%'],
            splitLine: {
                show: false
            }
        },
        series: [{
            name: 'AVT',
            type: 'line',
            showSymbol: false,
            hoverAnimation: false,
            data: avt_data
        }],
        grid: gridSettings,
    });

    charts[stat.name] = {};
    charts[stat.name]["date"] = date;
    charts[stat.name]["rps_chart"] = rps_chart;
    charts[stat.name]["rps_data"] = rps_data;
    charts[stat.name]["avt_chart"] = avt_chart;
    charts[stat.name]["avt_data"] = avt_data;

}

function updateCharts(stats) {
    var rsp_chart, rps_data, now, date, avt_chart, avt_data;

    $.each(stats, function(index, stat){
        if (stat.name != "Total") {
            // Only render charts for the total data
            return;
        }
        if (stat.name in charts) {
            now = new Date().toLocaleTimeString();
            date = charts[stat.name]["date"];
            date.push(now);

            rps_chart = charts[stat.name]["rps_chart"];
            rps_data = charts[stat.name]["rps_data"];
            rps_data.push({
                "name": now,
                "value": Math.round(stat.current_rps*100)/100
            });
            rps_chart.setOption({
                xAxis: {
                    data: date
                },
                series: [{
                    data: rps_data
                }]
            });

            avt_chart = charts[stat.name]["avt_chart"];
            avt_data = charts[stat.name]["avt_data"];
            avt_data.push({
                "name": now,
                "value": Math.round(stat.avg_response_time)
            });

            avt_chart.setOption({
                xAxis: {
                    data: date
                },
                series: [{
                    data: avt_data
                }]
            });
        } else {
            initChart(stat);
        }
    });
}

function updateStats() {
    $.get('/stats/requests', function (data) {
        report = JSON.parse(data);
        $("#total_rps").html(Math.round(report.total_rps*100)/100);
        //$("#fail_ratio").html(Math.round(report.fail_ratio*10000)/100);
        $("#fail_ratio").html(Math.round(report.fail_ratio*100));
        $("#status_text").html(report.state);
        $("#userCount").html(report.user_count);

        if (typeof report.slave_count !== "undefined")
            $("#slaveCount").html(report.slave_count)

        $('#stats tbody').empty();
        $('#errors tbody').empty();

        alternate = false;

        totalRow = report.stats.pop()
        sortedStats = (report.stats).sort(sortBy(sortAttribute, desc))
        sortedStats.push(totalRow)
        $('#stats tbody').jqoteapp(stats_tpl, sortedStats);
        alternate = false;
        $('#errors tbody').jqoteapp(errors_tpl, (report.errors).sort(sortBy(sortAttribute, desc)));

        if (report.state !== "stopped"){
            updateCharts(sortedStats);
        }

        setTimeout(updateStats, 2000);
    });
}
updateStats();

function updateExceptions() {
    $.get('/exceptions', function (data) {
        $('#exceptions tbody').empty();
        $('#exceptions tbody').jqoteapp(exceptions_tpl, data.exceptions);
        setTimeout(updateExceptions, 5000);
    });
}
updateExceptions();
<commit_msg>Fixed typo<commit_after>$(window).ready(function() {
    if($("#locust_count").length > 0) {
        $("#locust_count").focus().select();
    }
});

$("#box_stop a").click(function(event) {
    event.preventDefault();
    $.get($(this).attr("href"));
    $("body").attr("class", "stopped");
    $(".box_stop").hide();
    $("a.new_test").show();
    $("a.edit_test").hide();
    $(".user_count").hide();
});

$("#box_reset a").click(function(event) {
    event.preventDefault();
    $.get($(this).attr("href"));
});

$("#new_test").click(function(event) {
    event.preventDefault();
    $("#start").show();
    $("#locust_count").focus().select();
});

$(".edit_test").click(function(event) {
    event.preventDefault();
    $("#edit").show();
    $("#new_locust_count").focus().select();
});

$(".close_link").click(function(event) {
    event.preventDefault();
    $(this).parent().parent().hide();
});

var alternate = false;

$("ul.tabs").tabs("div.panes > div");

var stats_tpl = $('#stats-template');
var errors_tpl = $('#errors-template');
var exceptions_tpl = $('#exceptions-template');

$('#swarm_form').submit(function(event) {
    event.preventDefault();
    $.post($(this).attr("action"), $(this).serialize(),
        function(response) {
            if (response.success) {
                $("body").attr("class", "hatching");
                $("#start").fadeOut();
                $("#status").fadeIn();
                $(".box_running").fadeIn();
                $("a.new_test").fadeOut();
                $("a.edit_test").fadeIn();
                $(".user_count").fadeIn();
            }
        }
    );
});

$('#edit_form').submit(function(event) {
    event.preventDefault();
    $.post($(this).attr("action"), $(this).serialize(),
        function(response) {
            if (response.success) {
                $("body").attr("class", "hatching");
                $("#edit").fadeOut();
            }
        }
    );
});

var sortBy = function(field, reverse, primer){
    reverse = (reverse) ? -1 : 1;
    return function(a,b){
        a = a[field];
        b = b[field];
       if (typeof(primer) != 'undefined'){
           a = primer(a);
           b = primer(b);
       }
       if (ab) return reverse * 1;
       return 0;
    }
}

// Sorting by column
var sortAttribute = "name";
var desc = false;
var report;
$(".stats_label").click(function(event) {
    event.preventDefault();
    sortAttribute = $(this).attr("data-sortkey");
    desc = !desc;

    $('#stats tbody').empty();
    $('#errors tbody').empty();
    alternate = false;
    totalRow = report.stats.pop()
    sortedStats = (report.stats).sort(sortBy(sortAttribute, desc))
    sortedStats.push(totalRow)
    $('#stats tbody').jqoteapp(stats_tpl, sortedStats);
    alternate = false;
    $('#errors tbody').jqoteapp(errors_tpl, (report.errors).sort(sortBy(sortAttribute, desc)));
});

var charts = {}

function initChart(stat) {
    var width = $(".main").width();
    var rps_element = $('

').css("width", width);
    var avt_element = $('

').css("width", width);
    rps_element.appendTo("#charts .charts-container");
    avt_element.appendTo("#charts .charts-container");

    var date = [];
    var rps_data = [];
    var avt_data = [];

    now = new Date().toLocaleTimeString();
    date.push(now);
    rps_data.push({
        "name": now,
        "value": Math.round(stat.current_rps*100)/100
    });

    avt_data.push({
        "name": now,
        "value": Math.round(stat.avg_response_time)
    });

    var gridSettings = {x:60, y:70, x2:40, y2:40};

    var rps_chart = echarts.init(rps_element.get(0), 'vintage');
    rps_chart.setOption({
        title: {
            text: 'RPS',
            x: 10,
            y: 10,
        },
        tooltip: {
            trigger: 'axis',
            formatter: function (params) {
                param = params[0];
                return param.name + ': ' + param.value + ' reqs/s';
            },
            axisPointer: {
                animation: true
            }
        },
        xAxis: {
            type: 'category',
            splitLine: {
                show: false
            },
            data: date
        },
        yAxis: {
            type: 'value',
            boundaryGap: [0, '100%'],
            splitLine: {
                show: false
            }
        },
        series: [{
            name: 'RPS',
            type: 'line',
            showSymbol: false,
            hoverAnimation: false,
            data: rps_data
        }],
        grid: gridSettings,
    });

    var avt_chart = echarts.init(avt_element.get(0), 'vintage');
    avt_chart.setOption({
        title: {
            text: 'Average Response Time',
            x: 10,
            y: 10,
        },
        tooltip: {
            trigger: 'axis',
            formatter: function (params) {
                param = params[0];
                return param.name + ': ' + param.value + 'ms';
            },
            axisPointer: {
                animation: true
            }
        },
        xAxis: {
            type: 'category',
            splitLine: {
                show: false
            },
            data: date
        },
        yAxis: {
            type: 'value',
            boundaryGap: [0, '100%'],
            splitLine: {
                show: false
            }
        },
        series: [{
            name: 'AVT',
            type: 'line',
            showSymbol: false,
            hoverAnimation: false,
            data: avt_data
        }],
        grid: gridSettings,
    });

    charts[stat.name] = {};
    charts[stat.name]["date"] = date;
    charts[stat.name]["rps_chart"] = rps_chart;
    charts[stat.name]["rps_data"] = rps_data;
    charts[stat.name]["avt_chart"] = avt_chart;
    charts[stat.name]["avt_data"] = avt_data;

}

function updateCharts(stats) {
    var rps_chart, rps_data, now, date, avt_chart, avt_data;

    $.each(stats, function(index, stat){
        if (stat.name != "Total") {
            // Only render charts for the total data
            return;
        }
        if (stat.name in charts) {
            now = new Date().toLocaleTimeString();
            date = charts[stat.name]["date"];
            date.push(now);

            rps_chart = charts[stat.name]["rps_chart"];
            rps_data = charts[stat.name]["rps_data"];
            rps_data.push({
                "name": now,
                "value": Math.round(stat.current_rps*100)/100
            });
            rps_chart.setOption({
                xAxis: {
                    data: date
                },
                series: [{
                    data: rps_data
                }]
            });

            avt_chart = charts[stat.name]["avt_chart"];
            avt_data = charts[stat.name]["avt_data"];
            avt_data.push({
                "name": now,
                "value": Math.round(stat.avg_response_time)
            });

            avt_chart.setOption({
                xAxis: {
                    data: date
                },
                series: [{
                    data: avt_data
                }]
            });
        } else {
            initChart(stat);
        }
    });
}

function updateStats() {
    $.get('/stats/requests', function (data) {
        report = JSON.parse(data);
        $("#total_rps").html(Math.round(report.total_rps*100)/100);
        //$("#fail_ratio").html(Math.round(report.fail_ratio*10000)/100);
        $("#fail_ratio").html(Math.round(report.fail_ratio*100));
        $("#status_text").html(report.state);
        $("#userCount").html(report.user_count);

        if (typeof report.slave_count !== "undefined")
            $("#slaveCount").html(report.slave_count)

        $('#stats tbody').empty();
        $('#errors tbody').empty();

        alternate = false;

        totalRow = report.stats.pop()
        sortedStats = (report.stats).sort(sortBy(sortAttribute, desc))
        sortedStats.push(totalRow)
        $('#stats tbody').jqoteapp(stats_tpl, sortedStats);
        alternate = false;
        $('#errors tbody').jqoteapp(errors_tpl, (report.errors).sort(sortBy(sortAttribute, desc)));

        if (report.state !== "stopped"){
            updateCharts(sortedStats);
        }

        setTimeout(updateStats, 2000);
    });
}
updateStats();

function updateExceptions() {
    $.get('/exceptions', function (data) {
        $('#exceptions tbody').empty();
        $('#exceptions tbody').jqoteapp(exceptions_tpl, data.exceptions);
        setTimeout(updateExceptions, 5000);
    });
}
updateExceptions();
<|endoftext|>