<commit_before>/*
 * Copyright 2013 Mirantis, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
**/
casper.start();
casper.createCluster({name: 'Test Cluster'});
var nodes = [
    {status: 'discover', manufacturer: 'Dell', mac: 'C0:8D:DF:52:76:F1'}
];
nodes.forEach(function(node) {
    casper.createNode(node);
});
var vmSDA;
var osSDA;

casper.loadPage('#cluster/1/nodes').waitForSelector('#tab-nodes > *');

casper.then(function() {
    this.test.comment('Testing nodes disks');

    var sdaDisk = '.disk-box[data-disk=sda]';
    var sdaDiskVM = sdaDisk + ' .volume-group-box[data-volume=vm]';
    var sdaDiskOS = sdaDisk + '  .volume-group-box[data-volume=os]';
    var vdaDisk = '.disk-box[data-disk=vda]';
    var vdaDiskVM = vdaDisk + ' .volume-group-box[data-volume=vm]';
    var vdaDiskOS = vdaDisk + '  .volume-group-box[data-volume=os]';

    this.then(function() {
        this.click('.node-list-compute .btn-add-nodes');
        this.test.assertSelectorAppears('.add-nodes-screen', 'Add compute nodes screen appears');
        this.test.assertSelectorDisappears('.add-nodes-screen .available-nodes .progress', 'Available for addition computes are loaded');
        this.then(function() {
            this.click('.add-nodes-screen .nodebox');
            this.click('.add-nodes-screen .btn-apply');
        });
        this.test.assertSelectorAppears('.nodes-by-roles-screen', 'Return to nodes tab');
        this.then(function() {
            this.click('.node-hardware');
        });
        this.test.assertSelectorAppears('.modal', 'Node details popup was opened');
        this.then(function() {
            this.click('.btn-edit-disks');
        });
        this.test.assertSelectorAppears('.edit-node-disks', 'Node disks configuration screen appears');
    });

    this.then(function() {
        this.test.comment('Testing nodes disks layout');
        this.test.assertEvalEquals(function() {return $('.disk-box').length}, 2, 'Number of disks is correct');
        this.test.assertExists('.btn-defaults:not(:disabled)', 'Load Defaults button is enabled');
        this.test.assertExists('.btn-revert-changes:disabled', 'Cancel button is disabled');
        this.test.assertExists('.btn-apply:disabled', 'Apply button is disabled');
    });

    this.then(function() {
        this.test.comment('Testing nodes disk block');
        this.click(sdaDisk + ' .toggle-volume');
        vmSDA= this.getElementAttribute(sdaDiskVM + ' input', 'value');
        osSDA= this.getElementAttribute(sdaDiskOS + ' input', 'value');
        this.test.assertExists(sdaDiskOS, 'Base system group form is presented');
        this.test.assertExists(sdaDiskVM, 'Virtual Storage group form is presented');
        this.test.assertExists(sdaDisk + ' .disk-visual .os .close-btn.hide', 'Button Close for Base system group is not presented');
        this.test.assertDoesntExist(sdaDisk + ' .disk-visual .vm .close-btn:visible', 'Button Close for Virtual Storage group is presented');
    });

    this.then(function() {
        this.test.comment('Testing button Apply: interractions');
        this.fill(sdaDiskVM, {'vm': '80'});
        this.evaluate(function(sdaDiskVM) {
            $(sdaDiskVM + ' input').keyup();
        },{sdaDiskVM: sdaDiskVM});
        this.test.assertExists('.btn-defaults:not(:disabled)', 'Load Defaults button is enabled');
        this.test.assertExists('.btn-revert-changes:not(:disabled)', 'Cancel button is enabled');
        this.test.assertExists('.btn-apply:not(:disabled)', 'Apply button is enabled');
        this.click(sdaDiskVM + ' .use-all-allowed');
        this.test.assertExists('.btn-defaults:not(:disabled)', 'Load Defaults button is enabled');
        this.test.assertExists('.btn-revert-changes:disabled', 'Cancel button is disabled');
        this.test.assertExists('.btn-apply:disabled', 'Apply button is disabled');
    });

    this.then(function() {
        this.test.comment('Testing button Load Defaults');
        this.test.assertExists('.btn-defaults:not(:disabled)', 'Load Defaults button is enabled');
        this.click('.btn-defaults');
        this.test.assertSelectorAppears('.btn-defaults:not(:disabled)', 'Defaults were loaded');
        this.then(function() {
            this.test.assertEvalEquals(function(sdaDiskVM) {return $(sdaDiskVM + ' input').attr('value')}, vmSDA, 'Volume group input control VM contains default value', {sdaDiskVM:sdaDiskVM});
            this.test.assertEvalEquals(function(sdaDiskOS) {return $(sdaDiskOS + ' input').attr('value')}, osSDA, 'Volume group input control OS contains default value', {sdaDiskOS:sdaDiskOS});
        });
    });

    this.then(function() {
        this.test.comment('Testing volume group deletion and Cancel button');
        this.click(sdaDisk + ' .disk-visual .vm .close-btn');
        this.test.assertEquals(this.getElementBounds(sdaDisk + ' .disk-visual .vm').width, 0, 'VM group was removed successfully');
        this.click('.btn-revert-changes');
        this.test.assertEvalEquals(function(sdaDiskVM) {return $(sdaDiskVM + ' input').attr('value')}, vmSDA, 'Volume group input control VM contains default value', {sdaDiskVM:sdaDiskVM});
        this.click(sdaDisk + ' .disk-visual .vm .close-btn');
        this.test.assertEval(function(sdaDisk) {return $(sdaDisk + ' .disk-visual .unallocated').width() > 0}, 'There is unallocated space after Virtual Storage VG removal',{sdaDisk:sdaDisk});
        this.test.assertEvalEquals(function(sdaDiskVM) {return $(sdaDiskVM + ' input').val()}, '0', 'Volume group input control contains correct value',{sdaDiskVM:sdaDiskVM});
        this.click(sdaDiskVM + ' .use-all-allowed');
        this.test.assertEquals(this.getElementBounds(sdaDisk + ' .disk-visual .unallocated').width, 0, 'Use all unallocated area for VM');
        this.fill(sdaDiskVM, {'vm': '0'});
        this.evaluate(function(sdaDiskVM) {
            $(sdaDiskVM + ' input').keyup();
        },{sdaDiskVM:sdaDiskVM});
        this.test.assertEquals(this.getElementBounds(sdaDisk + ' .disk-visual .vm').width, 0, 'VM group was removed successfully');
        this.test.assertEval(function(sdaDisk) {return $(sdaDisk + ' .disk-visual .unallocated').width() > 0}, 'There is unallocated space after Virtual Storage VG removal', {sdaDisk:sdaDisk});
        this.test.assertEvalEquals(function(sdaDiskVM) {return $(sdaDiskVM + ' input').val()},'0', 'Volume group input control contains correct value',{sdaDiskVM:sdaDiskVM});
    });

    this.then(function() {
        this.test.comment('Testing use all allowed link');
        this.click(sdaDiskOS + ' .use-all-allowed');
        this.test.assertEquals(this.getElementBounds(sdaDisk + ' .disk-visual .unallocated').width, 0, 'Use all allowed link works correctly');
    });

    this.then(function() {
        this.test.comment('Testing validation of VG size');
        this.fill(sdaDiskOS, {'os': '0'});
        this.evaluate(function(sdaDiskOS) {
            $(sdaDiskOS + ' input').keyup();
        },{sdaDiskOS:sdaDiskOS});
        this.test.assertExists(sdaDiskOS + ' input.error', 'Field validation has worked');
        this.test.assertEval(function(sdaDisk) {return $(sdaDisk + ' .disk-visual .os').width() > 0}, 'VG size was not changed',{sdaDisk:sdaDisk});
        this.click(vdaDisk + ' .toggle-volume');
        this.test.assertExists(vdaDiskVM, 'Virtual Storage group form is presented');
        this.fill(vdaDisk + ' .volume-group-box[data-volume=vm]', {'vm': '10000'});
        this.evaluate(function(vdaDisk) {
            $(vdaDisk + ' .volume-group-box[data-volume=vm] input').keyup();
        },{vdaDisk:vdaDisk});
        this.fill(vdaDisk + ' .volume-group-box[data-volume=os]', {'os': '20000'});
        this.evaluate(function(vdaDisk) {
            $(vdaDisk + ' .volume-group-box[data-volume=os] input').keyup();
        },{vdaDisk:vdaDisk});
        this.test.assertDoesntExist(vdaDisk + ' .disk-visual.invalid', 'Field validation has worked');
        this.fill(vdaDisk + ' .volume-group-box[data-volume=vm]', {'vm': '200000'});
        this.evaluate(function(vdaDisk) {
            $(vdaDisk + ' .volume-group-box[data-volume=vm] input').keyup();
        },{vdaDisk:vdaDisk});
        this.test.assertExists(vdaDisk + ' .disk-visual.invalid', 'Field validation has worked in case of number that bigger than available space on disk');
    });

});

casper.run(function() {
    this.test.done();
});
<commit_msg>Fix disks UI tests<commit_after>/*
 * Copyright 2013 Mirantis, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may
 * not use this file except in compliance with the License. You may obtain
 * a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
 * License for the specific language governing permissions and limitations
 * under the License.
**/
casper.start();
casper.createCluster({name: 'Test Cluster'});
var nodes = [
    {status: 'discover', manufacturer: 'Dell', mac: 'C0:8D:DF:52:76:F1', cluster_id: 1, roles: ['compute']}
];
nodes.forEach(function(node) {
    casper.createNode(node);
});
var vmSDA;
var osSDA;

casper.loadPage('#cluster/1/nodes').waitForSelector('#tab-nodes > *');

casper.then(function() {
    this.test.comment('Testing nodes disks');

    var sdaDisk = '.disk-box[data-disk=sda]';
    var sdaDiskVM = sdaDisk + ' .volume-group-box[data-volume=vm]';
    var sdaDiskOS = sdaDisk + '  .volume-group-box[data-volume=os]';
    var vdaDisk = '.disk-box[data-disk=vda]';
    var vdaDiskVM = vdaDisk + ' .volume-group-box[data-volume=vm]';
    var vdaDiskOS = vdaDisk + '  .volume-group-box[data-volume=os]';

    this.then(function() {
        this.then(function() {
            this.click('.node-hardware');
        });
        this.test.assertSelectorAppears('.modal', 'Node details popup was opened');
        this.then(function() {
            this.click('.btn-edit-disks');
        });
        this.test.assertSelectorAppears('.edit-node-disks', 'Node disks configuration screen appears');
    });

    this.then(function() {
        this.test.comment('Testing nodes disks layout');
        this.test.assertEvalEquals(function() {return $('.disk-box').length}, 2, 'Number of disks is correct');
        this.test.assertExists('.btn-defaults:not(:disabled)', 'Load Defaults button is enabled');
        this.test.assertExists('.btn-revert-changes:disabled', 'Cancel button is disabled');
        this.test.assertExists('.btn-apply:disabled', 'Apply button is disabled');
    });

    this.then(function() {
        this.test.comment('Testing nodes disk block');
        this.click(sdaDisk + ' .toggle-volume');
        vmSDA= this.getElementAttribute(sdaDiskVM + ' input', 'value');
        osSDA= this.getElementAttribute(sdaDiskOS + ' input', 'value');
        this.test.assertExists(sdaDiskOS, 'Base system group form is presented');
        this.test.assertExists(sdaDiskVM, 'Virtual Storage group form is presented');
        this.test.assertExists(sdaDisk + ' .disk-visual .os .close-btn.hide', 'Button Close for Base system group is not presented');
        this.test.assertDoesntExist(sdaDisk + ' .disk-visual .vm .close-btn:visible', 'Button Close for Virtual Storage group is presented');
    });

    this.then(function() {
        this.test.comment('Testing button Apply: interractions');
        this.fill(sdaDiskVM, {'vm': '80'});
        this.evaluate(function(sdaDiskVM) {
            $(sdaDiskVM + ' input').keyup();
        },{sdaDiskVM: sdaDiskVM});
        this.test.assertExists('.btn-defaults:not(:disabled)', 'Load Defaults button is enabled');
        this.test.assertExists('.btn-revert-changes:not(:disabled)', 'Cancel button is enabled');
        this.test.assertExists('.btn-apply:not(:disabled)', 'Apply button is enabled');
        this.click(sdaDiskVM + ' .use-all-allowed');
        this.test.assertExists('.btn-defaults:not(:disabled)', 'Load Defaults button is enabled');
        this.test.assertExists('.btn-revert-changes:disabled', 'Cancel button is disabled');
        this.test.assertExists('.btn-apply:disabled', 'Apply button is disabled');
    });

    this.then(function() {
        this.test.comment('Testing button Load Defaults');
        this.test.assertExists('.btn-defaults:not(:disabled)', 'Load Defaults button is enabled');
        this.click('.btn-defaults');
        this.test.assertSelectorAppears('.btn-defaults:not(:disabled)', 'Defaults were loaded');
        this.then(function() {
            this.test.assertEvalEquals(function(sdaDiskVM) {return $(sdaDiskVM + ' input').attr('value')}, vmSDA, 'Volume group input control VM contains default value', {sdaDiskVM:sdaDiskVM});
            this.test.assertEvalEquals(function(sdaDiskOS) {return $(sdaDiskOS + ' input').attr('value')}, osSDA, 'Volume group input control OS contains default value', {sdaDiskOS:sdaDiskOS});
        });
    });

    this.then(function() {
        this.test.comment('Testing volume group deletion and Cancel button');
        this.click(sdaDisk + ' .disk-visual .vm .close-btn');
        this.test.assertEquals(this.getElementBounds(sdaDisk + ' .disk-visual .vm').width, 0, 'VM group was removed successfully');
        this.click('.btn-revert-changes');
        this.test.assertEvalEquals(function(sdaDiskVM) {return $(sdaDiskVM + ' input').attr('value')}, vmSDA, 'Volume group input control VM contains default value', {sdaDiskVM:sdaDiskVM});
        this.click(sdaDisk + ' .disk-visual .vm .close-btn');
        this.test.assertEval(function(sdaDisk) {return $(sdaDisk + ' .disk-visual .unallocated').width() > 0}, 'There is unallocated space after Virtual Storage VG removal',{sdaDisk:sdaDisk});
        this.test.assertEvalEquals(function(sdaDiskVM) {return $(sdaDiskVM + ' input').val()}, '0', 'Volume group input control contains correct value',{sdaDiskVM:sdaDiskVM});
        this.click(sdaDiskVM + ' .use-all-allowed');
        this.test.assertEquals(this.getElementBounds(sdaDisk + ' .disk-visual .unallocated').width, 0, 'Use all unallocated area for VM');
        this.fill(sdaDiskVM, {'vm': '0'});
        this.evaluate(function(sdaDiskVM) {
            $(sdaDiskVM + ' input').keyup();
        },{sdaDiskVM:sdaDiskVM});
        this.test.assertEquals(this.getElementBounds(sdaDisk + ' .disk-visual .vm').width, 0, 'VM group was removed successfully');
        this.test.assertEval(function(sdaDisk) {return $(sdaDisk + ' .disk-visual .unallocated').width() > 0}, 'There is unallocated space after Virtual Storage VG removal', {sdaDisk:sdaDisk});
        this.test.assertEvalEquals(function(sdaDiskVM) {return $(sdaDiskVM + ' input').val()},'0', 'Volume group input control contains correct value',{sdaDiskVM:sdaDiskVM});
    });

    this.then(function() {
        this.test.comment('Testing use all allowed link');
        this.click(sdaDiskOS + ' .use-all-allowed');
        this.test.assertEquals(this.getElementBounds(sdaDisk + ' .disk-visual .unallocated').width, 0, 'Use all allowed link works correctly');
    });

    this.then(function() {
        this.test.comment('Testing validation of VG size');
        this.fill(sdaDiskOS, {'os': '0'});
        this.evaluate(function(sdaDiskOS) {
            $(sdaDiskOS + ' input').keyup();
        },{sdaDiskOS:sdaDiskOS});
        this.test.assertExists(sdaDiskOS + ' input.error', 'Field validation has worked');
        this.test.assertEval(function(sdaDisk) {return $(sdaDisk + ' .disk-visual .os').width() > 0}, 'VG size was not changed',{sdaDisk:sdaDisk});
        this.click(vdaDisk + ' .toggle-volume');
        this.test.assertExists(vdaDiskVM, 'Virtual Storage group form is presented');
        this.fill(vdaDisk + ' .volume-group-box[data-volume=vm]', {'vm': '10000'});
        this.evaluate(function(vdaDisk) {
            $(vdaDisk + ' .volume-group-box[data-volume=vm] input').keyup();
        },{vdaDisk:vdaDisk});
        this.fill(vdaDisk + ' .volume-group-box[data-volume=os]', {'os': '20000'});
        this.evaluate(function(vdaDisk) {
            $(vdaDisk + ' .volume-group-box[data-volume=os] input').keyup();
        },{vdaDisk:vdaDisk});
        this.test.assertDoesntExist(vdaDisk + ' .disk-visual.invalid', 'Field validation has worked');
        this.fill(vdaDisk + ' .volume-group-box[data-volume=vm]', {'vm': '200000'});
        this.evaluate(function(vdaDisk) {
            $(vdaDisk + ' .volume-group-box[data-volume=vm] input').keyup();
        },{vdaDisk:vdaDisk});
        this.test.assertExists(vdaDisk + ' .disk-visual.invalid', 'Field validation has worked in case of number that bigger than available space on disk');
    });

});

casper.run(function() {
    this.test.done();
});
<|endoftext|>


<commit_before>require("requirish")._(module);

var subscription_service = require("lib/services/subscription_service");
var NotificationMessage = subscription_service.NotificationMessage;

var s = require("lib/datamodel/structures");
var StatusCodes = require("lib/datamodel/opcua_status_code").StatusCodes;
var assert = require("better-assert");

var _ = require("underscore");

var debugLog  = require("lib/misc/utils").make_debugLog(__filename);

/**
 * A client side implementation to deal with publish service.
 *
 * @class ClientSidePublishEngine
 *
 * @param session {ClientSession} - the client session
 * @param options {object} - the client session
 * @param options.keepalive_interval {number} - the keep alive interval ( default = 1000 ms)
 * @constructor
 *
 * The ClientSidePublishEngine encapsulates the mechanism to
 * deal with a OPCUA Server and constantly sending PublishRequest
 * The ClientSidePublishEngine also performs  notification acknowledgements.
 * Finally, ClientSidePublishEngine dispatch PublishResponse to the correct
 * Subscription id callback
 */
function ClientSidePublishEngine(session,options) {
        assert(session instanceof Object);

    options = options || {};
    options.keepalive_interval = options.keepalive_interval || 1000;

    this.session = session;
    this.keepalive_interval = options.keepalive_interval;

    this.subscriptionAcknowledgements = [];
    this.subscriptionIdFuncMap = {};
}

/**
 * @method acknowledge_notification
 * @param subscriptionId {Number} the subscription id
 * @param sequenceNumber {Number} the sequence number
 */
ClientSidePublishEngine.prototype.acknowledge_notification = function (subscriptionId, sequenceNumber) {
    this.subscriptionAcknowledgements.push({
        subscriptionId: subscriptionId,
        sequenceNumber: sequenceNumber
    });
};

/**
 * @method send_publish_request
 */
ClientSidePublishEngine.prototype.send_publish_request = function () {
    var self = this;
    setImmediate(function(){
        if (!self.session) {
            // session has been terminated
            return ;
        }
        self._send_publish_request();
    });
};

ClientSidePublishEngine.prototype._send_publish_request = function () {

    debugLog("sending publish request");
    var self = this;
    assert( self.session , "ClientSidePublishEngine terminated ?");

    var subscriptionAcknowledgements = self.subscriptionAcknowledgements;
    self.subscriptionAcknowledgements = [];

    var publish_request = new subscription_service.PublishRequest({
        subscriptionAcknowledgements: subscriptionAcknowledgements
    });
    self.session.publish(publish_request, function (err, response) {
        if (err) {
            console.log("xxxxxxxxxxxxxxxxxxx ClientSidePublishEngine.prototype._send_publish_request ".red ,err.message.yellow);
        } else {
            self._receive_publish_response(response);
        }
    });
};

ClientSidePublishEngine.prototype.terminate = function() {
    this.session = null;
};

/**
 * the number of active subscriptions managed by this publish engine.
 * @property subscriptionCount
 * @type {Number}
 */
ClientSidePublishEngine.prototype.__defineGetter__("subscriptionCount",function() {
    var self = this;
    return Object.keys(self.subscriptionIdFuncMap).length;
});

/**
 * @method registerSubscriptionCallback
 *
 * @param subscriptionId
 * @param {Function} callback
 */
ClientSidePublishEngine.prototype.registerSubscriptionCallback = function(subscriptionId,callback) {
    var self = this;
    assert(!self.subscriptionIdFuncMap.hasOwnProperty(subscriptionId)); // already registered ?
    self.subscriptionIdFuncMap[subscriptionId] = callback;
    self.send_publish_request();
};
/**
 * @method unregisterSubscriptionCallback
 *
 * @param subscriptionId
 */
ClientSidePublishEngine.prototype.unregisterSubscriptionCallback = function(subscriptionId) {
    var self = this;
    if (subscriptionId==="pending") {
        console.log("special subscriptionId here");
    }
    assert(self.subscriptionIdFuncMap.hasOwnProperty(subscriptionId));
    delete self.subscriptionIdFuncMap[subscriptionId];
};

ClientSidePublishEngine.prototype._receive_publish_response= function(response){

    debugLog("receive publish response");
    var self = this;

    // the id of the subscription sending the notification message
    var subscriptionId = response.subscriptionId;

    // the sequence numbers available in this subscription
    // for retransmission and not acknowledged by the client
    var available_seq = response.availableSequenceNumbers;

    // has the server more notification for us ?
    var moreNotifications = response.moreNotifications;

    var notificationMessage = response.notificationMessage;
    //  notificationMessage.sequenceNumber
    //  notificationMessage.publishTime
    //  notificationMessage.notificationData[]
    if (notificationMessage.notificationData.length === 0) {
        // this is a keep-alive notification
        // in this case , we shall not acknowledge notificationMessage.sequenceNumber
        // which is only an information of what will be the future sequenceNumber.
    } else {
        self.acknowledge_notification(subscriptionId, notificationMessage.sequenceNumber);
    }

    var callback_for_subscription =  self.subscriptionIdFuncMap[subscriptionId];
    if (callback_for_subscription && ! (self.session === null) ) {
        // feed the server with a new publish Request to the server
        self.send_publish_request();

        // delegate notificationData to the subscription callback
        callback_for_subscription(notificationMessage.notificationData,notificationMessage.publishTime);
    } else {
       debugLog(" ignoring notificationMessage",notificationMessage," for subscription", subscriptionId);
       debugLog(" because there is no callback for the subscription.");
       debugLog(" or because there is no session for the subscription (session terminated ?).");
    }
};
exports.ClientSidePublishEngine = ClientSidePublishEngine;<commit_msg>#40 set timeoutHint to non zero value to cope with servers that wrongly assume that  timeoutHint =0 is 0s ( instead of 'no timeout' as per spec)<commit_after>require("requirish")._(module);

var subscription_service = require("lib/services/subscription_service");
var NotificationMessage = subscription_service.NotificationMessage;

var s = require("lib/datamodel/structures");
var StatusCodes = require("lib/datamodel/opcua_status_code").StatusCodes;
var assert = require("better-assert");

var _ = require("underscore");

var debugLog  = require("lib/misc/utils").make_debugLog(__filename);

/**
 * A client side implementation to deal with publish service.
 *
 * @class ClientSidePublishEngine
 *
 * @param session {ClientSession} - the client session
 * @param options {object} - the client session
 * @param options.keepalive_interval {number} - the keep alive interval ( default = 1000 ms)
 * @constructor
 *
 * The ClientSidePublishEngine encapsulates the mechanism to
 * deal with a OPCUA Server and constantly sending PublishRequest
 * The ClientSidePublishEngine also performs  notification acknowledgements.
 * Finally, ClientSidePublishEngine dispatch PublishResponse to the correct
 * Subscription id callback
 */
function ClientSidePublishEngine(session,options) {
        assert(session instanceof Object);

    options = options || {};
    options.keepalive_interval = options.keepalive_interval || 1000;

    this.session = session;
    this.keepalive_interval = options.keepalive_interval;

    this.subscriptionAcknowledgements = [];
    this.subscriptionIdFuncMap = {};
}

/**
 * @method acknowledge_notification
 * @param subscriptionId {Number} the subscription id
 * @param sequenceNumber {Number} the sequence number
 */
ClientSidePublishEngine.prototype.acknowledge_notification = function (subscriptionId, sequenceNumber) {
    this.subscriptionAcknowledgements.push({
        subscriptionId: subscriptionId,
        sequenceNumber: sequenceNumber
    });
};

/**
 * @method send_publish_request
 */
ClientSidePublishEngine.prototype.send_publish_request = function () {
    var self = this;
    setImmediate(function(){
        if (!self.session) {
            // session has been terminated
            return ;
        }
        self._send_publish_request();
    });
};

ClientSidePublishEngine.prototype._send_publish_request = function () {

    debugLog("sending publish request");
    var self = this;
    assert( self.session , "ClientSidePublishEngine terminated ?");

    var subscriptionAcknowledgements = self.subscriptionAcknowledgements;
    self.subscriptionAcknowledgements = [];

    // as started in the spec (Spec 1.02 part 4 page 81 5.13.2.2 Function DequeuePublishReq())
    // the server will dequeue the PublishRequest  in first-in first-out order
    // and will validate if the publish request is still valid by checking the timeoutHint in the RequestHeader.
    // If the request timed out, the server will send a Bad_Timeout service result for the request and de-queue
    // another publish request.
    //
    // in Part 4. page 144 Request Header the timeoutHint is described this way.
    // timeoutHint UInt32 This timeout in milliseconds is used in the Client side Communication Stack to
    //                    set the timeout on a per-call base.
    //                    For a Server this timeout is only a hint and can be used to cancel long running
    //                    operations to free resources. If the Server detects a timeout, he can cancel the
    //                    operation by sending the Service result Bad_Timeout. The Server should wait
    //                    at minimum the timeout after he received the request before cancelling the operation.
    //                    The value of 0 indicates no timeout.
    // In issue#40 (MonitoredItem on changed not fired), we have found that some server migh wrongly interpret
    // the timeoutHint of the request header ( and will bang a Bad_Timeout regardless if client send timeoutHint=0)
    // as a work around here , we force the timeoutHint to be 1 second

    var publish_request = new subscription_service.PublishRequest({
        requestHeader: { timeoutHint: 1000 }, // see note
        subscriptionAcknowledgements: subscriptionAcknowledgements
    });
    self.session.publish(publish_request, function (err, response) {
        if (err) {
            //xx console.log("xxxxxxxxxxxxxxxxxxx ClientSidePublishEngine.prototype._send_publish_request ".red ,err.message.yellow);
        } else {
            self._receive_publish_response(response);
        }
    });
};

ClientSidePublishEngine.prototype.terminate = function() {
    this.session = null;
};

/**
 * the number of active subscriptions managed by this publish engine.
 * @property subscriptionCount
 * @type {Number}
 */
ClientSidePublishEngine.prototype.__defineGetter__("subscriptionCount",function() {
    var self = this;
    return Object.keys(self.subscriptionIdFuncMap).length;
});

/**
 * @method registerSubscriptionCallback
 *
 * @param subscriptionId
 * @param {Function} callback
 */
ClientSidePublishEngine.prototype.registerSubscriptionCallback = function(subscriptionId,callback) {
    var self = this;
    assert(!self.subscriptionIdFuncMap.hasOwnProperty(subscriptionId)); // already registered ?
    self.subscriptionIdFuncMap[subscriptionId] = callback;

    // Spec 1.02 part 4 5.13.5 Publish
    // [..] in high latency networks, the Client may wish to pipeline Publish requests
    // to ensure cyclic reporting from the Server. Pipelining involves sending more than one Publish
    // request for each Subscription before receiving a response. For example, if the network introduces a
    // delay between the Client and the Server of 5 seconds and the publishing interval for a Subscription
    // is one second, then the Client will have to issue Publish requests every second instead of waiting for
    // a response to be received before sending the next request.
    self.send_publish_request();
    self.send_publish_request();
    self.send_publish_request();
    self.send_publish_request();
    self.send_publish_request();
};
/**
 * @method unregisterSubscriptionCallback
 *
 * @param subscriptionId
 */
ClientSidePublishEngine.prototype.unregisterSubscriptionCallback = function(subscriptionId) {
    var self = this;
    if (subscriptionId==="pending") {
        console.log("special subscriptionId here");
    }
    assert(self.subscriptionIdFuncMap.hasOwnProperty(subscriptionId));
    delete self.subscriptionIdFuncMap[subscriptionId];
};

ClientSidePublishEngine.prototype._receive_publish_response= function(response){

    debugLog("receive publish response");
    var self = this;

    // the id of the subscription sending the notification message
    var subscriptionId = response.subscriptionId;

    // the sequence numbers available in this subscription
    // for retransmission and not acknowledged by the client
    var available_seq = response.availableSequenceNumbers;

    // has the server more notification for us ?
    var moreNotifications = response.moreNotifications;

    var notificationMessage = response.notificationMessage;
    //  notificationMessage.sequenceNumber
    //  notificationMessage.publishTime
    //  notificationMessage.notificationData[]
    if (notificationMessage.notificationData.length === 0) {
        // this is a keep-alive notification
        // in this case , we shall not acknowledge notificationMessage.sequenceNumber
        // which is only an information of what will be the future sequenceNumber.
    } else {
        self.acknowledge_notification(subscriptionId, notificationMessage.sequenceNumber);
    }

    var callback_for_subscription =  self.subscriptionIdFuncMap[subscriptionId];
    if (callback_for_subscription && ! (self.session === null) ) {
        // feed the server with a new publish Request to the server
        self.send_publish_request();

        // delegate notificationData to the subscription callback
        callback_for_subscription(notificationMessage.notificationData,notificationMessage.publishTime);
    } else {
       debugLog(" ignoring notificationMessage",notificationMessage," for subscription", subscriptionId);
       debugLog(" because there is no callback for the subscription.");
       debugLog(" or because there is no session for the subscription (session terminated ?).");
    }
};
exports.ClientSidePublishEngine = ClientSidePublishEngine;<|endoftext|>


<commit_before>var education = {
  "name": "Education",
  "entries": [{
    "business": "Massachusetts Institute of Technology",
    "position": "Junior",
    "startYear": 2012,
    "endYear": null,
    "features": [
      "Candidate for Bachelor of Science in Computer Science"
    ],
    "importance": 10
  }],
  "entryTemplate": genericEntryTemplate,
  "sectionTemplate": genericSectionTemplate
};

var experience = {
  "name": "Experience",
  "entries": [
  {
    "business": "MIT Media Lab",
    "moreLink": "http://fluid.media.mit.edu/node/227",
    "position": "Fluid Media Group Researcher",
    "startYear": 2012,
    "endYear": null,
    "features": [
      "Creating systems for controlling arbitrary household devices",
      "Designing meaningful multimodal interactions between connected objects",
      //"Implementing low-latency network connections using AllJoyn and\
      //  OpenSoundControl"
      "Optimizing low-latency data transfer over HTTP"
    ],
    "importance": 10
  },
  {
    "business": "Mozilla",
    "moreLink": "https://www.mozilla.org/",
    "position": "Intern",
    "startYear": 2014,
    "endYear": 2014,
    "features": [
      "Worked on the Firefox OS Systems Front End team",
      "Produced several major features and improvements integrated with gargantuan codebase",
      "Ensured full test coverage and production-grade reliability of work"
    ],
    "importance": 9
  },
  {
    "business": "iNetDispatch, LLC.",
    "moreLink": "http://www.inetdispatch.com/",
    "position": "Intern",
    "startYear": 2011,
    "endYear": 2012,
    "features": [
      "Developed a real-time chat system for dispatchers and units in the field",
      "Implemented global drag and drop for user interactions",
      "Created a playback feature to show the previous locations of units",
    ],
    "importance": 3
  },
  {
    "business": "Dryden Flight Research Center",
    "position": "Data Acquisition Engineer Intern",
    "startYear": 2011,
    "endYear": 2011,
    "features": [
      "Wrote utilities for the analysis of flight data with an\
       emphasis on real-time visualization",
      "Coordinated flight test with group, designing procedures to\
       guarantee safety and repeatability",
      "Shared findings and practices in a technical presentation"
    ],
    "importance": 6
  },
  {
    "business": "Infinite Heart",
    "position": "Interaction Designer",
    "startYear": 2011,
    "endYear": 2011,
    "features": [
      "Designed a series of 3D interactive applets for use on the Infinite "+
       "Heart website and in Infinite heart seminars",
      "Synthesized several novel visualization and interaction methods"
    ],
    "importance": 1
  },
  {
    "business": "Phoenix Country Day School",
    "moreLink": "http://community.pcds.org/netcommunity/page.aspx?pid=1228",
    "position": "SMALLab Teaching Assistant",
    "startYear": 2011,
    "endYear": 2012,
    "features": [
      "Planned and taught a series of introductory programming lectures",
      "Advised and assisted students in the creation of digital "+
       "interactive art and educational games",
      "Created a SMALLab-tailored gesture recognition engine to facilitate "+
       "students’ use of non-standard interaction designs"
    ],
    "importance": 1
  }],
  "entryTemplate": genericEntryTemplate,
  "sectionTemplate": genericSectionTemplate
};

var projects = {
  "name": "Projects",
  "entries": [
  //{
  //  "name": "Human + Computer",
  //  "startYear": 2014,
  //  "endYear": 2014,
  //  "features": [
  //    "Designed and implemented an interactive art project exploring transhumanism",
  //  ]
  //},
  {
    "name": "Tansley Effect",
    "startYear": 2014,
    "endYear": null,
    "features": [
      "Cofounded a startup to optimize data sharing within academic research groups",
      "Interviewed potential customers to refine and improve business strategy and design",
      "Prepared a website on a compressed timescale for a demo day presentation"
    ]
  },
  {
    "name": "Firefox Development",
    "startYear": 2011,
    "endYear": null,
    "features": [
      "Fixed bugs #659576 and #570760",
      "Currently creating a global keyboard shortcuts popup (bug #492557)",
    ],
    "importance": 1
  },
  {
    "name": "Random Hall Laundry Server",
    "moreLink": "http://laundry.mit.edu/",
    "startYear": 2013,
    "endYear": null,
    "features": [
      "Developed a joint web application and hardware installation",
      "Hardware uses off-the-shelf components to determine the power-on state of any machine",
      "Web application interfaces with the hardware to display machine usage in real-time"
    ]
  },
  {
    "name": "Student Groups",
    "startYear": 2013,
    "endYear": null,
    "features": [
      "President of the Laboratory for Chocolate Science"
      // "Secretary of the MIT Science Fiction Society"
      // "Computer Chair, Laundry King, and Webmaster for Random Hall"
    ]
  }
  ],
  "entryTemplate": projectsEntryTemplate,
  "sectionTemplate": genericSectionTemplate
};

var resume = {
  "name": "James Hobin",
  "email": "hobinjk@mit.edu",
  "address": "282 Massachusetts Ave. Cambridge, MA 02139",
  "phone": "(623) 297–8310",
  "username": "hobinjk",
  "sections": [
    education,
    experience,
    projects
  ]
};
<commit_msg>Update resume for 2015<commit_after>var education = {
  "name": "Education",
  "entries": [{
    "business": "Massachusetts Institute of Technology",
    "position": "Senior",
    "startYear": 2012,
    "endYear": null,
    "features": [
      "Candidate for Bachelor of Science in Computer Science",
      "Accepted into EECS Masters of Engineering Program"
    ],
    "importance": 10
  }],
  "entryTemplate": genericEntryTemplate,
  "sectionTemplate": genericSectionTemplate
};

var experience = {
  "name": "Experience",
  "entries": [
  {
    "business": "CSAIL Distributed Robotics Laboratory",
    "moreLink": "http://groups.csail.mit.edu/drl/wiki/index.php?title=Main_Page",
    "position": "Undergraduate Researcher",
    "startYear": 2015,
    "endYear": null,
    "features": [
      "Devising an online game for crowdsourcing the design of robots",
      "Collaborating to integrate with DRL's state-of-the-art simulation and fabrication technology"
    ],
    "importance": 10
  },
  {
    "business": "Mozilla",
    "moreLink": "https://www.mozilla.org/",
    "position": "Intern",
    "startYear": 2014,
    "endYear": 2015,
    "features": [
      "Worked for two summers on the Firefox OS Systems Front End team",
      "Produced several major features and improvements integrated with gargantuan codebase",
      "Ensured full test coverage and production-grade reliability of work",
      "Spearheaded research project to uncover a significant market opportunity for Firefox OS"
    ],
    "importance": 10
  },
  {
    "business": "MIT Media Lab",
    "moreLink": "http://fluid.media.mit.edu/node/227",
    "position": "Fluid Media Group Researcher",
    "startYear": 2012,
    "endYear": 2014,
    "features": [
      "Created systems for controlling arbitrary household devices",
      "Designed meaningful multimodal interactions between connected objects",
      //"Implementing low-latency network connections using AllJoyn and\
      //  OpenSoundControl"
      "Optimized low-latency data transfer over HTTP"
    ],
    "importance": 8
  },
  {
    "business": "iNetDispatch, LLC.",
    "moreLink": "http://www.inetdispatch.com/",
    "position": "Intern",
    "startYear": 2011,
    "endYear": 2012,
    "features": [
      "Developed a real-time chat system for dispatchers and units in the field",
      "Implemented global drag and drop for user interactions",
      "Created a playback feature to show the previous locations of units",
    ],
    "importance": 3
  },
  {
    "business": "Dryden Flight Research Center",
    "position": "Data Acquisition Engineer Intern",
    "startYear": 2011,
    "endYear": 2011,
    "features": [
      "Wrote utilities for the analysis of flight data with an\
       emphasis on real-time visualization",
      "Coordinated flight test with group, designing procedures to\
       guarantee safety and repeatability",
      "Shared findings and practices in a technical presentation"
    ],
    "importance": 6
  },
  {
    "business": "Infinite Heart",
    "position": "Interaction Designer",
    "startYear": 2011,
    "endYear": 2011,
    "features": [
      "Designed a series of 3D interactive applets for use on the Infinite "+
       "Heart website and in Infinite heart seminars",
      "Synthesized several novel visualization and interaction methods"
    ],
    "importance": 1
  },
  {
    "business": "Phoenix Country Day School",
    "moreLink": "http://community.pcds.org/netcommunity/page.aspx?pid=1228",
    "position": "SMALLab Teaching Assistant",
    "startYear": 2011,
    "endYear": 2012,
    "features": [
      "Planned and taught a series of introductory programming lectures",
      "Advised and assisted students in the creation of digital "+
       "interactive art and educational games",
      "Created a SMALLab-tailored gesture recognition engine to facilitate "+
       "students’ use of non-standard interaction designs"
    ],
    "importance": 1
  }],
  "entryTemplate": genericEntryTemplate,
  "sectionTemplate": genericSectionTemplate
};

var projects = {
  "name": "Projects",
  "entries": [
  {
    "name": "Human + Computer",
    "startYear": 2014,
    "endYear": 2014,
    "features": [
      "Designed and implemented an interactive art project exploring transhumanism",
    ]
  },
  {
    "name": "Tansley Effect",
    "startYear": 2014,
    "endYear": null,
    "features": [
      "Cofounded a startup to optimize data sharing within academic research groups",
      "Interviewed potential customers to refine and improve business strategy and design",
      "Prepared a website on a compressed timescale for a demo day presentation"
    ]
  },
  {
    "name": "Firefox Development",
    "startYear": 2011,
    "endYear": null,
    "features": [
      "Fixed bugs #659576 and #570760",
      "Currently creating a global keyboard shortcuts popup (bug #492557)",
    ],
    "importance": 1
  },
  {
    "name": "Random Hall Laundry Server",
    "moreLink": "http://laundry.mit.edu/",
    "startYear": 2013,
    "endYear": null,
    "features": [
      "Developed a joint web application and hardware installation",
      "Hardware uses off-the-shelf components to determine the power-on state of any machine",
      "Web application interfaces with the hardware to display machine usage in real-time"
    ]
  },
  {
    "name": "Student Groups",
    "startYear": 2013,
    "endYear": null,
    "features": [
      "President of the Laboratory for Chocolate Science",
      "Judiciary Committee Chair of Random Hall"
      // "Secretary of the MIT Science Fiction Society"
      // "Computer Chair, Laundry King, and Webmaster for Random Hall"
    ]
  }
  ],
  "entryTemplate": projectsEntryTemplate,
  "sectionTemplate": genericSectionTemplate
};

var resume = {
  "name": "James Hobin",
  "email": "hobinjk@mit.edu",
  "address": "MIT, Cambridge, MA 02139",
  "phone": "(623) 297–8310",
  "username": "hobinjk",
  "sections": [
    education,
    experience,
    projects
  ]
};
<|endoftext|>


<commit_before>$(document).ready(function() {
  var index = 0;
  var commInd = 0;
  var commands = [];

  var themes = {"normal": {"text": "#FFFFFF", "bg": "#000000"}, "ubuntu": {"text": "#DADADA", "bg": "#300A24"}, "white": {"text": "#000000", "bg": "#FFFFFF"}};

  function parseCmd(cmd) {
    if (cmd.split(" ")[0] === "sudo") {

      if (cmd.split(" ")[1] !== undefined) {
        if (cmd.split(" ")[1] === "make") {
          if (cmd.split(" ")[2] === "me" && cmd.split(" ")[3] === "a" && cmd.split(" ")[4] === "sandwich") {
            $(".input").after("
Okay.");
          } else if (cmd.split(" ")[2] === "me" && cmd.split(" ")[3] === "a" && cmd.split(" ")[4] === "cake") {
            $(".input").after("
The cake is still a lie.");
          } else {
            $(".input").after("
sudo: make it yourself");
          }
        }

        else if (cmd.split(" ")[1] === "ls" || cmd.split(" ")[1] === "cd") {
          $(".input").after("
Listen, sudo is not going to help you here. There is still no files.")
        }

        else if (cmd.split(" ")[1] === "help") {
          $(".input").after("
List of useful commands:
make    Makes something, I guess.")
        }

        else {
          $(".input").after("
sudo: "+cmd.split(" ")[1]+": command not found");
        }
      }

      else {
        $(".input").after("
sudo: no command specified");
      }
    } else {

      if (cmd.split(" ")[0] === "make") {
        if (cmd.split(" ")[1] === "me" && cmd.split(" ")[2] === "a" && cmd.split(" ")[3] === "sandwich") {
          $(".input").after("
What? Make it yourself.");
        } else if (cmd.split(" ")[1] === "me" && cmd.split(" ")[2] === "a" && cmd.split(" ")[3] === "cake") {
          $(".input").after("
The cake is a lie.");
        } else {
          $(".input").after("
make: make it yourself");
        }
      }

      else if (cmd.split(" ")[0] === "help") {
        $(".input").after("
List of useful commands:
load    Loads something. Type \"load --help\" for help on loading.
make    Makes something, I guess.
sudo    Administrator privileges (for everyone). Type \"sudo help\" for help with sudo commands.
setbg   Followed by a hex code. Sets the terminal background colour.
settext Followed by a hex code. Sets the terminal text colour.
theme   Followed by a theme name. Sets the terminal colours according to chosen theme. Type \"theme --help\" for a list of themes.");
      }

      else if (cmd.split(" ")[0] === "load") {
        var loadThis = cmd.split(" ")[1];
        if (loadThis === "--help") {
          $(".input").after("
cats    Loads kittens. :3
about   Loads some stuff about me.");
        } else if (loadThis === "cats") {
          $(".input").after("
Loading cats...");
          setTimeout(function() {
            window.location.href = "https://snowstormer.github.io/cats.html";
          }, 500)
        } else if (loadThis === "about") {
          $(".input").after("
Hi. My name is Snowstormer. I live in Estonia. Meow.");
        } else {
          $(".input").after("
Cannot load this. Please execute \"load --help\" for help on loading.")
        }
      }

      else if (cmd.split(" ")[0] === "ls" || cmd.split(" ")[0] === "cd") {
        $(".input").after("
I know, I know. This all is really interesting, but it's not a real terminal. There is no files here.")
      }

      else if (cmd.split(" ")[0] === "setbg") {
        if (/(^#[0-9A-Fa-f]{6}$)|(^#[0-9A-Fa-f]{3}$)/i.test(cmd.split(" ")[1])) {
          $("body").css("background", cmd.split(" ")[1]);
        } else {
          $(".input").after("
Invalid hex code.");
        }
      }

      else if (cmd.split(" ")[0] === "settext") {
        if (/(^#[0-9A-Fa-f]{6}$)|(^#[0-9A-Fa-f]{3}$)/i.test(cmd.split(" ")[1])) {
          $("body").css("color", cmd.split(" ")[1]);
        } else {
          $(".input").after("
Invalid hex code.");
        }
      }

      else if (cmd.split(" ")[0] === "theme") {
        if (cmd.split(" ")[1] !== "--help") {
          if (cmd.split(" ")[1] in themes) {
            $("body").css("color", themes[cmd.split(" ")[1]]["text"]);
            $("body").css("background", themes[cmd.split(" ")[1]]["bg"]);
          } else {
            $(".input").after("
No such theme exists, please type \"theme --help\" to get a list of themes.");
          }
        } else {
          $(".input").after("
List of available themes:
normal  The normal terminal look (white on black).
white   Inverted terminal (black on white).
ubuntu  Ubuntu terminal style (light grey on purple).");
        }
      }

      else {
        $(".input").after("
"+cmd.split(" ")[0]+": command not found");
      }
    }
  }

  function enterPressed() {
    if ($(".input").val() !== "") {
      parseCmd($(".input").val());
      commands.push($(".input").val());
      $(".input").replaceWith($(".input").val());
      $(".inptext"+index).after("
> <input type=\"text\" class=\"input\" spellcheck=\"false\" />")
      index += 1;
      commInd = 0;
      $(".input").focus();
    }
  }

  $(".input").focus();

  $("html").click(function() {
    $(".input").focus();
  });

  $(document).keyup(function(e){
    if(e.keyCode == 13) {
      enterPressed();
    } else if (e.keyCode == 38) {
      var newCommands = commands.slice().reverse();
      if (newCommands[commInd] != undefined && commInd != -1) {
        $(".input").val(newCommands[commInd]);
        commInd += 1;
      } else {
        $(".input").val(newCommands[newCommands.length-1]);
      }
    } else if (e.keyCode == 40) {
      var newCommands = commands.slice().reverse();
      commInd -= 1;
      if (newCommands[commInd] != undefined && commInd != -1) {
        $(".input").val(newCommands[commInd]);
      } else {
        commInd = 0;
        $(".input").val("");
      }
    } else {
      commInd = 0;
    }
  });
});
<commit_msg>matrix terminal<commit_after>$(document).ready(function() {
  var index = 0;
  var commInd = 0;
  var commands = [];

  var themes = {"normal": {"text": "#FFFFFF", "bg": "#000000"}, "ubuntu": {"text": "#DADADA", "bg": "#300A24"}, "white": {"text": "#000000", "bg": "#FFFFFF"}, "matrix": {"text": "#0FFF00", "bg": "#000000"}};
  function parseCmd(cmd) {
    if (cmd.split(" ")[0] === "sudo") {

      if (cmd.split(" ")[1] !== undefined) {
        if (cmd.split(" ")[1] === "make") {
          if (cmd.split(" ")[2] === "me" && cmd.split(" ")[3] === "a" && cmd.split(" ")[4] === "sandwich") {
            $(".input").after("
Okay.");
          } else if (cmd.split(" ")[2] === "me" && cmd.split(" ")[3] === "a" && cmd.split(" ")[4] === "cake") {
            $(".input").after("
The cake is still a lie.");
          } else {
            $(".input").after("
sudo: make it yourself");
          }
        }

        else if (cmd.split(" ")[1] === "ls" || cmd.split(" ")[1] === "cd") {
          $(".input").after("
Listen, sudo is not going to help you here. There is still no files.")
        }

        else if (cmd.split(" ")[1] === "help") {
          $(".input").after("
List of useful commands:
make    Makes something, I guess.")
        }

        else {
          $(".input").after("
sudo: "+cmd.split(" ")[1]+": command not found");
        }
      }

      else {
        $(".input").after("
sudo: no command specified");
      }
    } else {

      if (cmd.split(" ")[0] === "make") {
        if (cmd.split(" ")[1] === "me" && cmd.split(" ")[2] === "a" && cmd.split(" ")[3] === "sandwich") {
          $(".input").after("
What? Make it yourself.");
        } else if (cmd.split(" ")[1] === "me" && cmd.split(" ")[2] === "a" && cmd.split(" ")[3] === "cake") {
          $(".input").after("
The cake is a lie.");
        } else {
          $(".input").after("
make: make it yourself");
        }
      }

      else if (cmd.split(" ")[0] === "help") {
        $(".input").after("
List of useful commands:
load    Loads something. Type \"load --help\" for help on loading.
make    Makes something, I guess.
sudo    Administrator privileges (for everyone). Type \"sudo help\" for help with sudo commands.
setbg   Followed by a hex code. Sets the terminal background colour.
settext Followed by a hex code. Sets the terminal text colour.
theme   Followed by a theme name. Sets the terminal colours according to chosen theme. Type \"theme --help\" for a list of themes.");
      }

      else if (cmd.split(" ")[0] === "load") {
        var loadThis = cmd.split(" ")[1];
        if (loadThis === "--help") {
          $(".input").after("
cats    Loads kittens. :3
about   Loads some stuff about me.");
        } else if (loadThis === "cats") {
          $(".input").after("
Loading cats...");
          setTimeout(function() {
            window.location.href = "https://snowstormer.github.io/cats.html";
          }, 500)
        } else if (loadThis === "about") {
          $(".input").after("
Hi. My name is Snowstormer. I live in Estonia. Meow.");
        } else {
          $(".input").after("
Cannot load this. Please execute \"load --help\" for help on loading.")
        }
      }

      else if (cmd.split(" ")[0] === "ls" || cmd.split(" ")[0] === "cd") {
        $(".input").after("
I know, I know. This all is really interesting, but it's not a real terminal. There is no files here.")
      }

      else if (cmd.split(" ")[0] === "setbg") {
        if (/(^#[0-9A-Fa-f]{6}$)|(^#[0-9A-Fa-f]{3}$)/i.test(cmd.split(" ")[1])) {
          $("body").css("background", cmd.split(" ")[1]);
        } else {
          $(".input").after("
Invalid hex code.");
        }
      }

      else if (cmd.split(" ")[0] === "settext") {
        if (/(^#[0-9A-Fa-f]{6}$)|(^#[0-9A-Fa-f]{3}$)/i.test(cmd.split(" ")[1])) {
          $("body").css("color", cmd.split(" ")[1]);
        } else {
          $(".input").after("
Invalid hex code.");
        }
      }

      else if (cmd.split(" ")[0] === "theme") {
        if (cmd.split(" ")[1] !== "--help") {
          if (cmd.split(" ")[1] in themes) {
            $("body").css("color", themes[cmd.split(" ")[1]]["text"]);
            $("body").css("background", themes[cmd.split(" ")[1]]["bg"]);
          } else {
            $(".input").after("
No such theme exists, please type \"theme --help\" to get a list of themes.");
          }
        } else {
          $(".input").after("
List of available themes:
normal  The normal terminal look (white on black).
white   Inverted terminal (black on white).
ubuntu  Ubuntu terminal style (light grey on purple).
matrix   Matrix terminal (light green on black).");
        }
      }

      else {
        $(".input").after("
"+cmd.split(" ")[0]+": command not found");
      }
    }
  }

  function enterPressed() {
    if ($(".input").val() !== "") {
      parseCmd($(".input").val());
      commands.push($(".input").val());
      $(".input").replaceWith($(".input").val());
      $(".inptext"+index).after("
> <input type=\"text\" class=\"input\" spellcheck=\"false\" />")
      index += 1;
      commInd = 0;
      $(".input").focus();
    }
  }

  $(".input").focus();

  $("html").click(function() {
    $(".input").focus();
  });

  $(document).keyup(function(e){
    if(e.keyCode == 13) {
      enterPressed();
    } else if (e.keyCode == 38) {
      var newCommands = commands.slice().reverse();
      if (newCommands[commInd] != undefined && commInd != -1) {
        $(".input").val(newCommands[commInd]);
        commInd += 1;
      } else {
        $(".input").val(newCommands[newCommands.length-1]);
      }
    } else if (e.keyCode == 40) {
      var newCommands = commands.slice().reverse();
      commInd -= 1;
      if (newCommands[commInd] != undefined && commInd != -1) {
        $(".input").val(newCommands[commInd]);
      } else {
        commInd = 0;
        $(".input").val("");
      }
    } else {
      commInd = 0;
    }
  });
});
<|endoftext|>


<commit_before>// @flow

import * as React from 'react';
import { Platform } from 'react-native';
import { BottomTabBar } from '@react-navigation/bottom-tabs';

import {
  type KeyboardState,
  withKeyboardState,
} from '../keyboard/keyboard-state';

type Props = {|
  ...React.ElementConfig<typeof BottomTabBar>,
  // withKeyboardState
  keyboardState: ?KeyboardState,
|};
function TabBar(props: Props) {
  const { keyboardState, ...rest } = props;
  if (
    Platform.OS === 'android' &&
    keyboardState &&
    keyboardState.keyboardShowing
  ) {
    return null;
  }
  return <BottomTabBar {...rest} />;
}

const WrappedTabBar = withKeyboardState(TabBar);
export default props => <WrappedTabBar {...props} />;
<commit_msg>[native] Animate TabBar for keyboard events on Android<commit_after>// @flow

import type { LayoutEvent } from '../types/react-native';

import * as React from 'react';
import { Platform, View, StyleSheet } from 'react-native';
import { BottomTabBar } from '@react-navigation/bottom-tabs';
import Animated, { Easing } from 'react-native-reanimated';

import { KeyboardContext } from '../keyboard/keyboard-state';

/* eslint-disable import/no-named-as-default-member */
const { Value, timing, interpolate } = Animated;
/* eslint-enable import/no-named-as-default-member */

type Props = React.ElementConfig<typeof BottomTabBar>;
function TabBar(props: Props) {
  const tabBarVisibleRef = new React.useRef();
  if (!tabBarVisibleRef.current) {
    tabBarVisibleRef.current = new Value(1);
  }
  const tabBarVisible = tabBarVisibleRef.current;

  const keyboardState = React.useContext(KeyboardContext);
  const keyboardShowing = keyboardState && keyboardState.keyboardShowing;

  const animateTabBar = React.useCallback(
    toValue => timing(tabBarVisible, {
      toValue,
      duration: 200,
      easing: Easing.inOut(Easing.ease),
    }).start(),
    [tabBarVisible],
  );

  const keyboardWasShowingRef = React.useRef(false);
  React.useEffect(() => {
    const keyboardWasShowing = keyboardWasShowingRef.current;
    if (keyboardShowing && !keyboardWasShowing) {
      animateTabBar(0);
    } else if (!keyboardShowing && keyboardWasShowing) {
      animateTabBar(1);
    }
    keyboardWasShowingRef.current = keyboardShowing;
  }, [keyboardShowing, animateTabBar]);

  const [tabBarHeight, setTabBarHeight] = React.useState(0);
  const handleLayout = React.useCallback((e: LayoutEvent) => {
    const { height } = e.nativeEvent.layout;
    if (height) {
      setTabBarHeight(height);
    }
  }, [setTabBarHeight]);

  const containerHeight = React.useMemo(() => interpolate(tabBarVisible, {
    inputRange: [0, 1],
    outputRange: [0, tabBarHeight],
  }), [tabBarVisible, tabBarHeight]);
  const containerStyle = React.useMemo(() => ({
    height: tabBarHeight ? containerHeight : undefined,
    ...styles.container,
  }), [tabBarHeight, containerHeight]);

  if (Platform.OS !== 'android') {
    return (
      <View onLayout={handleLayout} style={styles.container}>
        <BottomTabBar {...props} />
      </View>
    );
  }

  return (
    <Animated.View style={containerStyle}>
      <View onLayout={handleLayout} style={styles.tabBar}>
        <BottomTabBar {...props} />
      </View>
    </Animated.View>
  );
}

const styles = StyleSheet.create({
  container: {
    left: 0,
    right: 0,
    bottom: 0,
  },
  tabBar: {
    position: 'absolute',
    left: 0,
    right: 0,
    top: 0,
  },
});

export default props => <TabBar {...props} />;
<|endoftext|>


<commit_before>import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import Stats from 'stats.js';
import { GUI } from 'dat.gui';
import { computeBoundsTree, MeshBVHVisualizer, getBVHExtremes, StaticGeometryGenerator } from '..';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;

const params = {
	display: true,
	displayOriginal: true,
	material: 'wireframe',
	updatePositionOnly: false,

	skeletonHelper: false,
	bvhHelper: true,
	bvhHelperDepth: 10,

	autoUpdate: true,
	updateRate: 2.5,
	pause: false,
	regenerate: () => {

		regenerateMesh();

	}
};

let renderer, camera, scene, clock, gui, stats;
let outputContainer;
let controls, mixer, animationAction, model;
let bvhHelper, skeletonHelper, meshHelper, staticGeometryGenerator;
let timeSinceUpdate = 0;
let initialExtremes = null;
let wireframeMaterial, normalMaterials, originalMaterials;

init();
render();

// TODO: afford use of materials on the final model to validate

function init() {

	const bgColor = 0x111111;

	outputContainer = document.getElementById( 'output' );

	// renderer setup
	renderer = new THREE.WebGLRenderer( { antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.setClearColor( bgColor, 1 );
	renderer.shadowMap.enabled = true;
	renderer.shadowMap.type = THREE.PCFSoftShadowMap;
	renderer.outputEncoding = THREE.sRGBEncoding;
	document.body.appendChild( renderer.domElement );

	// scene setup
	scene = new THREE.Scene();

	const light = new THREE.DirectionalLight( 0xffffff, 1 );
	light.position.set( 5, 5, 2.5 );
	light.shadow.mapSize.setScalar( 1024 );
	light.shadow.normalBias = 1e-2;
	light.castShadow = true;

	const shadowCam = light.shadow.camera;
	shadowCam.left = shadowCam.bottom = - 7.5;
	shadowCam.right = shadowCam.top = 7.5;
	shadowCam.updateProjectionMatrix();
	scene.add( light );
	scene.add( new THREE.AmbientLight( 0xb0bec5, 0.8 ) );

	// camera setup
	camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 );
	camera.position.set( 10, 0, 0 );
	camera.far = 100;
	camera.updateProjectionMatrix();

	controls = new OrbitControls( camera, renderer.domElement );

	clock = new THREE.Clock();

	// stats setup
	stats = new Stats();
	document.body.appendChild( stats.dom );

	// load the model
	new GLTFLoader().load( 'https://raw.githubusercontent.com/gkjohnson/gltf-demo-models/main/trex/scene.gltf', gltf => {

		// prep the model and add it to the scene
		model = gltf.scene;
		const meshes = [];
		model.traverse( object => {

			if ( object.isMesh ) {

				object.castShadow = true;
				object.receiveShadow = true;
				object.frustumCulled = false;
				meshes.push( object );

			}

		} );

		model.updateMatrixWorld( true );
		scene.add( model );

		// skeleton helper
		skeletonHelper = new THREE.SkeletonHelper( model );
		skeletonHelper.visible = false;
		scene.add( skeletonHelper );

		// animations
		const animations = gltf.animations;
		mixer = new THREE.AnimationMixer( model );

		animationAction = mixer.clipAction( animations[ 0 ] );
		animationAction.play();
		animationAction.paused = params.pause;

		// camera setup
		const box = new THREE.Box3();
		box.setFromObject( model );
		box.getCenter( controls.target );
		box.getCenter( camera.position );
		camera.position.x = 7.5;
		camera.position.z = 3.5;
		controls.update();

		// prep the geometry
		staticGeometryGenerator = new StaticGeometryGenerator( model );
		originalMaterials = staticGeometryGenerator.getMaterials();

		normalMaterials = originalMaterials.map( m => {

			return new THREE.MeshNormalMaterial( {
				normalMap: m.normalMap
			} );

		} );

		wireframeMaterial = new THREE.MeshBasicMaterial( {
			wireframe: true,
			transparent: true,
			opacity: 0.05,
			depthWrite: false,
		} );
		meshHelper = new THREE.Mesh( new THREE.BufferGeometry(), wireframeMaterial );
		meshHelper.receiveShadow = true;

		scene.add( meshHelper );

		bvhHelper = new MeshBVHVisualizer( meshHelper, 10 );
		scene.add( bvhHelper );

		regenerateMesh();

	} );

	const plane = new THREE.Mesh( new THREE.PlaneBufferGeometry(), new THREE.ShadowMaterial( { color: 0xffffff, opacity: 0.025, transparent: true } ) );
	plane.rotation.x = - Math.PI / 2;
	plane.receiveShadow = true;
	plane.scale.setScalar( 50 );
	scene.add( plane );

	gui = new GUI();
	const staticFolder = gui.addFolder( 'static mesh' );
	staticFolder.add( params, 'display' );
	staticFolder.add( params, 'displayOriginal' );
	staticFolder.add( params, 'material', [ 'wireframe', 'normal', 'original' ] ).onChange( v => {

		if ( ! meshHelper ) {

			return;

		}

		switch ( v ) {

			case 'wireframe':
				meshHelper.material = wireframeMaterial;
				meshHelper.castShadow = false;
				break;
			case 'normal':
				meshHelper.material = normalMaterials;
				meshHelper.castShadow = true;
				break;
			case 'original':
				meshHelper.material = originalMaterials;
				meshHelper.castShadow = true;
				break;

		}

	} );
	staticFolder.add( params, 'updatePositionOnly' ).onChange( v => {

		staticGeometryGenerator.attributes = v ? [ 'position' ] : [ 'position', 'normal', 'tangent', 'uv', 'uv2' ];

		// TODO: if we don't dispose and create a new geometry then it seems like the performance gets slower with the
		// original meshes??
		const geometry = meshHelper.geometry;
		geometry.dispose();
		for ( const key in geometry.attributes ) {

			geometry.deleteAttribute( key );

		}

	} );
	staticFolder.open();

	const helperFolder = gui.addFolder( 'helpers' );
	helperFolder.add( params, 'skeletonHelper' );
	helperFolder.add( params, 'bvhHelper' );
	helperFolder.add( params, 'bvhHelperDepth', 1, 20 ).onChange( v => {

		bvhHelper.depth = parseInt( v );
		bvhHelper.update();

	} );
	helperFolder.open();

	const bvhFolder = gui.addFolder( 'bvh animation' );
	bvhFolder.add( params, 'autoUpdate' );
	bvhFolder.add( params, 'updateRate', 0, 5, 0.001 );
	bvhFolder.add( params, 'pause' ).onChange( v => {

		if ( animationAction ) {

			animationAction.paused = v;

		}

	} );
	bvhFolder.add( params, 'regenerate' );
	bvhFolder.open();

	gui.open();

	window.addEventListener( 'resize', function () {

		camera.aspect = window.innerWidth / window.innerHeight;
		camera.updateProjectionMatrix();

		renderer.setSize( window.innerWidth, window.innerHeight );

	}, false );

}

// regenerate the mesh and bvh
function regenerateMesh() {

	if ( meshHelper ) {

		let generateTime, refitTime, startTime;

		// time the geometry generation
		startTime = window.performance.now();
		staticGeometryGenerator.generate( meshHelper.geometry );
		generateTime = window.performance.now() - startTime;

		// time the bvh refitting
		startTime = window.performance.now();
		if ( ! meshHelper.geometry.boundsTree ) {

			meshHelper.geometry.computeBoundsTree();
			refitTime = '-';

		} else {

			meshHelper.geometry.boundsTree.refit();
			refitTime = ( window.performance.now() - startTime ).toFixed( 2 );

		}

		bvhHelper.update();
		timeSinceUpdate = 0;

		const extremes = getBVHExtremes( meshHelper.geometry.boundsTree );
		if ( initialExtremes === null ) {

			initialExtremes = extremes;

		}

		let score = 0;
		let initialScore = 0;
		for ( const i in extremes ) {

			score += extremes[ i ].surfaceAreaScore;
			initialScore += initialExtremes[ i ].surfaceAreaScore;

		}

		const degradation = ( score / initialScore ) - 1.0;

		// update time display
		outputContainer.innerHTML =
			`mesh generation time: ${ generateTime.toFixed( 2 ) } ms\n` +
			`refit time: ${ refitTime } ms\n` +
			`bvh degradation: ${ ( 100 * degradation ).toFixed( 2 ) }%`;

		window.MESH = meshHelper;

	}

}

function render() {

	stats.update();
	requestAnimationFrame( render );

	const delta = Math.min( clock.getDelta(), 30 * 0.001 );

	// update animation and helpers
	if ( mixer ) {

		mixer.update( delta );
		skeletonHelper.visible = params.skeletonHelper;
		meshHelper.visible = params.display;
		bvhHelper.visible = params.bvhHelper;
		model.visible = params.displayOriginal;

	}

	scene.updateMatrixWorld( true );

	// refit on a cycle
	if ( params.autoUpdate && ! params.pause ) {

		if ( timeSinceUpdate > params.updateRate ) {

			regenerateMesh();

		}

		timeSinceUpdate += delta;

	} else {

		timeSinceUpdate = 0;

	}

	renderer.render( scene, camera );

}
<commit_msg>Update skinnedMesh.js<commit_after>import * as THREE from 'three';
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
import Stats from 'stats.js';
import { GUI } from 'dat.gui';
import { computeBoundsTree, MeshBVHVisualizer, getBVHExtremes, StaticGeometryGenerator, SAH } from '..';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';

THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;

const params = {
	display: true,
	displayOriginal: true,
	material: 'wireframe',
	updatePositionOnly: false,

	skeletonHelper: false,
	bvhHelper: true,
	bvhHelperDepth: 10,

	autoUpdate: true,
	updateRate: 2.5,
	pause: false,
	regenerate: () => {

		regenerateMesh();

	}
};

let renderer, camera, scene, clock, gui, stats;
let outputContainer;
let controls, mixer, animationAction, model;
let bvhHelper, skeletonHelper, meshHelper, staticGeometryGenerator;
let timeSinceUpdate = 0;
let initialExtremes = null;
let wireframeMaterial, normalMaterials, originalMaterials;

init();
render();

// TODO: afford use of materials on the final model to validate

function init() {

	const bgColor = 0x111111;

	outputContainer = document.getElementById( 'output' );

	// renderer setup
	renderer = new THREE.WebGLRenderer( { antialias: true } );
	renderer.setPixelRatio( window.devicePixelRatio );
	renderer.setSize( window.innerWidth, window.innerHeight );
	renderer.setClearColor( bgColor, 1 );
	renderer.shadowMap.enabled = true;
	renderer.shadowMap.type = THREE.PCFSoftShadowMap;
	renderer.outputEncoding = THREE.sRGBEncoding;
	document.body.appendChild( renderer.domElement );

	// scene setup
	scene = new THREE.Scene();

	const light = new THREE.DirectionalLight( 0xffffff, 1 );
	light.position.set( 5, 5, 2.5 );
	light.shadow.mapSize.setScalar( 1024 );
	light.shadow.normalBias = 1e-2;
	light.castShadow = true;

	const shadowCam = light.shadow.camera;
	shadowCam.left = shadowCam.bottom = - 7.5;
	shadowCam.right = shadowCam.top = 7.5;
	shadowCam.updateProjectionMatrix();
	scene.add( light );
	scene.add( new THREE.AmbientLight( 0xb0bec5, 0.8 ) );

	// camera setup
	camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 0.1, 50 );
	camera.position.set( 10, 0, 0 );
	camera.far = 100;
	camera.updateProjectionMatrix();

	controls = new OrbitControls( camera, renderer.domElement );

	clock = new THREE.Clock();

	// stats setup
	stats = new Stats();
	document.body.appendChild( stats.dom );

	// load the model
	new GLTFLoader().load( 'https://raw.githubusercontent.com/gkjohnson/gltf-demo-models/main/trex/scene.gltf', gltf => {

		// prep the model and add it to the scene
		model = gltf.scene;
		const meshes = [];
		model.traverse( object => {

			if ( object.isMesh ) {

				object.castShadow = true;
				object.receiveShadow = true;
				object.frustumCulled = false;
				meshes.push( object );

			}

		} );

		model.updateMatrixWorld( true );
		scene.add( model );

		// skeleton helper
		skeletonHelper = new THREE.SkeletonHelper( model );
		skeletonHelper.visible = false;
		scene.add( skeletonHelper );

		// animations
		const animations = gltf.animations;
		mixer = new THREE.AnimationMixer( model );

		animationAction = mixer.clipAction( animations[ 0 ] );
		animationAction.play();
		animationAction.paused = params.pause;

		// camera setup
		const box = new THREE.Box3();
		box.setFromObject( model );
		box.getCenter( controls.target );
		box.getCenter( camera.position );
		camera.position.x = 7.5;
		camera.position.z = 3.5;
		controls.update();

		// prep the geometry
		staticGeometryGenerator = new StaticGeometryGenerator( model );
		originalMaterials = staticGeometryGenerator.getMaterials();

		normalMaterials = originalMaterials.map( m => {

			return new THREE.MeshNormalMaterial( {
				normalMap: m.normalMap
			} );

		} );

		wireframeMaterial = new THREE.MeshBasicMaterial( {
			wireframe: true,
			transparent: true,
			opacity: 0.05,
			depthWrite: false,
		} );
		meshHelper = new THREE.Mesh( new THREE.BufferGeometry(), wireframeMaterial );
		meshHelper.receiveShadow = true;

		scene.add( meshHelper );

		bvhHelper = new MeshBVHVisualizer( meshHelper, 10 );
		scene.add( bvhHelper );

		regenerateMesh();

	} );

	const plane = new THREE.Mesh( new THREE.PlaneBufferGeometry(), new THREE.ShadowMaterial( { color: 0xffffff, opacity: 0.025, transparent: true } ) );
	plane.rotation.x = - Math.PI / 2;
	plane.receiveShadow = true;
	plane.scale.setScalar( 50 );
	scene.add( plane );

	gui = new GUI();
	const staticFolder = gui.addFolder( 'static mesh' );
	staticFolder.add( params, 'display' );
	staticFolder.add( params, 'displayOriginal' );
	staticFolder.add( params, 'material', [ 'wireframe', 'normal', 'original' ] ).onChange( v => {

		if ( ! meshHelper ) {

			return;

		}

		switch ( v ) {

			case 'wireframe':
				meshHelper.material = wireframeMaterial;
				meshHelper.castShadow = false;
				break;
			case 'normal':
				meshHelper.material = normalMaterials;
				meshHelper.castShadow = true;
				break;
			case 'original':
				meshHelper.material = originalMaterials;
				meshHelper.castShadow = true;
				break;

		}

	} );
	staticFolder.add( params, 'updatePositionOnly' ).onChange( v => {

		staticGeometryGenerator.attributes = v ? [ 'position' ] : [ 'position', 'normal', 'tangent', 'uv', 'uv2' ];

		// TODO: if we don't dispose and create a new geometry then it seems like the performance gets slower with the
		// original meshes??
		const geometry = meshHelper.geometry;
		geometry.dispose();
		for ( const key in geometry.attributes ) {

			geometry.deleteAttribute( key );

		}

	} );
	staticFolder.open();

	const helperFolder = gui.addFolder( 'helpers' );
	helperFolder.add( params, 'skeletonHelper' );
	helperFolder.add( params, 'bvhHelper' );
	helperFolder.add( params, 'bvhHelperDepth', 1, 20 ).onChange( v => {

		bvhHelper.depth = parseInt( v );
		bvhHelper.update();

	} );
	helperFolder.open();

	const bvhFolder = gui.addFolder( 'bvh animation' );
	bvhFolder.add( params, 'autoUpdate' );
	bvhFolder.add( params, 'updateRate', 0, 5, 0.001 );
	bvhFolder.add( params, 'pause' ).onChange( v => {

		if ( animationAction ) {

			animationAction.paused = v;

		}

	} );
	bvhFolder.add( params, 'regenerate' );
	bvhFolder.open();

	gui.open();

	window.addEventListener( 'resize', function () {

		camera.aspect = window.innerWidth / window.innerHeight;
		camera.updateProjectionMatrix();

		renderer.setSize( window.innerWidth, window.innerHeight );

	}, false );

}

// regenerate the mesh and bvh
function regenerateMesh() {

	if ( meshHelper ) {

		let generateTime, refitTime, startTime;

		// time the geometry generation
		startTime = window.performance.now();
		staticGeometryGenerator.generate( meshHelper.geometry );
		generateTime = window.performance.now() - startTime;

		// time the bvh refitting
		startTime = window.performance.now();
		if ( ! meshHelper.geometry.boundsTree ) {

			meshHelper.geometry.computeBoundsTree( { strategy: SAH } );
			refitTime = '-';

		} else {

			meshHelper.geometry.boundsTree.refit();
			refitTime = ( window.performance.now() - startTime ).toFixed( 2 );

		}

		bvhHelper.update();
		timeSinceUpdate = 0;

		const extremes = getBVHExtremes( meshHelper.geometry.boundsTree );
		if ( initialExtremes === null ) {

			initialExtremes = extremes;

		}

		let score = 0;
		let initialScore = 0;
		for ( const i in extremes ) {

			score += extremes[ i ].surfaceAreaScore;
			initialScore += initialExtremes[ i ].surfaceAreaScore;

		}

		const degradation = ( score / initialScore ) - 1.0;

		// update time display
		outputContainer.innerHTML =
			`mesh generation time: ${ generateTime.toFixed( 2 ) } ms\n` +
			`refit time: ${ refitTime } ms\n` +
			`bvh degradation: ${ ( 100 * degradation ).toFixed( 2 ) }%`;

		window.MESH = meshHelper;

	}

}

function render() {

	stats.update();
	requestAnimationFrame( render );

	const delta = Math.min( clock.getDelta(), 30 * 0.001 );

	// update animation and helpers
	if ( mixer ) {

		mixer.update( delta );
		skeletonHelper.visible = params.skeletonHelper;
		meshHelper.visible = params.display;
		bvhHelper.visible = params.bvhHelper;
		model.visible = params.displayOriginal;

	}

	scene.updateMatrixWorld( true );

	// refit on a cycle
	if ( params.autoUpdate && ! params.pause ) {

		if ( timeSinceUpdate > params.updateRate ) {

			regenerateMesh();

		}

		timeSinceUpdate += delta;

	} else {

		timeSinceUpdate = 0;

	}

	renderer.render( scene, camera );

}
<|endoftext|>


<commit_before>const voting = require('./voting')
const split  = require('pull-split')
const debug  = require('./debug').sub('data')
const stps   = require('stream-to-pull-stream')
const pull   = require('pull-stream')
const path   = require('path')
const util   = require('util')
const EE     = require('events').EventEmitter
const fs     = require('fs')

debug.movies = debug.sub('movies')
debug.people = debug.sub('people')

const data = new EE

const dataPath = path.join(__dirname, 'movies')

data.people = (function() {
	const people = []
	people.nextID = 0

	for(var k in EE.prototype) {
		people[k] = EE.prototype[k]
	}
	EE.call(people)

	const Person = (function() {
		function Person(name) {
			if(!(this instanceof Person)) return new Person(name)

			EE.call(this)

			this.id = people.nextID++
			people[this.id] = this

			this.name = name

			debug.people('new person: %s (%d)', this.name, this.id)

			this.update([])

			const self = this
			process.nextTick(function() { people.emit('new', self) })
		}
		util.inherits(Person, EE)

		;(function() {
			this.rename = function(name) {
				debug.people('renaming %s (%d) to %s', this.name, this.id, name)

				const oldName = this.name
				this.name = name
				this.emit('rename', name, oldName)
				people.emit('rename', this, name, oldName)

				return this
			}

			this.update = function(vote) {
				debug.people('updating %s (%d)\'s vote to: %j', this.name, this.id, vote)

				const oldVote = this.vote
				this.vote = vote
				this.emit('update', vote, oldVote)
				people.emit('update', this, vote, oldVote)

				data.voting.update(this)()

				return this
			}

			this.delete = function() {
				debug.people('deleting %s (%d)', this.name, this.id)

				people[this.id] = undefined
				this.vote = []

				data.voting.delete(this)()

				this.emit('delete')
				people.emit('delete', this)

				return this
			}
		}).call(Person.prototype)

		return Person
	})()

	people.reset = function() {
		people.splice(0, people.nextID)
		debug.people('reset')
	}

	people.new = function(name) {
		return new Person(name)
	}

	return people
})()

data.movies = (function() {
	const movies = []

	for(var k in EE.prototype) {
		movies[k] = EE.prototype[k]
	}
	EE.call(movies)

	movies.reset = function() {
		movies.splice(0, movies.length)
		debug.movies('reset')
	}

	movies.load = function() { return function(cb) {
		if(!cb) cb = function(err) { if(err) throw err }

		movies.reset()

		debug.movies('loading')

		pull(
			stps.source(fs.createReadStream(dataPath)),
			split(),
			pull.filter(function(line) {
				return line.length > 0
			}),
			pull.map(function(entry) {
				return {
					name: entry
				}
			}),
			pull.drain(function(movie) {
				movie.id = movies.length
				movies.push(movie)
			}, function() {
				debug.movies('loaded')
				movies.emit('loaded')
				cb(null, data)
			})
		)
	} }

	movies.save = function() { return function(cb) {
		if(!cb) cb = function(err) { if(err) throw err }

		debug.movies('saving')
		pull(
			pull.values(movies),
			pull.map(function(movie) {
				return movie.name + '\n'
			}),
			stps.sink(fs.createWriteStream(dataPath).on('end', function() {
				debug.movies('saved')
				cb(null)
			}).on('error', function(err) {
				debug.movies('error while saving', err)
				cb(err)
			}))
		)
	} }

	return movies
})()

data.voting = voting(data)

function par(thunks) {
	const results = []
	const cbs = []
	var done = 0
	var isDone = false

	thunks.forEach(function(thunk, i) {
		thunk(function(err, res) {
			done += 1
			if(!isDone && done >= thunks.length) {
				isDone = true
				process.nextTick(function() {
					cbs.forEach(function(cb) {
						if(err)
							cb(err)
						else
							cb(null, results)
					})
				})
			}
		})
	})

	return function(cb) {
		cbs.push(cb)
	}
}

data.load = function() { return function(cb) {
	if(!cb) cb = function(err) { if(err) throw err }

	data.people.reset()
	// data.movies.reset()

	par([
		data.movies.load(),
		data.voting.reset()
	])(function(err) {
		if(err) {
			debug('error', err)
			data.emit('error', err)
		} else {
			debug('loaded')
			data.emit('loaded')
		}
		cb(err)
	})
} }

data.save = function() {
	data.movies.save()
}

data.load()(function(err) {
	if(err)
		throw err
	else {
		data.save()

		// data.people.new('John Doe')
		// data.people.new('Jane Doe')
		// data.people.new('Fiz Buz' )

		data.people.new('Jeremy')
		data.people.new('Becky' )
		data.people.new('Drew'  )
		data.people.new('Dad'   )
		data.people.new('Mom'   )
	}
})

module.exports = data<commit_msg>Disable loading movies from disk I've been using this for testing so I don't have to add the movies every time I change something<commit_after>const voting = require('./voting')
const split  = require('pull-split')
const debug  = require('./debug').sub('data')
const stps   = require('stream-to-pull-stream')
const pull   = require('pull-stream')
const path   = require('path')
const util   = require('util')
const EE     = require('events').EventEmitter
const fs     = require('fs')

debug.movies = debug.sub('movies')
debug.people = debug.sub('people')

const data = new EE

const dataPath = path.join(__dirname, 'movies')

data.people = (function() {
	const people = []
	people.nextID = 0

	for(var k in EE.prototype) {
		people[k] = EE.prototype[k]
	}
	EE.call(people)

	const Person = (function() {
		function Person(name) {
			if(!(this instanceof Person)) return new Person(name)

			EE.call(this)

			this.id = people.nextID++
			people[this.id] = this

			this.name = name

			debug.people('new person: %s (%d)', this.name, this.id)

			this.update([])

			const self = this
			process.nextTick(function() { people.emit('new', self) })
		}
		util.inherits(Person, EE)

		;(function() {
			this.rename = function(name) {
				debug.people('renaming %s (%d) to %s', this.name, this.id, name)

				const oldName = this.name
				this.name = name
				this.emit('rename', name, oldName)
				people.emit('rename', this, name, oldName)

				return this
			}

			this.update = function(vote) {
				debug.people('updating %s (%d)\'s vote to: %j', this.name, this.id, vote)

				const oldVote = this.vote
				this.vote = vote
				this.emit('update', vote, oldVote)
				people.emit('update', this, vote, oldVote)

				data.voting.update(this)()

				return this
			}

			this.delete = function() {
				debug.people('deleting %s (%d)', this.name, this.id)

				people[this.id] = undefined
				this.vote = []

				data.voting.delete(this)()

				this.emit('delete')
				people.emit('delete', this)

				return this
			}
		}).call(Person.prototype)

		return Person
	})()

	people.reset = function() {
		people.splice(0, people.nextID)
		debug.people('reset')
	}

	people.new = function(name) {
		return new Person(name)
	}

	return people
})()

data.movies = (function() {
	const movies = []

	for(var k in EE.prototype) {
		movies[k] = EE.prototype[k]
	}
	EE.call(movies)

	movies.reset = function() {
		movies.splice(0, movies.length)
		debug.movies('reset')
	}

	movies.load = function() { return function(cb) {
		if(!cb) cb = function(err) { if(err) throw err }

		movies.reset()

		debug.movies('loading')

		pull(
			stps.source(fs.createReadStream(dataPath)),
			split(),
			pull.filter(function(line) {
				return line.length > 0
			}),
			pull.map(function(entry) {
				return {
					name: entry
				}
			}),
			pull.drain(function(movie) {
				movie.id = movies.length
				movies.push(movie)
			}, function() {
				debug.movies('loaded')
				movies.emit('loaded')
				cb(null, data)
			})
		)
	} }

	movies.save = function() { return function(cb) {
		if(!cb) cb = function(err) { if(err) throw err }

		debug.movies('saving')
		pull(
			pull.values(movies),
			pull.map(function(movie) {
				return movie.name + '\n'
			}),
			stps.sink(fs.createWriteStream(dataPath).on('end', function() {
				debug.movies('saved')
				cb(null)
			}).on('error', function(err) {
				debug.movies('error while saving', err)
				cb(err)
			}))
		)
	} }

	return movies
})()

data.voting = voting(data)

function par(thunks) {
	const results = []
	const cbs = []
	var done = 0
	var isDone = false

	thunks.forEach(function(thunk, i) {
		thunk(function(err, res) {
			done += 1
			if(!isDone && done >= thunks.length) {
				isDone = true
				process.nextTick(function() {
					cbs.forEach(function(cb) {
						if(err)
							cb(err)
						else
							cb(null, results)
					})
				})
			}
		})
	})

	return function(cb) {
		cbs.push(cb)
	}
}

data.load = function() { return function(cb) {
	if(!cb) cb = function(err) { if(err) throw err }

	data.people.reset()
	data.movies.reset()

	par([
		// data.movies.load(),
		data.voting.reset()
	])(function(err) {
		if(err) {
			debug('error', err)
			data.emit('error', err)
		} else {
			debug('loaded')
			data.emit('loaded')
		}
		cb(err)
	})
} }

data.save = function() {
	data.movies.save()
}

data.load()(function(err) {
	if(err)
		throw err
	else {
		data.save()

		// data.people.new('John Doe')
		// data.people.new('Jane Doe')
		// data.people.new('Fiz Buz' )

		data.people.new('Jeremy')
		data.people.new('Becky' )
		data.people.new('Drew'  )
		data.people.new('Dad'   )
		data.people.new('Mom'   )
	}
})

module.exports = data<|endoftext|>


<commit_before>var terminal = (function() {
    var history = (localStorage.getItem("history") ? localStorage.getItem("history").split(",") : []),
        historyIndex = history.length;
        self = {};

    var KEY_UP   = 38,
        KEY_DOWN = 40;

    // Auxiliary functions

    var resetPrompt = function(terminal, prompt) {
        var newPrompt = prompt.parentNode.cloneNode(true);
        prompt.setAttribute("contenteditable", false);
        terminal.appendChild(newPrompt);
        newPrompt.querySelector(".input").innerHTML = " ";
        newPrompt.querySelector(".input").focus();
    };

    var runCommand = function(terminal, cmd, args) {
        terminal.innerHTML += (self.commands[cmd](args));
    };

    var updateHistory = function(cmd) {
        history.push(cmd);
        localStorage.setItem("history", history);
        historyIndex = history.length;
    };

    var browseHistory = function(prompt, direction) {
        var changedPrompt = false;
        if(direction == KEY_UP && historyIndex > 0) {
            prompt.textContent = history[--historyIndex];
            changedPrompt = true;
        } else if(direction == KEY_DOWN) {
            if(historyIndex < history.length) ++historyIndex;
            if(historyIndex < history.length) prompt.textContent = history[historyIndex];
            else prompt.textContent = " ";
            changedPrompt = true;
        }

        if(changedPrompt) {
            var range = document.createRange();
            var sel = window.getSelection();
            range.setStart(prompt.childNodes[0], prompt.textContent.length);
            range.collapse(true);
            sel.removeAllRanges();
            sel.addRange(range);
        }
    };

    var autoCompleteInput = function(input) {
        var cmds        = self.commands,
            re          = new RegExp("^" + input, "ig"),
            suggestions = [];
        console.log("*" + input + "*", cmds);
        for(var cmd in cmds) {
            console.log(cmd);
            if(cmds.hasOwnProperty(cmd) && cmd.match(re)) {
                suggestions.push(cmd);
            }
        }
        return suggestions;
    };

    // Terminal functions

    self.init = function(elem, commands) {
        self.commands = commands;

        elem.addEventListener("keydown", function(event) {
            if(event.keyCode == 9) { //TAB was hit
                var prompt = event.target;
                var suggestions = autoCompleteInput(prompt.textContent.replace(/\s+/g, ""));

                if(suggestions.length == 1) {
                    prompt.textContent = suggestions[0];
                    var range = document.createRange();
                    var sel = window.getSelection();
                    range.setStart(prompt.childNodes[0], suggestions[0].length);
                    range.collapse(true);
                    sel.removeAllRanges();
                    sel.addRange(range);
                }

                event.preventDefault(true);
                return false;
            }
        });

        elem.addEventListener("keyup", function(event) {
            if(historyIndex < 0) return;
            browseHistory(event.target, event.keyCode);
        });

        elem.addEventListener("keypress", function(event) {
            var prompt = event.target;
            if(event.keyCode != 13) return false;

            updateHistory(prompt.textContent);

            var input = prompt.textContent.split(" ");
            if(input[0] && input[0] in commands) {
                runCommand(elem, input[0], input);
            }

            resetPrompt(elem, prompt);
            event.preventDefault();
        });

        elem.querySelector(".input").focus();
    };

    return self;
})();
<commit_msg>Got rid of the magic number for TAB<commit_after>var terminal = (function() {
    var history = (localStorage.getItem("history") ? localStorage.getItem("history").split(",") : []),
        historyIndex = history.length;
        self = {};

    var KEY_UP   = 38,
        KEY_DOWN = 40,
        KEY_TAB  = 9;

    // Auxiliary functions

    var resetPrompt = function(terminal, prompt) {
        var newPrompt = prompt.parentNode.cloneNode(true);
        prompt.setAttribute("contenteditable", false);
        terminal.appendChild(newPrompt);
        newPrompt.querySelector(".input").innerHTML = " ";
        newPrompt.querySelector(".input").focus();
    };

    var runCommand = function(terminal, cmd, args) {
        terminal.innerHTML += (self.commands[cmd](args));
    };

    var updateHistory = function(cmd) {
        history.push(cmd);
        localStorage.setItem("history", history);
        historyIndex = history.length;
    };

    var browseHistory = function(prompt, direction) {
        var changedPrompt = false;
        if(direction == KEY_UP && historyIndex > 0) {
            prompt.textContent = history[--historyIndex];
            changedPrompt = true;
        } else if(direction == KEY_DOWN) {
            if(historyIndex < history.length) ++historyIndex;
            if(historyIndex < history.length) prompt.textContent = history[historyIndex];
            else prompt.textContent = " ";
            changedPrompt = true;
        }

        if(changedPrompt) {
            var range = document.createRange();
            var sel = window.getSelection();
            range.setStart(prompt.childNodes[0], prompt.textContent.length);
            range.collapse(true);
            sel.removeAllRanges();
            sel.addRange(range);
        }
    };

    var autoCompleteInput = function(input) {
        var cmds        = self.commands,
            re          = new RegExp("^" + input, "ig"),
            suggestions = [];
        for(var cmd in cmds) {
            if(cmds.hasOwnProperty(cmd) && cmd.match(re)) {
                suggestions.push(cmd);
            }
        }
        return suggestions;
    };

    // Terminal functions

    self.init = function(elem, commands) {
        self.commands = commands;

        elem.addEventListener("keydown", function(event) {
            if(event.keyCode == KEY_TAB) {
                var prompt = event.target;
                var suggestions = autoCompleteInput(prompt.textContent.replace(/\s+/g, ""));

                if(suggestions.length == 1) {
                    prompt.textContent = suggestions[0];
                    var range = document.createRange();
                    var sel = window.getSelection();
                    range.setStart(prompt.childNodes[0], suggestions[0].length);
                    range.collapse(true);
                    sel.removeAllRanges();
                    sel.addRange(range);
                }

                event.preventDefault(true);
                return false;
            }
        });

        elem.addEventListener("keyup", function(event) {
            if(historyIndex < 0) return;
            browseHistory(event.target, event.keyCode);
        });

        elem.addEventListener("keypress", function(event) {
            var prompt = event.target;
            if(event.keyCode != 13) return false;

            updateHistory(prompt.textContent);

            var input = prompt.textContent.split(" ");
            if(input[0] && input[0] in commands) {
                runCommand(elem, input[0], input);
            }

            resetPrompt(elem, prompt);
            event.preventDefault();
        });

        elem.querySelector(".input").focus();
    };

    return self;
})();
<|endoftext|>


<commit_before>var npcs = {
  npc1 : [49.8623, 23.9172],
  npc2 : [49.8633, 23.9182],
  npc3 : [49.8613, 23.9162]
};

var targets = {
  'code1' : [49.8622, 23.9171],
  'code2' : [49.8624, 23.9173],
  'code3' : [49.8630, 23.9171],
  'code4' : [49.8621, 23.9174]
};

exports.npcs = npcs;
exports.targets = targets;<commit_msg>real data<commit_after>var npcs = {
  npc1 : [49.862155, 23.916207],
  npc2 : [49.864416, 23.921962],
  npc3 : [49.860313, 23.925817]
};

var targets = {
  '2b2' : [49.864980, 23.916439],
  '6i4' : [49.863714, 23.919054],
  '8f9' : [49.863142, 23.918019],

  '13b9' : [49.861783, 23.916379],
  '16d9' : [49.860947, 23.917136],
  '18a5' : [49.860401, 23.916113],
  '16f9' : [49.860974, 23.917971],
  '2k1' : [49.864875, 23.920000],
  '3o5' : [49.864416, 23.921962],

  '5n7' : [49.863911, 23.921213],
  '14k3' : [49.861599, 23.920163],
  '17m4' : [49.860762, 23.921164],
  '18o9' : [49.860472, 23.921783],
  '11v4' : [49.862068, 23.924883],
  '20t7' : [49.859834, 23.923664],
  '18u8' : [49.860437, 23.924135]
};

exports.npcs = npcs;
exports.targets = targets;<|endoftext|>