<commit_before>/**
 * Functions related to the loading and manipulation of CommonJS Modules in
 * Kanso apps.
 *
 * @module
 */

var utils = require('./utils'),
    async = require('async'),
    path = require('path'),
    fs = require('fs'),
    evals = process.binding('evals'),
    Script = evals.Script || evals.NodeScript;

// As modules are added, their original file paths are added to this object
// eg: {'lib': {'mymodule': '/home/user/project/lib/mymodule.js'}, ...}
exports.originalPaths = {};

/**
 * Add the module source to the document in the correct location for requiring
 * server-side, then add the path to the _modules property for use by the
 * modules plugin postprocessor (when creating the kanso.js attachment)
 *
 * Returns the updated document.
 *
 * @param {Object} doc
 * @param {String} path
 * @param {String} src
 * @returns {Object}
 */

exports.add = function (doc, path, src) {
    utils.setPropertyPath(doc, path, src);
    if (!doc._modules) {
        doc._modules = {};
    }
    doc._modules[path] = null;
    return doc;
};

/**
 * Wraps module source code with useful comments and module cache boilerplate
 * code.
 *
 * @param {Object} doc
 * @param {String} path
 * @param {String} src
 * @returns {Object}
 */

exports.wrap = function (path, src) {
    return '/********** ' + path + ' **********/\n\n' +
        'kanso.moduleCache["' + path.replace('"', '\\"') + '"] = ' +
        '{load: (function (module, exports, require) {\n\n' + src + '\n\n})};' +
        '\n\n';
};

/**
 * Searchs a path for commonjs modules, adding them to the document.
 *
 * @param {String} pkgdir - path to the source package
 * @param {String} p      - path to a module file or directory of modules
 * @param {Object} doc    - the document to extend
 * @param {Function} callback
 */

exports.addPath = function (pkgdir, p, doc, callback) {
    p = utils.abspath(p, pkgdir);
    exports.find(p, function (err, files) {
        if (err) {
            return callback(err);
        }
        async.forEach(files, function (f, cb) {
            exports.addFile(pkgdir, f, doc, cb);
        }, callback);
    });
};

/**
 * Loads a module file and adds its contents to the document
 *
 * @param {String} pkgdir
 * @param {String} p
 * @param {Object} doc
 * @param {Function} callback
 */

exports.addFile = function (pkgdir, p, doc, callback) {
    fs.readFile(p, function (err, content) {
        if (err) {
            return callback(err);
        }
        var rel = utils.relpath(p, pkgdir);
        var module_path = rel.replace(/\.js$/, '');
        var src = content.toString();
        exports.add(doc, module_path, src);
        utils.setPropertyPath(exports.originalPaths, rel, p);
        callback()
    });
};

/**
 * Find all modules below or at a given path, recursing through subdirectories
 *
 * @param {String} p - the path to search
 * @param {Function} callback
 */

exports.find = async.memoize(function (p, callback) {
    utils.find(p, exports.filenameFilter(p), callback);
});

/**
 * Creates a filter used when searching for module files. This function tests
 * for a .js extension and omits hidden dot-preceeded filenames.
 *
 * @param {String} p - the path to the directory being searched
 * @returns {Function}
 */

exports.filenameFilter = function (p) {
    return function (f) {
        if (f === p) {
            return true;
        }
        var relpath = utils.relpath(f, p);
        // should not start with a '.'
        if (/^\.[^\/]?/.test(relpath)) {
            return false;
        }
        // should not contain a file or folder starting with a '.'
        if (/\/\./.test(relpath)) {
            return false;
        }
        // should have a .js extension
        if (!/\.js$/.test(f)) {
            return false;
        }
        return true;
    };
};

/**
 * Loads a commonjs module from the loaded design document, returning
 * the exported properties. The current_dir and target parameters are not the
 * path of the module on the filesystem, but rather the path of the module
 * within couchdb, root ('/') being the design doc itself.
 *
 * @param {Object} module_cache
 * @param {Object} doc
 * @param {String} current_dir
 * @param {String} target
 * @param {Object} context - optional, extends sandbox object
 */

exports.require = function (module_cache, doc, current, target, context) {
    var current_dir = path.dirname(current);
    if (target.charAt(0) !== '.') {
        current_dir = '/';
    }
    var p = path.normalize(path.join(current_dir, target));
    if (module_cache[p]) {
        return module_cache[p];
    }

    var nodes = p.split('/').slice(1);
    var content = nodes.reduce(function (a, x) {
        if (a[x] === undefined) {
            throw new Error(
                //'Could not require module: ' + target + ' ' +
                'Could not require module: ' + p + ' ' +
                '(from: ' + current + ')'
            );
        }
        a = a[x];
        return a;
    }, doc);

    var filename = utils.getPropertyPath(
        exports.originalPaths, p.substr(1), true
    );

    var module = {
        id: p.substr(1),
        exports: {},
        // TODO: this property is not provided by couchdb, but is by node:
        //filename: filename,
        // TODO: this property is not provided by couchdb, but is by node:
        //require: async.apply(exports.require, module_cache, doc, p)
        // TODO: module properties provided by couchdb, but not by kanso
        // * current
        // * parent
    };
    var sandbox = {
        module: module,
        exports: module.exports,
        //require: module.require,
        require: async.apply(exports.require, module_cache, doc, p),
        log: function () {
            console.log.apply(console, arguments);
        }
    };

    // copy context into sandbox
    if (context) {
        Object.keys(context).forEach(function (k) {
            sandbox[k] = context[k];
        });
    }

    // Create a placeholder for this module's exports so circular requires
    // are possible. TODO: node.js uses a loaded = false attribute on the
    // cached module object to mark this as a placeholder.
    module_cache[p] = {};

    try {
        var s = new Script(content, filename).runInNewContext(sandbox);
    }
    catch (e) {
        if (e instanceof SyntaxError && filename) {
            // gives a better syntax error than runInNewContext
            // with filename and line number
            require(filename);
        }
        if (!e.got_filename) {
            e.message = 'Error loading: ' + filename + '\n' + e.message;
            e.got_filename = true;
        }
        throw e;
    }
    module_cache[p] = sandbox.module.exports;
    return module_cache[p];
};
<commit_msg>Improved error reporting when attempting to require a directory<commit_after>/**
 * Functions related to the loading and manipulation of CommonJS Modules in
 * Kanso apps.
 *
 * @module
 */

var utils = require('./utils'),
    async = require('async'),
    path = require('path'),
    fs = require('fs'),
    evals = process.binding('evals'),
    Script = evals.Script || evals.NodeScript;

// As modules are added, their original file paths are added to this object
// eg: {'lib': {'mymodule': '/home/user/project/lib/mymodule.js'}, ...}
exports.originalPaths = {};

/**
 * Add the module source to the document in the correct location for requiring
 * server-side, then add the path to the _modules property for use by the
 * modules plugin postprocessor (when creating the kanso.js attachment)
 *
 * Returns the updated document.
 *
 * @param {Object} doc
 * @param {String} path
 * @param {String} src
 * @returns {Object}
 */

exports.add = function (doc, path, src) {
    utils.setPropertyPath(doc, path, src);
    if (!doc._modules) {
        doc._modules = {};
    }
    doc._modules[path] = null;
    return doc;
};

/**
 * Wraps module source code with useful comments and module cache boilerplate
 * code.
 *
 * @param {Object} doc
 * @param {String} path
 * @param {String} src
 * @returns {Object}
 */

exports.wrap = function (path, src) {
    return '/********** ' + path + ' **********/\n\n' +
        'kanso.moduleCache["' + path.replace('"', '\\"') + '"] = ' +
        '{load: (function (module, exports, require) {\n\n' + src + '\n\n})};' +
        '\n\n';
};

/**
 * Searchs a path for commonjs modules, adding them to the document.
 *
 * @param {String} pkgdir - path to the source package
 * @param {String} p      - path to a module file or directory of modules
 * @param {Object} doc    - the document to extend
 * @param {Function} callback
 */

exports.addPath = function (pkgdir, p, doc, callback) {
    p = utils.abspath(p, pkgdir);
    exports.find(p, function (err, files) {
        if (err) {
            return callback(err);
        }
        async.forEach(files, function (f, cb) {
            exports.addFile(pkgdir, f, doc, cb);
        }, callback);
    });
};

/**
 * Loads a module file and adds its contents to the document
 *
 * @param {String} pkgdir
 * @param {String} p
 * @param {Object} doc
 * @param {Function} callback
 */

exports.addFile = function (pkgdir, p, doc, callback) {
    fs.readFile(p, function (err, content) {
        if (err) {
            return callback(err);
        }
        var rel = utils.relpath(p, pkgdir);
        var module_path = rel.replace(/\.js$/, '');
        var src = content.toString();
        exports.add(doc, module_path, src);
        utils.setPropertyPath(exports.originalPaths, rel, p);
        callback()
    });
};

/**
 * Find all modules below or at a given path, recursing through subdirectories
 *
 * @param {String} p - the path to search
 * @param {Function} callback
 */

exports.find = async.memoize(function (p, callback) {
    utils.find(p, exports.filenameFilter(p), callback);
});

/**
 * Creates a filter used when searching for module files. This function tests
 * for a .js extension and omits hidden dot-preceeded filenames.
 *
 * @param {String} p - the path to the directory being searched
 * @returns {Function}
 */

exports.filenameFilter = function (p) {
    return function (f) {
        if (f === p) {
            return true;
        }
        var relpath = utils.relpath(f, p);
        // should not start with a '.'
        if (/^\.[^\/]?/.test(relpath)) {
            return false;
        }
        // should not contain a file or folder starting with a '.'
        if (/\/\./.test(relpath)) {
            return false;
        }
        // should have a .js extension
        if (!/\.js$/.test(f)) {
            return false;
        }
        return true;
    };
};

/**
 * Parses the exports.originalPaths object to return an array of filenames
 */

exports.getFilenames = function (obj) {
    var names = [];
    for (var k in obj) {
        if (typeof obj[k] === 'object') {
            names.concat(getFilenames(obj[k]).map(function (n) {
                return k + '/' + n;
            }));
        }
        else {
            names.push(k);
        }
    }
    return names;
};

/**
 * Loads a commonjs module from the loaded design document, returning
 * the exported properties. The current_dir and target parameters are not the
 * path of the module on the filesystem, but rather the path of the module
 * within couchdb, root ('/') being the design doc itself.
 *
 * @param {Object} module_cache
 * @param {Object} doc
 * @param {String} current_dir
 * @param {String} target
 * @param {Object} context - optional, extends sandbox object
 */

exports.require = function (module_cache, doc, current, target, context) {
    var current_dir = path.dirname(current);
    if (target.charAt(0) !== '.') {
        current_dir = '/';
    }
    var p = path.normalize(path.join(current_dir, target));
    if (module_cache[p]) {
        return module_cache[p];
    }

    var nodes = p.split('/').slice(1);
    var content = nodes.reduce(function (a, x) {
        if (a[x] === undefined) {
            throw new Error(
                //'Could not require module: ' + target + ' ' +
                'Could not require module: ' + p.replace(/^\//, '') + ' ' +
                '(from: ' + current.replace(/^\//, '') + ') - ' +
                'Module not found'
            );
        }
        a = a[x];
        return a;
    }, doc);

    var filename = utils.getPropertyPath(
        exports.originalPaths, p.substr(1), true
    );

    // if you attempt to require
    if (typeof filename === 'object') {
        var filenames = exports.getFilenames(filename);
        throw new Error(
            //'Could not require module: ' + target + ' ' +
            'Could not require module: ' + p.replace(/^\//, '') + ' ' +
            '(from: ' + current.replace(/^\//, '') + ')\n\n' +
            'It\'s not possible to require directories of modules in CouchDB,\n' +
            'you have to specify one of the following sub-modules directly:\n' +
            filenames.map(function (f) {
                return '  ' + p.replace(/^\//, '') + '/' + f;
            }).join('\n') + '\n'
        );
    }

    var module = {
        id: p.substr(1),
        exports: {},
        // TODO: this property is not provided by couchdb, but is by node:
        //filename: filename,
        // TODO: this property is not provided by couchdb, but is by node:
        //require: async.apply(exports.require, module_cache, doc, p)
        // TODO: module properties provided by couchdb, but not by kanso
        // * current
        // * parent
    };
    var sandbox = {
        module: module,
        exports: module.exports,
        //require: module.require,
        require: async.apply(exports.require, module_cache, doc, p),
        log: function () {
            console.log.apply(console, arguments);
        }
    };

    // copy context into sandbox
    if (context) {
        Object.keys(context).forEach(function (k) {
            sandbox[k] = context[k];
        });
    }

    // Create a placeholder for this module's exports so circular requires
    // are possible. TODO: node.js uses a loaded = false attribute on the
    // cached module object to mark this as a placeholder.
    module_cache[p] = {};

    try {
        var s = new Script(content, filename).runInNewContext(sandbox);
    }
    catch (e) {
        if (e instanceof SyntaxError && filename) {
            // gives a better syntax error than runInNewContext
            // with filename and line number
            require(filename);
        }
        if (!e.got_filename) {
            e.message = 'Error loading: ' + filename + '\n' + e.message;
            e.got_filename = true;
        }
        throw e;
    }
    module_cache[p] = sandbox.module.exports;
    return module_cache[p];
};
<|endoftext|>


<commit_before>/*
Language: PHP
Author: Victor Karamzin <Victor.Karamzin@enterra-inc.com>
*/

hljs.LANGUAGES.php = {
  case_insensitive: true,
  defaultMode: {
    keywords: {
      'and': 1, 'include_once': 1, 'list': 1, 'abstract': 1, 'global': 1,
      'private': 1, 'echo': 1, 'interface': 1, 'as': 1, 'static': 1,
      'endswitch': 1, 'array': 1, 'null': 1, 'if': 1, 'endwhile': 1, 'or': 1,
      'const': 1, 'for': 1, 'endforeach': 1, 'self': 1, 'var': 1, 'while': 1,
      'isset': 1, 'public': 1, 'protected': 1, 'exit': 1, 'foreach': 1,
      'throw': 1, 'elseif': 1, 'extends': 1, 'include': 1, '__FILE__': 1,
      'empty': 1, 'require_once': 1, 'function': 1, 'do': 1, 'xor': 1,
      'return': 1, 'implements': 1, 'parent': 1, 'clone': 1, 'use': 1,
      '__CLASS__': 1, '__LINE__': 1, 'else': 1, 'break': 1, 'print': 1,
      'eval': 1, 'new': 1, 'catch': 1, '__METHOD__': 1, 'class': 1, 'case': 1,
      'exception': 1, 'php_user_filter': 1, 'default': 1, 'die': 1,
      'require': 1, '__FUNCTION__': 1, 'enddeclare': 1, 'final': 1, 'try': 1,
      'this': 1, 'switch': 1, 'continue': 1, 'endfor': 1, 'endif': 1,
      'declare': 1, 'unset': 1, 'true': 1, 'false': 1, 'namespace': 1
    },
    contains: [
      hljs.C_LINE_COMMENT_MODE,
      hljs.HASH_COMMENT_MODE,
      {
        className: 'comment',
        begin: '/\\*', end: '\\*/',
        contains: [{
            className: 'phpdoc',
            begin: '\\s@[A-Za-z]+',
            relevance: 10
        }]
      },
      hljs.C_NUMBER_MODE,
      hljs.inherit(hljs.APOS_STRING_MODE, {illegal: null}),
      hljs.inherit(hljs.QUOTE_STRING_MODE, {illegal: null}),
      {
        className: 'variable',
        begin: '\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
      },
      {
        className: 'preprocessor',
        begin: '<\\?php',
        relevance: 10
      },
      {
        className: 'preprocessor',
        begin: '\\?>'
      }
    ]
  }
};
<commit_msg>Added missing keywords, constants and staff. Also number format was corrected<commit_after>/*
Language: PHP
Author: Victor Karamzin <Victor.Karamzin@enterra-inc.com>
*/

hljs.LANGUAGES.php = {
  case_insensitive: true,
  defaultMode: {
    keywords: {
      'and': 1, 'include_once': 1, 'list': 1, 'abstract': 1, 'global': 1,
      'private': 1, 'echo': 1, 'interface': 1, 'as': 1, 'static': 1,
      'endswitch': 1, 'array': 1, 'null': 1, 'if': 1, 'endwhile': 1, 'or': 1,
      'const': 1, 'for': 1, 'endforeach': 1, 'self': 1, 'var': 1, 'while': 1,
      'isset': 1, 'public': 1, 'protected': 1, 'exit': 1, 'foreach': 1,
      'throw': 1, 'elseif': 1, 'extends': 1, 'include': 1, '__FILE__': 1,
      'empty': 1, 'require_once': 1, 'function': 1, 'do': 1, 'xor': 1,
      'return': 1, 'implements': 1, 'parent': 1, 'clone': 1, 'use': 1,
      '__CLASS__': 1, '__LINE__': 1, 'else': 1, 'break': 1, 'print': 1,
      'eval': 1, 'new': 1, 'catch': 1, '__METHOD__': 1, 'class': 1, 'case': 1,
      'exception': 1, 'php_user_filter': 1, 'default': 1, 'die': 1,
      'require': 1, '__FUNCTION__': 1, 'enddeclare': 1, 'final': 1, 'try': 1,
      'this': 1, 'switch': 1, 'continue': 1, 'endfor': 1, 'endif': 1,
      'declare': 1, 'unset': 1, 'true': 1, 'false': 1, 'namespace': 1, 'trait':1,
      'goto':1, 'instanceof':1, '__DIR__':1, '__NAMESPACE__':1, '__halt_compiler':1
    },
    contains: [
      hljs.C_LINE_COMMENT_MODE,
      hljs.HASH_COMMENT_MODE,
      {
        className: 'comment',
        begin: '/\\*', end: '\\*/',
        contains: [{
            className: 'phpdoc',
            begin: '\\s@[A-Za-z]+',
            relevance: 10
        }]
      },
      {
        className: 'number',
        begin: '\\b(0b[01]+|0[xX]9[A-Za-z0-9]+|\\d+(\\.\\d+)?)',
        relevance: 0
      },
      hljs.C_NUMBER_MODE,
      hljs.inherit(hljs.APOS_STRING_MODE, {illegal: null}),
      hljs.inherit(hljs.QUOTE_STRING_MODE, {illegal: null}),
      {
        className: 'variable',
        begin: '\\$[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*'
      },
      {
        className: 'preprocessor',
        begin: '<\\?php',
        relevance: 10
      },
      {
        className: 'preprocessor',
        begin: '\\?>'
      }
    ]
  }
};
<|endoftext|>


<commit_before>(function () {
  var providers = {};

  L.TileLayer.Provider = L.TileLayer.extend({
    initialize: function (arg) {
      var parts = arg.split('.');

      var providerName = parts[0];
      var variantName = parts[1];

      if (!providers[providerName]) {
        throw "No such provider (" + providerName + ")";
      }

      var provider = {
        url: providers[providerName].url,
        options: providers[providerName].options
      };

      // overwrite values in provider from variant.
      if (variantName && 'variants' in providers[providerName]) {
        if (!(variantName in providers[providerName].variants)) {
          throw "No such name in provider (" + variantName + ")";
        }
        var variant = providers[providerName].variants[variantName];
        provider = {
          url: variant.url || provider.url,
          options: L.Util.extend({}, provider.options, variant.options)
        };
      }

      // replace attribution placeholders with their values from toplevel provider attribution.
      var attribution = provider.options.attribution;
      if (attribution.indexOf('{attribution.') != -1) {
        provider.options.attribution = attribution.replace(/\{attribution.(\w*)\}/,
          function(match, attributionName){
            return providers[attributionName].options.attribution;
          });
      }
      L.TileLayer.prototype.initialize.call(this, provider.url, provider.options);
    }
  });

  /**
   * Definition of providers.
   * see http://leafletjs.com/reference.html#tilelayer for options in the options map.
   */
  providers = {
    OpenStreetMap: {
      url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      options: {
        attribution: '© OpenStreetMap contributors, CC-BY-SA'
      },
      variants: {
        Mapnik: {},
        BlackAndWhite: {
          url: 'http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png'
        },
        DE: {
          url: 'http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png'
        }
      }
    },
    OpenCycleMap: {
      url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
      options: {
        attribution: '© OpenCycleMap, OpenStreetMap contributors, CC-BY-SA'
      }
    },
    Thunderforest: {
      url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
      options: {
        attribution: '{attribution.OpenCycleMap}'
      },
      variants: {
        OpenCycleMap: {},
        Transport: {
          url: 'http://{s}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png'
        },
        Landscape: {
          url: 'http://{s}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png'
        }
      }
    },
    MapQuestOpen: {
      url: 'http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png',
      options: {
        attribution: 'Tiles Courtesy of MapQuest — Map data {attribution.OpenStreetMap}',
        subdomains: '1234'
      },
      variants: {
        OSM: {},
        Aerial:{
          url: 'http://oatile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg',
          options: {
            attribution:
              'Tiles Courtesy of MapQuest — ' +
              'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency'
          }
        }
      }
    },
    MapBox: {
      url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-simple/{z}/{x}/{y}.png',
      options: {
        attribution: 'Imagery from MapBox — Map data {attribution.OpenStreetMap}',
        subdomains: 'abcd'
      },
      variants: {
        Simple: {},
        Streets: {
          url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-streets/{z}/{x}/{y}.png'
        },
        Light: {
          url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-light/{z}/{x}/{y}.png'
        },
        Lacquer: {
          url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-lacquer/{z}/{x}/{y}.png'
        },
        Warden: {
          url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-warden/{z}/{x}/{y}.png'
        }
      }
    },
    Stamen: {
      url: 'http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png',
      options: {
        attribution:
          'Map tiles by Stamen Design, CC BY 3.0 — ' +
          'Map data {attribution.OpenStreetMap}',
        subdomains: 'abcd',
        minZoom: 0,
        maxZoom: 20
      },
      variants: {
        Toner: {},
        TonerBackground: {
          url: 'http://{s}.tile.stamen.com/toner-background/{z}/{x}/{y}.png'
        },
        TonerHybrid: {
          url: 'http://{s}.tile.stamen.com/toner-hybrid/{z}/{x}/{y}.png'
        },
        TonerLines: {
          url: 'http://{s}.tile.stamen.com/toner-lines/{z}/{x}/{y}.png'
        },
        TonerLabels: {
          url: 'http://{s}.tile.stamen.com/toner-labels/{z}/{x}/{y}.png'
        },
        TonerLite: {
          url: 'http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png'
        },
        Terrain: {
          url: 'http://{s}.tile.stamen.com/terrain/{z}/{x}/{y}.png',
          options: {
            minZoom: 4,
            maxZoom: 18
          }
        },
        Watercolor: {
          url: 'http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.png',
          options: {
            minZoom: 3,
            maxZoom: 16
          }
        }
      }
    },
    Esri: {
      url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
      options: {
        attribution: 'Tiles © Esri'
      },
      variants: {
        WorldStreetMap: {},
        DeLorme: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            maxZoom: 11,
            attribution: '{attribution.Esri} — Copyright: ©2012 DeLorme'
          }
        },
        WorldTopoMap: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'
          }
        },
        WorldImagery: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
          }
        },
        OceanBasemap: {
          url: 'http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{z}/{y}/{x}',
          options: {
            maxZoom: 11,
            attribution: '{attribution.Esri} — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri'
          }
        },
        NatGeoWorldMap: {
          url: 'http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC'
          }
        }
      }
    }
  };
}());

L.TileLayer.provider = function(provider){
  return new L.TileLayer.Provider(provider);
};

L.Control.Layers.Provided = L.Control.Layers.extend({
    initialize: function(base, overlay, options){
        var out = {},
        len = base.length,
        i=0;
        while(i<len){
            if (i === 0) {
                this._first = L.TileLayer.provider(base[0]);
                out[base[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = this._first;
            } else {
                out[base[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = L.TileLayer.provider(base[i]);
            }
            i++;
        }
        L.Control.Layers.prototype.initialize.call(this, out, overlay, options);
    },
    onAdd: function(map){
        this._first.addTo(map);
        return L.Control.Layers.prototype.onAdd.call(this, map);
    }
});
L.control.layers.provided = function (baseLayers, overlays, options) {
    return new L.Control.Layers.Provided(baseLayers, overlays, options);
};
<commit_msg>works with overlays<commit_after>(function () {
  var providers = {};

  L.TileLayer.Provider = L.TileLayer.extend({
    initialize: function (arg) {
      var parts = arg.split('.');

      var providerName = parts[0];
      var variantName = parts[1];

      if (!providers[providerName]) {
        throw "No such provider (" + providerName + ")";
      }

      var provider = {
        url: providers[providerName].url,
        options: providers[providerName].options
      };

      // overwrite values in provider from variant.
      if (variantName && 'variants' in providers[providerName]) {
        if (!(variantName in providers[providerName].variants)) {
          throw "No such name in provider (" + variantName + ")";
        }
        var variant = providers[providerName].variants[variantName];
        provider = {
          url: variant.url || provider.url,
          options: L.Util.extend({}, provider.options, variant.options)
        };
      }

      // replace attribution placeholders with their values from toplevel provider attribution.
      var attribution = provider.options.attribution;
      if (attribution.indexOf('{attribution.') != -1) {
        provider.options.attribution = attribution.replace(/\{attribution.(\w*)\}/,
          function(match, attributionName){
            return providers[attributionName].options.attribution;
          });
      }
      L.TileLayer.prototype.initialize.call(this, provider.url, provider.options);
    }
  });

  /**
   * Definition of providers.
   * see http://leafletjs.com/reference.html#tilelayer for options in the options map.
   */
  providers = {
    OpenStreetMap: {
      url: 'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',
      options: {
        attribution: '© OpenStreetMap contributors, CC-BY-SA'
      },
      variants: {
        Mapnik: {},
        BlackAndWhite: {
          url: 'http://{s}.www.toolserver.org/tiles/bw-mapnik/{z}/{x}/{y}.png'
        },
        DE: {
          url: 'http://{s}.tile.openstreetmap.de/tiles/osmde/{z}/{x}/{y}.png'
        }
      }
    },
    OpenCycleMap: {
      url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
      options: {
        attribution: '© OpenCycleMap, OpenStreetMap contributors, CC-BY-SA'
      }
    },
    Thunderforest: {
      url: 'http://{s}.tile.opencyclemap.org/cycle/{z}/{x}/{y}.png',
      options: {
        attribution: '{attribution.OpenCycleMap}'
      },
      variants: {
        OpenCycleMap: {},
        Transport: {
          url: 'http://{s}.tile2.opencyclemap.org/transport/{z}/{x}/{y}.png'
        },
        Landscape: {
          url: 'http://{s}.tile3.opencyclemap.org/landscape/{z}/{x}/{y}.png'
        }
      }
    },
    MapQuestOpen: {
      url: 'http://otile{s}.mqcdn.com/tiles/1.0.0/osm/{z}/{x}/{y}.png',
      options: {
        attribution: 'Tiles Courtesy of MapQuest — Map data {attribution.OpenStreetMap}',
        subdomains: '1234'
      },
      variants: {
        OSM: {},
        Aerial:{
          url: 'http://oatile{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg',
          options: {
            attribution:
              'Tiles Courtesy of MapQuest — ' +
              'Portions Courtesy NASA/JPL-Caltech and U.S. Depart. of Agriculture, Farm Service Agency'
          }
        }
      }
    },
    MapBox: {
      url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-simple/{z}/{x}/{y}.png',
      options: {
        attribution: 'Imagery from MapBox — Map data {attribution.OpenStreetMap}',
        subdomains: 'abcd'
      },
      variants: {
        Simple: {},
        Streets: {
          url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-streets/{z}/{x}/{y}.png'
        },
        Light: {
          url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-light/{z}/{x}/{y}.png'
        },
        Lacquer: {
          url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-lacquer/{z}/{x}/{y}.png'
        },
        Warden: {
          url: 'http://{s}.tiles.mapbox.com/v3/mapbox.mapbox-warden/{z}/{x}/{y}.png'
        }
      }
    },
    Stamen: {
      url: 'http://{s}.tile.stamen.com/toner/{z}/{x}/{y}.png',
      options: {
        attribution:
          'Map tiles by Stamen Design, CC BY 3.0 — ' +
          'Map data {attribution.OpenStreetMap}',
        subdomains: 'abcd',
        minZoom: 0,
        maxZoom: 20
      },
      variants: {
        Toner: {},
        TonerBackground: {
          url: 'http://{s}.tile.stamen.com/toner-background/{z}/{x}/{y}.png'
        },
        TonerHybrid: {
          url: 'http://{s}.tile.stamen.com/toner-hybrid/{z}/{x}/{y}.png'
        },
        TonerLines: {
          url: 'http://{s}.tile.stamen.com/toner-lines/{z}/{x}/{y}.png'
        },
        TonerLabels: {
          url: 'http://{s}.tile.stamen.com/toner-labels/{z}/{x}/{y}.png'
        },
        TonerLite: {
          url: 'http://{s}.tile.stamen.com/toner-lite/{z}/{x}/{y}.png'
        },
        Terrain: {
          url: 'http://{s}.tile.stamen.com/terrain/{z}/{x}/{y}.png',
          options: {
            minZoom: 4,
            maxZoom: 18
          }
        },
        Watercolor: {
          url: 'http://{s}.tile.stamen.com/watercolor/{z}/{x}/{y}.png',
          options: {
            minZoom: 3,
            maxZoom: 16
          }
        }
      }
    },
    Esri: {
      url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer/tile/{z}/{y}/{x}',
      options: {
        attribution: 'Tiles © Esri'
      },
      variants: {
        WorldStreetMap: {},
        DeLorme: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            maxZoom: 11,
            attribution: '{attribution.Esri} — Copyright: ©2012 DeLorme'
          }
        },
        WorldTopoMap: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Esri, DeLorme, NAVTEQ, TomTom, Intermap, iPC, USGS, FAO, NPS, NRCAN, GeoBase, Kadaster NL, Ordnance Survey, Esri Japan, METI, Esri China (Hong Kong), and the GIS User Community'
          }
        },
        WorldImagery: {
          url: 'http://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
          }
        },
        OceanBasemap: {
          url: 'http://services.arcgisonline.com/ArcGIS/rest/services/Ocean_Basemap/MapServer/tile/{z}/{y}/{x}',
          options: {
            maxZoom: 11,
            attribution: '{attribution.Esri} — Sources: GEBCO, NOAA, CHS, OSU, UNH, CSUMB, National Geographic, DeLorme, NAVTEQ, and Esri'
          }
        },
        NatGeoWorldMap: {
          url: 'http://services.arcgisonline.com/ArcGIS/rest/services/NatGeo_World_Map/MapServer/tile/{z}/{y}/{x}',
          options: {
            attribution: '{attribution.Esri} — National Geographic, Esri, DeLorme, NAVTEQ, UNEP-WCMC, USGS, NASA, ESA, METI, NRCAN, GEBCO, NOAA, iPC'
          }
        }
      }
    }
  };
}());

L.TileLayer.provider = function(provider){
  return new L.TileLayer.Provider(provider);
};

L.Control.Layers.Provided = L.Control.Layers.extend({
    initialize: function(base, overlay, options){
        var first;
        if(base.length){
            (function(){
                var out = {},
                len = base.length,
                i=0;
                while(i<len){
                    if (i === 0) {
                        first = L.TileLayer.provider(base[0]);
                        out[base[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = first;
                    } else {
                        out[base[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = L.TileLayer.provider(base[i]);
                    }
                    i++;
                }
                base = out;
            }());
        this._first = first;
        }
        if(overlay.length){
            (function(){
                var out = {},
                len = overlay.length,
                i=0;
                while(i<len){
                    out[base[i].replace(/\./g,": ").replace(/([a-z])([A-Z])/g,"$1 $2")] = L.TileLayer.provider(base[i]);
                    i++;
                }
                overlay = out;
            }());
        }
        L.Control.Layers.prototype.initialize.call(this, base, overlay, options);
    },
    onAdd: function(map){
        this._first.addTo(map);
        return L.Control.Layers.prototype.onAdd.call(this, map);
    }
});
L.control.layers.provided = function (baseLayers, overlays, options) {
    return new L.Control.Layers.Provided(baseLayers, overlays, options);
};
<|endoftext|>


<commit_before>/*global jQuery */
var pyconde = (function($) {
    function createMultiuserSelectBox() {
        function createUserTrigger(data, selectBox) {
            var speakerPks = selectBox.data('speaker_pks');
            if (speakerPks[data.value]) {
                return;
            }
            var span = $('');
            var remove = $('x');
            var opt = data.el || $('<option>').val(data.value).text(data.label).attr('selected', 'selected').appendTo(selectBox);
            span.text(data.label);
            span.append(remove);
            speakerPks[data.value] = true;
            remove.click(function(evt) {
                evt.preventDefault();
                opt.remove();
                span.remove();
            });
            return span;
        }
        if ($.ui && $.ui.autocomplete) {
            $('select.multiselect-user').each(function() {
                var $that = $(this);
                var input = $('<input />').attr('type', 'text');
                var listing = $('
').addClass('ui-autocomplete-result');
                $that.data('speaker_pks', {});
                input.insertAfter($that);
                listing.insertAfter(input);
                $('option', $that).each(function() {
                    var $opt = $(this);
                    var value = $opt.val();
                    if (value === '') {
                        return;
                    }
                    listing.append(createUserTrigger({'label': $opt.text(), 'value': value, 'el': $opt}, $that));
                });
                input.autocomplete({source:'/accounts/ajax/users'}).unbind('autocompleteselect').bind('autocompleteselect', function(evt, ui) {
                    evt.preventDefault();
                    $(this).val('');
                    listing.append(createUserTrigger(ui.item, $that));
                });
                $that.hide();
            });
        }
    }
    function createSponsorSlides() {
        $('.cmsplugin-sponsorlist.split').each(function() {
            var container = $(this), height;
            var slideContainer = $(this).find('> div');
            var numSlides = slideContainer.find('ul').length;
            if (container.hasClass('slides-2rows')) {
                height = 150;
            } else if (container.hasClass('slides-3rows')){
                height = 225;
            } else {
                height = 75;
            }
            if (numSlides < 2) {
                return;
            }
            slideContainer.slidesjs({
                width: container.width(),
                height: 150,
                navigation: {active: false},
                effect: {fade: {speed: 1000}},
                pagination: {effect: 'fade'},
                play: {
                    effect: "fade",
                    auto: true,
                    interval: 5000,
                    pauseOnHover: true
                },
                callback: {
                    start: function(num) {
                        container.find('.slidesjs-pagination-item a').removeClass('active');
                        container.find('a[data-slidesjs-item=' + (num % numSlides) + ']').addClass('active');
                    },
                    loaded: function(num) {
                        container.find('a[data-slidesjs-item=' + (num-1) + ']').addClass('active');
                    }
                }
            });
        });
    }

    function init() {
        createSponsorSlides();
        $(createMultiuserSelectBox);
        $('div.navbar').mouseenter(function() {
            $('#dropout-menu').slideDown();
        });
        $('#dropout-menu').mouseleave(function() {
            $('#dropout-menu').slideUp();
        });
    }

    init();
})(jQuery);<commit_msg>Make dropout menu less hectic.<commit_after>/*global jQuery */
var pyconde = (function($) {
    function createMultiuserSelectBox() {
        function createUserTrigger(data, selectBox) {
            var speakerPks = selectBox.data('speaker_pks');
            if (speakerPks[data.value]) {
                return;
            }
            var span = $('');
            var remove = $('x');
            var opt = data.el || $('<option>').val(data.value).text(data.label).attr('selected', 'selected').appendTo(selectBox);
            span.text(data.label);
            span.append(remove);
            speakerPks[data.value] = true;
            remove.click(function(evt) {
                evt.preventDefault();
                opt.remove();
                span.remove();
            });
            return span;
        }
        if ($.ui && $.ui.autocomplete) {
            $('select.multiselect-user').each(function() {
                var $that = $(this);
                var input = $('<input />').attr('type', 'text');
                var listing = $('
').addClass('ui-autocomplete-result');
                $that.data('speaker_pks', {});
                input.insertAfter($that);
                listing.insertAfter(input);
                $('option', $that).each(function() {
                    var $opt = $(this);
                    var value = $opt.val();
                    if (value === '') {
                        return;
                    }
                    listing.append(createUserTrigger({'label': $opt.text(), 'value': value, 'el': $opt}, $that));
                });
                input.autocomplete({source:'/accounts/ajax/users'}).unbind('autocompleteselect').bind('autocompleteselect', function(evt, ui) {
                    evt.preventDefault();
                    $(this).val('');
                    listing.append(createUserTrigger(ui.item, $that));
                });
                $that.hide();
            });
        }
    }
    function createSponsorSlides() {
        $('.cmsplugin-sponsorlist.split').each(function() {
            var container = $(this), height;
            var slideContainer = $(this).find('> div');
            var numSlides = slideContainer.find('ul').length;
            if (container.hasClass('slides-2rows')) {
                height = 150;
            } else if (container.hasClass('slides-3rows')){
                height = 225;
            } else {
                height = 75;
            }
            if (numSlides < 2) {
                return;
            }
            slideContainer.slidesjs({
                width: container.width(),
                height: 150,
                navigation: {active: false},
                effect: {fade: {speed: 1000}},
                pagination: {effect: 'fade'},
                play: {
                    effect: "fade",
                    auto: true,
                    interval: 5000,
                    pauseOnHover: true
                },
                callback: {
                    start: function(num) {
                        container.find('.slidesjs-pagination-item a').removeClass('active');
                        container.find('a[data-slidesjs-item=' + (num % numSlides) + ']').addClass('active');
                    },
                    loaded: function(num) {
                        container.find('a[data-slidesjs-item=' + (num-1) + ']').addClass('active');
                    }
                }
            });
        });
    }

    function init() {
        createSponsorSlides();
        $(createMultiuserSelectBox);
        $('div.navbar').mouseenter(function() {
            $('#dropout-menu').slideDown();
        });
        $('#page').mouseenter(function() {
            $('#dropout-menu').slideUp();
        });
    }

    init();
})(jQuery);<|endoftext|>


<commit_before>L.Handler.PolylineHandles = L.Handler.extend({

    includes: L.Mixin.Events,

    options: {
        overlapRadius: 15, // pixels
        markerFactory: null,
        attachOnClick: true,
        detachOnClick: true
    },

    initialize: function (map) {
        L.Handler.prototype.initialize.call(this, map);

        if (map.almostOver === undefined) {
            throw 'Leaflet.AlmostOver handler is required on this map.';
        }
        this._dragging = false;
        this._attached = [];
        this._marker = null;
        this._snap = null;
        this._layers = [];
    },

    addHooks: function () {
        this._toggleAlmostEvent(true);
        this.on('attach', this._onAttach, this);
        this.on('detach', this._onDetach, this);
    },

    removeHooks: function () {
        this.off('attach', this._onAttach, this);
        this.off('detach', this._onDetach, this);
        this._toggleAlmostEvent(false);
    },

    addGuideLayer: function (l) {
        this._layers.push(l);
    },

    _toggleAlmostEvent: function (state) {
        var method = state ? 'on' : 'off';
        this._map[method]('almost:over', this._onAlmostOver, this);
        this._map[method]('almost:move', this._onAlmostMove, this);
        this._map[method]('almost:out', this._onAlmostOut, this);
        this._map[method]('almost:click', this._onAlmostClick, this);
    },

    _onAttach: function (e) {
        this._attached.push(e.marker);
    },

    _onDetach: function (e) {
        var idx = this._attached.indexOf(e.marker);
        this._attached.splice(idx, 1);
    },

    _onAlmostOver: function (e) {
        if (this.options.markerFactory) {
            this._marker = this.options.markerFactory(e.latlng);
        }
        else {
            // Default marker and icon
            var handleIcon = L.divIcon({className: 'handle-icon'});
            this._marker = L.marker(e.latlng, {icon: handleIcon});
        }
        this._marker.attached = false;
        this._marker.on('dragstart', this._onDragStart, this);
        this._marker.on('dragend', this._onDragEnd, this);
        this._marker.on('click', this._onClick, this);
    },

    _onAlmostMove: function (e) {
        // Hide current marker when approaching the ones already attached.
        for (var i=0, n=this._attached.length; i<n; i++) {
            var attached = this._attached[i],
                space = L.GeometryUtil.distance(this._map,
                                                attached.getLatLng(),
                                                e.latlng);
            if (space < this.options.overlapRadius) {
                this._map.removeLayer(this._marker);
                return;
            }
        }
        // No attached marker around
        if (i == n) {
            this._map.addLayer(this._marker);
            this._marker.dragging = new L.Handler.MarkerDrag(this._marker);
            this._marker.dragging.enable();
        }
        this._marker.setLatLng(e.latlng);
    },

    _onAlmostOut: function (e) {
        this._marker.off('dragstart', this._onDragStart, this);
        this._marker.off('dragend', this._onDragEnd, this);
        this._marker.off('click', this._onClick, this);
        this._map.removeLayer(this._marker);
        this._marker = null;
    },

    _onAlmostClick: function (e) {
        // this._marker should be set in almost:over
        console.assert(this._marker);
        this._marker.fire('click', e);
    },

    refreshMarker: function () {
        // Will retrigger almost:over
        this._map.fire('mousemovesample', {latlng: L.latLng([0, 0])});
    },

    // When marker starts dragging :
    //  - ignore map mouse moves
    //  - detach if was attached
    //  - initialize snapping if new marker
    _onDragStart: function (e) {
        this._toggleAlmostEvent(false);

        var marker = e.target;

        if (!!marker.attached) {
            this._detach(marker);
        }
        else {
            marker.snapediting = new L.Handler.MarkerSnap(this._map, marker, {snapVertices: false});
            for (var i=0, n=this._layers.length; i<n; i++) {
                marker.snapediting.addGuideLayer(this._layers[i]);
            }
        }
        marker.snapediting.enable();
        marker.on('snap', this._onSnap, this);
        marker.on('unsnap', this._onUnsnap, this);
        // Don't listen to click while dragging
        marker.off('click', this._onClick, this);
    },

    _onSnap: function (e) {
        this._snap = e.layer;
    },

    _onUnsnap: function (e) {
        this._snap = null;
    },

    // When marker dragging stops :
    //  - 'attach' and start again if marker was snapped
    //  - 'detach' if marker was previously attached
    _onDragEnd: function (e) {
        var marker = e.target;
        marker.snapediting.disable();
        marker.off('snap', this._onSnap, this);
        marker.off('unsnap', this._onUnsnap, this);

        if (this._snap) {
            this._attach(marker, this._snap);
            // Start over with new marker
            this._onAlmostOver({latlng: marker.getLatLng()});
        }
        else {
            this._map.removeLayer(marker);
        }
        this._toggleAlmostEvent(true);
    },

    _attach: function (marker, layer) {
        marker.attached = true;
        this.fire('attach', {marker: marker, layer: layer});
        if (marker._icon) L.DomUtil.addClass(marker._icon, 'marker-attached');

        // Detach on click
        // Since mouse-up fires click at 'dragend'. Wait.
        if (!!this.options.detachOnClick) {
            setTimeout(L.Util.bind(function () {
                marker.on('click', function (e) {
                    this._detach(marker);
                    this._map.removeLayer(e.target);
                }, this);
            }, this), 100);
        }
    },

    _detach: function (marker) {
        marker.attached = false;
        this.fire('detach', {marker: marker});
        if (marker._icon) L.DomUtil.removeClass(marker._icon, 'marker-attached');
    },

    _onClick: function (e) {
        if (!!this.options.attachOnClick) {
            // Simulate drag-snap on click
            var marker = e.target;
            this._onDragStart(e);
            marker.fire('move');
            this._onDragEnd(e);
        }
    },
});

L.Handler.PolylineHandles.singleton = function (map) {
    if (this.__polylineHandles)
        return this.__polylineHandles;
    this.__polylineHandles = new L.Handler.PolylineHandles(map);
    return this.__polylineHandles;
};

L.Polyline.mergeOptions({
    polylineHandles: false
});

L.PolylineHandlesMixin = {
    __onAdd: L.Polyline.prototype.onAdd,
    __onRemove: L.Polyline.prototype.onRemove,

    onAdd: function (map) {
        this.__onAdd.call(this, map);

        this.polylineHandles = L.Handler.PolylineHandles.singleton(map);
        if (this.options.polylineHandles) {
            this.polylineHandles.enable();
        }
    },

    onRemove: function (map) {
        this.polylineHandles.disable();
        this.__onRemove.call(this, map);
    },
};

L.Polyline.include(L.PolylineHandlesMixin);
<commit_msg>Fire grab on layers<commit_after>L.Handler.PolylineHandles = L.Handler.extend({

    includes: L.Mixin.Events,

    options: {
        overlapRadius: 15, // pixels
        markerFactory: null,
        attachOnClick: true,
        detachOnClick: true
    },

    initialize: function (map) {
        L.Handler.prototype.initialize.call(this, map);

        if (map.almostOver === undefined) {
            throw 'Leaflet.AlmostOver handler is required on this map.';
        }
        this._dragging = false;
        this._attached = [];
        this._marker = null;
        this._snap = null;
        this._layers = [];
    },

    addHooks: function () {
        this._toggleAlmostEvent(true);
        this.on('attach', this._onAttach, this);
        this.on('detach', this._onDetach, this);
    },

    removeHooks: function () {
        this.off('attach', this._onAttach, this);
        this.off('detach', this._onDetach, this);
        this._toggleAlmostEvent(false);
    },

    addGuideLayer: function (l) {
        this._layers.push(l);
    },

    _toggleAlmostEvent: function (state) {
        var method = state ? 'on' : 'off';
        this._map[method]('almost:over', this._onAlmostOver, this);
        this._map[method]('almost:move', this._onAlmostMove, this);
        this._map[method]('almost:out', this._onAlmostOut, this);
        this._map[method]('almost:click', this._onAlmostClick, this);
    },

    _onAttach: function (e) {
        this._attached.push(e.marker);
    },

    _onDetach: function (e) {
        var idx = this._attached.indexOf(e.marker);
        this._attached.splice(idx, 1);
    },

    _onAlmostOver: function (e) {
        if (this.options.markerFactory) {
            this._marker = this.options.markerFactory(e.latlng);
        }
        else {
            // Default marker and icon
            var handleIcon = L.divIcon({className: 'handle-icon'});
            this._marker = L.marker(e.latlng, {icon: handleIcon});
        }
        this._marker.attached = false;
        this._marker.on('dragstart', this._onDragStart, this);
        this._marker.on('dragend', this._onDragEnd, this);
        this._marker.on('click', this._onClick, this);
    },

    _onAlmostMove: function (e) {
        // Hide current marker when approaching the ones already attached.
        for (var i=0, n=this._attached.length; i<n; i++) {
            var attached = this._attached[i],
                space = L.GeometryUtil.distance(this._map,
                                                attached.getLatLng(),
                                                e.latlng);
            if (space < this.options.overlapRadius) {
                this._map.removeLayer(this._marker);
                return;
            }
        }
        // No attached marker around
        if (i == n) {
            this._map.addLayer(this._marker);
            this._marker.dragging = new L.Handler.MarkerDrag(this._marker);
            this._marker.dragging.enable();
        }
        this._marker.from = e.layer;
        this._marker.setLatLng(e.latlng);
    },

    _onAlmostOut: function (e) {
        this._marker.off('dragstart', this._onDragStart, this);
        this._marker.off('dragend', this._onDragEnd, this);
        this._marker.off('click', this._onClick, this);
        this._map.removeLayer(this._marker);
        this._marker = null;
    },

    _onAlmostClick: function (e) {
        // this._marker should be set in almost:over
        console.assert(this._marker);
        this._marker.fire('click', e);
    },

    refreshMarker: function () {
        // Will retrigger almost:over
        this._map.fire('mousemovesample', {latlng: L.latLng([0, 0])});
    },

    // When marker starts dragging :
    //  - ignore map mouse moves
    //  - detach if was attached
    //  - initialize snapping if new marker
    _onDragStart: function (e) {
        this._toggleAlmostEvent(false);

        var marker = e.target;

        if (!!marker.attached) {
            this._detach(marker);
        }
        else {
            marker.snapediting = new L.Handler.MarkerSnap(this._map, marker, {snapVertices: false});
            for (var i=0, n=this._layers.length; i<n; i++) {
                marker.snapediting.addGuideLayer(this._layers[i]);
            }
        }

        marker.from.fire('grab', {marker: marker});

        marker.snapediting.enable();
        marker.on('snap', this._onSnap, this);
        marker.on('unsnap', this._onUnsnap, this);
        // Don't listen to click while dragging
        marker.off('click', this._onClick, this);
    },

    _onSnap: function (e) {
        this._snap = e.layer;
    },

    _onUnsnap: function (e) {
        this._snap = null;
    },

    // When marker dragging stops :
    //  - 'attach' and start again if marker was snapped
    //  - 'detach' if marker was previously attached
    _onDragEnd: function (e) {
        var marker = e.target;
        marker.snapediting.disable();
        marker.off('snap', this._onSnap, this);
        marker.off('unsnap', this._onUnsnap, this);

        if (this._snap) {
            this._attach(marker, this._snap);
            // Start over with new marker
            this._onAlmostOver({latlng: marker.getLatLng()});
        }
        else {
            this._map.removeLayer(marker);
        }
        this._toggleAlmostEvent(true);
    },

    _attach: function (marker, layer) {
        marker.attached = true;
        this.fire('attach', {marker: marker,
                             from: marker.from,
                             layer: layer});
        if (marker._icon) L.DomUtil.addClass(marker._icon, 'marker-attached');

        // Detach on click
        // Since mouse-up fires click at 'dragend'. Wait.
        if (!!this.options.detachOnClick) {
            setTimeout(L.Util.bind(function () {
                marker.on('click', function (e) {
                    this._detach(marker);
                    this._map.removeLayer(e.target);
                }, this);
            }, this), 100);
        }
    },

    _detach: function (marker) {
        marker.attached = false;
        this.fire('detach', {marker: marker});
        if (marker._icon) L.DomUtil.removeClass(marker._icon, 'marker-attached');
    },

    _onClick: function (e) {
        if (!!this.options.attachOnClick) {
            // Simulate drag-snap on click
            var marker = e.target;
            this._onDragStart(e);
            marker.fire('move');
            this._onDragEnd(e);
        }
    },
});

L.Handler.PolylineHandles.singleton = function (map) {
    if (this.__polylineHandles)
        return this.__polylineHandles;
    this.__polylineHandles = new L.Handler.PolylineHandles(map);
    return this.__polylineHandles;
};

L.Polyline.mergeOptions({
    polylineHandles: false
});

L.PolylineHandlesMixin = {
    __onAdd: L.Polyline.prototype.onAdd,
    __onRemove: L.Polyline.prototype.onRemove,

    onAdd: function (map) {
        this.__onAdd.call(this, map);

        this.polylineHandles = L.Handler.PolylineHandles.singleton(map);
        if (this.options.polylineHandles) {
            this.polylineHandles.enable();
        }
    },

    onRemove: function (map) {
        this.polylineHandles.disable();
        this.__onRemove.call(this, map);
    },
};

L.Polyline.include(L.PolylineHandlesMixin);
<|endoftext|>


<commit_before>;(function() {

Editor = function() {}

Editor.prototype = {
  init: function(elem, data) {
    // UI elements
    this.$container      = elem;
    this.$itemsContainer = elem.find('[data-editor-items]');
    this.$title          = elem.find('[data-editor-title]');
    this.$subtitle       = elem.find('[data-editor-subtitle]');
    this.$hero_image     = elem.find('[hero-background]');
    this.$hero_gif_search= elem.find('[hero-gif-search]');
    this.$addButton      = elem.find('[data-editor-add]');

    console.log(this.$title);
    this.lastSearch = '';

    // Content item template
    var template = $('#content-template').html();
    this.contentItem = Handlebars.compile(template);

    // Add event listeners
    this.events();

    if (typeof data === 'object') {
      this.deserialize(data);
    }
    else {
        // add gif searcher
        var $contentItem = $(this.contentItem(data || {}));
        $contentItem.find('.gif-searcher').clone().appendTo(this.$hero_gif_search);
        var gif_searcher = new GIFSearcher(this.$hero_gif_search.find('.gif-searcher'), this);
        var _this = this;
        gif_searcher.showImage = function(src) {
            gif_searcher.views.search.hide();
            gif_searcher.views.results.hide();
            gif_searcher.views.image.hide();

            if (typeof src !== 'undefined') {
                _this.$hero_image.attr('src', src);
            }
        };
      // this.addItem();
    }
  },
  events: function() {
    var _this = this;

    // Make each item sortable
    // this.$itemsContainer.sortable({
    //   handle: '[data-editor-handle]',
    //   placeholder: 'editor__placeholder'
    // });

    // Block carriage returns on title editor, blur the field instead
    this.$title.on('keypress', function(event) {
      if (event.which === 13) {
        $(this).blur();
      }
    });

    // Add text or image units when those buttons are clicked
    this.$addButton.click(function() {
      _this.addItem();
    });

    // Delete an item when that button is clicked
    this.$itemsContainer.on('click', '[data-editor-delete]', function() {
      _this.deleteItem($(this));
    });

  },
  addItem: function(data) {
    var _this = this;
    var $contentItem = $(this.contentItem(data || {}));
    $contentItem
      .appendTo(this.$itemsContainer)
      .hide(0)
      .slideDown(250);
    var gif_searcher = new GIFSearcher($contentItem.find('.gif-searcher'), this);

    this.$itemsContainer.sortable({
      handle: '[data-editor-handle]',
      placeholder: 'editor__placeholder'
    });

    $('textarea:visible').expanding();

    cb_suggest = function(event) {
        if (event.which == '.'.charCodeAt(0) || event.which == '?'.charCodeAt(0) || event.which == "!".charCodeAt(0) || event.which == " ".charCodeAt(0)) {
            var content = $contentItem.find('.content-item__title').val() + ' . ' + $contentItem.find('.content-item__body').val();
            var request = $.getJSON('http://guarded-caverns-4613.herokuapp.com/ner', {text: content});
            request.done(function(data) {
                    var ul = $contentItem.find('.gif-searcher__suggest > ul');
                    ul.empty();
                    $.each(data.best_guess, function(index, item) {
                            var list_item = $('
'+item+'
');
                            ul.append(list_item);
                            list_item.click(function() {
                                    gif_searcher.getImages(item)
                            });
                    });
                });
        }};
     $contentItem.find('.content-item__body').on('keypress', cb_suggest);
     $contentItem.find('.content-item__title').on('keypress', cb_suggest);
  },
  deleteItem: function(elem) {
    elem.closest('.content-item').slideUp(400, function(){
      $(this).remove();
    });
  },
  serialize: function() {
    var json = [];
    this.$itemsContainer.find('.content-item').each(function() {
      json.push({
        'title': $(this).find('.content-item__title').val(),
        'image': $(this).find('.gif-searcher__show > img').attr('src'),
        'body': $(this).find('.content-item__body').val()
      });
    });

    return json;
  },
  save: function() {
    return this.serialize();
  },
  deserialize: function(data) {
    var _this = this;

    $.each(data, function(index, item) {
      _this.addItem(item);
    });
  }
}

var GIFSearcher = function($elem, editor) {
  this.$container = $elem;

  this.views = {
    search:  this.$container.children('.gif-searcher__search'),
    results: this.$container.children('.gif-searcher__results'),
    image:   this.$container.children('.gif-searcher__show'),
    suggest: this.$container.children('.gif-searcher__suggest')
  }
  this.query = '';

  this.editor = editor;

  this.events();

  if (typeof this.views.search.children('img').attr('src') === 'undefined') {
    this.showSearch();
  }
  else {
    this.showImage();
  }
}
GIFSearcher.prototype = {
  events: function() {
    var _this = this;

    this.views.search.children('input').on('keypress', function(event) {
      if (event.which !== 13) return;

      var searchTerm = this.value;
      var search = _this.getImages(searchTerm);
    });

    this.views.results.on('click', 'li', function() {
      var src = $(this).children('img').attr('src');
      _this.showImage(src);
    });

    this.views.image.find('.delete').click(function() {
      _this.views.image.children('img').attr('src', '');
      _this.showSearch();
    });
  },

  showSearch: function() {
    this.views.search.show();
    this.views.results.hide();
    this.views.image.hide();

    if (this.editor.lastSearch) {
      this.views.search.children('input').val(this.editor.lastSearch);
    }
  },
  showResults: function(data) {
    var _this = this;

    this.views.search.hide();
    this.views.results.show();
    this.views.image.hide();

    // If there's no data, do a search for some
    if (typeof data === 'undefined') {

    }
    else {
      var $container = this.views.results.children('ul').eq(0);
      $container.empty();
      $.each(data.data, function(i, image) {
        var $item = $('

');
        $container.append($item);
      });
    }
  },
  showImage: function(src) {
    this.views.search.hide();
    this.views.results.hide();
    this.views.image.show();

    if (typeof src !== 'undefined') {
      this.views.image.children('img').attr('src', src);
    }
  },

  getImages: function(query) {
        var _this = this;
        fn = function() {
            // Set a default
            if (typeof query === 'undefined' || query == '') query = this.query || 'cats';
            // Encode it
            query = encodeURI(query.replace(" ","+"));

            var queryURL = 'http://api.giphy.com/v1/gifs/search'
            , params   = {q: query, limit: 9, api_key: 'dc6zaTOxFJmzC'}

            var search = $.getJSON(queryURL, params);
            return search;
        }().done(function(data, textStatus, jqxhr) {
                if (data.data.length === 0) {
                    _this.views.search.append('
Couldn\'t find any images :( Try again?');
                }
                else {
                    _this.editor.lastSearch = query;
                    _this.showResults(data);
                }
            })
        .fail(function(jqxhr, textStatus, error) {
                console.log(textStatus+', '+error);
            });
  }
}

})();<commit_msg>Changed header gif text<commit_after>;(function() {

Editor = function() {}

Editor.prototype = {
  init: function(elem, data) {
    // UI elements
    this.$container      = elem;
    this.$itemsContainer = elem.find('[data-editor-items]');
    this.$title          = elem.find('[data-editor-title]');
    this.$subtitle       = elem.find('[data-editor-subtitle]');
    this.$hero_image     = elem.find('[hero-background]');
    this.$hero_gif_search= elem.find('[hero-gif-search]');
    this.$addButton      = elem.find('[data-editor-add]');

    console.log(this.$title);
    this.lastSearch = '';

    // Content item template
    var template = $('#content-template').html();
    this.contentItem = Handlebars.compile(template);

    // Add event listeners
    this.events();

    if (typeof data === 'object') {
      this.deserialize(data);
    }
    else {
        // add gif searcher
        var $contentItem = $(this.contentItem(data || {}));
        $contentItem.find('.gif-searcher').clone().appendTo(this.$hero_gif_search);
        this.$hero_gif_search.find('.gif-searcher__search > input').attr('placeholder', "Add a header GIF");
        var gif_searcher = new GIFSearcher(this.$hero_gif_search.find('.gif-searcher'), this);
        var _this = this;
        gif_searcher.showImage = function(src) {
            gif_searcher.views.search.hide();
            gif_searcher.views.results.hide();
            gif_searcher.views.image.hide();

            if (typeof src !== 'undefined') {
                _this.$hero_image.attr('src', src);
            }
        };
      // this.addItem();
    }
  },
  events: function() {
    var _this = this;

    // Make each item sortable
    // this.$itemsContainer.sortable({
    //   handle: '[data-editor-handle]',
    //   placeholder: 'editor__placeholder'
    // });

    // Block carriage returns on title editor, blur the field instead
    this.$title.on('keypress', function(event) {
      if (event.which === 13) {
        $(this).blur();
      }
    });

    // Add text or image units when those buttons are clicked
    this.$addButton.click(function() {
      _this.addItem();
    });

    // Delete an item when that button is clicked
    this.$itemsContainer.on('click', '[data-editor-delete]', function() {
      _this.deleteItem($(this));
    });

  },
  addItem: function(data) {
    var _this = this;
    var $contentItem = $(this.contentItem(data || {}));
    $contentItem
      .appendTo(this.$itemsContainer)
      .hide(0)
      .slideDown(250);
    var gif_searcher = new GIFSearcher($contentItem.find('.gif-searcher'), this);

    this.$itemsContainer.sortable({
      handle: '[data-editor-handle]',
      placeholder: 'editor__placeholder'
    });

    $('textarea:visible').expanding();

    cb_suggest = function(event) {
        if (event.which == '.'.charCodeAt(0) || event.which == '?'.charCodeAt(0) || event.which == "!".charCodeAt(0) || event.which == " ".charCodeAt(0)) {
            var content = $contentItem.find('.content-item__title').val() + ' . ' + $contentItem.find('.content-item__body').val();
            var request = $.getJSON('http://guarded-caverns-4613.herokuapp.com/ner', {text: content});
            request.done(function(data) {
                    var ul = $contentItem.find('.gif-searcher__suggest > ul');
                    ul.empty();
                    $.each(data.best_guess, function(index, item) {
                            var list_item = $('
'+item+'
');
                            ul.append(list_item);
                            list_item.click(function() {
                                    gif_searcher.getImages(item)
                            });
                    });
                });
        }};
     $contentItem.find('.content-item__body').on('keypress', cb_suggest);
     $contentItem.find('.content-item__title').on('keypress', cb_suggest);
  },
  deleteItem: function(elem) {
    elem.closest('.content-item').slideUp(400, function(){
      $(this).remove();
    });
  },
  serialize: function() {
    var json = [];
    this.$itemsContainer.find('.content-item').each(function() {
      json.push({
        'title': $(this).find('.content-item__title').val(),
        'image': $(this).find('.gif-searcher__show > img').attr('src'),
        'body': $(this).find('.content-item__body').val()
      });
    });

    return json;
  },
  save: function() {
    return this.serialize();
  },
  deserialize: function(data) {
    var _this = this;

    $.each(data, function(index, item) {
      _this.addItem(item);
    });
  }
}

var GIFSearcher = function($elem, editor) {
  this.$container = $elem;

  this.views = {
    search:  this.$container.children('.gif-searcher__search'),
    results: this.$container.children('.gif-searcher__results'),
    image:   this.$container.children('.gif-searcher__show'),
    suggest: this.$container.children('.gif-searcher__suggest')
  }
  this.query = '';

  this.editor = editor;

  this.events();

  if (typeof this.views.search.children('img').attr('src') === 'undefined') {
    this.showSearch();
  }
  else {
    this.showImage();
  }
}
GIFSearcher.prototype = {
  events: function() {
    var _this = this;

    this.views.search.children('input').on('keypress', function(event) {
      if (event.which !== 13) return;

      var searchTerm = this.value;
      var search = _this.getImages(searchTerm);
    });

    this.views.results.on('click', 'li', function() {
      var src = $(this).children('img').attr('src');
      _this.showImage(src);
    });

    this.views.image.find('.delete').click(function() {
      _this.views.image.children('img').attr('src', '');
      _this.showSearch();
    });
  },

  showSearch: function() {
    this.views.search.show();
    this.views.results.hide();
    this.views.image.hide();

    if (this.editor.lastSearch) {
      this.views.search.children('input').val(this.editor.lastSearch);
    }
  },
  showResults: function(data) {
    var _this = this;

    this.views.search.hide();
    this.views.results.show();
    this.views.image.hide();

    // If there's no data, do a search for some
    if (typeof data === 'undefined') {

    }
    else {
      var $container = this.views.results.children('ul').eq(0);
      $container.empty();
      $.each(data.data, function(i, image) {
        var $item = $('

');
        $container.append($item);
      });
    }
  },
  showImage: function(src) {
    this.views.search.hide();
    this.views.results.hide();
    this.views.image.show();

    if (typeof src !== 'undefined') {
      this.views.image.children('img').attr('src', src);
    }
  },

  getImages: function(query) {
        var _this = this;
        fn = function() {
            // Set a default
            if (typeof query === 'undefined' || query == '') query = this.query || 'cats';
            // Encode it
            query = encodeURI(query.replace(" ","+"));

            var queryURL = 'http://api.giphy.com/v1/gifs/search'
            , params   = {q: query, limit: 9, api_key: 'dc6zaTOxFJmzC'}

            var search = $.getJSON(queryURL, params);
            return search;
        }().done(function(data, textStatus, jqxhr) {
                if (data.data.length === 0) {
                    _this.views.search.append('
Couldn\'t find any images :( Try again?');
                }
                else {
                    _this.editor.lastSearch = query;
                    _this.showResults(data);
                }
            })
        .fail(function(jqxhr, textStatus, error) {
                console.log(textStatus+', '+error);
            });
  }
}

})();<|endoftext|>


<commit_before>var redis = require('redis')
  // , util = require('util')
  , request = require('request')
  , npid = require('./lib/pid')
  , parser = require('./lib/parser')
  , cfg = require('./cfg/cfg')
  , ss_address = cfg.ss_address
  , verbose = cfg.verbose;
var subscriberClient = redis.createClient(8005);

// Create a pidfile with the worker's ID and pid
try {
  npid.create(__dirname + '/worker-' + process.pid + '.pid', true);
} catch (err) {
  console.log(err);
  process.exit(1);
}

process.on('SIGTERM', exit);
process.on('SIGINT', exit);
// process.on('SIGKILL', exit); // This breaks in node v0.10.x

// This is for removing the pidfile when nodemon restarts due to changes
process.once('SIGUSR2', function() {
  npid.remove(__dirname + '/worker-' + process.pid + '.pid');
  process.nextTick(function() {
    process.kill(process.pid, 'SIGUSR2');
  });
});

function exit (code) {
  process.nextTick(function() {
    process.exit(code || 0);
  });
}

function LogStreamHandler (tf2server, redisClient) {
  this.tf2server = tf2server;
  this.redisClient = redisClient;
  this.parser = new parser(cfg.parser_options);
  this.requestIsInProgress = false;
  this.updateTimer = 0;

  this.setupListeners();
}

LogStreamHandler.prototype.setupListeners = function () {
  var self = this;

  this.parser.on('got sessionid', function (sessionId) {
    self.sessionId = sessionId;
    self.getMatchId(sessionId, function (err, matchId) {
      if (err) {return console.trace(err);}
      verbose && console.log('got matchId', matchId);
      self.matchId = matchId;
      self.startStreamingStats();
    });
  });

  this.parser.on('round end', function (stats) {
    // Check for matchId
    var matchId = self.matchId;
    if (!matchId) {
      verbose && console.log('aborting round-end-update, no matchId');
      return false;
    }

    verbose && console.log('round ended');
    self.requestIsInProgress = true;

    request.post({uri: ss_address + '/api/log/stats/roundover'
                , json: {stats: stats}
                , headers: {sessionid: self.sessionId}
                , timeout: 3000
                }, function (err, res, body) {
      if (err) {
        return console.log(err);
      }
      if (res.statusCode > 202) {
        console.log(new Error('API Error: ' + res.statusCode));
      }

      verbose && console.log(res.body);
      self.requestIsInProgress = false;
    });
  });

  this.parser.on('match end', function (chats, matchDuration) {
    // verbose && console.log('match ended:', matchDuration);

    // request.post({uri: ss_address + '/api/log/stats/gameover'
    //             , json: {chats: chats}
    //             , headers: {sessionid: self.sessionId, matchduration: matchDuration}
    //             , timeout: 3000
    //             }, function (err, res, body) {
    //   // Error checking
    //   if (err) {
    //     console.log(err);
    //   }
    //   if (res.statusCode > 202) {
    //     console.log(new Error('API Error: ' + res.statusCode));
    //   }

    //   verbose && console.log(res.body);
      self.sessionid = null;
    // });
  });
};

LogStreamHandler.prototype.sendStatsUpdate = function (stats) {
  var self = this;
  // Check for matchId
  var matchId = self.matchId;
  if (!matchId) {
    verbose && console.log('aborting stats update, no matchId');
    return false;
  }

  verbose && console.log('stats update');
  if (self.requestIsInProgress) {
    return false;
  }

  self.requestIsInProgress = true;

  stats._id = matchId;

  request.post({uri: ss_address + '/api/log/stats/update'
              , json: {stats: stats}
              , timeout: 3000
              }, function (err, res, body) {
    if (err) {
      console.log(err);
    }
    if (res.statusCode > 202) {
      console.log(new Error('API Error: ' + res.statusCode));
    }

    verbose && console.log(res.body);
    self.requestIsInProgress = false;
  });
};

LogStreamHandler.prototype.startStreamingStats = function () {
  var self = this;
  self.updateTimer = setInterval(function () {
    self.sendStatsUpdate(self.parser.trustedFormattedStats());
  }, cfg.stats_update_rate);
};

LogStreamHandler.prototype.stopStreamingStats = function () {
  clearTimeout(this.updateTimer);
};

LogStreamHandler.prototype.getMatchId = function (sessionId, cb) {
  var self = this;
  self.requestIsInProgress = true;

  request.post({uri: ss_address + '/api/log/matchid'
              , headers: {sessionid: sessionId}
              , timeout: 3000
              }, function (err, res, body) {
    self.requestIsInProgress = false;

    if (err) {return cb(err);}
    if (res.statusCode !== 200) {
      return cb(new Error('Error checking sessionid: ' + res.statusCode));
    }

    cb(null, parseInt(res.headers.matchid, 10));
  });
};

LogStreamHandler.prototype.destroy = function () {
  var self = this;
  var redisCounterKey = 'counter' + self.tf2server;

  self.redisClient.multi()
    .del(redisCounterKey)
    .del(self.tf2server)
    .exec(function (err, replies) {
      if (err) {console.trace(err);}
      self.redisClient.quit(function (err) {
        if (err) {console.trace(err);}
        delete redisClients[self.tf2server];
        delete logStreamHandlers[self.tf2server];
        verbose && console.log(Object.keys(logStreamHandlers).length, 'logstream handlers remaining.');
        verbose && console.log(Object.keys(redisClients).length, 'redis clients remaining.');
      });
    });
};

var logStreamHandlers = {};
var redisClients = {};

// param `channel` is 'logmessage'
subscriberClient.on('message', function (channel, tf2server) {
  var logStreamHandler = logStreamHandlers[tf2server];
  var client = redisClients[tf2server];

  if (!client) {
    client = redisClients[tf2server] = redis.createClient(cfg.redis_port, cfg.redis_host);
  }
  if (!logStreamHandler) {
    logStreamHandler = logStreamHandlers[tf2server] = new LogStreamHandler(tf2server, client);
    console.log('Creating new parser for list', tf2server);
  }

  var readMessage = function () {
    client.brpop(tf2server, cfg.loghandler_timeout, function (err, messageBuffer) {
      if (err) {return console.log(err);}
      if (!messageBuffer) {
        // Timeout expired, so handle that
        verbose && console.log('Listener for', tf2server, 'timed out.');
        return logStreamHandler.destroy();
      }
      if (!messageBuffer.length || messageBuffer.length < 2) {
        return console.log('length < 2???', messageBuffer);
      }
      message = messageBuffer[1].toString('ascii').slice(5,-1);
      logStreamHandler.parser.parse(message);

      process.nextTick(readMessage);
    });
  };
  readMessage();

});

subscriberClient.subscribe('logmessage');
<commit_msg>Fixed typo.<commit_after>var redis = require('redis')
  // , util = require('util')
  , request = require('request')
  , npid = require('./lib/pid')
  , parser = require('./lib/parser')
  , cfg = require('./cfg/cfg')
  , ss_address = cfg.ss_address
  , verbose = cfg.verbose;
var subscriberClient = redis.createClient(8005);

// Create a pidfile with the worker's ID and pid
try {
  npid.create(__dirname + '/worker-' + process.pid + '.pid', true);
} catch (err) {
  console.log(err);
  process.exit(1);
}

process.on('SIGTERM', exit);
process.on('SIGINT', exit);
// process.on('SIGKILL', exit); // This breaks in node v0.10.x

// This is for removing the pidfile when nodemon restarts due to changes
process.once('SIGUSR2', function() {
  npid.remove(__dirname + '/worker-' + process.pid + '.pid');
  process.nextTick(function() {
    process.kill(process.pid, 'SIGUSR2');
  });
});

function exit (code) {
  process.nextTick(function() {
    process.exit(code || 0);
  });
}

function LogStreamHandler (tf2server, redisClient) {
  this.tf2server = tf2server;
  this.redisClient = redisClient;
  this.parser = new parser(cfg.parser_options);
  this.requestIsInProgress = false;
  this.updateTimer = 0;

  this.setupListeners();
}

LogStreamHandler.prototype.setupListeners = function () {
  var self = this;

  this.parser.on('got sessionid', function (sessionId) {
    self.sessionId = sessionId;
    self.getMatchId(sessionId, function (err, matchId) {
      if (err) {return console.trace(err);}
      verbose && console.log('got matchId', matchId);
      self.matchId = matchId;
      self.startStreamingStats();
    });
  });

  this.parser.on('round end', function (stats) {
    // Check for matchId
    var matchId = self.matchId;
    if (!matchId) {
      verbose && console.log('aborting round-end-update, no matchId');
      return false;
    }

    verbose && console.log('round ended');
    self.requestIsInProgress = true;

    request.post({uri: ss_address + '/api/log/stats/roundover'
                , json: {stats: stats}
                , headers: {sessionid: self.sessionId}
                , timeout: 3000
                }, function (err, res, body) {
      if (err) {
        return console.log(err);
      }
      if (res.statusCode > 202) {
        console.log(new Error('API Error: ' + res.statusCode));
      }

      verbose && console.log(res.body);
      self.requestIsInProgress = false;
    });
  });

  this.parser.on('match end', function (chats, matchDuration) {
    // verbose && console.log('match ended:', matchDuration);

    // request.post({uri: ss_address + '/api/log/stats/gameover'
    //             , json: {chats: chats}
    //             , headers: {sessionid: self.sessionId, matchduration: matchDuration}
    //             , timeout: 3000
    //             }, function (err, res, body) {
    //   // Error checking
    //   if (err) {
    //     console.log(err);
    //   }
    //   if (res.statusCode > 202) {
    //     console.log(new Error('API Error: ' + res.statusCode));
    //   }

    //   verbose && console.log(res.body);
      self.sessionid = null;
    // });
  });
};

LogStreamHandler.prototype.sendStatsUpdate = function (stats) {
  var self = this;
  // Check for matchId
  var matchId = self.matchId;
  if (!matchId) {
    verbose && console.log('aborting stats update, no matchId');
    return false;
  }

  verbose && console.log('stats update');
  if (self.requestIsInProgress) {
    return false;
  }

  self.requestIsInProgress = true;

  stats._id = matchId;

  request.post({uri: ss_address + '/api/log/stats/update'
              , json: {stats: stats}
              , timeout: 3000
              }, function (err, res, body) {
    if (err) {
      console.log(err);
    }
    if (res.statusCode > 202) {
      console.log(new Error('API Error: ' + res.statusCode));
    }

    verbose && console.log(res.body);
    self.requestIsInProgress = false;
  });
};

LogStreamHandler.prototype.startStreamingStats = function () {
  var self = this;
  self.updateTimer = setInterval(function () {
    self.sendStatsUpdate(self.parser.trustedFormattedStats());
  }, cfg.stats_update_rate);
};

LogStreamHandler.prototype.stopStreamingStats = function () {
  clearTimeout(this.updateTimer);
};

LogStreamHandler.prototype.getMatchId = function (sessionId, cb) {
  var self = this;
  self.requestIsInProgress = true;

  request.post({uri: ss_address + '/api/log/matchid'
              , headers: {sessionid: sessionId}
              , timeout: 3000
              }, function (err, res, body) {
    self.requestIsInProgress = false;

    if (err) {return cb(err);}
    if (res.statusCode !== 200) {
      return cb(new Error('Error checking sessionid: ' + res.statusCode));
    }

    cb(null, parseInt(res.headers.matchid, 10));
  });
};

LogStreamHandler.prototype.destroy = function () {
  var self = this;
  var redisCounterKey = 'counter:' + self.tf2server;

  self.redisClient.multi()
    .del(redisCounterKey)
    .del(self.tf2server)
    .exec(function (err, replies) {
      if (err) {console.trace(err);}
      self.redisClient.quit(function (err) {
        if (err) {console.trace(err);}
        delete redisClients[self.tf2server];
        delete logStreamHandlers[self.tf2server];
        verbose && console.log(Object.keys(logStreamHandlers).length, 'logstream handlers remaining.');
        verbose && console.log(Object.keys(redisClients).length, 'redis clients remaining.');
      });
    });
};

var logStreamHandlers = {};
var redisClients = {};

// param `channel` is 'logmessage'
subscriberClient.on('message', function (channel, tf2server) {
  var logStreamHandler = logStreamHandlers[tf2server];
  var client = redisClients[tf2server];

  if (!client) {
    client = redisClients[tf2server] = redis.createClient(cfg.redis_port, cfg.redis_host);
  }
  if (!logStreamHandler) {
    logStreamHandler = logStreamHandlers[tf2server] = new LogStreamHandler(tf2server, client);
    console.log('Creating new parser for list', tf2server);
  }

  var readMessage = function () {
    client.brpop(tf2server, cfg.loghandler_timeout, function (err, messageBuffer) {
      if (err) {return console.log(err);}
      if (!messageBuffer) {
        // Timeout expired, so handle that
        verbose && console.log('Listener for', tf2server, 'timed out.');
        return logStreamHandler.destroy();
      }
      if (!messageBuffer.length || messageBuffer.length < 2) {
        return console.log('length < 2???', messageBuffer);
      }
      message = messageBuffer[1].toString('ascii').slice(5,-1);
      logStreamHandler.parser.parse(message);

      process.nextTick(readMessage);
    });
  };
  readMessage();

});

subscriberClient.subscribe('logmessage');
<|endoftext|>