<commit_before>function hide(time) {
    setInterval(function(){
        $("#msg_center").html('')
    }, time)
};

function smsg(str) {
    $("#msg_center").html('' + str + '');
    hide(3000);
};

function emsg(str) {
    $("#msg_center").html('' + str + '')
    hide(3000);
};

function post() {
    var tickle = $("#tickle").val().trim();
    if (tickle.length > 0) {
        var json = {'text':tickle}
            jsRoutes.controllers.Application.tickle().ajax({
                data: JSON.stringify(json),
                contentType: "application/json",
                success: function(data) {
                    if (data.success) {
                        smsg("Succesfully posted :).")
                    }
                    if (data.failure) {
                        emsg("Posting failed. reason: " + data.failure.reason);
                    }
                }
            });
    } else {
        emsg("Tickle is Empty. Post Something.")
    }
}<commit_msg>prepend tickle as soon as successfully posted<commit_after>function hide(time) {
    setInterval(function(){
        $("#msg_center").html('')
    }, time)
};

function smsg(str) {
    $("#msg_center").html('' + str + '');
    hide(3000);
};

function emsg(str) {
    $("#msg_center").html('' + str + '')
    hide(3000);
};

function post() {
    var tickle = $("#tickle").val().trim();
    if (tickle.length > 0) {
        var json = {'text':tickle}
            jsRoutes.controllers.Application.tickle().ajax({
                data: JSON.stringify(json),
                contentType: "application/json",
                success: function(data) {
                    if (data.success) {
                        smsg("Succesfully posted :).")
                        $("#tickles").prepend('
' + json.text + '
');
                    }
                    if (data.failure) {
                        emsg("Posting failed. reason: " + data.failure.reason);
                    }
                }
            });
    } else {
        emsg("Tickle is Empty. Post Something.")
    }
}<|endoftext|>


<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 SQL-Based Databse or a KeyValue Database that can only save strings(and no objects), as a JSON KeyValue Store.

The idea of the dbWrapper is to provide following features:

* automatic JSON serialize/deserialize to abstract that away from the database driver and the module user.
* cache reads. A amount of KeyValues are hold in the memory, so that reading is faster.
* Buffer DB Writings. Sets and deletes should be buffered to make them in a setted interval with a bulk. This reduces the overhead of database transactions and makes the database 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 database. 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 wrappedDB The Database that should be wrapped
 @param settings (optional) The settings that should be applied to the wrapper
*/
exports.database = function(wrappedDB, settings)
{
  //saved the wrappedDB
  this.wrappedDB=wrappedDB;

  //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(wrappedDB.settings != null)
  {
    if(wrappedDB.settings.cache         != null)  this.settings.cache         = wrappedDB.settings.cache;
    if(wrappedDB.settings.writeInterval != null)  this.settings.writeInterval = wrappedDB.settings.writeInterval;
    if(wrappedDB.settings.json          != null)  this.settings.json          = wrappedDB.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;

  //ensure the buffer is flushed before the application ends
  process.on('exit', function ()
  {
    flush(this);
  });
};

/**
 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 database 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.database.prototype.buffer = {};

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

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

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

/**
 Gets the value trough the wrapper.
*/
exports.database.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.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.buffer[key].timestamp = new Date().getTime();
    callback(null, this.buffer[key].value);
  }
  //get it direct
  else
  {
    var self = this;

    this.wrappedDB.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();

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

/**
 Sets the value trough the wrapper
*/
exports.database.prototype.set = function(key, value, bufferCallback, writeCallback)
{
  //writing cache is enabled, so simply write it into the buffer
  if(this.settings.writeInterval > 0)
  {
    //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 database,
    //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 database
  else
  {
    //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.wrappedDB.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.wrappedDB.set(key,value,callback);
    }
  }
}

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

  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.database.prototype.getSub = function(key, sub, callback)
{
  //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;
        }
      }

      callback(err, subvalue);
    }
  });
}

/**
 Removes the value trough the wrapper
*/
exports.database.prototype.remove = function(key, bufferCallback, writeCallback)
{
  //make a set to null out of it
  this.set(key, null, bufferCallback, writeCallback);
}

/**
 Garbage Collector of the cache
*/
exports.database.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;
    });

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

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

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

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

      //stringify the value if stringifying is enabled
      if(db.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(db.buffer[i].callbacks);

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

  //send the bulk to the database driver and call the callbacks with the results
  if(operations.length > 0)
  {
    //set the flushing flag
    db.isFlushing = true;

    console.error("FLUSH!");

    db.wrappedDB.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)
      {
        db.buffer[operations[i].key].writingInProgress = false;
      }

      if(callback) callback();

      console.error("FLUSH DONE!");

      //set the flushing flag to false
      db.isFlushing = false;
    });
  }
}

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>removed debug messages<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 SQL-Based Databse or a KeyValue Database that can only save strings(and no objects), as a JSON KeyValue Store.

The idea of the dbWrapper is to provide following features:

* automatic JSON serialize/deserialize to abstract that away from the database driver and the module user.
* cache reads. A amount of KeyValues are hold in the memory, so that reading is faster.
* Buffer DB Writings. Sets and deletes should be buffered to make them in a setted interval with a bulk. This reduces the overhead of database transactions and makes the database 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 database. 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 wrappedDB The Database that should be wrapped
 @param settings (optional) The settings that should be applied to the wrapper
*/
exports.database = function(wrappedDB, settings)
{
  //saved the wrappedDB
  this.wrappedDB=wrappedDB;

  //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(wrappedDB.settings != null)
  {
    if(wrappedDB.settings.cache         != null)  this.settings.cache         = wrappedDB.settings.cache;
    if(wrappedDB.settings.writeInterval != null)  this.settings.writeInterval = wrappedDB.settings.writeInterval;
    if(wrappedDB.settings.json          != null)  this.settings.json          = wrappedDB.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;

  //ensure the buffer is flushed before the application ends
  process.on('exit', function ()
  {
    flush(this);
  });
};

/**
 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 database 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.database.prototype.buffer = {};

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

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

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

/**
 Gets the value trough the wrapper.
*/
exports.database.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.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.buffer[key].timestamp = new Date().getTime();
    callback(null, this.buffer[key].value);
  }
  //get it direct
  else
  {
    var self = this;

    this.wrappedDB.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();

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

/**
 Sets the value trough the wrapper
*/
exports.database.prototype.set = function(key, value, bufferCallback, writeCallback)
{
  //writing cache is enabled, so simply write it into the buffer
  if(this.settings.writeInterval > 0)
  {
    //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 database,
    //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 database
  else
  {
    //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.wrappedDB.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.wrappedDB.set(key,value,callback);
    }
  }
}

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

  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.database.prototype.getSub = function(key, sub, callback)
{
  //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;
        }
      }

      callback(err, subvalue);
    }
  });
}

/**
 Removes the value trough the wrapper
*/
exports.database.prototype.remove = function(key, bufferCallback, writeCallback)
{
  //make a set to null out of it
  this.set(key, null, bufferCallback, writeCallback);
}

/**
 Garbage Collector of the cache
*/
exports.database.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;
    });

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

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

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

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

      //stringify the value if stringifying is enabled
      if(db.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(db.buffer[i].callbacks);

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

  //send the bulk to the database driver and call the callbacks with the results
  if(operations.length > 0)
  {
    //set the flushing flag
    db.isFlushing = true;

    db.wrappedDB.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)
      {
        db.buffer[operations[i].key].writingInProgress = false;
      }

      if(callback) callback();

      //console.error("FLUSH DONE!");

      //set the flushing flag to false
      db.isFlushing = false;
    });
  }
}

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>// manipulate.js

__$__.Manipulate = {
    /**
     * @param edgeData
     * @param callback
     */
    addEdge: function(edgeData, callback) {
        // TODO: should search simultaneously for efficiency
        // search candidates for reference of the node on the from side
        let from = __$__.Manipulate.searchNode(edgeData.from);
        // search candidates for reference of the node on the to side
        let to = __$__.Manipulate.searchNode(edgeData.to);

        let candidates = __$__.Manipulate.candidates(from, to);
        callback();
        // TODO: move the cursor position to a safe position.
        __$__.Manipulate.startAutoCompletion(candidates);
    },

    /**
     * @param edgeData
     * @param callback
     */
    editEdge: function(edgeData, callback) {
        if (__$__.edges._data[edgeData.id].from !== edgeData.from) {
            // In the case that the user edits the from-side of the edge
            callback();
        } else {
            // In the case that the user edits the to-side of the edge

            // TODO: should search simultaneously for efficiency
            // search candidates for reference of the node on the from side
            let from = __$__.Manipulate.searchNode(edgeData.from);
            // search candidates for reference of the node on the to side
            let to = __$__.Manipulate.searchNode(edgeData.to);

            let candidates = __$__.Manipulate.candidates(from, to, edgeData.id);
            callback();
            // TODO: move the cursor position to a safe position.
            __$__.Manipulate.startAutoCompletion(candidates);
        }
    },

    /**
     * @param endNodeId{String}
     *
     * @return Object
     */
    searchNode: function(/*startNodeIds, */endNodeId) {
        // this variable is a table that represents which edges the node is pointed to.
        // {nodeId: [edgeId1, edgeId2, ...]}
        let node2Edges = new Object();
        Object.keys(__$__.edges._data).forEach(edgeId => {
            let edge = __$__.edges._data[edgeId];
            if (node2Edges[edge.to]) {
                node2Edges[edge.to].push(edge.id);
            } else {
                node2Edges[edge.to] = [edge.id];
            }
        });

        // this table is a memo for dynamic programming.
        let table = new Object();
        table[endNodeId] = [{label: []}];

        let rec = function(edgeId, acc) {
            if (acc.passedEdgeIds[edgeId]) {
                return;
            }
            let edge = __$__.edges._data[edgeId];

            acc.label.unshift(edge.label);
            acc.passedEdgeIds[edgeId] = true;

            // register
            if (!table[edge.from]) {
                table[edge.from] = [];
            }
            table[edge.from].push({
                label: Object.assign([], acc.label)
            });

            if (node2Edges[edge.from])
                node2Edges[edge.from].forEach(eId => {
                    rec(eId, acc)
                });

            acc.label.shift();
            delete acc.passedEdgeIds[edgeId];
        };

        if (node2Edges[endNodeId])
            node2Edges[endNodeId].forEach(edgeId => {
                rec(edgeId, {label: [], passedEdgeIds: {}});
            });
        return table;
    },

    /**
     * @param from
     * @param to
     * @param prop
     * @returns [{}]
     */
    candidates(from, to, prop) {
        let candidates = [];
        Object.keys(from).forEach(nodeId1 => {
            if (nodeId1.slice(0, 11) === '__Variable-') {
                Object.keys(to).forEach(nodeId2 => {
                    if (nodeId2.slice(0, 11) === '__Variable-') {
                        from[nodeId1].forEach(labels_l => {
                            let exp_l = labels_l.label.join('.') + '.' + ((prop) ? __$__.edges._data[prop].label : '${0:prop}');
                            let score_l = labels_l.label.length;
                            if (labels_l.label[0] === 'this') score_l += 0.2;

                            to[nodeId2].forEach(labels_r => {
                                let exp_r = labels_r.label.join('.');
                                let score_r = labels_r.label.length;
                                if (labels_r.label[0] === 'this') score_r += 0.2;

                                let score = score_l + score_r;
                                candidates.push({
                                    // score_l: score_l,
                                    // score_r: score_r,
                                    prop: prop,
                                    score: -score,
                                    stmt: exp_l + ' = ' + exp_r + ';',
                                    exp_l: exp_l,
                                    exp_r: exp_r
                                });
                            });
                        });
                    }
                });
            }
        });
        return candidates;
    },

    /**
     * @param candidates
     */
    startAutoCompletion(candidates) {
        if (Object.keys(candidates).length === 0) {
            let staticWordCompleter = {
                getCompletions: function(editor, session, pos, prefix, callback) {
                    callback(null, [{
                        caption: 'impossible',
                        value: '',
                        meta: 'impossible'
                    }]);
                }
            };
            __$__.langTools.setCompleters([staticWordCompleter]);
            __$__.startAutocomplete(__$__.editor);
        } else {
            let staticWordCompleter = {
                getCompletions: function (editor, session, pos, prefix, callback) {
                    let cs = candidates;
                    callback(null, cs.map(function (candidate) {
                        if (candidate.prop)
                            return {
                                caption: candidate.stmt,
                                value: candidate.stmt,
                                score: candidate.score
                            };
                        else
                            return {
                                caption: candidate.stmt.replace(/\$\{[\s\S]*\}/, "[prop]"),
                                score: candidate.score,
                                snippet: candidate.stmt
                            };
                    }));
                }
            };
            __$__.langTools.setCompleters([staticWordCompleter]);
            __$__.startAutocomplete(__$__.editor);
        }
    }
};<commit_msg>enable to change the reference of variable by DM<commit_after>// manipulate.js

__$__.Manipulate = {
    /**
     * @param edgeData
     * @param callback
     */
    addEdge: function(edgeData, callback) {
        // TODO: should search simultaneously for efficiency
        // search candidates for reference of the node on the from side
        let from = __$__.Manipulate.searchNode(edgeData.from);
        // search candidates for reference of the node on the to side
        let to = __$__.Manipulate.searchNode(edgeData.to);

        let candidates = __$__.Manipulate.candidates(from, to);
        callback();
        // TODO: move the cursor position to a safe position.
        __$__.Manipulate.startAutoCompletion(candidates);
    },

    /**
     * @param edgeData
     * @param callback
     */
    editEdge: function(edgeData, callback) {
        if (__$__.edges._data[edgeData.id].from !== edgeData.from) {
            // In the case that the user edits the from-side of the edge
            callback();
        } else {
            // In the case that the user edits the to-side of the edge

            // TODO: should search simultaneously for efficiency
            // search candidates for reference of the node on the from side
            let from = __$__.Manipulate.searchNode(edgeData.from);
            // search candidates for reference of the node on the to side
            let to = __$__.Manipulate.searchNode(edgeData.to);

            let candidates = __$__.Manipulate.candidates(from, to, edgeData.id);
            callback();
            // TODO: move the cursor position to a safe position.
            __$__.Manipulate.startAutoCompletion(candidates);
        }
    },

    /**
     * @param endNodeId{String}
     *
     * @return Object
     */
    searchNode: function(/*startNodeIds, */endNodeId) {
        // this variable is a table that represents which edges the node is pointed to.
        // {nodeId: [edgeId1, edgeId2, ...]}
        let node2Edges = new Object();
        Object.keys(__$__.edges._data).forEach(edgeId => {
            let edge = __$__.edges._data[edgeId];
            if (node2Edges[edge.to]) {
                node2Edges[edge.to].push(edge.id);
            } else {
                node2Edges[edge.to] = [edge.id];
            }
        });

        // this table is a memo for dynamic programming.
        let table = new Object();
        table[endNodeId] = [{label: []}];

        let rec = function(edgeId, acc) {
            if (acc.passedEdgeIds[edgeId]) {
                return;
            }
            let edge = __$__.edges._data[edgeId];

            acc.label.unshift(edge.label);
            acc.passedEdgeIds[edgeId] = true;

            // register
            if (!table[edge.from]) {
                table[edge.from] = [];
            }
            table[edge.from].push({
                label: Object.assign([], acc.label)
            });

            if (node2Edges[edge.from])
                node2Edges[edge.from].forEach(eId => {
                    rec(eId, acc)
                });

            acc.label.shift();
            delete acc.passedEdgeIds[edgeId];
        };

        if (node2Edges[endNodeId])
            node2Edges[endNodeId].forEach(edgeId => {
                rec(edgeId, {label: [], passedEdgeIds: {}});
            });
        return table;
    },

    /**
     * @param from
     * @param to
     * @param prop
     * @returns [{}]
     */
    candidates(from, to, prop) {
        let candidates = [];
        Object.keys(from).forEach(nodeId1 => {
            if (nodeId1.slice(0, 11) === '__Variable-') {
                Object.keys(to).forEach(nodeId2 => {
                    if (nodeId2.slice(0, 11) === '__Variable-') {
                        from[nodeId1].forEach(labels_l => {
                            if (!labels_l.length && nodeId1 === '__Variable-this')
                                return;
                            let exp_l = labels_l.label.concat([((prop) ? __$__.edges._data[prop].label : '${0:prop}')]).join('.');
                            let score_l = labels_l.label.length;
                            if (labels_l.label[0] === 'this') score_l += 0.2;

                            to[nodeId2].forEach(labels_r => {
                                let exp_r = labels_r.label.join('.');
                                let score_r = labels_r.label.length;
                                if (labels_r.label[0] === 'this') score_r += 0.2;

                                let score = score_l + score_r;
                                candidates.push({
                                    // score_l: score_l,
                                    // score_r: score_r,
                                    prop: prop,
                                    score: -score,
                                    stmt: exp_l + ' = ' + exp_r + ';',
                                    exp_l: exp_l,
                                    exp_r: exp_r
                                });
                            });
                        });
                    }
                });
            }
        });
        return candidates;
    },

    /**
     * @param candidates
     */
    startAutoCompletion(candidates) {
        if (Object.keys(candidates).length === 0) {
            let staticWordCompleter = {
                getCompletions: function(editor, session, pos, prefix, callback) {
                    callback(null, [{
                        caption: 'impossible',
                        value: '',
                        meta: 'impossible'
                    }]);
                }
            };
            __$__.langTools.setCompleters([staticWordCompleter]);
            __$__.startAutocomplete(__$__.editor);
        } else {
            let staticWordCompleter = {
                getCompletions: function (editor, session, pos, prefix, callback) {
                    let cs = candidates;
                    callback(null, cs.map(function (candidate) {
                        if (candidate.prop)
                            return {
                                caption: candidate.stmt,
                                value: candidate.stmt,
                                score: candidate.score
                            };
                        else
                            return {
                                caption: candidate.stmt.replace(/\$\{[\s\S]*\}/, "[prop]"),
                                score: candidate.score,
                                snippet: candidate.stmt
                            };
                    }));
                }
            };
            __$__.langTools.setCompleters([staticWordCompleter]);
            __$__.startAutocomplete(__$__.editor);
        }
    }
};<|endoftext|>


<commit_before>"use strict";

var assert = require('assert');

var statistics = function(s) {

    var min = function(x,y) { return Math.min(x,y); };

    var minimum = s.length === 0 ? undefined : s.reduce(min, s[0]);

    return {
	count: s.length,
	minimum: minimum
    };
};

describe('sequence processing', function() {
    it('should return the count', function() {
	assert.equal(0, statistics([]).count);
	assert.equal(3, statistics([1,2,3]).count);
    });

    it('avg, min and max should be undefined for empty lists', function() {
	var stats = statistics([]);

	assert.equal(undefined, stats.minimum);
	assert.equal(undefined, stats.maximum);
	assert.equal(undefined, stats.average);
    });

    it('min works as expected on lists', function() {
	assert.equal(0, statistics([0]).minimum);
    });
});
<commit_msg>demonstrate expected pass<commit_after>"use strict";

var assert = require('assert');

var statistics = function(s) {

    var min = function(x,y) { return Math.min(x,y); };

    var minimum = s.length === 0 ? undefined : s.reduce(min, s[0]);

    return {
	count: s.length,
	minimum: minimum
    };
};

describe('sequence processing', function() {
    it('should return the count', function() {
	assert.equal(0, statistics([]).count);
	assert.equal(3, statistics([1,2,3]).count);
    });

    it('avg, min and max should be undefined for empty lists', function() {
	var stats = statistics([]);

	assert.equal(undefined, stats.minimum);
	assert.equal(undefined, stats.maximum);
	assert.equal(undefined, stats.average);
    });

    it('min works as expected on lists', function() {
	assert.equal(0, statistics([0]).minimum);
	assert.equal(3, statistics([5,4,3]).minimum);
    });

});
<|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";
function detailFormatter(index, row) {
    var html = [];
    $.each(row, function (key, value) {
        html.push('
' + key + ': ' + value + '
');
    });
    return html.join('');
}
function isTouchDevice() {
    try {
        document.createEvent("TouchEvent");
        return true;
    } catch (e) {
        return false;
    }
}
function touchScroll(selector) {
    if (isTouchDevice()) {
        var scrollStartPosY = 0;
        var scrollStartPosX = 0;
        $("body").delegate(selector, 'touchstart', function (e) {
            scrollStartPosY = this.scrollTop + e.originalEvent.touches[0].pageY;
            scrollStartPosX = this.scrollLeft + e.originalEvent.touches[0].pageX;
        });
        $("body").delegate(selector, 'touchmove', function (e) {
            if ((this.scrollTop < this.scrollHeight - this.offsetHeight &&
                this.scrollTop + e.originalEvent.touches[0].pageY < scrollStartPosY - 5) ||
                (this.scrollTop != 0 && this.scrollTop + e.originalEvent.touches[0].pageY > scrollStartPosY + 5))
                e.preventDefault();
            if ((this.scrollLeft < this.scrollWidth - this.offsetWidth &&
                this.scrollLeft + e.originalEvent.touches[0].pageX < scrollStartPosX - 5) ||
                (this.scrollLeft != 0 && this.scrollLeft + e.originalEvent.touches[0].pageX > scrollStartPosX + 5))
                e.preventDefault();
            this.scrollTop = scrollStartPosY - e.originalEvent.touches[0].pageY;
            this.scrollLeft = scrollStartPosX - e.originalEvent.touches[0].pageX;
        });
    }
}

function array_unique(ar) {
    if (ar.length && typeof ar !== 'string') {
        var sorter = {};
        var out = [];
        for (var i = 0, j = ar.length; i < j; i++) {
            if (!sorter[ar[i] + typeof ar[i]]) {
                out.push(ar[i]);
                sorter[ar[i] + typeof ar[i]] = true;
            }
        }
    }
    return out || ar;
}

/**
 *
 *  Base64 encode / decode
 *  http://www.webtoolkit.info/
 *
 **/
var Base64 = {

    // private property
    _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

    // public method for encoding
    encode: function (input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = Base64._utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

        }

        return output;
    },

    // public method for decoding
    decode: function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = this._keyStr.indexOf(input.charAt(i++));
            enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++));
            enc4 = this._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }

        }

        output = Base64._utf8_decode(output);

        return output;

    },

    // private method for UTF-8 encoding
    _utf8_encode: function (string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    },

    // private method for UTF-8 decoding
    _utf8_decode: function (utftext) {
        var string = "", i, c1, c2, c3, c;
        c = 0;
        c1 = 0;
        c2 = 0;
        c3 = 0;

        while (i < utftext.length) {
            c = utftext.charCodeAt(i);
            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            } else if ((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            } else {
                c2 = utftext.charCodeAt(i + 1);
                c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }

        }
        return string;
    }
}<commit_msg>Refactoring<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";
function detailFormatter(index, row) {
    var html = [];
    $.each(row, function (key, value) {
        html.push('
' + key + ': ' + value + '
');
    });
    return html.join('');
}
function isTouchDevice() {
    try {
        document.createEvent("TouchEvent");
        return true;
    } catch (e) {
        return false;
    }
}
function touchScroll(selector) {
    if (isTouchDevice()) {
        var scrollStartPosY = 0;
        var scrollStartPosX = 0;
        $("body").delegate(selector, 'touchstart', function (e) {
            scrollStartPosY = this.scrollTop + e.originalEvent.touches[0].pageY;
            scrollStartPosX = this.scrollLeft + e.originalEvent.touches[0].pageX;
        });
        $("body").delegate(selector, 'touchmove', function (e) {
            if ((this.scrollTop < this.scrollHeight - this.offsetHeight &&
                this.scrollTop + e.originalEvent.touches[0].pageY < scrollStartPosY - 5) ||
                (this.scrollTop != 0 && this.scrollTop + e.originalEvent.touches[0].pageY > scrollStartPosY + 5))
                e.preventDefault();
            if ((this.scrollLeft < this.scrollWidth - this.offsetWidth &&
                this.scrollLeft + e.originalEvent.touches[0].pageX < scrollStartPosX - 5) ||
                (this.scrollLeft != 0 && this.scrollLeft + e.originalEvent.touches[0].pageX > scrollStartPosX + 5))
                e.preventDefault();
            this.scrollTop = scrollStartPosY - e.originalEvent.touches[0].pageY;
            this.scrollLeft = scrollStartPosX - e.originalEvent.touches[0].pageX;
        });
    }
}

function array_unique(ar) {
    return ar.filter( function onlyUnique(value, index, self) {
        return self.indexOf(value) === index;
    } )
}

/**
 *
 *  Base64 encode / decode
 *  http://www.webtoolkit.info/
 *
 **/
var Base64 = {

    // private property
    _keyStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

    // public method for encoding
    encode: function (input) {
        var output = "";
        var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
        var i = 0;

        input = Base64._utf8_encode(input);

        while (i < input.length) {

            chr1 = input.charCodeAt(i++);
            chr2 = input.charCodeAt(i++);
            chr3 = input.charCodeAt(i++);

            enc1 = chr1 >> 2;
            enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
            enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
            enc4 = chr3 & 63;

            if (isNaN(chr2)) {
                enc3 = enc4 = 64;
            } else if (isNaN(chr3)) {
                enc4 = 64;
            }

            output = output + this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) + this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);

        }

        return output;
    },

    // public method for decoding
    decode: function (input) {
        var output = "";
        var chr1, chr2, chr3;
        var enc1, enc2, enc3, enc4;
        var i = 0;

        input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");

        while (i < input.length) {

            enc1 = this._keyStr.indexOf(input.charAt(i++));
            enc2 = this._keyStr.indexOf(input.charAt(i++));
            enc3 = this._keyStr.indexOf(input.charAt(i++));
            enc4 = this._keyStr.indexOf(input.charAt(i++));

            chr1 = (enc1 << 2) | (enc2 >> 4);
            chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
            chr3 = ((enc3 & 3) << 6) | enc4;

            output = output + String.fromCharCode(chr1);

            if (enc3 != 64) {
                output = output + String.fromCharCode(chr2);
            }
            if (enc4 != 64) {
                output = output + String.fromCharCode(chr3);
            }

        }

        output = Base64._utf8_decode(output);

        return output;

    },

    // private method for UTF-8 encoding
    _utf8_encode: function (string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";

        for (var n = 0; n < string.length; n++) {

            var c = string.charCodeAt(n);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            } else if ((c > 127) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            } else {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }

        }

        return utftext;
    },

    // private method for UTF-8 decoding
    _utf8_decode: function (utftext) {
        var string = "", i, c1, c2, c3, c;
        c = 0;
        c1 = 0;
        c2 = 0;
        c3 = 0;

        while (i < utftext.length) {
            c = utftext.charCodeAt(i);
            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            } else if ((c > 191) && (c < 224)) {
                c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            } else {
                c2 = utftext.charCodeAt(i + 1);
                c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }

        }
        return string;
    }
}<|endoftext|>


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

HyperNotes.View = function($) {
  var my = {};

  my.getPermissions = function(model) {
    var isOwner = (Boolean(model.get('owner')) && model.get('owner') == HyperNotes.environ.account.id);
    return permissions = {
      edit: isOwner
      , delete: isOwner
    };
  },

  my.alert = function(msg, category) {
    var _category = category || 'info';
    var _template = '
×
${msg}

';
    var _templated = $.tmpl(_template, {
      msg: msg,
      category: _category
      });
    $('.alert-messages').append(_templated);
  }

  my.NoteView = Backbone.View.extend({
    className: 'note view summary',
    template: ' \
          {{if permissions.edit}} \

 \
            Edit \
             \

 \
          {{/if}} \

 \
            ${note.title} \

 \
          {{if note.image}} \

 \
             \

 \
          {{/if}} \

 \
            {{if note.start}} \
              ${note.start} (${note.start_parsed}) \
              {{if note.end}} \
               – ${note.end} (${note.end_parsed}) \
              {{/if}} \
           {{/if}} \

 \

 \
            {{if note.location.unparsed}} \
            @${note.location.unparsed} \
            {{/if}} \

 \

 \
            {{if note.tags.length}} \

 \
              {{each note.tags}} \

#${$value}
 \
              {{/each}} \

 \
            {{/if}} \

 \

 \
            ${note.description} \

 \

 \
            More » \
            Less « \

 \
    ',

    events: {
      'click .show-more' : 'onShowMore',
      'click .show-less' : 'onShowLess',
      'click .action-edit': 'onEdit'
    },

    initialize: function() {
      this.el = $(this.el);
      _.bindAll(this, 'render', 'onEditComplete');
      this.model.bind('change', this.render);
    },

    render: function() {
      var permissions = my.getPermissions(this.model);
      var tmplData = {
        note: this.model.toTemplateJSON(),
        permissions: permissions
      }
      var templated = $.tmpl(this.template, tmplData);
      this.el.html(templated);
      this.el.addClass('summary');
      this.el.show();
      return this;
    },

    remove: function() {
      $(this.el).remove();
    },

    onShowMore: function(e) {
      e.preventDefault();
      this.el.removeClass('summary');
    },

    onShowLess: function(e) {
      e.preventDefault();
      this.el.addClass('summary');
    },

    onEdit: function(e) {
      e.preventDefault();
      var editView = new my.NoteEdit({
        model: this.model
      });
      editView.bind('edit:complete', this.onEditComplete)
      editView.render();
      this.el.hide();
      this.el.after(editView.el);
    },

    onEditComplete: function() {
      this.el.show();
    },

    clear: function() {
      // TODO: decide whether we destory or put in deleted state or just
      // remove from collection
      // this.model.destroy();
      this.remove();
      this.model.trigger('destroy', this.model);
    }
  });

  my.NoteEdit = Backbone.View.extend({
    className: 'note edit',
    template: ' \

Editing – ${note.title}
 \
      <form> \
        <fieldset> \
          <legend> \
          </legend> \

 \
            Title \

 \
              <input \
                name="title" \
                type="text" \
                value="${note.title}" \
                class="xlarge" \
                /> \

 \

 \
             \

 \
            Start–End \

 \

 \
                <input \
                  name="start" \
                  type="text" \
                  value="${note.start}" \
                  class="small" \
                  /> \
                – \
                <input \
                  name="end" \
                  type="text" \
                  value="${note.end}" \
                  class="small" \
                  /> \

 \

 \

 \

 \
            Start–End (parsed) \

 \

 \
                <input \
                  name="start_parsed" \
                  type="text" \
                  value="${note.start_parsed}" \
                  class="small" \
                  /> \
                – \
                <input \
                  name="end_parsed" \
                  type="text" \
                  value="${note.end_parsed}" \
                  class="small" \
                  /> \

 \

 \

 \
             \

 \
            Description \

 \
              <textarea \
                name="description" \
                type="text" \
                class="xlarge" \
                rows="15" \
                >${note.description}</textarea> \

 \

 \
        </fieldset> \

 \
          <button type="submit" class="btn primary">Save changes</button> \
          <button type="reset" class="btn danger">Cancel</button> \

 \
      </form> \
    ',
    events: {
      'submit form': 'onSubmitForm',
      'reset form': 'onResetForm'
    },

    initialize: function() {
      this.el = $(this.el);
      _.bindAll(this, 'onSubmitForm');
    },

    render: function() {
      var tmplData = {
        note: this.model.toJSON(),
      }
      var templated = $.tmpl(this.template, tmplData);
      this.el.html(templated);
    },

    onSubmitForm: function(e) {
      var self = this;
      e.preventDefault();
      // TODO: save data ...
      var _data = this.el.find('form').serializeArray();
      modelData = {};
      $.each(_data, function(idx, value) {
        // ignore empty strings and other null values
        if (value.value!=null && value.value!='') {
          modelData[value.name] = value.value
        }
      });
      this.model.set(modelData);
      this.model.save({}, {
        success: function(model) {
          my.alert('Saved note.', 'success');
          self.trigger('edit:complete');
          self.remove();
        },
        error: function(model, error) {
          // TODO: Flash error ...
          my.alert('Error saving note' + error.responseText, 'error');
        }
      });
    },

    onResetForm: function(e) {
      e.preventDefault();
      this.trigger('edit:complete');
      this.remove();
    }
  });

  my.NoteListView = Backbone.View.extend({
    // Delegated events for creating new items, and clearing completed ones.
    initialize: function() {
      _.bindAll(this, 'addOne', 'addAll', 'render', 'removeOne');

      this.collection.bind('add',     this.addOne);
      this.collection.bind('reset', this.addAll);
      this.collection.bind('all',     this.render);
      this.collection.bind('destroy', this.removeOne);
    },

    addOne: function(note) {
      var view = new my.NoteView({model: note});
      var $li = $('
').append(view.render().el);
      this.el.find('> ul').append($li);
    },

    addAll: function() {
      this.collection.each(this.addOne);
    },

    removeOne: function(note) {
      this.collection.remove(note);
    }
  });

  my.TimeMapView = Backbone.View.extend({
    events: {
    },

    initialize: function() {
      _.bindAll(this, 'render');
      this.collection.bind('all', this.render);
    },

    _getDatasetForTimeMap: function() {
      var datasets = [
        {
          id: "xxxx",
          title: "my dataset",
          theme: "orange",
          type: "basic",
          options: {
            eventIcon: '/vendor/timemap/2.0/images/orange-circle.png',
            items: []
          }
        }
      ];
      $.each(this.collection.models, function(idx, model) {
        var modelJS = model.toJSON();
        var view = new my.NoteView({
          model: model
        });
        view.render();
        view.el.removeClass('summary');
        var viewHtml = view.el.clone().wrap('
').parent().html();
        var timemapObj = {
          title : modelJS.title,
          start : modelJS.start_parsed || modelJS.start,
          point: {
              lon : modelJS.location.centroid[0],
              lat : modelJS.location.centroid[1]
           },
          options : {
            infoHtml: viewHtml
          }
        };
        // timemap behaves oddly if you give it a null end date
        if (modelJS.end) {
          timemapObj.end = modelJS.end_parsed || modelJS.end;
        }
        datasets[0].options.items.push(timemapObj);
      });
      return datasets;
    },

    render: function() {
      var out = $.tmpl(HyperNotes.Template.timeMap, {});
      this.el.html(out);
      var datasets = this._getDatasetForTimeMap();
      var tm;
      try {
        tm = TimeMap.init({
          mapId: "map",
          timelineId: "timeline",
          datasets: datasets,
          options: {
            eventIconPath: "../vendor/timemap/2.0/images/"
          },
          bandIntervals: [
              Timeline.DateTime.DECADE,
              Timeline.DateTime.CENTURY
          ]
        });
      } catch (e) {
        // console.log(e);
      }

    }
  });

  my.ThreadView = Backbone.View.extend({
    template: ' \

 \
        Overview \
        TimeMap \

 \

${thread.title}
 \

 \

${thread.description}
 \

 \

 \

 \

 \

 \

 \
            \

 \

 \
          {{if permissions.edit}} \

 \
            <form> \
              Quick Add \

 \
                <input \
                  name="new-note" \
                  placeholder="My note title #my-tag ^1st September 1939^ @New York@" \
                  type="text" /> \

 \
               \
                Add note then enter to save. Use #... for tags, ^...^ for dates, @...@ for location \
               \
            </form> \

 \
          {{/if}} \

 \

 \

 \
    ',
    events: {
      "submit .note.quick-add form":  "createOnEnter",
    },

    initialize: function() {
      _.bindAll(this, 'render');
      this.model.bind('change', this.render);
      var permissions = my.getPermissions(this.model);
      var tmplData = {
        thread: this.model.toJSON()
        , permissions: permissions
      };
      var templated = $.tmpl(this.template, tmplData);
      $(this.el).html(templated);
      this.$notelist = this.el.find('.note-list');
      this.noteListView = new HyperNotes.View.NoteListView({
        el: this.$notelist,
        collection: this.model.notes
        });
      this.timemap = new HyperNotes.View.TimeMapView({
        el: this.el.find('#timemap'),
        collection: this.model.notes
      });
      this.timemap.render();
    },

    render: function() {
      this.el.find('h1.title').html(this.model.get('title'));
      this.el.find('.thread-info .description').html(this.model.get('description'));
      return this;
    },

    createOnEnter: function(e) {
      var self = this;
      e.preventDefault();
      var $form = $(e.target);
      var $input = $form.find('input[name="new-note"]');
      var summary = $input.val();
      HyperNotes.Model.createNoteFromSummary(summary, function(newNote) {
        newNote.set({'owner': HyperNotes.environ.account.id});
        // only add once we have saved and have id ...
        newNote.save(null, {
          success: function(data) {
            self.model.notes.add(newNote);
            my.alert('New note created and added to thread.', 'success');
          },
          error: function(data) {
            my.alert('Failed to create note.' + data, 'error');
          }
        });
      });
      $input.val('');
    }
  });

  my.ThreadCreate = Backbone.View.extend({
    initialize: function() {
    },

    events: {
      'submit form': 'onFormSubmit'
      , 'change input[name="title"]': 'onTitleChange'
    },

    onFormSubmit: function(e) {
      e.preventDefault();
      var self = this;
      var _data = $(e.target).serializeArray();
      var modelData = {};
      _.each(_data, function(item) {
        modelData[item.name] = item.value;
      });
      this.model.set(modelData);
      var $createBtn = this.el.find('form .action input');
      $createBtn.val('Saving ...');
      this.model.save()
        .then(function() {
          window.location = window.location + '/' + self.model.get('name');
        })
        .fail(function() {
          my.alert('Failed ' + arguments, 'error');
        });
        ;
    },

    onTitleChange: function(e) {
      var title = $(e.target).val();
      title = title.toLowerCase().replace(/ /g, '-');
      title = title.replace(/[^a-z0-9._-]/g, '');
      var $name = this.el.find('input[name="name"]');
      $name.val(title);
    }
  });

  return my;
}(jQuery);

<commit_msg>[js/thread/view][xs]: put no description message if no description for thread.<commit_after>var HyperNotes = HyperNotes || {};

HyperNotes.View = function($) {
  var my = {};

  my.getPermissions = function(model) {
    var isOwner = (Boolean(model.get('owner')) && model.get('owner') == HyperNotes.environ.account.id);
    return permissions = {
      edit: isOwner
      , delete: isOwner
    };
  },

  my.alert = function(msg, category) {
    var _category = category || 'info';
    var _template = '
×
${msg}

';
    var _templated = $.tmpl(_template, {
      msg: msg,
      category: _category
      });
    $('.alert-messages').append(_templated);
  }

  my.NoteView = Backbone.View.extend({
    className: 'note view summary',
    template: ' \
          {{if permissions.edit}} \

 \
            Edit \
             \

 \
          {{/if}} \

 \
            ${note.title} \

 \
          {{if note.image}} \

 \
             \

 \
          {{/if}} \

 \
            {{if note.start}} \
              ${note.start} (${note.start_parsed}) \
              {{if note.end}} \
               – ${note.end} (${note.end_parsed}) \
              {{/if}} \
           {{/if}} \

 \

 \
            {{if note.location.unparsed}} \
            @${note.location.unparsed} \
            {{/if}} \

 \

 \
            {{if note.tags.length}} \

 \
              {{each note.tags}} \

#${$value}
 \
              {{/each}} \

 \
            {{/if}} \

 \

 \
            ${note.description} \

 \

 \
            More » \
            Less « \

 \
    ',

    events: {
      'click .show-more' : 'onShowMore',
      'click .show-less' : 'onShowLess',
      'click .action-edit': 'onEdit'
    },

    initialize: function() {
      this.el = $(this.el);
      _.bindAll(this, 'render', 'onEditComplete');
      this.model.bind('change', this.render);
    },

    render: function() {
      var permissions = my.getPermissions(this.model);
      var tmplData = {
        note: this.model.toTemplateJSON(),
        permissions: permissions
      }
      var templated = $.tmpl(this.template, tmplData);
      this.el.html(templated);
      this.el.addClass('summary');
      this.el.show();
      return this;
    },

    remove: function() {
      $(this.el).remove();
    },

    onShowMore: function(e) {
      e.preventDefault();
      this.el.removeClass('summary');
    },

    onShowLess: function(e) {
      e.preventDefault();
      this.el.addClass('summary');
    },

    onEdit: function(e) {
      e.preventDefault();
      var editView = new my.NoteEdit({
        model: this.model
      });
      editView.bind('edit:complete', this.onEditComplete)
      editView.render();
      this.el.hide();
      this.el.after(editView.el);
    },

    onEditComplete: function() {
      this.el.show();
    },

    clear: function() {
      // TODO: decide whether we destory or put in deleted state or just
      // remove from collection
      // this.model.destroy();
      this.remove();
      this.model.trigger('destroy', this.model);
    }
  });

  my.NoteEdit = Backbone.View.extend({
    className: 'note edit',
    template: ' \

Editing – ${note.title}
 \
      <form> \
        <fieldset> \
          <legend> \
          </legend> \

 \
            Title \

 \
              <input \
                name="title" \
                type="text" \
                value="${note.title}" \
                class="xlarge" \
                /> \

 \

 \
             \

 \
            Start–End \

 \

 \
                <input \
                  name="start" \
                  type="text" \
                  value="${note.start}" \
                  class="small" \
                  /> \
                – \
                <input \
                  name="end" \
                  type="text" \
                  value="${note.end}" \
                  class="small" \
                  /> \

 \

 \

 \

 \
            Start–End (parsed) \

 \

 \
                <input \
                  name="start_parsed" \
                  type="text" \
                  value="${note.start_parsed}" \
                  class="small" \
                  /> \
                – \
                <input \
                  name="end_parsed" \
                  type="text" \
                  value="${note.end_parsed}" \
                  class="small" \
                  /> \

 \

 \

 \
             \

 \
            Description \

 \
              <textarea \
                name="description" \
                type="text" \
                class="xlarge" \
                rows="15" \
                >${note.description}</textarea> \

 \

 \
        </fieldset> \

 \
          <button type="submit" class="btn primary">Save changes</button> \
          <button type="reset" class="btn danger">Cancel</button> \

 \
      </form> \
    ',
    events: {
      'submit form': 'onSubmitForm',
      'reset form': 'onResetForm'
    },

    initialize: function() {
      this.el = $(this.el);
      _.bindAll(this, 'onSubmitForm');
    },

    render: function() {
      var tmplData = {
        note: this.model.toJSON(),
      }
      var templated = $.tmpl(this.template, tmplData);
      this.el.html(templated);
    },

    onSubmitForm: function(e) {
      var self = this;
      e.preventDefault();
      // TODO: save data ...
      var _data = this.el.find('form').serializeArray();
      modelData = {};
      $.each(_data, function(idx, value) {
        // ignore empty strings and other null values
        if (value.value!=null && value.value!='') {
          modelData[value.name] = value.value
        }
      });
      this.model.set(modelData);
      this.model.save({}, {
        success: function(model) {
          my.alert('Saved note.', 'success');
          self.trigger('edit:complete');
          self.remove();
        },
        error: function(model, error) {
          // TODO: Flash error ...
          my.alert('Error saving note' + error.responseText, 'error');
        }
      });
    },

    onResetForm: function(e) {
      e.preventDefault();
      this.trigger('edit:complete');
      this.remove();
    }
  });

  my.NoteListView = Backbone.View.extend({
    // Delegated events for creating new items, and clearing completed ones.
    initialize: function() {
      _.bindAll(this, 'addOne', 'addAll', 'render', 'removeOne');

      this.collection.bind('add',     this.addOne);
      this.collection.bind('reset', this.addAll);
      this.collection.bind('all',     this.render);
      this.collection.bind('destroy', this.removeOne);
    },

    addOne: function(note) {
      var view = new my.NoteView({model: note});
      var $li = $('
').append(view.render().el);
      this.el.find('> ul').append($li);
    },

    addAll: function() {
      this.collection.each(this.addOne);
    },

    removeOne: function(note) {
      this.collection.remove(note);
    }
  });

  my.TimeMapView = Backbone.View.extend({
    events: {
    },

    initialize: function() {
      _.bindAll(this, 'render');
      this.collection.bind('all', this.render);
    },

    _getDatasetForTimeMap: function() {
      var datasets = [
        {
          id: "xxxx",
          title: "my dataset",
          theme: "orange",
          type: "basic",
          options: {
            eventIcon: '/vendor/timemap/2.0/images/orange-circle.png',
            items: []
          }
        }
      ];
      $.each(this.collection.models, function(idx, model) {
        var modelJS = model.toJSON();
        var view = new my.NoteView({
          model: model
        });
        view.render();
        view.el.removeClass('summary');
        var viewHtml = view.el.clone().wrap('
').parent().html();
        var timemapObj = {
          title : modelJS.title,
          start : modelJS.start_parsed || modelJS.start,
          point: {
              lon : modelJS.location.centroid[0],
              lat : modelJS.location.centroid[1]
           },
          options : {
            infoHtml: viewHtml
          }
        };
        // timemap behaves oddly if you give it a null end date
        if (modelJS.end) {
          timemapObj.end = modelJS.end_parsed || modelJS.end;
        }
        datasets[0].options.items.push(timemapObj);
      });
      return datasets;
    },

    render: function() {
      var out = $.tmpl(HyperNotes.Template.timeMap, {});
      this.el.html(out);
      var datasets = this._getDatasetForTimeMap();
      var tm;
      try {
        tm = TimeMap.init({
          mapId: "map",
          timelineId: "timeline",
          datasets: datasets,
          options: {
            eventIconPath: "../vendor/timemap/2.0/images/"
          },
          bandIntervals: [
              Timeline.DateTime.DECADE,
              Timeline.DateTime.CENTURY
          ]
        });
      } catch (e) {
        // console.log(e);
      }

    }
  });

  my.ThreadView = Backbone.View.extend({
    template: ' \

 \
        Overview \
        TimeMap \

 \

${thread.title}
 \

 \

 \
          ${thread.description} \
          {{if !thread.description}} \
            No description yet ... \
          {{/if}} \

 \

 \

 \

 \

 \

 \

 \
            \

 \

 \
          {{if permissions.edit}} \

 \
            <form> \
              Quick Add \

 \
                <input \
                  name="new-note" \
                  placeholder="My note title #my-tag ^1st September 1939^ @New York@" \
                  type="text" /> \

 \
               \
                Add note then enter to save. Use #... for tags, ^...^ for dates, @...@ for location \
               \
            </form> \

 \
          {{/if}} \

 \

 \

 \
    ',
    events: {
      "submit .note.quick-add form":  "createOnEnter",
    },

    initialize: function() {
      _.bindAll(this, 'render');
      this.model.bind('change', this.render);
      var permissions = my.getPermissions(this.model);
      var tmplData = {
        thread: this.model.toJSON()
        , permissions: permissions
      };
      var templated = $.tmpl(this.template, tmplData);
      $(this.el).html(templated);
      this.$notelist = this.el.find('.note-list');
      this.noteListView = new HyperNotes.View.NoteListView({
        el: this.$notelist,
        collection: this.model.notes
        });
      this.timemap = new HyperNotes.View.TimeMapView({
        el: this.el.find('#timemap'),
        collection: this.model.notes
      });
      this.timemap.render();
    },

    render: function() {
      this.el.find('h1.title').html(this.model.get('title'));
      this.el.find('.thread-info .description').html(this.model.get('description'));
      return this;
    },

    createOnEnter: function(e) {
      var self = this;
      e.preventDefault();
      var $form = $(e.target);
      var $input = $form.find('input[name="new-note"]');
      var summary = $input.val();
      HyperNotes.Model.createNoteFromSummary(summary, function(newNote) {
        newNote.set({'owner': HyperNotes.environ.account.id});
        // only add once we have saved and have id ...
        newNote.save(null, {
          success: function(data) {
            self.model.notes.add(newNote);
            my.alert('New note created and added to thread.', 'success');
          },
          error: function(data) {
            my.alert('Failed to create note.' + data, 'error');
          }
        });
      });
      $input.val('');
    }
  });

  my.ThreadCreate = Backbone.View.extend({
    initialize: function() {
    },

    events: {
      'submit form': 'onFormSubmit'
      , 'change input[name="title"]': 'onTitleChange'
    },

    onFormSubmit: function(e) {
      e.preventDefault();
      var self = this;
      var _data = $(e.target).serializeArray();
      var modelData = {};
      _.each(_data, function(item) {
        modelData[item.name] = item.value;
      });
      this.model.set(modelData);
      var $createBtn = this.el.find('form .action input');
      $createBtn.val('Saving ...');
      this.model.save()
        .then(function() {
          window.location = window.location + '/' + self.model.get('name');
        })
        .fail(function() {
          my.alert('Failed ' + arguments, 'error');
        });
        ;
    },

    onTitleChange: function(e) {
      var title = $(e.target).val();
      title = title.toLowerCase().replace(/ /g, '-');
      title = title.replace(/[^a-z0-9._-]/g, '');
      var $name = this.el.find('input[name="name"]');
      $name.val(title);
    }
  });

  return my;
}(jQuery);

<|endoftext|>


<commit_before>var ANIMATION_DURATION = 250;

module.exports = Em.Component.extend({
    classNames: ['modal-mask'],

    classNameBindings: ['animated:modal-mask-animated'],

    attributeBindings: ['style'],

    animated: true,

    animationDuration: function() {
        return this.get('animated') ? ANIMATION_DURATION : 0;
    }.property('animated'),

    isAnimateDestroying: false,

    zIndex: 1000,

    style: function() {
        return 'z-index:'+this.get('zIndex')+';';
    }.property('zIndex'),

    show: function() {
        this.appendTo(this.container.lookup('application:main').get('rootElement'));
    },

    didInsertElement: function() {
        var self = this,
            el = this.$();
        Em.run.next(function() {
            if (self.get('isDestroying')) {
                return;
            }
            el.addClass('visible');
            self.blurBelowLayer(true);
        });
    },

    animateDestroy: function() {
        var el = this.$();
        el.removeClass('visible');
        this.blurBelowLayer(false);
        this.set('isAnimateDestroying', true);
        Em.run.later(this, function() {
            this.destroy();
        }, this.get('animationDuration'));
    },

    blurBelowLayer: function(blurred) {
        var zIndex = this.get('zIndex');
        var belowLayers = this.$().siblings('.application, .layer')
            .filter(function(index, s) {
                var sz = $(s).css('z-index');
                return (sz === 'auto' || sz < zIndex);
            });
        belowLayers[blurred ? 'addClass' : 'removeClass']('modal-mask-blur');
    }
});<commit_msg>Added `insertTime` property<commit_after>var ANIMATION_DURATION = 250;

module.exports = Em.Component.extend({
    classNames: ['modal-mask'],

    classNameBindings: ['animated:modal-mask-animated'],

    attributeBindings: ['style'],

    animated: true,

    animationDuration: function() {
        return this.get('animated') ? ANIMATION_DURATION : 0;
    }.property('animated'),

    isAnimateDestroying: false,

    insertTime: null,

    zIndex: 1000,

    style: function() {
        return 'z-index:'+this.get('zIndex')+';';
    }.property('zIndex'),

    show: function() {
        this.appendTo(this.container.lookup('application:main').get('rootElement'));
    },

    didInsertElement: function() {
        var self = this,
            el = this.$();
        this.set('insertTime', Date.now());
        Em.run.next(function() {
            if (self.get('isDestroying')) {
                return;
            }
            el.addClass('visible');
            self.blurBelowLayer(true);
        });
    },

    animateDestroy: function() {
        var el = this.$();
        el.removeClass('visible');
        this.blurBelowLayer(false);
        this.set('isAnimateDestroying', true);
        Em.run.later(this, function() {
            this.destroy();
        }, this.get('animationDuration'));
    },

    blurBelowLayer: function(blurred) {
        var zIndex = this.get('zIndex');
        var belowLayers = this.$().siblings('.application, .layer')
            .filter(function(index, s) {
                var sz = $(s).css('z-index');
                return (sz === 'auto' || sz < zIndex);
            });
        belowLayers[blurred ? 'addClass' : 'removeClass']('modal-mask-blur');
    }
});<|endoftext|>


<commit_before>
$(document).ready(function() {
  var w = window,
      d = document,
      e = d.documentElement,
      g = d.getElementsByTagName('body')[0],
      x = w.innerWidth || e.clientWidth || g.clientWidth,
      y = w.innerHeight|| e.clientHeight|| g.clientHeight;

  window.viewport = {
      width: x, height: y
  };
  console.log("Viewport: w=" + viewport.width + ", h=" + viewport.height);
});

/*
 * Exploring the various ways of rendering the images:
 *  - series of img tags
 *  - series of canvas tags
 *  - single canvas, add all photos to it
 */

var ImagesView = Backbone.View.extend({

  initialize: function(options) {
    this.el = $("canvas#c1"); // ?
    this.collection.on("add", this.onAdd, this);
    this.collection.on("remove", this.onRemove, this);
    this.collection.on("update", this.onUpdate, this);
    this.__whoami = 'ImagesView'; //debugging
    this.setup_canvas();
  },

  setup_canvas: function() {
    var canvas = document.getElementById("c1"); // not really the backbone idiom
    canvas.width  = viewport.width;
    canvas.height = viewport.height;
  },

  render_to_1canvas: function(im, hscale, alpha) {
    var self = this;
    var canvas = document.getElementById("c1"); // not really the backbone idiom
    var ctx = canvas.getContext('2d');
    // alpha (0-1): conversion from opacity (0-100 percent)
    ctx.globalAlpha = alpha;
    // horizontal scaling
    ctx.scale(hscale, 1.0);
    ctx.drawImage(im,0,0, viewport.width, viewport.height);
  },

  render_canvas: function(img, hstretch, opacity) {
    var self = this;

    var alpha = (opacity / 100.0);
    self.render_to_1canvas(img, hstretch, alpha);
  },

  render_image: function(img_element, img_model) {
    var self = this;
    var depth = img_model.get('depth');
    var mag = img_model.get('mag');
    // depth translates to blur, from 0-800 range to 0.0-3.0
    var blur_by_depth = (depth / 200.0);
    // mag(nitude) translates to opacity & horizontal-stretch
    //   ranges:: mag: 0-10, opacity: 10-90 percent, hstretch: 1.0-3.0
    //var opacity_by_mag = (parseFloat(img_model.get('mag')) / 12.0);
    var opacity_by_mag = Math.floor(8.0 * mag + 10.0); // percent: 10-90 range
    var hstretch_by_mag = (mag / 5.0) + 1.0;

    // image size/width & opacity
    var jq_img = $(img_element);
    var vp_width = viewport.width;
    if (vp_width > 50) {
      jq_img.attr({width: vp_width});
    }
    jq_img.css({opacity: opacity_by_mag});

    Pixastic.process(img_element, "blurfast", {amount: blur_by_depth});
    self.render_canvas(img_element, hstretch_by_mag, opacity_by_mag);
  },

  // event is tied to this method:
  onAdd: function(img_model) {
    var self = this;
    var vp_width = viewport.width;
    var url = img_model.get('proxy_url');
    var iid = img_model.get('image_id'); // flickr image id
    var eid = img_model.get('eqid');
    console.log("onAdd(image) iid=" + iid + " for " + eid);

    var img = new Image();
    img.onload = function(ev) {
      var img_elem = ev.srcElement;
      $('#hidden-images').append(img_elem); // storage place for images
      self.render_image(img_elem, img_model);
    }
    img.id = eid;
    img.src = url;
    console.log("  *onAdd-image(queued): " + eid);
  },

  onRemove: function(img_model) {
    var eid = img_model.get('eqid');
    console.log(" *onRemove-image: " + eid);
    $('#' + eid).remove();
  },

  onUpdate: function(img_model)  {
    var eid = img_model.get('eqid');
    console.log(" *onUpdate-image: " + eid);
  },

});

<commit_msg>rearrange view initializer<commit_after>
$(document).ready(function() {
  var w = window,
      d = document,
      e = d.documentElement,
      g = d.getElementsByTagName('body')[0],
      x = w.innerWidth || e.clientWidth || g.clientWidth,
      y = w.innerHeight|| e.clientHeight|| g.clientHeight;

  window.viewport = {
      width: x, height: y
  };
  console.log("Viewport: w=" + viewport.width + ", h=" + viewport.height);
});

/*
 * Exploring the various ways of rendering the images:
 *  - series of img tags
 *  - series of canvas tags
 *  - single canvas, add all photos to it
 */

var ImagesView = Backbone.View.extend({

  initialize: function(options) {
    this.__whoami = 'ImagesView'; //debugging
    this.collection.on("add", this.onAdd, this);
    this.collection.on("remove", this.onRemove, this);
    this.collection.on("update", this.onUpdate, this);
    this.el = $("canvas#c1"); // ?
    var canvas = document.getElementById("c1"); // not really the backbone idiom
    canvas.width  = viewport.width;
    canvas.height = viewport.height;
  },

  render_to_1canvas: function(im, hscale, alpha) {
    var self = this;
    var canvas = document.getElementById("c1"); // not really the backbone idiom
    var ctx = canvas.getContext('2d');
    // alpha (0-1): conversion from opacity (0-100 percent)
    ctx.globalAlpha = alpha;
    // horizontal scaling
    ctx.scale(hscale, 1.0);
    ctx.drawImage(im,0,0, viewport.width, viewport.height);
  },

  render_canvas: function(img, hstretch, opacity) {
    var self = this;

    var alpha = (opacity / 100.0);
    self.render_to_1canvas(img, hstretch, alpha);
  },

  render_image: function(img_element, img_model) {
    var self = this;
    var depth = img_model.get('depth');
    var mag = img_model.get('mag');
    // depth translates to blur, from 0-800 range to 0.0-3.0
    var blur_by_depth = (depth / 200.0);
    // mag(nitude) translates to opacity & horizontal-stretch
    //   ranges:: mag: 0-10, opacity: 10-90 percent, hstretch: 1.0-3.0
    //var opacity_by_mag = (parseFloat(img_model.get('mag')) / 12.0);
    var opacity_by_mag = Math.floor(8.0 * mag + 10.0); // percent: 10-90 range
    var hstretch_by_mag = (mag / 5.0) + 1.0;

    // image size/width & opacity
    var jq_img = $(img_element);
    var vp_width = viewport.width;
    if (vp_width > 50) {
      jq_img.attr({width: vp_width});
    }
    jq_img.css({opacity: opacity_by_mag});

    Pixastic.process(img_element, "blurfast", {amount: blur_by_depth});

    self.render_canvas(img_element, hstretch_by_mag, opacity_by_mag);
  },

  // event is tied to this method:
  onAdd: function(img_model) {
    var self = this;
    var vp_width = viewport.width;
    var url = img_model.get('proxy_url');
    var iid = img_model.get('image_id'); // flickr image id
    var eid = img_model.get('eqid');
    console.log("onAdd(image) iid=" + iid + " for " + eid);

    var img = new Image();
    img.onload = function(ev) {
      var img_elem = ev.srcElement;
      $('#hidden-images').append(img_elem); // storage place for images
      self.render_image(img_elem, img_model);
    }
    img.id = eid;
    img.src = url;
    console.log("  *onAdd-image(queued): " + eid);
  },

  onRemove: function(img_model) {
    var eid = img_model.get('eqid');
    console.log(" *onRemove-image: " + eid);
    $('#' + eid).remove();
  },

  onUpdate: function(img_model)  {
    var eid = img_model.get('eqid');
    console.log(" *onUpdate-image: " + eid);
  },

});

<|endoftext|>


<commit_before>/*global angular, DocumentTouch*/

(function () {
  'use strict';

  var magnify = angular.module('ngMagnify', []);

  magnify.directive('ngMagnify', function () {
    return {
      restrict: 'EA',
      replace: true,
      template: '
' +
                  '

' +
                  '' +
                '
',
      scope: {
        imageSrc: '@',
        imageWidth: '=',
        imageHeight: '=',
        glassWidth: '=',
        glassHeight: '='
      },
      link: function (scope, element) {
        var glass = element.find('div'),
          image = element.find('img'),
          el, nWidth, nHeight, magnifyCSS;

        // if touch devices, do something
        if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
          return;
        }
        element.on('mousemove', function (evt) {
          el = angular.extend(scope.getOffset(element[0]), {
            width: element[0].offsetWidth,
            height: element[0].offsetHeight,
            imageWidth: image[0].offsetWidth,
            imageHeight: image[0].offsetHeight,
            glassWidth: glass[0].offsetWidth,
            glassHeight: glass[0].offsetHeight
          });
          magnifyCSS = scope.magnify(evt);

          if (magnifyCSS) {
            glass.css( magnifyCSS );
          }
        })
        .on('mouseout', function () {
          glass.on('mouseleave', function () {
            glass.css({
              opacity: 0,
              filter: 'alpha(opacity=0)'
            });
          });
        });

        scope.magnify = function (evt) {
          var mx, my, rx, ry, px, py, bgp, img;

          if (!nWidth && !nHeight) {
            img = new Image();
            img.onload = function () {
              nWidth = img.width;
              nHeight = img.height;
            };
            img.src = scope.imageSrc;
          } else {
            // IE8 uses evt.x and evt.y
            mx = (evt.pageX) ? (evt.pageX - el.left) : evt.x;
            my = (evt.pageY) ? (evt.pageY - el.top) : evt.y;

            if (mx < el.width && my < el.height && mx > 0 && my > 0) {
              glass.css({
                opacity: 1,
                'z-index': 1,
                filter: 'alpha(opacity=100)'
              });
            } else {
              glass.css({
                opacity: 0,
                'z-index': -1,
                filter: 'alpha(opacity=0)'
              });
              return;
            }

            rx = Math.round(mx/el.imageWidth*nWidth - el.glassWidth/2)*-1;
            ry = Math.round(my/el.imageHeight*nHeight - el.glassHeight/2)*-1;
            bgp = rx + 'px ' + ry + 'px';

            px = mx - el.glassWidth/2;
            py = my - el.glassHeight/2;

            return { left: px+'px', top: py+'px', backgroundPosition: bgp };
          }
          return;
        };

        scope.getOffset = function (_el) {
          var de = document.documentElement;
          var box = _el.getBoundingClientRect();
          var top = box.top + window.pageYOffset - de.clientTop;
          var left = box.left + window.pageXOffset - de.clientLeft;
          return { top: top, left: left };
        };

        scope.getContainerStyle = function () {
          return {
            width: (scope.imageWidth) ? scope.imageWidth + 'px' : '',
            height: (scope.imageHeight) ? scope.imageHeight + 'px' : ''
          };
        };

        scope.getGlassStyle = function () {
          return {
            background: 'url(' + scope.imageSrc + ') no-repeat',
            width: (scope.glassWidth) ? scope.glassWidth + 'px' : '',
            height: (scope.glassHeight) ? scope.glassHeight + 'px' : ''
          };
        };
      }
    };
  });
})();
<commit_msg>Update ng-magnify.js<commit_after>/*global angular, DocumentTouch*/

(function () {
  'use strict';

  var magnify = angular.module('ngMagnify', []);

  magnify.directive('ngMagnify', function () {
    return {
      restrict: 'EA',
      replace: true,
      template: '
' +
                  '

' +
                  '' +
                '
',
      scope: {
        imageSrc: '@',
        imageWidth: '=',
        imageHeight: '=',
        glassWidth: '=',
        glassHeight: '='
      },
      link: function (scope, element) {
        var glass = element.find('div'),
          image = element.find('img'),
          el, nWidth, nHeight, magnifyCSS;

        // if touch devices, do something
        if (('ontouchstart' in window) || window.DocumentTouch && document instanceof DocumentTouch) {
          return;
        }
        element.on('mousemove', function (evt) {
          el = angular.extend(scope.getOffset(element[0]), {
            width: element[0].offsetWidth,
            height: element[0].offsetHeight,
            imageWidth: image[0].offsetWidth,
            imageHeight: image[0].offsetHeight,
            glassWidth: glass[0].offsetWidth,
            glassHeight: glass[0].offsetHeight
          });
          magnifyCSS = scope.magnify(evt);

          if (magnifyCSS) {
            glass.css( magnifyCSS );
          }
        })
        .on('mouseout', function () {
          glass.on('mouseleave', function () {
            glass.css({
              opacity: 0,
              filter: 'alpha(opacity=0)'
            });
          });
        });

        scope.magnify = function (evt) {
          var mx, my, rx, ry, px, py, bgp, img;

          if (!nWidth && !nHeight) {
            img = new Image();
            img.onload = function () {
              nWidth = img.width;
              nHeight = img.height;
            };
            img.src = scope.imageSrc;
          } else {
            // IE8 uses evt.x and evt.y
            mx = (evt.pageX) ? (evt.pageX - el.left) : evt.x;
            my = (evt.pageY) ? (evt.pageY - el.top) : evt.y;

            if (mx < el.width && my < el.height && mx > 0 && my > 0) {
              glass.css({
                opacity: 1,
                'z-index': 1,
                filter: 'alpha(opacity=100)'
              });
            } else {
              glass.css({
                opacity: 0,
                'z-index': -1,
                filter: 'alpha(opacity=0)'
              });
              return;
            }

            rx = Math.round(mx/el.imageWidth*nWidth - el.glassWidth/2)*-1;
            ry = Math.round(my/el.imageHeight*nHeight - el.glassHeight/2)*-1;
            bgp = rx + 'px ' + ry + 'px';

            px = mx - el.glassWidth/2;
            py = my - el.glassHeight/2;

            return { left: px+'px', top: py+'px', backgroundPosition: bgp };
          }
          return;
        };

        scope.getOffset = function (_el) {
          var de = document.documentElement;
          var box = _el.getBoundingClientRect();
          var top = box.top + window.pageYOffset - de.clientTop;
          var left = box.left + window.pageXOffset - de.clientLeft;
          return { top: top, left: left };
        };

        scope.getContainerStyle = function () {
          return {
            width: (scope.imageWidth) ? scope.imageWidth + 'px' : '',
            height: (scope.imageHeight) ? scope.imageHeight + 'px' : ''
          };
        };

        scope.getGlassStyle = function () {
          return {
            background: 'url("' + scope.imageSrc + '") no-repeat',
            width: (scope.glassWidth) ? scope.glassWidth + 'px' : '',
            height: (scope.glassHeight) ? scope.glassHeight + 'px' : ''
          };
        };
      }
    };
  });
})();
<|endoftext|>