<commit_before>
export('readOnlyPropertyDesc',
        'writeOnlyPropertyDesc',
        'readWritePropertyDesc',
        'jsonDateReviver',
        'format',
        'getScriptStack',
        'getJavaStack',
        'timer');

/**
 * Create a read-only property descriptor to be used as third argument in
 * Object.defineProperty that maps a property to a property in
 * another object.
 *
 * The third argument is optional and can be used to define additional
 * settings on the descriptor such as enumerable, writable, or configurable.
 *
 * @param obj the target object
 * @param name the target property name
 * @param desc optional: the descriptor object, or undefined
 * @return a property descriptor object that maps to a property in a target object
 */
function readOnlyPropertyDesc(obj, name, desc) {
    desc = desc || {};
    desc.get = function() { return obj[name]; }
    return desc;
}

/**
 * Create a write-only property descriptor to be used as third argument in
 * Object.defineProperty that maps a property to a property in
 * another object.
 *
 * The third argument is optional and can be used to define additional
 * settings on the descriptor such as enumerable, writable, or configurable.
 *
 * @param obj the target object
 * @param name the target property name
 * @param desc optional: the descriptor object, or undefined
 * @return a property descriptor object that maps to a property in a target object
 */
function writeOnlyPropertyDesc(obj, name, desc) {
    desc = desc || {};
    desc.set = function(value) { obj[name] = value; }
    return desc;
}

/**
 * Create a read-write property descriptor to be used as third argument in
 * Object.defineProperty that maps a property to a property in
 * another object.
 *
 * The third argument is optional and can be used to define additional
 * settings on the descriptor such as enumerable, writable, or configurable.
 *
 * @param obj the target object
 * @param name the target property name
 * @param desc optional: the descriptor object, or undefined
 * @return a property descriptor object that maps to a property in a target object
 */
function readWritePropertyDesc(obj, name, desc) {
    desc = desc || {};
    desc.get = function() { return obj[name]; }
    desc.set = function(value) { obj[name] = value; }
    return desc;
}

/**
 * JSON reviver function for Date values. Pass this as second argument to
 * JSON.parse to convert stringified dates back into Date objects. Borrowed
 * from http://www.west-wind.com/weblog/posts/729630.aspx
 *
 * @param key the JSON key
 * @param value the JSON value
 */
function jsonDateReviver(key, value) {
    if (typeof value === 'string') {
        var a = jsonDateRegexp.exec(value);
        if (a) {
            return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));
        }
    }
    return value;
}

var jsonDateRegexp = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/;

/**
 * General purpuse timer.
 */
function timer(fn) {
    var start = java.lang.System.nanoTime();
    fn();
    var stop = java.lang.System.nanoTime();
    print(Math.round((stop - start) / 1000000), 'millis');
}

/**
 * A simple string formatter. If the first argument is a format string
 * containing a number of curly bracket pairs {} as placeholders,
 * the same number of following arguments will be used to replace the curly
 * bracket pairs in the format string. If the first argument is not a string
 * or does not contain any curly brackets, the arguments are simply concatenated
 * to a string and returned.
 *
 * @param {String} format string, followed by a variable number of values
 * @return {String} the formatted string
 */
function format() {
    var format = arguments[0];
    if (typeof format === 'string' && format.indexOf('{}') > -1) {
        for (var i = 1; i < arguments.length; i++) {
            format = format.replace("{}", String(arguments[i]));
        }
    } else {
        format = arguments.join(' ');
    }
    return format || '';
}

/**
 * Get a rendered JavaScript stack trace from a caught error.
 * @param {Error} error an error object
 * @return {String} the rendered JavaScript stack trace
 */
function getScriptStack(error) {
    var exception = error && error.rhinoException ?
        error.rhinoException : error;
    return exception instanceof org.mozilla.javascript.RhinoException ?
        exception.scriptStackTrace : '';
}

/**
 * Get a rendered JavaScript stack trace from a caught error.
 * @param {Error} error an error object
 * @return {String} the rendered JavaScript stack trace
 */
function getJavaStack(error) {
    var exception = error && error.rhinoException ?
        error.rhinoException : error;
    if (exception instanceof java.lang.Throwable) {
        var writer = new java.io.StringWriter();
        var printer = new java.io.PrintWriter(writer);
        exception.printStackTrace(printer);
        return writer.toString();
    }
    return '';
}
<commit_msg>Improve format() implementation.<commit_after>
export('readOnlyPropertyDesc',
        'writeOnlyPropertyDesc',
        'readWritePropertyDesc',
        'jsonDateReviver',
        'format',
        'getScriptStack',
        'getJavaStack',
        'timer');

/**
 * Create a read-only property descriptor to be used as third argument in
 * Object.defineProperty that maps a property to a property in
 * another object.
 *
 * The third argument is optional and can be used to define additional
 * settings on the descriptor such as enumerable, writable, or configurable.
 *
 * @param obj the target object
 * @param name the target property name
 * @param desc optional: the descriptor object, or undefined
 * @return a property descriptor object that maps to a property in a target object
 */
function readOnlyPropertyDesc(obj, name, desc) {
    desc = desc || {};
    desc.get = function() { return obj[name]; }
    return desc;
}

/**
 * Create a write-only property descriptor to be used as third argument in
 * Object.defineProperty that maps a property to a property in
 * another object.
 *
 * The third argument is optional and can be used to define additional
 * settings on the descriptor such as enumerable, writable, or configurable.
 *
 * @param obj the target object
 * @param name the target property name
 * @param desc optional: the descriptor object, or undefined
 * @return a property descriptor object that maps to a property in a target object
 */
function writeOnlyPropertyDesc(obj, name, desc) {
    desc = desc || {};
    desc.set = function(value) { obj[name] = value; }
    return desc;
}

/**
 * Create a read-write property descriptor to be used as third argument in
 * Object.defineProperty that maps a property to a property in
 * another object.
 *
 * The third argument is optional and can be used to define additional
 * settings on the descriptor such as enumerable, writable, or configurable.
 *
 * @param obj the target object
 * @param name the target property name
 * @param desc optional: the descriptor object, or undefined
 * @return a property descriptor object that maps to a property in a target object
 */
function readWritePropertyDesc(obj, name, desc) {
    desc = desc || {};
    desc.get = function() { return obj[name]; }
    desc.set = function(value) { obj[name] = value; }
    return desc;
}

/**
 * JSON reviver function for Date values. Pass this as second argument to
 * JSON.parse to convert stringified dates back into Date objects. Borrowed
 * from http://www.west-wind.com/weblog/posts/729630.aspx
 *
 * @param key the JSON key
 * @param value the JSON value
 */
function jsonDateReviver(key, value) {
    if (typeof value === 'string') {
        var a = jsonDateRegexp.exec(value);
        if (a) {
            return new Date(Date.UTC(+a[1], +a[2] - 1, +a[3], +a[4], +a[5], +a[6]));
        }
    }
    return value;
}

var jsonDateRegexp = /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2}(?:\.\d*)?)Z$/;

/**
 * General purpuse timer.
 */
function timer(fn) {
    var start = java.lang.System.nanoTime();
    fn();
    var stop = java.lang.System.nanoTime();
    print(Math.round((stop - start) / 1000000), 'millis');
}

/**
 * A simple string formatter. If the first argument is a format string
 * containing a number of curly bracket pairs {} as placeholders,
 * the same number of following arguments will be used to replace the curly
 * bracket pairs in the format string. If the first argument is not a string
 * or does not contain any curly brackets, the arguments are simply concatenated
 * to a string and returned.
 *
 * @param {String} format string, followed by a variable number of values
 * @return {String} the formatted string
 */
function format() {
    var format = arguments[0];
    var index = 1;
    // Replace placehoder with argument as long as possible
    if (typeof format === "string") {
        while (format.indexOf("{}") > -1 && index < arguments.length) {
            format = format.replace("{}", String(arguments[index++]));
        }
    }
    // append remaining arguments separated by " "
    if (index < arguments.length) {
        format = format + " " + Array.slice(arguments, index).join(" ");
    }
    return format;
}

/**
 * Get a rendered JavaScript stack trace from a caught error.
 * @param {Error} error an error object
 * @return {String} the rendered JavaScript stack trace
 */
function getScriptStack(error) {
    var exception = error && error.rhinoException ?
        error.rhinoException : error;
    return exception instanceof org.mozilla.javascript.RhinoException ?
        exception.scriptStackTrace : '';
}

/**
 * Get a rendered JavaScript stack trace from a caught error.
 * @param {Error} error an error object
 * @return {String} the rendered JavaScript stack trace
 */
function getJavaStack(error) {
    var exception = error && error.rhinoException ?
        error.rhinoException : error;
    if (exception instanceof java.lang.Throwable) {
        var writer = new java.io.StringWriter();
        var printer = new java.io.PrintWriter(writer);
        exception.printStackTrace(printer);
        return writer.toString();
    }
    return '';
}
<|endoftext|>


<commit_before>/**
 * Name: Users
 * Description: Track known users
 */
var users = function(dbot) {
    var knownUsers = dbot.db.knownUsers;
    var getServerUsers = function(event) {
        if(!knownUsers.hasOwnProperty(event.server)) {
            knownUsers[event.server] = { 'users': [], 'aliases': {} };
        }
        return knownUsers[event.server];
    };

    dbot.instance.addListener('366', 'users', function(event) {
        var knownUsers = getServerUsers(event);
        for(var nick in event.channel.nicks) {
            if(!knownUsers.users.include(nick) && !knownUsers.aliases.hasOwnProperty(nick) &&
                    event.channel.nicks.hasOwnProperty(nick)) {
                knownUsers.users.push(nick);
            }
        }
    });

    return {
        'name': 'users',
        'ignorable': false,

        'commands': {
            '~alias': function(event) {
                var knownUsers = getServerUsers(event);
                var alias = event.params[1].trim();
                if(knownUsers.aliases.hasOwnProperty(alias)) {
                    event.reply(alias + ' is an alias of ' + knownUsers.aliases[alias]);
                } else {
                    event.reply(alias + ' is not known as an alias to me.');
                }
            }
        },

        'listener': function(event) {
            var knownUsers = getServerUsers(event);
            if(event.action == 'JOIN') {
                if(!knownUsers.users.include(event.user)) {
                    knownUsers.users.push(event.user);
                }
            } else if(event.action == 'NICK') {
                var newNick = event.params.substr(1);
                knownUsers.aliases[newNick] = event.user;
            }
        },
        'on': ['JOIN', 'NICK'],

        'onLoad': function() {
            // Trigger updateNickLists to stat current users in channel
            var connections = dbot.instance.connections;
            for(var conn in connections) {
                if(connections.hasOwnProperty(conn)) {
                    connections[conn].updateNickLists();
                }
            }
        }
    };
};

exports.fetch = function(dbot) {
    return users(dbot);
};
<commit_msg>Stop alias if already exists in user<commit_after>/**
 * Name: Users
 * Description: Track known users
 */
var users = function(dbot) {
    var knownUsers = dbot.db.knownUsers;
    var getServerUsers = function(event) {
        if(!knownUsers.hasOwnProperty(event.server)) {
            knownUsers[event.server] = { 'users': [], 'aliases': {} };
        }
        return knownUsers[event.server];
    };

    dbot.instance.addListener('366', 'users', function(event) {
        var knownUsers = getServerUsers(event);
        for(var nick in event.channel.nicks) {
            if(!knownUsers.users.include(nick) && !knownUsers.aliases.hasOwnProperty(nick) &&
                    event.channel.nicks.hasOwnProperty(nick)) {
                knownUsers.users.push(nick);
            }
        }
    });

    return {
        'name': 'users',
        'ignorable': false,

        'commands': {
            '~alias': function(event) {
                var knownUsers = getServerUsers(event);
                var alias = event.params[1].trim();
                if(knownUsers.aliases.hasOwnProperty(alias)) {
                    event.reply(alias + ' is an alias of ' + knownUsers.aliases[alias]);
                } else {
                    event.reply(alias + ' is not known as an alias to me.');
                }
            }
        },

        'listener': function(event) {
            var knownUsers = getServerUsers(event);
            if(event.action == 'JOIN') {
                if(!knownUsers.users.include(event.user)) {
                    knownUsers.users.push(event.user);
                }
            } else if(event.action == 'NICK') {
                var newNick = event.params.substr(1);
                if(!knownUsers.users.include(newNick)) {
                    knownUsers.aliases[newNick] = event.user;
                }
            }
        },
        'on': ['JOIN', 'NICK'],

        'onLoad': function() {
            // Trigger updateNickLists to stat current users in channel
            var connections = dbot.instance.connections;
            for(var conn in connections) {
                if(connections.hasOwnProperty(conn)) {
                    connections[conn].updateNickLists();
                }
            }
        }
    };
};

exports.fetch = function(dbot) {
    return users(dbot);
};
<|endoftext|>


<commit_before>
function LobbyCtrl($scope, toaster) {
  DarkWallet.service().ready(function() {
    $scope.pairCode = '';
    $scope.requests = [];
    $scope.peers = [];
    $scope.peerIds = [];
    $scope.subscribed = false;
    var SHA256 = Bitcoin.Crypto.SHA256;
    var identity = DarkWallet.getIdentity();

    // Session key
    if (!identity.sessionKey) {
        identity.sessionKey = new Bitcoin.Key();
        identity.sessionKey.compressed = true;
    }
    var sessionKey = identity.sessionKey;

    // Identity (communications) key
    var selfKey;
    if (identity.store.get('commsKey')) {
        selfKey = new Bitcoin.Key(identity.store.get('commsKey'));
    }
    else {
        selfKey = new Bitcoin.Key();
        selfKey.compressed = true;
        identity.store.set('commsKey', selfKey.export('bytes'));
        identity.store.save();
    }

    // Subscribe to given channel
    var channelSubscribe = function(channel, callback, update_cb) {
        client = DarkWallet.getClient();
        var channelHash = SHA256(SHA256($scope.pairCode)+$scope.pairCode);
        client.chan_subscribe("b", channelHash, callback, update_cb);
    }

    // Post to given channel
    var channelPost = function(channel, data, callback) {
        var channelHash = SHA256(SHA256(channel)+channel);
        client = DarkWallet.getClient();
        client.chan_post("b", channelHash, data, callback);
    }

    // Get a simple mnemonic name
    var getMnemoname = function(dataBytes) {
        var mnemonic = new Mnemonic(64);
        mnemonic.random = [];
        mnemonic.random[0] = Bitcoin.Util.bytesToNum(dataBytes.slice(0,4));
        mnemonic.random[1] = Bitcoin.Util.bytesToNum(dataBytes.slice(8,16));
        var mnemoName = mnemonic.toWords().slice(0,4).join(" ");
        return mnemoName;

    }

    // Initialize peer structure
    var initializePeer = function(pubKeyBytes, iconSize) {
        var pubKeyHex = Bitcoin.convert.bytesToHex(pubKeyBytes);
        var mnemoname = getMnemoname(pubKeyBytes);
        var newPeer = {pubKeyHex: pubKeyHex, name: mnemoname};
        // TODO the identicon should be loaded in an angular directive,
        // but we do it now here for testing
        setTimeout(function(){
            new Identicon('peer'+pubKeyHex, Bitcoin.Util.bytesToNum(pubKeyBytes.slice(8,16)), iconSize);
        },1000);
        return newPeer;

    }

    // Initialize and add peer to scope
    var addPeer = function(pubKeyBytes) {
        var newPeer = initializePeer(pubKeyBytes, 24);
        $scope.peerIds.push(newPeer.pubKeyHex);
        $scope.peers.push(newPeer);
    }

    // Initialize some own data
    $scope.comms = initializePeer(sessionKey.getPub(), 32);
    $scope.myself = initializePeer(selfKey.getPub(), 32);

    // Callback for data received on channel
    var onChannelData = function(pairCodeHash, message) {
        console.log("data for channel", message);
        var decrypted;
        var decoded = JSON.parse(message.data);
        // Just an encrypted message
        if (decoded.cipher) {
            decrypted = sjcl.decrypt(pairCodeHash, message.data);
            var decryptedBytes = Bitcoin.convert.hexToBytes(decrypted);
            if (decrypted != $scope.myself.pubKeyHex) {
                if ($scope.peerIds.indexOf(decrypted) == -1) {
                    addPeer(decryptedBytes);
                }
                startPairing($scope.pairCode, decryptedBytes);
            }
        // Stealth message to us (maybe)
        } else if (decoded.pub) {
            console.log("stealth", sessionKey, decoded);
            decrypted = Stealth.decrypt(sessionKey, decoded);
        }
        if (decrypted) {
            $scope.requests.push({data: decrypted});
            console.log("data for channel", decrypted);
        }
        if(!$scope.$$phase) {
            $scope.$apply();
        }

    }

    // Start pairing with another identity
    var startPairing = function(channel, pubKey) {
        // pair to a specific user session public key
        var msg = 'hello';
        var encrypted = Stealth.encrypt(pubKey, msg);
        channelPost(channel, JSON.stringify(encrypted), function(err, data){
            console.log("channel post2", err, data)
        });
    }

    // Action to start announcements and reception
    $scope.announceSelf = function() {
        client = DarkWallet.getClient();
        var pairCodeHash = SHA256(SHA256($scope.pairCode)+$scope.pairCode);
        var pubKeyHash = Bitcoin.convert.bytesToHex(sessionKey.getPub())
        var encrypted = sjcl.encrypt(pairCodeHash, pubKeyHash, {ks: 256, ts: 128});
        // chan tests
        if ($scope.subscribed != pairCodeHash && !client.handler_map["chan.update." + pairCodeHash]) {
            console.log("announcing", pairCodeHash, pubKeyHash, encrypted);
            channelSubscribe($scope.pairCode, function(err, data){
                if (!err) {
                    $scope.subscribed = pairCodeHash;
                }
                console.log("channel subscribed", err, data)
            }, function(_data) {onChannelData(pairCodeHash, _data)});
        }
        channelPost($scope.pairCode, encrypted, function(err, data){
            console.log("channel post", err, data)
        });
        /*
        client.chan_get("b", "announcements", function(err, data){console.log("channel get", err, data)})
        client.chan_list("b", function(err, data){console.log("channel list", err, data)})*/

    }
  });
}
<commit_msg>check wrong key<commit_after>
function LobbyCtrl($scope, toaster) {
  DarkWallet.service().ready(function() {
    $scope.pairCode = '';
    $scope.requests = [];
    $scope.peers = [];
    $scope.peerIds = [];
    $scope.subscribed = false;
    var SHA256 = Bitcoin.Crypto.SHA256;
    var identity = DarkWallet.getIdentity();

    // Session key
    if (!identity.sessionKey) {
        identity.sessionKey = new Bitcoin.Key();
        identity.sessionKey.compressed = true;
    }
    var sessionKey = identity.sessionKey;

    // Identity (communications) key
    var selfKey;
    if (identity.store.get('commsKey')) {
        selfKey = new Bitcoin.Key(identity.store.get('commsKey'));
    }
    else {
        selfKey = new Bitcoin.Key();
        selfKey.compressed = true;
        identity.store.set('commsKey', selfKey.export('bytes'));
        identity.store.save();
    }

    // Subscribe to given channel
    var channelSubscribe = function(channel, callback, update_cb) {
        client = DarkWallet.getClient();
        var channelHash = SHA256(SHA256($scope.pairCode)+$scope.pairCode);
        client.chan_subscribe("b", channelHash, callback, update_cb);
    }

    // Post to given channel
    var channelPost = function(channel, data, callback) {
        var channelHash = SHA256(SHA256(channel)+channel);
        client = DarkWallet.getClient();
        client.chan_post("b", channelHash, data, callback);
    }

    // Get a simple mnemonic name
    var getMnemoname = function(dataBytes) {
        var mnemonic = new Mnemonic(64);
        mnemonic.random = [];
        mnemonic.random[0] = Bitcoin.Util.bytesToNum(dataBytes.slice(0,4));
        mnemonic.random[1] = Bitcoin.Util.bytesToNum(dataBytes.slice(8,16));
        var mnemoName = mnemonic.toWords().slice(0,4).join(" ");
        return mnemoName;

    }

    // Initialize peer structure
    var initializePeer = function(pubKeyBytes, iconSize) {
        var pubKeyHex = Bitcoin.convert.bytesToHex(pubKeyBytes);
        var mnemoname = getMnemoname(pubKeyBytes);
        var newPeer = {pubKeyHex: pubKeyHex, name: mnemoname};
        // TODO the identicon should be loaded in an angular directive,
        // but we do it now here for testing
        setTimeout(function(){
            new Identicon('peer'+pubKeyHex, Bitcoin.Util.bytesToNum(pubKeyBytes.slice(8,16)), iconSize);
        },1000);
        return newPeer;

    }

    // Initialize and add peer to scope
    var addPeer = function(pubKeyBytes) {
        var newPeer = initializePeer(pubKeyBytes, 24);
        $scope.peerIds.push(newPeer.pubKeyHex);
        $scope.peers.push(newPeer);
    }

    // Initialize some own data
    $scope.comms = initializePeer(sessionKey.getPub(), 32);
    $scope.myself = initializePeer(selfKey.getPub(), 32);

    // Callback for data received on channel
    var onChannelData = function(pairCodeHash, message) {
        console.log("data for channel", message);
        var decrypted;
        var decoded = JSON.parse(message.data);
        // Just an encrypted message
        if (decoded.cipher) {
            decrypted = sjcl.decrypt(pairCodeHash, message.data);
            var decryptedBytes = Bitcoin.convert.hexToBytes(decrypted);
            if (decrypted != $scope.comms.pubKeyHex) {
                if ($scope.peerIds.indexOf(decrypted) == -1) {
                    addPeer(decryptedBytes);
                }
                startPairing($scope.pairCode, decryptedBytes);
            }
        // Stealth message to us (maybe)
        } else if (decoded.pub) {
            console.log("stealth", sessionKey, decoded);
            decrypted = Stealth.decrypt(sessionKey, decoded);
        }
        if (decrypted) {
            $scope.requests.push({data: decrypted});
            console.log("data for channel", decrypted);
        }
        if(!$scope.$$phase) {
            $scope.$apply();
        }

    }

    // Start pairing with another identity
    var startPairing = function(channel, pubKey) {
        // pair to a specific user session public key
        var msg = 'hello';
        var encrypted = Stealth.encrypt(pubKey, msg);
        channelPost(channel, JSON.stringify(encrypted), function(err, data){
            console.log("channel post2", err, data)
        });
    }

    // Action to start announcements and reception
    $scope.announceSelf = function() {
        client = DarkWallet.getClient();
        var pairCodeHash = SHA256(SHA256($scope.pairCode)+$scope.pairCode);
        var pubKeyHash = Bitcoin.convert.bytesToHex(sessionKey.getPub())
        var encrypted = sjcl.encrypt(pairCodeHash, pubKeyHash, {ks: 256, ts: 128});
        // chan tests
        if ($scope.subscribed != pairCodeHash && !client.handler_map["chan.update." + pairCodeHash]) {
            console.log("announcing", pairCodeHash, pubKeyHash, encrypted);
            channelSubscribe($scope.pairCode, function(err, data){
                if (!err) {
                    $scope.subscribed = pairCodeHash;
                }
                console.log("channel subscribed", err, data)
            }, function(_data) {onChannelData(pairCodeHash, _data)});
        }
        channelPost($scope.pairCode, encrypted, function(err, data){
            console.log("channel post", err, data)
        });
        /*
        client.chan_get("b", "announcements", function(err, data){console.log("channel get", err, data)})
        client.chan_list("b", function(err, data){console.log("channel list", err, data)})*/

    }
  });
}
<|endoftext|>


<commit_before>var Vis = require('vis');
var fs = require('fs');

var network;
var inc = 0;

$(document).ready(function () {
    $('#submit-btn').click(function () {
        if (network.body.data.nodes.get().length == 0) {
            createAlert("You have not added any nodes to the graph.");
            return;
        }
        if (network.body.data.edges.get().length == 0) {
            createAlert("You have not added any edges to the graph.");
            return;
        }
        var rootNode = findRootNode(network.body.data.nodes.get());
        if (!rootNode) {
            var nodes = network.body.data.nodes.get();
            rootNode = nodes[0];
            rootNode.root = true;
            network = rebuildNetwork(network, container, options, nodes);
            $("#algo-panel").prepend(alertUserThatNoRoot());
        }
        setupTable(network.body.data.nodes.get());
        setupDistances(network.body.data.nodes.get());
        dijkstraAnimation(network.body.data.nodes.get());
    });
    $('#random-btn').click(function () {
        var numberOfNodes = Math.floor((Math.random() * 30) + 10);
        if (network !== null) {
            network.destroy();
            network = null;
        }
        var data = getFreeScaleNetworkWithWeights(numberOfNodes);
        network = new Vis.Network(container, data, options);
    });
});

// create a network
var container = $('#tree-simple')[0];

var options = {
    autoResize: true,
    manipulation: {
        initiallyActive: true,
        addNode: function (nodeData, callback) {
            if (network.body.nodes === {}) {
                inc = 1;
            }
            else {
                inc++;
            }
            nodeData.root = false;
            nodeData.label = inc;
            nodeData.color = '#009688';
            nodeData.font = {
                color: '#fff'
            };
            nodeData.visited = false;
            nodeData.adjacencyList = [];
            nodeData.predecessor = null;
            callback(nodeData);
        },
        editNode: function (nodeData, callback) {
            editNodeCustom(network, nodeData, callback);
        },
        addEdge: function (data, callback) {
            if ($('#directed-chechbox').prop('checked')){
                data.arrows = {};
                data.arrows.to = true;
            }
            if (data.from === data.to) {
                var r = confirm('Do you want to connect the node to itself?');
                if (r === true) {
                    callback(null);
                    return;
                }
            }
            editEdgeCustom(network, data, callback);
        },
        editEdge: {
            editWithoutDrag: function (data, callback) {
                editEdgeCustom(network, data, callback);
            }
        }
    },
    interaction: {
        navigationButtons: true
    },
    physics: {
        enabled: false
    }
};

network = new Vis.Network(container, [], options);

function dijkstraAnimation(nodes) {
    var nodeRoot = findRootNode(nodes);
    var S = [];
    var distances = [];
    var nodesArrayLength = nodes.length;
    for (var i = 0; i < nodes.length; i++) {
        if (nodes[i] == nodeRoot) {
            distances[nodes[i]] = 0;
        }
        else if (containsObject(nodes[i], nodeRoot.adjacencyList)) {
            distances[nodes[i]] = getEdgeWeight(nodeRoot, nodes[i]);
        }
        else {
            distances[nodes[i]] = Number.POSITIVE_INFINITY;
        }
    }
    while (S.length != nodes.length) {
        var minNode = findMinimumDistanceNode(nodes, S, distances);
        S.push(minNode);
        console.log("S contains:");
        console.log(S);
        for (var j = 0; j < nodesArrayLength; j++) {
            if (!containsObject(nodes[j], S)) {
                distances[nodes[j]] = Math.min(distances[nodes[j]], (distances[minNode] + getEdgeWeight(minNode, nodes[j])));
                console.log("made it until here");
            }
        }
    }
    for (var z = 0; z < nodesArrayLength; z++) {
        console.log(distances[nodes[z]]);
    }
}

function findMinimumDistanceNode (nodes, S, distances) {
    var min = Number.MAX_VALUE;
    var minNode = null;
    var len = nodes.length;
    for (var i = 0; i < len; i++) {
        if (!containsObject(nodes[i], S)) {
            if (distances[nodes[i]] < min) {
                min = distances[nodes[i]];
                minNode = nodes[i];
            }
        }
    }
    return minNode;
}

function setupTable(nodes) {
   for (var i = 0; i < nodes.length; i++) {
       var tr;
       tr = '
d(' + nodes[i].label + ')
';
       $('#distances-table').append(tr);
   }
}

function setupDistances(nodes) {
    var rootNode = findRootNode(nodes);
    for (var i = 0; i < nodes.length; i++) {
        if (nodes[i].root) {
            $("#distance-" + nodes[i].label).append(" 0 	");
        }
        else {
            if (rootNode.adjacencyList.lastIndexOf(nodes[i]) >= 0) {
                var edgeWeight = getEdgeWeight(rootNode, nodes[i]);
                $("#distance-" + nodes[i].label).append(" " + edgeWeight + " 	");
            }
            else {
                $("#distance-" + nodes[i].label).append(" ∞ 	");
            }
        }
    }
}

function containsObject(obj, list) {
    var i;
    for (i = 0; i < list.length; i++) {
        if (list[i].id == obj.id) {
            return true;
        }
    }
    return false;
}

function getEdgeWeight(nodeOne, nodeTwo) {
    var edgeBetweenNodes = network.body.data.edges.get().filter(function(x) {
        return (x.from === nodeOne.id && x.to === nodeTwo.id);
    });
    return parseInt(edgeBetweenNodes[0].label);
}
<commit_msg>changed how distances object is created<commit_after>var Vis = require('vis');
var fs = require('fs');

var network;
var inc = 0;

$(document).ready(function () {
    $('#submit-btn').click(function () {
        if (network.body.data.nodes.get().length == 0) {
            createAlert("You have not added any nodes to the graph.");
            return;
        }
        if (network.body.data.edges.get().length == 0) {
            createAlert("You have not added any edges to the graph.");
            return;
        }
        var rootNode = findRootNode(network.body.data.nodes.get());
        if (!rootNode) {
            var nodes = network.body.data.nodes.get();
            rootNode = nodes[0];
            rootNode.root = true;
            network = rebuildNetwork(network, container, options, nodes);
            $("#algo-panel").prepend(alertUserThatNoRoot());
        }
        setupTable(network.body.data.nodes.get());
        setupDistances(network.body.data.nodes.get());
        dijkstraAnimation(network.body.data.nodes.get());
    });
    $('#random-btn').click(function () {
        var numberOfNodes = Math.floor((Math.random() * 30) + 10);
        if (network !== null) {
            network.destroy();
            network = null;
        }
        var data = getFreeScaleNetworkWithWeights(numberOfNodes);
        network = new Vis.Network(container, data, options);
    });
});

// create a network
var container = $('#tree-simple')[0];

var options = {
    autoResize: true,
    manipulation: {
        initiallyActive: true,
        addNode: function (nodeData, callback) {
            if (network.body.nodes === {}) {
                inc = 1;
            }
            else {
                inc++;
            }
            nodeData.root = false;
            nodeData.label = inc;
            nodeData.color = '#009688';
            nodeData.font = {
                color: '#fff'
            };
            nodeData.visited = false;
            nodeData.adjacencyList = [];
            nodeData.predecessor = null;
            callback(nodeData);
        },
        editNode: function (nodeData, callback) {
            editNodeCustom(network, nodeData, callback);
        },
        addEdge: function (data, callback) {
            if ($('#directed-chechbox').prop('checked')){
                data.arrows = {};
                data.arrows.to = true;
            }
            if (data.from === data.to) {
                var r = confirm('Do you want to connect the node to itself?');
                if (r === true) {
                    callback(null);
                    return;
                }
            }
            editEdgeCustom(network, data, callback);
        },
        editEdge: {
            editWithoutDrag: function (data, callback) {
                editEdgeCustom(network, data, callback);
            }
        }
    },
    interaction: {
        navigationButtons: true
    },
    physics: {
        enabled: false
    }
};

network = new Vis.Network(container, [], options);

function dijkstraAnimation(nodes) {
    var nodeRoot = findRootNode(nodes);
    var S = [nodeRoot];
    var distances = [];
    var nodesArrayLength = nodes.length;
    for (var i = 0; i < nodesArrayLength; i++) {
        if (nodes[i] == nodeRoot) {
            distances[nodes[i].label] = 0;
        }
        else if (containsObject(nodes[i], nodeRoot.adjacencyList)) {
            distances[nodes[i].label] = getEdgeWeight(nodeRoot, nodes[i]);
        }
        else {
            distances[nodes[i].label] = Number.POSITIVE_INFINITY;
        }
    }
    while (S.length != nodes.length) {
        var minNode = findMinimumDistanceNode(nodes, S, distances);
        S.push(minNode);
        // console.log("S contains:");
        // console.log(S);
        for (var j = 0; j < nodesArrayLength; j++) {
            if (!containsObject(nodes[j], S)) {
                distances[nodes[j]] = Math.min(distances[nodes[j]], (distances[minNode] + getEdgeWeight(minNode, nodes[j])));
            }
        }
    }

}

function findMinimumDistanceNode (nodes, S, distances) {
    var min = 555;
    var minNode = null;
    var len = nodes.length;
    for (var i = 0; i < len; i++) {
        if (!containsObject(nodes[i], S)) {
            if (distances[nodes[i]] < min) {
                min = distances[nodes[i]];
                minNode = nodes[i];
            }
        }
    }
    return minNode;
}

function setupTable(nodes) {
   for (var i = 0; i < nodes.length; i++) {
       var tr;
       tr = '
d(' + nodes[i].label + ')
';
       $('#distances-table').append(tr);
   }
}

function setupDistances(nodes) {
    var rootNode = findRootNode(nodes);
    for (var i = 0; i < nodes.length; i++) {
        if (nodes[i].root) {
            $("#distance-" + nodes[i].label).append(" 0 	");
        }
        else {
            if (rootNode.adjacencyList.lastIndexOf(nodes[i]) >= 0) {
                var edgeWeight = getEdgeWeight(rootNode, nodes[i]);
                $("#distance-" + nodes[i].label).append(" " + edgeWeight + " 	");
            }
            else {
                $("#distance-" + nodes[i].label).append(" ∞ 	");
            }
        }
    }
}

function containsObject(obj, list) {
    var i;
    for (i = 0; i < list.length; i++) {
        if (list[i].id == obj.id) {
            return true;
        }
    }
    return false;
}

function getEdgeWeight(nodeOne, nodeTwo) {
    var edgeBetweenNodes = network.body.data.edges.get().filter(function(x) {
        return (x.from === nodeOne.id && x.to === nodeTwo.id);
    });
    return parseInt(edgeBetweenNodes[0].label);
}
<|endoftext|>


<commit_before>//this is a class
//game.PlayerEntity is a class that is why there both capital
//me.Entity is a class
game.PlayerEntity = me.Entity.extend({
	//this is a constructer function
	//melon js uses this contructer on most things for setutp
	//you need x y and settings
	//you're initializing the basic guy
	//super is reaching into the constructer of me.Entity you need settings
	//getShape function is returning a rectangle shape
	//the numbers of the width and height of the box
	//polygon is a method
	init: function(x, y, settings){
		this._super(me.Entity, 'init', [x, y, {
			image: "player",
			width: 64,
			height: 64,
			spritewidth: "64",
			spriteheight: "64",
			getShape: function(){
				return(new me.Rect(0, 0, 64, 64)).toPolygon();
			}
		}]);

		//choosing a velocity for the player
		//moving 5 units to the right
		//y is 20 so character is on the floor
		//me.game.viewport.follow(this.pos, me.game.viewport.AXIS.BOTH); makes it so the character is always being followed on the screen
		this.type = "PlayerEntity";
		this.health = game.data.playerHealth;
		this.body.setVelocity(game.data.playerMoveSpeed, 20);
		this.facing = "right";
		this.now = new Date().getTime();
		this.lastHit = this.now;
		this.attack = game.data.playerAttack;
		this.dead = false;
		this.lastAttack = new Date().getTime(); //haven't used this
		me.game.viewport.follow(this.pos, me.game.viewport.AXIS.BOTH);

		//this.renderable.addAnimation adds animation(makes your character look like its walking standing or attacking) using the pictures
		//80 miliseconds is the speed you go through each picture
		//the number is what picture from orcSpear.png the program uses
		this.renderable.addAnimation("idle", [78]);
		this.renderable.addAnimation("walk", [117, 118, 119, 120, 121, 122, 123, 124, 125], 80);
		this.renderable.addAnimation("attack", [65, 66, 67, 68, 69, 70, 71, 72], 80);

		//this is the animation it starts at (facing the screen)
		this.renderable.setCurrentAnimation("idle");
	},

	update: function(delta){
		this.now = new Date().getTime();
		if (this.health <= 0) {
			this.dead = true;
		};

		//checking if the right key is pressed
		if(me.input.isKeyPressed("right")){
			//if the key is pressed this is what happens
			//adds the position of my x by the velocity defined above in
			//setVelocity() and multiplying it by me.timer.tick
			//me.timer.tick makes the movement look smooth
			this.body.vel.x += this.body.accel.x * me.timer.tick;
			//this is so the character faces the right when moving to the right (if this isnt here the character faces the left when walking to the right)
			this.facing = "right";
			this.flipX(true);
		}
		//if you stop pressing the right key
		else if (me.input.isKeyPressed("left")){
			this.facing = "left";
			this.body.vel.x -=this.body.accel.x * me.timer.tick;
			this.flipX(false);
		}
		else{
			//it wont move
			this.body.vel.x = 0;
		}

		if(me.input.isKeyPressed("jump") && !this.body.falling && !this.body.jumping){
			this.body.jumping = true;
			this.body.vel.y -= this.body.accel.y * me.timer.tick;
		}

		//checking if attack is pressed
		if(me.input.isKeyPressed("attack")){
			//runs if your not attacking
			if(!this.renderable.isCurrentAnimation("attack")){
				//sets the current animation to attack and once that is over
				//goes back to the idle animation
				console.log(!this.renderable.isCurrentAnimation("attack"));
				this.renderable.setCurrentAnimation("attack", "idle");
				//makes it so that the next time we start the animation this sequence we begin
				//from the first animation, not wherever we left off when we
				//switched to another animation
				this.renderable.setAnimationFrame();
			}
		}
		//this will run only if the character is moving
		else if(this.body.vel.x !== 0 && !this.renderable.isCurrentAnimation("attack")){
			//this if statement checks what happening with the character
			////if its not moving it'll walk
			//if it isnt it'll walk
			if(!this.renderable.isCurrentAnimation("walk")){
				this.renderable.setCurrentAnimation("walk");
			}
		}
		//this will run if the velocity is not 0
		//this will make the walking stop
		else if(!this.renderable.isCurrentAnimation("attack")){
			this.renderable.setCurrentAnimation("idle");
		}

		me.collision.check(this, true, this.collideHandler.bind(this), true);
		//delta is the change of time its happened
		this.body.update(delta);

		//calling the parent class
		//this is updating the super class so the animations can update
		this._super(me.Entity, "update", [delta]);
		return true;
	},

	loseHealth: function(damage){
		this.health = this.health - damage;
		console.log(this.health);
	},

	collideHandler: function(response){
		if(response.b.type==='EnemyBaseEntity'){
			var ydif = this.pos.y - response.b.pos.y;
			var xdif = this.pos.x - response.b.pos.x;

			if(ydif<-40 && xdif< 70 && xdif>-35){
				this.body.falling = false;
				this.body.vel.y = -1;
			}
			else if(xdif>-35 && this.facing==='right' && (xdif<0)){
				this.body.vel.x = 0;
				this.pos.x = this.pos.x -1;
			}
			else if(xdif<70 && this.facing==='left' && xdif>0){
				this.body.vel.x = 0;
				this.pos.x = this.pos.x +1;
			}

			if(this.renderable.isCurrentAnimation("attack") && this.now-this.lastHit >=1000){
				this.lastHit = this.now;
				response.b.loseHealth();
			}

		}else if (response.b.type=== 'EnemyCreep') {
			var xdif = this.pos.x - response.b.pos.x;
			var ydif = this.pos.y - response.b.pos.y;
			if (xdif>0){
				this.pos.x = this.pos.x + 1;
				if(this.facing === "left"){
					this.body.vel.x = 0;
				}
			}else{
				this.pos.x = this.pos - 1;
				if(this.facing === "right"){
					this.body.vel.x = 0;
				}
			}

			if(this.renderable.isCurrentAnimation("attack") && this.now-this.lastHit >= game.data.playerAttackTimer
					&& (Math.abs(ydif) <=40) && (((xdif>0 ) && this.facing==="left")
					 || ((xdif<0) && this.facing==="right"))){
				this.lastHit = this.now;
			//if the creeps health is less than our attack, execute code if in statement
			if(response.b.health <= game.data.playerAttack){
				//adds one gold for a creep kill
				game.data.gold += 1;
				console.log("Current gold: " + game.data.gold);
			}

				response.b.loseHealth(game.data.playerAttack);
			}
		}
	}
});

<commit_msg>video 31 re factoring code<commit_after>//this is a class
//game.PlayerEntity is a class that is why there both capital
//me.Entity is a class
game.PlayerEntity = me.Entity.extend({
	//this is a constructer function
	//melon js uses this contructer on most things for setutp
	//you need x y and settings
	//you're initializing the basic guy
	//super is reaching into the constructer of me.Entity you need settings
	//getShape function is returning a rectangle shape
	//the numbers of the width and height of the box
	//polygon is a method
	init: function(x, y, settings){
		this.setSuper();
		this.setPlayerTimers();
		this.setAttributes();

		//choosing a velocity for the player
		//moving 5 units to the right
		//y is 20 so character is on the floor
		//me.game.viewport.follow(this.pos, me.game.viewport.AXIS.BOTH); makes it so the character is always being followed on the screen
		this.type = "PlayerEntity";
		this.setFlags();

		me.game.viewport.follow(this.pos, me.game.viewport.AXIS.BOTH);

		this.addAnimation();

		//this.renderable.addAnimation adds animation(makes your character look like its walking standing or attacking) using the pictures
		//80 miliseconds is the speed you go through each picture
		//the number is what picture from orcSpear.png the program uses

		//this is the animation it starts at (facing the screen)
		this.renderable.setCurrentAnimation("idle");
	},
	//
	setSuper: function(){
			this._super(me.Entity, 'init', [x, y, {
			image: "player",
			width: 64,
			height: 64,
			spritewidth: "64",
			spriteheight: "64",
			getShape: function(){
				return(new me.Rect(0, 0, 64, 64)).toPolygon();
			}
		}]);
	},

	setPlayerTimers: function(){
		this.now = new Date().getTime();
		this.lastHit = this.now;
		this.lastAttack = new Date().getTime(); //haven't used this
	},

    setAttributes: function(){
    	this.health = game.data.playerHealth;
		this.body.setVelocity(game.data.playerMoveSpeed, 20);
		this.attack = game.data.playerAttack;
    },

    setFlags: function(){
		this.facing = "right";
		this.dead = false;
    },

    setaddAnimation: function(){
    	this.renderable.addAnimation("idle", [78]);
		this.renderable.addAnimation("walk", [117, 118, 119, 120, 121, 122, 123, 124, 125], 80);
		this.renderable.addAnimation("attack", [65, 66, 67, 68, 69, 70, 71, 72], 80);
    },

	update: function(delta){
		this.now = new Date().getTime();
		if (this.health <= 0) {
			this.dead = true;
		};

		//checking if the right key is pressed
		if(me.input.isKeyPressed("right")){
			//if the key is pressed this is what happens
			//adds the position of my x by the velocity defined above in
			//setVelocity() and multiplying it by me.timer.tick
			//me.timer.tick makes the movement look smooth
			this.body.vel.x += this.body.accel.x * me.timer.tick;
			//this is so the character faces the right when moving to the right (if this isnt here the character faces the left when walking to the right)
			this.facing = "right";
			this.flipX(true);
		}
		//if you stop pressing the right key
		else if (me.input.isKeyPressed("left")){
			this.facing = "left";
			this.body.vel.x -=this.body.accel.x * me.timer.tick;
			this.flipX(false);
		}
		else{
			//it wont move
			this.body.vel.x = 0;
		}

		if(me.input.isKeyPressed("jump") && !this.body.falling && !this.body.jumping){
			this.body.jumping = true;
			this.body.vel.y -= this.body.accel.y * me.timer.tick;
		}

		//checking if attack is pressed
		if(me.input.isKeyPressed("attack")){
			//runs if your not attacking
			if(!this.renderable.isCurrentAnimation("attack")){
				//sets the current animation to attack and once that is over
				//goes back to the idle animation
				console.log(!this.renderable.isCurrentAnimation("attack"));
				this.renderable.setCurrentAnimation("attack", "idle");
				//makes it so that the next time we start the animation this sequence we begin
				//from the first animation, not wherever we left off when we
				//switched to another animation
				this.renderable.setAnimationFrame();
			}
		}
		//this will run only if the character is moving
		else if(this.body.vel.x !== 0 && !this.renderable.isCurrentAnimation("attack")){
			//this if statement checks what happening with the character
			////if its not moving it'll walk
			//if it isnt it'll walk
			if(!this.renderable.isCurrentAnimation("walk")){
				this.renderable.setCurrentAnimation("walk");
			}
		}
		//this will run if the velocity is not 0
		//this will make the walking stop
		else if(!this.renderable.isCurrentAnimation("attack")){
			this.renderable.setCurrentAnimation("idle");
		}

		me.collision.check(this, true, this.collideHandler.bind(this), true);
		//delta is the change of time its happened
		this.body.update(delta);

		//calling the parent class
		//this is updating the super class so the animations can update
		this._super(me.Entity, "update", [delta]);
		return true;
	},

	loseHealth: function(damage){
		this.health = this.health - damage;
		console.log(this.health);
	},

	collideHandler: function(response){
		if(response.b.type==='EnemyBaseEntity'){
			var ydif = this.pos.y - response.b.pos.y;
			var xdif = this.pos.x - response.b.pos.x;

			if(ydif<-40 && xdif< 70 && xdif>-35){
				this.body.falling = false;
				this.body.vel.y = -1;
			}
			else if(xdif>-35 && this.facing==='right' && (xdif<0)){
				this.body.vel.x = 0;
				this.pos.x = this.pos.x -1;
			}
			else if(xdif<70 && this.facing==='left' && xdif>0){
				this.body.vel.x = 0;
				this.pos.x = this.pos.x +1;
			}

			if(this.renderable.isCurrentAnimation("attack") && this.now-this.lastHit >=1000){
				this.lastHit = this.now;
				response.b.loseHealth();
			}

		}else if (response.b.type=== 'EnemyCreep') {
			var xdif = this.pos.x - response.b.pos.x;
			var ydif = this.pos.y - response.b.pos.y;
			if (xdif>0){
				this.pos.x = this.pos.x + 1;
				if(this.facing === "left"){
					this.body.vel.x = 0;
				}
			}else{
				this.pos.x = this.pos - 1;
				if(this.facing === "right"){
					this.body.vel.x = 0;
				}
			}

			if(this.renderable.isCurrentAnimation("attack") && this.now-this.lastHit >= game.data.playerAttackTimer
					&& (Math.abs(ydif) <=40) && (((xdif>0 ) && this.facing==="left")
					 || ((xdif<0) && this.facing==="right"))){
				this.lastHit = this.now;
			//if the creeps health is less than our attack, execute code if in statement
			if(response.b.health <= game.data.playerAttack){
				//adds one gold for a creep kill
				game.data.gold += 1;
				console.log("Current gold: " + game.data.gold);
			}

				response.b.loseHealth(game.data.playerAttack);
			}
		}
	}
});

<|endoftext|>


<commit_before>
(function(ionic) {

  ionic.registerComponent({

    isComponent: function(el) {
      // this is a Toggle component if it has a "toggle" classname
      return el.classList.contains("toggle");
    },

    init: function(el) {
      if(el) {

        // check if we've already created a Toggle instance for this element
        if(!el.component) {

          // find all the required elements that make up a toggle
          var opts = {
            el: el,
            checkbox: el.querySelector("input[type='checkbox']"),
            track: el.querySelector(".track"),
            handle: el.querySelector(".handle")
          };

          // validate its a well formed toggle with the required pieces
          if(!opts.checkbox || !opts.track || !opts.handle) return;

          // initialize an instance of a Toggle
          el.component = new ionic.views.Toggle(opts);
        }

        return el.component;
      }
    }

  });

})(ionic);<commit_msg>Quick logical shortcut<commit_after>
(function(ionic) {

  ionic.registerComponent({

    isComponent: function(el) {
      // this is a Toggle component if it has a "toggle" classname
      return el.classList.contains("toggle");
    },

    init: function(el) {
      if(!el) { return; }

      // check if we've already created a Toggle instance for this element
      if(!el.component) {

        // find all the required elements that make up a toggle
        var opts = {
          el: el,
          checkbox: el.querySelector("input[type='checkbox']"),
          track: el.querySelector(".track"),
          handle: el.querySelector(".handle")
        };

        // validate its a well formed toggle with the required pieces
        if(!opts.checkbox || !opts.track || !opts.handle) return;

        // initialize an instance of a Toggle
        el.component = new ionic.views.Toggle(opts);
      }

      return el.component;
    }

  });

})(ionic);
<|endoftext|>


<commit_before>// Copyright 2015, University of Colorado Boulder

/**
 * Full PhET-iO build including phet version, phet-io version and accompanying material.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

// modules
var assert = require( 'assert' );
var copyDirectory = require( '../../../chipper/js/grunt/copyDirectory' );
var child_process = require( 'child_process' );
var ChipperStringUtils = require( '../../../chipper/js/common/ChipperStringUtils' );

/**
 * @param grunt the grunt instance
 * @param {Object} buildConfig
 */
module.exports = function( grunt, buildConfig ) {
  'use strict';

  // TODO: chipper#101 eek, this is scary! we are importing from the repository dir. ideally we should just have uglify-js installed once in chipper?
  var uglify = require( '../../../' + buildConfig.name + '/node_modules/uglify-js' );

  // Read a JS file and uglify
  var readUgly = function( filename ) {
    var text = grunt.file.read( filename );
    return uglify.minify( text, {
      mangle: true,
      fromString: true,
      output: {
        inline_script: true, // escape </script
        beautify: false
      },
      compress: {
        global_defs: {}
      }
    } ).code;
  };

  var skipBuild = grunt.option( 'skipBuild' );
  var packageJSON = grunt.file.readJSON( 'package.json' );

  var cwd = process.cwd();

  var phetioBuildDir = cwd + '/build-phet-io';
  var buildDir = cwd + '/build';

  // Clean
  if ( grunt.file.exists( phetioBuildDir ) ) {
    grunt.file.delete( phetioBuildDir );
  }
  grunt.file.mkdir( phetioBuildDir );

  var createOtherPhETIOFiles = function() {

    // create devguide/
    grunt.file.mkdir( 'build/phet-io/devguide' );
    copyDirectory( grunt, '../sherpa/lib/highlight.js-9.1.0', 'build/phet-io/devguide/lib/highlight.js-9.1.0' );
    copyDirectory( grunt, '../phet-io/doc/devguide', 'build/phet-io/devguide', function( abspath, contents ) {
      if ( abspath.indexOf( '.html' ) >= 0 ) {
        return ChipperStringUtils.replaceAll( contents, '../../../sherpa/lib/highlight.js-9.1.0', 'lib/highlight.js-9.1.0' );
      }
    } );

    // output the SimIFrameClient.js and SimWrapperUtils.js to the top level lib (not protected), see https://github.com/phetsims/phet-io/issues/353
    grunt.file.mkdir( 'build/phet-io/lib' );

    // create protected/
    grunt.file.mkdir( 'build/phet-io/protected' );
    grunt.file.mkdir( 'build/phet-io/protected/api' );
    grunt.file.mkdir( 'build/phet-io/protected/wrappers' );

    // Load the template for instance-proxies.html
    var entry = packageJSON.phet[ 'phet-io' ];
    var preload = [];
    if ( entry && entry.preload ) {
      preload = entry.preload;
    }

    var simAPIDeclaration = '';
    for ( var i = 0; i < preload.length; i++ ) {
      simAPIDeclaration = simAPIDeclaration + readUgly( preload[ i ] );
    }
    var templateText = grunt.file.read( '../phet-io/html/templates/load-state.html' );
    var lines = templateText.split( '\n' );
    var templateString = '[';
    lines.forEach( function( line ) {
      templateString = templateString + '\'' + line + '\',\n';
    } );
    // "<script>" does not parse in an HTML file (even if in JS), so we split it apart into separate substrings.
    templateString = ChipperStringUtils.replaceAll( templateString, '<script>', '<scr\'+\'ipt>' );
    templateString = ChipperStringUtils.replaceAll( templateString, '</script>', '</scr\'+\'ipt>' );
    templateString = templateString + '\'\'].join(\'\\n\')';

    var filterWrapper = function( abspath, contents ) {
      var originalContents = contents + '';
      if ( abspath.indexOf( '.js' ) >= 0 || abspath.indexOf( '.html' ) >= 0 ) {
        contents = ChipperStringUtils.replaceAll( contents, '$SIMULATION_NAME$', buildConfig.name );
        contents = ChipperStringUtils.replaceAll( contents, '$SIMULATION_VERSION$', buildConfig.version );
      }
      if ( abspath.indexOf( '.html' ) >= 0 ) {

        // RULES FOR INSTANCE_PROXIES
        contents = ChipperStringUtils.replaceAll( contents,
          '"../../../sherpa/lib/lodash-2.4.1.min.js"',
          '"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.min.js"'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          '"../../../sherpa/lib/font-awesome-4.5.0/css/font-awesome.min.css"',
          '"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          '"../../../sherpa/lib/jquery-2.1.0.min.js"',
          '"https://code.jquery.com/jquery-2.2.3.min.js"'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          '"../../../sherpa/lib/d3-4.2.2.min.js"',
          '"https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          '<script type="text/javascript" src="../../../assert/js/assert.js"></script>',
          '<script>' + grunt.file.read( '../assert/js/assert.js' ) + '</script>'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          'var isBuiltMode = false;',
          'var isBuiltMode = true;' );
        contents = ChipperStringUtils.replaceAll( contents,
          'var templateText = null;',
          'var templateText = ' + templateString + ';'
        ); //template uses exclusively "
        // END RULES FOR INSTANCE_PROXIES

        contents = ChipperStringUtils.replaceAll( contents, '../../js/', '../../lib/' );
      }
      if ( contents !== originalContents ) {
        return contents;
      }
      else {
        return null; // signify no change (helps for images)
      }
    };
    copyDirectory( grunt, '../phet-io/html/wrappers', 'build/phet-io/protected/wrappers', filterWrapper );

    // TODO: what if something overwrites? These directories are being merged.
    var simSpecificPath = '../phet-io/html/' + buildConfig.name;
    if ( grunt.file.exists( simSpecificPath ) ) {
      var failOnExistingFiles = true;
      if ( skipBuild ) {
        failOnExistingFiles = false;
      }
      copyDirectory( grunt, simSpecificPath, 'build/phet-io/protected/wrappers', filterWrapper, { failOnExistingFiles: failOnExistingFiles } );
    }

    // The .htaccess file
    grunt.file.copy( '../phet-io/templates/.htaccess', 'build/phet-io/protected/.htaccess' );

    var wrapperHTML = grunt.file.read( '../phet-io/templates/wrappers.html' );
    wrapperHTML = ChipperStringUtils.replaceAll( wrapperHTML, '$SIM$', buildConfig.name );
    wrapperHTML = ChipperStringUtils.replaceAll( wrapperHTML, '$VERSION$', buildConfig.version );
    wrapperHTML = ChipperStringUtils.replaceAll( wrapperHTML, '$PHET_IO_HTML_SIM_FILENAME$', buildConfig.name + '_en-phetio.html' );
    wrapperHTML = ChipperStringUtils.replaceAll( wrapperHTML, '$SIM_NAME$', buildConfig.name );
    grunt.file.write( 'build/phet-io/protected/wrappers.html', wrapperHTML );

    var destinationPath = 'build/phet-io/lib';

    var minifyAndWrite = function( filename, precode ) {
      var minified = uglify.minify( [ '../phet-io/js/' + filename ], {
        mangle: true,
        output: {
          inline_script: true, // escape </script
          beautify: false
        },
        compress: {
          global_defs: {}
        }
      } ).code;

      var copyrightHeader = '// Copyright 2002-2016, University of Colorado Boulder\n' +
                            '// This PhET-iO file requires a license\n' +
                            '// USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.\n' +
                            '// For licensing, please contact phethelp@colorado.edu';
      grunt.file.write( destinationPath + '/' + filename, copyrightHeader + '\n\n' + precode + minified );
    };

    minifyAndWrite( 'SimIFrameClient.js', '' );

    // Determine which sim versions will be pointed to by SimWrapperUtils
    minifyAndWrite( 'SimWrapperUtils.js', 'window.useRelativeSimPath=true;' );
  };

  if ( skipBuild ) {
    grunt.file.mkdir( 'build/phet-io' );
    createOtherPhETIOFiles();
  }
  else {
    var done = grunt.task.current.async();
    var command1 = 'grunt --brand=phet-io';
    var command2 = 'grunt --brand=phet';
    grunt.log.writeln( 'Starting command: ' + command1 + '...' );
    child_process.exec( command1, { cwd: cwd }, function( error1, stdout1, stderr1 ) {
      assert( !error1, 'error in ' + command1 + '\n' + error1 );
      grunt.log.writeln( stdout1 );
      grunt.log.writeln( stderr1 );
      grunt.log.writeln( 'Finished command: ' + command1 );
      copyDirectory( grunt, buildDir, phetioBuildDir );

      grunt.log.writeln( 'Starting command: ' + command2 + '...' );
      child_process.exec( command2, { cwd: cwd }, function( error2, stdout2, stderr2 ) {
        assert( !error2, 'error in ' + command2 + '\n' + error2 );
        grunt.log.writeln( stdout2 );
        grunt.log.writeln( stderr2 );
        grunt.log.writeln( 'Finished command: ' + command2 );
        copyDirectory( grunt, phetioBuildDir, buildDir + '/phet-io' );

        createOtherPhETIOFiles();
        done();
      } );
    } );
  }
};<commit_msg>Use new directory structure for build, see https://github.com/phetsims/phet-io/issues/588<commit_after>// Copyright 2015, University of Colorado Boulder

/**
 * Full PhET-iO build including phet version, phet-io version and accompanying material.
 *
 * @author Sam Reid (PhET Interactive Simulations)
 */

// modules
var assert = require( 'assert' );
var copyDirectory = require( '../../../chipper/js/grunt/copyDirectory' );
var child_process = require( 'child_process' );
var ChipperStringUtils = require( '../../../chipper/js/common/ChipperStringUtils' );

/**
 * @param grunt the grunt instance
 * @param {Object} buildConfig
 */
module.exports = function( grunt, buildConfig ) {
  'use strict';

  // TODO: chipper#101 eek, this is scary! we are importing from the repository dir. ideally we should just have uglify-js installed once in chipper?
  var uglify = require( '../../../' + buildConfig.name + '/node_modules/uglify-js' );

  // Read a JS file and uglify
  var readUgly = function( filename ) {
    var text = grunt.file.read( filename );
    return uglify.minify( text, {
      mangle: true,
      fromString: true,
      output: {
        inline_script: true, // escape </script
        beautify: false
      },
      compress: {
        global_defs: {}
      }
    } ).code;
  };

  var skipBuild = grunt.option( 'skipBuild' );
  var packageJSON = grunt.file.readJSON( 'package.json' );

  var cwd = process.cwd();

  var phetioBuildDir = cwd + '/build-phet-io';
  var buildDir = cwd + '/build';

  // Clean
  if ( grunt.file.exists( phetioBuildDir ) ) {
    grunt.file.delete( phetioBuildDir );
  }
  grunt.file.mkdir( phetioBuildDir );

  var createOtherPhETIOFiles = function() {

    // create devguide/
    grunt.file.mkdir( 'build/phet-io/devguide' );
    copyDirectory( grunt, '../sherpa/lib/highlight.js-9.1.0', 'build/phet-io/devguide/lib/highlight.js-9.1.0' );
    copyDirectory( grunt, '../phet-io/doc/devguide', 'build/phet-io/devguide', function( abspath, contents ) {
      if ( abspath.indexOf( '.html' ) >= 0 ) {
        return ChipperStringUtils.replaceAll( contents, '../../../sherpa/lib/highlight.js-9.1.0', 'lib/highlight.js-9.1.0' );
      }
    } );

    // output the SimIFrameClient.js and SimWrapperUtils.js to the top level lib (not protected), see https://github.com/phetsims/phet-io/issues/353
    grunt.file.mkdir( 'build/phet-io/lib' );

    // create protected/
    grunt.file.mkdir( 'build/phet-io/protected' );

    // Load the template for instance-proxies.html
    var entry = packageJSON.phet[ 'phet-io' ];
    var preload = [];
    if ( entry && entry.preload ) {
      preload = entry.preload;
    }

    var simAPIDeclaration = '';
    for ( var i = 0; i < preload.length; i++ ) {
      simAPIDeclaration = simAPIDeclaration + readUgly( preload[ i ] );
    }
    var templateText = grunt.file.read( '../phet-io/html/templates/load-state.html' );
    var lines = templateText.split( '\n' );
    var templateString = '[';
    lines.forEach( function( line ) {
      templateString = templateString + '\'' + line + '\',\n';
    } );
    // "<script>" does not parse in an HTML file (even if in JS), so we split it apart into separate substrings.
    templateString = ChipperStringUtils.replaceAll( templateString, '<script>', '<scr\'+\'ipt>' );
    templateString = ChipperStringUtils.replaceAll( templateString, '</script>', '</scr\'+\'ipt>' );
    templateString = templateString + '\'\'].join(\'\\n\')';

    var filterWrapper = function( abspath, contents ) {
      var originalContents = contents + '';
      if ( abspath.indexOf( '.js' ) >= 0 || abspath.indexOf( '.html' ) >= 0 ) {
        contents = ChipperStringUtils.replaceAll( contents, '$SIMULATION_NAME$', buildConfig.name );
        contents = ChipperStringUtils.replaceAll( contents, '$SIMULATION_VERSION$', buildConfig.version );
      }
      if ( abspath.indexOf( '.html' ) >= 0 ) {

        // RULES FOR INSTANCE_PROXIES
        contents = ChipperStringUtils.replaceAll( contents,
          '"../../../sherpa/lib/lodash-2.4.1.min.js"',
          '"https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.11.2/lodash.min.js"'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          '"../../../sherpa/lib/font-awesome-4.5.0/css/font-awesome.min.css"',
          '"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.5.0/css/font-awesome.min.css"'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          '"../../../sherpa/lib/jquery-2.1.0.min.js"',
          '"https://code.jquery.com/jquery-2.2.3.min.js"'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          '"../../../sherpa/lib/d3-4.2.2.min.js"',
          '"https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.2/d3.min.js"'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          '<script type="text/javascript" src="../../../assert/js/assert.js"></script>',
          '<script>' + grunt.file.read( '../assert/js/assert.js' ) + '</script>'
        );
        contents = ChipperStringUtils.replaceAll( contents,
          'var isBuiltMode = false;',
          'var isBuiltMode = true;' );
        contents = ChipperStringUtils.replaceAll( contents,
          'var templateText = null;',
          'var templateText = ' + templateString + ';'
        ); //template uses exclusively "
        // END RULES FOR INSTANCE_PROXIES

        contents = ChipperStringUtils.replaceAll( contents, '../../js/', '../../lib/' );
      }
      if ( contents !== originalContents ) {
        return contents;
      }
      else {
        return null; // signify no change (helps for images)
      }
    };
    copyDirectory( grunt, '../phet-io/apps', 'build/phet-io/protected/apps', filterWrapper );

    // TODO: what if something overwrites? These directories are being merged.
    var simSpecificPath = '../phet-io/html/' + buildConfig.name;
    if ( grunt.file.exists( simSpecificPath ) ) {
      var failOnExistingFiles = true;
      if ( skipBuild ) {
        failOnExistingFiles = false;
      }
      copyDirectory( grunt, simSpecificPath, 'build/phet-io/protected/wrappers', filterWrapper, { failOnExistingFiles: failOnExistingFiles } );
    }

    // The .htaccess file
    grunt.file.copy( '../phet-io/templates/.htaccess', 'build/phet-io/protected/.htaccess' );

    var wrapperHTML = grunt.file.read( '../phet-io/templates/wrappers.html' );
    wrapperHTML = ChipperStringUtils.replaceAll( wrapperHTML, '$SIM$', buildConfig.name );
    wrapperHTML = ChipperStringUtils.replaceAll( wrapperHTML, '$VERSION$', buildConfig.version );
    wrapperHTML = ChipperStringUtils.replaceAll( wrapperHTML, '$PHET_IO_HTML_SIM_FILENAME$', buildConfig.name + '_en-phetio.html' );
    wrapperHTML = ChipperStringUtils.replaceAll( wrapperHTML, '$SIM_NAME$', buildConfig.name );
    grunt.file.write( 'build/phet-io/protected/wrappers.html', wrapperHTML );

    var destinationPath = 'build/phet-io/lib';

    var minifyAndWrite = function( filename, precode ) {
      var minified = uglify.minify( [ '../phet-io/apps/common/js/' + filename ], {
        mangle: true,
        output: {
          inline_script: true, // escape </script
          beautify: false
        },
        compress: {
          global_defs: {}
        }
      } ).code;

      var copyrightHeader = '// Copyright 2002-2016, University of Colorado Boulder\n' +
                            '// This PhET-iO file requires a license\n' +
                            '// USE WITHOUT A LICENSE AGREEMENT IS STRICTLY PROHIBITED.\n' +
                            '// For licensing, please contact phethelp@colorado.edu';
      grunt.file.write( destinationPath + '/' + filename, copyrightHeader + '\n\n' + precode + minified );
    };

    minifyAndWrite( 'SimIFrameClient.js', '' );

    // Determine which sim versions will be pointed to by SimWrapperUtils
    minifyAndWrite( 'SimWrapperUtils.js', 'window.useRelativeSimPath=true;' );
  };

  if ( skipBuild ) {
    grunt.file.mkdir( 'build/phet-io' );
    createOtherPhETIOFiles();
  }
  else {
    var done = grunt.task.current.async();
    var command1 = 'grunt --brand=phet-io';
    var command2 = 'grunt --brand=phet';
    grunt.log.writeln( 'Starting command: ' + command1 + '...' );
    child_process.exec( command1, { cwd: cwd }, function( error1, stdout1, stderr1 ) {
      assert( !error1, 'error in ' + command1 + '\n' + error1 );
      grunt.log.writeln( stdout1 );
      grunt.log.writeln( stderr1 );
      grunt.log.writeln( 'Finished command: ' + command1 );
      copyDirectory( grunt, buildDir, phetioBuildDir );

      grunt.log.writeln( 'Starting command: ' + command2 + '...' );
      child_process.exec( command2, { cwd: cwd }, function( error2, stdout2, stderr2 ) {
        assert( !error2, 'error in ' + command2 + '\n' + error2 );
        grunt.log.writeln( stdout2 );
        grunt.log.writeln( stderr2 );
        grunt.log.writeln( 'Finished command: ' + command2 );
        copyDirectory( grunt, phetioBuildDir, buildDir + '/phet-io' );

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


<commit_before>// Copyright 2018-2021, University of Colorado Boulder

/**
 * For generating random mouse/touch input to a Display, to hopefully discover bugs in an automated fashion.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

import Random from '../../../dot/js/Random.js';
import Vector2 from '../../../dot/js/Vector2.js';
import { scenery } from '../imports.js';

class InputFuzzer {
  /**
   * @param {Display} display
   * @param {number} seed
   */
  constructor( display, seed ) {

    // @private {Display}
    this.display = display;

    // @private {Array.<Object>} - { id: {number}, position: {Vector2} }
    this.touches = [];

    // @private {number}
    this.nextTouchID = 1;

    // @private {boolean}
    this.isMouseDown = false;

    // @private {Vector2} - Starts at 0,0, because why not
    this.mousePosition = new Vector2( 0, 0 );

    // @private {Random}
    this.random = new Random( { seed: seed } );

    // @private {function} - All of the various actions that may be options at certain times.
    this.mouseToggleAction = () => {
      this.mouseToggle();
    };
    this.mouseMoveAction = () => {
      this.mouseMove();
    };
    this.touchStartAction = () => {
      const touch = this.createTouch( this.getRandomPosition() );
      this.touchStart( touch );
    };
    this.touchMoveAction = () => {
      const touch = this.random.sample( this.touches );
      this.touchMove( touch );
    };
    this.touchEndAction = () => {
      const touch = this.random.sample( this.touches );
      this.touchEnd( touch );
      this.removeTouch( touch );
    };
    this.touchCancelAction = () => {
      const touch = this.random.sample( this.touches );
      this.touchCancel( touch );
      this.removeTouch( touch );
    };
  }

  /**
   * Sends a certain (expected) number of random events through the input system for the display.
   * @public
   *
   * @param {number} averageEventCount
   * @param {boolean} allowMouse
   * @param {boolean} allowTouch
   * @param {number} maximumPointerCount
   */
  fuzzEvents( averageEventCount, allowMouse, allowTouch, maximumPointerCount ) {
    assert && assert( averageEventCount > 0, `averageEventCount must be positive: ${averageEventCount}` );

    this.display._input.currentlyFiringEvents = true;

    // run a variable number of events, with a certain chance of bailing out (so no events are possible)
    // models a geometric distribution of events
    // See https://github.com/phetsims/joist/issues/343 for notes on the distribution.
    while ( this.random.nextDouble() < 1 - 1 / ( averageEventCount + 1 ) ) {
      const activePointerCount = this.touches.length + ( this.isMouseDown ? 1 : 0 ); // 1 extra for the mouse if down
      const canAddPointer = activePointerCount < maximumPointerCount;

      const potentialActions = [];

      if ( allowMouse ) {
        // We could always mouse up/move (if we are down), but can't 'down/move' without being able to add a pointer
        if ( this.isMouseDown || canAddPointer ) {
          potentialActions.push( this.mouseToggleAction );
          potentialActions.push( this.mouseMoveAction );
        }
      }

      if ( allowTouch ) {
        if ( canAddPointer ) {
          potentialActions.push( this.touchStartAction );
        }
        if ( this.touches.length ) {
          potentialActions.push( this.random.nextDouble() < 0.8 ? this.touchEndAction : this.touchCancelAction );
          potentialActions.push( this.touchMoveAction );
        }
      }

      const action = this.random.sample( potentialActions );
      action();
    }

    this.display._input.currentlyFiringEvents = false;
  }

  /**
   * Creates a touch event from multiple touch "items".
   * @private
   *
   * @param {string} type - The main event type, e.g. 'touchmove'.
   * @param {Array.<Object>} touches - A subset of touch objects stored on the fuzzer itself.
   * @returns {Event} - If possible a TouchEvent, but may be a CustomEvent
   */
  createTouchEvent( type, touches ) {
    const domElement = this.display.domElement;

    // A specification that looks like a Touch object (and may be used to create one)
    const touchItems = touches.map( touch => ( {
      identifier: touch.id,
      target: domElement,
      clientX: touch.position.x,
      clientY: touch.position.y
    } ) );

    // Check if we can use Touch/TouchEvent constructors, see https://www.chromestatus.com/feature/4923255479599104
    if ( window.Touch !== undefined &&
         window.TouchEvent !== undefined &&
         window.Touch.length === 1 &&
         window.TouchEvent.length === 1 ) {
      const rawTouches = touchItems.map( touchItem => new window.Touch( touchItem ) );

      return new window.TouchEvent( type, {
        cancelable: true,
        bubbles: true,
        touches: rawTouches,
        targetTouches: [],
        changedTouches: rawTouches,
        shiftKey: false // TODO: Do we need this?
      } );
    }
    // Otherwise, use a CustomEvent and "fake" it.
    else {
      const event = document.createEvent( 'CustomEvent' );
      event.initCustomEvent( type, true, true, {
        touches: touchItems,
        targetTouches: [],
        changedTouches: touchItems
      } );
      return event;
    }
  }

  /**
   * Returns a random position somewhere in the display's global coordinates.
   * @private
   *
   * @returns {Vector2}
   */
  getRandomPosition() {
    return new Vector2(
      Math.floor( this.random.nextDouble() * this.display.width ),
      Math.floor( this.random.nextDouble() * this.display.height )
    );
  }

  /**
   * Creates a touch from a position (and adds it).
   * @private
   *
   * @param {Vector2} position
   * @returns {Object}
   */
  createTouch( position ) {
    const touch = {
      id: this.nextTouchID++,
      position: position
    };
    this.touches.push( touch );
    return touch;
  }

  /**
   * Removes a touch from our list.
   * @private
   *
   * @param {Object} touch
   */
  removeTouch( touch ) {
    this.touches.splice( this.touches.indexOf( touch ), 1 );
  }

  /**
   * Triggers a touchStart for the given touch.
   * @private
   *
   * @param {Object} touch
   */
  touchStart( touch ) {
    const event = this.createTouchEvent( 'touchstart', [ touch ] );

    this.display._input.validatePointers();
    this.display._input.touchStart( touch.id, touch.position, event );
  }

  /**
   * Triggers a touchMove for the given touch (to a random position in the display).
   * @private
   *
   * @param {Object} touch
   */
  touchMove( touch ) {
    touch.position = this.getRandomPosition();

    const event = this.createTouchEvent( 'touchmove', [ touch ] );

    this.display._input.validatePointers();
    this.display._input.touchMove( touch.id, touch.position, event );
  }

  /**
   * Triggers a touchEnd for the given touch.
   * @private
   *
   * @param {Object} touch
   */
  touchEnd( touch ) {
    const event = this.createTouchEvent( 'touchend', [ touch ] );

    this.display._input.validatePointers();
    this.display._input.touchEnd( touch.id, touch.position, event );
  }

  /**
   * Triggers a touchCancel for the given touch.
   * @private
   *
   * @param {Object} touch
   */
  touchCancel( touch ) {
    const event = this.createTouchEvent( 'touchcancel', [ touch ] );

    this.display._input.validatePointers();
    this.display._input.touchCancel( touch.id, touch.position, event );
  }

  /**
   * Triggers a mouse toggle (switching from down => up or vice versa).
   * @private
   */
  mouseToggle() {
    const domEvent = document.createEvent( 'MouseEvent' );

    // technically deprecated, but DOM4 event constructors not out yet. people on #whatwg said to use it
    domEvent.initMouseEvent( this.isMouseDown ? 'mouseup' : 'mousedown', true, true, window, 1, // click count
      this.mousePosition.x, this.mousePosition.y, this.mousePosition.x, this.mousePosition.y,
      false, false, false, false,
      0, // button
      null );

    this.display._input.validatePointers();

    if ( this.isMouseDown ) {
      this.display._input.mouseUp( this.mousePosition, domEvent );
      this.isMouseDown = false;
    }
    else {
      this.display._input.mouseDown( null, this.mousePosition, domEvent );
      this.isMouseDown = true;
    }
  }

  /**
   * Triggers a mouse move (to a random position in the display).
   * @private
   */
  mouseMove() {
    this.mousePosition = this.getRandomPosition();

    // our move event
    const domEvent = document.createEvent( 'MouseEvent' ); // not 'MouseEvents' according to DOM Level 3 spec

    // technically deprecated, but DOM4 event constructors not out yet. people on #whatwg said to use it
    domEvent.initMouseEvent( 'mousemove', true, true, window, 0, // click count
      this.mousePosition.x, this.mousePosition.y, this.mousePosition.x, this.mousePosition.y,
      false, false, false, false,
      0, // button
      null );

    this.display._input.validatePointers();
    this.display._input.mouseMove( this.mousePosition, domEvent );
  }
}

scenery.register( 'InputFuzzer', InputFuzzer );
export default InputFuzzer;<commit_msg>update copyright dates from daily grunt work<commit_after>// Copyright 2018-2022, University of Colorado Boulder

/**
 * For generating random mouse/touch input to a Display, to hopefully discover bugs in an automated fashion.
 *
 * @author Jonathan Olson <jonathan.olson@colorado.edu>
 */

import Random from '../../../dot/js/Random.js';
import Vector2 from '../../../dot/js/Vector2.js';
import { scenery } from '../imports.js';

class InputFuzzer {
  /**
   * @param {Display} display
   * @param {number} seed
   */
  constructor( display, seed ) {

    // @private {Display}
    this.display = display;

    // @private {Array.<Object>} - { id: {number}, position: {Vector2} }
    this.touches = [];

    // @private {number}
    this.nextTouchID = 1;

    // @private {boolean}
    this.isMouseDown = false;

    // @private {Vector2} - Starts at 0,0, because why not
    this.mousePosition = new Vector2( 0, 0 );

    // @private {Random}
    this.random = new Random( { seed: seed } );

    // @private {function} - All of the various actions that may be options at certain times.
    this.mouseToggleAction = () => {
      this.mouseToggle();
    };
    this.mouseMoveAction = () => {
      this.mouseMove();
    };
    this.touchStartAction = () => {
      const touch = this.createTouch( this.getRandomPosition() );
      this.touchStart( touch );
    };
    this.touchMoveAction = () => {
      const touch = this.random.sample( this.touches );
      this.touchMove( touch );
    };
    this.touchEndAction = () => {
      const touch = this.random.sample( this.touches );
      this.touchEnd( touch );
      this.removeTouch( touch );
    };
    this.touchCancelAction = () => {
      const touch = this.random.sample( this.touches );
      this.touchCancel( touch );
      this.removeTouch( touch );
    };
  }

  /**
   * Sends a certain (expected) number of random events through the input system for the display.
   * @public
   *
   * @param {number} averageEventCount
   * @param {boolean} allowMouse
   * @param {boolean} allowTouch
   * @param {number} maximumPointerCount
   */
  fuzzEvents( averageEventCount, allowMouse, allowTouch, maximumPointerCount ) {
    assert && assert( averageEventCount > 0, `averageEventCount must be positive: ${averageEventCount}` );

    this.display._input.currentlyFiringEvents = true;

    // run a variable number of events, with a certain chance of bailing out (so no events are possible)
    // models a geometric distribution of events
    // See https://github.com/phetsims/joist/issues/343 for notes on the distribution.
    while ( this.random.nextDouble() < 1 - 1 / ( averageEventCount + 1 ) ) {
      const activePointerCount = this.touches.length + ( this.isMouseDown ? 1 : 0 ); // 1 extra for the mouse if down
      const canAddPointer = activePointerCount < maximumPointerCount;

      const potentialActions = [];

      if ( allowMouse ) {
        // We could always mouse up/move (if we are down), but can't 'down/move' without being able to add a pointer
        if ( this.isMouseDown || canAddPointer ) {
          potentialActions.push( this.mouseToggleAction );
          potentialActions.push( this.mouseMoveAction );
        }
      }

      if ( allowTouch ) {
        if ( canAddPointer ) {
          potentialActions.push( this.touchStartAction );
        }
        if ( this.touches.length ) {
          potentialActions.push( this.random.nextDouble() < 0.8 ? this.touchEndAction : this.touchCancelAction );
          potentialActions.push( this.touchMoveAction );
        }
      }

      const action = this.random.sample( potentialActions );
      action();
    }

    this.display._input.currentlyFiringEvents = false;
  }

  /**
   * Creates a touch event from multiple touch "items".
   * @private
   *
   * @param {string} type - The main event type, e.g. 'touchmove'.
   * @param {Array.<Object>} touches - A subset of touch objects stored on the fuzzer itself.
   * @returns {Event} - If possible a TouchEvent, but may be a CustomEvent
   */
  createTouchEvent( type, touches ) {
    const domElement = this.display.domElement;

    // A specification that looks like a Touch object (and may be used to create one)
    const touchItems = touches.map( touch => ( {
      identifier: touch.id,
      target: domElement,
      clientX: touch.position.x,
      clientY: touch.position.y
    } ) );

    // Check if we can use Touch/TouchEvent constructors, see https://www.chromestatus.com/feature/4923255479599104
    if ( window.Touch !== undefined &&
         window.TouchEvent !== undefined &&
         window.Touch.length === 1 &&
         window.TouchEvent.length === 1 ) {
      const rawTouches = touchItems.map( touchItem => new window.Touch( touchItem ) );

      return new window.TouchEvent( type, {
        cancelable: true,
        bubbles: true,
        touches: rawTouches,
        targetTouches: [],
        changedTouches: rawTouches,
        shiftKey: false // TODO: Do we need this?
      } );
    }
    // Otherwise, use a CustomEvent and "fake" it.
    else {
      const event = document.createEvent( 'CustomEvent' );
      event.initCustomEvent( type, true, true, {
        touches: touchItems,
        targetTouches: [],
        changedTouches: touchItems
      } );
      return event;
    }
  }

  /**
   * Returns a random position somewhere in the display's global coordinates.
   * @private
   *
   * @returns {Vector2}
   */
  getRandomPosition() {
    return new Vector2(
      Math.floor( this.random.nextDouble() * this.display.width ),
      Math.floor( this.random.nextDouble() * this.display.height )
    );
  }

  /**
   * Creates a touch from a position (and adds it).
   * @private
   *
   * @param {Vector2} position
   * @returns {Object}
   */
  createTouch( position ) {
    const touch = {
      id: this.nextTouchID++,
      position: position
    };
    this.touches.push( touch );
    return touch;
  }

  /**
   * Removes a touch from our list.
   * @private
   *
   * @param {Object} touch
   */
  removeTouch( touch ) {
    this.touches.splice( this.touches.indexOf( touch ), 1 );
  }

  /**
   * Triggers a touchStart for the given touch.
   * @private
   *
   * @param {Object} touch
   */
  touchStart( touch ) {
    const event = this.createTouchEvent( 'touchstart', [ touch ] );

    this.display._input.validatePointers();
    this.display._input.touchStart( touch.id, touch.position, event );
  }

  /**
   * Triggers a touchMove for the given touch (to a random position in the display).
   * @private
   *
   * @param {Object} touch
   */
  touchMove( touch ) {
    touch.position = this.getRandomPosition();

    const event = this.createTouchEvent( 'touchmove', [ touch ] );

    this.display._input.validatePointers();
    this.display._input.touchMove( touch.id, touch.position, event );
  }

  /**
   * Triggers a touchEnd for the given touch.
   * @private
   *
   * @param {Object} touch
   */
  touchEnd( touch ) {
    const event = this.createTouchEvent( 'touchend', [ touch ] );

    this.display._input.validatePointers();
    this.display._input.touchEnd( touch.id, touch.position, event );
  }

  /**
   * Triggers a touchCancel for the given touch.
   * @private
   *
   * @param {Object} touch
   */
  touchCancel( touch ) {
    const event = this.createTouchEvent( 'touchcancel', [ touch ] );

    this.display._input.validatePointers();
    this.display._input.touchCancel( touch.id, touch.position, event );
  }

  /**
   * Triggers a mouse toggle (switching from down => up or vice versa).
   * @private
   */
  mouseToggle() {
    const domEvent = document.createEvent( 'MouseEvent' );

    // technically deprecated, but DOM4 event constructors not out yet. people on #whatwg said to use it
    domEvent.initMouseEvent( this.isMouseDown ? 'mouseup' : 'mousedown', true, true, window, 1, // click count
      this.mousePosition.x, this.mousePosition.y, this.mousePosition.x, this.mousePosition.y,
      false, false, false, false,
      0, // button
      null );

    this.display._input.validatePointers();

    if ( this.isMouseDown ) {
      this.display._input.mouseUp( this.mousePosition, domEvent );
      this.isMouseDown = false;
    }
    else {
      this.display._input.mouseDown( null, this.mousePosition, domEvent );
      this.isMouseDown = true;
    }
  }

  /**
   * Triggers a mouse move (to a random position in the display).
   * @private
   */
  mouseMove() {
    this.mousePosition = this.getRandomPosition();

    // our move event
    const domEvent = document.createEvent( 'MouseEvent' ); // not 'MouseEvents' according to DOM Level 3 spec

    // technically deprecated, but DOM4 event constructors not out yet. people on #whatwg said to use it
    domEvent.initMouseEvent( 'mousemove', true, true, window, 0, // click count
      this.mousePosition.x, this.mousePosition.y, this.mousePosition.x, this.mousePosition.y,
      false, false, false, false,
      0, // button
      null );

    this.display._input.validatePointers();
    this.display._input.mouseMove( this.mousePosition, domEvent );
  }
}

scenery.register( 'InputFuzzer', InputFuzzer );
export default InputFuzzer;<|endoftext|>