<commit_before>/**
 * @file Main file for a Memcache Connection
 */

var debug = require('debug')('memcache-plus:connection');

var _ = require('lodash'),
    assert = require('chai').assert,
    carrier = require('carrier'),
    misc = require('./misc'),
    net = require('net'),
    Promise = require('bluebird'),
    Queue = require('collections/deque'),
    util = require('util');

/**
 * Connection constructor
 *
 * With the supplied options, connect.
 *
 * @param {object} opts - The options for this Connection instance
 */
function Connection(opts) {
    opts = opts || {};

    _.defaults(opts, {
        host: 'localhost',
        port: '11211',
        reconnect: true
    });

    this.host = opts.host;
    this.port = opts.port;

    this.queue = new Queue();

    if (opts.onConnect) {
        this.onConnect = opts.onConnect;
    }
    this.reconnect = opts.reconnect;
    this.disconnecting = false;
    this.ready = false;
    this.backoff = opts.backoff || 10;

    this.connect();
}

/**
 * Disconnect connection
 */
Connection.prototype.disconnect = function() {
    this.ready = false;
    this.disconnecting = true;
    this.client.end();
};

/**
 * Initialize connection
 *
 * @api private
 */
Connection.prototype.connect = function() {
    var self = this;
    var params = {
        port: this.port
    };

    if (this.host) {
        params.host = this.host;
    }

    debug('connecting to host %s:%s', params.host, params.port);

    // If a client already exists, we just want to reconnect
    if (this.client) {
        this.client.connect(params);

    } else {
        // Initialize a new client, connect
        this.client = net.connect(params);

        // If reconnect is enabled, we want to re-initiate connection if it is ended
        if (this.reconnect) {
            this.client.on('close', function() {
                this.ready = false;
                // Wait before retrying and double each time. Backoff starts at 10ms and will
                // plateau at 1 minute.
                if (this.backoff < 60000) {
                    this.backoff *= 2;
                }
                debug('connection to memcache lost, reconnecting in %sms...', this.backoff);
                setTimeout(function() {
                    // Only want to do this if a disconnect was not triggered intentionally
                    if (!self.disconnecting) {
                        debug('attempting to reconnect to memcache now.', this.backoff);
                        self.client.destroy();
                        self.connect();
                    }
                }, this.backoff);
            });
        }
    }
    Promise.promisifyAll(this.client);

    this.client.on('connect', function() {
        debug('successfully (re)connected!');
        this.ready = true;
        // Reset backoff if we connect successfully
        this.backoff = 10;

        // If an onConnect handler was specified, execute it
        if (this.onConnect) {
            this.onConnect();
            this.flushQueue();
        } else {
            this.flushQueue();
        }
    }.bind(this));

    carrier.carry(this.client, this.read.bind(this));
};

Connection.prototype.read = function(data) {
    debug('got data: %s', data);
    var deferred = this.queue.peek();
    if (deferred === undefined) {
        // We should not get here.
        debug('Got data we did not expect.');
        return;
    }

    if (data === 'ERROR') {
        if (this.queue.toArray().length > 0) {
            // We only want to do this if the last thing was not an error,
            // as if it were, we already would have notified about the error
            // last time so now we want to ignore it
            this.queue.shift();
            deferred.reject(new Error(util.format('Memcache returned an error: %s\r\nFor key %s', data, deferred.key)));
            this.data = null;
        }
    } else if (data.substr(0, 5) === 'VALUE') {
        // Do nothing, this is just metadata. May want to somehow store this
        // and send it back somehow in the future for debugging purposes
        debug('Got some metadata');
    } else if (data.substr(0, 3) === 'END') {
        this.queue.shift();
        deferred.resolve(this.data);
        this.data = null;
    } else if (data === 'STORED') {
        this.queue.shift();
        deferred.resolve(data);
        this.data = null;
    } else if (data === 'DELETED' || data === 'EXISTS' || data === 'TOUCHED') {
        this.queue.shift();
        deferred.resolve(data);
        this.data = null;
    } else if (data.substr(0, 9) === 'NOT_FOUND') {
        this.queue.shift();
        deferred.resolve(data);
        this.data = null;
    } else if (data === 'NOT_STORED') {
        this.queue.shift();
        deferred.reject(new Error('Issue writing to memcache, returned: NOT_STORED'));
        this.data = null;
    } else if (data.substr(0, 12) === 'SERVER_ERROR' || data.substr(0, 12) === 'CLIENT_ERROR') {
        this.queue.shift();
        deferred.reject(data.substr(13));
        this.data = null;
    } else if (data === 'INCRDECR' || data === 'ITEM' || data === 'STAT' || data === 'VERSION') {
        // For future expansion. For now, discarding
        this.queue.shift();
        deferred.resolve(data);
        this.data = null;
    } else if (data !== '') {
        this.data = data;
    } else {
        // If this is a response we do not recognize, what to do with it...
        this.queue.shift();
        deferred.reject(data);
        this.data = null;
    }
};

Connection.prototype.flushQueue = function() {
    if (this.writeBuffer && this.writeBuffer.length > 0) {
        debug('flushing connection write buffer');
        // @todo Watch out for and handle how this behaves with a very long buffer
        while(this.writeBuffer.length > 0) {
            this.client.write(this.writeBuffer.shift());
        }
    }
};

Connection.prototype.write = function(str) {
    debug('sending data: %s', str);
    this.writeBuffer = this.writeBuffer || new Queue();
    // If for some reason this connection is not yet ready and a request is tried,
    // we don't want to fire it off so we write it to a buffer and then will fire
    // them off when we finally do connect. And even if we are connected we don't
    // want to fire off requests unless the write buffer is emptied. So if say we
    // buffer 100 requests, then connect and chug through 10, there are 90 left to
    // be flushed before we send it new requests so we'll just keep pushing on the
    // end until it's flushed
    if (this.ready && this.writeBuffer.length < 1) {
        this.client.write(str);
        this.client.write('\r\n');
    } else if (this.writeBuffer.length < 1000) {
        this.writeBuffer.push(str);
        this.writeBuffer.push('\r\n');
        // Check if we should flush this queue. Useful in case it gets stuck for
        // some reason
        if (this.ready) {
            this.flushQueue();
        }
    } else {
        this.queue.shift().reject('Error, Connection to memcache lost and buffer over 1000 items');
    }
};

Connection.prototype.autodiscovery = function() {
    debug('starting autodiscovery');
    var deferred = misc.defer('autodiscovery');
    this.queue.push(deferred);

    this.write('config get cluster');
    return deferred.promise
        .then(function(data) {
            debug('got autodiscovery response from elasticache');
            // Elasticache returns hosts as a string like the following:
            // victor.di6cba.0001.use1.cache.amazonaws.com|10.10.8.18|11211 victor.di6cba.0002.use1.cache.amazonaws.com|10.10.8.133|11211
            // We want to break it into the correct pieces
            var hosts = data.toString().split(' ');
            return hosts.map(function(host) {
                host = host.split('|');
                return util.format('%s:%s', host[0], host[2]);
            });
        });
};

/**
 * set() - Set a value on this connection
 */
Connection.prototype.set = function(key, val, ttl) {
    debug('set %s:%s', key, val);
    assert(typeof key === 'string', 'Cannot set in memcache with a not string key');
    // This is 'temporary'-ish.
    //
    // Basically, it seems better to throw an error like this, pushing the handling
    // of this error to user space than it is to take a number, silently convert it
    // to a string, store it in memcache, then return a string later where an application
    // expected a number to be returned (as that's what it set).
    //
    // On the roadmap is to implement 'type safety' which means someone will be able to
    // set a value in memcache that is a string, number, object, etc. and actually get
    // that type of thing back from the get(), but until then we'll kick out a value
    // that is not a string because this seems much safer.
    assert(typeof val === 'string', 'Cannot set in memcache with a not string value');
    assert(key.length < 250, 'Key must be less than 250 characters long');
    // @todo ensure val is a string or Buffer before saving, or stringify object

    ttl = ttl || 0;
    var opts = {};

    if (_.isObject(ttl)) {
        opts = ttl;
        ttl = opts.ttl || 0;
    }

    var bVal = new Buffer(val);

    var compression = Promise.resolve(bVal);

    if (opts.compressed) {
        // Compress
        debug('compression enabled, compressing');
        bVal = new Buffer(val);
        compression = misc.compress(bVal);
    }

    return compression
        .bind(this)
        .then(function(v) {
            if (opts.compressed) {
                // Note, we use base64 encoding because this allows us to compress it
                // but also safely store/retrieve it in memcache. Not quite as efficient
                // as if we just used the zlib default, but it also includes a bunch of
                // funky characters that didn't seem to be safe to set/get from memcache
                // without messing with the data.
                v = new Buffer(v.toString('base64'));
            }
            if (v.length > 1048576) {
                throw new Error('Value too large to set in memcache');
            }

            // What we're doing here is a bit tricky as we need to invert control.
            // We are going to basically return a Promise that itself is made up of a
            // chain of Promises, most resolved here (the initial communication with
            // memcache), but the last is not resolved until some time in the future.
            // This Promise is put into a queue which will be processed whenever the
            // socket responds (usually immediately). This because we don't know
            // exactly when it's going to respond since it's an event emitter. So we
            // are doing some funky promise trickery to convert event emmitter into
            // Promise/or Callback. Since all actions in this library share the same
            // queue, order should be maintained and this trick should work!

            var deferred = misc.defer(key);
            this.queue.push(deferred);

            // First send the metadata for this request
            this.write(util.format('set %s 0 %d %d', key, ttl, v.length));

            // Then the actual value
            this.write(v);
            return deferred.promise
                .then(function(data) {
                    // data will be a buffer
                    if (data !== 'STORED') {
                        throw new Error(util.format('Something went wrong with the set. Expected STORED, got :%s:', data.toString()));
                    } else {
                        return Promise.resolve();
                    }
                });
        });
};

/**
 * get() - Get a value on this connection
 *
 * @param {String} key - The key for the value to retrieve
 * @param {Object} [opts] - Any additional options for this get
 * @returns {Promise}
 */
Connection.prototype.get = function(key, opts) {
    debug('get %s', key);
    opts = opts || {};
    // Do the get
    var deferred = misc.defer(key);
    this.queue.push(deferred);

    this.write(util.format('get %s', key));

    return deferred.promise
    // .timeout() // @todo add this as a setting
        .bind(this)
        .then(function(data) {
            if (data) {
                if (opts.compressed) {
                    return misc.decompress(new Buffer(data, 'base64'))
                        .then(function(d) {
                            return d.toString();
                        })
                        .catch(function(err) {
                            if (err.toString() === 'OperationalError: incorrect header check') {
                                // Basically we get this OperationalError when we try to decompress a value
                                // which was not previously compressed. We return null instead of bubbling
                                // this error up the chain because we want to represent it as a cache miss
                                // rather than an actual error. By looking like a cache miss like this, it
                                // makes it so someone can turn on compression and it'll work fine, looking
                                // like a cache miss. So in the usual workflow the client tries to set it
                                // again which means the second time around the compressed version is set
                                // as the client wanted/expected.
                                return null;
                            } else {
                                throw err;
                            }
                        });
                } else {
                    return data.toString();
                }
            } else {
                return null;
            }
        });
};

/**
 * delete() - Delete value for this key on this connection
 *
 * @param {String} key - The key to delete
 * @returns {Promise}
 */
Connection.prototype.delete = function(key) {
    debug('delete %s', key);
    // Do the delete
    var deferred = misc.defer(key);
    this.queue.push(deferred);

    this.write(util.format('delete %s', key));

    return deferred.promise
        .then(function(v) {
            if (v === 'DELETED') {
                return true;
            } else {
                return false;
            }
        });
        // .timeout() // @todo add this as a setting
};

module.exports = Connection;
<commit_msg>Fix a minor bug where memcache was using the Nagle algorithm on its socket causing it to Buffer requests unnecessarily resulting in an unwanted delay for every get<commit_after>/**
 * @file Main file for a Memcache Connection
 */

var debug = require('debug')('memcache-plus:connection');

var _ = require('lodash'),
    assert = require('chai').assert,
    carrier = require('carrier'),
    misc = require('./misc'),
    net = require('net'),
    Promise = require('bluebird'),
    Queue = require('collections/deque'),
    util = require('util');

/**
 * Connection constructor
 *
 * With the supplied options, connect.
 *
 * @param {object} opts - The options for this Connection instance
 */
function Connection(opts) {
    opts = opts || {};

    _.defaults(opts, {
        host: 'localhost',
        port: '11211',
        reconnect: true
    });

    this.host = opts.host;
    this.port = opts.port;

    this.queue = new Queue();

    if (opts.onConnect) {
        this.onConnect = opts.onConnect;
    }
    this.reconnect = opts.reconnect;
    this.disconnecting = false;
    this.ready = false;
    this.backoff = opts.backoff || 10;

    this.connect();
}

/**
 * Disconnect connection
 */
Connection.prototype.disconnect = function() {
    this.ready = false;
    this.disconnecting = true;
    this.client.end();
};

/**
 * Initialize connection
 *
 * @api private
 */
Connection.prototype.connect = function() {
    var self = this;
    var params = {
        port: this.port
    };

    if (this.host) {
        params.host = this.host;
    }

    debug('connecting to host %s:%s', params.host, params.port);

    // If a client already exists, we just want to reconnect
    if (this.client) {
        this.client.connect(params);

    } else {
        // Initialize a new client, connect
        this.client = net.connect(params);
        this.client.setNoDelay(true);

        // If reconnect is enabled, we want to re-initiate connection if it is ended
        if (this.reconnect) {
            this.client.on('close', function() {
                this.ready = false;
                // Wait before retrying and double each time. Backoff starts at 10ms and will
                // plateau at 1 minute.
                if (this.backoff < 60000) {
                    this.backoff *= 2;
                }
                debug('connection to memcache lost, reconnecting in %sms...', this.backoff);
                setTimeout(function() {
                    // Only want to do this if a disconnect was not triggered intentionally
                    if (!self.disconnecting) {
                        debug('attempting to reconnect to memcache now.', this.backoff);
                        self.client.destroy();
                        self.connect();
                    }
                }, this.backoff);
            });
        }
    }

    this.client.on('connect', function() {
        debug('successfully (re)connected!');
        this.ready = true;
        // Reset backoff if we connect successfully
        this.backoff = 10;

        // If an onConnect handler was specified, execute it
        if (this.onConnect) {
            this.onConnect();
        }
        this.flushQueue();
    }.bind(this));

    carrier.carry(this.client, this.read.bind(this));
};

Connection.prototype.read = function(data) {
    debug('got data: %s', data);
    var deferred = this.queue.peek();
    if (deferred === undefined) {
        // We should not get here.
        debug('Got data we did not expect.');
        return;
    }

    if (data === 'ERROR' && this.queue.toArray().length > 0) {
        // We only want to do this if the last thing was not an error,
        // as if it were, we already would have notified about the error
        // last time so now we want to ignore it
        this.queue.shift();
        deferred.reject(new Error(util.format('Memcache returned an error: %s\r\nFor key %s', data, deferred.key)));
        this.data = null;
    } else if (data.substr(0, 5) === 'VALUE') {
        // Do nothing, this is just metadata. May want to somehow store this
        // and send it back somehow in the future for debugging purposes
        debug('Got some metadata');
    } else if (data.substr(0, 3) === 'END') {
        this.queue.shift();
        deferred.resolve(this.data);
        this.data = null;
    } else if (data === 'STORED' || data === 'DELETED' ||
               data === 'EXISTS' || data === 'TOUCHED'||
               data.substr(0, 9) === 'NOT_FOUND') {
        this.queue.shift();
        deferred.resolve(data);
        this.data = null;
    } else if (data === 'NOT_STORED') {
        this.queue.shift();
        deferred.reject(new Error('Issue writing to memcache, returned: NOT_STORED'));
        this.data = null;
    } else if (data.substr(0, 12) === 'SERVER_ERROR' || data.substr(0, 12) === 'CLIENT_ERROR') {
        this.queue.shift();
        deferred.reject(data.substr(13));
        this.data = null;
    } else if (data === 'INCRDECR' || data === 'ITEM' || data === 'STAT' || data === 'VERSION') {
        // For future expansion. For now, discarding
        this.queue.shift();
        deferred.resolve(data);
        this.data = null;
    } else if (data !== '') {
        this.data = data;
    } else {
        // If this is a response we do not recognize, what to do with it...
        this.queue.shift();
        deferred.reject(data);
        this.data = null;
    }
};

Connection.prototype.flushQueue = function() {
    if (this.writeBuffer && this.writeBuffer.length > 0) {
        debug('flushing connection write buffer');
        // @todo Watch out for and handle how this behaves with a very long buffer
        while(this.writeBuffer.length > 0) {
            this.client.write(this.writeBuffer.shift());
        }
    }
};

Connection.prototype.write = function(str) {
    debug('sending data: %s', str);
    this.writeBuffer = this.writeBuffer || new Queue();
    // If for some reason this connection is not yet ready and a request is tried,
    // we don't want to fire it off so we write it to a buffer and then will fire
    // them off when we finally do connect. And even if we are connected we don't
    // want to fire off requests unless the write buffer is emptied. So if say we
    // buffer 100 requests, then connect and chug through 10, there are 90 left to
    // be flushed before we send it new requests so we'll just keep pushing on the
    // end until it's flushed
    if (this.ready && this.writeBuffer.length < 1) {
        this.client.write(str);
        this.client.write('\r\n');
    } else if (this.writeBuffer.length < 1000) {
        this.writeBuffer.push(str);
        this.writeBuffer.push('\r\n');
        // Check if we should flush this queue. Useful in case it gets stuck for
        // some reason
        if (this.ready) {
            this.flushQueue();
        }
    } else {
        this.queue.shift().reject('Error, Connection to memcache lost and buffer over 1000 items');
    }
};

Connection.prototype.autodiscovery = function() {
    debug('starting autodiscovery');
    var deferred = misc.defer('autodiscovery');
    this.queue.push(deferred);

    this.write('config get cluster');
    return deferred.promise
        .then(function(data) {
            debug('got autodiscovery response from elasticache');
            // Elasticache returns hosts as a string like the following:
            // victor.di6cba.0001.use1.cache.amazonaws.com|10.10.8.18|11211 victor.di6cba.0002.use1.cache.amazonaws.com|10.10.8.133|11211
            // We want to break it into the correct pieces
            var hosts = data.toString().split(' ');
            return hosts.map(function(host) {
                host = host.split('|');
                return util.format('%s:%s', host[0], host[2]);
            });
        });
};

/**
 * set() - Set a value on this connection
 */
Connection.prototype.set = function(key, val, ttl) {
    debug('set %s:%s', key, val);
    assert(typeof key === 'string', 'Cannot set in memcache with a not string key');
    // This is 'temporary'-ish.
    //
    // Basically, it seems better to throw an error like this, pushing the handling
    // of this error to user space than it is to take a number, silently convert it
    // to a string, store it in memcache, then return a string later where an application
    // expected a number to be returned (as that's what it set).
    //
    // On the roadmap is to implement 'type safety' which means someone will be able to
    // set a value in memcache that is a string, number, object, etc. and actually get
    // that type of thing back from the get(), but until then we'll kick out a value
    // that is not a string because this seems much safer.
    assert(typeof val === 'string', 'Cannot set in memcache with a not string value');
    assert(key.length < 250, 'Key must be less than 250 characters long');
    // @todo ensure val is a string or Buffer before saving, or stringify object

    ttl = ttl || 0;
    var opts = {};

    if (_.isObject(ttl)) {
        opts = ttl;
        ttl = opts.ttl || 0;
    }

    var bVal = new Buffer(val);

    var compression = Promise.resolve(bVal);

    if (opts.compressed) {
        // Compress
        debug('compression enabled, compressing');
        bVal = new Buffer(val);
        compression = misc.compress(bVal);
    }

    return compression
        .bind(this)
        .then(function(v) {
            if (opts.compressed) {
                // Note, we use base64 encoding because this allows us to compress it
                // but also safely store/retrieve it in memcache. Not quite as efficient
                // as if we just used the zlib default, but it also includes a bunch of
                // funky characters that didn't seem to be safe to set/get from memcache
                // without messing with the data.
                v = new Buffer(v.toString('base64'));
            }
            if (v.length > 1048576) {
                throw new Error('Value too large to set in memcache');
            }

            // What we're doing here is a bit tricky as we need to invert control.
            // We are going to basically return a Promise that itself is made up of a
            // chain of Promises, most resolved here (the initial communication with
            // memcache), but the last is not resolved until some time in the future.
            // This Promise is put into a queue which will be processed whenever the
            // socket responds (usually immediately). This because we don't know
            // exactly when it's going to respond since it's an event emitter. So we
            // are doing some funky promise trickery to convert event emmitter into
            // Promise/or Callback. Since all actions in this library share the same
            // queue, order should be maintained and this trick should work!

            var deferred = misc.defer(key);
            this.queue.push(deferred);

            // First send the metadata for this request
            this.write(util.format('set %s 0 %d %d', key, ttl, v.length));

            // Then the actual value
            this.write(v);
            return deferred.promise
                .then(function(data) {
                    // data will be a buffer
                    if (data !== 'STORED') {
                        throw new Error(util.format('Something went wrong with the set. Expected STORED, got :%s:', data.toString()));
                    } else {
                        return Promise.resolve();
                    }
                });
        });
};

/**
 * get() - Get a value on this connection
 *
 * @param {String} key - The key for the value to retrieve
 * @param {Object} [opts] - Any additional options for this get
 * @returns {Promise}
 */
Connection.prototype.get = function(key, opts) {
    debug('get %s', key);
    opts = opts || {};
    // Do the get
    var deferred = misc.defer(key);
    this.queue.push(deferred);

    this.write('get ' + key);

    return deferred.promise
    // .timeout() // @todo add this as a setting
        .then(function(data) {
            if (data) {
                if (opts.compressed) {
                    return misc.decompress(new Buffer(data, 'base64'))
                        .then(function(d) {
                            return d.toString();
                        })
                        .catch(function(err) {
                            if (err.toString() === 'OperationalError: incorrect header check') {
                                // Basically we get this OperationalError when we try to decompress a value
                                // which was not previously compressed. We return null instead of bubbling
                                // this error up the chain because we want to represent it as a cache miss
                                // rather than an actual error. By looking like a cache miss like this, it
                                // makes it so someone can turn on compression and it'll work fine, looking
                                // like a cache miss. So in the usual workflow the client tries to set it
                                // again which means the second time around the compressed version is set
                                // as the client wanted/expected.
                                return null;
                            } else {
                                throw err;
                            }
                        });
                } else {
                    return data.toString();
                }
            } else {
                return null;
            }
        });
};

/**
 * delete() - Delete value for this key on this connection
 *
 * @param {String} key - The key to delete
 * @returns {Promise}
 */
Connection.prototype.delete = function(key) {
    debug('delete %s', key);
    // Do the delete
    var deferred = misc.defer(key);
    this.queue.push(deferred);

    this.write(util.format('delete %s', key));

    return deferred.promise
        .then(function(v) {
            if (v === 'DELETED') {
                return true;
            } else {
                return false;
            }
        });
        // .timeout() // @todo add this as a setting
};

module.exports = Connection;
<|endoftext|>


<commit_before>'use strict';

var BarChart = require('./charts/Bar.js');
var LineChart = require('./charts/Line.js');

var data = [{
    label: 'Jan',
    data: 35
  }, {
    label: 'Feb',
    data: 20
  }, {
    label: 'Mar',
    data: 28
  }, {
    label: 'Apr',
    data: 8
  }, {
    label: 'May',
    data: 17
  }, {
    label: 'Jun',
    data: 38
  }, {
    label: 'Jul',
    data: 20
  }
];

// Bar Chart
var barChartConfig = {
  elementID: '#bar-chart',
  chartWidth: document.querySelector('.bar-chart-container').offsetWidth,
  chartHeight: 400,
  classes: {
    group: 'bar-group',
    bar: 'bar',
    label: 'labels'
  },
  axis: {
    y: {
      ticks: 8
    }
  },
  margin: {
    top: 20,
    bot: 30,
    left: 30,
    right: 0
  }
};

var barChart = new BarChart(data, barChartConfig);

(function updateChart(d){
  var updatedData = d || data.concat([
    { label: 'Aug', data: 70 },
    { label: 'Sep', data: 14 }
  ]);
  setTimeout(function(){
    barChart.updateData(updatedData);
    if (updatedData.length < 4) {
      updateChart(data);
    } else {
      updateChart(updatedData.slice(~~(Math.random() * updatedData.length - 2)));
    }
  }, ~~(Math.random() * 5000) + 5000);
})();

// Line Chart
var lineChartConfig = {
  elementID: '#line-chart',
  chartWidth: document.querySelector('.line-chart-container').offsetWidth,
  chartHeight: 400,
  axis: {
    y: {
      ticks: 8
    }
  },
  classes: {
    group: 'group',
    point: 'point',
    line: 'line',
    label: 'labels'
  },
  margin: {
      top: 20,
      bot: 30,
      left: 30,
      right: 0
  }
};

var lineChart = new LineChart(data, lineChartConfig);
<commit_msg>Logging chart information<commit_after>'use strict';

var BarChart = require('./charts/Bar.js');
var LineChart = require('./charts/Line.js');

var data = [{
    label: 'Jan',
    data: 35
  }, {
    label: 'Feb',
    data: 20
  }, {
    label: 'Mar',
    data: 28
  }, {
    label: 'Apr',
    data: 8
  }, {
    label: 'May',
    data: 17
  }, {
    label: 'Jun',
    data: 38
  }, {
    label: 'Jul',
    data: 20
  }
];

// Bar Chart
var barChartConfig = {
  elementID: '#bar-chart',
  chartWidth: document.querySelector('.bar-chart-container').offsetWidth,
  chartHeight: 400,
  classes: {
    group: 'bar-group',
    bar: 'bar',
    label: 'labels'
  },
  axis: {
    y: {
      ticks: 8
    }
  },
  margin: {
    top: 20,
    bot: 30,
    left: 30,
    right: 0
  }
};

var barChart = new BarChart(data, barChartConfig);
console.log(barChart);

(function updateChart(d){
  var updatedData = d || data.concat([
    { label: 'Aug', data: 70 },
    { label: 'Sep', data: 14 }
  ]);
  setTimeout(function(){
    barChart.updateData(updatedData);
    if (updatedData.length < 4) {
      updateChart(data);
    } else {
      updateChart(updatedData.slice(~~(Math.random() * updatedData.length - 2)));
    }
  }, ~~(Math.random() * 5000) + 5000);
})();

// Line Chart
var lineChartConfig = {
  elementID: '#line-chart',
  chartWidth: document.querySelector('.line-chart-container').offsetWidth,
  chartHeight: 400,
  axis: {
    y: {
      ticks: 8
    }
  },
  classes: {
    group: 'group',
    point: 'point',
    line: 'line',
    label: 'labels'
  },
  margin: {
      top: 20,
      bot: 30,
      left: 30,
      right: 0
  }
};

var lineChart = new LineChart(data, lineChartConfig);
console.log(lineChart);

<|endoftext|>


<commit_before>console.log('\'Allo \'Allo!');

( function( $ ) {

	function enableSkrollr(){
		console.log('we are on desktop');

		// Enable Skroll only for non-touch devices
		if(!Modernizr.touch){
			var s = skrollr.init({
	            forceHeight: false
	        });
        }

	}

	function disableSkrollr(){
		console.log('we are on mobile');

		// Destroy Skrollr
		var s = skrollr.init();
		s.destroy();
	}

	enquire.register("screen and (min-width: 768px)", {
	    match : function() {
	        enableSkrollr();
	    },
	    unmatch : function() {
	        disableSkrollr();
	    }
	});

} )( jQuery );
<commit_msg>Corrected jQuery syntax error in main.js<commit_after>console.log('\'Allo \'Allo!');

$(function() {

	function enableSkrollr(){
		console.log('we are on desktop');

		// Enable Skroll only for non-touch devices
		if(!Modernizr.touch){
			var s = skrollr.init({
	            forceHeight: false
	        });
        }

	}

	function disableSkrollr(){
		console.log('we are on mobile');

		// Destroy Skrollr
		var s = skrollr.init();
		s.destroy();
	}

	enquire.register("screen and (min-width: 768px)", {
	    match : function() {
	        enableSkrollr();
	    },
	    unmatch : function() {
	        disableSkrollr();
	    }
	});

});
<|endoftext|>


<commit_before>import * as ActionTypes from '../constants/ActionTypes';
import remove from 'lodash.remove';

const initialState = [];

const removeImageById = (state, id) => remove(state, { id })[0];

const actionsMap = {
  [ActionTypes.ADD_IMAGE](state, { image }) {
    return [...state, image];
  },
  [ActionTypes.SELECT_IMAGE](state, { id }) {
    const target = removeImageById(state, id)
    return [
      ...state,
      {
        ...target,
        isSelected: true
      }
    ];
  },
  [ActionTypes.DESELECT_IMAGE](state, { id }) {
    const target = removeImageById(state, id)
    return [
      ...state,
      {
        ...target,
        isSelected: false
      }
    ];
  }
};

export default function images(state = initialState, action) {
  const reduceFn = actionsMap[action.type];
  if (!reduceFn) return state;
  return reduceFn(state, action);
}
<commit_msg>Simplify tests<commit_after>import * as ActionTypes from '../constants/ActionTypes';
import remove from 'lodash.remove';

const initialState = [];

const updateImageInState = (state, id, changes) => {
  const target = remove(state, { id })[0];
  return [...state, { ...target, ...changes }];
};

const actionsMap = {
  [ActionTypes.ADD_IMAGE](state, { image }) {
    return [...state, image];
  },
  [ActionTypes.SELECT_IMAGE](state, { id }) {
    return updateImageInState(state, id, { isSelected: true })
  },
  [ActionTypes.DESELECT_IMAGE](state, { id }) {
    return updateImageInState(state, id, { isSelected: false })
  }
};

export default function images(state = initialState, action) {
  const reduceFn = actionsMap[action.type];
  if (!reduceFn) return state;
  return reduceFn(state, action);
}
<|endoftext|>


<commit_before>import Ember from 'ember';
import Space from 'hyperchannel/models/space';
import Channel from 'hyperchannel/models/channel';
import UserChannel from 'hyperchannel/models/user_channel';
import Message from 'hyperchannel/models/message';
import config from 'hyperchannel/config/environment';
import moment from 'moment';
import { storageFor as localStorageFor } from 'ember-local-storage';

const {
  Service,
  inject: {
    service
  },
  RSVP,
  isEmpty,
  isPresent,
  Logger,
  get
} = Ember;

export default Service.extend({
  userSettings: localStorageFor('user-settings'),
  ajax: service(),
  logger: service(),
  storage: service('remotestorage'),

  spaces: null,

  instantiateSpacesAndChannels() {
    this.set('spaces', []);
    let rs = this.get('storage.rs');

    return new RSVP.Promise((resolve, reject) => {
      rs.kosmos.spaces.getAll().then(spaceData => {
        if (isEmpty(Object.keys(spaceData))) {
          Logger.debug('No space data found in RS. Adding default space...');
          this.get('storage').addDefaultSpace().then((data) => {
            this.connectToIRCServer(data.space);
            this.get('spaces').pushObject(data.space);
            this.instantiateChannels(data.space, data.channels);
            resolve();
          });
        } else {
          Object.keys(spaceData).forEach((id) => {
            let space = Space.create({
              name: spaceData[id].name,
              protocol: spaceData[id].protocol,
              server: spaceData[id].server
            });
            this.connectToIRCServer(space);
            this.get('spaces').pushObject(space);
            this.instantiateChannels(space, spaceData[id].channels);
          });
          resolve();
        }
      }, e => {
        this.log('error', 'couldn\'d load spaces from RS', e);
        reject();
      });
    });
  },

  connectToIRCServer(space) {
    this.sockethub.ActivityStreams.Object.create({
      '@id': space.get('sockethubPersonId'),
      '@type': "person",
      displayName: space.get('server.nickname')
    });

    var credentials = {
      actor: space.get('sockethubPersonId'),
      context: 'irc',
      object: {
        '@type': 'credentials',
        nick: space.get('server.nickname'),
        server: space.get('server.hostname'),
        port: space.get('server.port'),
        secure: space.get('server.secure')
      }
    };

    this.log('connection', 'connecting to irc', credentials);
    this.sockethub.socket.emit('credentials', credentials);
  },

  transferMessage(space, target, content) {
    let job = {
      context: 'irc',
      '@type': 'send',
      actor: space.get('sockethubPersonId'),
      target: target,
      object: {
        '@type': 'message',
        content: content
      }
    };

    this.log('send', 'sending message job', job);
    this.sockethub.socket.emit('message', job);
  },

  transferMeMessage(space, target, content) {
    let job = {
      context: 'irc',
      '@type': 'send',
      actor: space.get('sockethubPersonId'),
      target: target,
      object: {
        '@type': 'me',
        content: content
      }
    };

    this.log('send', 'sending message job', job);
    this.sockethub.socket.emit('message', job);
  },

  setupListeners() {
    this.sockethub.socket.on('completed', (message) => {
      this.log('sh_completed', message);

      switch(message['@type']) {
        case 'join':
          if (message['@type'] === 'join') {
            var space = this.get('spaces').findBy('sockethubPersonId', message.actor);
            if (!isEmpty(space)) {
              var channel = space.get('channels').findBy('sockethubChannelId', message.target);
              if (!isEmpty(channel)) {
                channel.set('connected', true);
                this.observeChannel(space.get('sockethubPersonId'), channel.get('sockethubChannelId'));
              }
            }
          }
          break;
        case 'observe':
          if (message.object['@type'] === 'attendance') {
            this.updateChannelUserList(message);
          }
          break;
      }
    });

    this.sockethub.socket.on('message', (message) => {
      this.log('message', 'SH message', message);

      switch(message['@type']) {
        case 'observe':
          if (message.object['@type'] === 'attendance') {
            this.updateChannelUserList(message);
          }
          break;
        case 'join':
          this.addUserToChannelUserList(message);
          break;
        case 'leave':
          this.removeUserFromChannelUserList(message);
          break;
        case 'send':
          switch(message.object['@type']) {
            case 'message':
            case 'me':
              this.addMessageToChannel(message);
              break;
          }
          break;
        case 'update':
          if (message.object['@type'] === 'topic') {
            this.updateChannelTopic(message);
          }
          break;
      }

      // user list for a channel
    });

    this.sockethub.socket.on('failure', function(message) {
      Logger.error('SH failure', message);
    });
  },

  updateChannelUserList(message) {
    const channel = this.getChannelByMessage(message);
    if (channel) {
      channel.set('userList', message.object.members.sort());
    }
  },

  addUserToChannelUserList(message) {
    const channel = this.getChannelByMessage(message);
    if (channel) {
      channel.addUser(message.actor.displaName);
    }
  },

  removeUserFromChannelUserList(message) {
    const channel = this.getChannelByMessage(message);
    if (channel) {
      channel.removeUser(message.actor.displayName);
    }
  },

  getChannelByMessage(message) {
    var addressWithHostname, hostname;
    if (typeof message.actor === 'object') {
      addressWithHostname = message.actor['@id'];
    } else if (typeof message.actor === 'string') {
      addressWithHostname = message.actor;
    }

    hostname = addressWithHostname.match(/irc:\/\/(?:.+@)?(.+?)(?:\/|$)/)[1];

    var space = this.get('spaces').findBy('server.hostname', hostname);

    if (!isEmpty(space)) {
      var channel = space.get('channels').findBy('sockethubChannelId', message.target['@id']);
      if (!isEmpty(channel)) {
        return channel;
      }
    }
  },

  updateChannelTopic(message) {
    let hostname;
    if (typeof message.target === 'object') {
      hostname = message.target['@id'].match(/irc:\/\/(.+)\//)[1];
    } else if (typeof message.actor === 'string') {
      hostname = message.actor.match(/irc:\/\/.+\@(.+)/)[1];
    }

    let space = this.get('spaces').findBy('server.hostname', hostname);

    if (!isEmpty(space)) {
      let channel = space.get('channels').findBy('sockethubChannelId', message.target['@id']);

      if (isEmpty(channel)) {
        channel = this.createChannel(space, message.target['@id']);
      }

      let currentTopic = channel.get('topic');
      let newTopic = message.object.topic;

      channel.set('topic', newTopic);

      if (isPresent(currentTopic) && (newTopic !== currentTopic) && !channel.get('visible')) {
        Notification.requestPermission(function(){
          new Notification(channel.name, {
            body: `New Topic: ${newTopic}`
          });
        });
      }

      // let notification = Message.create({
      //   type: 'notification-topic-change',
      //   date: new Date(message.published),
      //   nickname: message.actor.displayName,
      //   content: message.object.topic
      // });

      // channel.get('messages').pushObject(notification);

    }
  },

  addMessageToChannel: function(message) {
    var space = this.get('spaces').findBy('server.hostname',
                message.actor['@id'].match(/irc:\/\/.+\@(.+)/)[1]);
    var nickname = space.get('userNickname');

    var targetChannelName;
    if (nickname === message.target.displayName) {
      targetChannelName = message.actor.displayName;
    } else {
      targetChannelName = message.target.displayName;
    }

    var channel = space.get('channels').findBy('name', targetChannelName);
    if (!channel) {
      channel = this.createChannel(space, targetChannelName);
    }

    let messageType;
    if (message.object['@type'] === 'me') {
      messageType = 'message-chat-me';
    } else {
      messageType = 'message-chat';
    }

    var channelMessage = Message.create({
      type: messageType,
      date: new Date(message.published),
      nickname: message.actor.displayName,
      content: message.object.content
    });

    // TODO should check for message and update sent status if exists
    if (message.actor.displayName !== nickname) {
      channel.addMessage(channelMessage);
    }
  },

  observeChannel: function(person, channelId) {
    var observeMsg = {
      context: 'irc',
      '@type': 'observe',
      actor: person,
      target: channelId,
      object: {
        '@type': 'attendance'
      }
    };

    Logger.debug('asking for attendance list', observeMsg);
    this.sockethub.socket.emit('message', observeMsg);
  },

  instantiateChannels: function(space, channels) {
    channels.forEach((channelName) => {
      this.createChannel(space, channelName);
    });
  },

  createChannel: function(space, channelName) {
    var channel = Channel.create({
      space: space,
      name: channelName,
      sockethubChannelId: `irc://${space.get('server.hostname')}/${channelName}`
    });

    this.joinChannel(space, channel, "room");
    space.get('channels').pushObject(channel);

    if (channel.get('isLogged')) {
      this.loadLastMessages(space, channel, moment(), 2).catch(() => {});
    }

    return channel;
  },

  loadLastMessages(space, channel, date, maximumDays = 14) {
    let day = date ? moment(date).utc() : moment().utc();

    let searchUntilDate;
    if (channel.get('searchedPreviousLogsUntilDate')) {
      searchUntilDate = moment(channel.get('searchedPreviousLogsUntilDate')).utc().subtract(maximumDays, 'days');
    } else {
      searchUntilDate = moment().utc().subtract(maximumDays, 'days');
    }

    if (day.isBefore(searchUntilDate)) {
      channel.set('searchedPreviousLogsUntilDate', day.format('YYYY-MM-DD'));
      return;
    }

    return this.loadArchiveMessages(space, channel, day).catch(() => {
      day.subtract(1, 'day');
      return this.loadLastMessages(space, channel, day);
    });
  },

  loadArchiveMessages(space, channel, date) {
    let logsUrl = `${config.publicLogsUrl}/${space.get('name').toLowerCase()}/channels/${channel.get('slug')}/`;
        logsUrl += date.format('YYYY/MM/DD');

    return this.get('ajax').request(logsUrl, {
      type: 'GET',
      dataType: 'json'
    }).then(archive => {
      get(archive, 'today.messages').forEach((message) => {
        this.log('message', message);

        let channelMessage = Message.create({
          type: 'message-chat',
          date: new Date(message.timestamp),
          nickname: message.from,
          content: message.text
        });

        channel.addMessage(channelMessage);
      });
      let previous = get(archive, 'today.previous');
      channel.set('searchedPreviousLogsUntilDate', previous.replace(/\//g, '-'));
    }).catch(error => {
      this.log('error', error);
      throw(error);
    });
  },

  createUserChannel: function(space, userName) {
    var channel = UserChannel.create({
      name: userName,
      sockethubChannelId: `irc://${space.get('server.hostname')}/${userName}`
    });

    this.joinChannel(space, channel, "person");
    space.get('channels').pushObject(channel);

    return channel;
  },

  removeChannel: function(space, channelName) {
    var channel = space.get('channels').findBy('name', channelName);
    this.leaveChannel(space, channel);

    space.get('channels').removeObject(channel);

    this.get('storage').saveSpace(space);

    return channel;
  },

  joinChannel: function(space, channel, type) {
    this.sockethub.ActivityStreams.Object.create({
      '@type': type,
      '@id': channel.get('sockethubChannelId'),
      displayName: channel.get('name')
    });

    var joinMsg = {
      context: 'irc',
      '@type': 'join',
      actor: space.get('sockethubPersonId'),
      target: channel.get('sockethubChannelId'),
      object: {}
    };

    this.log('join', 'joining channel', joinMsg);
    this.sockethub.socket.emit('message', joinMsg);
  },

  leaveChannel: function(space, channel) {
    this.sockethub.ActivityStreams.Object.create({
      '@type': "room",
      '@id': channel.get('sockethubChannelId'),
      displayName: channel.get('name')
    });

    var joinMsg = {
      context: 'irc',
      '@type': 'leave',
      actor: space.get('sockethubPersonId'),
      target: channel.get('sockethubChannelId'),
      object: {}
    };

    this.log('leave', 'leaving channel', joinMsg);
    this.sockethub.socket.emit('message', joinMsg);
  },

  changeTopic: function(space, channel, topic) {
    var topicMsg = {
      context: 'irc',
      '@type': 'update',
      actor: space.get('sockethubPersonId'),
      target: channel.get('sockethubChannelId'),
      object: {
        '@type': 'topic',
        topic: topic
      }
    };

    this.sockethub.socket.emit('message', topicMsg);
  },

  // Utility function
  log() {
    this.get('logger').log(...arguments);
  }
});
<commit_msg>Fix loading of previous messages archive<commit_after>import Ember from 'ember';
import Space from 'hyperchannel/models/space';
import Channel from 'hyperchannel/models/channel';
import UserChannel from 'hyperchannel/models/user_channel';
import Message from 'hyperchannel/models/message';
import config from 'hyperchannel/config/environment';
import moment from 'moment';
import { storageFor as localStorageFor } from 'ember-local-storage';

const {
  Service,
  inject: {
    service
  },
  RSVP,
  isEmpty,
  isPresent,
  Logger,
  get
} = Ember;

export default Service.extend({
  userSettings: localStorageFor('user-settings'),
  ajax: service(),
  logger: service(),
  storage: service('remotestorage'),

  spaces: null,

  instantiateSpacesAndChannels() {
    this.set('spaces', []);
    let rs = this.get('storage.rs');

    return new RSVP.Promise((resolve, reject) => {
      rs.kosmos.spaces.getAll().then(spaceData => {
        if (isEmpty(Object.keys(spaceData))) {
          Logger.debug('No space data found in RS. Adding default space...');
          this.get('storage').addDefaultSpace().then((data) => {
            this.connectToIRCServer(data.space);
            this.get('spaces').pushObject(data.space);
            this.instantiateChannels(data.space, data.channels);
            resolve();
          });
        } else {
          Object.keys(spaceData).forEach((id) => {
            let space = Space.create({
              name: spaceData[id].name,
              protocol: spaceData[id].protocol,
              server: spaceData[id].server
            });
            this.connectToIRCServer(space);
            this.get('spaces').pushObject(space);
            this.instantiateChannels(space, spaceData[id].channels);
          });
          resolve();
        }
      }, e => {
        this.log('error', 'couldn\'d load spaces from RS', e);
        reject();
      });
    });
  },

  connectToIRCServer(space) {
    this.sockethub.ActivityStreams.Object.create({
      '@id': space.get('sockethubPersonId'),
      '@type': "person",
      displayName: space.get('server.nickname')
    });

    var credentials = {
      actor: space.get('sockethubPersonId'),
      context: 'irc',
      object: {
        '@type': 'credentials',
        nick: space.get('server.nickname'),
        server: space.get('server.hostname'),
        port: space.get('server.port'),
        secure: space.get('server.secure')
      }
    };

    this.log('connection', 'connecting to irc', credentials);
    this.sockethub.socket.emit('credentials', credentials);
  },

  transferMessage(space, target, content) {
    let job = {
      context: 'irc',
      '@type': 'send',
      actor: space.get('sockethubPersonId'),
      target: target,
      object: {
        '@type': 'message',
        content: content
      }
    };

    this.log('send', 'sending message job', job);
    this.sockethub.socket.emit('message', job);
  },

  transferMeMessage(space, target, content) {
    let job = {
      context: 'irc',
      '@type': 'send',
      actor: space.get('sockethubPersonId'),
      target: target,
      object: {
        '@type': 'me',
        content: content
      }
    };

    this.log('send', 'sending message job', job);
    this.sockethub.socket.emit('message', job);
  },

  setupListeners() {
    this.sockethub.socket.on('completed', (message) => {
      this.log('sh_completed', message);

      switch(message['@type']) {
        case 'join':
          if (message['@type'] === 'join') {
            var space = this.get('spaces').findBy('sockethubPersonId', message.actor);
            if (!isEmpty(space)) {
              var channel = space.get('channels').findBy('sockethubChannelId', message.target);
              if (!isEmpty(channel)) {
                channel.set('connected', true);
                this.observeChannel(space.get('sockethubPersonId'), channel.get('sockethubChannelId'));
              }
            }
          }
          break;
        case 'observe':
          if (message.object['@type'] === 'attendance') {
            this.updateChannelUserList(message);
          }
          break;
      }
    });

    this.sockethub.socket.on('message', (message) => {
      this.log('message', 'SH message', message);

      switch(message['@type']) {
        case 'observe':
          if (message.object['@type'] === 'attendance') {
            this.updateChannelUserList(message);
          }
          break;
        case 'join':
          this.addUserToChannelUserList(message);
          break;
        case 'leave':
          this.removeUserFromChannelUserList(message);
          break;
        case 'send':
          switch(message.object['@type']) {
            case 'message':
            case 'me':
              this.addMessageToChannel(message);
              break;
          }
          break;
        case 'update':
          if (message.object['@type'] === 'topic') {
            this.updateChannelTopic(message);
          }
          break;
      }

      // user list for a channel
    });

    this.sockethub.socket.on('failure', function(message) {
      Logger.error('SH failure', message);
    });
  },

  updateChannelUserList(message) {
    const channel = this.getChannelByMessage(message);
    if (channel) {
      channel.set('userList', message.object.members.sort());
    }
  },

  addUserToChannelUserList(message) {
    const channel = this.getChannelByMessage(message);
    if (channel) {
      channel.addUser(message.actor.displaName);
    }
  },

  removeUserFromChannelUserList(message) {
    const channel = this.getChannelByMessage(message);
    if (channel) {
      channel.removeUser(message.actor.displayName);
    }
  },

  getChannelByMessage(message) {
    var addressWithHostname, hostname;
    if (typeof message.actor === 'object') {
      addressWithHostname = message.actor['@id'];
    } else if (typeof message.actor === 'string') {
      addressWithHostname = message.actor;
    }

    hostname = addressWithHostname.match(/irc:\/\/(?:.+@)?(.+?)(?:\/|$)/)[1];

    var space = this.get('spaces').findBy('server.hostname', hostname);

    if (!isEmpty(space)) {
      var channel = space.get('channels').findBy('sockethubChannelId', message.target['@id']);
      if (!isEmpty(channel)) {
        return channel;
      }
    }
  },

  updateChannelTopic(message) {
    let hostname;
    if (typeof message.target === 'object') {
      hostname = message.target['@id'].match(/irc:\/\/(.+)\//)[1];
    } else if (typeof message.actor === 'string') {
      hostname = message.actor.match(/irc:\/\/.+\@(.+)/)[1];
    }

    let space = this.get('spaces').findBy('server.hostname', hostname);

    if (!isEmpty(space)) {
      let channel = space.get('channels').findBy('sockethubChannelId', message.target['@id']);

      if (isEmpty(channel)) {
        channel = this.createChannel(space, message.target['@id']);
      }

      let currentTopic = channel.get('topic');
      let newTopic = message.object.topic;

      channel.set('topic', newTopic);

      if (isPresent(currentTopic) && (newTopic !== currentTopic) && !channel.get('visible')) {
        Notification.requestPermission(function(){
          new Notification(channel.name, {
            body: `New Topic: ${newTopic}`
          });
        });
      }

      // let notification = Message.create({
      //   type: 'notification-topic-change',
      //   date: new Date(message.published),
      //   nickname: message.actor.displayName,
      //   content: message.object.topic
      // });

      // channel.get('messages').pushObject(notification);

    }
  },

  addMessageToChannel: function(message) {
    var space = this.get('spaces').findBy('server.hostname',
                message.actor['@id'].match(/irc:\/\/.+\@(.+)/)[1]);
    var nickname = space.get('userNickname');

    var targetChannelName;
    if (nickname === message.target.displayName) {
      targetChannelName = message.actor.displayName;
    } else {
      targetChannelName = message.target.displayName;
    }

    var channel = space.get('channels').findBy('name', targetChannelName);
    if (!channel) {
      channel = this.createChannel(space, targetChannelName);
    }

    let messageType;
    if (message.object['@type'] === 'me') {
      messageType = 'message-chat-me';
    } else {
      messageType = 'message-chat';
    }

    var channelMessage = Message.create({
      type: messageType,
      date: new Date(message.published),
      nickname: message.actor.displayName,
      content: message.object.content
    });

    // TODO should check for message and update sent status if exists
    if (message.actor.displayName !== nickname) {
      channel.addMessage(channelMessage);
    }
  },

  observeChannel: function(person, channelId) {
    var observeMsg = {
      context: 'irc',
      '@type': 'observe',
      actor: person,
      target: channelId,
      object: {
        '@type': 'attendance'
      }
    };

    Logger.debug('asking for attendance list', observeMsg);
    this.sockethub.socket.emit('message', observeMsg);
  },

  instantiateChannels: function(space, channels) {
    channels.forEach((channelName) => {
      this.createChannel(space, channelName);
    });
  },

  createChannel: function(space, channelName) {
    var channel = Channel.create({
      space: space,
      name: channelName,
      sockethubChannelId: `irc://${space.get('server.hostname')}/${channelName}`
    });

    this.joinChannel(space, channel, "room");
    space.get('channels').pushObject(channel);

    if (channel.get('isLogged')) {
      this.loadLastMessages(space, channel, moment.utc(), 2).catch(() => {});
    }

    return channel;
  },

  loadLastMessages(space, channel, date, maximumDays = 14) {
    let searchUntilDate;
    if (channel.get('searchedPreviousLogsUntilDate')) {
      searchUntilDate = moment(channel.get('searchedPreviousLogsUntilDate')).subtract(maximumDays, 'days');
    } else {
      searchUntilDate = moment.utc().subtract(maximumDays, 'days');
    }

    if (date.isBefore(searchUntilDate, 'day')) {
      channel.set('searchedPreviousLogsUntilDate', date);
      return;
    }

    return this.loadArchiveMessages(space, channel, date).catch(() => {
      // didn't find any archive for this day, restart searching for the previous day
      return this.loadLastMessages(space, channel, date.subtract(1, 'day'));
    });
  },

  loadArchiveMessages(space, channel, date) {
    let logsUrl = `${config.publicLogsUrl}/${space.get('name').toLowerCase()}/channels/${channel.get('slug')}/`;
        logsUrl += date.format('YYYY/MM/DD');

    return this.get('ajax').request(logsUrl, {
      type: 'GET',
      dataType: 'json'
    }).then(archive => {
      get(archive, 'today.messages').forEach((message) => {
        this.log('message', message);

        let channelMessage = Message.create({
          type: 'message-chat',
          date: new Date(message.timestamp),
          nickname: message.from,
          content: message.text
        });

        channel.addMessage(channelMessage);
      });
      let previous = get(archive, 'today.previous');
      channel.set('searchedPreviousLogsUntilDate', moment.utc(previous.replace(/\//g, '-')));
    }).catch(error => {
      this.log('error', error);
      throw(error);
    });
  },

  createUserChannel: function(space, userName) {
    var channel = UserChannel.create({
      name: userName,
      sockethubChannelId: `irc://${space.get('server.hostname')}/${userName}`
    });

    this.joinChannel(space, channel, "person");
    space.get('channels').pushObject(channel);

    return channel;
  },

  removeChannel: function(space, channelName) {
    var channel = space.get('channels').findBy('name', channelName);
    this.leaveChannel(space, channel);

    space.get('channels').removeObject(channel);

    this.get('storage').saveSpace(space);

    return channel;
  },

  joinChannel: function(space, channel, type) {
    this.sockethub.ActivityStreams.Object.create({
      '@type': type,
      '@id': channel.get('sockethubChannelId'),
      displayName: channel.get('name')
    });

    var joinMsg = {
      context: 'irc',
      '@type': 'join',
      actor: space.get('sockethubPersonId'),
      target: channel.get('sockethubChannelId'),
      object: {}
    };

    this.log('join', 'joining channel', joinMsg);
    this.sockethub.socket.emit('message', joinMsg);
  },

  leaveChannel: function(space, channel) {
    this.sockethub.ActivityStreams.Object.create({
      '@type': "room",
      '@id': channel.get('sockethubChannelId'),
      displayName: channel.get('name')
    });

    var joinMsg = {
      context: 'irc',
      '@type': 'leave',
      actor: space.get('sockethubPersonId'),
      target: channel.get('sockethubChannelId'),
      object: {}
    };

    this.log('leave', 'leaving channel', joinMsg);
    this.sockethub.socket.emit('message', joinMsg);
  },

  changeTopic: function(space, channel, topic) {
    var topicMsg = {
      context: 'irc',
      '@type': 'update',
      actor: space.get('sockethubPersonId'),
      target: channel.get('sockethubChannelId'),
      object: {
        '@type': 'topic',
        topic: topic
      }
    };

    this.sockethub.socket.emit('message', topicMsg);
  },

  // Utility function
  log() {
    this.get('logger').log(...arguments);
  }
});
<|endoftext|>


<commit_before>module.exports = function () {
    var express = require('express');
    var unirest = require('unirest');
    var app = express();

    // Models
    var Game = require('../../models/game');

    // Enums
    var CODE = require('../../enums/codes');

    app
        // -----------------------------------------------------------------------------------
        // --                                       POST                                    --
        // -----------------------------------------------------------------------------------

        // Description : Add a new game
        // URL: http://localhost:8080/api/games
        // Form params :
        //          - name
        //          - overview
        //          - editors[]
        //          - developers[]
        //          - genres[]
        //          - platforms[]
        //          - releaseDate
        //          - metacritic{}
        //          - media{}
        .post('/games', function (req, res) {
            var name = req.body.name;  // set the games name (comes from the request)

            // Check if a game already exist with this name
            Game.findOneQ({name: name})
                .then(function (result) {
                    if (result) {
                        console.log("Find a game with this name : " + name);
                        res.json(CODE.ALREADY_EXIST);
                    } else {
                        // Create a new instance of the Game model
                        var game = new Game();
                        game.name = name;
                        game.overview = req.body.overview;
                        game.editors = req.body.editors;
                        game.developers = req.body.developers;
                        game.genres = req.body.genres;
                        game.platforms = req.body.platforms;
                        game.releaseDate = req.body.releaseDate;

                        if (req.body.metacritic) {
                            game.metacritic.score = req.body.metacritic.score;
                            game.metacritic.url = req.body.metacritic.url;
                        }

                        if (req.body.media) {
                            if (req.body.media.boxArt) {
                                game.media.boxArt.front = req.body.media.boxArt.front;
                                game.media.boxArt.rear = req.body.media.boxArt.rear;
                            }
                            game.media.thumbnails = req.body.media.thumbnails;
                            game.media.logos = req.body.media.logos;
                            game.media.banners = req.body.media.banners;
                            game.media.fanArt = req.body.media.fanArt;
                            game.media.screenshots = req.body.media.screenshots;
                            game.media.trailers = req.body.media.trailers;
                        }

                        // Save the game and check for errors
                        game.saveQ()
                            .then(function (game) {
                                console.log('Game ' + game.name + ' added !');

                                // Save game object to the response object
                                CODE.SUCCESS_POST.game = game;
                                res.json(CODE.SUCCESS);
                            })
                            .catch(function (err) {
                                console.error(err);
                                res.json(CODE.SERVER_ERROR);
                            });
                    }

                })
                .catch(function (err) {
                    console.error(err);
                    res.json(CODE.SERVER_ERROR);
                });
        })

        // -----------------------------------------------------------------------------------
        // --                                       GET                                     --
        // -----------------------------------------------------------------------------------

        // Description : Get all the games, come with pagination params
        // URL : http://localhost:8080/api/games/?skip=:skip&limit=:limit
        // URL params :
        //          - skip
        //          - limit
        .get('/games', function (req, res) {

            var skip = req.param('skip');
            var limit = req.param('limit');

            console.log("-- Searching games with skip '" + skip + "' and limit '" + limit + "'...");

            Game.find()
                .skip(skip)
                .limit(limit)
                .exec(function (err, games) {
                    if (err)
                        res.send(CODE.SERVER_ERROR);

                    var count = games.length;
                    console.log('-- ' + count + ' game(s) founded !');

                    // Build the response
                    CODE.SUCCESS.count = count;
                    CODE.SUCCESS.skip = skip;
                    CODE.SUCCESS.limit = limit;
                    CODE.SUCCESS.games = games;

                    res.json(CODE.SUCCESS);
                });
        })

        // Description : Get games by a name
        // URL : http://localhost:8080/api/games/by/name/:name
        .get('/games/by/name/:game_name', function (req, res) {
            Game.find({name: new RegExp('^'+req.params.game_name+'$', "i")}, function (err, games) {
                if (err)
                    res.send(CODE.SERVER_ERROR);

                Game.count({name: req.params.game_name}, function (err, count) {
                    if (err)
                        res.send(CODE.SERVER_ERROR);

                    console.log(count + ' game(s) founded !');

                    // Build the response
                    CODE.SUCCESS.count = count;
                    CODE.SUCCESS.games = games;

                    res.json(CODE.SUCCESS);
                });
            });
        })

        // Description : Get a game by an id
        // URL : http://localhost:8080/api/games/by/:id
        .get('/games/by/id/:game_id', function (req, res) {
            Game.findById(req.params.game_id, function (err, game) {
                if (err)
                    res.send(CODE.SERVER_ERROR);

                console.log('Searching for game id ' + req.params.game_id + ' : ' + game.name);

                // Build the response
                CODE.SUCCESS.game = game;

                res.json(CODE.SUCCESS);
            });
        })

        // -----------------------------------------------------------------------------------
        // --                                       PUT                                     --
        // -----------------------------------------------------------------------------------

        // Description : Update a game with an id
        // URL : http://localhost:8080/api/games/:game_id
        // Param :
        //          - id
        // Form params :
        //          - Game Object
        .put('/games/:game_id', function (req, res) {
            // Use our game model to find the game we want
            Game.findById(req.params.game_id, function (err, game) {
                if (err)
                    res.send(CODE.SERVER_ERROR);

                // Update the games info
                game.name = req.body.name;
                game.overview = req.body.overview;
                game.editors = req.body.editors;
                game.developers = req.body.developers;
                game.genres = req.body.genres;
                game.platforms = req.body.platforms;
                game.releaseDate = req.body.releaseDate;
                game.updateDate = new Date();

                if (req.body.metacritic) {
                    game.metacritic.score = req.body.metacritic.score;
                    game.metacritic.url = req.body.metacritic.url;
                }

                if (req.body.media) {
                    if (req.body.media.boxArt) {
                        game.media.boxArt.front = req.body.media.boxArt.front;
                        game.media.boxArt.rear = req.body.media.boxArt.rear;
                    }
                    game.media.thumbnails = req.body.media.thumbnails;
                    game.media.logos = req.body.media.logos;
                    game.media.banners = req.body.media.banners;
                    game.media.fanArt = req.body.media.fanArt;
                    game.media.screenshots = req.body.media.screenshots;
                    game.media.trailers = req.body.media.trailers;
                }

                // Save the game
                game.save(function (err) {
                    if (err)
                        res.send(CODE.SERVER_ERROR);

                    // Build the response
                    CODE.SUCCESS_PUT.game = game;

                    res.json(CODE.SUCCESS);
                });
            });
        })

        // -----------------------------------------------------------------------------------
        // --                                     DELETE                                    --
        // -----------------------------------------------------------------------------------

        // Description : Delete a game with an id
        // URL : http://localhost:8080/api/games/:game_id
        // Param :
        //          - id
        .delete('/games/:game_id', function (req, res) {
            Game.remove({
                _id: req.params.game_id
            }, function (err) {
                if (err)
                    res.send(CODE.SERVER_ERROR);

                res.json(CODE.SUCCESS_DELETE);
            });
        });

    return app;
}();<commit_msg>Add criteria like on games search.<commit_after>module.exports = function () {
    var express = require('express');
    var unirest = require('unirest');
    var app = express();

    // Models
    var Game = require('../../models/game');

    // Enums
    var CODE = require('../../enums/codes');

    app
        // -----------------------------------------------------------------------------------
        // --                                       POST                                    --
        // -----------------------------------------------------------------------------------

        // Description : Add a new game
        // URL: http://localhost:8080/api/games
        // Form params :
        //          - name
        //          - overview
        //          - editors[]
        //          - developers[]
        //          - genres[]
        //          - platforms[]
        //          - releaseDate
        //          - metacritic{}
        //          - media{}
        .post('/games', function (req, res) {
            var name = req.body.name;  // set the games name (comes from the request)

            // Check if a game already exist with this name
            Game.findOneQ({name: name})
                .then(function (result) {
                    if (result) {
                        console.log("Find a game with this name : " + name);
                        res.json(CODE.ALREADY_EXIST);
                    } else {
                        // Create a new instance of the Game model
                        var game = new Game();
                        game.name = name;
                        game.overview = req.body.overview;
                        game.editors = req.body.editors;
                        game.developers = req.body.developers;
                        game.genres = req.body.genres;
                        game.platforms = req.body.platforms;
                        game.releaseDate = req.body.releaseDate;

                        if (req.body.metacritic) {
                            game.metacritic.score = req.body.metacritic.score;
                            game.metacritic.url = req.body.metacritic.url;
                        }

                        if (req.body.media) {
                            if (req.body.media.boxArt) {
                                game.media.boxArt.front = req.body.media.boxArt.front;
                                game.media.boxArt.rear = req.body.media.boxArt.rear;
                            }
                            game.media.thumbnails = req.body.media.thumbnails;
                            game.media.logos = req.body.media.logos;
                            game.media.banners = req.body.media.banners;
                            game.media.fanArt = req.body.media.fanArt;
                            game.media.screenshots = req.body.media.screenshots;
                            game.media.trailers = req.body.media.trailers;
                        }

                        // Save the game and check for errors
                        game.saveQ()
                            .then(function (game) {
                                console.log('Game ' + game.name + ' added !');

                                // Save game object to the response object
                                CODE.SUCCESS_POST.game = game;
                                res.json(CODE.SUCCESS);
                            })
                            .catch(function (err) {
                                console.error(err);
                                res.json(CODE.SERVER_ERROR);
                            });
                    }

                })
                .catch(function (err) {
                    console.error(err);
                    res.json(CODE.SERVER_ERROR);
                });
        })

        // -----------------------------------------------------------------------------------
        // --                                       GET                                     --
        // -----------------------------------------------------------------------------------

        // Description : Get all the games, come with pagination params
        // URL : http://localhost:8080/api/games/?skip=:skip&limit=:limit
        // URL params :
        //          - skip
        //          - limit
        .get('/games', function (req, res) {

            var skip = req.param('skip');
            var limit = req.param('limit');

            console.log("-- Searching games with skip '" + skip + "' and limit '" + limit + "'...");

            Game.find()
                .skip(skip)
                .limit(limit)
                .exec(function (err, games) {
                    if (err)
                        res.send(CODE.SERVER_ERROR);

                    var count = games.length;
                    console.log('-- ' + count + ' game(s) founded !');

                    // Build the response
                    CODE.SUCCESS.count = count;
                    CODE.SUCCESS.skip = skip;
                    CODE.SUCCESS.limit = limit;
                    CODE.SUCCESS.games = games;

                    res.json(CODE.SUCCESS);
                });
        })

        // Description : Get games by a name
        // URL : http://localhost:8080/api/games/by/name/:name
        .get('/games/by/name/:game_name', function (req, res) {
            var query = {name: new RegExp(req.params.game_name, "i")};
            Game.find(query, function (err, games) {
                if (err)
                    res.send(CODE.SERVER_ERROR);

                Game.count(query, function (err, count) {
                    if (err)
                        res.send(CODE.SERVER_ERROR);

                    console.log(count + ' game(s) founded !');

                    // Build the response
                    CODE.SUCCESS.count = count;
                    CODE.SUCCESS.games = games;

                    res.json(CODE.SUCCESS);
                });
            });
        })

        // Description : Get a game by an id
        // URL : http://localhost:8080/api/games/by/:id
        .get('/games/by/id/:game_id', function (req, res) {
            Game.findById(req.params.game_id, function (err, game) {
                if (err)
                    res.send(CODE.SERVER_ERROR);

                console.log('Searching for game id ' + req.params.game_id + ' : ' + game.name);

                // Build the response
                CODE.SUCCESS.game = game;

                res.json(CODE.SUCCESS);
            });
        })

        // -----------------------------------------------------------------------------------
        // --                                       PUT                                     --
        // -----------------------------------------------------------------------------------

        // Description : Update a game with an id
        // URL : http://localhost:8080/api/games/:game_id
        // Param :
        //          - id
        // Form params :
        //          - Game Object
        .put('/games/:game_id', function (req, res) {
            // Use our game model to find the game we want
            Game.findById(req.params.game_id, function (err, game) {
                if (err)
                    res.send(CODE.SERVER_ERROR);

                // Update the games info
                game.name = req.body.name;
                game.overview = req.body.overview;
                game.editors = req.body.editors;
                game.developers = req.body.developers;
                game.genres = req.body.genres;
                game.platforms = req.body.platforms;
                game.releaseDate = req.body.releaseDate;
                game.updateDate = new Date();

                if (req.body.metacritic) {
                    game.metacritic.score = req.body.metacritic.score;
                    game.metacritic.url = req.body.metacritic.url;
                }

                if (req.body.media) {
                    if (req.body.media.boxArt) {
                        game.media.boxArt.front = req.body.media.boxArt.front;
                        game.media.boxArt.rear = req.body.media.boxArt.rear;
                    }
                    game.media.thumbnails = req.body.media.thumbnails;
                    game.media.logos = req.body.media.logos;
                    game.media.banners = req.body.media.banners;
                    game.media.fanArt = req.body.media.fanArt;
                    game.media.screenshots = req.body.media.screenshots;
                    game.media.trailers = req.body.media.trailers;
                }

                // Save the game
                game.save(function (err) {
                    if (err)
                        res.send(CODE.SERVER_ERROR);

                    // Build the response
                    CODE.SUCCESS_PUT.game = game;

                    res.json(CODE.SUCCESS);
                });
            });
        })

        // -----------------------------------------------------------------------------------
        // --                                     DELETE                                    --
        // -----------------------------------------------------------------------------------

        // Description : Delete a game with an id
        // URL : http://localhost:8080/api/games/:game_id
        // Param :
        //          - id
        .delete('/games/:game_id', function (req, res) {
            Game.remove({
                _id: req.params.game_id
            }, function (err) {
                if (err)
                    res.send(CODE.SERVER_ERROR);

                res.json(CODE.SUCCESS_DELETE);
            });
        });

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


<commit_before>// Foundation JavaScript
// Documentation can be found at: http://foundation.zurb.com/docs

(function($) {

	function wowGrid(){
	    $('[data-wow-grid], .data-wow-grid').each(function(){
		    var columns;
		    if( parseInt( $(this).attr('data-wow-columns') ) > 0){
			    columns = parseInt( $(this).attr('data-wow-columns') );
		    }else{
		    	columns = Math.floor( $(this).innerWidth() / $(this).children().eq(1).outerWidth() );
		    }
		    $(this).children().each(function(index){
			    var delay = (index % columns *0.2) + ( Math.floor(index/columns) * 0.1);
			    if( !$(this).hasClass('wow') ){
				    if(columns === 1){

					    $(this).addClass('wow fadeInUp');
				    }else{
					    $(this).addClass('wow fadeInRight');
				    }
			    }
			    $(this).attr('data-wow-delay',delay+"s");

		    });
		});
	}
    wowGrid();
    $( window ).resize(function() {
        wowGrid();
    });

    function wowList(){
	    $('[data-wow-list], .data-wow-list').each(function(){

		    $(this).children().each(function(index){
			    var delay = 0.2;

			    $(this).addClass('wow fadeInUp');
			    $(this).attr('data-wow-delay',delay+"s");

		    });
		});
	}
    wowList();

	$('[data-disabled]').on('click',function(e){e.preventDefault();});

    $('.ddl-full-width-row').addClass('expanded').addClass('collapse');

    //inject special media queries for interchange on retina devices
    var retinaQueries = configShared.mediaqueries.retina.replace("'","").split(",");
    configShared.breakpoints = JSON.parse( configShared.breakpoints.replace(/\s?\(\s?/i, '{"').replace(/\s?\)\s?/i, '"}').replace(/\s?(\,|\:)\s?/ig,'"$1"'));

    var sizes = ['smallplus','medium','large','xlarge','xxlarge','xxxlarge'];
    for( var k in sizes ){
        var mediaQuerySize = configShared.breakpoints[sizes[k]].match(/([0-9]{1,4})([a-z]{1,3})/i);
        Foundation.Interchange.SPECIAL_QUERIES[sizes[k]+'R'] = "";//"only screen and (min-width: "+configShared.breakpoints[sizes[k]]+")";
        var i = 0;
        for(var k2 in retinaQueries){

            if(i !== 0){
                Foundation.Interchange.SPECIAL_QUERIES[sizes[k]+'R'] += ', ';
            }
            Foundation.Interchange.SPECIAL_QUERIES[sizes[k]+'R'] += retinaQueries[k2] + ' and (min-width: '+ parseInt(mediaQuerySize[1])/2 + mediaQuerySize[2] +')';
            i++;
        }

    }

    $(document).foundation();

    //make interchange work with equalizer
    $(document).on('replaced.zf.interchange', 'img', function(e) {
	    $(e.target).trigger( 'resizeme.zf.trigger');
    });

    // Remove empty P tags created by WP inside of Accordion and Orbit
    jQuery('.accordion p:empty, .orbit p:empty').remove();

	 // Makes sure last grid item floats left
	jQuery( '.archive-grid > .columns' ).last().addClass( 'end' );

	// Adds Flex Video to YouTube and Vimeo Embeds
	jQuery( 'iframe[src*="youtube.com"], iframe[src*="vimeo.com"]' ).wrap("
");

    // sticky footer and padding for sticky header
    function stickyFooter(){
        var topOffset = 0;
        if($('#main-header').css('position') != 'absolute' && $('#main-header').css('position') != 'fixed' ){
        	topOffset = $('#main-header').outerHeight(true);
        }else{

        }
        var bottomOffset = $('#main-footer').outerHeight(true);
        var bodyOffset = $('body').outerHeight(true) - $('body').innerHeight();
        if($('#main-header').hasClass('sticky')){
            //topOffset = $('#main-header').outerHeight(true);
            $('.page-wrapper').css('margin-top',topOffset);
        }

        $('.page-wrapper').css('min-height',$(window).innerHeight() - topOffset - bottomOffset - bodyOffset);

    }
    stickyFooter();

    $( window ).resize(function() {
        stickyFooter();
    });

    $('[data-slick]').slick();

    if($(".animsition").length){
        $('.wow').css('visibility', 'hidden').css('animation-name', 'none');
        $(".animsition").animsition({
            inClass: 'animsition-fade-in',
            outClass: 'animsition-fade-out',
            inDuration: 1500,
            outDuration: 800,
            linkElement: ':not(.flex-control-nav li) > a:not([target="_blank"]):not([href^=#]):not(.no-animation):not([data-scroll-target])',
            // e.g. linkElement: 'a:not([target="_blank"]):not([href^=#])'
            loading: true,
            loadingParentElement: 'body', //animsition wrapper element
            loadingClass: 'animsition-loading',
            //loadingInner: "loading",
            browser: [
              'animation-duration',
              '-webkit-animation-duration',
              '-o-animation-duration'
            ],
            //"unSupportCss" option allows you to disable the "animsition" in case the css property in the array is not supported by your browser.
            //The default setting is to disable the "animsition" in a browser that does not support "animation-duration".
            overlay : false,
            overlayClass : 'animsition-overlay-slide',
            overlayParentElement : 'body'
        }).one('animsition.inStart', function(){
            window.setTimeout(function(){
                wow.init();
            },500);
        });
    }else{
        wow.init();
    }

// just insert a div like this:
//

    if($('[data-map]').length ){
        $('head').append('<link rel="stylesheet" href="https://api.tiles.mapbox.com/mapbox.js/v2.1.6/mapbox.css" type="text/css" />');
        $.getScript("https://api.tiles.mapbox.com/mapbox.js/v2.1.6/mapbox.js", function(){
            var lat = $(this).data('map-lat') || 45.514719, lon = $(this).data('map-lon') || 9.229454;
            $('[data-map]').each(function(){
                L.mapbox.accessToken = $(this).data('map-key');

                var map = L.mapbox.map($(this)[0], $(this).data('map-id') || 'examples.map-i86nkdio' ).setView([ lat , lon ], $(this).data('map-zoom') || 16);

                if (Modernizr.touch) {
                    map.dragging.disable();
                    if (map.tap) map.tap.disable();
                }else{
                    map.scrollWheelZoom.disable();
                }
                L.mapbox.featureLayer({
                    type: 'Feature',
                    geometry: {
                        type: 'Point',
                        coordinates: [
                            lon,
                            lat
                        ]
                    },
                    properties: {
                        title: $(this).data('map-marker-title') || false,
                        description: $(this).data('map-marker-description') || false,
                        'marker-size': $(this).data('map-marker-size') || 'medium',
                        'marker-color': $(this).data('map-marker-color') || "#00a3d2",
                        'marker-symbol': $(this).data('map-marker-symbol') || ""
                    }
                }).addTo(map);
            });
        });
    }

    outdatedBrowser({
        bgColor: '#c6352b',
        color: '#ffffff',
        lowerThan: 'transform',
        languagePath: ''
    });

})(jQuery);
<commit_msg>aggiunta di data-wow-selector a wow-grid<commit_after>// Foundation JavaScript
// Documentation can be found at: http://foundation.zurb.com/docs

(function($) {

	function wowGrid(){
	    $('[data-wow-grid], .data-wow-grid').each(function(){
		    var columns;
		    var $childrens;
		    var $el = $(this);
		    if( typeof( $el.attr('data-wow-selector') ) != 'undefined' ){
			    $childrens = $el.find( $el.attr('data-wow-selector') );
		    }else{
			    $childrens = $el.children();
		    }

		    if( parseInt( $el.attr('data-wow') ) > 0){
			    columns = parseInt( $el.attr('data-wow') );
		    }else{
		    	columns = Math.floor( $el.innerWidth() / $childrens.eq(1).outerWidth() );
		    }

		    $childrens.each(function(index){
			    var delay = (index % columns *0.2) + ( Math.floor(index/columns) * 0.1);
			    if( !$(this).hasClass('wow') ){
				    if(columns === 1){

					    $(this).addClass('wow fadeInUp');
				    }else{
					    $(this).addClass('wow fadeInRight');
				    }
			    }
			    $(this).attr('data-wow-delay',delay+"s");

		    });
		});
	}
    wowGrid();
    $( window ).resize(function() {
        wowGrid();
    });

    function wowList(){
	    $('[data-wow-list], .data-wow-list').each(function(){

		    $(this).children().each(function(index){
			    var delay = 0.2;

			    $(this).addClass('wow fadeInUp');
			    $(this).attr('data-wow-delay',delay+"s");

		    });
		});
	}
    wowList();

	$('[data-disabled]').on('click',function(e){e.preventDefault();});

    $('.ddl-full-width-row').addClass('expanded').addClass('collapse');

    //inject special media queries for interchange on retina devices
    var retinaQueries = configShared.mediaqueries.retina.replace("'","").split(",");
    configShared.breakpoints = JSON.parse( configShared.breakpoints.replace(/\s?\(\s?/i, '{"').replace(/\s?\)\s?/i, '"}').replace(/\s?(\,|\:)\s?/ig,'"$1"'));

    var sizes = ['smallplus','medium','large','xlarge','xxlarge','xxxlarge'];
    for( var k in sizes ){
        var mediaQuerySize = configShared.breakpoints[sizes[k]].match(/([0-9]{1,4})([a-z]{1,3})/i);
        Foundation.Interchange.SPECIAL_QUERIES[sizes[k]+'R'] = "";//"only screen and (min-width: "+configShared.breakpoints[sizes[k]]+")";
        var i = 0;
        for(var k2 in retinaQueries){

            if(i !== 0){
                Foundation.Interchange.SPECIAL_QUERIES[sizes[k]+'R'] += ', ';
            }
            Foundation.Interchange.SPECIAL_QUERIES[sizes[k]+'R'] += retinaQueries[k2] + ' and (min-width: '+ parseInt(mediaQuerySize[1])/2 + mediaQuerySize[2] +')';
            i++;
        }

    }

    $(document).foundation();

    //make interchange work with equalizer
    $(document).on('replaced.zf.interchange', 'img', function(e) {
	    $(e.target).trigger( 'resizeme.zf.trigger');
    });

    // Remove empty P tags created by WP inside of Accordion and Orbit
    jQuery('.accordion p:empty, .orbit p:empty').remove();

	 // Makes sure last grid item floats left
	jQuery( '.archive-grid > .columns' ).last().addClass( 'end' );

	// Adds Flex Video to YouTube and Vimeo Embeds
	jQuery( 'iframe[src*="youtube.com"], iframe[src*="vimeo.com"]' ).wrap("
");

    // sticky footer and padding for sticky header
    function stickyFooter(){
        var topOffset = 0;
        if($('#main-header').css('position') != 'absolute' && $('#main-header').css('position') != 'fixed' ){
        	topOffset = $('#main-header').outerHeight(true);
        }else{

        }
        var bottomOffset = $('#main-footer').outerHeight(true);
        var bodyOffset = $('body').outerHeight(true) - $('body').innerHeight();
        if($('#main-header').hasClass('sticky')){
            //topOffset = $('#main-header').outerHeight(true);
            $('.page-wrapper').css('margin-top',topOffset);
        }

        $('.page-wrapper').css('min-height',$(window).innerHeight() - topOffset - bottomOffset - bodyOffset);

    }
    stickyFooter();

    $( window ).resize(function() {
        stickyFooter();
    });

    $('[data-slick]').slick();

    if($(".animsition").length){
        $('.wow').css('visibility', 'hidden').css('animation-name', 'none');
        $(".animsition").animsition({
            inClass: 'animsition-fade-in',
            outClass: 'animsition-fade-out',
            inDuration: 1500,
            outDuration: 800,
            linkElement: ':not(.flex-control-nav li) > a:not([target="_blank"]):not([href^=#]):not(.no-animation):not([data-scroll-target])',
            // e.g. linkElement: 'a:not([target="_blank"]):not([href^=#])'
            loading: true,
            loadingParentElement: 'body', //animsition wrapper element
            loadingClass: 'animsition-loading',
            //loadingInner: "loading",
            browser: [
              'animation-duration',
              '-webkit-animation-duration',
              '-o-animation-duration'
            ],
            //"unSupportCss" option allows you to disable the "animsition" in case the css property in the array is not supported by your browser.
            //The default setting is to disable the "animsition" in a browser that does not support "animation-duration".
            overlay : false,
            overlayClass : 'animsition-overlay-slide',
            overlayParentElement : 'body'
        }).one('animsition.inStart', function(){
            window.setTimeout(function(){
                wow.init();
            },500);
        });
    }else{
        wow.init();
    }

// just insert a div like this:
//

    if($('[data-map]').length ){
        $('head').append('<link rel="stylesheet" href="https://api.tiles.mapbox.com/mapbox.js/v2.1.6/mapbox.css" type="text/css" />');
        $.getScript("https://api.tiles.mapbox.com/mapbox.js/v2.1.6/mapbox.js", function(){
            var lat = $(this).data('map-lat') || 45.514719, lon = $(this).data('map-lon') || 9.229454;
            $('[data-map]').each(function(){
                L.mapbox.accessToken = $(this).data('map-key');

                var map = L.mapbox.map($(this)[0], $(this).data('map-id') || 'examples.map-i86nkdio' ).setView([ lat , lon ], $(this).data('map-zoom') || 16);

                if (Modernizr.touch) {
                    map.dragging.disable();
                    if (map.tap) map.tap.disable();
                }else{
                    map.scrollWheelZoom.disable();
                }
                L.mapbox.featureLayer({
                    type: 'Feature',
                    geometry: {
                        type: 'Point',
                        coordinates: [
                            lon,
                            lat
                        ]
                    },
                    properties: {
                        title: $(this).data('map-marker-title') || false,
                        description: $(this).data('map-marker-description') || false,
                        'marker-size': $(this).data('map-marker-size') || 'medium',
                        'marker-color': $(this).data('map-marker-color') || "#00a3d2",
                        'marker-symbol': $(this).data('map-marker-symbol') || ""
                    }
                }).addTo(map);
            });
        });
    }

    outdatedBrowser({
        bgColor: '#c6352b',
        color: '#ffffff',
        lowerThan: 'transform',
        languagePath: ''
    });

})(jQuery);
<|endoftext|>