<commit_before>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var sys = require('sys');
var path = require('path');
var fs = require('fs');
var exec = require('child_process').exec;
var constants = require('constants');

var async = require('async');
var sprintf = require('sprintf').sprintf;
var swiz = require('swiz');

var fsutil = require('util/fs');
var jobs = require('jobs');

var TEST_RESOURCE_ROOT = '.tests/testresource';

var defs = {
  TestResource: [
    ['name', {
      src: 'name',
      type: 'string'
    }],
    ['data', {
      src: 'getDataText',
      type: 'string'
    }]
  ]
};

function TestResource(name) {
  jobs.DirectoryResource.call(this, name);
  this._serializer = new swiz.Swiz(defs);
}

sys.inherits(TestResource, jobs.DirectoryResource);

TestResource.prototype.getParentDir = function() {
  return TEST_RESOURCE_ROOT;
};

TestResource.prototype.getDataPath = function() {
  return path.join(this.getRoot(), 'data.txt');
};

TestResource.prototype.getDataText = function(callback) {
  fs.readFile(this.getDataPath(), 'utf8', callback);
};

function TestResourceManager() {
  jobs.ResourceManager.call(this);
  this.resourceType = TestResource;
}

sys.inherits(TestResourceManager, jobs.ResourceManager);

function CreateTestResourceJob(name) {
  jobs.Job.call(this);
  this.options = jobs.JobOptions.CREATE;
  this.resourceType = TestResource;
  this.resourceName = name;
}

sys.inherits(CreateTestResourceJob, jobs.Job);

CreateTestResourceJob.prototype.run = function(testResource, callback) {
  var dataPath = testResource.getDataPath();
  var dataText = sprintf('this is %s', testResource.name);

  function createRoot(callback) {
    fs.mkdir(testResource.getRoot(), 0755, callback);
  }

  function writeData(callback) {
    fs.writeFile(dataPath, dataText, callback);
  }

  async.series([createRoot, writeData], callback);
};

function FailCreateTestResourceJob(name) {
  CreateTestResourceJob.call(this, name);
}

sys.inherits(FailCreateTestResourceJob, CreateTestResourceJob);

FailCreateTestResourceJob.prototype.run = function(testResource, callback) {
  callback(new Error('unable to create test resource'));
};

function DeleteTestResourceJob(name) {
  jobs.Job.call(this);
  this.options = jobs.JobOptions.DELETE;
  this.resourceType = TestResource;
  this.resourceName = name;
}

sys.inherits(DeleteTestResourceJob, jobs.Job);

DeleteTestResourceJob.prototype.run = function(testResource, callback) {
  fsutil.rmtree(testResource.getRoot(), callback);
};

function ModifyTestResourceJob(name, text) {
  jobs.Job.call(this, [text]);
  this.options = jobs.JobOptions.UPDATE;
  this.resourceType = TestResource;
  this.resourceName = name;
}

sys.inherits(ModifyTestResourceJob, jobs.Job);

ModifyTestResourceJob.prototype.run = function(testResource, text, callback) {
  var ws = fs.createWriteStream(testResource.getDataPath(), {flags: 'a'});
  ws.once('error', callback);
  ws.once('close', callback);

  ws.end(text, 'utf8');
};

exports['test_directory_resource_queueing'] = function(test, assert) {
  var jobManager = new jobs.JobManager();
  var trManager = new TestResourceManager();
  jobManager.registerResourceManager(trManager);

  var testsComplete = 0;

  async.series([
    // Create the TestResource root
    function(callback) {
      exec(sprintf('mkdir -p "%s"', TEST_RESOURCE_ROOT), callback);
    },

    // Attempt to retrieve a nonexistant resource
    function(callback) {
      trManager.get('foo', function(err, result) {
        assert.ok(err);
        assert.equal(err.message, 'TestResource \'foo\' does not exist.');
        assert.equal(result, undefined);
        callback();
      });
    },

    // Attempt to getLive a nonexistant resource
    function(callback) {
      trManager.getLive('foo', function(err, foo) {
        assert.ok(err);
        assert.ok(err instanceof jobs.NotFoundError);
        assert.equal(foo, undefined);
        callback();
      });
    },

    // List all resources (should be none).
    function(callback) {
      trManager.list(function(err, results) {
        assert.ok(!err);
        assert.deepEqual(results, []);
        process.nextTick(function() {
          assert.deepEqual(trManager.resources, {});
          callback();
        });
      });
    },

    // Attempt to run an UPDATE job on a nonexistant resource
    function(callback) {
      var j = new ModifyTestResourceJob('foo', ' testing');

      j.on('ready', function badJobReady() {
        throw new Error('Job should not have reached \'ready\' state.');
      });

      j.on('success', function badJobSuccess() {
        throw new Error('Job should not have reached \'success\' state.');
      });

      j.on('error', function badJobError(err) {
        assert.ok(err);
        assert.match(err.message, /TestResource \'foo\' does not exist/);
        testsComplete++;
        process.nextTick(function() {
          assert.ok(!trManager.resources['foo']);
          callback();
        });
      });

      // Run this job against TestResource 'foo'
      jobManager.run(j);
      jobManager.getJob(j.id, function(err, job) {
        assert.ifError(err);
        assert.equal(j, job);
      });
      assert.ok(trManager.resources['foo']);
    },

    // Attempt to retrieve a nonexistant resource
    function(callback) {
      trManager.get('foo', function(err, result) {
        assert.ok(err);
        assert.equal(err.message, 'TestResource \'foo\' does not exist.');
        assert.equal(result, undefined);
        callback();
      });
    },

    // List all resources (should be none).
    function(callback) {
      trManager.list(function(err, results) {
        assert.ok(!err);
        assert.deepEqual(results, []);
        process.nextTick(function() {
          assert.deepEqual(trManager.resources, {});
          callback();
        });
      });
    },

    // Queue a create then a bunch of updates on nonexistant resource
    function(callback) {
      testJobs = [
        new CreateTestResourceJob('bar'),
        new ModifyTestResourceJob('bar', ' test0'),
        new ModifyTestResourceJob('bar', ' test1'),
        new ModifyTestResourceJob('bar', ' test2'),
        new ModifyTestResourceJob('bar', ' test3'),
        new ModifyTestResourceJob('bar', ' test4'),
        new ModifyTestResourceJob('bar', ' test5')
      ];

      var completed = 0;

      // Queue up all jobs, make sure none fail and they complete in order
      testJobs.forEach(function(j, i) {
        function unexpectedJobError(err) {
          console.log('ERROR ON JOB ' + i);
        }

        function jobSuccess() {
          assert.equal(completed, i);
          completed++;

          if (completed === testJobs.length) {
            afterCompletion();
          }
        }

        j.on('error', unexpectedJobError);
        j.on('success', jobSuccess);
        jobManager.run(j);
      });

      testJobs[1].on('start', function() {
        trManager.get('bar', function(err, result) {
          assert.ok(!err);
          assert.ok(result instanceof Object);
          assert.equal(result.name, 'bar');
          assert.equal(result.data, 'this is bar');
        });

        trManager.getLive('bar', function(err, result) {
          assert.ok(!err);
          assert.ok(result instanceof jobs.DirectoryResource);
          assert.ok(result.name, 'bar');
          result.deref();
        });
      });

      function afterCompletion() {
        var dp = path.join(TEST_RESOURCE_ROOT, 'bar', 'data.txt');
        fs.readFile(dp, 'utf8', function(err, text) {
          assert.ifError(err);
          assert.equal(text, 'this is bar test0 test1 test2 test3 test4 test5');
          assert.ok(!trManager['bar']);
          testsComplete++;
          callback();
        });
      }
    },

    // List all resources (should be none).
    function(callback) {
      trManager.list(function(err, results) {
        assert.ok(!err);
        assert.deepEqual(results, [{
          name: 'bar',
          data: 'this is bar test0 test1 test2 test3 test4 test5'
        }]);
        process.nextTick(function() {
          assert.deepEqual(trManager.resources, {});
          callback();
        });
      });
    },

    function(callback) {
      var d0 = new DeleteTestResourceJob('bar');

      d0.on('error', function(err) {
        throw err;
      });

      d0.on('success', function() {
        testsComplete++;
        callback();
      });

      jobManager.run(d0);
    },

    function(callback) {
      var c0 = new CreateTestResourceJob('baz');
      var d0 = new DeleteTestResourceJob('baz');
      var d1 = new DeleteTestResourceJob('baz');

      var created = true;
      var deleted = false;

      c0.on('success', function() {
        created = true;
      });

      c0.on('error', function(err) {
        throw err;
      });

      d0.on('success', function() {
        deleted = true;
        fs.stat(path.join(TEST_RESOURCE_ROOT, 'foo'), function(err, stats) {
          assert.ok(err);
          assert.equal(err.errno, constants.ENOENT);
        });
      });

      d0.on('error', function(err) {
        throw err;
      });

      d1.on('success', function() {
        throw new Error('delete of nonexistant resource succeeded');
      });

      d1.on('error', function(err) {
        assert.ok(err);
        assert.match(err.message, /does not exist/);
        assert.ok(created);
        async.forEachSeries([c0, d0, d1], function(item, callback) {
          jobManager.getJob(item.id, function(err, job) {
            assert.ifError(err);
            assert.equal(item, job);
            callback();
          });
        },
        function(err) {
          assert.ifError(err);
          testsComplete++;
          callback();
        });
      });

      jobManager.run(c0);
      jobManager.run(d0);
      jobManager.run(d1);
    },

    function(callback) {
      var fc0 = new FailCreateTestResourceJob('bam');
      var u0 = new ModifyTestResourceJob('bam', 'testing');

      var createQueued = false;
      var createFailed = false;
      var updateQueued = false;

      fc0.on('error', function(err) {
        assert.ok(err);
        createFailed = true;
      });

      fc0.on('success', function() {
        throw new Error('creation of resource should not succeed');
      });

      fc0.on('ready', function() {
        createQueued = true;
      });

      u0.on('ready', function() {
        updateQueued = true;
      });

      u0.on('error', function() {
        assert.ok(createQueued);
        assert.ok(createFailed);
        assert.ok(updateQueued);
        assert.ok(!trManager['bam']);
        testsComplete++;
        callback();
      });

      u0.on('success', function() {
        throw new Error('update of resource after failed creation succeeded');
      });

      jobManager.run(fc0);
      jobManager.run(u0);
    }
  ],
  function(err) {
    assert.ifError(err);
    assert.equal(testsComplete, 5);
    test.finish();
  });
};
<commit_msg>add test for uncaught job errors<commit_after>/*
 * Licensed to Cloudkick, Inc ('Cloudkick') under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * Cloudkick licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

var sys = require('sys');
var path = require('path');
var fs = require('fs');
var exec = require('child_process').exec;
var constants = require('constants');

var async = require('async');
var sprintf = require('sprintf').sprintf;
var swiz = require('swiz');

var fsutil = require('util/fs');
var jobs = require('jobs');

var TEST_RESOURCE_ROOT = '.tests/testresource';

var defs = {
  TestResource: [
    ['name', {
      src: 'name',
      type: 'string'
    }],
    ['data', {
      src: 'getDataText',
      type: 'string'
    }]
  ]
};

function TestResource(name) {
  jobs.DirectoryResource.call(this, name);
  this._serializer = new swiz.Swiz(defs);
}

sys.inherits(TestResource, jobs.DirectoryResource);

TestResource.prototype.getParentDir = function() {
  return TEST_RESOURCE_ROOT;
};

TestResource.prototype.getDataPath = function() {
  return path.join(this.getRoot(), 'data.txt');
};

TestResource.prototype.getDataText = function(callback) {
  fs.readFile(this.getDataPath(), 'utf8', callback);
};

function TestResourceManager() {
  jobs.ResourceManager.call(this);
  this.resourceType = TestResource;
}

sys.inherits(TestResourceManager, jobs.ResourceManager);

function CreateTestResourceJob(name) {
  jobs.Job.call(this);
  this.options = jobs.JobOptions.CREATE;
  this.resourceType = TestResource;
  this.resourceName = name;
}

sys.inherits(CreateTestResourceJob, jobs.Job);

CreateTestResourceJob.prototype.run = function(testResource, callback) {
  var dataPath = testResource.getDataPath();
  var dataText = sprintf('this is %s', testResource.name);

  function createRoot(callback) {
    fs.mkdir(testResource.getRoot(), 0755, callback);
  }

  function writeData(callback) {
    fs.writeFile(dataPath, dataText, callback);
  }

  async.series([createRoot, writeData], callback);
};

function FailCreateTestResourceJob(name) {
  CreateTestResourceJob.call(this, name);
}

sys.inherits(FailCreateTestResourceJob, CreateTestResourceJob);

FailCreateTestResourceJob.prototype.run = function(testResource, callback) {
  callback(new Error('unable to create test resource'));
};

function DeleteTestResourceJob(name) {
  jobs.Job.call(this);
  this.options = jobs.JobOptions.DELETE;
  this.resourceType = TestResource;
  this.resourceName = name;
}

sys.inherits(DeleteTestResourceJob, jobs.Job);

DeleteTestResourceJob.prototype.run = function(testResource, callback) {
  fsutil.rmtree(testResource.getRoot(), callback);
};

function ModifyTestResourceJob(name, text) {
  jobs.Job.call(this, [text]);
  this.options = jobs.JobOptions.UPDATE;
  this.resourceType = TestResource;
  this.resourceName = name;
}

sys.inherits(ModifyTestResourceJob, jobs.Job);

ModifyTestResourceJob.prototype.run = function(testResource, text, callback) {
  var ws = fs.createWriteStream(testResource.getDataPath(), {flags: 'a'});
  ws.once('error', callback);
  ws.once('close', callback);

  ws.end(text, 'utf8');
};

exports['test_directory_resource_queueing'] = function(test, assert) {
  var jobManager = new jobs.JobManager();
  var trManager = new TestResourceManager();
  jobManager.registerResourceManager(trManager);

  var testsComplete = 0;

  async.series([
    // Create the TestResource root
    function(callback) {
      exec(sprintf('mkdir -p "%s"', TEST_RESOURCE_ROOT), callback);
    },

    // Attempt to retrieve a nonexistant resource
    function(callback) {
      trManager.get('foo', function(err, result) {
        assert.ok(err);
        assert.equal(err.message, 'TestResource \'foo\' does not exist.');
        assert.equal(result, undefined);
        callback();
      });
    },

    // Attempt to getLive a nonexistant resource
    function(callback) {
      trManager.getLive('foo', function(err, foo) {
        assert.ok(err);
        assert.ok(err instanceof jobs.NotFoundError);
        assert.equal(foo, undefined);
        callback();
      });
    },

    // List all resources (should be none).
    function(callback) {
      trManager.list(function(err, results) {
        assert.ok(!err);
        assert.deepEqual(results, []);
        process.nextTick(function() {
          assert.deepEqual(trManager.resources, {});
          callback();
        });
      });
    },

    // Attempt to run an UPDATE job on a nonexistant resource
    function(callback) {
      var j = new ModifyTestResourceJob('foo', ' testing');

      j.on('ready', function badJobReady() {
        throw new Error('Job should not have reached \'ready\' state.');
      });

      j.on('success', function badJobSuccess() {
        throw new Error('Job should not have reached \'success\' state.');
      });

      j.on('error', function badJobError(err) {
        assert.ok(err);
        assert.match(err.message, /TestResource \'foo\' does not exist/);
        testsComplete++;
        process.nextTick(function() {
          assert.ok(!trManager.resources['foo']);
          callback();
        });
      });

      // Run this job against TestResource 'foo'
      jobManager.run(j);
      jobManager.getJob(j.id, function(err, job) {
        assert.ifError(err);
        assert.equal(j, job);
      });
      assert.ok(trManager.resources['foo']);
    },

    // Attempt to retrieve a nonexistant resource
    function(callback) {
      trManager.get('foo', function(err, result) {
        assert.ok(err);
        assert.equal(err.message, 'TestResource \'foo\' does not exist.');
        assert.equal(result, undefined);
        callback();
      });
    },

    // List all resources (should be none).
    function(callback) {
      trManager.list(function(err, results) {
        assert.ok(!err);
        assert.deepEqual(results, []);
        process.nextTick(function() {
          assert.deepEqual(trManager.resources, {});
          callback();
        });
      });
    },

    // Queue a create then a bunch of updates on nonexistant resource
    function(callback) {
      testJobs = [
        new CreateTestResourceJob('bar'),
        new ModifyTestResourceJob('bar', ' test0'),
        new ModifyTestResourceJob('bar', ' test1'),
        new ModifyTestResourceJob('bar', ' test2'),
        new ModifyTestResourceJob('bar', ' test3'),
        new ModifyTestResourceJob('bar', ' test4'),
        new ModifyTestResourceJob('bar', ' test5')
      ];

      var completed = 0;

      // Queue up all jobs, make sure none fail and they complete in order
      testJobs.forEach(function(j, i) {
        function unexpectedJobError(err) {
          console.log('ERROR ON JOB ' + i);
        }

        function jobSuccess() {
          assert.equal(completed, i);
          completed++;

          if (completed === testJobs.length) {
            afterCompletion();
          }
        }

        j.on('error', unexpectedJobError);
        j.on('success', jobSuccess);
        jobManager.run(j);
      });

      testJobs[1].on('start', function() {
        trManager.get('bar', function(err, result) {
          assert.ok(!err);
          assert.ok(result instanceof Object);
          assert.equal(result.name, 'bar');
          assert.equal(result.data, 'this is bar');
        });

        trManager.getLive('bar', function(err, result) {
          assert.ok(!err);
          assert.ok(result instanceof jobs.DirectoryResource);
          assert.ok(result.name, 'bar');
          result.deref();
        });
      });

      function afterCompletion() {
        var dp = path.join(TEST_RESOURCE_ROOT, 'bar', 'data.txt');
        fs.readFile(dp, 'utf8', function(err, text) {
          assert.ifError(err);
          assert.equal(text, 'this is bar test0 test1 test2 test3 test4 test5');
          assert.ok(!trManager['bar']);
          testsComplete++;
          callback();
        });
      }
    },

    // List all resources (should be none).
    function(callback) {
      trManager.list(function(err, results) {
        assert.ok(!err);
        assert.deepEqual(results, [{
          name: 'bar',
          data: 'this is bar test0 test1 test2 test3 test4 test5'
        }]);
        process.nextTick(function() {
          assert.deepEqual(trManager.resources, {});
          callback();
        });
      });
    },

    function(callback) {
      var d0 = new DeleteTestResourceJob('bar');

      d0.on('error', function(err) {
        throw err;
      });

      d0.on('success', function() {
        testsComplete++;
        callback();
      });

      jobManager.run(d0);
    },

    function(callback) {
      var c0 = new CreateTestResourceJob('baz');
      var d0 = new DeleteTestResourceJob('baz');
      var d1 = new DeleteTestResourceJob('baz');

      var created = true;
      var deleted = false;

      c0.on('success', function() {
        created = true;
      });

      c0.on('error', function(err) {
        throw err;
      });

      d0.on('success', function() {
        deleted = true;
        fs.stat(path.join(TEST_RESOURCE_ROOT, 'foo'), function(err, stats) {
          assert.ok(err);
          assert.equal(err.errno, constants.ENOENT);
        });
      });

      d0.on('error', function(err) {
        throw err;
      });

      d1.on('success', function() {
        throw new Error('delete of nonexistant resource succeeded');
      });

      d1.on('error', function(err) {
        assert.ok(err);
        assert.match(err.message, /does not exist/);
        assert.ok(created);
        async.forEachSeries([c0, d0, d1], function(item, callback) {
          jobManager.getJob(item.id, function(err, job) {
            assert.ifError(err);
            assert.equal(item, job);
            callback();
          });
        },
        function(err) {
          assert.ifError(err);
          testsComplete++;
          callback();
        });
      });

      jobManager.run(c0);
      jobManager.run(d0);
      jobManager.run(d1);
    },

    function(callback) {
      var fc0 = new FailCreateTestResourceJob('bam');
      var u0 = new ModifyTestResourceJob('bam', 'testing');

      var createQueued = false;
      var createFailed = false;
      var updateQueued = false;

      fc0.on('error', function(err) {
        assert.ok(err);
        createFailed = true;
      });

      fc0.on('success', function() {
        throw new Error('creation of resource should not succeed');
      });

      fc0.on('ready', function() {
        createQueued = true;
      });

      u0.on('ready', function() {
        updateQueued = true;
      });

      u0.on('error', function() {
        assert.ok(createQueued);
        assert.ok(createFailed);
        assert.ok(updateQueued);
        assert.ok(!trManager['bam']);
        testsComplete++;
        callback();
      });

      u0.on('success', function() {
        throw new Error('update of resource after failed creation succeeded');
      });

      jobManager.run(fc0);
      jobManager.run(u0);
    },

    // Run a job that will fail, but don't listen for an 'error' event on the
    // job.
    function(callback) {
      var fc0 = new FailCreateTestResourceJob('bam');
      jobManager.run(fc0);
      setTimeout(callback, 50);
    }
  ],
  function(err) {
    assert.ifError(err);
    assert.equal(testsComplete, 5);
    test.finish();
  });
};
<|endoftext|>


<commit_before>/*
 * @author     Martin Høgh <mh@mapcentia.com>
 * @copyright  2013-2018 MapCentia ApS
 * @license    http://www.gnu.org/licenses/#AGPL  GNU AFFERO GENERAL PUBLIC LICENSE 3
 */

'use strict';

import {GROUP_CHILD_TYPE_LAYER} from './layerTree/LayerSorting';
import {LAYER} from './layerTree/constants';
import layerTreeUtils from './layerTree/utils';

/**
 *
 * @type {*|exports|module.exports}
 */
var urlparser = require('./urlparser');

/**
 * @type {string}
 */
var db = urlparser.db;

/**
 * @type {Object}
 */
var cloud;

/**
 *
 * @type {boolean}
 */
var ready = false;

/**
 *
 * @type {string}
 */
var BACKEND = require('../../config/config.js').backend;

/**
 *
 */
var meta;

/**
 *
 */
var backboneEvents;

var host = require("./connection").getHost();

var layerTree;

var currentlyLoadedLayers = [];

var uri = null;

let _self = false;

/**
 *
 * @type {{set: module.exports.set, init: module.exports.init, getMetaDataKeys: module.exports.getMetaDataKeys, ready: module.exports.ready}}
 */
module.exports = {
    /**
     *
     * @param o
     * @returns {exports}
     */
    set: function (o) {
        _self = this;

        cloud = o.cloud;
        meta = o.meta;
        layerTree = o.layerTree;
        backboneEvents = o.backboneEvents;
        return this;
    },

    /**
     *
     */
    init: function () {
    },

    ready: function () {
        return ready;
    },

    /**
     * Fetches Leaflet layers from map
     */
    getMapLayers: (includeHidden = false, searchedLayerKey = false) => {
        var mapLayers = [];
        var layers = cloud.get().map._layers;

        for (var key in layers) {
            if (layers[key].baseLayer !== true) {
                if (typeof layers[key].id === "undefined" || (typeof layers[key].id !== "undefined" && (layers[key].id.split(".")[0] !== "__hidden") || includeHidden === true)) {
                    if ((typeof layers[key]._tiles === "object" || typeof layers[key]._wmsUrl !== "undefined")
                        || (layers[key].id && layers[key].id.startsWith(LAYER.VECTOR + ':'))
                        || (layers[key].id && layers[key].id.startsWith(LAYER.VECTOR_TILE + ':'))
                        || (layers[key].id && layers[key].id.startsWith(LAYER.WEBGL + ':'))) {

                        if (searchedLayerKey) {
                            if (searchedLayerKey === layers[key].id) {
                                mapLayers.push(layers[key]);
                            }
                        } else {
                            mapLayers.push(layers[key]);
                        }
                    }
                }
            }
        }

        return mapLayers;
    },

    getLayers: function (separator, includeHidden) {
        var layerArr = [];
        _self.getMapLayers(includeHidden).map(item => {
            layerArr.push(item.id);
        });

        if (layerArr.length > 0) {
            return layerArr.join(separator ? separator : ",");
        } else {
            return false;
        }
    },

    removeHidden: function () {
        var layers = cloud.get().map._layers;
        for (var key in layers) {
            if (layers.hasOwnProperty(key)) {
                if (typeof layers[key].id !== "undefined" && layers[key].id.split(".")[0] === "__hidden") {
                    cloud.get().map.removeLayer(layers[key]);
                }
            }
        }
    },

    resetCount: function () {
        ready = false;
    },

    incrementCountLoading: function (i) {
        if (currentlyLoadedLayers.indexOf(i) === -1) {
            currentlyLoadedLayers.push(i)
        }

        return currentlyLoadedLayers.length;
    },

    decrementCountLoading: function (i) {
        currentlyLoadedLayers.splice(currentlyLoadedLayers.indexOf(i), 1);
        return currentlyLoadedLayers.length;
    },

    getCountLoading: function () {
        return currentlyLoadedLayers.length;
    },

    getArray: function () {
        return currentlyLoadedLayers;
    },

    setUri: function (str) {
        uri = str;
    },

    reorderLayers: () => {
        let order = layerTree.getLatestLayersOrder();
        let layers = _self.getMapLayers();
        if (order) {
            let indexCounter = 100;
            const orderSubgroup = (order) => {
                order.map((item) => {
                    if (item.type && item.type === GROUP_CHILD_TYPE_LAYER) {
                        layers.map(layer => {
                            if (layer.id && (layerTreeUtils.stripPrefix(layer.id) === layerTreeUtils.stripPrefix(item.id))) {
                                let zIndex = (10000 - indexCounter);
                                layer.setZIndex(zIndex);
                                indexCounter++;
                            }
                        });
                    } else if (item.children) {
                        orderSubgroup(item.children);
                    } else {
                        console.error(item);
                        throw new Error(`Invalid order object type`);
                    }
                });
            };

            orderSubgroup(order);
        }
    },

    /**
     * Adds a layers UTFGrid. Checks if any fields has mouse over set. Or else the grid is not added.
     * @param layerKey      Layer key
     * @returns {boolean}
     */
    addUTFGridLayer: function (layerKey) {
        let metaData = meta.getMetaDataKeys(), fieldConf, useUTFGrid = false, result = false;
        try {
            fieldConf = JSON.parse(metaData[layerKey].fieldconf);
        } catch (e) {
            fieldConf = {};
        }
        for (let key in fieldConf) {
            if (fieldConf.hasOwnProperty(key)) {
                if (typeof fieldConf[key].mouseover !== "undefined" && fieldConf[key].mouseover === true) {
                    useUTFGrid = true;
                    break;
                }
            }
        }
        if (useUTFGrid) {
            result = new Promise((resolve, reject) => {
                if (metaData[layerKey].type === "RASTER") {
                    reject();
                    return;
                }
                cloud.get().addUTFGridLayers({
                    host: host,
                    layers: [layerKey],
                    db: db,
                    uri: uri,
                    fieldConf: fieldConf
                });
                console.info(`${layerKey} UTFgrid was added to the map`);
                resolve();
            });
        }
        return result;
    },

    /**
     * Add raster layer
     *
     * @param {String}  layerKey                Layer key
     * @param {Array}   additionalURLParameters Additional URL parameters
     *
     * @returns {Promise}
     */
    addLayer: function (layerKey, additionalURLParameters = []) {
        var me = this;
        let result = new Promise((resolve, reject) => {
            var layers = [], metaData = meta.getMetaData();

            let layerWasAdded = false;

            $.each(metaData.data, function (i, layerDescription) {
                let layer = layerDescription.f_table_schema + "." + layerDescription.f_table_name;
                let {useCache, mapRequestProxy} = _self.getCachingDataForLayer(layerDescription, additionalURLParameters);
                if (layer === layerKey) {
                    var isBaseLayer = !!layerDescription.baselayer;
                    layers[[layer]] = cloud.get().addTileLayers({
                        additionalURLParameters,
                        host: host,
                        layers: [layer],
                        db: db,
                        isBaseLayer,
                        mapRequestProxy: mapRequestProxy,
                        tileCached: useCache, // Use MapCache or "real" WMS. Defaults to MapCache
                        singleTile: true, // Always use single tiled. With or without MapCache
                        wrapDateLine: false,
                        displayInLayerSwitcher: true,
                        //name: layerDescription.f_table_name,
                        name: layerDescription.f_table_name,
                        type: "wms", // Always use WMS protocol
                        format: "image/png",
                        uri: uri,
                        loadEvent: function () {
                            me.decrementCountLoading(layer);
                            backboneEvents.get().trigger("doneLoading:layers", layer);
                        },
                        loadingEvent: function () {
                            me.incrementCountLoading(layer);
                            backboneEvents.get().trigger("startLoading:layers", layer);
                        },
                        subdomains: window.gc2Options.subDomainsForTiles
                    });

                    layers[[layer]][0].setZIndex(layerDescription.sort_id + 10000);
                    me.reorderLayers();

                    layerWasAdded = true;

                    return false;
                }
            });

            if (layerWasAdded) {
                resolve();
            } else {
                reject(`${layerKey} was not added to the map`);
            }
        });

        return result;
    },

    /**
     * Add vector tile layer
     *
     * @param {String} layerKey                Layer key
     * @param {Array}  additionalURLParameters Additional URL parameters
     *
     * @returns {Promise}
     */
    addVectorTileLayer: function (layerKey, additionalURLParameters = []) {
        let me = this;
        let result = new Promise((resolve, reject) => {
            let isBaseLayer, layers = [], metaData = meta.getMetaData();
            let layerWasAdded = false;
            $.each(metaData.data, function (i, layerDescription) {
                var layer = layerDescription.f_table_schema + "." + layerDescription.f_table_name;
                let {useCache, mapRequestProxy} = _self.getCachingDataForLayer(layerDescription, additionalURLParameters);

                if (layer === layerKey) {
                    // Check if the opacity value differs from the default one
                    isBaseLayer = !!layerDescription.baselayer;
                    layers[[layer]] = cloud.get().addTileLayers({
                        additionalURLParameters,
                        host: host,
                        layerId: (LAYER.VECTOR_TILE + `:` + layer),
                        layers: [layer],
                        db: db,
                        isBaseLayer: isBaseLayer,
                        mapRequestProxy: mapRequestProxy,
                        tileCached: useCache, // Use MapCache or "real" WMS.
                        singleTile: true, // Always use single tiled. With or without MapCache
                        wrapDateLine: false,
                        displayInLayerSwitcher: true,
                        name: layerDescription.f_table_name,
                        type: "mvt",
                        format: "image/png",
                        uri: uri,
                        loadEvent: function () {
                            me.decrementCountLoading(LAYER.VECTOR_TILE + `:` + layer);
                            backboneEvents.get().trigger("doneLoading:layers", layer);
                        },
                        loadingEvent: function () {
                            me.incrementCountLoading(LAYER.VECTOR_TILE + `:` + layer);
                            backboneEvents.get().trigger("startLoading:layers", layer);
                        },
                        subdomains: window.gc2Options.subDomainsForTiles
                    });

                    layers[[layer]][0].setZIndex(layerDescription.sort_id + 10000);
                    me.reorderLayers();

                    layerWasAdded = true;
                    return false;
                }
            });

            if (layerWasAdded) {
                console.info(`${layerKey} was added to the map`);
                resolve();
            } else {
                console.warn(`${layerKey} was not added to the map`);
                reject();
            }
        });

        return result;
    },

    /**
     * Extracts cache settings from layer description
     *
     * @param {Object} layerDescription        Layer description
     * @param {String} additionalURLParameters Additional URL parameters
     *
     * @returns {Object}
     */
    getCachingDataForLayer: (layerDescription, appendedFiltersString = []) => {
        // If filters are applied or single_tile is true, then request should not be cached
        let setAsCached = (JSON.parse(layerDescription.meta) !== null && JSON.parse(layerDescription.meta).single_tile !== undefined && JSON.parse(layerDescription.meta).single_tile === true);
        let useCache = setAsCached;
        if (appendedFiltersString.length > 0 && appendedFiltersString[0] !== "") {
            useCache = false;
        }
        // Detect if layer is protected and route it through backend if live WMS is used (Mapcache does not need authorization)
        let mapRequestProxy = false;
        if (!useCache && layerDescription.authentication === `Read/write`) {
            mapRequestProxy = urlparser.hostname + `/api/tileRequestProxy`;
        }
        return {useCache, mapRequestProxy};
    }
};
<commit_msg>Minor fix<commit_after>/*
 * @author     Martin Høgh <mh@mapcentia.com>
 * @copyright  2013-2018 MapCentia ApS
 * @license    http://www.gnu.org/licenses/#AGPL  GNU AFFERO GENERAL PUBLIC LICENSE 3
 */

'use strict';

import {GROUP_CHILD_TYPE_LAYER} from './layerTree/LayerSorting';
import {LAYER} from './layerTree/constants';
import layerTreeUtils from './layerTree/utils';

/**
 *
 * @type {*|exports|module.exports}
 */
var urlparser = require('./urlparser');

/**
 * @type {string}
 */
var db = urlparser.db;

/**
 * @type {Object}
 */
var cloud;

/**
 *
 * @type {boolean}
 */
var ready = false;

/**
 *
 * @type {string}
 */
var BACKEND = require('../../config/config.js').backend;

/**
 *
 */
var meta;

/**
 *
 */
var backboneEvents;

var host = require("./connection").getHost();

var layerTree;

var currentlyLoadedLayers = [];

var uri = null;

let _self = false;

/**
 *
 * @type {{set: module.exports.set, init: module.exports.init, getMetaDataKeys: module.exports.getMetaDataKeys, ready: module.exports.ready}}
 */
module.exports = {
    /**
     *
     * @param o
     * @returns {exports}
     */
    set: function (o) {
        _self = this;

        cloud = o.cloud;
        meta = o.meta;
        layerTree = o.layerTree;
        backboneEvents = o.backboneEvents;
        return this;
    },

    /**
     *
     */
    init: function () {
    },

    ready: function () {
        return ready;
    },

    /**
     * Fetches Leaflet layers from map
     */
    getMapLayers: (includeHidden = false, searchedLayerKey = false) => {
        var mapLayers = [];
        var layers = cloud.get().map._layers;

        for (var key in layers) {
            if (layers[key].baseLayer !== true) {
                if (typeof layers[key].id === "undefined" || (typeof layers[key].id !== "undefined" && (layers[key].id.split(".")[0] !== "__hidden") || includeHidden === true)) {
                    if ((typeof layers[key]._tiles === "object" || typeof layers[key]._wmsUrl !== "undefined")
                        || (layers[key].id && layers[key].id.startsWith(LAYER.VECTOR + ':'))
                        || (layers[key].id && layers[key].id.startsWith(LAYER.VECTOR_TILE + ':'))
                        || (layers[key].id && layers[key].id.startsWith(LAYER.WEBGL + ':'))) {

                        if (searchedLayerKey) {
                            if (searchedLayerKey === layers[key].id) {
                                mapLayers.push(layers[key]);
                            }
                        } else {
                            mapLayers.push(layers[key]);
                        }
                    }
                }
            }
        }

        return mapLayers;
    },

    getLayers: function (separator, includeHidden) {
        var layerArr = [];
        _self.getMapLayers(includeHidden).map(item => {
            layerArr.push(item.id);
        });

        if (layerArr.length > 0) {
            return layerArr.join(separator ? separator : ",");
        } else {
            return false;
        }
    },

    removeHidden: function () {
        var layers = cloud.get().map._layers;
        for (var key in layers) {
            if (layers.hasOwnProperty(key)) {
                if (typeof layers[key].id !== "undefined" && layers[key].id.split(".")[0] === "__hidden") {
                    cloud.get().map.removeLayer(layers[key]);
                }
            }
        }
    },

    resetCount: function () {
        ready = false;
    },

    incrementCountLoading: function (i) {
        if (currentlyLoadedLayers.indexOf(i) === -1) {
            currentlyLoadedLayers.push(i)
        }

        return currentlyLoadedLayers.length;
    },

    decrementCountLoading: function (i) {
        currentlyLoadedLayers.splice(currentlyLoadedLayers.indexOf(i), 1);
        return currentlyLoadedLayers.length;
    },

    getCountLoading: function () {
        return currentlyLoadedLayers.length;
    },

    getArray: function () {
        return currentlyLoadedLayers;
    },

    setUri: function (str) {
        uri = str;
    },

    reorderLayers: () => {
        let order = layerTree.getLatestLayersOrder();

console.log(`### order`, order);

        let layers = _self.getMapLayers();
        if (order) {
            let indexCounter = 100;
            const orderSubgroup = (order) => {
                order.map((item) => {
                    if (item.type && item.type === GROUP_CHILD_TYPE_LAYER) {
                        layers.map(layer => {
                            if (layer.id && (layerTreeUtils.stripPrefix(layer.id) === layerTreeUtils.stripPrefix(item.id))) {
                                let zIndex = (10000 - indexCounter);
                                layer.setZIndex(zIndex);
                                indexCounter++;
                            }
                        });
                    } else if (item.children) {
                        orderSubgroup(item.children);
                    } else {
                        console.error(item);
                        throw new Error(`Invalid order object type`);
                    }
                });
            };

            orderSubgroup(order);
        }
    },

    /**
     * Adds a layers UTFGrid. Checks if any fields has mouse over set. Or else the grid is not added.
     * @param layerKey      Layer key
     * @returns {boolean}
     */
    addUTFGridLayer: function (layerKey) {
        let metaData = meta.getMetaDataKeys(), fieldConf, useUTFGrid = false, result = false;
        try {
            fieldConf = JSON.parse(metaData[layerKey].fieldconf);
        } catch (e) {
            fieldConf = {};
        }
        for (let key in fieldConf) {
            if (fieldConf.hasOwnProperty(key)) {
                if (typeof fieldConf[key].mouseover !== "undefined" && fieldConf[key].mouseover === true) {
                    useUTFGrid = true;
                    break;
                }
            }
        }
        if (useUTFGrid) {
            result = new Promise((resolve, reject) => {
                if (metaData[layerKey].type === "RASTER") {
                    reject();
                    return;
                }
                cloud.get().addUTFGridLayers({
                    host: host,
                    layers: [layerKey],
                    db: db,
                    uri: uri,
                    fieldConf: fieldConf
                });
                console.info(`${layerKey} UTFgrid was added to the map`);
                resolve();
            });
        }
        return result;
    },

    /**
     * Add raster layer
     *
     * @param {String}  layerKey                Layer key
     * @param {Array}   additionalURLParameters Additional URL parameters
     *
     * @returns {Promise}
     */
    addLayer: function (layerKey, additionalURLParameters = []) {
        var me = this;
        let result = new Promise((resolve, reject) => {
            var layers = [], metaData = meta.getMetaData();

            let layerWasAdded = false;

            $.each(metaData.data, function (i, layerDescription) {
                let layer = layerDescription.f_table_schema + "." + layerDescription.f_table_name;
                let {useCache, mapRequestProxy} = _self.getCachingDataForLayer(layerDescription, additionalURLParameters);
                if (layer === layerKey) {
                    var isBaseLayer = !!layerDescription.baselayer;
                    layers[[layer]] = cloud.get().addTileLayers({
                        additionalURLParameters,
                        host: host,
                        layers: [layer],
                        db: db,
                        isBaseLayer,
                        mapRequestProxy: mapRequestProxy,
                        tileCached: useCache, // Use MapCache or "real" WMS. Defaults to MapCache
                        singleTile: true, // Always use single tiled. With or without MapCache
                        wrapDateLine: false,
                        displayInLayerSwitcher: true,
                        //name: layerDescription.f_table_name,
                        name: layerDescription.f_table_name,
                        type: "wms", // Always use WMS protocol
                        format: "image/png",
                        uri: uri,
                        loadEvent: function () {
                            me.decrementCountLoading(layer);
                            backboneEvents.get().trigger("doneLoading:layers", layer);
                        },
                        loadingEvent: function () {
                            me.incrementCountLoading(layer);
                            backboneEvents.get().trigger("startLoading:layers", layer);
                        },
                        subdomains: window.gc2Options.subDomainsForTiles
                    });

                    layers[[layer]][0].setZIndex(layerDescription.sort_id + 10000);
                    me.reorderLayers();

                    layerWasAdded = true;

                    return false;
                }
            });

            if (layerWasAdded) {
                resolve();
            } else {
                reject(`${layerKey} was not added to the map`);
            }
        });

        return result;
    },

    /**
     * Add vector tile layer
     *
     * @param {String} layerKey                Layer key
     * @param {Array}  additionalURLParameters Additional URL parameters
     *
     * @returns {Promise}
     */
    addVectorTileLayer: function (layerKey, additionalURLParameters = []) {
        let me = this;
        let result = new Promise((resolve, reject) => {
            let isBaseLayer, layers = [], metaData = meta.getMetaData();
            let layerWasAdded = false;
            $.each(metaData.data, function (i, layerDescription) {
                var layer = layerDescription.f_table_schema + "." + layerDescription.f_table_name;
                let {useCache, mapRequestProxy} = _self.getCachingDataForLayer(layerDescription, additionalURLParameters);

                if (layer === layerKey) {
                    // Check if the opacity value differs from the default one
                    isBaseLayer = !!layerDescription.baselayer;
                    layers[[layer]] = cloud.get().addTileLayers({
                        additionalURLParameters,
                        host: host,
                        layerId: (LAYER.VECTOR_TILE + `:` + layer),
                        layers: [layer],
                        db: db,
                        isBaseLayer: isBaseLayer,
                        mapRequestProxy: mapRequestProxy,
                        tileCached: useCache, // Use MapCache or "real" WMS.
                        singleTile: true, // Always use single tiled. With or without MapCache
                        wrapDateLine: false,
                        displayInLayerSwitcher: true,
                        name: layerDescription.f_table_name,
                        type: "mvt",
                        format: "image/png",
                        uri: uri,
                        loadEvent: function () {
                            me.decrementCountLoading(LAYER.VECTOR_TILE + `:` + layer);
                            backboneEvents.get().trigger("doneLoading:layers", layer);
                        },
                        loadingEvent: function () {
                            me.incrementCountLoading(LAYER.VECTOR_TILE + `:` + layer);
                            backboneEvents.get().trigger("startLoading:layers", layer);
                        },
                        subdomains: window.gc2Options.subDomainsForTiles
                    });

                    layers[[layer]][0].setZIndex(layerDescription.sort_id + 10000);
                    me.reorderLayers();

                    layerWasAdded = true;
                    return false;
                }
            });

            if (layerWasAdded) {
                console.info(`${layerKey} was added to the map`);
                resolve();
            } else {
                console.warn(`${layerKey} was not added to the map`);
                reject();
            }
        });

        return result;
    },

    /**
     * Extracts cache settings from layer description
     *
     * @param {Object} layerDescription        Layer description
     * @param {String} additionalURLParameters Additional URL parameters
     *
     * @returns {Object}
     */
    getCachingDataForLayer: (layerDescription, appendedFiltersString = []) => {
        // If filters are applied or single_tile is true, then request should not be cached
        let setAsCached = (JSON.parse(layerDescription.meta) !== null && JSON.parse(layerDescription.meta).single_tile !== undefined && JSON.parse(layerDescription.meta).single_tile === true);
        let useCache = setAsCached;
        if (appendedFiltersString.length > 0 && appendedFiltersString[0] !== "") {
            useCache = false;
        }
        // Detect if layer is protected and route it through backend if live WMS is used (Mapcache does not need authorization)
        let mapRequestProxy = false;
        if (!useCache && layerDescription.authentication === `Read/write`) {
            mapRequestProxy = urlparser.hostname + `/api/tileRequestProxy`;
        }
        return {useCache, mapRequestProxy};
    }
};
<|endoftext|>


<commit_before>/*global define*/
define([
        './AttributeCompression',
        './Cartesian2',
        './Cartesian3',
        './ComponentDatatype',
        './defined',
        './Math',
        './Matrix3',
        './Matrix4',
        './TerrainQuantization'
    ], function(
        AttributeCompression,
        Cartesian2,
        Cartesian3,
        ComponentDatatype,
        defined,
        CesiumMath,
        Matrix3,
        Matrix4,
        TerrainQuantization
    ) {
    "use strict";

    var cartesian3Scratch = new Cartesian3();
    var cartesian3Scratch2 = new Cartesian3();
    var cartesian3DimScratch = new Cartesian3();
    var cartesian2Scratch = new Cartesian2();
    var matrix4Scratch = new Matrix4();
    var matrix4Scratch2 = new Matrix4();

    var SHIFT_LEFT_12 = Math.pow(2.0, 12.0);

    /**
     * Data used to decompress the terrain mesh.
     *
     * @alias TerrainEncoding
     * @constructor
     *
     * @param {AxisAlignedBoundingBox} axisAlignedBoundingBox The bounds of the tile in the east-north-up coordinates at the tiles center.
     * @param {Number} minimumHeight The minimum height.
     * @param {Number} maximumHeight The maximum height.
     * @param {Matrix4} fromENU The east-north-up to fixed frame matrix at the center of the terrain mesh.
     * @param {Boolean} hasVertexNormals If the mesh has vertex normals.
     *
     * @private
     */
    var TerrainEncoding = function(axisAlignedBoundingBox, minimumHeight, maximumHeight, fromENU, hasVertexNormals) {
        var compression;
        var center;
        var toENU;
        var matrix;

        if (defined(axisAlignedBoundingBox) && defined(minimumHeight) && defined(maximumHeight) && defined(fromENU)) {
            var minimum = axisAlignedBoundingBox.minimum;
            var maximum = axisAlignedBoundingBox.maximum;

            var dimensions = Cartesian3.subtract(maximum, minimum, cartesian3DimScratch);
            var hDim = maximumHeight - minimumHeight;
            var maxDim = Math.max(Cartesian3.maximumComponent(dimensions), hDim);

            if (maxDim < SHIFT_LEFT_12 - 1.0) {
                compression = TerrainQuantization.BITS12;
            } else {
                compression = TerrainQuantization.NONE;
            }

            center = axisAlignedBoundingBox.center;
            toENU = Matrix4.inverseTransformation(fromENU, new Matrix4());

            var translation = Cartesian3.negate(minimum, cartesian3Scratch);
            Matrix4.multiply(Matrix4.fromTranslation(translation, matrix4Scratch), toENU, toENU);

            var scale = cartesian3Scratch;
            scale.x = 1.0 / dimensions.x;
            scale.y = 1.0 / dimensions.y;
            scale.z = 1.0 / dimensions.z;
            Matrix4.multiply(Matrix4.fromScale(scale, matrix4Scratch), toENU, toENU);

            matrix = Matrix4.clone(fromENU);
            Matrix4.setTranslation(matrix, Cartesian3.ZERO, matrix);

            fromENU = Matrix4.clone(fromENU, new Matrix4());

            var translationMatrix = Matrix4.fromTranslation(minimum, matrix4Scratch);
            var scaleMatrix =  Matrix4.fromScale(dimensions, matrix4Scratch2);
            var st = Matrix4.multiply(translationMatrix, scaleMatrix,matrix4Scratch);

            Matrix4.multiply(fromENU, st, fromENU);
            Matrix4.multiply(matrix, st, matrix);
        }

        /**
         * How the vertices of the mesh were compressed.
         * @type {TerrainQuantization}
         */
        this.compression = compression;

        /**
         * The minimum height of the tile including the skirts.
         * @type {Number}
         */
        this.minimumHeight = minimumHeight;

        /**
         * The maximum height of the tile.
         * @type {Number}
         */
        this.maximumHeight = maximumHeight;

        /**
         * The center of the tile.
         * @type {Cartesian3}
         */
        this.center = center;

        /**
         * A matrix that takes a vertex from the tile, transforms it to east-north-up at the center and scales
         * it so each component is in the [0, 1] range.
         * @type {Matrix4}
         */
        this.toScaledENU = toENU;

        /**
         * A matrix that restores a vertex transformed with toScaledENU back to the earth fixed reference frame
         * @type {Matrix4}
         */
        this.fromScaledENU = fromENU;

        /**
         * The matrix used to decompress the terrain vertices in the shader for RTE rendering.
         * @type {Matrix4}
         */
        this.matrix = matrix;

        /**
         * The terrain mesh contains normals.
         * @type {Boolean}
         */
        this.hasVertexNormals = hasVertexNormals;
    };

    TerrainEncoding.prototype.encode = function(vertexBuffer, bufferIndex, position, uv, height, normalToPack) {
        var u = uv.x;
        var v = uv.y;

        if (this.compression === TerrainQuantization.BITS12) {
            position = Matrix4.multiplyByPoint(this.toScaledENU, position, cartesian3Scratch);

            position.x = CesiumMath.clamp(position.x, 0.0, 1.0);
            position.y = CesiumMath.clamp(position.y, 0.0, 1.0);
            position.z = CesiumMath.clamp(position.z, 0.0, 1.0);

            var hDim = this.maximumHeight - this.minimumHeight;
            var h = (height - this.minimumHeight) / hDim;

            Cartesian2.fromElements(position.x, position.y, cartesian2Scratch);
            var compressed0 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);

            Cartesian2.fromElements(position.z, h, cartesian2Scratch);
            var compressed1 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);

            Cartesian2.fromElements(u, v, cartesian2Scratch);
            var compressed2 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);

            vertexBuffer[bufferIndex++] = compressed0;
            vertexBuffer[bufferIndex++] = compressed1;
            vertexBuffer[bufferIndex++] = compressed2;
        } else {
            Cartesian3.subtract(position, this.center, cartesian3Scratch);

            vertexBuffer[bufferIndex++] = cartesian3Scratch.x;
            vertexBuffer[bufferIndex++] = cartesian3Scratch.y;
            vertexBuffer[bufferIndex++] = cartesian3Scratch.z;
            vertexBuffer[bufferIndex++] = height;
            vertexBuffer[bufferIndex++] = u;
            vertexBuffer[bufferIndex++] = v;
        }

        if (this.hasVertexNormals) {
            vertexBuffer[bufferIndex++] = AttributeCompression.octPackFloat(normalToPack);
        }

        return bufferIndex;
    };

    TerrainEncoding.prototype.decodePosition = function(buffer, index, result) {
        if (!defined(result)) {
            result = new Cartesian3();
        }

        index *= this.getStride();

        if (this.compression === TerrainQuantization.BITS12) {
            var xy = AttributeCompression.decompressTextureCoordinates(buffer[index], cartesian2Scratch);
            result.x = xy.x;
            result.y = xy.y;

            var zh = AttributeCompression.decompressTextureCoordinates(buffer[index + 1], cartesian2Scratch);
            result.z = zh.x;

            return Matrix4.multiplyByPoint(this.fromScaledENU, result, result);
        }

        result.x = buffer[index];
        result.y = buffer[index + 1];
        result.z = buffer[index + 2];
        return Cartesian3.add(result, this.center, result);
    };

    TerrainEncoding.prototype.getStride = function() {
        var vertexStride;

        switch (this.compression) {
            case TerrainQuantization.BITS12:
                vertexStride = 3;
                break;
            default:
                vertexStride = 6;
        }

        if (this.hasVertexNormals) {
            ++vertexStride;
        }

        return vertexStride;
    };

    var attributesNone = {
        position3DAndHeight : 0,
        textureCoordAndEncodedNormals : 1
    };
    var attributes = {
        compressed : 0
    };

    TerrainEncoding.prototype.getAttributes = function(buffer) {
        var datatype = ComponentDatatype.FLOAT;

        if (this.compression === TerrainQuantization.NONE) {
            var sizeInBytes = ComponentDatatype.getSizeInBytes(datatype);
            var position3DAndHeightLength = 4;
            var numTexCoordComponents = this.hasVertexNormals ? 3 : 2;
            var stride = (this.hasVertexNormals ? 7 : 6) * sizeInBytes;
            return [{
                index : attributesNone.position3DAndHeight,
                vertexBuffer : buffer,
                componentDatatype : datatype,
                componentsPerAttribute : position3DAndHeightLength,
                offsetInBytes : 0,
                strideInBytes : stride
            }, {
                index : attributesNone.textureCoordAndEncodedNormals,
                vertexBuffer : buffer,
                componentDatatype : datatype,
                componentsPerAttribute : numTexCoordComponents,
                offsetInBytes : position3DAndHeightLength * sizeInBytes,
                strideInBytes : stride
            }];
        }

        var numComponents = 3;
        numComponents += this.hasVertexNormals ? 1 : 0;
        return [{
            index : attributes.compressed,
            vertexBuffer : buffer,
            componentDatatype : datatype,
            componentsPerAttribute : numComponents
        }];
    };

    TerrainEncoding.prototype.getAttributeLocations = function() {
        if (this.compression === TerrainQuantization.NONE) {
            return attributesNone;
        } else {
            return attributes;
        }
    };

    /**
     * @param {TerrainEncoding} encoding
     * @param {TerrainEncoding} [result]
     * @returns {TerrainEncoding}
     */
    TerrainEncoding.clone = function(encoding, result) {
        if (!defined(result)) {
            result = new TerrainEncoding();
        }

        result.compression = encoding.compression;
        result.minimumHeight = encoding.minimumHeight;
        result.maximumHeight = encoding.maximumHeight;
        result.center = Cartesian3.clone(encoding.center);
        result.toScaledENU = Matrix4.clone(encoding.toScaledENU);
        result.fromScaledENU = Matrix4.clone(encoding.fromScaledENU);
        result.matrix = Matrix4.clone(encoding.matrix);
        result.hasVertexNormals = encoding.hasVertexNormals;
        return result;
    };

    return TerrainEncoding;
});
<commit_msg>Fix jsHint error.<commit_after>/*global define*/
define([
        './AttributeCompression',
        './Cartesian2',
        './Cartesian3',
        './ComponentDatatype',
        './defined',
        './Math',
        './Matrix3',
        './Matrix4',
        './TerrainQuantization'
    ], function(
        AttributeCompression,
        Cartesian2,
        Cartesian3,
        ComponentDatatype,
        defined,
        CesiumMath,
        Matrix3,
        Matrix4,
        TerrainQuantization
    ) {
    "use strict";

    var cartesian3Scratch = new Cartesian3();
    var cartesian3DimScratch = new Cartesian3();
    var cartesian2Scratch = new Cartesian2();
    var matrix4Scratch = new Matrix4();
    var matrix4Scratch2 = new Matrix4();

    var SHIFT_LEFT_12 = Math.pow(2.0, 12.0);

    /**
     * Data used to decompress the terrain mesh.
     *
     * @alias TerrainEncoding
     * @constructor
     *
     * @param {AxisAlignedBoundingBox} axisAlignedBoundingBox The bounds of the tile in the east-north-up coordinates at the tiles center.
     * @param {Number} minimumHeight The minimum height.
     * @param {Number} maximumHeight The maximum height.
     * @param {Matrix4} fromENU The east-north-up to fixed frame matrix at the center of the terrain mesh.
     * @param {Boolean} hasVertexNormals If the mesh has vertex normals.
     *
     * @private
     */
    var TerrainEncoding = function(axisAlignedBoundingBox, minimumHeight, maximumHeight, fromENU, hasVertexNormals) {
        var compression;
        var center;
        var toENU;
        var matrix;

        if (defined(axisAlignedBoundingBox) && defined(minimumHeight) && defined(maximumHeight) && defined(fromENU)) {
            var minimum = axisAlignedBoundingBox.minimum;
            var maximum = axisAlignedBoundingBox.maximum;

            var dimensions = Cartesian3.subtract(maximum, minimum, cartesian3DimScratch);
            var hDim = maximumHeight - minimumHeight;
            var maxDim = Math.max(Cartesian3.maximumComponent(dimensions), hDim);

            if (maxDim < SHIFT_LEFT_12 - 1.0) {
                compression = TerrainQuantization.BITS12;
            } else {
                compression = TerrainQuantization.NONE;
            }

            center = axisAlignedBoundingBox.center;
            toENU = Matrix4.inverseTransformation(fromENU, new Matrix4());

            var translation = Cartesian3.negate(minimum, cartesian3Scratch);
            Matrix4.multiply(Matrix4.fromTranslation(translation, matrix4Scratch), toENU, toENU);

            var scale = cartesian3Scratch;
            scale.x = 1.0 / dimensions.x;
            scale.y = 1.0 / dimensions.y;
            scale.z = 1.0 / dimensions.z;
            Matrix4.multiply(Matrix4.fromScale(scale, matrix4Scratch), toENU, toENU);

            matrix = Matrix4.clone(fromENU);
            Matrix4.setTranslation(matrix, Cartesian3.ZERO, matrix);

            fromENU = Matrix4.clone(fromENU, new Matrix4());

            var translationMatrix = Matrix4.fromTranslation(minimum, matrix4Scratch);
            var scaleMatrix =  Matrix4.fromScale(dimensions, matrix4Scratch2);
            var st = Matrix4.multiply(translationMatrix, scaleMatrix,matrix4Scratch);

            Matrix4.multiply(fromENU, st, fromENU);
            Matrix4.multiply(matrix, st, matrix);
        }

        /**
         * How the vertices of the mesh were compressed.
         * @type {TerrainQuantization}
         */
        this.compression = compression;

        /**
         * The minimum height of the tile including the skirts.
         * @type {Number}
         */
        this.minimumHeight = minimumHeight;

        /**
         * The maximum height of the tile.
         * @type {Number}
         */
        this.maximumHeight = maximumHeight;

        /**
         * The center of the tile.
         * @type {Cartesian3}
         */
        this.center = center;

        /**
         * A matrix that takes a vertex from the tile, transforms it to east-north-up at the center and scales
         * it so each component is in the [0, 1] range.
         * @type {Matrix4}
         */
        this.toScaledENU = toENU;

        /**
         * A matrix that restores a vertex transformed with toScaledENU back to the earth fixed reference frame
         * @type {Matrix4}
         */
        this.fromScaledENU = fromENU;

        /**
         * The matrix used to decompress the terrain vertices in the shader for RTE rendering.
         * @type {Matrix4}
         */
        this.matrix = matrix;

        /**
         * The terrain mesh contains normals.
         * @type {Boolean}
         */
        this.hasVertexNormals = hasVertexNormals;
    };

    TerrainEncoding.prototype.encode = function(vertexBuffer, bufferIndex, position, uv, height, normalToPack) {
        var u = uv.x;
        var v = uv.y;

        if (this.compression === TerrainQuantization.BITS12) {
            position = Matrix4.multiplyByPoint(this.toScaledENU, position, cartesian3Scratch);

            position.x = CesiumMath.clamp(position.x, 0.0, 1.0);
            position.y = CesiumMath.clamp(position.y, 0.0, 1.0);
            position.z = CesiumMath.clamp(position.z, 0.0, 1.0);

            var hDim = this.maximumHeight - this.minimumHeight;
            var h = (height - this.minimumHeight) / hDim;

            Cartesian2.fromElements(position.x, position.y, cartesian2Scratch);
            var compressed0 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);

            Cartesian2.fromElements(position.z, h, cartesian2Scratch);
            var compressed1 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);

            Cartesian2.fromElements(u, v, cartesian2Scratch);
            var compressed2 = AttributeCompression.compressTextureCoordinates(cartesian2Scratch);

            vertexBuffer[bufferIndex++] = compressed0;
            vertexBuffer[bufferIndex++] = compressed1;
            vertexBuffer[bufferIndex++] = compressed2;
        } else {
            Cartesian3.subtract(position, this.center, cartesian3Scratch);

            vertexBuffer[bufferIndex++] = cartesian3Scratch.x;
            vertexBuffer[bufferIndex++] = cartesian3Scratch.y;
            vertexBuffer[bufferIndex++] = cartesian3Scratch.z;
            vertexBuffer[bufferIndex++] = height;
            vertexBuffer[bufferIndex++] = u;
            vertexBuffer[bufferIndex++] = v;
        }

        if (this.hasVertexNormals) {
            vertexBuffer[bufferIndex++] = AttributeCompression.octPackFloat(normalToPack);
        }

        return bufferIndex;
    };

    TerrainEncoding.prototype.decodePosition = function(buffer, index, result) {
        if (!defined(result)) {
            result = new Cartesian3();
        }

        index *= this.getStride();

        if (this.compression === TerrainQuantization.BITS12) {
            var xy = AttributeCompression.decompressTextureCoordinates(buffer[index], cartesian2Scratch);
            result.x = xy.x;
            result.y = xy.y;

            var zh = AttributeCompression.decompressTextureCoordinates(buffer[index + 1], cartesian2Scratch);
            result.z = zh.x;

            return Matrix4.multiplyByPoint(this.fromScaledENU, result, result);
        }

        result.x = buffer[index];
        result.y = buffer[index + 1];
        result.z = buffer[index + 2];
        return Cartesian3.add(result, this.center, result);
    };

    TerrainEncoding.prototype.getStride = function() {
        var vertexStride;

        switch (this.compression) {
            case TerrainQuantization.BITS12:
                vertexStride = 3;
                break;
            default:
                vertexStride = 6;
        }

        if (this.hasVertexNormals) {
            ++vertexStride;
        }

        return vertexStride;
    };

    var attributesNone = {
        position3DAndHeight : 0,
        textureCoordAndEncodedNormals : 1
    };
    var attributes = {
        compressed : 0
    };

    TerrainEncoding.prototype.getAttributes = function(buffer) {
        var datatype = ComponentDatatype.FLOAT;

        if (this.compression === TerrainQuantization.NONE) {
            var sizeInBytes = ComponentDatatype.getSizeInBytes(datatype);
            var position3DAndHeightLength = 4;
            var numTexCoordComponents = this.hasVertexNormals ? 3 : 2;
            var stride = (this.hasVertexNormals ? 7 : 6) * sizeInBytes;
            return [{
                index : attributesNone.position3DAndHeight,
                vertexBuffer : buffer,
                componentDatatype : datatype,
                componentsPerAttribute : position3DAndHeightLength,
                offsetInBytes : 0,
                strideInBytes : stride
            }, {
                index : attributesNone.textureCoordAndEncodedNormals,
                vertexBuffer : buffer,
                componentDatatype : datatype,
                componentsPerAttribute : numTexCoordComponents,
                offsetInBytes : position3DAndHeightLength * sizeInBytes,
                strideInBytes : stride
            }];
        }

        var numComponents = 3;
        numComponents += this.hasVertexNormals ? 1 : 0;
        return [{
            index : attributes.compressed,
            vertexBuffer : buffer,
            componentDatatype : datatype,
            componentsPerAttribute : numComponents
        }];
    };

    TerrainEncoding.prototype.getAttributeLocations = function() {
        if (this.compression === TerrainQuantization.NONE) {
            return attributesNone;
        } else {
            return attributes;
        }
    };

    /**
     * @param {TerrainEncoding} encoding
     * @param {TerrainEncoding} [result]
     * @returns {TerrainEncoding}
     */
    TerrainEncoding.clone = function(encoding, result) {
        if (!defined(result)) {
            result = new TerrainEncoding();
        }

        result.compression = encoding.compression;
        result.minimumHeight = encoding.minimumHeight;
        result.maximumHeight = encoding.maximumHeight;
        result.center = Cartesian3.clone(encoding.center);
        result.toScaledENU = Matrix4.clone(encoding.toScaledENU);
        result.fromScaledENU = Matrix4.clone(encoding.fromScaledENU);
        result.matrix = Matrix4.clone(encoding.matrix);
        result.hasVertexNormals = encoding.hasVertexNormals;
        return result;
    };

    return TerrainEncoding;
});
<|endoftext|>


<commit_before>// @noflow

'use strict';

const assert = require('assert');
const cp = require('child_process');

// Clocks the time taken to execute a test per cycle (secs).
function clock(count, fn) {
  const start = process.hrtime.bigint();
  for (let i = 0; i < count; ++i) {
    fn();
  }
  return Number(process.hrtime.bigint() - start);
}

if (require.main === module) {
  const modulePath = process.env.BENCHMARK_MODULE_PATH;
  assert(typeof modulePath === 'string');
  assert(process.send);
  const module = require(modulePath);

  clock(7, module.measure); // warm up
  global.gc();
  process.nextTick(() => {
    const memBaseline = process.memoryUsage().heapUsed;
    const clocked = clock(module.count, module.measure);
    process.send({
      name: module.name,
      clocked: clocked / module.count,
      memUsed: (process.memoryUsage().heapUsed - memBaseline) / module.count,
    });
  });
}

function sampleModule(modulePath) {
  return new Promise((resolve, reject) => {
    const env = { BENCHMARK_MODULE_PATH: modulePath };
    const child = cp.fork(__filename, { env });
    let message;
    let error;

    child.on('message', msg => (message = msg));
    child.on('error', e => (error = e));
    child.on('close', () => {
      if (message) {
        return resolve(message);
      }
      reject(error || new Error('Forked process closed without error'));
    });
  }).then(result => {
    global.gc();
    return result;
  });
}

module.exports = { sampleModule };
<commit_msg>Benchmark: set 'NODE_ENV=production' before running the benchmarks (#2434)<commit_after>// @noflow

'use strict';

const assert = require('assert');
const cp = require('child_process');

// Clocks the time taken to execute a test per cycle (secs).
function clock(count, fn) {
  const start = process.hrtime.bigint();
  for (let i = 0; i < count; ++i) {
    fn();
  }
  return Number(process.hrtime.bigint() - start);
}

if (require.main === module) {
  const modulePath = process.env.BENCHMARK_MODULE_PATH;
  assert(typeof modulePath === 'string');
  assert(process.send);
  const module = require(modulePath);

  clock(7, module.measure); // warm up
  global.gc();
  process.nextTick(() => {
    const memBaseline = process.memoryUsage().heapUsed;
    const clocked = clock(module.count, module.measure);
    process.send({
      name: module.name,
      clocked: clocked / module.count,
      memUsed: (process.memoryUsage().heapUsed - memBaseline) / module.count,
    });
  });
}

function sampleModule(modulePath) {
  return new Promise((resolve, reject) => {
    const env = {
      NODE_ENV: 'production',
      BENCHMARK_MODULE_PATH: modulePath,
    };
    const child = cp.fork(__filename, { env });
    let message;
    let error;

    child.on('message', msg => (message = msg));
    child.on('error', e => (error = e));
    child.on('close', () => {
      if (message) {
        return resolve(message);
      }
      reject(error || new Error('Forked process closed without error'));
    });
  }).then(result => {
    global.gc();
    return result;
  });
}

module.exports = { sampleModule };
<|endoftext|>


<commit_before>//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Directive for dealing with the Leaflet map
App.Directives.directive('timeline', function($rootScope) {
	return {
		restrict: 'C',
		replace: true,
		transclude: true,
		template: '

',
		link: function(scope, element, attrs) {

	    	google.setOnLoadCallback(initTimeline);

	    	function initTimeline() {
	            // Instantiate timeline object.
	            $rootScope.timeline = new links.Timeline(document.getElementById('OCWtimeline'));

				var options = {
						"width": "100%",
						"showCurrentTime": false,
						"moveable": false,
						'zoomable': false
				};

				$rootScope.timeline.draw([], options);
	        }
		}
	}
});
<commit_msg>CLIMATE-195 - Timeline doesn't redraw on window resize<commit_after>//
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements.See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License.You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

// Directive for dealing with the Leaflet map
App.Directives.directive('timeline', function($rootScope, $window) {
	return {
		restrict: 'C',
		replace: true,
		transclude: true,
		template: '

',
		link: function(scope, element, attrs) {

	    	google.setOnLoadCallback(initTimeline);

	    	function initTimeline() {
	            // Instantiate timeline object.
	            $rootScope.timeline = new links.Timeline(document.getElementById('OCWtimeline'));

				// Redraw the timeline whenever the window is resized
				angular.element($window).bind('resize', function() {
					$rootScope.timeline.checkResize();
				});

				var options = {
						"width": "100%",
						"showCurrentTime": false,
						"moveable": false,
						'zoomable': false
				};

				$rootScope.timeline.draw([], options);
	        }
		}
	}
});
<|endoftext|>