<commit_before>/*globals define, console, angular*/

define(['js/client'], function (Client) {

    console.log('loaded');
    console.log(angular);

    angular.module('gme.services', [])

        .service('DataStoreService', function ($timeout, $q) {
            var datastores = {};

            this.connectToDatabase = function (context) {
                var deferred = $q.defer(), client;

                if (datastores.hasOwnProperty(context.db)) {
                    // FIXME: this may or may not ready yet...
                    deferred.resolve();
                } else {
                    // TODO: replace CONFIG with context
                    client = new Client(/*CONFIG*/);

                    // hold a reference to the client instance
                    datastores[context.db] = {client: client};

                    // TODO: add event listeners to client

                    client.connectToDatabaseAsync({}, function (err) {
                        if (err) {
                            deferred.reject(err);
                            return;
                        }

                        deferred.resolve();
                    });

                }

                return deferred.promise;
            };

            this.getDatabaseConnection = function (context) {
                if (datastores.hasOwnProperty(context.db) && typeof datastores[context.db] === 'object') {
                    return datastores[context.db];
                }

                console.error(context + ' does not have an active database connection.');
            };

            this.getProjects = function (context) {
                var deferred = $q.defer();

                this.connectToDatabase(context).then(function () {
                    datastores[context.db].client.getAvailableProjectsAsync(function (err, projectIds) {
                        if (err) {
                            deferred.reject(err);
                            return;
                        }

                        deferred.resolve(projectIds);
                    });
                });

                return deferred.promise;
            };

            this.selectProject = function (context) {
                var deferred = $q.defer();

                this.getProjects(context).then(function (projectIds) {

                    if (projectIds.indexOf(context.projectId) > -1) {
                        datastores[context.db].client.selectProjectAsync(context.projectId, function (err) {
                            if (err) {
                                deferred.reject(err);
                                return;
                            }

                            datastores[context.db].projectId =
                                context.projectId;

                            deferred.resolve();
                        });
                    } else {
                        deferred.reject(new Error('Project does not exist. ' + context.projectId));
                    }
                });

                return deferred.promise;
            };

            this.selectBranch = function (context) {
                var deferred = $q.defer();

                this.selectProject(context).then(function () {
                    // FIXME: if branch does not exist the callback is not called,
                    //        then after (probably a timeout) it is called with no error???
                    datastores[context.db].client.selectBranchAsync(context.branchId,
                        function (err) {
                            if (err) {
                                deferred.reject(err);
                                return;
                            }

                            datastores[context.db].branchId =
                                context.branchId;

                            deferred.resolve();
                        });
                });

                return deferred.promise;
            };
        })

        .service('ProjectService', function ($timeout, $q, DataStoreService) {

            this.openProject = function (context) {
                return DataStoreService.selectProject(context);
            };

            this.selectBranch = function (context) {
                return DataStoreService.selectBranch(context);
            };
        })

        .service('BranchService', function ($timeout, $q, ProjectService) {

            this.selectBranch = function (context) {
                return ProjectService.selectBranch(context);
            };
        })

        .service('NodeService', function ($timeout, $q, DataStoreService) {
            var self = this,
                Node,
                nodes,
                getIdFromNodeOrString;

            getIdFromNodeOrString = function (nodeOrId) {
                if (typeof nodeOrId === 'string') {
                    return nodeOrId;
                }

                if (typeof nodeOrId === 'object') {
                    if (nodeOrId.hasOwnProperty('getId')) {
                        return nodeOrId.getId();
                    } else {
                        console.error(nodeOrId, ' does not have a getId function');
                    }
                } else {
                    console.error(nodeOrId, ' is not a string nor an object.');
                }
            };

            NodeObj = function (databaseConnection, id) {
                this.databaseConnection = databaseConnection;
                this.id = id;
                this._onUpdate = function (id) { };
                this._onUnload = function (id) { };
                // This will always be called on unload.
                this.__onUnload = function () {
                    if (databaseConnection.nodeService.territories.hasOwnProperty(id)) {
                        delete databaseConnection.nodeService.territories[id];
                        databaseConnection.client.removeUI(id);
                    }
                };
            };

            NodeObj.prototype.getAttribute = function (name) {
                return this.databaseConnection.client.getNode(this.id).getAttribute(name);
            };

            NodeObj.prototype.setAttribute = function (name, value, msg) {
                this.databaseConnection.client.setAttributes(this.id, name, value, msg);
            };

            NodeObj.prototype.getRegistry = function (name) {

            };

            NodeObj.prototype.setRegistry = function (name, value) {

            };

            NodeObj.prototype.getPointer = function (name) {
                return this.databaseConnection.client.getNode(this.id).getPointer(name);
            };

            NodeObj.prototype.setPointer = function (name, nodeOrId) {

            };

            // TODO: add sets

            NodeObj.prototype.getBaseNode = function () {

            };

            NodeObj.prototype.getParentId = function () {

            };

            NodeObj.prototype.getParentNode = function () {

            };

            NodeObj.prototype.getId = function () {
                return this.databaseConnection.client.getNode(this.id).getId();
            };

            NodeObj.prototype.getGuid = function () {
                return this.databaseConnection.client.getNode(this.id).getGuid();
            };

            NodeObj.prototype.getChildrenIds = function () {
                return this.databaseConnection.client.getNode(this.id).getChildrenIds();
            };

            // FIXME : Can context be resolved from the node
            NodeObj.prototype.loadChildren = function (context) {
                var childrenIds = this.getChildrenIds(),
                    queueList = [],
                    all,
                    i;

                for (i = 0; i < childrenIds.length; i += 1) {
                    queueList.push(self.loadNode2(context, childrenIds[i]));
                }
                return $q.all(queueList);
            };

            NodeObj.prototype.createChild = function (baseNodeOrId, name) {

            };

            NodeObj.prototype.destroy = function () {

            };

            NodeObj.prototype.getMetaType = function () {

            };

            NodeObj.prototype.isMetaTypeOf = function (nodeOrId) {

            };

            this.createChild = function (context, parameters) {
                // NS.createChild({parent: parentNode/id, base: baseNode/id}) – current one on client takes {parentId and baseId}
            };

            this.destroyNode = function (context, nodeOrId) {
                // NS.destroyNode(node/Id);
            };

            NodeObj.prototype.onUpdate = function(fn) {
                console.assert(typeof fn === 'function');
                this._onUpdate = fn;
            }

            NodeObj.prototype.onUnload = function(fn) {
                console.assert(typeof fn === 'function');
                this._onUnload = fn;
            }

            this.loadNode2 = function (context, id) {
                var deferred = $q.defer(),
                    dbConn = DataStoreService.getDatabaseConnection(context),
                    territory,
                    nodes;

                dbConn.nodeService = dbConn.nodeService || {};
                dbConn.nodeService.nodes =  dbConn.nodeService.nodes || {};
                dbConn.nodeService.territories = dbConn.nodeService.territories || {};

                nodes = dbConn.nodeService.nodes;

                if (nodes.hasOwnProperty(id)) {
                    deferred.resolve(nodes[id]);
                } else {
                    if (dbConn.nodeService.territories.hasOwnProperty(id)) {
//                        territory = dbConn.nodeService.territories[id];
                        deferred.reject('Territory exists, but node does not!');
                    } else {
                        dbConn.client.addUI(null, function (events) {
                            var i,
                                event;

                            for (i = 0; i < events.length; i += 1) {
                                event = events[i];
                                if (event.etype === 'load') {
                                    nodes[id] =  new NodeObj(dbConn, id);
                                    deferred.resolve(nodes[id]);
                                } else if (event.etype === 'update') {
                                    nodes[id]._onUpdate(event.eid);
                                } else if (event.etype === 'unload') {
                                    nodes[id]._onUnload(event.eid);
                                    nodes[id].__onUnload();
                                } else {
                                    throw 'Unexpected event type' + events[j].etype;
                                }
                            }
                        }, id);

                        territory = {};
                        dbConn.nodeService.territories[id] = territory;
                        territory.id = id;
                        territory.patterns = territory.patterns || {};
                        territory.patterns[id] = {children: 0}; // FIXME: How to detect new children??
                        dbConn.client.updateTerritory(id, territory.patterns);
                    }
                }

                return deferred.promise;
            };

            this.loadNode = function (context, id) {
                var deferred = $q.defer(),
                    dbConn = DataStoreService.getDatabaseConnection(context),
                    territory,
                    nodes;

                dbConn.nodeService = dbConn.nodeService || {};
                dbConn.nodeService.nodes =  dbConn.nodeService.nodes || {};
                dbConn.nodeService.territoies = dbConn.nodeService.territoies || {};

                nodes = dbConn.nodeService.nodes;

                if (nodes.hasOwnProperty(id)) {
                    deferred.resolve(nodes[id]);
                } else {
                    // TODO: create territory if does not exist
                    if (dbConn.nodeService.territoies.hasOwnProperty(context.territoryId)) {
                        territory = dbConn.nodeService.territoies[context.territoryId];
                    } else {
                        dbConn.client.addUI(null, function (events) {
                            var i,
                                event;

                            // TODO: fill in this
                            for (i = 0; i < events.length; i += 1) {
                                event = events[i];
                                if (event.eid === id && event.etype === 'load') {
                                    // TODO: when node is loaded resolve promise
                                    nodes[id] =  new NodeObj(dbConn, id);
                                    deferred.resolve(nodes[id]);
                                }
                            }

                        }, context.territoryId);

                        // TODO: add territory rule
                        territory = {};
                        territory.id = context.territoryId;
                        territory.patterns = territory.patterns || {};
                        territory.patterns[id] = {children: 0}; // FIXME: how to update this correctly ???

                        dbConn.nodeService.territoies[context.territoryId] = territory;

                        dbConn.client.updateTerritory(context.territoryId, territory.patterns);
                    }
                }

                return deferred.promise;
            };
        }
    );
});<commit_msg>Added onNewChildLoaded method on NodeObj. TODO: territory needs to be cleaned up.<commit_after>/*globals define, console, angular*/

define(['js/client'], function (Client) {

    console.log('loaded');
    console.log(angular);

    angular.module('gme.services', [])

        .service('DataStoreService', function ($timeout, $q) {
            var datastores = {};

            this.connectToDatabase = function (context) {
                var deferred = $q.defer(), client;

                if (datastores.hasOwnProperty(context.db)) {
                    // FIXME: this may or may not ready yet...
                    deferred.resolve();
                } else {
                    // TODO: replace CONFIG with context
                    client = new Client(/*CONFIG*/);

                    // hold a reference to the client instance
                    datastores[context.db] = {client: client};

                    // TODO: add event listeners to client

                    client.connectToDatabaseAsync({}, function (err) {
                        if (err) {
                            deferred.reject(err);
                            return;
                        }

                        deferred.resolve();
                    });

                }

                return deferred.promise;
            };

            this.getDatabaseConnection = function (context) {
                if (datastores.hasOwnProperty(context.db) && typeof datastores[context.db] === 'object') {
                    return datastores[context.db];
                }

                console.error(context + ' does not have an active database connection.');
            };

            this.getProjects = function (context) {
                var deferred = $q.defer();

                this.connectToDatabase(context).then(function () {
                    datastores[context.db].client.getAvailableProjectsAsync(function (err, projectIds) {
                        if (err) {
                            deferred.reject(err);
                            return;
                        }

                        deferred.resolve(projectIds);
                    });
                });

                return deferred.promise;
            };

            this.selectProject = function (context) {
                var deferred = $q.defer();

                this.getProjects(context).then(function (projectIds) {

                    if (projectIds.indexOf(context.projectId) > -1) {
                        datastores[context.db].client.selectProjectAsync(context.projectId, function (err) {
                            if (err) {
                                deferred.reject(err);
                                return;
                            }

                            datastores[context.db].projectId =
                                context.projectId;

                            deferred.resolve();
                        });
                    } else {
                        deferred.reject(new Error('Project does not exist. ' + context.projectId));
                    }
                });

                return deferred.promise;
            };

            this.selectBranch = function (context) {
                var deferred = $q.defer();

                this.selectProject(context).then(function () {
                    // FIXME: if branch does not exist the callback is not called,
                    //        then after (probably a timeout) it is called with no error???
                    datastores[context.db].client.selectBranchAsync(context.branchId,
                        function (err) {
                            if (err) {
                                deferred.reject(err);
                                return;
                            }

                            datastores[context.db].branchId =
                                context.branchId;

                            deferred.resolve();
                        });
                });

                return deferred.promise;
            };
        })

        .service('ProjectService', function ($timeout, $q, DataStoreService) {

            this.openProject = function (context) {
                return DataStoreService.selectProject(context);
            };

            this.selectBranch = function (context) {
                return DataStoreService.selectBranch(context);
            };
        })

        .service('BranchService', function ($timeout, $q, ProjectService) {

            this.selectBranch = function (context) {
                return ProjectService.selectBranch(context);
            };
        })

        .service('NodeService', function ($timeout, $q, DataStoreService) {
            var self = this,
                Node,
                nodes,
                getIdFromNodeOrString;

            getIdFromNodeOrString = function (nodeOrId) {
                if (typeof nodeOrId === 'string') {
                    return nodeOrId;
                }

                if (typeof nodeOrId === 'object') {
                    if (nodeOrId.hasOwnProperty('getId')) {
                        return nodeOrId.getId();
                    } else {
                        console.error(nodeOrId, ' does not have a getId function');
                    }
                } else {
                    console.error(nodeOrId, ' is not a string nor an object.');
                }
            };

            NodeObj = function (databaseConnection, id) {
                this.databaseConnection = databaseConnection;
                this.id = id;
                this._onUpdate = function (id) { };
                this._onUnload = function (id) { };
                // This will always be called on unload.
                this.__onUnload = function () {
                    if (databaseConnection.nodeService.territories.hasOwnProperty(id)) {
                        delete databaseConnection.nodeService.territories[id];
                        databaseConnection.client.removeUI(id);
                    }
                };
            };

            NodeObj.prototype.getAttribute = function (name) {
                return this.databaseConnection.client.getNode(this.id).getAttribute(name);
            };

            NodeObj.prototype.setAttribute = function (name, value, msg) {
                this.databaseConnection.client.setAttributes(this.id, name, value, msg);
            };

            NodeObj.prototype.getRegistry = function (name) {

            };

            NodeObj.prototype.setRegistry = function (name, value) {

            };

            NodeObj.prototype.getPointer = function (name) {
                return this.databaseConnection.client.getNode(this.id).getPointer(name);
            };

            NodeObj.prototype.setPointer = function (name, nodeOrId) {

            };

            // TODO: add sets

            NodeObj.prototype.getBaseNode = function () {

            };

            NodeObj.prototype.getParentId = function () {

            };

            NodeObj.prototype.getParentNode = function () {

            };

            NodeObj.prototype.getId = function () {
                return this.databaseConnection.client.getNode(this.id).getId();
            };

            NodeObj.prototype.getGuid = function () {
                return this.databaseConnection.client.getNode(this.id).getGuid();
            };

            NodeObj.prototype.getChildrenIds = function () {
                return this.databaseConnection.client.getNode(this.id).getChildrenIds();
            };

            // FIXME : Can context be resolved from the node?
            NodeObj.prototype.loadChildren = function (context) {
                var childrenIds = this.getChildrenIds(),
                    queueList = [],
                    all,
                    i;

                for (i = 0; i < childrenIds.length; i += 1) {
                    queueList.push(self.loadNode2(context, childrenIds[i]));
                }
                return $q.all(queueList);
            };

            NodeObj.prototype.createChild = function (baseNodeOrId, name) {

            };

            NodeObj.prototype.destroy = function () {

            };

            NodeObj.prototype.getMetaType = function () {

            };

            NodeObj.prototype.isMetaTypeOf = function (nodeOrId) {

            };

            this.createChild = function (context, parameters) {
                // NS.createChild({parent: parentNode/id, base: baseNode/id}) – current one on client takes {parentId and baseId}
            };

            this.destroyNode = function (context, nodeOrId) {
                // NS.destroyNode(node/Id);
            };

            NodeObj.prototype.onUpdate = function(fn) {
                console.assert(typeof fn === 'function');
                this._onUpdate = fn;
            }

            NodeObj.prototype.onUnload = function(fn) {
                console.assert(typeof fn === 'function');
                this._onUnload = fn;
            }

            // FIXME : Can context be resolved from the node?
            NodeObj.prototype.onNewChildLoaded = function (context, fn) {
                var dbConn = DataStoreService.getDatabaseConnection(context),
                    territory,
                    nodes,
                    id = this.id,
                    terrId = id + '_new_children_watch'; //FIXME: This probably needs some elaboration
                console.log(dbConn);
                if (dbConn.nodeService.territories.hasOwnProperty(terrId)) {
                    console.warn('Children are already being watched for ', terrId);
                } else {
                    dbConn.client.addUI(null, function (events) {
                        var i,
                            event;
                        for (i = 0; i < events.length; i += 1) {
                            event = events[i];
                            if (event.etype === 'load') {
                                if (dbConn.nodeService.territories.hasOwnProperty(event.eid) === false) {
                                    self.loadNode2(context, event.eid).then(function (newNode) {
                                        fn(newNode);
                                        console.log('Added new territory through onNewChildLoaded ', event.eid);
                                    });
                                } else {
                                    console.info('Node ' + event.eid + ' was loaded in ' + terrId + ' but it already has ' +
                                        'a territory.');
                                }
                            } else {
                                // These node are just watched for loading..
                            }
                        }
                    }, terrId);

                    territory = {};
                    dbConn.nodeService.territories[terrId] = territory;
                    territory.id = terrId;
                    territory.patterns = {};
                    territory.patterns[id] = {children: 1};
                    dbConn.client.updateTerritory(terrId, territory.patterns);
                }
            };

            this.loadNode2 = function (context, id) {
                var deferred = $q.defer(),
                    dbConn = DataStoreService.getDatabaseConnection(context),
                    territory,
                    nodes;

                dbConn.nodeService = dbConn.nodeService || {};
                dbConn.nodeService.nodes =  dbConn.nodeService.nodes || {};
                dbConn.nodeService.territories = dbConn.nodeService.territories || {};

                nodes = dbConn.nodeService.nodes;

                if (nodes.hasOwnProperty(id)) {
                    deferred.resolve(nodes[id]);
                    console.log('Node already loaded..');
                } else {
                    if (dbConn.nodeService.territories.hasOwnProperty(id)) {
//                        territory = dbConn.nodeService.territories[id];
                        deferred.reject('Territory exists, but node does not!');
                    } else {
                        dbConn.client.addUI(null, function (events) {
                            var i,
                                event;

                            for (i = 0; i < events.length; i += 1) {
                                event = events[i];
                                if (event.etype === 'load') {
                                    nodes[id] =  new NodeObj(dbConn, id);
                                    deferred.resolve(nodes[id]);
                                } else if (event.etype === 'update') {
                                    nodes[id]._onUpdate(event.eid);
                                } else if (event.etype === 'unload') {
                                    nodes[id]._onUnload(event.eid);
                                    nodes[id].__onUnload();
                                } else {
                                    throw 'Unexpected event type' + events[j].etype;
                                }
                            }
                        }, id);

                        territory = {};
                        dbConn.nodeService.territories[id] = territory;
                        territory.id = id;
                        territory.patterns = territory.patterns || {};
                        territory.patterns[id] = {children: 0};
                        dbConn.client.updateTerritory(id, territory.patterns);
                    }
                }

                return deferred.promise;
            };

            this.loadNode = function (context, id) {
                var deferred = $q.defer(),
                    dbConn = DataStoreService.getDatabaseConnection(context),
                    territory,
                    nodes;

                dbConn.nodeService = dbConn.nodeService || {};
                dbConn.nodeService.nodes =  dbConn.nodeService.nodes || {};
                dbConn.nodeService.territories = dbConn.nodeService.territories || {};

                nodes = dbConn.nodeService.nodes;

                if (nodes.hasOwnProperty(id)) {
                    deferred.resolve(nodes[id]);
                } else {
                    // TODO: create territory if does not exist
                    if (dbConn.nodeService.territories.hasOwnProperty(context.territoryId)) {
                        territory = dbConn.nodeService.territories[context.territoryId];
                    } else {
                        dbConn.client.addUI(null, function (events) {
                            var i,
                                event;

                            // TODO: fill in this
                            for (i = 0; i < events.length; i += 1) {
                                event = events[i];
                                if (event.eid === id && event.etype === 'load') {
                                    // TODO: when node is loaded resolve promise
                                    nodes[id] =  new NodeObj(dbConn, id);
                                    deferred.resolve(nodes[id]);
                                }
                            }

                        }, context.territoryId);

                        // TODO: add territory rule
                        territory = {};
                        territory.id = context.territoryId;
                        territory.patterns = territory.patterns || {};
                        territory.patterns[id] = {children: 0}; // FIXME: how to update this correctly ???

                        dbConn.nodeService.territories[context.territoryId] = territory;

                        dbConn.client.updateTerritory(context.territoryId, territory.patterns);
                    }
                }

                return deferred.promise;
            };

        }
    );
});<|endoftext|>


<commit_before>/**
 * Pollymer JavaScript Library v1.0.0
 * Copyright 2013 Fanout, Inc.
 * Released under the MIT license (see COPYING file in source distribution)
 */
(function () {
"use strict";
var DEBUG = true;
(function (window, undefined) {

    var NAMESPACE = "Pollymer";
    var emptyMethod = function () { };

    var consoleInfo;
    var consoleError;
    if (DEBUG) {
        // don't break if there's no console
        if (typeof (window.console) === "undefined") {
            window.console = { info: emptyMethod, error: emptyMethod };
        }
        consoleInfo = function (output) { window.console.info(output); };
        consoleError = function (output) { window.console.error(output); };
    } else {
        consoleInfo = emptyMethod;
        consoleError = emptyMethod;
    }

    var copyArray = function (array) {
        var args = Array.prototype.slice.call(arguments, 1);
        return Array.prototype.slice.apply(array, args);
    };

    var indexOfItemInArray = function (array, item) {
        for (var i = 0, length = array.length; i < length; i++) {
            if (array[i] === item) {
                return i;
            }
        }
        return -1;
    };
    var removeFromArray = function (array, item) {
        var again = true;
        while (again) {
            var index = indexOfItemInArray(array, item);
            if (index != -1) {
                array.splice(index, 1);
            } else {
                again = false;
            }
        }
    };

    var parseResponseHeaders = function (headerStr) {
        var headers = {};
        if (!headerStr) {
            return headers;
        }
        var headerPairs = headerStr.split('\u000d\u000a');
        for (var i = 0; i < headerPairs.length; i++) {
            var headerPair = headerPairs[i];
            if (headerPair[0] == '\u000a') {
                headerPair = headerPair.substring(1);
            }
            var index = headerPair.indexOf('\u003a\u0020');
            if (index > 0) {
                var key = headerPair.substring(0, index);
                headers[key] = headerPair.substring(index + 2);
            }
        }
        return headers;
    };

    var checkForErrorCode = function (codesStr, code) {
        var parts = codesStr.split(',');
        for (var i = 0; i < parts.length; i++) {
            var part = parts[i];
            var index = part.indexOf('-');
            if (index >= 0) {
                // part is a range
                var min = parseInt(part.substring(0, index), 10);
                var max = parseInt(part.substring(index + 1), 10);
                if (code >= min && code <= max) {
                    return true;
                }
            } else {
                // part is a single value
                var val = parseInt(part, 10);
                if (code == val) {
                    return true;
                }
            }
        }
        return false;
    };

    var addJsonpScriptToDom = function (src, scriptId) {
        var script = window.document.createElement("script");
        script.type = "text/javascript";
        script.id = scriptId;
        script.src = src;

        var head = window.document.getElementsByTagName("head")[0];
        head.appendChild(script);
    };
    var removeJsonpScriptFromDom = function (scriptId) {
        var script = window.document.getElementById(scriptId);
        script.parentNode.removeChild(script);
    };

    var jsonCallbacks = {
        id: 0,
        requests: {},
        getJsonpCallback: function (id) {
            var cb;
            var requests = this.requests;
            if (id in this.requests) {
                cb = function (result) { requests[id]._jsonpCallback(result); };
            } else {
                consoleInfo("no callback with id " + id);
                cb = emptyMethod;
            }
            return cb;
        },
        addJsonpCallback: function (id, obj) {
            this.requests[id] = obj;
        },
        removeJsonpCallback: function (id) {
            delete this.requests[id];
        },
        newCallbackInfo: function () {
            var callbackInfo = {
                id: "cb-" + this.id,
                scriptId: "pd-jsonp-script-" + this.id
            };
            this.id++;
            return callbackInfo;
        }
    };

    var corsAvailable = "withCredentials" in new window.XMLHttpRequest();

    var sameOrigin = function (url) {
        var loc = window.location;
        var a = window.document.createElement('a');
        a.href = url;
        return !a.hostname || (a.hostname == loc.hostname && a.port == loc.port && a.protocol == loc.protocol);
    };

    var chooseTransport = function (transportType, url) {
        var transport;
        if (transportType == transportTypes.Auto) {
            if (corsAvailable || sameOrigin(url)) {
                transport = transportTypes.Xhr;
            } else {
                transport = transportTypes.Jsonp;
            }
        } else {
            transport = transportType;
        }
        return transport;
    };

    var Events = function () {
        this._events = {};
    };
    Events.prototype._getHandlersForType = function (type) {
        if (!(type in this._events)) {
            this._events[type] = [];
        }
        return this._events[type];
    };
    Events.prototype.on = function (type, handler) {
        var handlers = this._getHandlersForType(type);
        handlers.push(handler);
    };
    Events.prototype.off = function (type) {
        if (arguments.length > 1) {
            var handler = arguments[1];
            var handlers = this._getHandlersForType(type);
            removeFromArray(handlers, handler);
        } else {
            delete this._events[type];
        }
    };
    Events.prototype.trigger = function (type, obj) {
        var args = copyArray(arguments, 2);
        var handlers = copyArray(this._getHandlersForType(type));
        for (var i = 0, n = handlers.length; i < n; i++) {
            var handler = handlers[i];
            handler.apply(obj, args);
        }
    };

    var errorTypes = {
        "TransportError": 0,
        "TimeoutError": 1
    };

    var transportTypes = {
        "Auto": 0,
        "Xhr": 1,
        "Jsonp": 2
    };

    // Pollymer.Request has callback members:
    // on('finished', int code, object result, object headers)
    // on('error', int reason)

    var Request = function () {
        if (!(this instanceof Request)) {
            throw new window.Error("Constructor called as a function");
        }

        this._events = new Events();
        this._tries = 0;
        this._delayNext = false;
        this._retryTime = 0;
        this._timer = null;
        this._jsonp = null;

        this._xhr = null;
        this._method = null;
        this._url = null;
        this._headers = null;
        this._body = null;
        this._transport = null;

        this.transport = transportTypes.Auto;
        this.rawResponse = false;
        this.maxTries = 1;
        this.maxDelay = 1000;
        this.recurring = false;
        this.withCredentials = false;
        this.timeout = 60000;
        this.errorCodes = '500-599';

        if (arguments.length > 0) {
            var config = arguments[0];
            if ("transport" in config) {
                this.transport = config.transport;
            }
            if ("rawResponse" in config) {
                this.rawResponse = config.rawResponse;
            }
            if ("maxTries" in config) {
                this.maxTries = config.maxTries;
            }
            if ("maxDelay" in config) {
                this.maxDelay = config.maxDelay;
            }
            if ("recurring" in config) {
                this.recurring = config.recurring;
            }
            if ("withCredentials" in config) {
                this.withCredentials = config.withCredentials;
            }
            if ("timeout" in config) {
                this.timeout = config.timeout;
            }
            if ("errorCodes" in config) {
                this.errorCodes = config.errorCodes;
            }
        }
    };
    Request.prototype.start = function (method, url, headers, body) {
        if (this._timer != null) {
            consoleError("pollymer: start() called on a Request object that is currently running.");
            return;
        }

        this._method = method;
        this._url = url;
        this._headers = headers;
        this._body = body;
        this._start();
    };
    Request.prototype._start = function () {
        this._tries = 0;

        var delayTime;
        if (this._delayNext) {
            this._delayNext = false;
            delayTime = Math.floor(Math.random() * this.maxDelay);
            consoleInfo("pollymer: polling again in " + delayTime + "ms");
        } else {
            delayTime = 0; // always queue the call, to prevent browser "busy"
        }

        this._initiate(delayTime);
    };
    Request.prototype.retry = function () {
        if (this._tries == 0) {
            consoleError("pollymer: retry() called on a Request object that has never been started.");
            return;
        }
        if (this._timer != null) {
            consoleError("pollymer: retry() called on a Request object that is currently running.");
            return;
        }
        this._retry();
    };
    Request.prototype._retry = function () {
        if (this._tries === 1) {
            this._retryTime = 1;
        } else if (this._tries < 8) {
            this._retryTime = this._retryTime * 2;
        }

        var delayTime = this._retryTime * 1000;
        delayTime += Math.floor(Math.random() * this.maxDelay);
        consoleInfo("pollymer: trying again in " + delayTime + "ms");

        this._initiate(delayTime);
    };
    Request.prototype._initiate = function (delayMsecs) {
        var self = this;
        self._timer = window.setTimeout(function () { self._startConnect(); }, delayMsecs);
    };
    Request.prototype._startConnect = function () {
        var self = this;
        this._timer = window.setTimeout(function () { self._timeout(); }, this.timeout);

        this._tries++;

        var method = this._method;
        var url = (typeof (this._url) == "function") ? this._url() : this._url;
        var headers = this._headers;
        var body = this._body;

        // Create a copy of the transport because we don't want
        // to give public access to it (changing it between now and
        // cleanup would be a no-no)
        this._transport = chooseTransport(this.transport, url);

        if (this._transport == transportTypes.Xhr) {
            this._xhr = this._startXhr(method, url, headers, body);
        } else { // Jsonp
            this._jsonp = this._startJsonp(method, url, headers, body);
        }
    };
    Request.prototype._cleanupConnect = function (abort) {
        window.clearTimeout(this._timer);
        this._timer = null;

        if (this._transport == transportTypes.Xhr) {
            consoleInfo("pollymer: XHR cleanup");
            this._cleanupXhr(this._xhr, abort);
            this._xhr = null;
        } else { // Jsonp
            consoleInfo("pollymer: json-p " + this._jsonp.id + " cleanup");
            this._cleanupJsonp(this._jsonp, abort);
            this._jsonp = null;
        }
    };
    Request.prototype.abort = function () {
        this._cleanupConnect(true);
    };
    Request.prototype.on = function (type, handler) {
        this._events.on(type, handler);
    };
    Request.prototype.off = function (type) {
        var args = copyArray(arguments, 1);
        args.unshift(type);
        this._events.off.apply(this._events, args);
    };
    Request.prototype._startXhr = function (method, url, headers, body) {
        var xhr = new window.XMLHttpRequest();

        // If header has Authorization, and cors is available, then set the
        // withCredentials flag.
        if (this.withCredentials && corsAvailable) {
            xhr.withCredentials = true;
        }

        var self = this;
        xhr.onreadystatechange = function () { self._xhrCallback(); };
        xhr.open(method, url, true);

        for (var key in headers) {
            if (headers.hasOwnProperty(key)) {
                xhr.setRequestHeader(key, headers[key]);
            }
        }

        xhr.send(body);

        consoleInfo("pollymer: XHR start " + url);

        return xhr;
    };
    Request.prototype._cleanupXhr = function (xhr, abort) {
        if (xhr != null) {
            xhr.onreadystatechange = emptyMethod;
            if (abort) {
                xhr.abort();
            }
        }
    };
    Request.prototype._xhrCallback = function () {
        var xhr = this._xhr;
        if (xhr != null && xhr.readyState === 4) {
            consoleInfo("pollymer: XHR finished");

            var code = xhr.status;
            var reason = xhr.statusText;
            var headers = parseResponseHeaders(xhr.getAllResponseHeaders());
            var body = xhr.responseText;

            this._handleResponse(code, reason, headers, body);
        }
    };
    Request.prototype._startJsonp = function (method, url, headers, body) {
        var jsonp = jsonCallbacks.newCallbackInfo();

        var paramList = [
            "callback=" + encodeURIComponent("window['" + NAMESPACE + "']._getJsonpCallback(\"" + jsonp.id + "\")")
        ];

        if (method != "GET") {
            paramList.push("_method=" + encodeURIComponent(method));
        }
        if (headers) {
            paramList.push("_headers=" + encodeURIComponent(JSON.stringify(headers)));
        }
        if (body) {
            paramList.push("_body=" + encodeURIComponent(body));
        }
        var params = paramList.join("&");

        var src = (url.indexOf("?") != -1) ? url + "&" + params : url + "?" + params;

        jsonCallbacks.addJsonpCallback(jsonp.id, this);
        addJsonpScriptToDom(src, jsonp.scriptId);

        consoleInfo("pollymer: json-p start " + jsonp.id + " " + src);

        return jsonp;
    };
    Request.prototype._cleanupJsonp = function (jsonp, abort) {
        if (jsonp != null) {
            jsonCallbacks.removeJsonpCallback(jsonp.id);
            removeJsonpScriptFromDom(jsonp.scriptId);
        }
    };
    Request.prototype._jsonpCallback = function (result) {
        consoleInfo("pollymer: json-p " + this._jsonp.id + " finished");

        var code = ("code" in result) ? result.code : 0;
        var reason = ("reason" in result) ? result.reason : null;
        var headers = ("headers" in result) ? result.headers : {};
        var body = ("body" in result) ? result.body : null;

        this._handleResponse(code, reason, headers, body);
    };
    Request.prototype._handleResponse = function (code, reason, headers, body) {
        this._cleanupConnect();

        if ((code == 0 || checkForErrorCode(this.errorCodes, code)) &&
            (this.maxTries == -1 || this._tries < this.maxTries)) {
            this._retry();
        } else {
            if (code > 0) {
                var result;
                if (this.rawResponse) {
                    result = body;
                } else {
                    try {
                        result = JSON.parse(body);
                    } catch (e) {
                        result = body;
                    }
                }
                this._finished(code, result, headers);
                if (this.recurring && code >= 200 && code < 300) {
                    this._start();
                }
            } else {
                this._error(errorTypes.TransportError);
            }
        }
    };
    Request.prototype._timeout = function () {
        this._cleanupConnect(true);

        if (this.maxTries == -1 || this._tries < this.maxTries) {
            this._retry();
        } else {
            this._error(errorTypes.TimeoutError);
        }
    };
    Request.prototype._finished = function (code, result, headers) {
        this._delayNext = true;
        this._events.trigger('finished', this, code, result, headers);
    };
    Request.prototype._error = function (reason) {
        this._delayNext = true;
        this._events.trigger('error', this, reason);
    };

    var exports = {
        Request: Request,
        TransportTypes: transportTypes,
        ErrorTypes: errorTypes,
        _getJsonpCallback: function (id) {
            return jsonCallbacks.getJsonpCallback(id);
        }
    };
    window[NAMESPACE] = window[NAMESPACE] || exports;

})(window);
})();
<commit_msg>add a comment about the IE workaround<commit_after>/**
 * Pollymer JavaScript Library v1.0.0
 * Copyright 2013 Fanout, Inc.
 * Released under the MIT license (see COPYING file in source distribution)
 */
(function () {
"use strict";
var DEBUG = true;
(function (window, undefined) {

    var NAMESPACE = "Pollymer";
    var emptyMethod = function () { };

    var consoleInfo;
    var consoleError;
    if (DEBUG) {
        // don't break if there's no console
        if (typeof (window.console) === "undefined") {
            window.console = { info: emptyMethod, error: emptyMethod };
        }
        consoleInfo = function (output) { window.console.info(output); };
        consoleError = function (output) { window.console.error(output); };
    } else {
        consoleInfo = emptyMethod;
        consoleError = emptyMethod;
    }

    var copyArray = function (array) {
        var args = Array.prototype.slice.call(arguments, 1);
        return Array.prototype.slice.apply(array, args);
    };

    var indexOfItemInArray = function (array, item) {
        for (var i = 0, length = array.length; i < length; i++) {
            if (array[i] === item) {
                return i;
            }
        }
        return -1;
    };
    var removeFromArray = function (array, item) {
        var again = true;
        while (again) {
            var index = indexOfItemInArray(array, item);
            if (index != -1) {
                array.splice(index, 1);
            } else {
                again = false;
            }
        }
    };

    var parseResponseHeaders = function (headerStr) {
        var headers = {};
        if (!headerStr) {
            return headers;
        }
        var headerPairs = headerStr.split('\u000d\u000a');
        for (var i = 0; i < headerPairs.length; i++) {
            var headerPair = headerPairs[i];
            // IE sometimes puts a newline at the start of header names
            if (headerPair[0] == '\u000a') {
                headerPair = headerPair.substring(1);
            }
            var index = headerPair.indexOf('\u003a\u0020');
            if (index > 0) {
                var key = headerPair.substring(0, index);
                headers[key] = headerPair.substring(index + 2);
            }
        }
        return headers;
    };

    var checkForErrorCode = function (codesStr, code) {
        var parts = codesStr.split(',');
        for (var i = 0; i < parts.length; i++) {
            var part = parts[i];
            var index = part.indexOf('-');
            if (index >= 0) {
                // part is a range
                var min = parseInt(part.substring(0, index), 10);
                var max = parseInt(part.substring(index + 1), 10);
                if (code >= min && code <= max) {
                    return true;
                }
            } else {
                // part is a single value
                var val = parseInt(part, 10);
                if (code == val) {
                    return true;
                }
            }
        }
        return false;
    };

    var addJsonpScriptToDom = function (src, scriptId) {
        var script = window.document.createElement("script");
        script.type = "text/javascript";
        script.id = scriptId;
        script.src = src;

        var head = window.document.getElementsByTagName("head")[0];
        head.appendChild(script);
    };
    var removeJsonpScriptFromDom = function (scriptId) {
        var script = window.document.getElementById(scriptId);
        script.parentNode.removeChild(script);
    };

    var jsonCallbacks = {
        id: 0,
        requests: {},
        getJsonpCallback: function (id) {
            var cb;
            var requests = this.requests;
            if (id in this.requests) {
                cb = function (result) { requests[id]._jsonpCallback(result); };
            } else {
                consoleInfo("no callback with id " + id);
                cb = emptyMethod;
            }
            return cb;
        },
        addJsonpCallback: function (id, obj) {
            this.requests[id] = obj;
        },
        removeJsonpCallback: function (id) {
            delete this.requests[id];
        },
        newCallbackInfo: function () {
            var callbackInfo = {
                id: "cb-" + this.id,
                scriptId: "pd-jsonp-script-" + this.id
            };
            this.id++;
            return callbackInfo;
        }
    };

    var corsAvailable = "withCredentials" in new window.XMLHttpRequest();

    var sameOrigin = function (url) {
        var loc = window.location;
        var a = window.document.createElement('a');
        a.href = url;
        return !a.hostname || (a.hostname == loc.hostname && a.port == loc.port && a.protocol == loc.protocol);
    };

    var chooseTransport = function (transportType, url) {
        var transport;
        if (transportType == transportTypes.Auto) {
            if (corsAvailable || sameOrigin(url)) {
                transport = transportTypes.Xhr;
            } else {
                transport = transportTypes.Jsonp;
            }
        } else {
            transport = transportType;
        }
        return transport;
    };

    var Events = function () {
        this._events = {};
    };
    Events.prototype._getHandlersForType = function (type) {
        if (!(type in this._events)) {
            this._events[type] = [];
        }
        return this._events[type];
    };
    Events.prototype.on = function (type, handler) {
        var handlers = this._getHandlersForType(type);
        handlers.push(handler);
    };
    Events.prototype.off = function (type) {
        if (arguments.length > 1) {
            var handler = arguments[1];
            var handlers = this._getHandlersForType(type);
            removeFromArray(handlers, handler);
        } else {
            delete this._events[type];
        }
    };
    Events.prototype.trigger = function (type, obj) {
        var args = copyArray(arguments, 2);
        var handlers = copyArray(this._getHandlersForType(type));
        for (var i = 0, n = handlers.length; i < n; i++) {
            var handler = handlers[i];
            handler.apply(obj, args);
        }
    };

    var errorTypes = {
        "TransportError": 0,
        "TimeoutError": 1
    };

    var transportTypes = {
        "Auto": 0,
        "Xhr": 1,
        "Jsonp": 2
    };

    // Pollymer.Request has callback members:
    // on('finished', int code, object result, object headers)
    // on('error', int reason)

    var Request = function () {
        if (!(this instanceof Request)) {
            throw new window.Error("Constructor called as a function");
        }

        this._events = new Events();
        this._tries = 0;
        this._delayNext = false;
        this._retryTime = 0;
        this._timer = null;
        this._jsonp = null;

        this._xhr = null;
        this._method = null;
        this._url = null;
        this._headers = null;
        this._body = null;
        this._transport = null;

        this.transport = transportTypes.Auto;
        this.rawResponse = false;
        this.maxTries = 1;
        this.maxDelay = 1000;
        this.recurring = false;
        this.withCredentials = false;
        this.timeout = 60000;
        this.errorCodes = '500-599';

        if (arguments.length > 0) {
            var config = arguments[0];
            if ("transport" in config) {
                this.transport = config.transport;
            }
            if ("rawResponse" in config) {
                this.rawResponse = config.rawResponse;
            }
            if ("maxTries" in config) {
                this.maxTries = config.maxTries;
            }
            if ("maxDelay" in config) {
                this.maxDelay = config.maxDelay;
            }
            if ("recurring" in config) {
                this.recurring = config.recurring;
            }
            if ("withCredentials" in config) {
                this.withCredentials = config.withCredentials;
            }
            if ("timeout" in config) {
                this.timeout = config.timeout;
            }
            if ("errorCodes" in config) {
                this.errorCodes = config.errorCodes;
            }
        }
    };
    Request.prototype.start = function (method, url, headers, body) {
        if (this._timer != null) {
            consoleError("pollymer: start() called on a Request object that is currently running.");
            return;
        }

        this._method = method;
        this._url = url;
        this._headers = headers;
        this._body = body;
        this._start();
    };
    Request.prototype._start = function () {
        this._tries = 0;

        var delayTime;
        if (this._delayNext) {
            this._delayNext = false;
            delayTime = Math.floor(Math.random() * this.maxDelay);
            consoleInfo("pollymer: polling again in " + delayTime + "ms");
        } else {
            delayTime = 0; // always queue the call, to prevent browser "busy"
        }

        this._initiate(delayTime);
    };
    Request.prototype.retry = function () {
        if (this._tries == 0) {
            consoleError("pollymer: retry() called on a Request object that has never been started.");
            return;
        }
        if (this._timer != null) {
            consoleError("pollymer: retry() called on a Request object that is currently running.");
            return;
        }
        this._retry();
    };
    Request.prototype._retry = function () {
        if (this._tries === 1) {
            this._retryTime = 1;
        } else if (this._tries < 8) {
            this._retryTime = this._retryTime * 2;
        }

        var delayTime = this._retryTime * 1000;
        delayTime += Math.floor(Math.random() * this.maxDelay);
        consoleInfo("pollymer: trying again in " + delayTime + "ms");

        this._initiate(delayTime);
    };
    Request.prototype._initiate = function (delayMsecs) {
        var self = this;
        self._timer = window.setTimeout(function () { self._startConnect(); }, delayMsecs);
    };
    Request.prototype._startConnect = function () {
        var self = this;
        this._timer = window.setTimeout(function () { self._timeout(); }, this.timeout);

        this._tries++;

        var method = this._method;
        var url = (typeof (this._url) == "function") ? this._url() : this._url;
        var headers = this._headers;
        var body = this._body;

        // Create a copy of the transport because we don't want
        // to give public access to it (changing it between now and
        // cleanup would be a no-no)
        this._transport = chooseTransport(this.transport, url);

        if (this._transport == transportTypes.Xhr) {
            this._xhr = this._startXhr(method, url, headers, body);
        } else { // Jsonp
            this._jsonp = this._startJsonp(method, url, headers, body);
        }
    };
    Request.prototype._cleanupConnect = function (abort) {
        window.clearTimeout(this._timer);
        this._timer = null;

        if (this._transport == transportTypes.Xhr) {
            consoleInfo("pollymer: XHR cleanup");
            this._cleanupXhr(this._xhr, abort);
            this._xhr = null;
        } else { // Jsonp
            consoleInfo("pollymer: json-p " + this._jsonp.id + " cleanup");
            this._cleanupJsonp(this._jsonp, abort);
            this._jsonp = null;
        }
    };
    Request.prototype.abort = function () {
        this._cleanupConnect(true);
    };
    Request.prototype.on = function (type, handler) {
        this._events.on(type, handler);
    };
    Request.prototype.off = function (type) {
        var args = copyArray(arguments, 1);
        args.unshift(type);
        this._events.off.apply(this._events, args);
    };
    Request.prototype._startXhr = function (method, url, headers, body) {
        var xhr = new window.XMLHttpRequest();

        // If header has Authorization, and cors is available, then set the
        // withCredentials flag.
        if (this.withCredentials && corsAvailable) {
            xhr.withCredentials = true;
        }

        var self = this;
        xhr.onreadystatechange = function () { self._xhrCallback(); };
        xhr.open(method, url, true);

        for (var key in headers) {
            if (headers.hasOwnProperty(key)) {
                xhr.setRequestHeader(key, headers[key]);
            }
        }

        xhr.send(body);

        consoleInfo("pollymer: XHR start " + url);

        return xhr;
    };
    Request.prototype._cleanupXhr = function (xhr, abort) {
        if (xhr != null) {
            xhr.onreadystatechange = emptyMethod;
            if (abort) {
                xhr.abort();
            }
        }
    };
    Request.prototype._xhrCallback = function () {
        var xhr = this._xhr;
        if (xhr != null && xhr.readyState === 4) {
            consoleInfo("pollymer: XHR finished");

            var code = xhr.status;
            var reason = xhr.statusText;
            var headers = parseResponseHeaders(xhr.getAllResponseHeaders());
            var body = xhr.responseText;

            this._handleResponse(code, reason, headers, body);
        }
    };
    Request.prototype._startJsonp = function (method, url, headers, body) {
        var jsonp = jsonCallbacks.newCallbackInfo();

        var paramList = [
            "callback=" + encodeURIComponent("window['" + NAMESPACE + "']._getJsonpCallback(\"" + jsonp.id + "\")")
        ];

        if (method != "GET") {
            paramList.push("_method=" + encodeURIComponent(method));
        }
        if (headers) {
            paramList.push("_headers=" + encodeURIComponent(JSON.stringify(headers)));
        }
        if (body) {
            paramList.push("_body=" + encodeURIComponent(body));
        }
        var params = paramList.join("&");

        var src = (url.indexOf("?") != -1) ? url + "&" + params : url + "?" + params;

        jsonCallbacks.addJsonpCallback(jsonp.id, this);
        addJsonpScriptToDom(src, jsonp.scriptId);

        consoleInfo("pollymer: json-p start " + jsonp.id + " " + src);

        return jsonp;
    };
    Request.prototype._cleanupJsonp = function (jsonp, abort) {
        if (jsonp != null) {
            jsonCallbacks.removeJsonpCallback(jsonp.id);
            removeJsonpScriptFromDom(jsonp.scriptId);
        }
    };
    Request.prototype._jsonpCallback = function (result) {
        consoleInfo("pollymer: json-p " + this._jsonp.id + " finished");

        var code = ("code" in result) ? result.code : 0;
        var reason = ("reason" in result) ? result.reason : null;
        var headers = ("headers" in result) ? result.headers : {};
        var body = ("body" in result) ? result.body : null;

        this._handleResponse(code, reason, headers, body);
    };
    Request.prototype._handleResponse = function (code, reason, headers, body) {
        this._cleanupConnect();

        if ((code == 0 || checkForErrorCode(this.errorCodes, code)) &&
            (this.maxTries == -1 || this._tries < this.maxTries)) {
            this._retry();
        } else {
            if (code > 0) {
                var result;
                if (this.rawResponse) {
                    result = body;
                } else {
                    try {
                        result = JSON.parse(body);
                    } catch (e) {
                        result = body;
                    }
                }
                this._finished(code, result, headers);
                if (this.recurring && code >= 200 && code < 300) {
                    this._start();
                }
            } else {
                this._error(errorTypes.TransportError);
            }
        }
    };
    Request.prototype._timeout = function () {
        this._cleanupConnect(true);

        if (this.maxTries == -1 || this._tries < this.maxTries) {
            this._retry();
        } else {
            this._error(errorTypes.TimeoutError);
        }
    };
    Request.prototype._finished = function (code, result, headers) {
        this._delayNext = true;
        this._events.trigger('finished', this, code, result, headers);
    };
    Request.prototype._error = function (reason) {
        this._delayNext = true;
        this._events.trigger('error', this, reason);
    };

    var exports = {
        Request: Request,
        TransportTypes: transportTypes,
        ErrorTypes: errorTypes,
        _getJsonpCallback: function (id) {
            return jsonCallbacks.getJsonpCallback(id);
        }
    };
    window[NAMESPACE] = window[NAMESPACE] || exports;

})(window);
})();
<|endoftext|>


<commit_before>module.exports = {
  gruntfile: ['gruntfile.js'],
  options: { jshintrc: '.jshintrc'},
  lib: {
    src: ['lib/**/*.js'],
    options: { jshintrc: '.jshintrc'}
  },
  tests: {
    src: ['tests/**/*.spec.js'],
    options: { jshintrc: 'tests/.jshintrc'}
  },
  dist: {
    src: ['dist/ember-oauth2.js', 'dist/ember-oauth2.amd.js'],
    options: { jshintrc: '.jshintrc'}
  }
}
<commit_msg>update formatting<commit_after>module.exports = {
  gruntfile: ['gruntfile.js'],
  options: { jshintrc: '.jshintrc'},
  lib: {
    src: ['lib/**/*.js'],
    options: { jshintrc: '.jshintrc' }
  },
  tests: {
    src: ['tests/**/*.spec.js'],
    options: { jshintrc: 'tests/.jshintrc' }
  },
  dist: {
    src: ['dist/ember-oauth2.js', 'dist/ember-oauth2.amd.js'],
    options: { jshintrc: '.jshintrc' }
  }
}
<|endoftext|>


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

// https://0x657573.wordpress.com/2010/11/30/3x3-sudoku-puzzle-solver/

foam.CLASS({
  package: 'com.google.foam.demos.sudoku',
  name: 'Sudoku',
  extends: 'foam.u2.Controller',

  classes: [
    {
      name: 'Cell',
      extends: 'foam.u2.TextField'

/*
      properties: [
        {
          name: 'data',
          adapt: function(_, v) { return v || ''; },
          displayWidth: 1
        }
      ]
      */
    }
  ],

  properties: [
    {
      name: 'cells',
      adapt: function(_, cs) {
        cs = foam.Array.clone(cs);
        for ( var a = 0 ; a < 3 ; a++ )
          for ( var b = 0 ; b < 3 ; b++ )
            for ( var c = 0 ; c < 3 ; c++ )
              for ( var d = 0 ; d < 3 ; d++ )
                cs[a][b][c][d] = this.Cell.create({data: cs[a][b][c][d]});
        return cs;
      }
    }
  ],

  methods: [
    function init() {
      this.SUPER();
      this.cells = [
        [[[0,0,0],[0,7,1],[0,0,5]], [[5,0,0],[0,6,9],[0,7,1]], [[0,7,1],[8,5,3],[4,2,0]]],
        [[[0,1,0],[0,0,2],[0,0,0]], [[7,8,0],[1,5,4],[0,9,2]], [[0,4,0],[3,6,0],[1,8,0]]],
        [[[0,6,4],[0,2,3],[0,5,0]], [[9,0,5],[0,1,0],[0,0,0]], [[7,0,0],[5,9,0],[0,0,0]]]
      ];
    },

    function initE(X) {
      var cells = this.cells;

      for ( var a = 0 ; a < 3 ; a++ ) {
        this.start().style({display: 'block'}).call(function() {
          for ( var b = 0 ; b < 3 ; b++ ) {
            this.start().style({display: 'inline-block', border:'1px solid black'}).call(function() {
              for ( var c = 0 ; c < 3 ; c++ ) {
                this.start().call(function() {
                  for ( var d = 0 ; d < 3 ; d++ ) {
                    this.add(cells[a][b][c][d]);
                  }
                });
              }
            });
          }
        });
      }

      this.add(this.SOLVE);
    },

    function get(a, b, c, d) {
      return this.cells[a][b][c][d].data;
    },

    function set(a, b, c, d, n) {
      for ( var x = 0 ; x < 3 ; x++ )
        for ( var y = 0 ; y < 3 ; y++ )
          if ( this.get(a, b, x, y) == n || this.get(a, x, c, y) == n || this.get(x, b, y, d) == n ) return false;
      this.cells[a][b][c][d].data = n;
      return true;
    },

    function s(a, b, c, d) {
      if ( d == 3 ) { d = 0; c++; }
      if ( c == 3 ) { c = 0; b++; }
      if ( b == 3 ) { b = 0; a++; }
      if ( a == 3 ) return true;
      if ( this.get(a,b,c,d) ) return this.s(a, b, c, d+1);
      for ( var n = 1 ; n <= 9 ; n++ )
        if ( this.set(a, b, c, d, n) && this.s(a, b, c, d+1) ) return true;
      this.cells[a][b][c][d].data = 0;
      return false;
    }
  ],

  actions: [
    function solve() { this.s(0,0,0,0); }
  ]
});
<commit_msg>Finished sudoku demo.<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
 */

// https://0x657573.wordpress.com/2010/11/30/3x3-sudoku-puzzle-solver/

foam.CLASS({
  package: 'com.google.foam.demos.sudoku',
  name: 'Sudoku',
  extends: 'foam.u2.Controller',

  classes: [
    {
      name: 'Cell',
      extends: 'foam.u2.TextField',

      properties: [
        {
          name: 'data',
          adapt: function(_, v) { return ! v || v == '0' || v.toString().trim() == '' ? '' : v; }
        }
      ]
    }
  ],

  properties: [
    {
      name: 'cells',
      adapt: function(_, cs) {
        cs = foam.Array.clone(cs);
        for ( var a = 0 ; a < 3 ; a++ )
          for ( var b = 0 ; b < 3 ; b++ )
            for ( var c = 0 ; c < 3 ; c++ )
              for ( var d = 0 ; d < 3 ; d++ )
                cs[a][b][c][d] = this.Cell.create({data: cs[a][b][c][d]});
        return cs;
      }
    }
  ],

  methods: [
    function init() {
      //this.SUPER();
      this.cells = [
        [[[0,0,0],[0,7,1],[0,0,5]], [[5,0,0],[0,6,9],[0,7,1]], [[0,7,1],[8,5,3],[4,2,0]]],
        [[[0,1,0],[0,0,2],[0,0,0]], [[7,8,0],[1,5,4],[0,9,2]], [[0,4,0],[3,6,0],[1,8,0]]],
        [[[0,6,4],[0,2,3],[0,5,0]], [[9,0,5],[0,1,0],[0,0,0]], [[7,0,0],[5,9,0],[0,0,0]]]
      ];
    },

    function initE(X) {
      var cells = this.cells;

      for ( var a = 0 ; a < 3 ; a++ ) {
        this.start().style({display: 'block'}).call(function() {
          for ( var b = 0 ; b < 3 ; b++ ) {
            this.start().style({display: 'inline-block', border:'1px solid black'}).call(function() {
              for ( var c = 0 ; c < 3 ; c++ ) {
                this.start().call(function() {
                  for ( var d = 0 ; d < 3 ; d++ ) {
                    this.add(cells[a][b][c][d]);
                  }
                });
              }
            });
          }
        });
      }

      this.add(this.SOLVE);
    },

    function get(a, b, c, d) { return this.cells[a][b][c][d].data; },

    function set(a, b, c, d, n) {
      for ( var x = 0 ; x < 3 ; x++ )
        for ( var y = 0 ; y < 3 ; y++ )
          if ( this.get(a, b, x, y) == n || this.get(a, x, c, y) == n || this.get(x, b, y, d) == n ) return false;
      this.cells[a][b][c][d].data = n;
      return true;
    },

    function s(a, b, c, d) {
      if ( d == 3 ) { d = 0; c++; }
      if ( c == 3 ) { c = 0; b++; }
      if ( b == 3 ) { b = 0; a++; }
      if ( a == 3 ) return true;
      if ( this.get(a,b,c,d) ) return this.s(a, b, c, d+1);
      for ( var n = 1 ; n <= 9 ; n++ )
        if ( this.set(a, b, c, d, n) && this.s(a, b, c, d+1) ) return true;
      this.cells[a][b][c][d].data = 0;
      return false;
    }
  ],

  actions: [ function solve() { this.s(0,0,0,0); } ]
});
<|endoftext|>


<commit_before>/*jslint esversion: 6 */
let timerEventId;

function publishTimer(timer) {
    "use strict";

    const timerElement = document.getElementById("timerText");
    timerElement.innerHTML = timer;
    document.title = "[" + timer + "] Pomodoro Timer";
}

function stopTimer() {
    const alarmElement = document.getElementById("alarm");

    clearInterval(timerEventId);
    alarmElement.pause();
    publishTimer('00:00');
};

function countdown(interval) {
    "use strict";

    const alarmElement = document.getElementById("alarm");

    let endTime;

    function formatTimeSegment(segment) {
        if (segment.toString().length < 2) {
            segment = "0" + segment;
        }

        return segment;
    };

    function setTimer() {
        let formattedTime, timeLeft, hours, minutes, seconds;
        timeLeft = new Date(endTime - Date.now());

        if (timeLeft.getTime() < 1000) {
            formattedTime = "00:00";
            publishTimer(formattedTime);
            clearInterval(timerEventId);
            alarmElement.currentTime = 0;
            alarmElement.play();
        } else {
            hours = timeLeft.getUTCHours();
            minutes = formatTimeSegment(timeLeft.getUTCMinutes());
            seconds = formatTimeSegment(timeLeft.getUTCSeconds());
            formattedTime = (hours ? hours + ":" + minutes : minutes) + ":" + seconds;
            publishTimer(formattedTime);
        }
    };

    clearInterval(timerEventId);
    alarmElement.pause();
    endTime = Date.now() + (1000 * interval) + 1000;
    timerEventId = setInterval(setTimer, 1000);
};

window.onload = function () {
    "use strict";

    stopTimer();
};
<commit_msg>Increasing interval for fluid time change<commit_after>/*jslint esversion: 6 */
let timerEventId;

function publishTimer(timer) {
    "use strict";

    const timerElement = document.getElementById("timerText");
    timerElement.innerHTML = timer;
    document.title = "[" + timer + "] Pomodoro Timer";
}

function stopTimer() {
    const alarmElement = document.getElementById("alarm");

    clearInterval(timerEventId);
    alarmElement.pause();
    publishTimer('00:00');
};

function countdown(interval) {
    "use strict";

    const alarmElement = document.getElementById("alarm");

    let endTime;

    function formatTimeSegment(segment) {
        if (segment.toString().length < 2) {
            segment = "0" + segment;
        }

        return segment;
    };

    function setTimer() {
        let formattedTime, timeLeft, hours, minutes, seconds;
        timeLeft = new Date(endTime - Date.now());

        if (timeLeft.getTime() < 1000) {
            formattedTime = "00:00";
            publishTimer(formattedTime);
            clearInterval(timerEventId);
            alarmElement.currentTime = 0;
            alarmElement.play();
        } else {
            hours = timeLeft.getUTCHours();
            minutes = formatTimeSegment(timeLeft.getUTCMinutes());
            seconds = formatTimeSegment(timeLeft.getUTCSeconds());
            formattedTime = (hours ? hours + ":" + minutes : minutes) + ":" + seconds;
            publishTimer(formattedTime);
        }
    };

    clearInterval(timerEventId);
    alarmElement.pause();
    endTime = Date.now() + (1000 * interval) + 1000;
    timerEventId = setInterval(setTimer, 100);
};

window.onload = function () {
    "use strict";

    stopTimer();
};
<|endoftext|>


<commit_before>/**
 * Simple HTTP requests monitor and analyzer
 */
exports.version = '1.1';

exports.module = function(phantomas) {
	// imports
	var HTTP_STATUS_CODES = phantomas.require('http').STATUS_CODES,
		parseUrl = phantomas.require('url').parse;

	var requests = [];

	// register metric
	phantomas.setMetric('requests');
	phantomas.setMetric('gzipRequests');
	phantomas.setMetric('postRequests');
	phantomas.setMetric('httpsRequests');
	phantomas.setMetric('notFound');
	phantomas.setMetric('timeToFirstByte');
	phantomas.setMetric('timeToLastByte');
	phantomas.setMetric('bodySize'); // content only
	phantomas.setMetric('contentLength'); // content only

	// parse given URL to get protocol and domain
	function parseEntryUrl(entry) {
		var parsed;

		if (entry.url.indexOf('data:') !== 0) {
			// @see http://nodejs.org/api/url.html#url_url
			parsed = parseUrl(entry.url) || {};

			entry.protocol = parsed.protocol.replace(':', '');
			entry.domain = parsed.hostname;
			entry.query = parsed.query;

			if (entry.protocol === 'https') {
				entry.isSSL = true;
			}
		}
		else {
			// base64 encoded data
			entry.domain = false;
			entry.protocol = false;
			entry.isBase64 = true;
		}
	}

	// when the monitoring started?
	var loadStartedTime;
	phantomas.on('loadStarted', function(res) {
		loadStartedTime = Date.now();
	});

	phantomas.on('onResourceRequested', function(res, request) {
		// store current request data
		var entry = requests[res.id] = {
			id: res.id,
			url: res.url,
			method: res.method,
			requestHeaders: {},
			sendTime: res.time,
			bodySize: 0,
			isBlocked: false
		};

		// allow modules to block requests
		entry.block = function() {
			this.isBlocked = true;
		};

		res.headers.forEach(function(header) {
			entry.requestHeaders[header.name] = header.value;

			switch (header.name.toLowerCase()) {
				// AJAX requests
				case 'x-requested-with':
					if (header.value === 'XMLHttpRequest') {
						entry.isAjax = true;
					}
					break;
			}
		});

		parseEntryUrl(entry);

		if (entry.isBase64) {
			return;
		}

		// give modules a chance to block requests using entry.block()
		// @see https://github.com/ariya/phantomjs/issues/10230
		phantomas.emit('beforeSend', entry, res);

		if ( (entry.isBlocked === true) && (typeof request !== 'undefined') ) {
			phantomas.log('Blocked request: <' + entry.url + '>');
			request.abort();
			return;
		}

		// proceed
		phantomas.emit('send', entry, res);
	});

	phantomas.on('onResourceReceived', function(res) {
		// current request data
		var entry = requests[res.id] || {};

		// fix for blocked requests still "emitting" onResourceReceived with "stage" = 'end' and empty "status" (issue #122)
		if (res.status === null ) {
			if (entry.isBlocked) {
				return;
			} else {
				phantomas.emit('abort', entry, res);
			}
		}

		// current request data
		var entry = requests[res.id] || {};

		switch(res.stage) {
			// the beginning of response
			case 'start':
				entry.recvStartTime = res.time;
				entry.timeToFirstByte = res.time - entry.sendTime;

				// FIXME: buggy
				// @see https://github.com/ariya/phantomjs/issues/10169
				entry.bodySize += res.bodySize || 0;
				break;

			// the end of response
			case 'end':
				// timing
				entry.recvEndTime = res.time;
				entry.timeToLastByte = res.time - entry.sendTime;
				entry.receiveTime = entry.recvEndTime - entry.recvStartTime;

				// request method
				switch(entry.method) {
					case 'POST':
						phantomas.incrMetric('postRequests');
						phantomas.addOffender('postRequests', entry.url);
						break;
				}

				// asset type
				entry.type = 'other';

				// analyze HTTP headers
				entry.headers = {};
				res.headers.forEach(function(header) {
					entry.headers[header.name] = header.value;

					switch (header.name.toLowerCase()) {
						// TODO: why it's not gzipped?
						// because: https://github.com/ariya/phantomjs/issues/10156
						// should equal bodySize
						case 'content-length':
							entry.contentLength = parseInt(header.value, 10);
							break;

						// detect content type
						case 'content-type':
							// parse header value
							var value = header.value.split(';').shift().toLowerCase();

							switch(value) {
								case 'text/html':
									entry.type = 'html';
									entry.isHTML = true;
									break;

								case 'text/css':
									entry.type = 'css';
									entry.isCSS = true;
									break;

								case 'application/x-javascript':
								case 'application/javascript':
								case 'text/javascript':
									entry.type = 'js';
									entry.isJS = true;
									break;

								case 'application/json':
									entry.type = 'json';
									entry.isJSON = true;
									break;

								case 'image/png':
								case 'image/jpeg':
								case 'image/gif':
								case 'image/svg+xml':
									entry.type = 'image';
									entry.isImage = true;
									break;

								// @see http://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts
								case 'application/font-wof':
								case 'application/font-woff':
								case 'application/vnd.ms-fontobject':
								case 'application/x-font-opentype':
								case 'application/x-font-truetype':
								case 'application/x-font-ttf':
								case 'application/x-font-woff':
									entry.type = 'webfont';
									entry.isWebFont = true;
									break;

								case 'application/octet-stream':
									var ext = (entry.url || '').split('.').pop();

									switch(ext) {
										// @see http://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts#comment-8077637
										case 'otf':
											entry.type = 'webfont';
											entry.isWebFont = true;
											break;
									}
									break;

								default:
									phantomas.log('Unknown content type found: ' + value + ' for <' + entry.url + '>');
							}
							break;

						// detect content encoding
						case 'content-encoding':
							if (header.value === 'gzip') {
								entry.gzip = true;
							}
							break;
					}
				});

				parseEntryUrl(entry);

				// HTTP code
				entry.status = res.status || 200 /* for base64 data */;
				entry.statusText = HTTP_STATUS_CODES[entry.status];

				switch(entry.status) {
					case 301: // Moved Permanently
					case 302: // Found
						entry.isRedirect = true;
						break;

					case 404: // Not Found
						phantomas.incrMetric('notFound');
						phantomas.addOffender('notFound', entry.url);
						break;
				}

				// requests stats
				if (!entry.isBase64) {
					phantomas.incrMetric('requests');

					phantomas.incrMetric('bodySize', entry.bodySize); // content only
					phantomas.incrMetric('contentLength', entry.contentLength || entry.bodySize); // content only
				}

				if (entry.gzip) {
					phantomas.incrMetric('gzipRequests');
				}

				if (entry.isSSL) {
					phantomas.incrMetric('httpsRequests');
					phantomas.addOffender('httpsRequests', entry.url);
				}

				// emit an event for other modules
				phantomas.emit(entry.isBase64 ? 'base64recv' : 'recv' , entry, res);
				//phantomas.log(entry);
				break;
		}
	});

	// TTFB / TTLB metrics
	var ttfbMeasured = false;

	phantomas.on('recv', function(entry, res) {
		// check the first response which is not a redirect (issue #74)
		if (!ttfbMeasured && !entry.isRedirect) {
			phantomas.setMetric('timeToFirstByte', entry.timeToFirstByte);
			phantomas.setMetric('timeToLastByte', entry.timeToLastByte);

			ttfbMeasured = true;

			phantomas.emit('responseEnd', entry, res);
		}

		// completion of the last HTTP request
		phantomas.setMetric('httpTrafficCompleted', entry.recvEndTime - loadStartedTime);
	});
};
<commit_msg>delete double assignment<commit_after>/**
 * Simple HTTP requests monitor and analyzer
 */
exports.version = '1.1';

exports.module = function(phantomas) {
	// imports
	var HTTP_STATUS_CODES = phantomas.require('http').STATUS_CODES,
		parseUrl = phantomas.require('url').parse;

	var requests = [];

	// register metric
	phantomas.setMetric('requests');
	phantomas.setMetric('gzipRequests');
	phantomas.setMetric('postRequests');
	phantomas.setMetric('httpsRequests');
	phantomas.setMetric('notFound');
	phantomas.setMetric('timeToFirstByte');
	phantomas.setMetric('timeToLastByte');
	phantomas.setMetric('bodySize'); // content only
	phantomas.setMetric('contentLength'); // content only

	// parse given URL to get protocol and domain
	function parseEntryUrl(entry) {
		var parsed;

		if (entry.url.indexOf('data:') !== 0) {
			// @see http://nodejs.org/api/url.html#url_url
			parsed = parseUrl(entry.url) || {};

			entry.protocol = parsed.protocol.replace(':', '');
			entry.domain = parsed.hostname;
			entry.query = parsed.query;

			if (entry.protocol === 'https') {
				entry.isSSL = true;
			}
		}
		else {
			// base64 encoded data
			entry.domain = false;
			entry.protocol = false;
			entry.isBase64 = true;
		}
	}

	// when the monitoring started?
	var loadStartedTime;
	phantomas.on('loadStarted', function(res) {
		loadStartedTime = Date.now();
	});

	phantomas.on('onResourceRequested', function(res, request) {
		// store current request data
		var entry = requests[res.id] = {
			id: res.id,
			url: res.url,
			method: res.method,
			requestHeaders: {},
			sendTime: res.time,
			bodySize: 0,
			isBlocked: false
		};

		// allow modules to block requests
		entry.block = function() {
			this.isBlocked = true;
		};

		res.headers.forEach(function(header) {
			entry.requestHeaders[header.name] = header.value;

			switch (header.name.toLowerCase()) {
				// AJAX requests
				case 'x-requested-with':
					if (header.value === 'XMLHttpRequest') {
						entry.isAjax = true;
					}
					break;
			}
		});

		parseEntryUrl(entry);

		if (entry.isBase64) {
			return;
		}

		// give modules a chance to block requests using entry.block()
		// @see https://github.com/ariya/phantomjs/issues/10230
		phantomas.emit('beforeSend', entry, res);

		if ( (entry.isBlocked === true) && (typeof request !== 'undefined') ) {
			phantomas.log('Blocked request: <' + entry.url + '>');
			request.abort();
			return;
		}

		// proceed
		phantomas.emit('send', entry, res);
	});

	phantomas.on('onResourceReceived', function(res) {
		// current request data
		var entry = requests[res.id] || {};

		// fix for blocked requests still "emitting" onResourceReceived with "stage" = 'end' and empty "status" (issue #122)
		if (res.status === null ) {
			if (entry.isBlocked) {
				return;
			} else {
				phantomas.emit('abort', entry, res);
			}
		}

		switch(res.stage) {
			// the beginning of response
			case 'start':
				entry.recvStartTime = res.time;
				entry.timeToFirstByte = res.time - entry.sendTime;

				// FIXME: buggy
				// @see https://github.com/ariya/phantomjs/issues/10169
				entry.bodySize += res.bodySize || 0;
				break;

			// the end of response
			case 'end':
				// timing
				entry.recvEndTime = res.time;
				entry.timeToLastByte = res.time - entry.sendTime;
				entry.receiveTime = entry.recvEndTime - entry.recvStartTime;

				// request method
				switch(entry.method) {
					case 'POST':
						phantomas.incrMetric('postRequests');
						phantomas.addOffender('postRequests', entry.url);
						break;
				}

				// asset type
				entry.type = 'other';

				// analyze HTTP headers
				entry.headers = {};
				res.headers.forEach(function(header) {
					entry.headers[header.name] = header.value;

					switch (header.name.toLowerCase()) {
						// TODO: why it's not gzipped?
						// because: https://github.com/ariya/phantomjs/issues/10156
						// should equal bodySize
						case 'content-length':
							entry.contentLength = parseInt(header.value, 10);
							break;

						// detect content type
						case 'content-type':
							// parse header value
							var value = header.value.split(';').shift().toLowerCase();

							switch(value) {
								case 'text/html':
									entry.type = 'html';
									entry.isHTML = true;
									break;

								case 'text/css':
									entry.type = 'css';
									entry.isCSS = true;
									break;

								case 'application/x-javascript':
								case 'application/javascript':
								case 'text/javascript':
									entry.type = 'js';
									entry.isJS = true;
									break;

								case 'application/json':
									entry.type = 'json';
									entry.isJSON = true;
									break;

								case 'image/png':
								case 'image/jpeg':
								case 'image/gif':
								case 'image/svg+xml':
									entry.type = 'image';
									entry.isImage = true;
									break;

								// @see http://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts
								case 'application/font-wof':
								case 'application/font-woff':
								case 'application/vnd.ms-fontobject':
								case 'application/x-font-opentype':
								case 'application/x-font-truetype':
								case 'application/x-font-ttf':
								case 'application/x-font-woff':
									entry.type = 'webfont';
									entry.isWebFont = true;
									break;

								case 'application/octet-stream':
									var ext = (entry.url || '').split('.').pop();

									switch(ext) {
										// @see http://stackoverflow.com/questions/2871655/proper-mime-type-for-fonts#comment-8077637
										case 'otf':
											entry.type = 'webfont';
											entry.isWebFont = true;
											break;
									}
									break;

								default:
									phantomas.log('Unknown content type found: ' + value + ' for <' + entry.url + '>');
							}
							break;

						// detect content encoding
						case 'content-encoding':
							if (header.value === 'gzip') {
								entry.gzip = true;
							}
							break;
					}
				});

				parseEntryUrl(entry);

				// HTTP code
				entry.status = res.status || 200 /* for base64 data */;
				entry.statusText = HTTP_STATUS_CODES[entry.status];

				switch(entry.status) {
					case 301: // Moved Permanently
					case 302: // Found
						entry.isRedirect = true;
						break;

					case 404: // Not Found
						phantomas.incrMetric('notFound');
						phantomas.addOffender('notFound', entry.url);
						break;
				}

				// requests stats
				if (!entry.isBase64) {
					phantomas.incrMetric('requests');

					phantomas.incrMetric('bodySize', entry.bodySize); // content only
					phantomas.incrMetric('contentLength', entry.contentLength || entry.bodySize); // content only
				}

				if (entry.gzip) {
					phantomas.incrMetric('gzipRequests');
				}

				if (entry.isSSL) {
					phantomas.incrMetric('httpsRequests');
					phantomas.addOffender('httpsRequests', entry.url);
				}

				// emit an event for other modules
				phantomas.emit(entry.isBase64 ? 'base64recv' : 'recv' , entry, res);
				//phantomas.log(entry);
				break;
		}
	});

	// TTFB / TTLB metrics
	var ttfbMeasured = false;

	phantomas.on('recv', function(entry, res) {
		// check the first response which is not a redirect (issue #74)
		if (!ttfbMeasured && !entry.isRedirect) {
			phantomas.setMetric('timeToFirstByte', entry.timeToFirstByte);
			phantomas.setMetric('timeToLastByte', entry.timeToLastByte);

			ttfbMeasured = true;

			phantomas.emit('responseEnd', entry, res);
		}

		// completion of the last HTTP request
		phantomas.setMetric('httpTrafficCompleted', entry.recvEndTime - loadStartedTime);
	});
};
<|endoftext|>


<commit_before>'use strict'

var util = require('util')
var stream = require('readable-stream')
var IncomingMessage = require('./lib/incoming-message')
var debug = require('./lib/debug')

var HEADER_MAX_BYTES = 1024 // TODO: Figure out the best max size of the header
var CR = 0x0d
var NL = 0x0a
var NO_BODY_STATUS_CODES = [100, 304]

var Decoder = module.exports = function (opts) {
  if (!(this instanceof Decoder)) return new Decoder(opts)

  stream.Writable.call(this, opts)

  this._inBody = false
  this._headerOffset = 0
  this._header = new Buffer(HEADER_MAX_BYTES)
  this._bodyBytesWritten = 0
}

util.inherits(Decoder, stream.Writable)

Decoder.prototype._write = function (chunk, encoding, cb) {
  this._writeOffset(chunk, 0, cb)
}

Decoder.prototype._writeOffset = function (chunk, offset, cb) {
  while (offset < chunk.length) {
    debug('processing chunk', util.inspect(chunk.slice(offset).toString()))
    if (this._inBody) {
      offset = this._writeBody(chunk, offset, cb)
      if (offset === 0) return // chunk not consumed - _writeOffset will be called again when ready
    } else {
      offset = this._writeHead(chunk, offset)
    }
  }
  cb()
}

Decoder.prototype._writeHead = function (chunk, offset) {
  if (this._headerOffset === 0) debug('start of header')

  chunk.copy(this._header, this._headerOffset, offset)
  var origHeaderOffset = this._headerOffset
  this._headerOffset += chunk.length - offset
  var bodyStart = offset + indexOfBody(this._header, Math.max(origHeaderOffset - 3, 0), this._headerOffset)
  if (!~bodyStart) return chunk.length // still reading the header

  debug('end of header')

  this._msg = new IncomingMessage(this._header)

  this._headerOffset = 0
  this._header = new Buffer(HEADER_MAX_BYTES)

  if (~NO_BODY_STATUS_CODES.indexOf(this._msg.statusCode)) {
    this._bodySize = 0
  } else {
    this._bodySize = parseInt(this._msg.headers['content-length'], 10) || 0
  }

  if (this._bodySize > 0) this._inBody = true

  // _writeBody logic to handle back-pressure
  var self = this
  this._msg.on('drain', function () {
    if (!this._chunk) return
    self._writeOffset(this._chunk, this._end, this._cb)
    this._chunk = null
    this._end = null
    this._cb = null
  })

  if (this._msg.method) this.emit('request', this._msg)
  else this.emit('response', this._msg)

  return bodyStart - origHeaderOffset
}

// If the given chunk cannot be consumed due to back-pressure, _writeBody will
// return 0 indicating that no part of the chunk have been processed. At the
// same time _writeBody will take over responsibility for the chunk. The
// callback is only called in that particular case
Decoder.prototype._writeBody = function (chunk, offset, cb) {
  if (this._bodyBytesWritten === 0) debug('start of body')

  var bytesLeft = this._bodySize - this._bodyBytesWritten
  var end = bytesLeft < chunk.length - offset ? offset + bytesLeft : chunk.length

  var drained = this._msg.write(chunk.slice(offset, end))

  this._bodyBytesWritten += chunk.length

  if (this._bodyBytesWritten >= this._bodySize) {
    debug('end of body')
    this._inBody = false
    this._bodyBytesWritten = 0
    this._msg.end()
  }

  if (!drained) {
    debug('back pressure detected in IncomingMessage')
    this._msg._chunk = chunk
    this._msg._end = end
    this._msg._cb = cb
    return 0 // indicate we didn't consume the chunk (yet)
  }

  return end
}

function indexOfBody (buf, start, end) {
  for (var n = start; n < end - 1; n++) {
    if (buf[n] === CR && buf[n + 1] === NL && buf[n + 2] === CR && buf[n + 3] === NL && n <= end - 4) return n + 4
    if (buf[n] === NL && buf[n + 1] === NL) return n + 2
    if (buf[n] === CR && buf[n + 1] === CR) return n + 2 // weird, but the RFC allows it
  }
  return -1
}
<commit_msg>Improve decoder debugging output<commit_after>'use strict'

var util = require('util')
var stream = require('readable-stream')
var IncomingMessage = require('./lib/incoming-message')
var debug = require('./lib/debug')

var HEADER_MAX_BYTES = 1024 // TODO: Figure out the best max size of the header
var CR = 0x0d
var NL = 0x0a
var NO_BODY_STATUS_CODES = [100, 304]

var Decoder = module.exports = function (opts) {
  if (!(this instanceof Decoder)) return new Decoder(opts)

  stream.Writable.call(this, opts)

  this._inBody = false
  this._headerOffset = 0
  this._header = new Buffer(HEADER_MAX_BYTES)
  this._bodyBytesWritten = 0
}

util.inherits(Decoder, stream.Writable)

Decoder.prototype._write = function (chunk, encoding, cb) {
  this._writeOffset(chunk, 0, cb)
}

Decoder.prototype._writeOffset = function (chunk, offset, cb) {
  while (offset < chunk.length) {
    debug('decoding chunk', util.inspect(chunk.slice(offset).toString()))
    if (this._inBody) {
      offset = this._writeBody(chunk, offset, cb)
      if (offset === 0) return // chunk not consumed - _writeOffset will be called again when ready
    } else {
      offset = this._writeHead(chunk, offset)
    }
  }
  cb()
}

Decoder.prototype._writeHead = function (chunk, offset) {
  if (this._headerOffset === 0) debug('start of header')

  chunk.copy(this._header, this._headerOffset, offset)
  var origHeaderOffset = this._headerOffset
  this._headerOffset += chunk.length - offset
  var bodyStart = offset + indexOfBody(this._header, Math.max(origHeaderOffset - 3, 0), this._headerOffset)
  if (!~bodyStart) return chunk.length // still reading the header

  debug('end of header')

  this._msg = new IncomingMessage(this._header)

  this._headerOffset = 0
  this._header = new Buffer(HEADER_MAX_BYTES)

  if (~NO_BODY_STATUS_CODES.indexOf(this._msg.statusCode)) {
    this._bodySize = 0
  } else {
    this._bodySize = parseInt(this._msg.headers['content-length'], 10) || 0
  }

  if (this._bodySize > 0) this._inBody = true

  // _writeBody logic to handle back-pressure
  var self = this
  this._msg.on('drain', function () {
    if (!this._chunk) return
    self._writeOffset(this._chunk, this._end, this._cb)
    this._chunk = null
    this._end = null
    this._cb = null
  })

  if (this._msg.method) this.emit('request', this._msg)
  else this.emit('response', this._msg)

  return bodyStart - origHeaderOffset
}

// If the given chunk cannot be consumed due to back-pressure, _writeBody will
// return 0 indicating that no part of the chunk have been processed. At the
// same time _writeBody will take over responsibility for the chunk. The
// callback is only called in that particular case
Decoder.prototype._writeBody = function (chunk, offset, cb) {
  if (this._bodyBytesWritten === 0) debug('start of body')

  var bytesLeft = this._bodySize - this._bodyBytesWritten
  var end = bytesLeft < chunk.length - offset ? offset + bytesLeft : chunk.length

  var drained = this._msg.write(chunk.slice(offset, end))

  this._bodyBytesWritten += chunk.length

  if (this._bodyBytesWritten >= this._bodySize) {
    debug('end of body')
    this._inBody = false
    this._bodyBytesWritten = 0
    this._msg.end()
  }

  if (!drained) {
    debug('back pressure detected in IncomingMessage')
    this._msg._chunk = chunk
    this._msg._end = end
    this._msg._cb = cb
    return 0 // indicate we didn't consume the chunk (yet)
  }

  return end
}

function indexOfBody (buf, start, end) {
  for (var n = start; n < end - 1; n++) {
    if (buf[n] === CR && buf[n + 1] === NL && buf[n + 2] === CR && buf[n + 3] === NL && n <= end - 4) return n + 4
    if (buf[n] === NL && buf[n + 1] === NL) return n + 2
    if (buf[n] === CR && buf[n + 1] === CR) return n + 2 // weird, but the RFC allows it
  }
  return -1
}
<|endoftext|>