<commit_before>import async from 'async';
import { errors } from 'arsenal';
import config from '../Config';

import collectCorsHeaders from '../utilities/collectCorsHeaders';
import constants from '../../constants';
import data from '../data/wrapper';
import services from '../services';
import { pushMetric } from '../utapi/utilities';

/**
 * multipartDelete - DELETE an open multipart upload from a bucket
 * @param  {AuthInfo} authInfo -Instance of AuthInfo class with requester's info
 * @param  {object} request - request object given by router,
 *                            includes normalized headers
 * @param  {object} log - the log request
 * @param  {function} callback - final callback to call with the
 *                          result and response headers
 * @return {undefined} calls callback from router
 * with err, result and responseMetaHeaders as arguments
 */
export default
function multipartDelete(authInfo, request, log, callback) {
    log.debug('processing request', { method: 'multipartDelete' });

    const bucketName = request.bucketName;
    const objectKey = request.objectKey;
    const uploadId = request.query.uploadId;
    const metadataValMPUparams = {
        authInfo,
        bucketName,
        objectKey,
        uploadId,
        requestType: 'deleteMPU',
        log,
    };
    // For validating the request at the destinationBucket level
    // params are the same as validating at the MPU level
    // but the requestType is the more general 'objectDelete'
    const metadataValParams = Object.assign({}, metadataValMPUparams);
    metadataValParams.requestType = 'objectPut';

    async.waterfall([
        function checkDestBucketVal(next) {
            services.metadataValidateAuthorization(metadataValParams,
                (err, destinationBucket) => {
                    if (err) {
                        return next(err, destinationBucket);
                    }
                    if (destinationBucket.policies) {
                        // TODO: Check bucket policies to see if user is granted
                        // permission or forbidden permission to take
                        // given action.
                        // If permitted, add 'bucketPolicyGoAhead'
                        // attribute to params for validating at MPU level.
                        // This is GH Issue#76
                        metadataValMPUparams.requestType =
                            'bucketPolicyGoAhead';
                    }
                    return next(null, destinationBucket);
                });
        },
        function checkMPUval(destBucket, next) {
            services.metadataValidateMultipart(metadataValParams,
                (err, mpuBucket, mpuOverviewArray) => {
                    if (err) {
                        return next(err, destBucket);
                    }
                    return next(err, mpuBucket, mpuOverviewArray, destBucket);
                });
        },
        function getPartLocations(mpuBucket, mpuOverviewArray, destBucket,
            next) {
            services.getMPUparts(mpuBucket.getName(), uploadId, log,
                (err, result) => {
                    if (err) {
                        return next(err, destBucket);
                    }
                    const storedParts = result.Contents;
                    return next(null, mpuBucket, storedParts, destBucket);
                });
        },
        function deleteData(mpuBucket, storedParts, destBucket, next) {
            // The locations were sent to metadata as an array
            // under partLocations.  Pull the partLocations.
            let locations = storedParts.map(item => item.value.partLocations);
            if (locations.length === 0) {
                return next(null, mpuBucket, storedParts, destBucket);
            }
            // flatten the array
            locations = [].concat.apply([], locations);
            return async.eachLimit(locations, 5, (loc, cb) => {
                data.delete(loc, log, err => {
                    if (err) {
                        log.fatal('delete ObjectPart failed', { err });
                    }
                    cb();
                });
            }, () => next(null, mpuBucket, storedParts, destBucket));
        },
        function deleteMetadata(mpuBucket, storedParts, destBucket, next) {
            let splitter = constants.splitter;
            // BACKWARD: Remove to remove the old splitter
            if (mpuBucket.getMdBucketModelVersion() < 2) {
                splitter = constants.oldSplitter;
            }
            // Reconstruct mpuOverviewKey
            const mpuOverviewKey =
                `overview${splitter}${objectKey}${splitter}${uploadId}`;
            const keysToDelete = storedParts.map(item => item.key);
            keysToDelete.push(mpuOverviewKey);
            services.batchDeleteObjectMetadata(mpuBucket.getName(),
                keysToDelete, log, err => {
                    if (err) {
                        return next(err, destBucket);
                    }
                    pushMetric('abortMultipartUpload', log, {
                        authInfo,
                        bucket: bucketName,
                    });
                    return next(null, destBucket);
                });
        },
    ], (err, destinationBucket) => {
        const corsHeaders = collectCorsHeaders(request.headers.origin,
            request.method, destinationBucket);
        const locationConstraint = destinationBucket ?
          destinationBucket.getLocationConstraint() : null;
        if (err === errors.NoSuchUpload) {
            log.trace('did not find valid mpu with uploadId' +
            `${uploadId}`, { method: 'multipartDelete' });
            // if legacy behavior is enabled for 'us-east-1' and
            // request is from 'us-east-1', return 404 instead of
            // 204
            if (config.usEastBehavior &&
                locationConstraint === 'us-east-1') {
                return callback(err, corsHeaders);
            }
            // otherwise ignore error and return 204 status code
            return callback(null, corsHeaders);
        }
        return callback(err, corsHeaders);
    });
}
<commit_msg>FIX: Include part size sum for Utapi object<commit_after>import async from 'async';
import { errors } from 'arsenal';
import config from '../Config';

import collectCorsHeaders from '../utilities/collectCorsHeaders';
import constants from '../../constants';
import data from '../data/wrapper';
import services from '../services';
import { pushMetric } from '../utapi/utilities';

/**
 * multipartDelete - DELETE an open multipart upload from a bucket
 * @param  {AuthInfo} authInfo -Instance of AuthInfo class with requester's info
 * @param  {object} request - request object given by router,
 *                            includes normalized headers
 * @param  {object} log - the log request
 * @param  {function} callback - final callback to call with the
 *                          result and response headers
 * @return {undefined} calls callback from router
 * with err, result and responseMetaHeaders as arguments
 */
export default
function multipartDelete(authInfo, request, log, callback) {
    log.debug('processing request', { method: 'multipartDelete' });

    const bucketName = request.bucketName;
    const objectKey = request.objectKey;
    const uploadId = request.query.uploadId;
    const metadataValMPUparams = {
        authInfo,
        bucketName,
        objectKey,
        uploadId,
        requestType: 'deleteMPU',
        log,
    };
    // For validating the request at the destinationBucket level
    // params are the same as validating at the MPU level
    // but the requestType is the more general 'objectDelete'
    const metadataValParams = Object.assign({}, metadataValMPUparams);
    metadataValParams.requestType = 'objectPut';

    async.waterfall([
        function checkDestBucketVal(next) {
            services.metadataValidateAuthorization(metadataValParams,
                (err, destinationBucket) => {
                    if (err) {
                        return next(err, destinationBucket);
                    }
                    if (destinationBucket.policies) {
                        // TODO: Check bucket policies to see if user is granted
                        // permission or forbidden permission to take
                        // given action.
                        // If permitted, add 'bucketPolicyGoAhead'
                        // attribute to params for validating at MPU level.
                        // This is GH Issue#76
                        metadataValMPUparams.requestType =
                            'bucketPolicyGoAhead';
                    }
                    return next(null, destinationBucket);
                });
        },
        function checkMPUval(destBucket, next) {
            services.metadataValidateMultipart(metadataValParams,
                (err, mpuBucket, mpuOverviewArray) => {
                    if (err) {
                        return next(err, destBucket);
                    }
                    return next(err, mpuBucket, mpuOverviewArray, destBucket);
                });
        },
        function getPartLocations(mpuBucket, mpuOverviewArray, destBucket,
            next) {
            services.getMPUparts(mpuBucket.getName(), uploadId, log,
                (err, result) => {
                    if (err) {
                        return next(err, destBucket);
                    }
                    const storedParts = result.Contents;
                    return next(null, mpuBucket, storedParts, destBucket);
                });
        },
        function deleteData(mpuBucket, storedParts, destBucket, next) {
            // The locations were sent to metadata as an array
            // under partLocations.  Pull the partLocations.
            let locations = storedParts.map(item => item.value.partLocations);
            if (locations.length === 0) {
                return next(null, mpuBucket, storedParts, destBucket);
            }
            // flatten the array
            locations = [].concat.apply([], locations);
            return async.eachLimit(locations, 5, (loc, cb) => {
                data.delete(loc, log, err => {
                    if (err) {
                        log.fatal('delete ObjectPart failed', { err });
                    }
                    cb();
                });
            }, () => next(null, mpuBucket, storedParts, destBucket));
        },
        function deleteMetadata(mpuBucket, storedParts, destBucket, next) {
            let splitter = constants.splitter;
            // BACKWARD: Remove to remove the old splitter
            if (mpuBucket.getMdBucketModelVersion() < 2) {
                splitter = constants.oldSplitter;
            }
            // Reconstruct mpuOverviewKey
            const mpuOverviewKey =
                `overview${splitter}${objectKey}${splitter}${uploadId}`;
            // Get the sum of all part sizes to include in pushMetric object
            const partSizeSum = storedParts.map(item => item.value.Size)
                .reduce((currPart, nextPart) => currPart + nextPart, 0);
            const keysToDelete = storedParts.map(item => item.key);
            keysToDelete.push(mpuOverviewKey);
            services.batchDeleteObjectMetadata(mpuBucket.getName(),
                keysToDelete, log, err => {
                    if (err) {
                        return next(err, destBucket);
                    }
                    pushMetric('abortMultipartUpload', log, {
                        authInfo,
                        bucket: bucketName,
                        byteLength: partSizeSum,
                    });
                    return next(null, destBucket);
                });
        },
    ], (err, destinationBucket) => {
        const corsHeaders = collectCorsHeaders(request.headers.origin,
            request.method, destinationBucket);
        const locationConstraint = destinationBucket ?
          destinationBucket.getLocationConstraint() : null;
        if (err === errors.NoSuchUpload) {
            log.trace('did not find valid mpu with uploadId' +
            `${uploadId}`, { method: 'multipartDelete' });
            // if legacy behavior is enabled for 'us-east-1' and
            // request is from 'us-east-1', return 404 instead of
            // 204
            if (config.usEastBehavior &&
                locationConstraint === 'us-east-1') {
                return callback(err, corsHeaders);
            }
            // otherwise ignore error and return 204 status code
            return callback(null, corsHeaders);
        }
        return callback(err, corsHeaders);
    });
}
<|endoftext|>


<commit_before>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const CaseSensitiveModulesWarning = require("./CaseSensitiveModulesWarning");

class WarnCaseSensitiveModulesPlugin {
	apply(compiler) {
		compiler.plugin("compilation", compilation => {
			compilation.plugin("seal", function() {
				const moduleWithoutCase = new Map();
				this.modules.forEach(module => {
					const identifier = module.identifier().toLowerCase();
					const moduleInstance = moduleWithoutCase.get(`$${identifier}`)
					if(moduleInstance) {
						moduleWithoutCase.set(`$${identifier}`, moduleInstance.push(module));
					} else {
						moduleWithoutCase.set(`$${identifier}`, [module]);
					}
				});
				Object.keys(moduleWithoutCase).forEach(key => {
					const moduleInstance = moduleWithoutCase.get(key);
					if(moduleInstance.length > 1)
						this.warnings.push(new CaseSensitiveModulesWarning(moduleInstance));
				}, this);
			});
		});
	}
}

module.exports = WarnCaseSensitiveModulesPlugin;
<commit_msg>refactor(WarnCaseSensitiveModulesPlugin): applying feedback<commit_after>/*
	MIT License http://www.opensource.org/licenses/mit-license.php
	Author Tobias Koppers @sokra
*/
"use strict";

const CaseSensitiveModulesWarning = require("./CaseSensitiveModulesWarning");

class WarnCaseSensitiveModulesPlugin {
	apply(compiler) {
		compiler.plugin("compilation", compilation => {
			compilation.plugin("seal", () => {
				const moduleWithoutCase = Object.create(null);
				compilation.modules.forEach(module => {
					const identifier = module.identifier().toLowerCase();
					if(moduleWithoutCase[identifier]) {
						moduleWithoutCase[identifier].push(module);
					} else {
						moduleWithoutCase[identifier] = [module];
					}
				});
				Object.keys(moduleWithoutCase).forEach(key => {
					if(moduleWithoutCase[key].length > 1)
						compilation.warnings.push(new CaseSensitiveModulesWarning(moduleWithoutCase[key]));
				});
			});
		});
	}
}

module.exports = WarnCaseSensitiveModulesPlugin;
<|endoftext|>


<commit_before>(function() {
  var checker = setInterval(function() {
    if (!jasmine.running) {
      var results = {};
      var specs = jsApiReporter.specs();
      results.runtime = jsApiReporter.executionTime();
      results.total = 0;
      results.passed = 0;
      results.failed = 0;
      results.tracebacks = [];

      for (var spec in specs) {
        if (specs[spec].status === 'passed') {
            results.passed++;
        } else {
            results.tracebacks.push(specs[spec].description);
            results.failed = true;
        }
      }

      results.total = results.passed + results.failed;
      results.url = window.location.pathname;
      BrowserStack.post('/_report', results, function(){});
    }
    clearInterval(checker);
  }, 1000);
})();

<commit_msg>count failed specs<commit_after>(function() {
  var checker = setInterval(function() {
    if (!jasmine.running) {
      var results = {};
      var specs = jsApiReporter.specs();
      results.runtime = jsApiReporter.executionTime();
      results.total = 0;
      results.passed = 0;
      results.failed = 0;
      results.tracebacks = [];

      for (var spec in specs) {
        if (specs[spec].status === 'passed') {
            results.passed++;
        } else {
            results.tracebacks.push(specs[spec].description);
            results.failed++;
        }
      }

      results.total = results.passed + results.failed;
      results.url = window.location.pathname;
      BrowserStack.post('/_report', results, function(){});
    }
    clearInterval(checker);
  }, 1000);
})();

<|endoftext|>


<commit_before>var app = app || {};

(function($) {
  'use strict';

  app.JobSingleView = app.JobAbstractView.extend({

    template: _.template($('#job-template').html()),

    events: {
      'click .job-kill': 'killJob'
    },

    initialize: function() {
      this.setElement(this.el);
      this.listenTo(this.model, 'change', this.render);
    },

    render: function() {
      var job = this.model;
      job.set('statusColor', this.getStatusColor(job.get('status')));
      this.$el.html(this.template(job.toJSON())).find('.timeago').timeago();
    },

    killJob: function() {
      $.post('/job/' + this.model.id + '/kill');
    }
  });

})(jQuery);
<commit_msg>Scroll to bottom on job update.<commit_after>var app = app || {};

(function($) {
  'use strict';

  app.JobSingleView = app.JobAbstractView.extend({

    template: _.template($('#job-template').html()),

    events: {
      'click .job-kill': 'killJob'
    },

    initialize: function() {
      this.setElement(this.el);
      this.listenTo(this.model, 'change', this.render);
    },

    render: function() {
      var job = this.model;
      job.set('statusColor', this.getStatusColor(job.get('status')));
      this.$el.html(this.template(job.toJSON())).find('.timeago').timeago();
      window.scrollTo(0, document.body.scrollHeight);
    },

    killJob: function() {
      $.post('/job/' + this.model.id + '/kill');
    }
  });

})(jQuery);
<|endoftext|>


<commit_before>'use strict';

var express = require('express');
var app = express();
var configHelper = require('../config');
var filteredConfig = configHelper.getFilteredConfig();
var cheerio = require('cheerio');
var fs = require('fs');
var path = require('path');
var util = require('util');
var log = require('../logger')('nodecg/lib/bundle_views');
var Bundles = require('../bundles');

require('string.prototype.endswith');

log.trace('Adding Express routes');

app.set('views', path.resolve(__dirname, '../..'));

app.get('/view/:bundleName*', function(req, res, next) {
    var bundleName = req.params.bundleName;

    var bundle = Bundles.find(bundleName);

    if (!bundle) {
        next();
        return;
    }

    // We start out assuming the user is trying to reach the index page
    var resName = 'index.html';

    //  We need a trailing slash for view index pages so that relatively linked assets can be reached as expected.
    if (!req.params[0] && !req.url.endsWith('/')) {
        res.redirect(req.url + '/');
        return;
    }

    // If the first param isn't just a slash, the user must be trying to resolve an asset and not the index page
    if (req.params[0] !== '/' && !req.url.endsWith('/')) {
        resName = req.params[0].substr(1);
    }

    var fileLocation = path.join(bundle.dir, 'view', resName);

    // Check if the file exists
    if (!fs.existsSync(fileLocation)) {
        next();
        return;
    }

    // If it's a HTML file, inject the viewsetup script and serve that
    // otherwise, send the file unmodified
    if (resName.endsWith('.html')) {
        var file = fs.readFileSync(fileLocation);
        var $ = cheerio.load(file);

        var scripts = '<script src="/socket.io/socket.io.js"></script>' +
            '<script src="/nodecg-api.js"></script>' +
            '<script>var socket = io("//%s/");' +
            'window.nodecg = new NodeCG("%s", %s, %s, socket);</script>';

        scripts = util.format(scripts, filteredConfig.baseURL, bundle.name,
            JSON.stringify(filteredConfig), JSON.stringify(bundle.config));

        var currentHead = $('head').html();
        $('head').html(scripts + currentHead);

        res.send($.html());
    } else {
        res.sendFile(fileLocation);
    }
});

app.get('/view/:bundle/components/*', function(req, res, next) {
    var bundleName = req.params.bundle;

    var bundle = Bundles.find(bundleName);

    if (!bundle) {
        next();
        return;
    }

    var resName = req.params[0];

    var fileLocation = path.join(bundle.dir, 'bower_components', resName);

    // Check if the file exists
    if (!fs.existsSync(fileLocation)) {
        next();
        return;
    }

    res.sendFile(fileLocation);
});

module.exports = app;
<commit_msg>Fix bundle config and NodeCG config being switched.<commit_after>'use strict';

var express = require('express');
var app = express();
var configHelper = require('../config');
var filteredConfig = configHelper.getFilteredConfig();
var cheerio = require('cheerio');
var fs = require('fs');
var path = require('path');
var util = require('util');
var log = require('../logger')('nodecg/lib/bundle_views');
var Bundles = require('../bundles');

require('string.prototype.endswith');

log.trace('Adding Express routes');

app.set('views', path.resolve(__dirname, '../..'));

app.get('/view/:bundleName*', function(req, res, next) {
    var bundleName = req.params.bundleName;

    var bundle = Bundles.find(bundleName);

    if (!bundle) {
        next();
        return;
    }

    // We start out assuming the user is trying to reach the index page
    var resName = 'index.html';

    //  We need a trailing slash for view index pages so that relatively linked assets can be reached as expected.
    if (!req.params[0] && !req.url.endsWith('/')) {
        res.redirect(req.url + '/');
        return;
    }

    // If the first param isn't just a slash, the user must be trying to resolve an asset and not the index page
    if (req.params[0] !== '/' && !req.url.endsWith('/')) {
        resName = req.params[0].substr(1);
    }

    var fileLocation = path.join(bundle.dir, 'view', resName);

    // Check if the file exists
    if (!fs.existsSync(fileLocation)) {
        next();
        return;
    }

    // If it's a HTML file, inject the viewsetup script and serve that
    // otherwise, send the file unmodified
    if (resName.endsWith('.html')) {
        var file = fs.readFileSync(fileLocation);
        var $ = cheerio.load(file);

        var scripts = '<script src="/socket.io/socket.io.js"></script>' +
            '<script src="/nodecg-api.js"></script>' +
            '<script>var socket = io("//%s/");' +
            'window.nodecg = new NodeCG("%s", %s, %s, socket);</script>';

        scripts = util.format(scripts, filteredConfig.baseURL, bundle.name,
            JSON.stringify(bundle.config), JSON.stringify(filteredConfig));

        var currentHead = $('head').html();
        $('head').html(scripts + currentHead);

        res.send($.html());
    } else {
        res.sendFile(fileLocation);
    }
});

app.get('/view/:bundle/components/*', function(req, res, next) {
    var bundleName = req.params.bundle;

    var bundle = Bundles.find(bundleName);

    if (!bundle) {
        next();
        return;
    }

    var resName = req.params[0];

    var fileLocation = path.join(bundle.dir, 'bower_components', resName);

    // Check if the file exists
    if (!fs.existsSync(fileLocation)) {
        next();
        return;
    }

    res.sendFile(fileLocation);
});

module.exports = app;
<|endoftext|>


<commit_before>
export { Base };

/**
 * Testing Filters of multiple layers.
 *
 * @member {string} name This test filters' name.
 */
class Base {

  /**
   * @param sourceHeight      The height (and width) of the source image which will be processed by apply().
   * @param sourceDepth       The channel count of the source image.
   * @param targetHeight      The taregt image height (and width).
   * @param strAvgMaxConv     "Avg" or "Max" or "Conv". for average pooling, max pooling, depthwise convolution.
   *
   * @param depthwiseActivationName
   *   The activation function name after depthwise convolution. One of the following "", "relu", "relu6", "sigmoid", "tanh", "sin".
   *
   * @param filterHeight      The height (and width) of each depthwise convolution.
   * @param bPointwise        If true, there will be pointwise convolution after every layer of depthwise convolution.
   *
   * @param pointwiseActivationName
   *   The activation function name after pointwise convolution. One of the following "", "relu", "relu6", "sigmoid", "tanh", "sin".
   * If ( bPointwise == false ), this activation function will be ignored.
   */
  init( sourceHeight, sourceDepth, targetHeight, filterHeight, strAvgMaxConv, depthwiseActivationName, bPointwise, pointwiseActivationName ) {
    this.disposeTensors();

//    this.name = name;

    let differenceHeight = sourceHeight - targetHeight;
    let filterWidth = filterHeight;
    let channelMultiplier = 1;

    this.strAvgMaxConv = strAvgMaxConv;
    switch ( strAvgMaxConv ) {
      case "Avg":  this.bDepthwiseAvg = true;
      case "Max":  this.bDepthwiseMax = true;
      case "Conv": this.bDepthwiseConv = true;
    }

    this.depthwiseActivationName = depthwiseActivationName;
    switch ( depthwiseActivationName ) {
      case "relu":    this.depthwiseActivationFunction = tf.relu;    break;
      case "relu6":   this.depthwiseActivationFunction = tf.relu6;   break;
      case "sigmoid": this.depthwiseActivationFunction = tf.sigmoid; break;
      case "tanh":    this.depthwiseActivationFunction = tf.tanh;     break;
      case "sin":     this.depthwiseActivationFunction = tf.sin;     break;
      //default:
    }

    this.bPointwise = bPointwise;

    this.pointwiseActivationName = pointwiseActivationName;
    switch ( pointwiseActivationName ) {
      case "relu":    this.pointwiseActivationFunction = tf.relu;    break;
      case "relu6":   this.pointwiseActivationFunction = tf.relu6;   break;
      case "sigmoid": this.pointwiseActivationFunction = tf.sigmoid; break;
      case "tanh":    this.pointwiseActivationFunction = tf.tanh;     break;
      case "sin":     this.pointwiseActivationFunction = tf.sin;     break;
      //default:
    }

    this.depthwiseFiltersShape = [ filterHeight, filterWidth, sourceDepth, channelMultiplier ];
    this.depthwiseFilterHeightWidth = [ filterHeight, filterWidth ];

    let depthwiseFiltersValueCount = tf.util.sizeFromShape( this.depthwiseFiltersShape );

    this.pointwiseFiltersShape = [ 1, 1, sourceDepth, sourceDepth ];  // Assume output depth is the same as input.
    this.pointwiseFilterHeightWidth = [ 1, 1 ];

    let pointwiseFiltersValueCount = tf.util.sizeFromShape( this.pointwiseFiltersShape );

    // The height of processed image will be reduced a little for any depthwise filter larger than 1x1.
    let heightReducedPerStep = filterHeight - 1;

    // The step count for reducing sourceHeight to targetHeight by depthwise convolution filter.
    this.blockCount = Math.floor( differenceHeight / heightReducedPerStep );

    // e.g. "DConv_101x101_RELU_1x1_RELU_1_Block"
    this.name = `D${strAvgMaxConv}_${filterHeight}x${filterHeight}`
      + `${ this.depthwiseActivationFunction ? ( "_" + depthwiseActivationName ) : ""}`
      + `${ this.bPointwise ? "_1x1" : ""}`
      + `${ this.pointwiseActivationFunction ? ( "_" + pointwiseActivationName ) : ""}`
      + `_Block_${this.blockCount}`
    ;

    // Every element (Tensor4d) is a depthwiseFilters for one layer (i.e. one step).
    this.depthwiseFiltersTensor4dArray = tf.tidy( () => {
      let filtersTensor4dArray = new Array( this.blockCount );
      for ( let i = 0; i < this.blockCount; ++i ) {
        if ( this.bDepthwiseConv ) {
          let filtersTensor1d = tf.range( 0, depthwiseFiltersValueCount, 1 );
          let filtersTensor4d = filtersTensor1d.reshape( this.depthwiseFiltersShape );
          filtersTensor4dArray[ i ] = filtersTensor4d;
        } else {
          filtersTensor4dArray[ i ] = null;
        }
      }
      return filtersTensor4dArray;
    });

    // Every element (Tensor4d) is a pointwiseFilters for one layer (i.e. one step).
    this.pointwiseFiltersTensor4dArray = tf.tidy( () => {
      let filtersTensor4dArray = new Array( this.blockCount );
      for ( let i = 0; i < this.blockCount; ++i ) {
        if ( bPointwise ) {
          let filtersTensor1d = tf.range( 0, pointwiseFiltersValueCount, 1 );
          let filtersTensor4d = filtersTensor1d.reshape( this.pointwiseFiltersShape );
          filtersTensor4dArray[ i ] = filtersTensor4d;
        } else {
          filtersTensor4dArray[ i ] = null;
        }
      }
      return filtersTensor4dArray;
    });

  }

  disposeTensors() {
    if ( this.depthwiseFiltersTensor4dArray ) {
      tf.dispose( this.depthwiseFiltersTensor4dArray );
      this.depthwiseFiltersTensor4dArray = null;
    }

    if ( this.pointwiseFiltersTensor4dArray ) {
      tf.dispose( this.pointwiseFiltersTensor4dArray );
      this.pointwiseFiltersTensor4dArray = null;
    }
  }

  /**
   * @param sourceImage
   *   The image which will be processed.
   *
   * @param bReturn
   *   If true, the result convoluiotn will be returned.
   *
   * @return
   *   If ( bReturn == true ), return the convolution result (tensor). Otheriwse, reurn null.
   */
  apply( sourceImage, bReturn ) {
    return tf.tidy( () => {

      let t, tNew;
//      let depthwiseFiltersTensor4d;
//      let pointwiseFiltersTensor4d;

      // Layer 0
      //
      // So that every layer could dispose previous result without worry about mis-dispose source.

      if ( this.bDepthwiseAvg ) {
        t = sourceImage.pool( this.depthwiseFilterHeightWidth, "avg", "valid", 1, 1 );
      } else if ( this.bDepthwiseMax ) {
        t = sourceImage.pool( this.depthwiseFilterHeightWidth, "max", "valid", 1, 1 );
      } else if ( this.bDepthwiseConv ) {
        t = sourceImage.depthwiseConv2d( this.depthwiseFiltersTensor4dArray[ 0 ], 1, "valid" );  // Stride = 1
      }
      // NOTE: Do not dispose the original data.

      if ( this.depthwiseActivationFunction ) {
        tNew = this.depthwiseActivationFunction( t );
        t.dispose();                                         // Dispose all intermediate (temporary) data.
        t = tNew;
      }

      if ( this.bPointwise ) {
        tNew = t.conv2d( this.pointwiseFiltersTensor4dArray[ 0 ], 1, "valid" ); // 1x1, Stride = 1
        t.dispose();                                         // Dispose all intermediate (temporary) data.
        t = tNew;

        if ( this.pointwiseActivationFunction ) {
          tNew = this.pointwiseActivationFunction( t );
          t.dispose();                                         // Dispose all intermediate (temporary) data.
          t = tNew;
        }
      }

      // Layer 1, ...
      if ( this.bDepthwiseAvg ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.pool( this.depthwiseFilterHeightWidth, "avg", "valid", 1, 1 );
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = tNew;

          if ( this.depthwiseActivationFunction ) {
            tNew = this.depthwiseActivationFunction( t );
            t.dispose();                                         // Dispose all intermediate (temporary) data.
            t = tNew;
          }

          if ( this.bPointwise ) {
            tNew = t.conv2d( this.pointwiseFiltersTensor4dArray[ i ], 1, "valid" ); // 1x1, Stride = 1
            t.dispose();                                         // Dispose all intermediate (temporary) data.
            t = tNew;

            if ( this.pointwiseActivationFunction ) {
              tNew = this.pointwiseActivationFunction( t );
              t.dispose();                                         // Dispose all intermediate (temporary) data.
              t = tNew;
            }
          }
        }

      } else if ( this.bDepthwiseMax ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.pool( this.depthwiseFilterHeightWidth, "max", "valid", 1, 1 );
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = tNew;

          if ( this.depthwiseActivationFunction ) {
            tNew = this.depthwiseActivationFunction( t );
            t.dispose();                                         // Dispose all intermediate (temporary) data.
            t = tNew;
          }

          if ( this.bPointwise ) {
            tNew = t.conv2d( this.pointwiseFiltersTensor4dArray[ i ], 1, "valid" ); // 1x1, Stride = 1
            t.dispose();                                         // Dispose all intermediate (temporary) data.
            t = tNew;

            if ( this.pointwiseActivationFunction ) {
              tNew = this.pointwiseActivationFunction( t );
              t.dispose();                                         // Dispose all intermediate (temporary) data.
              t = tNew;
            }
          }
        }

      } else if ( this.bDepthwiseConv ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.depthwiseConv2d( this.depthwiseFiltersTensor4dArray[ i ], 1, "valid" );  // Stride = 1
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = tNew;

          if ( this.depthwiseActivationFunction ) {
            tNew = this.depthwiseActivationFunction( t );
            t.dispose();                                         // Dispose all intermediate (temporary) data.
            t = tNew;
          }

          if ( this.bPointwise ) {
            tNew = t.conv2d( this.pointwiseFiltersTensor4dArray[ i ], 1, "valid" ); // 1x1, Stride = 1
            t.dispose();                                         // Dispose all intermediate (temporary) data.
            t = tNew;

            if ( this.pointwiseActivationFunction ) {
              tNew = this.pointwiseActivationFunction( t );
              t.dispose();                                         // Dispose all intermediate (temporary) data.
              t = tNew;
            }
          }
        }

      }

      if ( bReturn )
        return t;
    });
  }

}

<commit_msg>Update TestFilters2D.js<commit_after>
export { Base };

/**
 * Testing Filters of multiple layers.
 *
 * @member {string} name This test filters' name.
 */
class Base {

  /**
   * @param sourceHeight      The height (and width) of the source image which will be processed by apply().
   * @param sourceDepth       The channel count of the source image.
   * @param targetHeight      The taregt image height (and width).
   * @param filterHeight      The height (and width) of each depthwise convolution.
   *
   * @param strAvgMaxConv
   *   Depthwise operation. "Avg" or "Max" or "Conv" for average pooling, max pooling, depthwise convolution.
   *
   * @param bDepthwiseBias
   *   If true, there will be a bias after depthwise convolution.
   *
   * @param depthwiseActivationName
   *   The activation function name after depthwise convolution. One of the following "", "relu", "relu6", "sigmoid", "tanh", "sin".
   *
   * @param bPointwise
   *   If true, there will be pointwise convolution after every layer of depthwise convolution.
   *
   * @param bPointwiseBias
   *   If true, there will be a bias after pointwise convolution. If ( bPointwise == false ), this will be also ignored.
   *
   * @param pointwiseActivationName
   *   The activation function name after pointwise convolution. One of the following "", "relu", "relu6", "sigmoid", "tanh", "sin".
   * If ( bPointwise == false ), this activation function will be also ignored.
   */
  init(
    sourceHeight, sourceDepth, targetHeight, filterHeight,
    strAvgMaxConv, bDepthwiseBias, depthwiseActivationName,
    bPointwise, bPointwiseBias, pointwiseActivationName ) {

    this.disposeTensors();

//    this.name = name;

    let differenceHeight = sourceHeight - targetHeight;
    let filterWidth = filterHeight;
    let channelMultiplier = 1;

    this.strAvgMaxConv = strAvgMaxConv;
    switch ( strAvgMaxConv ) {
      case "Avg":  this.bDepthwiseAvg = true;
      case "Max":  this.bDepthwiseMax = true;
      case "Conv": this.bDepthwiseConv = true;
    }

    this.bDepthwiseBias = bDepthwiseBias;

    this.depthwiseActivationName = depthwiseActivationName;
    switch ( depthwiseActivationName ) {
      case "relu":    this.depthwiseActivationFunction = tf.relu;    break;
      case "relu6":   this.depthwiseActivationFunction = tf.relu6;   break;
      case "sigmoid": this.depthwiseActivationFunction = tf.sigmoid; break;
      case "tanh":    this.depthwiseActivationFunction = tf.tanh;     break;
      case "sin":     this.depthwiseActivationFunction = tf.sin;     break;
      //default:
    }

    this.bPointwise = bPointwise;
    this.bPointwiseBias = bPointwiseBias;

    this.pointwiseActivationName = pointwiseActivationName;
    switch ( pointwiseActivationName ) {
      case "relu":    this.pointwiseActivationFunction = tf.relu;    break;
      case "relu6":   this.pointwiseActivationFunction = tf.relu6;   break;
      case "sigmoid": this.pointwiseActivationFunction = tf.sigmoid; break;
      case "tanh":    this.pointwiseActivationFunction = tf.tanh;     break;
      case "sin":     this.pointwiseActivationFunction = tf.sin;     break;
      //default:
    }

    // The height of processed image will be reduced a little for any depthwise filter larger than 1x1.
    let heightReducedPerStep = filterHeight - 1;

    // The step count for reducing sourceHeight to targetHeight by depthwise convolution filter.
    this.blockCount = Math.floor( differenceHeight / heightReducedPerStep );

    // e.g. "DConv_101x101_RELU_1x1_RELU_1_Block"
    this.name = `D${strAvgMaxConv}_${filterHeight}x${filterHeight}`
      + `${ this.depthwiseActivationFunction ? ( "_" + depthwiseActivationName ) : ""}`
      + `${ this.bPointwise ? "_1x1" : ""}`
      + `${ this.pointwiseActivationFunction ? ( "_" + pointwiseActivationName ) : ""}`
      + `_Block_${this.blockCount}`
    ;

    // Depthwise Filters and Biases

    this.depthwiseFilterHeightWidth = [ filterHeight, filterWidth ];
    this.depthwiseFiltersShape = [ filterHeight, filterWidth, sourceDepth, channelMultiplier ];
    this.depthwiseBiasesShape =  [            1,           1, sourceDepth, channelMultiplier ];

    let depthwiseFiltersValueCount = tf.util.sizeFromShape( this.depthwiseFiltersShape );
    let depthwiseBiasesValueCount =  tf.util.sizeFromShape( this.depthwiseBiasesShape );

    // Every element (Tensor4d) is a depthwiseFilters for one block.
    this.depthwiseFiltersTensor4dArray
      = Base.generateTensor4dArray( this.blockCount, depthwiseFiltersValueCount, this.depthwiseFiltersShape, this.bDepthwiseConv );

    // Every element (Tensor4d) is a depthwiseBiases for one block.
    this.depthwiseBiasesTensor4dArray
      = Base.generateTensor4dArray( this.blockCount, depthwiseBiasesValueCount, this.depthwiseBiasesShape, ( this.bDepthwiseConv && bDepthwiseBias ) );

    // Pointwise Filters and Biases

    let pointwiseInputDepth =  sourceDepth;
    let pointwiseOutputDepth = sourceDepth; // Assume output depth is the same as input.

    this.pointwiseFilterHeightWidth = [ 1, 1 ];
    this.pointwiseFiltersShape = [ 1, 1, pointwiseInputDepth, pointwiseOutputDepth ];

    // Both input depth and output depth of pointwise bias are the same as pointwise convolution output.
    this.pointwiseBiasesShape =  [ 1, 1, pointwiseOutputDepth, pointwiseOutputDepth ];

    let pointwiseFiltersValueCount = tf.util.sizeFromShape( this.pointwiseFiltersShape );
    let pointwiseBiasesValueCount =  tf.util.sizeFromShape( this.pointwiseBiasesShape );

    // Every element (Tensor4d) is a pointwiseFilters for one block.
    this.pointwiseFiltersTensor4dArray
      = Base.generateTensor4dArray( this.blockCount, pointwiseFiltersValueCount, this.pointwiseFiltersShape, bPointwise );

    // Every element (Tensor4d) is a pointwiseBiases for one block.
    this.pointwiseBiasesTensor4dArray
      = Base.generateTensor4dArray( this.blockCount, pointwiseBiasesValueCount, this.pointwiseBiasesShape, ( bPointwise && bPointwiseBias ) );
  }

  /**
   * @param {number}   blockCount    The element count (i.e. length) of the returned array.
   * @param {number}   valueCount    The element count of every tensor4d (which is an element of the returned array).
   * @param {number[]} tensor4dShape The tensor's shape of every element of the returned array.
   * @param {boolean}  bNullElement  If true, every element of the return array will be null.
   * @return {tf.tensor4d[]} Return a array whose every element is a tensor4d (for one block) of null (if ( bNullElement == true ) ).
   */
  static generateTensor4dArray( blockCount, valueCount, tensor4dShape, bNullElement ) {
    return tf.tidy( () => {
      let tensor4dArray = new Array( blockCount );
      for ( let i = 0; i < blockCount; ++i ) {
        if ( bNullElement ) {
          let tensor1d = tf.range( 0, valueCount, 1 );
          let tensor4d = tensor1d.reshape( tensor4dShape );
          tensor4dArray[ i ] = tensor4d;
        } else {
          tensor4dArray[ i ] = null;
        }
      }
      return tensor4dArray;
    });
  }

  disposeTensors() {
    if ( this.depthwiseFiltersTensor4dArray ) {
      tf.dispose( this.depthwiseFiltersTensor4dArray );
      this.depthwiseFiltersTensor4dArray = null;
    }

    if ( this.depthwiseBiasesTensor4dArray ) {
      tf.dispose( this.depthwiseBiasesTensor4dArray );
      this.depthwiseBiasesTensor4dArray = null;
    }

    if ( this.pointwiseFiltersTensor4dArray ) {
      tf.dispose( this.pointwiseFiltersTensor4dArray );
      this.pointwiseFiltersTensor4dArray = null;
    }

    if ( this.pointwiseBiasesTensor4dArray ) {
      tf.dispose( this.pointwiseBiasesTensor4dArray );
      this.pointwiseBiasesTensor4dArray = null;
    }
  }

  /**
   * @param  {tf.tensor4d} inputTensor  Apply this tensor with depthwise (bias and activation) and pointwise (bias and activation).
   * @param  {number}      blockIndex   Which block's depthwise and pointwise operrations.
   * @return {tf.tensor4d} Return a new tensor. All other tensors (including inputTensor) were disposed.
   */
  apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( inputTensor, blockIndex ) {
    let t = inputTensor, tNew;

    if ( this.bDepthwiseBias ) {
      tNew = t.add( this.depthwiseBiasesTensor4dArray[ blockIndex ] );
      t.dispose();                                         // Dispose all intermediate (temporary) data.
      t = tNew;
    }

    if ( this.depthwiseActivationFunction ) {
      tNew = this.depthwiseActivationFunction( t );
      t.dispose();                                         // Dispose all intermediate (temporary) data.
      t = tNew;
    }

    if ( this.bPointwise ) {
      tNew = t.conv2d( this.pointwiseFiltersTensor4dArray[ blockIndex ], 1, "valid" ); // 1x1, Stride = 1
      t.dispose();                                         // Dispose all intermediate (temporary) data.
      t = tNew;

      if ( this.bPointwiseBias ) {
        tNew = t.add( this.pointwiseBiasesTensor4dArray[ blockIndex ] );
        t.dispose();                                       // Dispose all intermediate (temporary) data.
        t = tNew;
      }

      if ( this.pointwiseActivationFunction ) {
        tNew = this.pointwiseActivationFunction( t );
        t.dispose();                                       // Dispose all intermediate (temporary) data.
        t = tNew;
      }
    }

    return t;
  }

  /**
   * @param sourceImage
   *   The image which will be processed.
   *
   * @param bReturn
   *   If true, the result convoluiotn will be returned.
   *
   * @return
   *   If ( bReturn == true ), return the convolution result (tensor). Otheriwse, reurn null.
   */
  apply( sourceImage, bReturn ) {
    return tf.tidy( () => {

      let t, tNew;

      // Layer 0
      //
      // So that every layer could dispose previous result without worry about mis-dispose source.

      if ( this.bDepthwiseAvg ) {
        t = sourceImage.pool( this.depthwiseFilterHeightWidth, "avg", "valid", 1, 1 );
      } else if ( this.bDepthwiseMax ) {
        t = sourceImage.pool( this.depthwiseFilterHeightWidth, "max", "valid", 1, 1 );
      } else if ( this.bDepthwiseConv ) {
        t = sourceImage.depthwiseConv2d( this.depthwiseFiltersTensor4dArray[ 0 ], 1, "valid" );  // Stride = 1
      }
      // NOTE: Do not dispose the original data.

      t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( t, 0 );

      // Layer 1, ...
      if ( this.bDepthwiseAvg ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.pool( this.depthwiseFilterHeightWidth, "avg", "valid", 1, 1 );
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( tNew, i );
        }

      } else if ( this.bDepthwiseMax ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.pool( this.depthwiseFilterHeightWidth, "max", "valid", 1, 1 );
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( tNew, i );
        }

      } else if ( this.bDepthwiseConv ) {

        for ( let i = 1; i < this.blockCount; ++i ) {
          tNew = t.depthwiseConv2d( this.depthwiseFiltersTensor4dArray[ i ], 1, "valid" );  // Stride = 1
          t.dispose();                                           // Dispose all intermediate (temporary) data.
          t = this.apply_Depthwise_Bias_Activation_Pointwise_Bias_Activation( tNew, i );
        }

      }

      if ( bReturn )
        return t;
    });
  }

}

<|endoftext|>


<commit_before>const validMessageTypes = [
  'commentAdded',
  'messageAdded',
  'userTyping',
  'newThread',
  'newNotification',
  'newPost'
]

export function pushToSockets (room, messageType, payload, socketToExclude) {
  if (!validMessageTypes.includes(messageType)) {
    throw new Error(`unknown message type: ${messageType}`)
  }

  if (process.env.NODE_ENV === 'test') return Promise.resolve({room, messageType, payload})
  sails.sockets.broadcast(room, messageType, payload, socketToExclude)
  return Promise.resolve()
}

const makeRoomAction = method => (req, res, type, id, options = {}) => {
  const callback = options.callback || emptyResponse(res)
  const room = roomTypes[type](id)
  sails.log.info(`${method}: ${room}`)
  return sails.sockets[method](req, room, callback)
}

export const joinRoom = makeRoomAction('join')
export const leaveRoom = makeRoomAction('leave')

export function userRoom (userId) {
  return `users/${userId}`
}

export function postRoom (postId) {
  return `posts/${postId}`
}

export function communityRoom (communityId) {
  return `communities/${communityId}`
}

const roomTypes = {
  user: userRoom,
  post: postRoom,
  community: communityRoom
}

const emptyResponse = res => err => err ? res.serverError(err) : res.ok({})
<commit_msg>improve websocket logging a bit<commit_after>import { cyan } from 'chalk'

const validMessageTypes = [
  'commentAdded',
  'messageAdded',
  'userTyping',
  'newThread',
  'newNotification',
  'newPost'
]

export function pushToSockets (room, messageType, payload, socketToExclude) {
  if (!validMessageTypes.includes(messageType)) {
    throw new Error(`unknown message type: ${messageType}`)
  }

  sails.log.info(`${cyan('Websockets:')} pushToSockets: ${room}, ${messageType}`)
  if (process.env.NODE_ENV === 'test') return Promise.resolve({room, messageType, payload})
  sails.sockets.broadcast(room, messageType, payload, socketToExclude)
  return Promise.resolve()
}

const makeRoomAction = method => (req, res, type, id, options = {}) => {
  const callback = options.callback || emptyResponse(res)
  const room = roomTypes[type](id)
  sails.log.info(`${cyan('Websockets:')} ${method}: ${room}`)
  return sails.sockets[method](req, room, callback)
}

export const joinRoom = makeRoomAction('join')
export const leaveRoom = makeRoomAction('leave')

export function userRoom (userId) {
  return `users/${userId}`
}

export function postRoom (postId) {
  return `posts/${postId}`
}

export function communityRoom (communityId) {
  return `communities/${communityId}`
}

const roomTypes = {
  user: userRoom,
  post: postRoom,
  community: communityRoom
}

const emptyResponse = res => err => err ? res.serverError(err) : res.ok({})
<|endoftext|>


<commit_before>/**
 * @module ol/interaction/Translate
 */
import Collection from '../Collection.js';
import Event from '../events/Event.js';
import InteractionProperty from './Property.js';
import PointerInteraction from './Pointer.js';
import {TRUE} from '../functions.js';
import {always} from '../events/condition.js';

/**
 * @enum {string}
 */
const TranslateEventType = {
  /**
   * Triggered upon feature translation start.
   * @event TranslateEvent#translatestart
   * @api
   */
  TRANSLATESTART: 'translatestart',
  /**
   * Triggered upon feature translation.
   * @event TranslateEvent#translating
   * @api
   */
  TRANSLATING: 'translating',
  /**
   * Triggered upon feature translation end.
   * @event TranslateEvent#translateend
   * @api
   */
  TRANSLATEEND: 'translateend',
};

/**
 * A function that takes an {@link module:ol/Feature~Feature} or
 * {@link module:ol/render/Feature~RenderFeature} and an
 * {@link module:ol/layer/Layer~Layer} and returns `true` if the feature may be
 * translated or `false` otherwise.
 * @typedef {function(import("../Feature.js").FeatureLike, import("../layer/Layer.js").default<import("../source/Source").default>):boolean} FilterFunction
 */

/**
 * @typedef {Object} Options
 * @property {import("../events/condition.js").Condition} [condition] A function that
 * takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
 * boolean to indicate whether that event should be handled.
 * Default is {@link module:ol/events/condition.always}.
 * @property {Collection<import("../Feature.js").default>} [features] Features contained in this collection will be able to be translated together.
 * @property {Array<import("../layer/Layer.js").default>|function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean} [layers] A list of layers from which features should be
 * translated. Alternatively, a filter function can be provided. The
 * function will be called for each layer in the map and should return
 * `true` for layers that you want to be translatable. If the option is
 * absent, all visible layers will be considered translatable.
 * Not used if `features` is provided.
 * @property {FilterFunction} [filter] A function
 * that takes an {@link module:ol/Feature~Feature} and an
 * {@link module:ol/layer/Layer~Layer} and returns `true` if the feature may be
 * translated or `false` otherwise. Not used if `features` is provided.
 * @property {number} [hitTolerance=0] Hit-detection tolerance. Pixels inside the radius around the given position
 * will be checked for features.
 */

/**
 * @classdesc
 * Events emitted by {@link module:ol/interaction/Translate~Translate} instances
 * are instances of this type.
 */
export class TranslateEvent extends Event {
  /**
   * @param {TranslateEventType} type Type.
   * @param {Collection<import("../Feature.js").default>} features The features translated.
   * @param {import("../coordinate.js").Coordinate} coordinate The event coordinate.
   * @param {import("../coordinate.js").Coordinate} startCoordinate The original coordinates before.translation started
   * @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
   */
  constructor(type, features, coordinate, startCoordinate, mapBrowserEvent) {
    super(type);

    /**
     * The features being translated.
     * @type {Collection<import("../Feature.js").default>}
     * @api
     */
    this.features = features;

    /**
     * The coordinate of the drag event.
     * @const
     * @type {import("../coordinate.js").Coordinate}
     * @api
     */
    this.coordinate = coordinate;

    /**
     * The coordinate of the start position before translation started.
     * @const
     * @type {import("../coordinate.js").Coordinate}
     * @api
     */
    this.startCoordinate = startCoordinate;

    /**
     * Associated {@link module:ol/MapBrowserEvent~MapBrowserEvent}.
     * @type {import("../MapBrowserEvent.js").default}
     * @api
     */
    this.mapBrowserEvent = mapBrowserEvent;
  }
}

/***
 * @template Return
 * @typedef {import("../Observable").OnSignature<import("../Observable").EventTypes, import("../events/Event.js").default, Return> &
 *   import("../Observable").OnSignature<import("../ObjectEventType").Types|
 *     'change:active', import("../Object").ObjectEvent, Return> &
 *   import("../Observable").OnSignature<'translateend'|'translatestart'|'translating', TranslateEvent, Return> &
 *   import("../Observable").CombinedOnSignature<import("../Observable").EventTypes|import("../ObjectEventType").Types|
 *     'change:active'|'translateend'|'translatestart'|'translating', Return>} TranslateOnSignature
 */

/**
 * @classdesc
 * Interaction for translating (moving) features.
 * If you want to translate multiple features in a single action (for example,
 * the collection used by a select interaction), construct the interaction with
 * the `features` option.
 *
 * @fires TranslateEvent
 * @api
 */
class Translate extends PointerInteraction {
  /**
   * @param {Options} [options] Options.
   */
  constructor(options) {
    options = options ? options : {};

    super(/** @type {import("./Pointer.js").Options} */ (options));

    /***
     * @type {TranslateOnSignature<import("../events").EventsKey>}
     */
    this.on;

    /***
     * @type {TranslateOnSignature<import("../events").EventsKey>}
     */
    this.once;

    /***
     * @type {TranslateOnSignature<void>}
     */
    this.un;

    /**
     * The last position we translated to.
     * @type {import("../coordinate.js").Coordinate}
     * @private
     */
    this.lastCoordinate_ = null;

    /**
     * The start position before translation started.
     * @type {import("../coordinate.js").Coordinate}
     * @private
     */
    this.startCoordinate_ = null;

    /**
     * @type {Collection<import("../Feature.js").default>|null}
     * @private
     */
    this.features_ = options.features !== undefined ? options.features : null;

    /** @type {function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean} */
    let layerFilter;
    if (options.layers && !this.features_) {
      if (typeof options.layers === 'function') {
        layerFilter = options.layers;
      } else {
        const layers = options.layers;
        layerFilter = function (layer) {
          return layers.includes(layer);
        };
      }
    } else {
      layerFilter = TRUE;
    }

    /**
     * @private
     * @type {function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean}
     */
    this.layerFilter_ = layerFilter;

    /**
     * @private
     * @type {FilterFunction}
     */
    this.filter_ = options.filter && !this.features_ ? options.filter : TRUE;

    /**
     * @private
     * @type {number}
     */
    this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;

    /**
     * @private
     * @type {import("../events/condition.js").Condition}
     */
    this.condition_ = options.condition ? options.condition : always;

    /**
     * @type {import("../Feature.js").default}
     * @private
     */
    this.lastFeature_ = null;

    this.addChangeListener(
      InteractionProperty.ACTIVE,
      this.handleActiveChanged_
    );
  }

  /**
   * Handle pointer down events.
   * @param {import("../MapBrowserEvent.js").default} event Event.
   * @return {boolean} If the event was consumed.
   */
  handleDownEvent(event) {
    if (!event.originalEvent || !this.condition_(event)) {
      return false;
    }
    this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map);
    if (!this.lastCoordinate_ && this.lastFeature_) {
      this.startCoordinate_ = event.coordinate;
      this.lastCoordinate_ = event.coordinate;
      this.handleMoveEvent(event);

      const features = this.features_ || new Collection([this.lastFeature_]);

      this.dispatchEvent(
        new TranslateEvent(
          TranslateEventType.TRANSLATESTART,
          features,
          event.coordinate,
          this.startCoordinate_,
          event
        )
      );
      return true;
    }
    return false;
  }

  /**
   * Handle pointer up events.
   * @param {import("../MapBrowserEvent.js").default} event Event.
   * @return {boolean} If the event was consumed.
   */
  handleUpEvent(event) {
    if (this.lastCoordinate_) {
      this.lastCoordinate_ = null;
      this.handleMoveEvent(event);

      const features = this.features_ || new Collection([this.lastFeature_]);

      this.dispatchEvent(
        new TranslateEvent(
          TranslateEventType.TRANSLATEEND,
          features,
          event.coordinate,
          this.startCoordinate_,
          event
        )
      );
      // cleanup
      this.startCoordinate_ = null;
      return true;
    }
    return false;
  }

  /**
   * Handle pointer drag events.
   * @param {import("../MapBrowserEvent.js").default} event Event.
   */
  handleDragEvent(event) {
    if (this.lastCoordinate_) {
      const newCoordinate = event.coordinate;
      const deltaX = newCoordinate[0] - this.lastCoordinate_[0];
      const deltaY = newCoordinate[1] - this.lastCoordinate_[1];

      const features = this.features_ || new Collection([this.lastFeature_]);

      features.forEach(function (feature) {
        const geom = feature.getGeometry();
        geom.translate(deltaX, deltaY);
        feature.setGeometry(geom);
      });

      this.lastCoordinate_ = newCoordinate;

      this.dispatchEvent(
        new TranslateEvent(
          TranslateEventType.TRANSLATING,
          features,
          newCoordinate,
          this.startCoordinate_,
          event
        )
      );
    }
  }

  /**
   * Handle pointer move events.
   * @param {import("../MapBrowserEvent.js").default} event Event.
   */
  handleMoveEvent(event) {
    const elem = event.map.getViewport();

    // Change the cursor to grab/grabbing if hovering any of the features managed
    // by the interaction
    if (this.featuresAtPixel_(event.pixel, event.map)) {
      elem.classList.remove(this.lastCoordinate_ ? 'ol-grab' : 'ol-grabbing');
      elem.classList.add(this.lastCoordinate_ ? 'ol-grabbing' : 'ol-grab');
    } else {
      elem.classList.remove('ol-grab', 'ol-grabbing');
    }
  }

  /**
   * Tests to see if the given coordinates intersects any of our selected
   * features.
   * @param {import("../pixel.js").Pixel} pixel Pixel coordinate to test for intersection.
   * @param {import("../Map.js").default} map Map to test the intersection on.
   * @return {import("../Feature.js").default} Returns the feature found at the specified pixel
   * coordinates.
   * @private
   */
  featuresAtPixel_(pixel, map) {
    return map.forEachFeatureAtPixel(
      pixel,
      (feature, layer) => {
        feature = /** @type {import("../Feature.js").default} */ (feature);
        if (this.filter_(feature, layer)) {
          if (!this.features_ || this.features_.getArray().includes(feature)) {
            return feature;
          }
        }
      },
      {
        layerFilter: this.layerFilter_,
        hitTolerance: this.hitTolerance_,
      }
    );
  }

  /**
   * Returns the Hit-detection tolerance.
   * @return {number} Hit tolerance in pixels.
   * @api
   */
  getHitTolerance() {
    return this.hitTolerance_;
  }

  /**
   * Hit-detection tolerance. Pixels inside the radius around the given position
   * will be checked for features.
   * @param {number} hitTolerance Hit tolerance in pixels.
   * @api
   */
  setHitTolerance(hitTolerance) {
    this.hitTolerance_ = hitTolerance;
  }

  /**
   * Remove the interaction from its current map and attach it to the new map.
   * Subclasses may set up event handlers to get notified about changes to
   * the map here.
   * @param {import("../Map.js").default} map Map.
   */
  setMap(map) {
    const oldMap = this.getMap();
    super.setMap(map);
    this.updateState_(oldMap);
  }

  /**
   * @private
   */
  handleActiveChanged_() {
    this.updateState_(null);
  }

  /**
   * @param {import("../Map.js").default} oldMap Old map.
   * @private
   */
  updateState_(oldMap) {
    let map = this.getMap();
    const active = this.getActive();
    if (!map || !active) {
      map = map || oldMap;
      if (map) {
        const elem = map.getViewport();
        elem.classList.remove('ol-grab', 'ol-grabbing');
      }
    }
  }
}

export default Translate;
<commit_msg>Translate interaction can't work with RenderFeature<commit_after>/**
 * @module ol/interaction/Translate
 */
import Collection from '../Collection.js';
import Event from '../events/Event.js';
import Feature from '../Feature.js';
import InteractionProperty from './Property.js';
import PointerInteraction from './Pointer.js';
import {TRUE} from '../functions.js';
import {always} from '../events/condition.js';

/**
 * @enum {string}
 */
const TranslateEventType = {
  /**
   * Triggered upon feature translation start.
   * @event TranslateEvent#translatestart
   * @api
   */
  TRANSLATESTART: 'translatestart',
  /**
   * Triggered upon feature translation.
   * @event TranslateEvent#translating
   * @api
   */
  TRANSLATING: 'translating',
  /**
   * Triggered upon feature translation end.
   * @event TranslateEvent#translateend
   * @api
   */
  TRANSLATEEND: 'translateend',
};

/**
 * A function that takes an {@link module:ol/Feature~Feature} or
 * {@link module:ol/render/Feature~RenderFeature} and an
 * {@link module:ol/layer/Layer~Layer} and returns `true` if the feature may be
 * translated or `false` otherwise.
 * @typedef {function(Feature, import("../layer/Layer.js").default<import("../source/Source").default>):boolean} FilterFunction
 */

/**
 * @typedef {Object} Options
 * @property {import("../events/condition.js").Condition} [condition] A function that
 * takes an {@link module:ol/MapBrowserEvent~MapBrowserEvent} and returns a
 * boolean to indicate whether that event should be handled.
 * Default is {@link module:ol/events/condition.always}.
 * @property {Collection<Feature>} [features] Features contained in this collection will be able to be translated together.
 * @property {Array<import("../layer/Layer.js").default>|function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean} [layers] A list of layers from which features should be
 * translated. Alternatively, a filter function can be provided. The
 * function will be called for each layer in the map and should return
 * `true` for layers that you want to be translatable. If the option is
 * absent, all visible layers will be considered translatable.
 * Not used if `features` is provided.
 * @property {FilterFunction} [filter] A function
 * that takes an {@link module:ol/Feature~Feature} and an
 * {@link module:ol/layer/Layer~Layer} and returns `true` if the feature may be
 * translated or `false` otherwise. Not used if `features` is provided.
 * @property {number} [hitTolerance=0] Hit-detection tolerance. Pixels inside the radius around the given position
 * will be checked for features.
 */

/**
 * @classdesc
 * Events emitted by {@link module:ol/interaction/Translate~Translate} instances
 * are instances of this type.
 */
export class TranslateEvent extends Event {
  /**
   * @param {TranslateEventType} type Type.
   * @param {Collection<Feature>} features The features translated.
   * @param {import("../coordinate.js").Coordinate} coordinate The event coordinate.
   * @param {import("../coordinate.js").Coordinate} startCoordinate The original coordinates before.translation started
   * @param {import("../MapBrowserEvent.js").default} mapBrowserEvent Map browser event.
   */
  constructor(type, features, coordinate, startCoordinate, mapBrowserEvent) {
    super(type);

    /**
     * The features being translated.
     * @type {Collection<Feature>}
     * @api
     */
    this.features = features;

    /**
     * The coordinate of the drag event.
     * @const
     * @type {import("../coordinate.js").Coordinate}
     * @api
     */
    this.coordinate = coordinate;

    /**
     * The coordinate of the start position before translation started.
     * @const
     * @type {import("../coordinate.js").Coordinate}
     * @api
     */
    this.startCoordinate = startCoordinate;

    /**
     * Associated {@link module:ol/MapBrowserEvent~MapBrowserEvent}.
     * @type {import("../MapBrowserEvent.js").default}
     * @api
     */
    this.mapBrowserEvent = mapBrowserEvent;
  }
}

/***
 * @template Return
 * @typedef {import("../Observable").OnSignature<import("../Observable").EventTypes, import("../events/Event.js").default, Return> &
 *   import("../Observable").OnSignature<import("../ObjectEventType").Types|
 *     'change:active', import("../Object").ObjectEvent, Return> &
 *   import("../Observable").OnSignature<'translateend'|'translatestart'|'translating', TranslateEvent, Return> &
 *   import("../Observable").CombinedOnSignature<import("../Observable").EventTypes|import("../ObjectEventType").Types|
 *     'change:active'|'translateend'|'translatestart'|'translating', Return>} TranslateOnSignature
 */

/**
 * @classdesc
 * Interaction for translating (moving) features.
 * If you want to translate multiple features in a single action (for example,
 * the collection used by a select interaction), construct the interaction with
 * the `features` option.
 *
 * @fires TranslateEvent
 * @api
 */
class Translate extends PointerInteraction {
  /**
   * @param {Options} [options] Options.
   */
  constructor(options) {
    options = options ? options : {};

    super(/** @type {import("./Pointer.js").Options} */ (options));

    /***
     * @type {TranslateOnSignature<import("../events").EventsKey>}
     */
    this.on;

    /***
     * @type {TranslateOnSignature<import("../events").EventsKey>}
     */
    this.once;

    /***
     * @type {TranslateOnSignature<void>}
     */
    this.un;

    /**
     * The last position we translated to.
     * @type {import("../coordinate.js").Coordinate}
     * @private
     */
    this.lastCoordinate_ = null;

    /**
     * The start position before translation started.
     * @type {import("../coordinate.js").Coordinate}
     * @private
     */
    this.startCoordinate_ = null;

    /**
     * @type {Collection<Feature>|null}
     * @private
     */
    this.features_ = options.features !== undefined ? options.features : null;

    /** @type {function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean} */
    let layerFilter;
    if (options.layers && !this.features_) {
      if (typeof options.layers === 'function') {
        layerFilter = options.layers;
      } else {
        const layers = options.layers;
        layerFilter = function (layer) {
          return layers.includes(layer);
        };
      }
    } else {
      layerFilter = TRUE;
    }

    /**
     * @private
     * @type {function(import("../layer/Layer.js").default<import("../source/Source").default>): boolean}
     */
    this.layerFilter_ = layerFilter;

    /**
     * @private
     * @type {FilterFunction}
     */
    this.filter_ = options.filter && !this.features_ ? options.filter : TRUE;

    /**
     * @private
     * @type {number}
     */
    this.hitTolerance_ = options.hitTolerance ? options.hitTolerance : 0;

    /**
     * @private
     * @type {import("../events/condition.js").Condition}
     */
    this.condition_ = options.condition ? options.condition : always;

    /**
     * @type {Feature}
     * @private
     */
    this.lastFeature_ = null;

    this.addChangeListener(
      InteractionProperty.ACTIVE,
      this.handleActiveChanged_
    );
  }

  /**
   * Handle pointer down events.
   * @param {import("../MapBrowserEvent.js").default} event Event.
   * @return {boolean} If the event was consumed.
   */
  handleDownEvent(event) {
    if (!event.originalEvent || !this.condition_(event)) {
      return false;
    }
    this.lastFeature_ = this.featuresAtPixel_(event.pixel, event.map);
    if (!this.lastCoordinate_ && this.lastFeature_) {
      this.startCoordinate_ = event.coordinate;
      this.lastCoordinate_ = event.coordinate;
      this.handleMoveEvent(event);

      const features = this.features_ || new Collection([this.lastFeature_]);

      this.dispatchEvent(
        new TranslateEvent(
          TranslateEventType.TRANSLATESTART,
          features,
          event.coordinate,
          this.startCoordinate_,
          event
        )
      );
      return true;
    }
    return false;
  }

  /**
   * Handle pointer up events.
   * @param {import("../MapBrowserEvent.js").default} event Event.
   * @return {boolean} If the event was consumed.
   */
  handleUpEvent(event) {
    if (this.lastCoordinate_) {
      this.lastCoordinate_ = null;
      this.handleMoveEvent(event);

      const features = this.features_ || new Collection([this.lastFeature_]);

      this.dispatchEvent(
        new TranslateEvent(
          TranslateEventType.TRANSLATEEND,
          features,
          event.coordinate,
          this.startCoordinate_,
          event
        )
      );
      // cleanup
      this.startCoordinate_ = null;
      return true;
    }
    return false;
  }

  /**
   * Handle pointer drag events.
   * @param {import("../MapBrowserEvent.js").default} event Event.
   */
  handleDragEvent(event) {
    if (this.lastCoordinate_) {
      const newCoordinate = event.coordinate;
      const deltaX = newCoordinate[0] - this.lastCoordinate_[0];
      const deltaY = newCoordinate[1] - this.lastCoordinate_[1];

      const features = this.features_ || new Collection([this.lastFeature_]);

      features.forEach(function (feature) {
        const geom = feature.getGeometry();
        geom.translate(deltaX, deltaY);
        feature.setGeometry(geom);
      });

      this.lastCoordinate_ = newCoordinate;

      this.dispatchEvent(
        new TranslateEvent(
          TranslateEventType.TRANSLATING,
          features,
          newCoordinate,
          this.startCoordinate_,
          event
        )
      );
    }
  }

  /**
   * Handle pointer move events.
   * @param {import("../MapBrowserEvent.js").default} event Event.
   */
  handleMoveEvent(event) {
    const elem = event.map.getViewport();

    // Change the cursor to grab/grabbing if hovering any of the features managed
    // by the interaction
    if (this.featuresAtPixel_(event.pixel, event.map)) {
      elem.classList.remove(this.lastCoordinate_ ? 'ol-grab' : 'ol-grabbing');
      elem.classList.add(this.lastCoordinate_ ? 'ol-grabbing' : 'ol-grab');
    } else {
      elem.classList.remove('ol-grab', 'ol-grabbing');
    }
  }

  /**
   * Tests to see if the given coordinates intersects any of our selected
   * features.
   * @param {import("../pixel.js").Pixel} pixel Pixel coordinate to test for intersection.
   * @param {import("../Map.js").default} map Map to test the intersection on.
   * @return {Feature} Returns the feature found at the specified pixel
   * coordinates.
   * @private
   */
  featuresAtPixel_(pixel, map) {
    return map.forEachFeatureAtPixel(
      pixel,
      (feature, layer) => {
        if (!(feature instanceof Feature) || !this.filter_(feature, layer)) {
          return undefined;
        }
        if (this.features_ && !this.features_.getArray().includes(feature)) {
          return undefined;
        }
        return feature;
      },
      {
        layerFilter: this.layerFilter_,
        hitTolerance: this.hitTolerance_,
      }
    );
  }

  /**
   * Returns the Hit-detection tolerance.
   * @return {number} Hit tolerance in pixels.
   * @api
   */
  getHitTolerance() {
    return this.hitTolerance_;
  }

  /**
   * Hit-detection tolerance. Pixels inside the radius around the given position
   * will be checked for features.
   * @param {number} hitTolerance Hit tolerance in pixels.
   * @api
   */
  setHitTolerance(hitTolerance) {
    this.hitTolerance_ = hitTolerance;
  }

  /**
   * Remove the interaction from its current map and attach it to the new map.
   * Subclasses may set up event handlers to get notified about changes to
   * the map here.
   * @param {import("../Map.js").default} map Map.
   */
  setMap(map) {
    const oldMap = this.getMap();
    super.setMap(map);
    this.updateState_(oldMap);
  }

  /**
   * @private
   */
  handleActiveChanged_() {
    this.updateState_(null);
  }

  /**
   * @param {import("../Map.js").default} oldMap Old map.
   * @private
   */
  updateState_(oldMap) {
    let map = this.getMap();
    const active = this.getActive();
    if (!map || !active) {
      map = map || oldMap;
      if (map) {
        const elem = map.getViewport();
        elem.classList.remove('ol-grab', 'ol-grabbing');
      }
    }
  }
}

export default Translate;
<|endoftext|>


<commit_before>/**
 * @author mrdoob / http://mrdoob.com/
 */

function WebGLAttributes( gl ) {

	var buffers = {};

	function createBuffer( attribute, bufferType ) {

		var array = attribute.array;
		var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;

		var buffer = gl.createBuffer();

		gl.bindBuffer( bufferType, buffer );
		gl.bufferData( bufferType, array, usage );

		attribute.onUploadCallback();

		var type = gl.FLOAT;

		if ( array instanceof Float32Array ) {

			type = gl.FLOAT;

		} else if ( array instanceof Float64Array ) {

			console.warn( "Unsupported data buffer format: Float64Array" );

		} else if ( array instanceof Uint16Array ) {

			type = gl.UNSIGNED_SHORT;

		} else if ( array instanceof Int16Array ) {

			type = gl.SHORT;

		} else if ( array instanceof Uint32Array ) {

			type = gl.UNSIGNED_INT;

		} else if ( array instanceof Int32Array ) {

			type = gl.INT;

		} else if ( array instanceof Int8Array ) {

			type = gl.BYTE;

		} else if ( array instanceof Uint8Array ) {

			type = gl.UNSIGNED_BYTE;

		}

		return {
			buffer: buffer,
			type: type,
			bytesPerElement: array.BYTES_PER_ELEMENT,
			version: attribute.version
		};

	}

	function updateBuffer( buffer, attribute, bufferType ) {

		var array = attribute.array;
		var updateRange = attribute.updateRange;

		gl.bindBuffer( bufferType, buffer );

		if ( attribute.dynamic === false ) {

			gl.bufferData( bufferType, array, gl.STATIC_DRAW );

		} else if ( updateRange.count === - 1 ) {

			// Not using update ranges

			gl.bufferSubData( bufferType, 0, array );

		} else if ( updateRange.count === 0 ) {

			console.error( 'THREE.WebGLObjects.updateBuffer: dynamic THREE.BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.' );

		} else {

			gl.bufferSubData( bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,
				array.subarray( updateRange.offset, updateRange.offset + updateRange.count ) );

			updateRange.count = 0; // reset range

		}

	}

	//

	function get( attribute ) {

		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;

		return buffers[ attribute.uuid ];

	}

	function remove( attribute ) {

		var data = buffers[ attribute.uuid ];

		if ( data ) {

			gl.deleteBuffer( data.buffer );

			delete buffers[ attribute.uuid ];

		}

	}

	function update( attribute, bufferType ) {

		if ( attribute.isInterleavedBufferAttribute ) attribute = attribute.data;

		var data = buffers[ attribute.uuid ];

		if ( data === undefined ) {

			buffers[ attribute.uuid ] = createBuffer( attribute, bufferType );

		} else if ( data.version < attribute.version ) {

			updateBuffer( data.buffer, attribute, bufferType );

			data.version = attribute.version;

		}

	}

	return {

		get: get,
		remove: remove,
		update: update

	};

}

export { WebGLAttributes };
<commit_msg>Reviewed WebGLAttributes<commit_after>
// TODO:
// - save buffers when WebGL crashes (_glVersion)
// - buffers should be stored in the objects themselves, not here
// - no more UUID

function WebGLAttributes(gl) {
    var buffers = {};

    function createBuffer(attribute, bufferType) {
        var array = attribute.array;
        var usage = attribute.dynamic ? gl.DYNAMIC_DRAW : gl.STATIC_DRAW;

        var buffer = gl.createBuffer();

        gl.bindBuffer(bufferType, buffer);
        gl.bufferData(bufferType, array, usage);

        attribute.onUploadCallback(); // TODO: Should we keep that?

        var type = gl.FLOAT;

        if (array instanceof Float32Array) {
            type = gl.FLOAT;
        } else if (array instanceof Float64Array) {
            console.warn('Unsupported data buffer format: Float64Array');
        } else if (array instanceof Uint16Array) {
            type = gl.UNSIGNED_SHORT;
        } else if (array instanceof Int16Array) {
            type = gl.SHORT;
        } else if (array instanceof Uint32Array) {
            type = gl.UNSIGNED_INT;
        } else if (array instanceof Int32Array) {
            type = gl.INT;
        } else if (array instanceof Int8Array) {
            type = gl.BYTE;
        } else if (array instanceof Uint8Array) {
            type = gl.UNSIGNED_BYTE;
        }

        return {
            buffer: buffer,
            type: type,
            bytesPerElement: array.BYTES_PER_ELEMENT,
            version: attribute.version
        };
    }

    function updateBuffer(buffer, attribute, bufferType) {
        var array = attribute.array;
        var updateRange = attribute.updateRange;

        gl.bindBuffer(bufferType, buffer);

        if (attribute.dynamic === false) {
            gl.bufferData(bufferType, array, gl.STATIC_DRAW);
        } else if (updateRange.count === -1) {
            // Not using update ranges
            gl.bufferSubData(bufferType, 0, array);
        } else if (updateRange.count === 0) {
            console.error('WebGLObjects.updateBuffer: dynamic BufferAttribute marked as needsUpdate but updateRange.count is 0, ensure you are using set methods or updating manually.');
        } else {
            gl.bufferSubData(bufferType, updateRange.offset * array.BYTES_PER_ELEMENT,
                array.subarray(updateRange.offset, updateRange.offset + updateRange.count));

            updateRange.count = 0; // reset range
        }
    }

    //

    function get(attribute) {
        if (attribute.isInterleavedBufferAttribute) attribute = attribute.data;
        return buffers[attribute.uuid];
    }

    function remove(attribute) {
        var data = buffers[attribute.uuid];
        if (data) {
            gl.deleteBuffer(data.buffer);
            delete buffers[attribute.uuid];
        }
    }

    function update(attribute, bufferType) {
        if (attribute.isInterleavedBufferAttribute) attribute = attribute.data;

        var data = buffers[attribute.uuid];

        if (data === undefined) {
            buffers[attribute.uuid] = createBuffer(attribute, bufferType);
        } else if (data.version < attribute.version) {
            updateBuffer(data.buffer, attribute, bufferType);
            data.version = attribute.version;
        }
    }

    return {
        get: get,
        remove: remove,
        update: update
    };
}

export { WebGLAttributes };
<|endoftext|>


<commit_before>/*
	The MIT License (MIT)

	Copyright (c) 2013 Fernando Bevilacqua

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to
	use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
	the Software, and to permit persons to whom the Software is furnished to do so,
	subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
	FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
	COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
	IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
	CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

var CodebotFilesPanel = function() {
    var mUI = null;
    var mIO = null;
    var mSelf = null;
    var mFocusedNode = null;
    var mRootNode = null;
    var mTree = null;

    var onClick = function(theEvent, theItem) {
        // If the currently focused node has the rename form active,
        // close it and finish the renaming process.
        if(mFocusedNode && mFocusedNode.node.isEditing()) {
            mFocusedNode.node.endEdit();
        }
        mFocusedNode = theItem;
        console.debug('FilesPanel.click() ', theEvent, theItem);
	};

	var onDoubleClick = function(theEvent, theItem) {
		var aNode = theItem.node;

		console.debug('File panel double click: ' + theEvent + ' , folder: ' + aNode.folder);

		if(!aNode.folder) {
            openNodeInTab(aNode);
		}
	};

    var openNodeInTab = function(theNode) {
        var aEditorPrefs = {};
        var aTab = null;

        $.extend(aEditorPrefs, CODEBOT.getPrefs().editor);

        aEditorPrefs.mode        = 'javascript', // TODO: dynamic mode?
        aEditorPrefs.value       = '';
        aEditorPrefs.autofocus   = true;

        // Create a new tab based on node data
        mIO.readFile(theNode.data, function(theData) {
            aEditorPrefs.value = theData;

            aTab = mUI.tabs.add({
                favicon: 'file-text-o', // TODO: dynamic icon?
                title: theNode.data.name,
                file: theNode.data.name,
                path: theNode.data.path,
                editor: null
                //TODO: add "entry: theNodeData.entry" for Chrome Packaged Apps
            });

            aTab.editor = CodeMirror(document.getElementById(aTab.container), aEditorPrefs);
        });
    };

    var onDragStart = function(theNode, theDragData) {
        /** This function MUST be defined to enable dragging for the tree.
         *  Return false to cancel dragging of node.
         */
        return true;
    };

    var onDragEnter = function(theDestinationNode, theDragData) {
        /** data.otherNode may be null for non-fancytree droppables.
         *  Return false to disallow dropping on node. In this case
         *  dragOver and dragLeave are not called.
         *  Return 'over', 'before, or 'after' to force a hitMode.
         *  Return ['before', 'after'] to restrict available hitModes.
         *  Any other return value will calc the hitMode from the cursor position.
         */
        // Prevent dropping a parent below another parent (only sort
        // nodes under the same parent)
        //if(node.parent !== data.otherNode.parent){
        //    return false;
        //}
        // Don't allow dropping *over* a node (would create a child)
        //return ["before", "after"];

        if(!theDestinationNode.folder) {
            return ['after', 'before'];

        } else {
            return true;
        }
    };

    /**
     * Called when a node is dropped in the files panel.
     *
     * @theDestinationNode the node used to guide the dragging process.
     * @theDragData An object containing information regarding the dragging process, e.g. node being dragged, destination, hit strategy.
     */
    var onDragDrop = function(theDestinationNode, theDragData) {
        var aNodeBeingDragged = theDragData.otherNode;

        console.debug('Drag and drop event', theDragData);

        aNodeBeingDragged.moveTo(theDestinationNode, theDragData.hitMode);

        var aOldPath = aNodeBeingDragged.data;
        var aNewPath = null;

        if(theDragData.hitMode == "over") {
            // Dragging node into a folder. In that case, the destination node (the folder)
            // already has a nice path to be used. e.g. /proj/test/folder/
            aNewPath = theDestinationNode.data.path;

        } else {
            // Dragging node after or before another node. In that case, we need to get the
            // path to this neighbour file, removing the file name.
            aNewPath = CODEBOT.utils.dirName(theDestinationNode.data.path);
        }

        aNewPath += aNodeBeingDragged.data.name;

        // TODO: only move the UI item when the IO opperation informs everything went ok.
        // TODO: fix the {path: } because it breaks IO layer.
        mIO.move(aOldPath, {path: aNewPath}, function(theError) {
            if(theError) {
                console.log('Problem with move!');
                // TODO: warn about error and reload tree.
            }
        });
    };

    var onRename = function(theEvent, theData) {
        var aNode    = theData.node;
        var aNewName = theData.value;

        if(aNewName != aNode.data.name) {
            var aNewNode = {path: CODEBOT.utils.dirName(aNode.data.path, aNode.folder ? 3 : 1) + aNewName}; // TODO: fix this, it breaks IO layer.

            mIO.move(aNode.data, aNewNode, function(theError) {
                if(theError) {
                    console.log('Problem with rename/move!');
                    // TODO: warn about error and reload tree.
                }
            });

            // TODO: update open tab containing the renamed node.
        }
    };

    var onContexAction = function(theNode, theAction, theOptions) {
        console.debug('Selected action "' + theAction + '" on node ', theNode, theNode.data);

        switch(theAction) {
            case 'new-folder':
                var aName = prompt('Directory name');
                mIO.createDirectory(aName, theNode.data, function(theInfo) {
                    if(theInfo instanceof Error) {
                        console.error('Problem with createDirectory!' + theInfo);
                    } else {
                        mSelf.refreshTree();
                    }
                });
                break;
            case 'new-file':
                var aName = prompt('File name');
                mIO.createFile(aName, theNode.data, '', function(theInfo) {
                    if(theInfo instanceof Error) {
                        console.error('Problem with createFile!' + theInfo);
                    } else {
                        mSelf.refreshTree();
                        // TODO: open node in new tab
                    }
                });
                break;
            case 'rename':
                theNode.startEdit();
                break;
        }
    };

    var initTree = function() {
        $("#folders").fancytree({
            extensions: ['dnd', 'edit', 'awesome', 'contextMenu'],
            click: onClick,
            dblclick: onDoubleClick,
            source: [],
            checkbox: false,
            selectMode: 3,
            debugLevel: 0,

            dnd: {
                preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
                preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
                autoExpandMS: 400,
                dragStart: onDragStart,
                dragEnter: onDragEnter,
                dragDrop: onDragDrop
            },
            edit: {
                save: onRename,
                triggerCancel: ['esc', 'tab', 'click'],
                triggerStart: ['f2']
            },
            contextMenu: {
                menu: {
                    'edit': { 'name': 'Edit', 'icon': 'edit' },
                    'rename': { 'name': 'Rename', 'icon': 'rename' },
                    'delete': { 'name': 'Delete', 'icon': 'delete', 'disabled': true },
                    'sep1': '---------',
                    'new': {
                        'name': 'New',
                        'items': {
                            'new-folder': { 'name': 'Folder' },
                            'new-file': { 'name': 'File' }
                        }
                    }
                },
                actions: onContexAction
            }
        });

        mTree = $("#folders").fancytree("getTree");
	};

    this.renameFocusedNode = function() {
        if(mFocusedNode) {
            mFocusedNode.node.startEdit();
            console.log('Open rename panel', mFocusedNode);
        }
    };

    this.refreshTree = function() {
        mIO.readDirectory(mRootNode, mSelf.populateTree);
    }

    this.populateTree = function(theNodes) {
        if(theNodes && theNodes.length > 0) {
            mTree.reload(theNodes);
            console.debug('Tree has been populated.');
        }
    };

    this.init = function(theUI, theIO) {
        mUI = theUI;
        mIO = theIO;
        mSelf = this;

        initTree();

        $('#files-panel header a').on('click', function() {
            mIO.chooseDirectory(function(theNode) {
                mRootNode = theNode;
                mIO.readDirectory(theNode, mSelf.populateTree);
            });
        });
    };
};<commit_msg>Expand folders with click<commit_after>/*
	The MIT License (MIT)

	Copyright (c) 2013 Fernando Bevilacqua

	Permission is hereby granted, free of charge, to any person obtaining a copy of
	this software and associated documentation files (the "Software"), to deal in
	the Software without restriction, including without limitation the rights to
	use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
	the Software, and to permit persons to whom the Software is furnished to do so,
	subject to the following conditions:

	The above copyright notice and this permission notice shall be included in all
	copies or substantial portions of the Software.

	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
	IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
	FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
	COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
	IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
	CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

var CodebotFilesPanel = function() {
    var mUI = null;
    var mIO = null;
    var mSelf = null;
    var mFocusedNode = null;
    var mRootNode = null;
    var mTree = null;

    var onClick = function(theEvent, theItem) {
        // If the currently focused node has the rename form active,
        // close it and finish the renaming process.
        if(mFocusedNode && mFocusedNode.node.isEditing()) {
            mFocusedNode.node.endEdit();
        }
        mFocusedNode = theItem;

        if(mFocusedNode.node.folder) {
            mFocusedNode.node.toggleExpanded();
        }

        console.debug('FilesPanel.click() ', theEvent, theItem);
	};

	var onDoubleClick = function(theEvent, theItem) {
		var aNode = theItem.node;

		console.debug('File panel double click: ' + theEvent + ' , folder: ' + aNode.folder);

		if(!aNode.folder) {
            openNodeInTab(aNode);
		}
	};

    var openNodeInTab = function(theNode) {
        var aEditorPrefs = {};
        var aTab = null;

        $.extend(aEditorPrefs, CODEBOT.getPrefs().editor);

        aEditorPrefs.mode        = 'javascript', // TODO: dynamic mode?
        aEditorPrefs.value       = '';
        aEditorPrefs.autofocus   = true;

        // Create a new tab based on node data
        mIO.readFile(theNode.data, function(theData) {
            aEditorPrefs.value = theData;

            aTab = mUI.tabs.add({
                favicon: 'file-text-o', // TODO: dynamic icon?
                title: theNode.data.name,
                file: theNode.data.name,
                path: theNode.data.path,
                editor: null
                //TODO: add "entry: theNodeData.entry" for Chrome Packaged Apps
            });

            aTab.editor = CodeMirror(document.getElementById(aTab.container), aEditorPrefs);
        });
    };

    var onDragStart = function(theNode, theDragData) {
        /** This function MUST be defined to enable dragging for the tree.
         *  Return false to cancel dragging of node.
         */
        return true;
    };

    var onDragEnter = function(theDestinationNode, theDragData) {
        /** data.otherNode may be null for non-fancytree droppables.
         *  Return false to disallow dropping on node. In this case
         *  dragOver and dragLeave are not called.
         *  Return 'over', 'before, or 'after' to force a hitMode.
         *  Return ['before', 'after'] to restrict available hitModes.
         *  Any other return value will calc the hitMode from the cursor position.
         */
        // Prevent dropping a parent below another parent (only sort
        // nodes under the same parent)
        //if(node.parent !== data.otherNode.parent){
        //    return false;
        //}
        // Don't allow dropping *over* a node (would create a child)
        //return ["before", "after"];

        if(!theDestinationNode.folder) {
            return ['after', 'before'];

        } else {
            return true;
        }
    };

    /**
     * Called when a node is dropped in the files panel.
     *
     * @theDestinationNode the node used to guide the dragging process.
     * @theDragData An object containing information regarding the dragging process, e.g. node being dragged, destination, hit strategy.
     */
    var onDragDrop = function(theDestinationNode, theDragData) {
        var aNodeBeingDragged = theDragData.otherNode;

        console.debug('Drag and drop event', theDragData);

        aNodeBeingDragged.moveTo(theDestinationNode, theDragData.hitMode);

        var aOldPath = aNodeBeingDragged.data;
        var aNewPath = null;

        if(theDragData.hitMode == "over") {
            // Dragging node into a folder. In that case, the destination node (the folder)
            // already has a nice path to be used. e.g. /proj/test/folder/
            aNewPath = theDestinationNode.data.path;

        } else {
            // Dragging node after or before another node. In that case, we need to get the
            // path to this neighbour file, removing the file name.
            aNewPath = CODEBOT.utils.dirName(theDestinationNode.data.path);
        }

        aNewPath += aNodeBeingDragged.data.name;

        // TODO: only move the UI item when the IO opperation informs everything went ok.
        // TODO: fix the {path: } because it breaks IO layer.
        mIO.move(aOldPath, {path: aNewPath}, function(theError) {
            if(theError) {
                console.log('Problem with move!');
                // TODO: warn about error and reload tree.
            }
        });
    };

    var onRename = function(theEvent, theData) {
        var aNode    = theData.node;
        var aNewName = theData.value;

        if(aNewName != aNode.data.name) {
            var aNewNode = {path: CODEBOT.utils.dirName(aNode.data.path, aNode.folder ? 3 : 1) + aNewName}; // TODO: fix this, it breaks IO layer.

            mIO.move(aNode.data, aNewNode, function(theError) {
                if(theError) {
                    console.log('Problem with rename/move!');
                    // TODO: warn about error and reload tree.
                }
            });

            // TODO: update open tab containing the renamed node.
        }
    };

    var onContexAction = function(theNode, theAction, theOptions) {
        console.debug('Selected action "' + theAction + '" on node ', theNode, theNode.data);

        switch(theAction) {
            case 'new-folder':
                var aName = prompt('Directory name');
                mIO.createDirectory(aName, theNode.data, function(theInfo) {
                    if(theInfo instanceof Error) {
                        console.error('Problem with createDirectory!' + theInfo);
                    } else {
                        mSelf.refreshTree();
                    }
                });
                break;
            case 'new-file':
                var aName = prompt('File name');
                mIO.createFile(aName, theNode.data, '', function(theInfo) {
                    if(theInfo instanceof Error) {
                        console.error('Problem with createFile!' + theInfo);
                    } else {
                        mSelf.refreshTree();
                        // TODO: open node in new tab
                    }
                });
                break;
            case 'rename':
                theNode.startEdit();
                break;
        }
    };

    var initTree = function() {
        $("#folders").fancytree({
            extensions: ['dnd', 'edit', 'awesome', 'contextMenu'],
            click: onClick,
            dblclick: onDoubleClick,
            source: [],
            checkbox: false,
            selectMode: 3,
            debugLevel: 0,

            dnd: {
                preventVoidMoves: true, // Prevent dropping nodes 'before self', etc.
                preventRecursiveMoves: true, // Prevent dropping nodes on own descendants
                autoExpandMS: 400,
                dragStart: onDragStart,
                dragEnter: onDragEnter,
                dragDrop: onDragDrop
            },
            edit: {
                save: onRename,
                triggerCancel: ['esc', 'tab', 'click'],
                triggerStart: ['f2']
            },
            contextMenu: {
                menu: {
                    'edit': { 'name': 'Edit', 'icon': 'edit' },
                    'rename': { 'name': 'Rename', 'icon': 'rename' },
                    'delete': { 'name': 'Delete', 'icon': 'delete', 'disabled': true },
                    'sep1': '---------',
                    'new': {
                        'name': 'New',
                        'items': {
                            'new-folder': { 'name': 'Folder' },
                            'new-file': { 'name': 'File' }
                        }
                    }
                },
                actions: onContexAction
            }
        });

        mTree = $("#folders").fancytree("getTree");
	};

    this.renameFocusedNode = function() {
        if(mFocusedNode) {
            mFocusedNode.node.startEdit();
            console.log('Open rename panel', mFocusedNode);
        }
    };

    this.refreshTree = function() {
        mIO.readDirectory(mRootNode, mSelf.populateTree);
    }

    this.populateTree = function(theNodes) {
        if(theNodes && theNodes.length > 0) {
            mTree.reload(theNodes);
            console.debug('Tree has been populated.');
        }
    };

    this.init = function(theUI, theIO) {
        mUI = theUI;
        mIO = theIO;
        mSelf = this;

        initTree();

        $('#files-panel header a').on('click', function() {
            mIO.chooseDirectory(function(theNode) {
                mRootNode = theNode;
                mIO.readDirectory(theNode, mSelf.populateTree);
            });
        });
    };
};<|endoftext|>