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

/**
This module is made for the case, you want to use a RemoteStorage as a JSON KeyValue Store.

The idea of the backendWrapper is to provide following features:

* automatic JSON serialize/deserialize to abstract that away from the remote driver and the module user.
* cache reads. A amount of KeyValues are hold in the memory, so that reading is faster.
* Buffer Backend Writings. Sets and deletes should be buffered to make them in a setted interval with a bulk. This reduces the overhead of remote transactions and makes the remote faster. But there is also a danger to loose data integrity, to keep that, we should provide a flush function.

All Features can be disabled or configured. The Wrapper provides default settings that can be overwriden by the driver and by the module user.
*/

var async = require("async");

var defaultSettings =
{
  //the number of elements that should be cached. To Disable cache just set it to zero
  cache: 1000,
  //the interval in ms the wrapper writes to the remote. To Disable interval writes just set it to zero
  writeInterval: 100,
  //a flag if the data sould be serialized/deserialized to json
  json:true
}

/**
 The constructor of the wrapper
 @param wrappedBackend The Backend that should be wrapped
 @param settings (optional) The settings that should be applied to the wrapper
*/
exports.remote = function(wrappedBackend, settings, logger)
{
  //saved the wrappedBackend
  this.wrappedBackend=wrappedBackend;
  this.logger = logger;

  //apply default settings
  this.settings               = {};
  this.settings.cache         = defaultSettings.cache;
  this.settings.writeInterval = defaultSettings.writeInterval;
  this.settings.json          = defaultSettings.json;

  //try to apply the settings of the driver
  if(wrappedBackend.settings != null)
  {
    if(wrappedBackend.settings.cache         != null)  this.settings.cache         = wrappedBackend.settings.cache;
    if(wrappedBackend.settings.writeInterval != null)  this.settings.writeInterval = wrappedBackend.settings.writeInterval;
    if(wrappedBackend.settings.json          != null)  this.settings.json          = wrappedBackend.settings.json;
  }

  //try to apply the settings given with the constructor
  if(settings != null)
  {
    if(settings.cache         != null)  this.settings.cache         = settings.cache;
    if(settings.writeInterval != null)  this.settings.writeInterval = settings.writeInterval;
    if(settings.json          != null)  this.settings.json          = settings.json;
  }

  //freeze the settings at this point
  this.settings = Object.freeze(this.settings);

  //start the write Interval
  if(this.settings.writeInterval > 0)
  {
    setInterval(flush, this.settings.writeInterval, this);
  }

  //set the flushing flag to false, this flag shows that there is a flushing action happing at the moment
  this.isFlushing = false;
};

/**
 key is the key of the keyValue Object
 value is a object
 {
   value: the value of the keyValue object, contains only deserialized data
   dirty: true or false, means if its already written to the remote or not
   callback: (optional) a array of callbacks that should be called once this value is written
   timestamp: a timestamp that shows when this item was read or write the last time.
              The Garbage collector needs this value
 }
*/
exports.remote.prototype.buffer = {};

//the length of the Buffer.
exports.remote.prototype.bufferLength = 0;

/**
 wraps the init function of the original DB
*/
exports.remote.prototype.init = function(callback)
{
  this.wrappedBackend.init(callback);
}

/**
 wraps the close function of the original DB
*/
exports.remote.prototype.close = function(callback)
{
  this.wrappedBackend.close(callback);
}

/**
 Calls the callback the next time all buffers are flushed
*/
exports.remote.prototype.doShutdown = function(callback)
{
  //wait until the buffer is fully written
  if(this.settings.writeInterval > 0)
    this.shutdownCallback = callback;
  //we write direct, so there is no need to wait for a callback
  else
    callback();
}

/**
 Gets the value trough the wrapper.
*/
exports.remote.prototype.get = function(key, callback)
{
  //if cache is enabled and data is in the cache, get the value from the cache
  if(this.settings.cache > 0 && this.buffer[key])
  {
    this.logger.debug("GET    - " + key + " - " + JSON.stringify(this.buffer[key].value) + " - from cache");
    this.buffer[key].timestamp = new Date().getTime();
    callback(null, this.buffer[key].value);
  }
  //caching is disabled but its still in a dirty writing cache, so we have to get the value out of the cache too
  else if(this.settings.cache == 0 && this.buffer[key] && this.buffer[key].dirty)
  {
    this.logger.debug("GET    - " + key + " - " + JSON.stringify(this.buffer[key].value) + " - from dirty buffer");
    this.buffer[key].timestamp = new Date().getTime();
    callback(null, this.buffer[key].value);
  }
  //get it direct
  else
  {
    var self = this;

    this.wrappedBackend.get(key, function(err,value)
    {
      if(self.settings.json)
      {
        try
        {
          value = JSON.parse(value);
        }
        catch(e)
        {
          console.error("JSON-PROBLEM:" + value);
          callback(e);
          return;
        }
      }

      //cache the value if caching is enabled
      if(self.settings.cache > 0)
        self.buffer[key] = {"value":value, dirty:false, timestamp: new Date().getTime(), writingInProgress: false};

      self.bufferLength++;

      //call the garbage collector
      self.gc();

      self.logger.debug("GET    - " + key + " - " + JSON.stringify(value) + " - from remote ");

      callback(err,value);
    });
  }
}

/**
 Sets the value trough the wrapper
*/
exports.remote.prototype.set = function(key, value, bufferCallback, writeCallback)
{
  //writing cache is enabled, so simply write it into the buffer
  if(this.settings.writeInterval > 0)
  {
    this.logger.debug("SET    - "+ key + " - " + JSON.stringify(value) + " - to buffer");

    //initalize the buffer object if it not exists
    if(!this.buffer[key])
    {
      this.buffer[key] = {};
      this.bufferLength++;
    }

    //set the new values
    this.buffer[key].value = value;
    this.buffer[key].dirty = true;
    this.buffer[key].timestamp = new Date().getTime();

    //call the garbage collector
    this.gc();

    //initalize the callback array in the buffer object if it not exists.
    //we need this as an array, cause the value may be many times overwritten bevors its finally written to the remote,
    //but all callbacks must be called
    if(!this.buffer[key].callbacks)
      this.buffer[key].callbacks=[];

    //add this callback to the array
    if(writeCallback) this.buffer[key].callbacks.push(writeCallback);
    else
    {
      this.buffer[key].callbacks.push(function(err)
      {
        if(err) throw err;
      });
    }

    //call the buffer callback
    if (bufferCallback) bufferCallback();
  }
  //writecache is disabled, so we write directly to the remote
  else
  {
    this.logger.debug("SET    - " + key + " - " + JSON.stringify(value) + " - to remote");

    //create a wrapper callback for write and buffer callback
    var callback = function (err)
    {
      if (bufferCallback) bufferCallback(err);
      if (writeCallback) writeCallback(err);
    }

    //The value is null, means this no set operation, this is a remove operation
    if(value==null)
    {
      this.wrappedBackend.remove(key,callback);
    }
    //thats a correct value
    else
    {
      //stringify the value if stringifying is enabled
      if(this.settings.json == true)
        value = JSON.stringify(value);

      this.wrappedBackend.set(key,value,callback);
    }
  }
}

/**
 Sets a subvalue
*/
exports.remote.prototype.setSub = function(key, sub, value, bufferCallback, writeCallback)
{
  var _this = this;

  this.logger.debug("SETSUB - " + key + JSON.stringify(sub) + " - "+ JSON.stringify(value));

  async.waterfall([
    //get the full value
    function(callback)
    {
      _this.get(key, callback);
    },
    //set the sub value and set the full value again
    function(fullValue, callback)
    {
      //get the subvalue parent
      var subvalueParent = fullValue;
      for (var i=0 ; i < (sub.length-1) ; i++)
      {
        //test if the subvalue exist
        if(subvalueParent != null && subvalueParent[sub[i]] !== undefined)
        {
          subvalueParent = subvalueParent[sub[i]];
        }
        //the subvalue doesn't exist, create it
        else
        {
          subvalueParent[sub[i]] = {};
          subvalueParent = subvalueParent[sub[i]];
        }
      }

      //set the subvalue, we're doing that with the parent element
      subvalueParent[sub[sub.length-1]] = value;

      _this.set(key, fullValue, bufferCallback, writeCallback);
    }
  ],function(err)
  {
    if(callback) callback(err);
    else if(err != null) throw err;
  })
}

/**
 Returns a sub value of the object
 @param sub is a array, for example if you want to access object.test.bla, the array is ["test", "bla"]
*/
exports.remote.prototype.getSub = function(key, sub, callback)
{
  var _this = this;

  //get the full value
  this.get(key, function (err, value)
  {
    //there happens an errror while getting this value, call callback
    if(err)
    {
      callback(err);
    }
    //everything is correct, navigate to the subvalue and return it
    else
    {
      var subvalue = value;

      for (var i=0 ; i<sub.length ; i++)
      {
        //test if the subvalue exist
        if(subvalue != null && subvalue[sub[i]] !== undefined)
        {
          subvalue = subvalue[sub[i]];
        }
        //the subvalue doesn't exist, break the loop and return null
        else
        {
          subvalue = null;
          break;
        }
      }

      _this.logger.debug("GETSUB - " + key + JSON.stringify(sub) + " - " + JSON.stringify(subvalue));
      callback(err, subvalue);
    }
  });
}

/**
 Removes the value trough the wrapper
*/
exports.remote.prototype.remove = function(key, bufferCallback, writeCallback)
{
  this.logger.debug("REMOVE - " + key);

  //make a set to null out of it
  this.set(key, null, bufferCallback, writeCallback);
}

/**
 Garbage Collector of the cache
*/
exports.remote.prototype.gc = function()
{
  //If the buffer size is under the settings size or cache is disabled -> return cause there is nothing to do
  if(this.bufferLength < this.settings.cache || this.settings.cache == 0)
  {
    return;
  }

  //collect all values that are not dirty
  var deleteCandidates = [];
  for(var i in this.buffer)
  {
    if(this.buffer[i].dirty == false && this.buffer[i].writingInProgress == false)
    {
      deleteCandidates.push({key: i, timestamp: this.buffer[i].timestamp});
    }
  }

  if(deleteCandidates.length > 0)
  {
    //sort them based on the timestamp
    deleteCandidates.sort(function(a,b){
      return a.timestamp-b.timestamp;
    });

    var collected = 0;

    //delete the half buffer
    for(var i=0; (this.bufferLength > this.settings.cache/2) && i<deleteCandidates.length;i++)
    {
      delete this.buffer[deleteCandidates[i].key];
      this.bufferLength--;
      collected++;
    }

    this.logger.info("garbage collected "+ collected+ " values");
  }
}

/**
 Writes all dirty values to the remote
*/
function flush (backend, callback)
{
  //return if there is a flushing action in process
  if(backend.isFlushing)
  {
    if(callback) callback();
    return;
  }

  var operations = [];
  var callbacks = [];

  //run trough the buffer and search for dirty values
  for(var i in backend.buffer)
  {
    if(backend.buffer[i].dirty == true)
    {
      //collect all data for the operation
      var type  = backend.buffer[i].value == null ? "remove" : "set";
      var key   = i;
      var value = backend.buffer[i].value;

      //stringify the value if stringifying is enabled
      if(backend.settings.json == true && value != null)
        value = JSON.stringify(value);
      else
        value = clone(value);

      //add the operation to the operations array
      operations.push({"type":type, "key":key, "value":value});

      //collect callbacks
      callbacks = callbacks.concat(backend.buffer[i].callbacks);

      //clean callbacks
      backend.buffer[i].callbacks = [];
      //set the dirty flag to false
      backend.buffer[i].dirty = false;
      //set the writingInProgress flag to true
      backend.buffer[i].writingInProgress = true;
    }
  }

  //send the bulk to the remote driver and call the callbacks with the results
  if(operations.length > 0)
  {
    backend.logger.info("Flushed " + operations.length + " values");

    //set the flushing flag
    backend.isFlushing = true;

    backend.wrappedBackend.doBulk(operations, function(err)
    {
      //call all writingCallbacks
      for(var i in callbacks)
      {
        callbacks[i](err);
      }

      //set the writingInProgress flag to false
      for(var i in operations)
      {
        backend.buffer[operations[i].key].writingInProgress = false;
      }

      if(callback) callback();

      //call the garbage collector
      backend.gc();

      //set the flushing flag to false
      backend.isFlushing = false;
    });
  }
  //the writing buffer is empty and there is a shutdown callback, call it!
  else if(backend.shutdownCallback != null)
  {
    backend.shutdownCallback();
    backend.shutdownCallback = null;
  }
}

function clone(obj)
{
  // Handle the 3 simple types, and null or undefined
  if (null == obj || "object" != typeof obj) return obj;

  // Handle Date
  if (obj instanceof Date)
  {
    var copy = new Date();
    copy.setTime(obj.getTime());
    return copy;
  }

  // Handle Array
  if (obj instanceof Array)
  {
    var copy = [];
    for (var i = 0, len = obj.length; i < len; ++i)
    {
      copy[i] = clone(obj[i]);
    }
    return copy;
  }

  // Handle Object
  if (obj instanceof Object)
  {
    var copy = {};
    for (var attr in obj)
    {
      if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
    }
    return copy;
  }

  throw new Error("Unable to copy obj! Its type isn't supported.");
}

<commit_msg>we were sharing the same buffer for different remoteStorages d'oh<commit_after>/**
 * 2011 Peter 'Pita' Martischka
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS-IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/**
This module is made for the case, you want to use a RemoteStorage as a JSON KeyValue Store.

The idea of the backendWrapper is to provide following features:

* automatic JSON serialize/deserialize to abstract that away from the remote driver and the module user.
* cache reads. A amount of KeyValues are hold in the memory, so that reading is faster.
* Buffer Backend Writings. Sets and deletes should be buffered to make them in a setted interval with a bulk. This reduces the overhead of remote transactions and makes the remote faster. But there is also a danger to loose data integrity, to keep that, we should provide a flush function.

All Features can be disabled or configured. The Wrapper provides default settings that can be overwriden by the driver and by the module user.
*/

var async = require("async");

var defaultSettings =
{
  //the number of elements that should be cached. To Disable cache just set it to zero
  cache: 1000,
  //the interval in ms the wrapper writes to the remote. To Disable interval writes just set it to zero
  writeInterval: 100,
  //a flag if the data sould be serialized/deserialized to json
  json:true
}

/**
 The constructor of the wrapper
 @param wrappedBackend The Backend that should be wrapped
 @param settings (optional) The settings that should be applied to the wrapper
*/
exports.remote = function(wrappedBackend, settings, logger)
{
  //saved the wrappedBackend
  this.wrappedBackend=wrappedBackend;
  this.logger = logger;

  //apply default settings
  this.settings               = {};
  this.settings.cache         = defaultSettings.cache;
  this.settings.writeInterval = defaultSettings.writeInterval;
  this.settings.json          = defaultSettings.json;

  //try to apply the settings of the driver
  if(wrappedBackend.settings != null)
  {
    if(wrappedBackend.settings.cache         != null)  this.settings.cache         = wrappedBackend.settings.cache;
    if(wrappedBackend.settings.writeInterval != null)  this.settings.writeInterval = wrappedBackend.settings.writeInterval;
    if(wrappedBackend.settings.json          != null)  this.settings.json          = wrappedBackend.settings.json;
  }

  //try to apply the settings given with the constructor
  if(settings != null)
  {
    if(settings.cache         != null)  this.settings.cache         = settings.cache;
    if(settings.writeInterval != null)  this.settings.writeInterval = settings.writeInterval;
    if(settings.json          != null)  this.settings.json          = settings.json;
  }

  //freeze the settings at this point
  this.settings = Object.freeze(this.settings);

  /**
   key is the key of the keyValue Object
   value is a object
   {
     value: the value of the keyValue object, contains only deserialized data
     dirty: true or false, means if its already written to the remote or not
     callback: (optional) a array of callbacks that should be called once this value is written
     timestamp: a timestamp that shows when this item was read or write the last time.
                The Garbage collector needs this value
   }
   I moved this into the remote so it's per remote and not shared.
  */
  this.buffer = {};

  //the length of the Buffer.
  this.bufferLength = 0;

  //start the write Interval
  if(this.settings.writeInterval > 0)
  {
    setInterval(flush, this.settings.writeInterval, this);
  }

  //set the flushing flag to false, this flag shows that there is a flushing action happing at the moment
  this.isFlushing = false;
};

/**
 wraps the init function of the original DB
*/
exports.remote.prototype.init = function(callback)
{
  this.wrappedBackend.init(callback);
}

/**
 wraps the close function of the original DB
*/
exports.remote.prototype.close = function(callback)
{
  this.wrappedBackend.close(callback);
}

/**
 Calls the callback the next time all buffers are flushed
*/
exports.remote.prototype.doShutdown = function(callback)
{
  //wait until the buffer is fully written
  if(this.settings.writeInterval > 0)
    this.shutdownCallback = callback;
  //we write direct, so there is no need to wait for a callback
  else
    callback();
}

/**
 Gets the value trough the wrapper.
*/
exports.remote.prototype.get = function(key, callback)
{
  //if cache is enabled and data is in the cache, get the value from the cache
  if(this.settings.cache > 0 && this.buffer[key])
  {
    this.logger.debug("GET    - " + key + " - " + JSON.stringify(this.buffer[key].value) + " - from cache");
    this.buffer[key].timestamp = new Date().getTime();
    callback(null, this.buffer[key].value);
  }
  //caching is disabled but its still in a dirty writing cache, so we have to get the value out of the cache too
  else if(this.settings.cache == 0 && this.buffer[key] && this.buffer[key].dirty)
  {
    this.logger.debug("GET    - " + key + " - " + JSON.stringify(this.buffer[key].value) + " - from dirty buffer");
    this.buffer[key].timestamp = new Date().getTime();
    callback(null, this.buffer[key].value);
  }
  //get it direct
  else
  {
    var self = this;

    this.wrappedBackend.get(key, function(err,value)
    {
      if(self.settings.json)
      {
        try
        {
          value = JSON.parse(value);
        }
        catch(e)
        {
          console.error("JSON-PROBLEM:" + value);
          callback(e);
          return;
        }
      }

      //cache the value if caching is enabled
      if(self.settings.cache > 0)
        self.buffer[key] = {"value":value, dirty:false, timestamp: new Date().getTime(), writingInProgress: false};

      self.bufferLength++;

      //call the garbage collector
      self.gc();

      self.logger.debug("GET    - " + key + " - " + JSON.stringify(value) + " - from remote ");

      callback(err,value);
    });
  }
}

/**
 Sets the value trough the wrapper
*/
exports.remote.prototype.set = function(key, value, bufferCallback, writeCallback)
{
  //writing cache is enabled, so simply write it into the buffer
  if(this.settings.writeInterval > 0)
  {
    this.logger.debug("SET    - "+ key + " - " + JSON.stringify(value) + " - to buffer");

    //initalize the buffer object if it not exists
    if(!this.buffer[key])
    {
      this.buffer[key] = {};
      this.bufferLength++;
    }

    //set the new values
    this.buffer[key].value = value;
    this.buffer[key].dirty = true;
    this.buffer[key].timestamp = new Date().getTime();

    //call the garbage collector
    this.gc();

    //initalize the callback array in the buffer object if it not exists.
    //we need this as an array, cause the value may be many times overwritten bevors its finally written to the remote,
    //but all callbacks must be called
    if(!this.buffer[key].callbacks)
      this.buffer[key].callbacks=[];

    //add this callback to the array
    if(writeCallback) this.buffer[key].callbacks.push(writeCallback);
    else
    {
      this.buffer[key].callbacks.push(function(err)
      {
        if(err) throw err;
      });
    }

    //call the buffer callback
    if (bufferCallback) bufferCallback();
  }
  //writecache is disabled, so we write directly to the remote
  else
  {
    this.logger.debug("SET    - " + key + " - " + JSON.stringify(value) + " - to remote");

    //create a wrapper callback for write and buffer callback
    var callback = function (err)
    {
      if (bufferCallback) bufferCallback(err);
      if (writeCallback) writeCallback(err);
    }

    //The value is null, means this no set operation, this is a remove operation
    if(value==null)
    {
      this.wrappedBackend.remove(key,callback);
    }
    //thats a correct value
    else
    {
      //stringify the value if stringifying is enabled
      if(this.settings.json == true)
        value = JSON.stringify(value);

      this.wrappedBackend.set(key,value,callback);
    }
  }
}

/**
 Sets a subvalue
*/
exports.remote.prototype.setSub = function(key, sub, value, bufferCallback, writeCallback)
{
  var _this = this;

  this.logger.debug("SETSUB - " + key + JSON.stringify(sub) + " - "+ JSON.stringify(value));

  async.waterfall([
    //get the full value
    function(callback)
    {
      _this.get(key, callback);
    },
    //set the sub value and set the full value again
    function(fullValue, callback)
    {
      //get the subvalue parent
      var subvalueParent = fullValue;
      for (var i=0 ; i < (sub.length-1) ; i++)
      {
        //test if the subvalue exist
        if(subvalueParent != null && subvalueParent[sub[i]] !== undefined)
        {
          subvalueParent = subvalueParent[sub[i]];
        }
        //the subvalue doesn't exist, create it
        else
        {
          subvalueParent[sub[i]] = {};
          subvalueParent = subvalueParent[sub[i]];
        }
      }

      //set the subvalue, we're doing that with the parent element
      subvalueParent[sub[sub.length-1]] = value;

      _this.set(key, fullValue, bufferCallback, writeCallback);
    }
  ],function(err)
  {
    if(callback) callback(err);
    else if(err != null) throw err;
  })
}

/**
 Returns a sub value of the object
 @param sub is a array, for example if you want to access object.test.bla, the array is ["test", "bla"]
*/
exports.remote.prototype.getSub = function(key, sub, callback)
{
  var _this = this;

  //get the full value
  this.get(key, function (err, value)
  {
    //there happens an errror while getting this value, call callback
    if(err)
    {
      callback(err);
    }
    //everything is correct, navigate to the subvalue and return it
    else
    {
      var subvalue = value;

      for (var i=0 ; i<sub.length ; i++)
      {
        //test if the subvalue exist
        if(subvalue != null && subvalue[sub[i]] !== undefined)
        {
          subvalue = subvalue[sub[i]];
        }
        //the subvalue doesn't exist, break the loop and return null
        else
        {
          subvalue = null;
          break;
        }
      }

      _this.logger.debug("GETSUB - " + key + JSON.stringify(sub) + " - " + JSON.stringify(subvalue));
      callback(err, subvalue);
    }
  });
}

/**
 Removes the value trough the wrapper
*/
exports.remote.prototype.remove = function(key, bufferCallback, writeCallback)
{
  this.logger.debug("REMOVE - " + key);

  //make a set to null out of it
  this.set(key, null, bufferCallback, writeCallback);
}

/**
 Garbage Collector of the cache
*/
exports.remote.prototype.gc = function()
{
  //If the buffer size is under the settings size or cache is disabled -> return cause there is nothing to do
  if(this.bufferLength < this.settings.cache || this.settings.cache == 0)
  {
    return;
  }

  //collect all values that are not dirty
  var deleteCandidates = [];
  for(var i in this.buffer)
  {
    if(this.buffer[i].dirty == false && this.buffer[i].writingInProgress == false)
    {
      deleteCandidates.push({key: i, timestamp: this.buffer[i].timestamp});
    }
  }

  if(deleteCandidates.length > 0)
  {
    //sort them based on the timestamp
    deleteCandidates.sort(function(a,b){
      return a.timestamp-b.timestamp;
    });

    var collected = 0;

    //delete the half buffer
    for(var i=0; (this.bufferLength > this.settings.cache/2) && i<deleteCandidates.length;i++)
    {
      delete this.buffer[deleteCandidates[i].key];
      this.bufferLength--;
      collected++;
    }

    this.logger.info("garbage collected "+ collected+ " values");
  }
}

/**
 Writes all dirty values to the remote
*/
function flush (backend, callback)
{
  //return if there is a flushing action in process
  if(backend.isFlushing)
  {
    if(callback) callback();
    return;
  }

  var operations = [];
  var callbacks = [];

  //run trough the buffer and search for dirty values
  for(var i in backend.buffer)
  {
    if(backend.buffer[i].dirty == true)
    {
      //collect all data for the operation
      var type  = backend.buffer[i].value == null ? "remove" : "set";
      var key   = i;
      var value = backend.buffer[i].value;

      //stringify the value if stringifying is enabled
      if(backend.settings.json == true && value != null)
        value = JSON.stringify(value);
      else
        value = clone(value);

      //add the operation to the operations array
      operations.push({"type":type, "key":key, "value":value});

      //collect callbacks
      callbacks = callbacks.concat(backend.buffer[i].callbacks);

      //clean callbacks
      backend.buffer[i].callbacks = [];
      //set the dirty flag to false
      backend.buffer[i].dirty = false;
      //set the writingInProgress flag to true
      backend.buffer[i].writingInProgress = true;
    }
  }

  //send the bulk to the remote driver and call the callbacks with the results
  if(operations.length > 0)
  {
    backend.logger.info("Flushed " + operations.length + " values");

    //set the flushing flag
    backend.isFlushing = true;

    backend.wrappedBackend.doBulk(operations, function(err)
    {
      //call all writingCallbacks
      for(var i in callbacks)
      {
        callbacks[i](err);
      }

      //set the writingInProgress flag to false
      for(var i in operations)
      {
        backend.buffer[operations[i].key].writingInProgress = false;
      }

      if(callback) callback();

      //call the garbage collector
      backend.gc();

      //set the flushing flag to false
      backend.isFlushing = false;
    });
  }
  //the writing buffer is empty and there is a shutdown callback, call it!
  else if(backend.shutdownCallback != null)
  {
    backend.shutdownCallback();
    backend.shutdownCallback = null;
  }
}

function clone(obj)
{
  // Handle the 3 simple types, and null or undefined
  if (null == obj || "object" != typeof obj) return obj;

  // Handle Date
  if (obj instanceof Date)
  {
    var copy = new Date();
    copy.setTime(obj.getTime());
    return copy;
  }

  // Handle Array
  if (obj instanceof Array)
  {
    var copy = [];
    for (var i = 0, len = obj.length; i < len; ++i)
    {
      copy[i] = clone(obj[i]);
    }
    return copy;
  }

  // Handle Object
  if (obj instanceof Object)
  {
    var copy = {};
    for (var attr in obj)
    {
      if (obj.hasOwnProperty(attr)) copy[attr] = clone(obj[attr]);
    }
    return copy;
  }

  throw new Error("Unable to copy obj! Its type isn't supported.");
}

<|endoftext|>


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

/**
 * model of a cloud that can block energy coming from the sun
 *
 * @author  John Blanco
 * @author  Andrew Adare
 */
define( function( require ) {
  'use strict';

  // modules
  var energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  var inherit = require( 'PHET_CORE/inherit' );
  var NumberProperty = require( 'AXON/NumberProperty' );
  var Shape = require( 'KITE/Shape' );

  // constants
  var WIDTH = 0.035; // In meters, though obviously not to scale.  Empirically determined.
  var HEIGHT = WIDTH * 0.55; // determined from the approximate aspect ratio of the image

  /**
   * @param {Vector2} offsetFromParent
   * @param {Property.<Vector2>} parentPositionProperty
   * @constructor
   */
  function Cloud( offsetFromParent, parentPositionProperty ) {
    var self = this;

    // @public {NumberProperty} - existence strength, which basically translates to opacity, of the cloud
    this.existenceStrengthProperty = new NumberProperty( 1.0 );

    // @public (read-only) {number} - offset position for this cloud
    this.offsetFromParent = offsetFromParent;

    // @private {number} - used to calculate this cloud's position
    this.parentPositionProperty = parentPositionProperty;

    this.cloudEllipse = null;

    this.parentPositionProperty.link( function( parentPosition ) {
      var center = parentPosition.plus( self.offsetFromParent );
      self.cloudEllipse = Shape.ellipse( center.x, center.y, WIDTH / 2, HEIGHT / 2, 0, 0, 0, false );
    } );
  }

  energyFormsAndChanges.register( 'Cloud', Cloud );

  return inherit( Object, Cloud, {

    /**
     * return ellipse with size of this cloud
     * @returns {Shape.ellipse} - ellipse with axes sized to width and height of cloud
     * @public
     */
    getCloudAbsorptionReflectionShape: function() {
      return this.cloudEllipse;
    },

    /**
     * @returns {Vector2} Center position of cloud
     * @public
     */
    getCenterPosition: function() {
      return this.parentPositionProperty.get().plus( this.offsetFromParent );
    }

  }, {

    // statics
    WIDTH: WIDTH,
    HEIGHT: HEIGHT
  } );
} );

<commit_msg>Convert to ES6, see https://github.com/phetsims/energy-forms-and-changes/issues/211<commit_after>// Copyright 2016-2019, University of Colorado Boulder

/**
 * model of a cloud that can block energy coming from the sun
 *
 * @author  John Blanco
 * @author  Andrew Adare
 */
define( require => {
  'use strict';

  // modules
  const energyFormsAndChanges = require( 'ENERGY_FORMS_AND_CHANGES/energyFormsAndChanges' );
  const NumberProperty = require( 'AXON/NumberProperty' );
  const Shape = require( 'KITE/Shape' );

  // constants
  const WIDTH = 0.035; // In meters, though obviously not to scale.  Empirically determined.
  const HEIGHT = WIDTH * 0.55; // determined from the approximate aspect ratio of the image

  class Cloud {

    /**
     * @param {Vector2} offsetFromParent
     * @param {Property.<Vector2>} parentPositionProperty
     */
    constructor( offsetFromParent, parentPositionProperty ) {

      // @public {NumberProperty} - existence strength, which basically translates to opacity, of the cloud
      this.existenceStrengthProperty = new NumberProperty( 1.0 );

      // @public (read-only) {number} - offset position for this cloud
      this.offsetFromParent = offsetFromParent;

      // @private {number} - used to calculate this cloud's position
      this.parentPositionProperty = parentPositionProperty;

      this.cloudEllipse = null;

      this.parentPositionProperty.link( parentPosition => {
        const center = parentPosition.plus( this.offsetFromParent );
        this.cloudEllipse = Shape.ellipse( center.x, center.y, WIDTH / 2, HEIGHT / 2, 0, 0, 0, false );
      } );
    }

    /**
     * return ellipse with size of this cloud
     * @returns {Shape.ellipse} - ellipse with axes sized to width and height of cloud
     * @public
     */
    getCloudAbsorptionReflectionShape() {
      return this.cloudEllipse;
    }

    /**
     * @returns {Vector2} Center position of cloud
     * @public
     */
    getCenterPosition() {
      return this.parentPositionProperty.get().plus( this.offsetFromParent );
    }

  }

  // statics
  Cloud.WIDTH = WIDTH;
  Cloud.HEIGHT = HEIGHT;

  return energyFormsAndChanges.register( 'Cloud', Cloud );
} );

<|endoftext|>


<commit_before>// Polyfill for the Math.sign function, only available in some browsers
Math.sign = Math.sign || function(x) {return (x < 0) ? -1 : ((x > 0) ? 1 : 0); }

L.PolylineOffset = {
  translatePoint: function(pt, dist, radians) {
    // Y coordinates expand downward, hence the minus
    return L.point(pt.x + dist * Math.cos(radians), pt.y - dist * Math.sin(radians));
  },

  /**
  Computes the angle of the vecteur a->b
  as a radian angle between -Pi and Pi.
  */
  radianAngle: function(a, b) {
    // vertical
    if (a.x == b.x) {
      return Math.sign(a.y - b.y) * Math.PI / 2;  // Inverted Y coords
    }
    // horizontal
    if (a.y == b.y) {
      return (a.x < b.x) ? 0 : Math.PI;
    }
    // general case
    return Math.sign(a.y - b.y) * Math.atan2(Math.abs(b.y - a.y), b.x - a.x);
  },

  offsetPointLine: function(points, distance) {
    var l = points.length;
    if (l < 2)
      throw new Error('Line should be defined by at least 2 points');

    var a = points[0], b;
    var segmentAngle;
    var deltaAngle = -Math.PI / 2,
        offsetAngle;
    var offsetSegments = [];

    for(var i=1; i < l; i++) {
      b = points[i];
      segmentAngle = this.radianAngle(a, b);
      offsetAngle = this._normalizeAngle(segmentAngle + deltaAngle);

      // store offset point and other information to avoid recomputing it later
      offsetSegments.push({
        angle: segmentAngle,
        offsetAngle: offsetAngle,
        distance: distance,
        original: [a, b],
        offset: [
          this.translatePoint(a, distance, offsetAngle),
          this.translatePoint(b, distance, offsetAngle)
        ]
      });
      a = b;
    }

    return offsetSegments;
  },

  latLngsToPoints: function(ll, map) {
    var pts = [];
    for(var i=0, l=ll.length; i<l; i++) {
      pts[i] = map.project(ll[i]);
    }
    return pts;
  },

  pointsToLatLngs: function(pts, map) {
    var ll = [];
    for(var i=0, l=pts.length; i<l; i++) {
      ll[i] = map.unproject(pts[i]);
    }
    return ll;
  },

  offsetLatLngs: function(ll, offset, map) {
    var offsetPoints = this.offsetLatLngsToPoints(ll, offset, map);
    return this.pointsToLatLngs(offsetPoints, map);
  },

  offsetLatLngsToPoints: function(ll, offset, map) {
    var origPoints = this.latLngsToPoints(ll, map);
    return this.offsetPoints(origPoints, offset);
  },

  offsetPoints: function(pts, offset) {
    var offsetSegments = this.offsetPointLine(pts, offset);
    return this.joinLineSegments(offsetSegments, offset, 'round');
  },

  /**
  Return the intersection point of two lines defined by two points each
  */
  intersection: function(l1a, l1b, l2a, l2b) {
    var line1 = this.lineEquation(l1a, l1b),
        line2 = this.lineEquation(l2a, l2b);

    if(line1.hasOwnProperty('x')) {
      return L.point(line1.x, line2.a * line1.x + line2.b);
    }
    if(line2.hasOwnProperty('x')) {
      return L.point(line2.x, line1.a * line2.x + line1.b);
    }

    var x = (line2.b - line1.b) / (line1.a - line2.a),
        y = line1.a * x + line1.b;

    return L.point(x, y);
  },

  /**
  Find the coefficients (a,b) of a line of equation y = a.x + b,
  or the constant x for vertical lines
  */
  lineEquation: function(pt1, pt2) {
    if (pt1.x == pt2.x) {
      return {
        x: pt1.x
      }
    }

    var a = (pt2.y - pt1.y) / (pt2.x - pt1.x),
        b = pt1.y - a * pt1.x;

    return {
      a: a,
      b: b
    }
  },

  /**
  Normalize a radian angle so it's expressed between (-Pi; Pi).
  */
  _normalizeAngle: function(rad) {
    // TODO: find a better way (?)
    rad += Math.PI;
    var pipi = 2 * Math.PI;

    while(rad > pipi) {
      rad = rad - pipi;
    }

    while(rad < 0) {
      rad = rad + pipi;
    }

    return rad - Math.PI;
  },

  /**
  Join 2 line segments defined by 2 points each,
  with a specified methodnormalizeAngle( (default : intersection);
  */
  joinSegments: function(s1, s2, offset, joinStyle) {
    var jointPoints;
    // for inward joints, just intersect
    if((Math.sign(this._normalizeAngle(s2.offsetAngle - s1.offsetAngle)) != Math.sign(offset))) {
      joinStyle = 'intersection';
    }
    switch(joinStyle) {
      case 'round':
        jointPoints = this.circularArc(s1.original[1], s1.distance, s1.offsetAngle, s2.offsetAngle, (offset > 0));
        break;
      case 'cut':
        jointPoints = [
          this.intersection(s1.offset[0], s1.offset[1], s2.original[0], s2.original[1]),
          this.intersection(s1.original[0], s1.original[1], s2.offset[0], s2.offset[1])
        ];
        break;
      case 'straight':
        jointPoints = [s1.offset[1], s2.offset[0]];
        break;
      case 'intersection':
      default:
        jointPoints = [this.intersection(s1.offset[0], s1.offset[1], s2.offset[0], s2.offset[1])];
    }
    return jointPoints;
  },

  joinLineSegments: function(segments, offset, joinStyle) {
    var l = segments.length;
    var joinedPoints = [];
    var s1 = segments[0], s2 = segments[0];
    joinedPoints.push(s1.offset[0]);

    for(var i=1; i<l; i++) {
      s2 = segments[i];
      joinedPoints = joinedPoints.concat(this.joinSegments(s1, s2, offset, joinStyle));
      s1 = s2;
    }
    joinedPoints.push(s2.offset[1]);

    return joinedPoints;
  },

  /**
  Interpolates points on a circular arc given a center,
  two angles, and a direction of rotation
  */
  circularArc: function(center, radius, startAngle, endAngle, trigoDir, angleInc) {
      var points = [];
      var angleInc = angleInc || Math.PI / 16;

      // negative order of rotation
      if(!trigoDir) {
        angleInc = -angleInc;
      }

      // TODO: fix so that the first and final angles are equal
      for(var alpha = startAngle; true; ) {
        points.push(this.translatePoint(center, radius, alpha));
        alpha = this._normalizeAngle(alpha + angleInc);
        if(Math.abs(alpha - endAngle) < Math.abs(angleInc)) {
          break;
        }
      }

      return points;
  }
}

// Modify the L.Polyline class by overwriting the projection function,
// to add offset related code
// Versions < 0.8
if(L.version.charAt(0) == '0' && parseInt(L.version.charAt(2)) < 8) {
  L.Polyline.include({
    projectLatlngs: function() {
      this._originalPoints = [];

      for (var i = 0, len = this._latlngs.length; i < len; i++) {
        this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
      }
      // Offset management hack ---
      if(this.options.offset) {
        this._originalPoints = L.PolylineOffset.offsetPoints(this._originalPoints, this.options.offset);
      }
      // Offset management hack END ---
    }
  });
} else {
// Versions >= 0.8
  L.Polyline.include({
    _projectLatlngs: function (latlngs, result) {
      var flat = latlngs[0] instanceof L.LatLng,
          len = latlngs.length,
          i, ring;

      if (flat) {
        ring = [];
        for (i = 0; i < len; i++) {
          ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
        }
        // Offset management hack ---
        if(this.options.offset) {
          ring = L.PolylineOffset.offsetPoints(ring, this.options.offset);
        }
        // Offset management hack END ---
        result.push(ring);
      } else {
        for (i = 0; i < len; i++) {
          this._projectLatlngs(latlngs[i], result);
        }
      }
    }
  });
}

L.Polyline.include({
  setOffset: function(offset) {
    this.options.offset = offset;
    this.redraw();
    return this;
  }
});
<commit_msg>Fix turn-around issues and make algorithms more able to cope with zero-distance calculations<commit_after>L.PolylineOffset = {
  translatePoint: function(pt, dist, radians) {
    return L.point(pt.x + dist * Math.cos(radians), pt.y + dist * Math.sin(radians));
  },

  offsetPointLine: function(points, distance) {
    var l = points.length;
    if (l < 2)
      throw new Error('Line should be defined by at least 2 points');

    var a = points[0], b;
    var offsetAngle, segmentAngle;
    var offsetSegments = [];

    for(var i=1; i < l; i++) {
      b = points[i];
      // angle in (-PI, PI]
      segmentAngle = Math.atan2(a.y - b.y, a.x - b.x);
      // angle in (-1.5 * PI, PI/2]
      offsetAngle = segmentAngle - Math.PI/2;

      // store offset point and other information to avoid recomputing it later
      offsetSegments.push({
        angle: segmentAngle,
        offsetAngle: offsetAngle,
        distance: distance,
        original: [a, b],
        offset: [
          this.translatePoint(a, distance, offsetAngle),
          this.translatePoint(b, distance, offsetAngle)
        ]
      });
      a = b;
    }

    return offsetSegments;
  },

  latLngsToPoints: function(ll, map) {
    var pts = [];
    for(var i=0, l=ll.length; i<l; i++) {
      pts[i] = map.project(ll[i]);
    }
    return pts;
  },

  pointsToLatLngs: function(pts, map) {
    var ll = [];
    for(var i=0, l=pts.length; i<l; i++) {
      ll[i] = map.unproject(pts[i]);
    }
    return ll;
  },

  offsetLatLngs: function(ll, offset, map) {
    var offsetPoints = this.offsetLatLngsToPoints(ll, offset, map);
    return this.pointsToLatLngs(offsetPoints, map);
  },

  offsetLatLngsToPoints: function(ll, offset, map) {
    var origPoints = this.latLngsToPoints(ll, map);
    return this.offsetPoints(origPoints, offset);
  },

  offsetPoints: function(pts, offset) {
    var offsetSegments = this.offsetPointLine(pts, offset);
    return this.joinLineSegments(offsetSegments, offset, 'round');
  },

  /**
  Return the intersection point of two lines defined by two points each
  Return null when there's no unique intersection
  */
  intersection: function(l1a, l1b, l2a, l2b) {
    var line1 = this.lineEquation(l1a, l1b),
        line2 = this.lineEquation(l2a, l2b);

    if (line1 == null || line2 == null)
      return null;

    if(line1.hasOwnProperty('x')) {
      if(line2.hasOwnProperty('x'))
        return null;
      return L.point(line1.x, line2.a * line1.x + line2.b);
    }
    if(line2.hasOwnProperty('x'))
      return L.point(line2.x, line1.a * line2.x + line1.b);

    if (line1.a == line2.a)
      return null;

    var x = (line2.b - line1.b) / (line1.a - line2.a),
        y = line1.a * x + line1.b;

    return L.point(x, y);
  },

  /**
  Find the coefficients (a,b) of a line of equation y = a.x + b,
  or the constant x for vertical lines
  Return null if there's no equation possible
  */
  lineEquation: function(pt1, pt2) {
    if (pt1.x != pt2.x)
    {
      var a = (pt2.y - pt1.y) / (pt2.x - pt1.x);
      return {
        a: a,
        b: pt1.y - a * pt1.x
      };
    }

    if (pt1.y != pt2.y)
      return { x: pt1.x };

    return null;
  },

  /**
  Join 2 line segments defined by 2 points each,
  with a specified methodnormalizeAngle( (default : intersection);
  */
  joinSegments: function(s1, s2, offset, joinStyle) {
    var jointPoints = [];
    switch(joinStyle) {
      case 'round':
        jointPoints = this.circularArc(s1, s2, offset);
        break;
      case 'cut':
        jointPoints = [
          this.intersection(s1.offset[0], s1.offset[1], s2.original[0], s2.original[1]),
          this.intersection(s1.original[0], s1.original[1], s2.offset[0], s2.offset[1])
        ];
        break;
      case 'straight':
        jointPoints = [s1.offset[1], s2.offset[0]];
        break;
      case 'intersection':
      default:
        jointPoints = [this.intersection(s1.offset[0], s1.offset[1], s2.offset[0], s2.offset[1])];
    }
    // filter out null-results
    return jointPoints.filter(function(v) {return v;});
  },

  joinLineSegments: function(segments, offset, joinStyle) {
    var l = segments.length;
    var joinedPoints = [];
    var s1 = segments[0], s2 = segments[0];
    joinedPoints.push(s1.offset[0]);

    for(var i=1; i<l; i++) {
      s2 = segments[i];
      joinedPoints = joinedPoints.concat(this.joinSegments(s1, s2, offset, joinStyle));
      s1 = s2;
    }
    joinedPoints.push(s2.offset[1]);

    return joinedPoints;
  },

  /**
  Interpolates points between two offset segments in a circular form
  */
  circularArc: function(s1, s2, distance) {
      if (s1.angle == s2.angle)
        return [s1.offset[1]];

      var center = s1.original[1];
      var points = [];

      if (distance < 0) {
        var startAngle = s1.offsetAngle;
        var endAngle = s2.offsetAngle;
      } else {
        // switch start and end angle when going right
        var startAngle = s2.offsetAngle;
        var endAngle = s1.offsetAngle;
      }

      if (endAngle < startAngle)
        endAngle += Math.PI * 2; // the end angle should be bigger than the start angle

      if (endAngle > startAngle + Math.PI)
        return [this.intersection(s1.offset[0], s1.offset[1], s2.offset[0], s2.offset[1])];

      // Step is distance dependent. Bigger distance results in more steps to take
      var step = Math.abs(8/distance);
      for (var a = startAngle; a < endAngle; a += step)
        points.push(this.translatePoint(center, distance, a));
      points.push(this.translatePoint(center, distance, endAngle));

      if (distance > 0)
        // reverse all points again when going right
        points.reverse();

      return points;
  }
}

// Modify the L.Polyline class by overwriting the projection function,
// to add offset related code
// Versions < 0.8
if(L.version.charAt(0) == '0' && parseInt(L.version.charAt(2)) < 8) {
  L.Polyline.include({
    projectLatlngs: function() {
      this._originalPoints = [];

      for (var i = 0, len = this._latlngs.length; i < len; i++) {
        this._originalPoints[i] = this._map.latLngToLayerPoint(this._latlngs[i]);
      }
      // Offset management hack ---
      if(this.options.offset) {
        this._originalPoints = L.PolylineOffset.offsetPoints(this._originalPoints, this.options.offset);
      }
      // Offset management hack END ---
    }
  });
} else {
// Versions >= 0.8
  L.Polyline.include({
    _projectLatlngs: function (latlngs, result) {
      var flat = latlngs[0] instanceof L.LatLng,
          len = latlngs.length,
          i, ring;

      if (flat) {
        ring = [];
        for (i = 0; i < len; i++) {
          ring[i] = this._map.latLngToLayerPoint(latlngs[i]);
        }
        // Offset management hack ---
        if(this.options.offset) {
          ring = L.PolylineOffset.offsetPoints(ring, this.options.offset);
        }
        // Offset management hack END ---
        result.push(ring);
      } else {
        for (i = 0; i < len; i++) {
          this._projectLatlngs(latlngs[i], result);
        }
      }
    }
  });
}

L.Polyline.include({
  setOffset: function(offset) {
    this.options.offset = offset;
    this.redraw();
    return this;
  }
});
<|endoftext|>


<commit_before>/**
 * @fileoverview Tests a _project.yaml file.
 *
 * @author Pete LePage <petele@google.com>
 */
'use strict';

const testHelpers = require('./helpers');

const JSONValidator = require('jsonschema').Validator;

JSONValidator.prototype.customFormats.wfUAString = function(input) {
  return input === 'UA-52746336-1';
};
JSONValidator.prototype.customFormats.doesFileExist = function(input) {
  return testHelpers.doesFileExist(input);
};
const SCHEMA_PROJECT = {
  id: '/Project',
  type: 'object',
  properties: {
    is_family_root: {type: 'boolean'},
    parent_project_metadata_path: {
      type: 'string',
      pattern: /^\/web\/_project.yaml$/,
    },
    name: {type: 'string', required: true},
    description: {type: 'string', required: true},
    home_url: {type: 'string', pattern: /^\/web\//i, required: true},
    color: {
      type: 'string',
      pattern: /^google-blue|orange$/,
      required: true,
    },
    buganizer_id: {type: 'number', pattern: /^180451$/, required: true},
    content_license: {
      type: 'string',
      pattern: /^cc3-apache2$/,
      required: true,
    },
    footer_path: {type: 'string', required: true},
    icon: {
      type: 'object',
      properties: {
        path: {type: 'string', required: true},
      },
      additionalProperties: false,
      required: true,
    },
    google_analytics_ids: {
      type: 'array',
      items: {type: 'string', format: 'wfUAString'},
      required: true,
    },
    tags: {type: 'array'},
    announcement: {
      type: 'object',
      properties: {
        description: {type: 'string', required: true},
        background: {type: 'string', required: false},
      },
      additionalProperties: false,
    },
    social_media: {
      type: 'object',
      properties: {
        image: {
          type: 'object',
          properties: {
            path: {type: 'string', format: 'doesFileExist', required: true},
            width: {type: 'number'},
            height: {type: 'number'},
          },
          required: true,
          additionalProperties: false,
        },
      },
      additionalProperties: false,
    },
  },
  additionalProperties: false,
};

/**
 * Tests and validates a _project.yaml file.
 *   Note: The returned promise always resolves, it will never reject.
 *
 * @param {string} filename The name of the file to be tested.
 * @param {Object} project The parsed contents of the project file.
 * @return {Promise} A promise with the result of the test.
 */
function test(filename, project) {
  return new Promise(function(resolve, reject) {
    const results = [];
    let validator = new JSONValidator();
    validator.validate(project, SCHEMA_PROJECT).errors.forEach((err) => {
      let msg = `${err.stack || err.message}`;
      msg = msg.replace('{}', '(' + err.instance + ')');
      const result = {
        level: 'ERROR',
        filename: filename,
        message: msg,
      };
      results.push(result);
    });
    if (results.length > 0) {
      reject(results);
      return;
    }
    resolve(true);
  });
}

exports.test = test;
<commit_msg>Add support for gcs_id to project.yaml (#5910)<commit_after>/**
 * @fileoverview Tests a _project.yaml file.
 *
 * @author Pete LePage <petele@google.com>
 */
'use strict';

const testHelpers = require('./helpers');

const JSONValidator = require('jsonschema').Validator;

JSONValidator.prototype.customFormats.wfUAString = function(input) {
  return input === 'UA-52746336-1';
};
JSONValidator.prototype.customFormats.doesFileExist = function(input) {
  return testHelpers.doesFileExist(input);
};
const SCHEMA_PROJECT = {
  id: '/Project',
  type: 'object',
  properties: {
    name: {type: 'string', required: true},
    description: {type: 'string', required: true},
    home_url: {type: 'string', pattern: /^\/web\//i, required: true},
    buganizer_id: {type: 'number', pattern: /^180451$/, required: true},
    content_license: {
      type: 'string',
      pattern: /^cc3-apache2$/,
      required: true,
    },
    announcement: {
      type: 'object',
      properties: {
        description: {type: 'string', required: true},
        background: {type: 'string', required: false},
      },
      additionalProperties: false,
    },
    color: {
      type: 'string',
      pattern: /^google-blue|orange$/,
      required: true,
    },
    footer_path: {type: 'string', required: true},
    gcs_id: {type: 'string'},
    google_analytics_ids: {
      type: 'array',
      items: {type: 'string', format: 'wfUAString'},
      required: true,
    },
    icon: {
      type: 'object',
      properties: {
        path: {type: 'string', required: true},
      },
      additionalProperties: false,
      required: true,
    },
    is_family_root: {type: 'boolean'},
    parent_project_metadata_path: {
      type: 'string',
      pattern: /^\/web\/_project.yaml$/,
    },
    social_media: {
      type: 'object',
      properties: {
        image: {
          type: 'object',
          properties: {
            path: {type: 'string', format: 'doesFileExist', required: true},
            width: {type: 'number'},
            height: {type: 'number'},
          },
          required: true,
          additionalProperties: false,
        },
      },
      additionalProperties: false,
    },
    tags: {type: 'array'},
  },
  additionalProperties: false,
};

/**
 * Tests and validates a _project.yaml file.
 *   Note: The returned promise always resolves, it will never reject.
 *
 * @param {string} filename The name of the file to be tested.
 * @param {Object} project The parsed contents of the project file.
 * @return {Promise} A promise with the result of the test.
 */
function test(filename, project) {
  return new Promise(function(resolve, reject) {
    const results = [];
    let validator = new JSONValidator();
    validator.validate(project, SCHEMA_PROJECT).errors.forEach((err) => {
      let msg = `${err.stack || err.message}`;
      msg = msg.replace('{}', '(' + err.instance + ')');
      const result = {
        level: 'ERROR',
        filename: filename,
        message: msg,
      };
      results.push(result);
    });
    if (results.length > 0) {
      reject(results);
      return;
    }
    resolve(true);
  });
}

exports.test = test;
<|endoftext|>


<commit_before>// the stub placed in request-enabled bundle wrapper code, expected to always execute within
// the scope of a bundle wrapper with access to its own variables
(function () {
	if (!scope.Promise) throw new Error('No Promise implementation found, please provide a polyfill for this platform');
	var   request = enyo.request || (defineProperty(enyo, 'request', {value: enyoRequest}) && enyo.request)
		, pending = enyo.__pendingRequests__ || (defineProperty(enyo, '__pendingRequests__', {value: {}}))
		, Request = enyo.__Request__;

	if (!Request) {
		/*
		This class is a facade of a Promise instance. It is returned from enyoRequest because
		an actual Promise instance fires its executor method immediately upon instantiation.
		A tenant of our "request" mechanism is that it does not fire until the first time a
		"then" handler is past to it. Here, we facade the normal behavior by passing a nop to
		the internal Promise instance and borrow its actual resolve/reject methods for our
		own "real" executor.
		*/
		Request = function Request (target, exec) {
			var resolve, reject, promise, fired;
			promise = new Promise(function (r1, r2) {
				resolve = r1;
				reject  = r2;
			});
			this.then = function then () {
				promise.then.apply(promise, arguments);
				if (!fired) {
					fired = true;
					exec(resolve, reject);
				}
			};
		};
		defineProperty(enyo, '__Request__', {value: Request});
		defineProperty(enyoRequest, 'isRequest', {value: isRequest});
		defineProperty(enyo, 'isRequest', {value: isRequest});
	}

	function enyoRequest (target) {
		var req = pending[target];
		if (!req) {
			req = pending[target] = new Request(target, function (resolve, reject) {
				var entry, deps;

				function handle () {
					try {
						resolve(require(target));
					} catch (e) {
						reject(e);
					}
				}

				function send () {
					var el;
					if (entry.style) {
						el      = document.createElement('link');
						el.rel  = 'stylesheet';
						el.href = entry.style;
						document.head.appendChild(el);
					}
					if (entry.source) {
						el = document.createElement('script');
						el.onload  = function () {
							resolve();
						};
						el.onerror = function (e) {
							reject(new Error(
								'Error loading module "' + target + '":\n\n' + e.stack + '\n'
							));
						};
						el.async   = true;
						el.src     = entry.source;
						document.head.appendChild(el);
					}
				}

				// if it has already been resolved, we're done
				if (exported.hasOwnProperty(target)) {
					return handle();
				}

				entry = manifest[target];
				if (!entry) throw new Error('Could not find module "' + target + '"');
				if (typeof entry == 'string') {
					return request(entry).then(function () {
						handle();
					});
				}
				if (!(entry.source || entry.style) && !(entry instanceof Array)) throw new Error(
					'The shared module manifest has been corrupted, the module is invalid "' + target + '"'
				);
				if (entry instanceof Array) {
					return handle();
				}
				if (entry.dependencies && entry.dependencies.length) {
					deps = entry.dependencies.map(function (name) {
						return new Promise(function (resolve) {
							request(name).then(resolve);
						});
					});
					Promise.all(deps).then(send);
				} else send();
			});
		}
		return req;
	}

	function isRequest (req) {
		return req != null && req instanceof Request;
	}
})();<commit_msg>cleanup incorrect word in comment<commit_after>// the stub placed in request-enabled bundle wrapper code, expected to always execute within
// the scope of a bundle wrapper with access to its own variables
(function () {
	if (!scope.Promise) throw new Error('No Promise implementation found, please provide a polyfill for this platform');
	var   request = enyo.request || (defineProperty(enyo, 'request', {value: enyoRequest}) && enyo.request)
		, pending = enyo.__pendingRequests__ || (defineProperty(enyo, '__pendingRequests__', {value: {}}))
		, Request = enyo.__Request__;

	if (!Request) {
		/*
		This class is a facade of a Promise instance. It is returned from enyoRequest because
		an actual Promise instance fires its executor method immediately upon instantiation.
		A tenant of our "request" mechanism is that it does not fire until the first time a
		"then" handler is passed to it. Here, we facade the normal behavior by passing a nop to
		the internal Promise instance and borrow its actual resolve/reject methods for our
		own "real" executor.
		*/
		Request = function Request (target, exec) {
			var resolve, reject, promise, fired;
			promise = new Promise(function (r1, r2) {
				resolve = r1;
				reject  = r2;
			});
			this.then = function then () {
				promise.then.apply(promise, arguments);
				if (!fired) {
					fired = true;
					exec(resolve, reject);
				}
			};
		};
		defineProperty(enyo, '__Request__', {value: Request});
		defineProperty(enyoRequest, 'isRequest', {value: isRequest});
		defineProperty(enyo, 'isRequest', {value: isRequest});
	}

	function enyoRequest (target) {
		var req = pending[target];
		if (!req) {
			req = pending[target] = new Request(target, function (resolve, reject) {
				var entry, deps;

				function handle () {
					try {
						resolve(require(target));
					} catch (e) {
						reject(e);
					}
				}

				function send () {
					var el;
					if (entry.style) {
						el      = document.createElement('link');
						el.rel  = 'stylesheet';
						el.href = entry.style;
						document.head.appendChild(el);
					}
					if (entry.source) {
						el = document.createElement('script');
						el.onload  = function () {
							resolve();
						};
						el.onerror = function (e) {
							reject(new Error(
								'Error loading module "' + target + '":\n\n' + e.stack + '\n'
							));
						};
						el.async   = true;
						el.src     = entry.source;
						document.head.appendChild(el);
					}
				}

				// if it has already been resolved, we're done
				if (exported.hasOwnProperty(target)) {
					return handle();
				}

				entry = manifest[target];
				if (!entry) throw new Error('Could not find module "' + target + '"');
				if (typeof entry == 'string') {
					return request(entry).then(function () {
						handle();
					});
				}
				if (!(entry.source || entry.style) && !(entry instanceof Array)) throw new Error(
					'The shared module manifest has been corrupted, the module is invalid "' + target + '"'
				);
				if (entry instanceof Array) {
					return handle();
				}
				if (entry.dependencies && entry.dependencies.length) {
					deps = entry.dependencies.map(function (name) {
						return new Promise(function (resolve) {
							request(name).then(resolve);
						});
					});
					Promise.all(deps).then(send);
				} else send();
			});
		}
		return req;
	}

	function isRequest (req) {
		return req != null && req instanceof Request;
	}
})();<|endoftext|>


<commit_before>var oldTicketArray = [];
var newTicketArray = [];

$( document ).ready(function() {
    $.getJSON( "/TicketsWcf/TicketsService.svc/GetOpenTickets", function (data) {
		oldTicketArray = data;
		$.each (data, function (index, ticketObject) {
			createDiv(ticketObject)
		})

		$(".ticketdiv").click(function() {
			deleteDiv($(this).data("TicketNumber"));
		});
	});

	setInterval(function() {
		$.getJSON( "/TicketsWcf/TicketsService.svc/GetOpenTickets", function (data) {
			newTicketArray = data;

			for (var i = 0; i < oldTicketArray.length; i++){
				//check for ticket numbers in new array.  Anything found compare the rest of the ticket info and update div if necissary. anything missing close

			}
			for (var i = 0; i < newTicketArray.length; i++){
				//check for ticket numbers in old array.  Anything missing open
			}
		});
	}, 30000);

});

function updateDiv(ticketNum){

}

function deleteDiv(ticketNum){
	$(".ticketdiv").each(function() {
		if ($(this).data("TicketNumber") === ticketNum){
			$(this).hide( 1000, function(){
				$(this).remove();
			});
			return false;
		}
	});
}

function createDiv(ticket){
	var ticketDiv = document.createElement("div");
	$(ticketDiv).addClass("ticketdiv");
	$(ticketDiv).data(ticket);

	var clientDiv = document.createElement("div");
	$(clientDiv).addClass("clientdiv");

	var clientDivText = document.createElement("span");
	clientDivText.innerHTML = ticket.ClientName;
	$(clientDiv).append(clientDivText);

	var descriptionDiv = document.createElement("div");
	$(descriptionDiv).addClass("deescriptiondiv");

	var descriptionDivText = document.createElement("span");
	descriptionDivText.innerHTML = ticket.Description;
	$(descriptionDiv).append(descriptionDivText);

	var assignDiv = document.createElement("div");
	$(assignDiv).addClass("assigndiv");

	var assignDivText = document.createElement("span");
	assignDivText.innerHTML = ticket.AssignTech;
	$(assignDiv).append(assignDivText);

	$(ticketDiv).append(clientDiv);
	$(ticketDiv).append(descriptionDiv);
	$(ticketDiv).append(assignDiv);

	$("#wrapper").append(ticketDiv);
}

<commit_msg>added effect to showing new divs<commit_after>var oldTicketArray = [];
var newTicketArray = [];

$( document ).ready(function() {
    $.getJSON( "/TicketsWcf/TicketsService.svc/GetOpenTickets", function (data) {
		oldTicketArray = data;
		$.each (data, function (index, ticketObject) {
			createDiv(ticketObject)
		})

		$(".ticketdiv").click(function() {
			deleteDiv($(this).data("TicketNumber"));
		});
	});

	setInterval(function() {
		$.getJSON( "/TicketsWcf/TicketsService.svc/GetOpenTickets", function (data) {
			newTicketArray = data;

			for (var i = 0; i < oldTicketArray.length; i++){
				//check for ticket numbers in new array.  Anything found compare the rest of the ticket info and update div if necissary. anything missing close

			}
			for (var i = 0; i < newTicketArray.length; i++){
				//check for ticket numbers in old array.  Anything missing open
			}
		});
	}, 30000);

});

function updateDiv(ticket){

}

function deleteDiv(ticketNum){
	$(".ticketdiv").each(function() {
		if ($(this).data("TicketNumber") === ticketNum){
			$(this).hide( 1000, function(){
				$(this).remove();
			});
			return false;
		}
	});
}

function createDiv(ticket){
	var ticketDiv = document.createElement("div");
	$(ticketDiv).addClass("ticketdiv");
	$(ticketDiv).data(ticket);

	var clientDiv = document.createElement("div");
	$(clientDiv).addClass("clientdiv");

	var clientDivText = document.createElement("span");
	clientDivText.innerHTML = ticket.ClientName;
	$(clientDiv).append(clientDivText);

	var descriptionDiv = document.createElement("div");
	$(descriptionDiv).addClass("deescriptiondiv");

	var descriptionDivText = document.createElement("span");
	descriptionDivText.innerHTML = ticket.Description;
	$(descriptionDiv).append(descriptionDivText);

	var assignDiv = document.createElement("div");
	$(assignDiv).addClass("assigndiv");

	var assignDivText = document.createElement("span");
	assignDivText.innerHTML = ticket.AssignTech;
	$(assignDiv).append(assignDivText);

	$(ticketDiv).append(clientDiv);
	$(ticketDiv).append(descriptionDiv);
	$(ticketDiv).append(assignDiv);

	$("#wrapper").append(ticketDiv);
	$(ticketDiv).hide(0);
	$(ticketDiv).show( 1000 );
}

<|endoftext|>


<commit_before>var browserify   = require('browserify');
var gulp         = require('gulp');
var babelify     = require('babelify');
var sourcemaps   = require('gulp-sourcemaps');
var uglify       = require('gulp-uglify');
var gutil        = require('gulp-util');
var assign       = require('lodash.assign');
var buffer       = require('vinyl-buffer');
var source       = require('vinyl-source-stream');
var watchify     = require('watchify');
var config       = require('../config/browserify');
var maps         = require('../config/sourcemaps');
var handleErrors = require('../lib/handleErrors');

var opts = assign({}, watchify.args, config.browserify);
var bwatch = watchify(browserify(opts));

gulp.task('browserify', bundle);
bwatch.on('update', bundle);
bwatch.on('log', gutil.log);

function bundle () {
    return bwatch
        .add(require.resolve('babel/polyfill'))
        .transform(babelify.configure({ignore: ['node_modules', 'bower_components']}))
        .bundle()
        .on('error', handleErrors)
        .pipe(source(config.name))
        .pipe(buffer())
        // .pipe(sourcemaps.init({loadMaps: true}))
        // .pipe(uglify())
        // .pipe(sourcemaps.write(maps.dest))
        .pipe(gulp.dest(config.dest));
}
<commit_msg>Optimisation for gulp browserify<commit_after>var browserify   = require('browserify');
var gulp         = require('gulp');
var babelify     = require('babelify');
var sourcemaps   = require('gulp-sourcemaps');
var uglify       = require('gulp-uglify');
var gutil        = require('gulp-util');
var assign       = require('lodash.assign');
var buffer       = require('vinyl-buffer');
var source       = require('vinyl-source-stream');
var watchify     = require('watchify');
var config       = require('../config/browserify');
var maps         = require('../config/sourcemaps');
var handleErrors = require('../lib/handleErrors');

var opts = assign({}, watchify.args, config.browserify);
var bwatch = watchify(
  browserify(opts)
    .add(require.resolve('babel/polyfill'))
    .transform(babelify.configure({ignore: ['node_modules', 'bower_components']}))
);

gulp.task('browserify', bundle);
bwatch.on('update', bundle);
bwatch.on('log', gutil.log);

function bundle () {
    return bwatch
        .bundle()
        .on('error', handleErrors)
        .pipe(source(config.name))
        .pipe(buffer())
        // .pipe(sourcemaps.init({loadMaps: true}))
        // .pipe(uglify())
        // .pipe(sourcemaps.write(maps.dest))
        .pipe(gulp.dest(config.dest));
}
<|endoftext|>


<commit_before>
// console fun with tagged String litteralstremplate
// To deactivate debug
// log.noop()

(function( global ){

    var log = (ss,...p)=> log.level > 2 && console.log.apply(console, [ss.join('%')].concat(p));
    log.lev/* exemples:
    el = 3; // 0> nothing, 1> errors, 2> infos, 3> logs, 4> accessors, 5> proxies
    log.I = (ss,...p)=> log.level > 1 && console.info.apply(console, [ss.join('%')].concat(p));
    log.E = (ss,...p)=> log.level > 0 && console.error.apply(console, [ss.join('%')].concat(p));
    log.G = (ss,...p)=> log.level > 2 && console.groupCollapsed.apply(console, [ss.join('%')].concat(p));

    log.noop = ()=> { log = o=>o; log.I = log.G = log.E = log; global.log = log }
    global.log = log;

})( global || window )

/* exemples:

log `
window = ${window}o
innerHeight = ${`color:blue`}c${window.innerHeight}d${``}cpx
${`color:white;background:green`}cOK`

log.I `
Hello ${window}o, tu est grande de ${`color:blue`}c${window.innerHeight}d${``}c pixels !
${`color:green`}c Bravo!!`

log.E `
Hello ${window}o tu est grande que de ${window.innerHeight}d pixels !
${`color:white;background:red`}c Pas bien!!`

var bigreen = `color:white;background:green;font-size:1.2em;padding:0 .3em`
  , bigred = `color:white;background:red;font-size:1.2em;padding:0 .3em`
log `${bigreen}cOK`
log `${bigred}cKO`
log `${window.innerHeight<500 ? bigred : bigreen}c${window.innerHeight<500 ? "trop petiiiiit!s" : "Ok mon grand .."}s`

// var toLog = window;
// log.G`[class ${'color: indigo'}c${toLog.constructor.name}s${''}c]
//     this = ${window}o
//     innerWidth = ${`color:blue`}c${window.innerWidth}d${``}cpx
//     innerHeight = ${`color:blue`}c${window.innerHeight}d${``}cpx
//     document = ${document}o
//         title = ${`color:white;background:indigo`}c"${document.title}s"${``}c
// `

*/
<commit_msg>Corrected typo<commit_after>
// console fun with tagged String litteralstremplate
// To deactivate debug
// log.noop()

(function( global ){

    var log = (ss,...p)=> log.level > 2 && console.log.apply(console, [ss.join('%')].concat(p));
    log.level = 3; // 0> nothing, 1> errors, 2> infos, 3> logs, 4> accessors, 5> proxies
    log.I = (ss,...p)=> log.level > 1 && console.info.apply(console, [ss.join('%')].concat(p));
    log.E = (ss,...p)=> log.level > 0 && console.error.apply(console, [ss.join('%')].concat(p));
    log.G = (ss,...p)=> log.level > 2 && console.groupCollapsed.apply(console, [ss.join('%')].concat(p));

    log.noop = ()=> { log = o=>o; log.I = log.G = log.E = log; global.log = log }
    global.log = log;

})( global || window )

/* exemples:

log `
window = ${window}o
innerHeight = ${`color:blue`}c${window.innerHeight}d${``}cpx
${`color:white;background:green`}cOK`

log.I `
Hello ${window}o, tu est grande de ${`color:blue`}c${window.innerHeight}d${``}c pixels !
${`color:green`}c Bravo!!`

log.E `
Hello ${window}o tu est grande que de ${window.innerHeight}d pixels !
${`color:white;background:red`}c Pas bien!!`

var bigreen = `color:white;background:green;font-size:1.2em;padding:0 .3em`
  , bigred = `color:white;background:red;font-size:1.2em;padding:0 .3em`
log `${bigreen}cOK`
log `${bigred}cKO`
log `${window.innerHeight<500 ? bigred : bigreen}c${window.innerHeight<500 ? "trop petiiiiit!s" : "Ok mon grand .."}s`

// var toLog = window;
// log.G`[class ${'color: indigo'}c${toLog.constructor.name}s${''}c]
//     this = ${window}o
//     innerWidth = ${`color:blue`}c${window.innerWidth}d${``}cpx
//     innerHeight = ${`color:blue`}c${window.innerHeight}d${``}cpx
//     document = ${document}o
//         title = ${`color:white;background:indigo`}c"${document.title}s"${``}c
// `

*/
<|endoftext|>


<commit_before>
// To run:
//     node 2.js
//
// Problem:
//      Each new term in the Fibonacci sequence is generated by adding the
//      previous two terms. By starting with 1 and 2, the first 10 terms will
//      be:
//
//      1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
//
//      By considering the terms in the Fibonacci sequence whose values do not
//      exceed four million, find the sum of the even-valued terms.

solve = function() {
    var sum = 0,
        prev = 1,
        goal = 4e6,
        temp;

    for(i=1; i<=goal;) {
        if(i % 2 == 0) {
            sum += i;
        }
        temp = prev;
        prev = i;
        i += temp;
    }
    return sum
}

// unfortunately the accurracy seems to be only 1ms.
console.time('solver');
console.log(solve());
console.timeEnd('solver');
<commit_msg>P2 -- update 2.js<commit_after>
// To run:
//     node 2.js
//
// Problem:
//      Each new term in the Fibonacci sequence is generated by adding the
//      previous two terms. By starting with 1 and 2, the first 10 terms will
//      be:
//
//      1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ...
//
//      By considering the terms in the Fibonacci sequence whose values do not
//      exceed four million, find the sum of the even-valued terms.

solve = function() {
    var sum = 0,
        _i = 1,
        goal = 4e6,
        prev;

    for(i=1; i<=goal; i += prev) {
        if(i % 2 == 0) {
            sum += i;
        }
        prev = _i;
        _i = i;
    }
    return sum
}

// unfortunately the accurracy seems to be only 1ms.
console.time('solver');
console.log(solve());
console.timeEnd('solver');
<|endoftext|>


<commit_before>define(function() {

  'use strict';

  return {
    byExtension: {
      '7z': 'application/x-7z-compressed',
      ace: 'application/x-ace-compressed',
      adf: 'application/x-amiga-disk-format',
      adz: 'application/gzip; compressed-type=application/x-amiga-disk-format',
      aif: 'audio/x-aiff',
      aiff: 'audio/x-aiff',
      avi: 'video/x-avi',
      bat: 'application/x-bat',
      bmp: 'image/bmp',
      bz: 'application/x-bzip',
      bz2: 'application/x-bzip2',
      class: 'application/x-java-class',
      com: 'application/x-exe-msdos-simple',
      css: 'text/css',
      dll: 'application/x-exe-ambiguous',
      dms: 'application/x-disk-masher-system',
      doc: 'application/x-doc-ambiguous',
      docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      exe: 'application/x-exe-ambiguous',
      flc: 'video/x-flic',
      fli: 'video/x-flic',
      flv: 'video/x-flv',
      gif: 'image/gif',
      hlp: 'application/x-winhelp',
      htm: 'text/html',
      html: 'text/html',
      ico: 'image/x-icon',
      jar: 'application/zip',
      jpg: 'image/jpeg',
      jpeg: 'image/jpeg',
      js: 'application/js',
      json: 'application/json',
      lha: 'application/x-lha',
      mid: 'audio/midi',
      midi: 'audio/midi',
      mov: 'video/quicktime',
      ogg: 'application/ogg',
      pct: 'image/x-pict',
      pcx: 'image/x-pcx',
      pic: 'image/x-pict',
      pict: 'image/x-pict',
      png: 'image/png',
      ppt: 'application/vnd.ms-powerpoint',
      psd: 'image/x-photoshop',
      rar: 'application/x-rar-compressed',
      rsrc: 'application/x-mac-resource-fork',
      sit: 'application/x-stuffit',
      swf: 'application/x-shockwave-flash',
      tar: 'application/x-tar',
      tgz: 'application/gzip; compressed-type=application/x-tar',
      tiff: 'image/tiff',
      txt: 'text/plain',
      wav: 'audio/x-wav',
      wri: 'application/x-mswrite',
      xls: 'application/vnd.ms-excel',
      xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      zip: 'application/zip',
    },
    byMacResourceType: {
      '8BIM': 'image/x-photoshop-image-resource-block',
      ACHR: 'application/x-world-builder-character',
      ACOD: 'application/x-world-builder-code',
    },
    byMacFileType: {
      AIFF: 'audio/x-aiff',
      MOOV: 'video/quicktime',
      MV93: 'application/x-director',
      'M!93': 'application/x-director',
    },
  };

});
<commit_msg>Add some more file formats<commit_after>define(function() {

  'use strict';

  return {
    byExtension: {
      '7z': 'application/x-7z-compressed',
      ace: 'application/x-ace-compressed',
      adf: 'application/x-amiga-disk-format',
      adz: 'application/gzip; compressed-type=application/x-amiga-disk-format',
      aif: 'audio/x-aiff',
      aiff: 'audio/x-aiff',
      au: 'audio/basic',
      avi: 'video/x-avi',
      bat: 'application/x-bat',
      bmp: 'image/bmp',
      bz: 'application/x-bzip',
      bz2: 'application/x-bzip2',
      class: 'application/x-java-class',
      com: 'application/x-exe-msdos-simple',
      css: 'text/css',
      dll: 'application/x-exe-ambiguous',
      dms: 'application/x-disk-masher-system',
      doc: 'application/x-doc-ambiguous',
      docx: 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
      exe: 'application/x-exe-ambiguous',
      flc: 'video/x-flic',
      fli: 'video/x-flic',
      flv: 'video/x-flv',
      gif: 'image/gif',
      hlp: 'application/x-winhelp',
      htm: 'text/html',
      html: 'text/html',
      ico: 'image/x-icon',
      ini: 'application/x-ini',
      jar: 'application/zip',
      jpg: 'image/jpeg',
      jpeg: 'image/jpeg',
      js: 'application/js',
      json: 'application/json',
      lha: 'application/x-lha',
      mid: 'audio/midi',
      midi: 'audio/midi',
      mov: 'video/quicktime',
      ogg: 'application/ogg',
      pct: 'image/x-pict',
      pcx: 'image/x-pcx',
      pic: 'image/x-pict',
      pict: 'image/x-pict',
      png: 'image/png',
      ppt: 'application/vnd.ms-powerpoint',
      psd: 'image/x-photoshop',
      rar: 'application/x-rar-compressed',
      rsrc: 'application/x-mac-resource-fork',
      sit: 'application/x-stuffit',
      swf: 'application/x-shockwave-flash',
      tar: 'application/x-tar',
      tgz: 'application/gzip; compressed-type=application/x-tar',
      tiff: 'image/tiff',
      txt: 'text/plain',
      wav: 'audio/x-wav',
      wri: 'application/x-mswrite',
      xls: 'application/vnd.ms-excel',
      xlsx: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
      zip: 'application/zip',
      zzt: 'application/x-zzt',
    },
    byMacResourceType: {
      '8BIM': 'image/x-photoshop-image-resource-block',
      ACHR: 'application/x-world-builder-character',
      ACOD: 'application/x-world-builder-code',
    },
    byMacFileType: {
      AIFF: 'audio/x-aiff',
      MOOV: 'video/quicktime',
      MV93: 'application/x-director',
      'M!93': 'application/x-director',
    },
  };

});
<|endoftext|>


<commit_before>import add from '../support/task';
import addStatTotalToMouseover from '../common/addStatTotalToMouseover';
import ajaxifyProfileSections from './ajaxifyProfileSections';
// import {closestForm} from '../common/closest';
import colouredDots from '../common/colouredDots';
import components from './components/components';
// import dontPost from '../common/dontPost';
import fallback from '../system/fallback';
import fastDebuff from './debuff';
import getElementsByTagName from '../common/getElementsByTagName';
import getText from '../common/getText';
import getUrlParameter from '../system/getUrlParameter';
import highlightPvpProtection from './highlightPvpProtection';
import injectFastWear from './fastWear';
import jQueryNotPresent from '../common/jQueryNotPresent';
import nekidBtn from './nekidBtn';
// import on from '../common/on';
import {pCC} from '../support/layout';
import playerId from '../common/playerId';
import playerName from '../common/playerName';
import {profileInjectGuildRel} from './profileInjectGuildRel';
import profileInjectQuickButton from './profileInjectQuickButton';
import profileParseAllyEnemy from './profileAllyEnemy';
import profileRenderBio from './bio/bio';
import querySelector from '../common/querySelector';
import quickWearLink from './quickWearLink';
import selectAllLink from './selectAllLink';
import storeVL from './storeVL';
import updateNmv from './updateNmv';
import updateStatistics from './updateStatistics';

function ifSelf(self) {
  if (self) {
    // self inventory
    fastDebuff();
    profileParseAllyEnemy();
    injectFastWear();
    components();
    quickWearLink();
    selectAllLink();
    storeVL();
    nekidBtn();
    ajaxifyProfileSections();
  }
}

function guildRelationship(avyImg, playername, self) {
  // Must be before profileInjectQuickButton
  profileInjectGuildRel(self);
  // It sets up guildId and currentGuildRelationship
  var playerid = fallback(getUrlParameter('player_id'), playerId());
  profileInjectQuickButton(avyImg, playerid, playername);
}

function updateDom(avyImg, playername, self) {
  ifSelf(self);
  guildRelationship(avyImg, playername, self);
  updateNmv();
  updateStatistics();
  highlightPvpProtection();
  add(3, profileRenderBio, [self]);
  addStatTotalToMouseover();
  add(3, colouredDots);
}

// function updateUrl(e) {
//   e.preventDefault();
//   dontPost(closestForm(e.target).parentNode);
// }

// function allowBack(self) {
//   if (!self) {
//     on(querySelector('#profileRightColumn'), 'submit', updateUrl);
//   }
// }

export default function injectProfile() { // Legacy
  if (jQueryNotPresent()) {return;}
  var avyImg = querySelector('#profileLeftColumn img[oldtitle*="\'s Avatar"]');
  if (!avyImg) {return;}
  var playername = getText(getElementsByTagName('h1', pCC)[0]);
  var self = playername === playerName();
  updateDom(avyImg, playername, self);
  // allowBack(self);
}
<commit_msg>allow back in profile view attack<commit_after>import add from '../support/task';
import addStatTotalToMouseover from '../common/addStatTotalToMouseover';
import ajaxifyProfileSections from './ajaxifyProfileSections';
//#if _DEV  //  allow back
import {arrayFrom} from '../common/arrayFrom';
//#endif
import colouredDots from '../common/colouredDots';
import components from './components/components';
import fallback from '../system/fallback';
import fastDebuff from './debuff';
import getElementsByTagName from '../common/getElementsByTagName';
import getText from '../common/getText';
import getUrlParameter from '../system/getUrlParameter';
import highlightPvpProtection from './highlightPvpProtection';
//#if _DEV  //  allow back
import {indexPhp} from '../support/constants';
//#endif
import injectFastWear from './fastWear';
import jQueryNotPresent from '../common/jQueryNotPresent';
import nekidBtn from './nekidBtn';
//#if _DEV  //  allow back
import on from '../common/on';
//#endif
import {pCC} from '../support/layout';
import playerId from '../common/playerId';
import playerName from '../common/playerName';
import {profileInjectGuildRel} from './profileInjectGuildRel';
import profileInjectQuickButton from './profileInjectQuickButton';
import profileParseAllyEnemy from './profileAllyEnemy';
import profileRenderBio from './bio/bio';
import querySelector from '../common/querySelector';
import quickWearLink from './quickWearLink';
import selectAllLink from './selectAllLink';
import storeVL from './storeVL';
import updateNmv from './updateNmv';
import updateStatistics from './updateStatistics';

function ifSelf(self) {
  if (self) {
    // self inventory
    fastDebuff();
    profileParseAllyEnemy();
    injectFastWear();
    components();
    quickWearLink();
    selectAllLink();
    storeVL();
    nekidBtn();
    ajaxifyProfileSections();
  }
}

function guildRelationship(avyImg, playername, self) {
  // Must be before profileInjectQuickButton
  profileInjectGuildRel(self);
  // It sets up guildId and currentGuildRelationship
  var playerid = fallback(getUrlParameter('player_id'), playerId());
  profileInjectQuickButton(avyImg, playerid, playername);
}

function updateDom(avyImg, playername, self) {
  ifSelf(self);
  guildRelationship(avyImg, playername, self);
  updateNmv();
  updateStatistics();
  highlightPvpProtection();
  add(3, profileRenderBio, [self]);
  addStatTotalToMouseover();
  add(3, colouredDots);
}
//#if _DEV  //  allow back

function updateUrl(e) {
  e.preventDefault();
  var validInputs = arrayFrom(e.target.closest('form').elements)
    .filter(i => i.type !== 'submit')
    .map(i => i.name + '=' + i.value).join('&');
  window.location = indexPhp + '?' + validInputs;
}

function allowBack(self) {
  if (!self) {
    on(querySelector('#profileRightColumn'), 'submit', updateUrl);
  }
}
//#endif

export default function injectProfile() { // Legacy
  if (jQueryNotPresent()) {return;}
  var avyImg = querySelector('#profileLeftColumn img[oldtitle*="\'s Avatar"]');
  if (!avyImg) {return;}
  var playername = getText(getElementsByTagName('h1', pCC)[0]);
  var self = playername === playerName();
  updateDom(avyImg, playername, self);
  //#if _DEV  //  allow back
  allowBack(self);
  //#endif
}
<|endoftext|>


<commit_before>var mongoose = require("mongoose"),
    Schema = mongoose.Schema,
    mongoose_types = require('j-forms').types,
    ObjectId = Schema.ObjectId,
    utils = require('./../utils');

var tag_suggestions =  {
    tag_name: String,
    tag_offers: {type:[ObjectId], ref:'User'}
};

var InformationItem = module.exports = new Schema({
    title: {type: String, required: true},
    tooltip:String,
    subject_id:[{type:ObjectId, ref:'Subject',required:true}],
    category:{type:String, "enum":['test', 'statistics', 'infographic', 'graph'], required:true},
    text_field:{type:mongoose_types.Text, required:true},
    text_field_preview:{type:mongoose_types.Html},
    image_field: {type:mongoose_types.File,required:true},
    image_field_preview: {type:mongoose_types.File},
    tags:{type:[String], index:true},
    users:{type:[ObjectId], ref:'User',editable:false},
    discussions:[{type:ObjectId, ref:'Discussion', index:true}],
    cycles:{type:[ObjectId], ref:'Cycle', index:true},
    //this is for later
    actions:{type:[ObjectId], ref:'Action', index:true},
    is_visible:{type:Boolean, 'default':true},
    creation_date:{type:Date, 'default':Date.now,editable:false},
    is_hot_object:{type:Boolean, 'default':false},
    is_hot_info_item: {type:Boolean, 'default':false},
    tag_suggestions: {type:[tag_suggestions] ,editable:false},
    like_counter: {type: Number, 'default': 0, editable: false},
    view_counter: {type: Number, 'default': '0'},

    //this two fields are for user suggestion of InformationItem, when admin create this it will remain false
    created_by: {creator_id:{type: ObjectId, ref: 'User', editable: false}, did_user_created_this_item: {type: Boolean, 'default': false, editable: false}},
    discussion_counter:{type:},
    status: {type: String, "enum": ['approved', 'denied', 'waiting']},
    gamification: {rewarded_creator_for_high_liked: {type: String, 'default': false, editable: false},
        rewarded_creator_for_approval: {type: String, 'default': false, editable: false}, editable:false},
    gui_order:{type:Number,'default':9999999,editable:false}
}, {strict: true});<commit_msg>forgotten<commit_after>var mongoose = require("mongoose"),
    Schema = mongoose.Schema,
    mongoose_types = require('j-forms').types,
    ObjectId = Schema.ObjectId,
    utils = require('./../utils');

var tag_suggestions =  {
    tag_name: String,
    tag_offers: {type:[ObjectId], ref:'User'}
};

var InformationItem = module.exports = new Schema({
    title: {type: String, required: true},
    tooltip:String,
    subject_id:[{type:ObjectId, ref:'Subject',required:true}],
    category:{type:String, "enum":['test', 'statistics', 'infographic', 'graph'], required:true},
    text_field:{type:mongoose_types.Text, required:true},
    text_field_preview:{type:mongoose_types.Html},
    image_field: {type:mongoose_types.File,required:true},
    image_field_preview: {type:mongoose_types.File},
    tags:{type:[String], index:true},
    users:{type:[ObjectId], ref:'User',editable:false},
    discussions:[{type:ObjectId, ref:'Discussion', index:true}],
    cycles:{type:[ObjectId], ref:'Cycle', index:true},
    //this is for later
    actions:{type:[ObjectId], ref:'Action', index:true},
    is_visible:{type:Boolean, 'default':true},
    creation_date:{type:Date, 'default':Date.now,editable:false},
    is_hot_object:{type:Boolean, 'default':false},
    is_hot_info_item: {type:Boolean, 'default':false},
    tag_suggestions: {type:[tag_suggestions] ,editable:false},
    like_counter: {type: Number, 'default': 0, editable: false},
    view_counter: {type: Number, 'default': '0'},

    //this two fields are for user suggestion of InformationItem, when admin create this it will remain false
    created_by: {creator_id:{type: ObjectId, ref: 'User', editable: false}, did_user_created_this_item: {type: Boolean, 'default': false, editable: false}},
    discussion_counter:{type:Number,'default':0},
    status: {type: String, "enum": ['approved', 'denied', 'waiting']},
    gamification: {rewarded_creator_for_high_liked: {type: String, 'default': false, editable: false},
        rewarded_creator_for_approval: {type: String, 'default': false, editable: false}, editable:false},
    gui_order:{type:Number,'default':9999999,editable:false}
}, {strict: true});<|endoftext|>


<commit_before>var exec = require ('child_process') .exec;

console.log ('Checking vnStat requirement...');

exec ('vnstat --version', function (err, res, stderr) {
  if (err) { throw err; }
  res.replace (/^vnStat (\d+)\.(\d+) /, function (s, major, minor) {
    if (major >= 1 && minor >= 13) {
      console.log ('vnStat correct');
    } else {
      console.log ('Wrong vnStat version: requires >= v1.13, but v'+ major +'.'+ minor +' installed');
      process.exit (1);
    }
  });
});<commit_msg>Clean up preinstall.js<commit_after>var exec = require ('child_process') .exec;

console.log ('Checking vnStat requirement...');

exec ('vnstat --version', function (err, res, stderr) {
  if (err) {
    throw err;
  }

  res.replace (/^vnStat (\d+)\.(\d+) /, function (s, major, minor) {
    if (major >= 1 && minor >= 13) {
      console.log ('vnStat correct');
      return;
    }

    console.log ('Wrong vnStat version: requires >= v1.13, but v' + major + '.' + minor + ' installed');
    process.exit (1);
  });
});
<|endoftext|>