<commit_before>import * as watchLibrary from "pathwatcher";
import { Profile } from "../tool-env/profile.js";
import {
  statOrNull,
  pathDirname,
  convertToOSPath,
  convertToStandardPath,
  watchFile,
  unwatchFile,
} from "./files.js";

// Set METEOR_WATCH_FORCE_POLLING environment variable to a truthy value to
// force the use of files.watchFile instead of watchLibrary.watch.
// Enabled on Mac and Linux and disabled on Windows by default.
var WATCHER_ENABLED = ! process.env.METEOR_WATCH_FORCE_POLLING;
if (process.platform === "win32") {
  WATCHER_ENABLED = false;
}

var DEFAULT_POLLING_INTERVAL =
  ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 5000;

var NO_WATCHER_POLLING_INTERVAL =
  ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 500;

// This may seems like a long time to wait before actually closing the
// file watchers, but it's to our advantage if they survive restarts.
const WATCHER_CLEANUP_DELAY_MS = 30000;

const entries = Object.create(null);

// Pathwatcher complains (using console.error, ugh) if you try to watch
// two files with the same stat.ino number but different paths, so we have
// to deduplicate files by ino.
const entriesByIno = new Map;

// Set of paths for which a change event has been fired, watched with
// watchLibrary.watch if available. This could be an LRU cache, but in
// practice it should never grow large enough for that to matter.
const recentlyChanged = new Set;

function acquireWatcher(absPath, callback) {
  const entry = entries[absPath] || (
    entries[absPath] = startNewWatcher(absPath));

  // Watches successfully established in the past may have become invalid
  // because the watched file was deleted or renamed, so we need to make
  // sure we're still watching every time we call safeWatcher.watch.
  entry.rewatch();

  // The size of the entry.callbacks Set also serves as a reference count
  // for this watcher.
  entry.callbacks.add(callback);

  return entry;
}

function startNewWatcher(absPath) {
  const stat = statOrNull(absPath);
  const ino = stat && stat.ino;
  if (ino > 0 && entriesByIno.has(ino)) {
    return entriesByIno.get(ino);
  }

  function safeUnwatch() {
    if (watcher) {
      watcher.close();
      watcher = null;
      if (ino > 0) {
        entriesByIno.delete(ino);
      }
    }
  }

  let lastWatcherEventTime = +new Date;
  const callbacks = new Set;
  let watcherCleanupTimer = null;
  let watcher;

  function getPollingInterval() {
    if (watcher) {
      return DEFAULT_POLLING_INTERVAL;
    }

    if (recentlyChanged.has(absPath)) {
      return NO_WATCHER_POLLING_INTERVAL;
    }

    if (WATCHER_ENABLED) {
      return DEFAULT_POLLING_INTERVAL;
    }

    return NO_WATCHER_POLLING_INTERVAL;
  }

  function fire(event) {
    if (event !== "change") {
      // When we receive a "delete" or "rename" event, the watcher is
      // probably not going to generate any more notifications for this
      // file, so we close and nullify the watcher to ensure that
      // entry.rewatch() will attempt to reestablish the watcher the next
      // time we call safeWatcher.watch.
      safeUnwatch();

      // Make sure we don't throttle the watchFile callback after a
      // "delete" or "rename" event, since it is now our only reliable
      // source of file change notifications.
      lastWatcherEventTime = 0;

    } else {
      recentlyChanged.add(absPath);
      rewatch();
    }

    callbacks.forEach(cb => cb.call(this, event));
  }

  function watchWrapper(event) {
    lastWatcherEventTime = +new Date;
    fire.call(this, event);

    // It's tempting to call unwatchFile(absPath, watchFileWrapper) here,
    // but previous watcher success is no guarantee of future watcher
    // reliability. For example, watchLibrary.watch works just fine when file
    // changes originate from within a Vagrant VM, but changes to shared
    // files made outside the VM are invisible to watcher, so our only
    // hope of catching them is to continue polling.
  }

  function rewatch() {
    if (recentlyChanged.has(absPath)) {
      if (watcher) {
        // Already watching; nothing to do.
        return;
      }
      watcher = watchLibraryWatch(absPath, watchWrapper);
    } else if (watcher) {
      safeUnwatch();
    }

    // This is a no-op if we're not watching the file.
    unwatchFile(absPath, watchFileWrapper);

    // We use files.watchFile in addition to watcher.watch as a fail-safe
    // to detect file changes even on network file systems.  However
    // (unless the user disabled watcher or this watcher call failed), we
    // use a relatively long default polling interval of 5000ms to save
    // CPU cycles.
    watchFile(absPath, {
      persistent: false,
      interval: getPollingInterval(),
    }, watchFileWrapper);
  }

  function watchFileWrapper(...args) {
    const [newStat, oldStat] = args;

    if (newStat.ino === 0 &&
        oldStat.ino === 0 &&
        +newStat.mtime === +oldStat.mtime) {
      // Node calls the watchFile listener once with bogus identical stat
      // objects, which should not trigger a file change event.
      return;
    }

    // If a watcher event fired in the last polling interval, ignore
    // this event.
    if (new Date - lastWatcherEventTime > getPollingInterval()) {
      fire.call(this, "change");
    }
  }

  const entry = {
    callbacks,
    rewatch,

    release(callback) {
      if (! entries[absPath]) {
        return;
      }

      callbacks.delete(callback);
      if (callbacks.size > 0) {
        return;
      }

      // Once there are no more callbacks in the Set, close both watchers
      // and nullify the shared data.
      clearTimeout(watcherCleanupTimer);
      watcherCleanupTimer = setTimeout(() => {
        if (callbacks.size > 0) {
          // If another callback was added while the timer was pending, we
          // can avoid tearing anything down.
          return;
        }
        entry.close();
      }, WATCHER_CLEANUP_DELAY_MS);
    },

    close() {
      if (entries[absPath] !== entry) return;
      entries[absPath] = null;

      if (watcherCleanupTimer) {
        clearTimeout(watcherCleanupTimer);
        watcherCleanupTimer = null;
      }

      safeUnwatch();

      unwatchFile(absPath, watchFileWrapper);
    }
  };

  if (ino > 0) {
    entriesByIno.set(ino, entry);
  }

  return entry;
}

export function closeAllWatchers() {
  Object.keys(entries).forEach(absPath => {
    const entry = entries[absPath];
    if (entry) {
      entry.close();
    }
  });
}

function watchLibraryWatch(absPath, callback) {
  if (WATCHER_ENABLED) {
    try {
      return watchLibrary.watch(convertToOSPath(absPath), callback);
    } catch (e) {
      maybeSuggestRaisingWatchLimit(e);
      // ... ignore the error.  We'll still have watchFile, which is good
      // enough.
    }
  }

  return null;
}

let suggestedRaisingWatchLimit = false;

function maybeSuggestRaisingWatchLimit(error) {
  var constants = require('constants');
  var archinfo = require('../utils/archinfo.js');
  if (! suggestedRaisingWatchLimit &&
      // Note: the not-super-documented require('constants') maps from
      // strings to SYSTEM errno values. System errno values aren't the same
      // as the numbers used internally by libuv! Once we're upgraded
      // to Node 0.12, we'll have the system errno as a string (on 'code'),
      // but the support for that wasn't in Node 0.10's uv.
      // See our PR https://github.com/atom/node-pathwatcher/pull/53
      // (and make sure to read the final commit message, not the original
      // proposed PR, which had a slightly different interface).
      error.errno === constants.ENOSPC &&
      // The only suggestion we currently have is for Linux.
      archinfo.matches(archinfo.host(), 'os.linux')) {
    suggestedRaisingWatchLimit = true;
    var Console = require('../console/console.js').Console;
    if (! Console.isHeadless()) {
      Console.arrowWarn(
        "It looks like a simple tweak to your system's configuration will " +
          "make many tools (including this Meteor command) more efficient. " +
          "To learn more, see " +
          Console.url("https://github.com/meteor/meteor/wiki/File-Change-Watcher-Efficiency"));
    }
  }
}

export const watch = Profile(
  "safeWatcher.watch",
  (absPath, callback) => {
    const entry = acquireWatcher(absPath, callback);
    return {
      close() {
        entry.release(callback);
      }
    };
  }
);
<commit_msg>Require opting into the behavior introduced by the previous commit.<commit_after>import * as watchLibrary from "pathwatcher";
import { Profile } from "../tool-env/profile.js";
import {
  statOrNull,
  pathDirname,
  convertToOSPath,
  convertToStandardPath,
  watchFile,
  unwatchFile,
} from "./files.js";

// Set METEOR_WATCH_FORCE_POLLING environment variable to a truthy value to
// force the use of files.watchFile instead of watchLibrary.watch.
// Enabled on Mac and Linux and disabled on Windows by default.
var WATCHER_ENABLED = ! process.env.METEOR_WATCH_FORCE_POLLING;
if (process.platform === "win32") {
  WATCHER_ENABLED = false;
}

var PRIORITIZE_CHANGED =
  process.env.METEOR_WATCH_PRIORITIZE_CHANGED &&
  JSON.parse(process.env.METEOR_WATCH_PRIORITIZE_CHANGED);

var DEFAULT_POLLING_INTERVAL =
  ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 5000;

var NO_WATCHER_POLLING_INTERVAL =
  ~~process.env.METEOR_WATCH_POLLING_INTERVAL_MS || 500;

// This may seems like a long time to wait before actually closing the
// file watchers, but it's to our advantage if they survive restarts.
const WATCHER_CLEANUP_DELAY_MS = 30000;

const entries = Object.create(null);

// Pathwatcher complains (using console.error, ugh) if you try to watch
// two files with the same stat.ino number but different paths, so we have
// to deduplicate files by ino.
const entriesByIno = new Map;

// Set of paths for which a change event has been fired, watched with
// watchLibrary.watch if available. This could be an LRU cache, but in
// practice it should never grow large enough for that to matter.
const changedPaths = new Set;

function hasPriority(absPath) {
  // If we're not prioritizing changed files, then all files have
  // priority, which means they should be watched with native file
  // watchers if the platform supports them. If we are prioritizing
  // changed files, then only changed files get priority.
  return PRIORITIZE_CHANGED
    ? changedPaths.has(absPath)
    : true;
}

function acquireWatcher(absPath, callback) {
  const entry = entries[absPath] || (
    entries[absPath] = startNewWatcher(absPath));

  // Watches successfully established in the past may have become invalid
  // because the watched file was deleted or renamed, so we need to make
  // sure we're still watching every time we call safeWatcher.watch.
  entry.rewatch();

  // The size of the entry.callbacks Set also serves as a reference count
  // for this watcher.
  entry.callbacks.add(callback);

  return entry;
}

function startNewWatcher(absPath) {
  const stat = statOrNull(absPath);
  const ino = stat && stat.ino;
  if (ino > 0 && entriesByIno.has(ino)) {
    return entriesByIno.get(ino);
  }

  function safeUnwatch() {
    if (watcher) {
      watcher.close();
      watcher = null;
      if (ino > 0) {
        entriesByIno.delete(ino);
      }
    }
  }

  let lastWatcherEventTime = +new Date;
  const callbacks = new Set;
  let watcherCleanupTimer = null;
  let watcher;

  function getPollingInterval() {
    if (watcher) {
      return DEFAULT_POLLING_INTERVAL;
    }

    if (hasPriority(absPath)) {
      return NO_WATCHER_POLLING_INTERVAL;
    }

    if (WATCHER_ENABLED) {
      return DEFAULT_POLLING_INTERVAL;
    }

    return NO_WATCHER_POLLING_INTERVAL;
  }

  function fire(event) {
    if (event !== "change") {
      // When we receive a "delete" or "rename" event, the watcher is
      // probably not going to generate any more notifications for this
      // file, so we close and nullify the watcher to ensure that
      // entry.rewatch() will attempt to reestablish the watcher the next
      // time we call safeWatcher.watch.
      safeUnwatch();

      // Make sure we don't throttle the watchFile callback after a
      // "delete" or "rename" event, since it is now our only reliable
      // source of file change notifications.
      lastWatcherEventTime = 0;

    } else {
      changedPaths.add(absPath);
      rewatch();
    }

    callbacks.forEach(cb => cb.call(this, event));
  }

  function watchWrapper(event) {
    lastWatcherEventTime = +new Date;
    fire.call(this, event);

    // It's tempting to call unwatchFile(absPath, watchFileWrapper) here,
    // but previous watcher success is no guarantee of future watcher
    // reliability. For example, watchLibrary.watch works just fine when file
    // changes originate from within a Vagrant VM, but changes to shared
    // files made outside the VM are invisible to watcher, so our only
    // hope of catching them is to continue polling.
  }

  function rewatch() {
    if (hasPriority(absPath)) {
      if (watcher) {
        // Already watching; nothing to do.
        return;
      }
      watcher = watchLibraryWatch(absPath, watchWrapper);
    } else if (watcher) {
      safeUnwatch();
    }

    // This is a no-op if we're not watching the file.
    unwatchFile(absPath, watchFileWrapper);

    // We use files.watchFile in addition to watcher.watch as a fail-safe
    // to detect file changes even on network file systems.  However
    // (unless the user disabled watcher or this watcher call failed), we
    // use a relatively long default polling interval of 5000ms to save
    // CPU cycles.
    watchFile(absPath, {
      persistent: false,
      interval: getPollingInterval(),
    }, watchFileWrapper);
  }

  function watchFileWrapper(...args) {
    const [newStat, oldStat] = args;

    if (newStat.ino === 0 &&
        oldStat.ino === 0 &&
        +newStat.mtime === +oldStat.mtime) {
      // Node calls the watchFile listener once with bogus identical stat
      // objects, which should not trigger a file change event.
      return;
    }

    // If a watcher event fired in the last polling interval, ignore
    // this event.
    if (new Date - lastWatcherEventTime > getPollingInterval()) {
      fire.call(this, "change");
    }
  }

  const entry = {
    callbacks,
    rewatch,

    release(callback) {
      if (! entries[absPath]) {
        return;
      }

      callbacks.delete(callback);
      if (callbacks.size > 0) {
        return;
      }

      // Once there are no more callbacks in the Set, close both watchers
      // and nullify the shared data.
      clearTimeout(watcherCleanupTimer);
      watcherCleanupTimer = setTimeout(() => {
        if (callbacks.size > 0) {
          // If another callback was added while the timer was pending, we
          // can avoid tearing anything down.
          return;
        }
        entry.close();
      }, WATCHER_CLEANUP_DELAY_MS);
    },

    close() {
      if (entries[absPath] !== entry) return;
      entries[absPath] = null;

      if (watcherCleanupTimer) {
        clearTimeout(watcherCleanupTimer);
        watcherCleanupTimer = null;
      }

      safeUnwatch();

      unwatchFile(absPath, watchFileWrapper);
    }
  };

  if (ino > 0) {
    entriesByIno.set(ino, entry);
  }

  return entry;
}

export function closeAllWatchers() {
  Object.keys(entries).forEach(absPath => {
    const entry = entries[absPath];
    if (entry) {
      entry.close();
    }
  });
}

function watchLibraryWatch(absPath, callback) {
  if (WATCHER_ENABLED) {
    try {
      return watchLibrary.watch(convertToOSPath(absPath), callback);
    } catch (e) {
      maybeSuggestRaisingWatchLimit(e);
      // ... ignore the error.  We'll still have watchFile, which is good
      // enough.
    }
  }

  return null;
}

let suggestedRaisingWatchLimit = false;

function maybeSuggestRaisingWatchLimit(error) {
  var constants = require('constants');
  var archinfo = require('../utils/archinfo.js');
  if (! suggestedRaisingWatchLimit &&
      // Note: the not-super-documented require('constants') maps from
      // strings to SYSTEM errno values. System errno values aren't the same
      // as the numbers used internally by libuv! Once we're upgraded
      // to Node 0.12, we'll have the system errno as a string (on 'code'),
      // but the support for that wasn't in Node 0.10's uv.
      // See our PR https://github.com/atom/node-pathwatcher/pull/53
      // (and make sure to read the final commit message, not the original
      // proposed PR, which had a slightly different interface).
      error.errno === constants.ENOSPC &&
      // The only suggestion we currently have is for Linux.
      archinfo.matches(archinfo.host(), 'os.linux')) {
    suggestedRaisingWatchLimit = true;
    var Console = require('../console/console.js').Console;
    if (! Console.isHeadless()) {
      Console.arrowWarn(
        "It looks like a simple tweak to your system's configuration will " +
          "make many tools (including this Meteor command) more efficient. " +
          "To learn more, see " +
          Console.url("https://github.com/meteor/meteor/wiki/File-Change-Watcher-Efficiency"));
    }
  }
}

export const watch = Profile(
  "safeWatcher.watch",
  (absPath, callback) => {
    const entry = acquireWatcher(absPath, callback);
    return {
      close() {
        entry.release(callback);
      }
    };
  }
);
<|endoftext|>


<commit_before>var path = require('path');

module.exports = {
    "extends": path.join(process.cwd(), ".eslintrc"),
    "globals": {
        "isic": true
    }
};
<commit_msg>Find Girder base ".eslintrc" in a more robust way<commit_after>var fs = require('fs');
var path = require('path');

var pluginPath = path.basename(path.basename(__dirname));
var possibleGirderPaths = [
    path.basename(path.basename(pluginPath)),
    path.join(path.basename(pluginPath), 'girder'),
    process.cwd()
];

var configPath = possibleGirderPaths
    .map(function (possibleGirderPath) {
        return path.join(possibleGirderPath, '.eslintrc');
    })
    .find(function (configPath) {
        try {
            fs.accessSync(configPath);
            return true;
        } catch (e) {
            return false;
        }
    });

module.exports = {
    'extends': configPath,
    'globals': {
        'isic': true
    }
};
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const { format, supportedDialects } = require('../lib/index');
const fs = require('fs');
const { version } = require('../package.json');
const { ArgumentParser } = require('argparse');

class PrettierSQLArgs {
  constructor() {
    this.parser = this.getParser();
    this.args = this.parser.parse_args();
    this.cfg = this.readConfig();

    this.query = this.getInput();
    const formattedQuery = format(this.query, this.cfg).trim() + '\n';
    this.writeOutput(this.args.output, formattedQuery);
  }

  getParser() {
    const parser = new ArgumentParser({
      add_help: true,
      description: 'SQL Formatter',
    });

    parser.add_argument('file', {
      metavar: 'FILE',
      nargs: '?',
      help: 'Input SQL file (defaults to stdin)',
    });

    parser.add_argument('-o', '--output', {
      help: 'File to write SQL output (defaults to stdout)',
    });

    parser.add_argument('-l', '--language', {
      help: 'SQL Formatter dialect (defaults to basic sql)',
      choices: supportedDialects,
      default: 'sql',
    });

    parser.add_argument('-c', '--config', {
      help: 'Path to config json file (will use default configs if unspecified)',
    });

    parser.add_argument('--version', {
      action: 'version',
      version,
    });

    return parser;
  }

  readConfig() {
    if (Object.entries(this.args).every(([k, v]) => k === 'language' || v === undefined)) {
      this.parser.print_help();
      process.exit(0);
    }

    if (this.args.config)
      try {
        const configFile = fs.readFileSync(this.args.config);
        const configJson = JSON.parse(configFile);
        return { language: this.args.language, ...configJson };
      } catch (e) {
        if (e instanceof SyntaxError) {
          console.error(`Error: unable to parse JSON at file ${this.args.config}`);
          process.exit(1);
        }
        if (e.code === 'ENOENT') {
          console.error(`Error: could not open file ${this.args.config}`);
          process.exit(1);
        }
        console.error('An unknown error has occurred, please file a bug report at:');
        console.log('https://github.com/zeroturnaround/sql-formatter/issues\n');
        throw e;
      }
    return {
      language: this.args.language,
    };
  }

  getInput() {
    const infile = this.args.file || process.stdin.fd;
    try {
      return fs.readFileSync(infile, 'utf-8');
    } catch (e) {
      if (e.code === 'EAGAIN') {
        console.error('Error: no file specified and no data in stdin');
        process.exit(1);
      }
      if (e.code === 'ENOENT') {
        console.error(`Error: could not open file ${infile}`);
        process.exit(1);
      }
      console.error('An unknown error has occurred, please file a bug report at:');
      console.log('https://github.com/zeroturnaround/sql-formatter/issues\n');
      throw e;
    }
  }

  writeOutput(file, query) {
    if (!file) {
      // No output file, write to console
      process.stdout.write(query);
    } else {
      fs.writeFileSync(file, query);
    }
  }
}

new PrettierSQLArgs();
<commit_msg>Allow no arguments when cli command is piped<commit_after>#!/usr/bin/env node

'use strict';

const { format, supportedDialects } = require('../lib/index');
const fs = require('fs');
const tty = require('tty');
const { version } = require('../package.json');
const { ArgumentParser } = require('argparse');

class PrettierSQLArgs {
  constructor() {
    this.parser = this.getParser();
    this.args = this.parser.parse_args();
    this.cfg = this.readConfig();

    this.query = this.getInput();
    const formattedQuery = format(this.query, this.cfg).trim() + '\n';
    this.writeOutput(this.args.output, formattedQuery);
  }

  getParser() {
    const parser = new ArgumentParser({
      add_help: true,
      description: 'SQL Formatter',
    });

    parser.add_argument('file', {
      metavar: 'FILE',
      nargs: '?',
      help: 'Input SQL file (defaults to stdin)',
    });

    parser.add_argument('-o', '--output', {
      help: 'File to write SQL output (defaults to stdout)',
    });

    parser.add_argument('-l', '--language', {
      help: 'SQL Formatter dialect (defaults to basic sql)',
      choices: supportedDialects,
      default: 'sql',
    });

    parser.add_argument('-c', '--config', {
      help: 'Path to config json file (will use default configs if unspecified)',
    });

    parser.add_argument('--version', {
      action: 'version',
      version,
    });

    return parser;
  }

  readConfig() {
    if (tty.isatty(0) && Object.entries(this.args).every(([k, v]) => k === 'language' || v === undefined)) {
      this.parser.print_help();
      process.exit(0);
    }

    if (this.args.config)
      try {
        const configFile = fs.readFileSync(this.args.config);
        const configJson = JSON.parse(configFile);
        return { language: this.args.language, ...configJson };
      } catch (e) {
        if (e instanceof SyntaxError) {
          console.error(`Error: unable to parse JSON at file ${this.args.config}`);
          process.exit(1);
        }
        if (e.code === 'ENOENT') {
          console.error(`Error: could not open file ${this.args.config}`);
          process.exit(1);
        }
        console.error('An unknown error has occurred, please file a bug report at:');
        console.log('https://github.com/zeroturnaround/sql-formatter/issues\n');
        throw e;
      }
    return {
      language: this.args.language,
    };
  }

  getInput() {
    const infile = this.args.file || process.stdin.fd;
    try {
      return fs.readFileSync(infile, 'utf-8');
    } catch (e) {
      if (e.code === 'EAGAIN') {
        console.error('Error: no file specified and no data in stdin');
        process.exit(1);
      }
      if (e.code === 'ENOENT') {
        console.error(`Error: could not open file ${infile}`);
        process.exit(1);
      }
      console.error('An unknown error has occurred, please file a bug report at:');
      console.log('https://github.com/zeroturnaround/sql-formatter/issues\n');
      throw e;
    }
  }

  writeOutput(file, query) {
    if (!file) {
      // No output file, write to console
      process.stdout.write(query);
    } else {
      fs.writeFileSync(file, query);
    }
  }
}

new PrettierSQLArgs();
<|endoftext|>


<commit_before>L.EditDrag = L.EditDrag || {};

L.EditDrag.Polyline = L.Handler.extend({

  options: {
    distance: 30,   //distance from pointer to the polyline
    tollerance: 5,  //tollerance for snap effect to vertex
    vertices: {
      //first: true,  //first vertex is draggable
      //middle: true, //middle vertices are draggables
      //last: true,   //last vertex draggable
      //insert: true, //define if the number of polyline's vertices can change
    },
    icon: new L.DivIcon({
      iconSize: new L.Point(8, 8),
      className: 'leaflet-div-icon leaflet-editing-icon'
    })
  },

  initialize: function(poly) {
    this._poly = poly;
    this._marker = null;
    L.Util.setOptions(this, poly.options);
  },

  addHooks: function() {
    if (this._poly._map) {
      this._map = this._poly._map;
      this._map.on('mousemove', this._mouseMove, this);
    }
  },

  removeHooks: function() {
    this._map.off('mousemove');
  },

  /**
  * return the closest point on the closest segment
  */
  _getClosestPointAndSegment: function(latlng) {
    var distanceMin = Infinity;
    var segmentMin = null;

    for (var i = 0, len = (this._poly._latlngs.length - 1); i < len; i++) {
      var segment = [ this._poly._latlngs[i], this._poly._latlngs[i + 1] ];
      var distance = L.GeometryUtil.distanceSegment(this._map, latlng, segment[0], segment[1]);
      if (distance < distanceMin) {
        distanceMin = distance;
        segmentMin = segment;
      }
    }

    return { point: L.GeometryUtil.closestOnSegment(this._map, latlng, segmentMin[0], segmentMin[1]) , segment: segmentMin };
  },

  _mouseContextClick: function(e) {
    var closest = L.GeometryUtil.closest(this._map, this._poly, e.latlng, true);

    if (closest.distance < this.options.tollerance) {
      this._poly.spliceLatLngs(this._poly._latlngs.indexOf(closest), 1);
    }
  },

  _mouseMove: function(e) {
    var closest = L.GeometryUtil.closestLayerSnap(this._map, [this._poly], e.latlng, this.options.distance, false);

    if (this._marker && closest) {
      this._marker.addTo(this._map);
      L.extend(this._marker._latlng, closest.latlng);
      this._marker.options.draggable = true;
      this._marker.update();

    } else if (!this._marker && closest) {
      this._marker = L.marker(closest.latlng, { draggable: true, icon: this.options.icon }).addTo(this._map);
      this._marker.on('dragstart', this._markerDragStart, this);
      this._marker.on('drag', this._markerDrag, this);
      this._marker.on('dragend', this._markerDragEnd, this);
      this._marker.on('contextmenu', this._mouseContextClick, this);

    } else if (this._marker) {
      this._map.removeLayer(this._marker);
      this._marker = null;
    }
  },

  _disableDrag: function() {
    this.closest = null;
    this._marker.options.draggable = false;
  },

  _markerDragStart: function(e) {
    var latlng = e.target.getLatLng();

    this.closest = L.GeometryUtil.closest(this._map, this._poly, latlng, true);
    var index = this._poly._latlngs.indexOf(this.closest);
    var maxIndex = (this._poly._latlngs.length - 1);

    if ((this.options.vertices.first === false && index == 0) ||
        (this.options.vertices.last === false && index == maxIndex) ||
        (this.options.vertices.middle === false && (index > 0 && index < maxIndex))) {
        this._disableDrag();
        return;
    }

    //set a tollerance of 5
    if (this.closest.distance > this.options.tollerance) {

      if (this.options.vertices.insert == false) {
        this._disableDrag();
        return;
      }

      this.closest = this._getClosestPointAndSegment(latlng);

      //add a new vertex
      var insertAt = this._poly._latlngs.indexOf(this.closest.segment[1]);
      this._poly._latlngs.splice(insertAt, 0, this.closest);
    }

    this._poly.off('mousemove');
  },

  _markerDrag: function(e) {
    if (this.closest) {
      this.closest.lat = e.target.getLatLng().lat;
      this.closest.lng = e.target.getLatLng().lng;
      this._poly.redraw();
    }
  },

  _markerDragEnd: function(e) {
    this._map.on('mousemove', this._mouseMove, this);
  }
});

L.Polyline.addInitHook(function() {

  if (this.edit_with_drag) {
    return;
  }

  if (L.EditDrag.Polyline) {
    this.editingDrag = new L.EditDrag.Polyline(this);

    if (this.options.edit_with_drag) {
      this.editingDrag.enable();
    }
  }

  this.on('add', function () {
    if (this.editingDrag && this.editingDrag.enabled()) {
      this.editingDrag.addHooks();
    }
  });

  this.on('remove', function () {
    if (this.editingDrag && this.editingDrag.enabled()) {
      this.editingDrag.removeHooks();
    }
  });
});<commit_msg>remove the marker after deleting a vertex<commit_after>L.EditDrag = L.EditDrag || {};

L.EditDrag.Polyline = L.Handler.extend({

  options: {
    distance: 30,   //distance from pointer to the polyline
    tollerance: 5,  //tollerance for snap effect to vertex
    vertices: {
      //first: true,  //first vertex is draggable
      //middle: true, //middle vertices are draggables
      //last: true,   //last vertex draggable
      //insert: true, //define if the number of polyline's vertices can change
    },
    icon: new L.DivIcon({
      iconSize: new L.Point(8, 8),
      className: 'leaflet-div-icon leaflet-editing-icon'
    })
  },

  initialize: function(poly) {
    this._poly = poly;
    this._marker = null;
    L.Util.setOptions(this, poly.options);
  },

  addHooks: function() {
    if (this._poly._map) {
      this._map = this._poly._map;
      this._map.on('mousemove', this._mouseMove, this);
    }
  },

  removeHooks: function() {
    this._map.off('mousemove');
  },

  /**
  * return the closest point on the closest segment
  */
  _getClosestPointAndSegment: function(latlng) {
    var distanceMin = Infinity;
    var segmentMin = null;

    for (var i = 0, len = (this._poly._latlngs.length - 1); i < len; i++) {
      var segment = [ this._poly._latlngs[i], this._poly._latlngs[i + 1] ];
      var distance = L.GeometryUtil.distanceSegment(this._map, latlng, segment[0], segment[1]);
      if (distance < distanceMin) {
        distanceMin = distance;
        segmentMin = segment;
      }
    }

    return { point: L.GeometryUtil.closestOnSegment(this._map, latlng, segmentMin[0], segmentMin[1]) , segment: segmentMin };
  },

  _mouseContextClick: function(e) {
    var closest = L.GeometryUtil.closest(this._map, this._poly, e.latlng, true);

    if (closest.distance < this.options.tollerance) {
      this._poly.spliceLatLngs(this._poly._latlngs.indexOf(closest), 1);
      this._map.removeLayer(this._marker);
      this._marker = null;
    }
  },

  _mouseMove: function(e) {
    var closest = L.GeometryUtil.closestLayerSnap(this._map, [this._poly], e.latlng, this.options.distance, false);

    if (this._marker && closest) {
      this._marker.addTo(this._map);
      L.extend(this._marker._latlng, closest.latlng);
      this._marker.options.draggable = true;
      this._marker.update();

    } else if (!this._marker && closest) {
      this._marker = L.marker(closest.latlng, { draggable: true, icon: this.options.icon }).addTo(this._map);
      this._marker.on('dragstart', this._markerDragStart, this);
      this._marker.on('drag', this._markerDrag, this);
      this._marker.on('dragend', this._markerDragEnd, this);
      this._marker.on('contextmenu', this._mouseContextClick, this);

    } else if (this._marker) {
      this._map.removeLayer(this._marker);
      this._marker = null;
    }
  },

  _disableDrag: function() {
    this.closest = null;
    this._marker.options.draggable = false;
  },

  _markerDragStart: function(e) {
    var latlng = e.target.getLatLng();

    this.closest = L.GeometryUtil.closest(this._map, this._poly, latlng, true);
    var index = this._poly._latlngs.indexOf(this.closest);
    var maxIndex = (this._poly._latlngs.length - 1);

    if ((this.options.vertices.first === false && index == 0) ||
        (this.options.vertices.last === false && index == maxIndex) ||
        (this.options.vertices.middle === false && (index > 0 && index < maxIndex))) {
        this._disableDrag();
        return;
    }

    //set a tollerance of 5
    if (this.closest.distance > this.options.tollerance) {

      if (this.options.vertices.insert == false) {
        this._disableDrag();
        return;
      }

      this.closest = this._getClosestPointAndSegment(latlng);

      //add a new vertex
      var insertAt = this._poly._latlngs.indexOf(this.closest.segment[1]);
      this._poly._latlngs.splice(insertAt, 0, this.closest);
    }

    this._poly.off('mousemove');
  },

  _markerDrag: function(e) {
    if (this.closest) {
      this.closest.lat = e.target.getLatLng().lat;
      this.closest.lng = e.target.getLatLng().lng;
      this._poly.redraw();
    }
  },

  _markerDragEnd: function(e) {
    this._map.on('mousemove', this._mouseMove, this);
  }
});

L.Polyline.addInitHook(function() {

  if (this.edit_with_drag) {
    return;
  }

  if (L.EditDrag.Polyline) {
    this.editingDrag = new L.EditDrag.Polyline(this);

    if (this.options.edit_with_drag) {
      this.editingDrag.enable();
    }
  }

  this.on('add', function () {
    if (this.editingDrag && this.editingDrag.enabled()) {
      this.editingDrag.addHooks();
    }
  });

  this.on('remove', function () {
    if (this.editingDrag && this.editingDrag.enabled()) {
      this.editingDrag.removeHooks();
    }
  });
});<|endoftext|>


<commit_before>import {compose} from 'compose'
import {connect} from 'store'
import {createAoiFeatureLayerSource} from 'app/home/map/aoiFeatureLayerSource'
import {createGoogleSatelliteImageLayerSource} from 'app/home/map/imageLayerSource/googleSatelliteImageLayerSource'
import {createLabelsFeatureLayerSource} from 'app/home/map/labelsFeatureLayerSource'
import {createNicfiPlanetImageLayerSource} from 'app/home/map/imageLayerSource/planetImageLayerSource'
import {msg} from 'translate'
import {recipeAccess} from '../recipeAccess'
import {recipeActionBuilder} from '../recipe'
import {selectFrom} from 'stateUtils'
import {withRecipe} from '../recipeContext'
import Notifications from 'widget/notifications'
import PropTypes from 'prop-types'
import React from 'react'
import guid from 'guid'

const mapStateToProps = (state, {source: {sourceConfig: {recipeId}}}) => ({
    recipe: selectFrom(state, ['process.loadedRecipes', recipeId])
})

class _RecipeImageLayerSource extends React.Component {
    render() {
        return null
    }

    componentDidMount() {
        this.loadRecipe()
    }

    componentDidUpdate(prevProps) {
        const {recipe: prevRecipe} = prevProps
        const {stream, recipe} = this.props
        if (!stream('LOAD_RECIPE').active && (!recipe || recipe.id !== prevRecipe.id)) {
            this.loadRecipe()
        }
        if (recipe && toDescription(recipe) !== toDescription(prevRecipe)) {
            this.updateSourceConfig(recipe)
        }
    }

    loadRecipe() {
        const {stream, source: {sourceConfig: {recipeId}}, loadRecipe$} = this.props
        stream('LOAD_RECIPE',
            loadRecipe$(recipeId),
            recipe => this.updateSourceConfig(recipe),
            error => Notifications.error({message: msg('imageLayerSources.Recipe.loadError', {error}), error})
        )
    }

    updateSourceConfig(recipe) {
        const {recipeId, source, recipeActionBuilder} = this.props
        const description = toDescription(recipe)
        if (recipeId !== source.sourceConfig.recipeId) {
            recipeActionBuilder('UPDATE_RECIPE_IMAGE_LAYER_SOURCE', {description})
                .set(['layers.additionalImageLayerSources', {id: source.id}, 'sourceConfig.description'], description)
                .dispatch()
        }
    }
}

export const RecipeImageLayerSource = compose(
    _RecipeImageLayerSource,
    connect(mapStateToProps),
    withRecipe(),
    recipeAccess()
)

RecipeImageLayerSource.propTypes = {
    source: PropTypes.object.isRequired
}

export const initializeLayers = ({recipeId, savedLayers, additionalFeatureLayerSources = [], skipThis}) => {
    const recipeImageLayerSource = !skipThis && createCurrentRecipeImageLayerSource(recipeId)
    const planetImageLayerSource = createNicfiPlanetImageLayerSource()
    const googleSatelliteImageLayerSource = createGoogleSatelliteImageLayerSource()
    const imageLayerSources = [
        ...skipThis ? [] : [recipeImageLayerSource],
        planetImageLayerSource,
        googleSatelliteImageLayerSource
    ]

    const aoiLayerSource = createAoiFeatureLayerSource()
    const labelsLayerSource = createLabelsFeatureLayerSource()
    const featureLayerSources = [
        aoiLayerSource,
        labelsLayerSource,
        ...additionalFeatureLayerSources
    ]
    const layers = (savedLayers && savedLayers.areas) || {
        areas: {
            'center': {
                id: guid(),
                imageLayer: {
                    sourceId: skipThis ? googleSatelliteImageLayerSource.id : recipeImageLayerSource.id
                },
                featureLayers: [
                    {sourceId: aoiLayerSource.id}
                ]
            }
        },
        mode: 'stack'
    }
    const actionBuilder = recipeActionBuilder(recipeId)
    actionBuilder('INITIALIZE_LAYER_SOURCES')
        .setAll({
            'ui.imageLayerSources': imageLayerSources,
            'ui.featureLayerSources': featureLayerSources,
            layers,
        })
        .dispatch()
}

const createCurrentRecipeImageLayerSource = recipeId => ({
    id: 'this-recipe',
    type: 'Recipe',
    sourceConfig: {
        recipeId,
        description: msg('imageLayerSources.Recipe.thisRecipeDescription'),
    }
})

const toDescription = recipe =>
    recipe && (recipe.title || recipe.placeholder)
<commit_msg>Fixed incorrect initialization of layers.<commit_after>import {compose} from 'compose'
import {connect} from 'store'
import {createAoiFeatureLayerSource} from 'app/home/map/aoiFeatureLayerSource'
import {createGoogleSatelliteImageLayerSource} from 'app/home/map/imageLayerSource/googleSatelliteImageLayerSource'
import {createLabelsFeatureLayerSource} from 'app/home/map/labelsFeatureLayerSource'
import {createNicfiPlanetImageLayerSource} from 'app/home/map/imageLayerSource/planetImageLayerSource'
import {msg} from 'translate'
import {recipeAccess} from '../recipeAccess'
import {recipeActionBuilder} from '../recipe'
import {selectFrom} from 'stateUtils'
import {withRecipe} from '../recipeContext'
import Notifications from 'widget/notifications'
import PropTypes from 'prop-types'
import React from 'react'
import guid from 'guid'

const mapStateToProps = (state, {source: {sourceConfig: {recipeId}}}) => ({
    recipe: selectFrom(state, ['process.loadedRecipes', recipeId])
})

class _RecipeImageLayerSource extends React.Component {
    render() {
        return null
    }

    componentDidMount() {
        this.loadRecipe()
    }

    componentDidUpdate(prevProps) {
        const {recipe: prevRecipe} = prevProps
        const {stream, recipe} = this.props
        if (!stream('LOAD_RECIPE').active && (!recipe || recipe.id !== prevRecipe.id)) {
            this.loadRecipe()
        }
        if (recipe && toDescription(recipe) !== toDescription(prevRecipe)) {
            this.updateSourceConfig(recipe)
        }
    }

    loadRecipe() {
        const {stream, source: {sourceConfig: {recipeId}}, loadRecipe$} = this.props
        stream('LOAD_RECIPE',
            loadRecipe$(recipeId),
            recipe => this.updateSourceConfig(recipe),
            error => Notifications.error({message: msg('imageLayerSources.Recipe.loadError', {error}), error})
        )
    }

    updateSourceConfig(recipe) {
        const {recipeId, source, recipeActionBuilder} = this.props
        const description = toDescription(recipe)
        if (recipeId !== source.sourceConfig.recipeId) {
            recipeActionBuilder('UPDATE_RECIPE_IMAGE_LAYER_SOURCE', {description})
                .set(['layers.additionalImageLayerSources', {id: source.id}, 'sourceConfig.description'], description)
                .dispatch()
        }
    }
}

export const RecipeImageLayerSource = compose(
    _RecipeImageLayerSource,
    connect(mapStateToProps),
    withRecipe(),
    recipeAccess()
)

RecipeImageLayerSource.propTypes = {
    source: PropTypes.object.isRequired
}

export const initializeLayers = ({recipeId, savedLayers, additionalFeatureLayerSources = [], skipThis}) => {
    const recipeImageLayerSource = !skipThis && createCurrentRecipeImageLayerSource(recipeId)
    const planetImageLayerSource = createNicfiPlanetImageLayerSource()
    const googleSatelliteImageLayerSource = createGoogleSatelliteImageLayerSource()
    const imageLayerSources = [
        ...skipThis ? [] : [recipeImageLayerSource],
        planetImageLayerSource,
        googleSatelliteImageLayerSource
    ]

    const aoiLayerSource = createAoiFeatureLayerSource()
    const labelsLayerSource = createLabelsFeatureLayerSource()
    const featureLayerSources = [
        aoiLayerSource,
        labelsLayerSource,
        ...additionalFeatureLayerSources
    ]
    const layers = savedLayers && savedLayers.areas
        ? savedLayers
        : {
            areas: {
                'center': {
                    id: guid(),
                    imageLayer: {
                        sourceId: skipThis ? googleSatelliteImageLayerSource.id : recipeImageLayerSource.id
                    },
                    featureLayers: [
                        {sourceId: aoiLayerSource.id}
                    ]
                }
            },
            mode: 'stack'
        }
    const actionBuilder = recipeActionBuilder(recipeId)
    actionBuilder('INITIALIZE_LAYER_SOURCES')
        .setAll({
            'ui.imageLayerSources': imageLayerSources,
            'ui.featureLayerSources': featureLayerSources,
            layers,
        })
        .dispatch()
}

const createCurrentRecipeImageLayerSource = recipeId => ({
    id: 'this-recipe',
    type: 'Recipe',
    sourceConfig: {
        recipeId,
        description: msg('imageLayerSources.Recipe.thisRecipeDescription'),
    }
})

const toDescription = recipe =>
    recipe && (recipe.title || recipe.placeholder)
<|endoftext|>


<commit_before>/**
 * Copyright reelyActive 2014
 * We believe in an open Internet of Things
 */

var identifier = require('../../common/util/identifier');

/**
 * Process a raw Bluetooth Low Energy radio payload into semantically
 * meaningful information.
 * address.  Note that the payload is LSB first.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {AdvA48} Advertiser address identifier.
 */
function process(payload) {
  var advA48 = toAdvertiserAddress(payload.substr(4,12));
  advA48.advHeader = toAdvertiserHeader(payload.substr(0,4));
  advA48.advData = toAdvertiserData(payload.substr(16));
  return advA48;
}

/**
 * Convert a raw Bluetooth Low Energy advertiser address.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {Object} 48-bit advertiser address.
 */
function toAdvertiserAddress(payload) {
  var advAString = payload.substr(10,2);
  advAString += payload.substr(8,2);
  advAString += payload.substr(6,2);
  advAString += payload.substr(4,2);
  advAString += payload.substr(2,2);
  advAString += payload.substr(0,2);
  return new identifier(identifier.ADVA48, advAString);
}

/**
 * Convert a raw Bluetooth Low Energy advertiser header into its meaningful
 * parts.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {Object} Semantically meaningful advertiser header.
 */
function toAdvertiserHeader(payload) {
  var addressType = parseInt(payload.substr(0,1),16);
  var typeCode = payload.substr(1,1);
  var length = parseInt(payload.substr(2,2),16) % 64;
  var rxAdd = "public";
  var txAdd = "public";
  if(addressType & 0x8) {
    rxAdd = "random";
  }
  if(addressType & 0x4) {
    txAdd = "random";
  }
  var type;
  switch(typeCode) {
    case("0"):
      type = "ADV_IND";
      break;
    case("1"):
      type = "ADV_DIRECT_IND";
      break;
    case("2"):
      type = "ADV_NONCONNECT_IND";
      break;
    case("3"):
      type = "SCAN_REQ";
      break;
    case("4"):
      type = "SCAN_RSP";
      break;
    case("5"):
      type = "CONNECT_REQ";
      break;
    case("6"):
      type = "ADV_DISCOVER_IND";
      break;
  }
  return { type: type,
           length: length,
           txAdd: txAdd,
           rxAdd: rxAdd
  };
}

/**
 * Convert a raw Bluetooth Low Energy advertiser header into its meaningful
 * parts.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {Object} Semantically meaningful advertiser header.
 */
function toAdvertiserData(payload) {
  var advertiserDataLength = payload.length;
  var cursor = 0;
  var advertiserData = {};
  while(cursor < advertiserDataLength) {
    var length = (parseInt(payload.substr(cursor,2),16) + 1) * 2;
    var tag = payload.substr(cursor+2,2);
    switch(tag) {
      case("01"):
        parseFlags(payload, cursor, advertiserData);
        break;
      case("02"):
        parseNonComplete16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("03"):
        parseComplete16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("06"):
        parseNonComplete128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("07"):
        parseComplete128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("08"):
        parseShortenedLocalName(payload, cursor, advertiserData);
        break;
      case("09"):
        parseCompleteLocalName(payload, cursor, advertiserData);
        break;
      case("0a"):
        parseTxPower(payload, cursor, advertiserData);
        break;
      case("12"):
        parseSlaveConnectionIntervalRange(payload, cursor, advertiserData);
        break;
      case("14"):
        parseSolicitation16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("15"):
        parseSolicitation128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("16"):
        parseServiceData(payload, cursor, advertiserData);
        break;
      case("ff"):
        parseManufacturerSpecificData(payload, cursor, advertiserData);
        break;
      default:
        console.log("Unhandled BLE tag");
    }
    cursor += length;
  }
  return advertiserData;
}

/**
 * Parse BLE advertiser data flags.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseFlags(payload, cursor, advertiserData) {
  var flags = parseInt(payload.substr(cursor+4,2),16);
  var result = [];
  if(flags & 0x01) {
    result.push("LE Limited Discoverable Mode");
  }
  if(flags & 0x02) {
    result.push("LE General Discoverable Mode");
  }
  if(flags & 0x04) {
    result.push("BR/EDR Not Supported");
  }
  if(flags & 0x08) {
    result.push("Simultaneous LE and BR/EDR to Same Device Capable (Controller)");
  }
  if(flags & 0x10) {
    result.push("Simultaneous LE and BR/EDR to Same Device Capable (Host)");
  }
  advertiserData.flags = result;
}

/**
 * Parse BLE advertiser non-complete 16-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseNonComplete16BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var nonComplete16BitUUIDs = reverseBytes(data);
  advertiserData.nonComplete16BitUUIDs = nonComplete16BitUUIDs;
}

/**
 * Parse BLE advertiser complete 16-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseComplete16BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var complete16BitUUIDs = reverseBytes(data);
  advertiserData.complete16BitUUIDs = complete16BitUUIDs;
}

/**
 * Parse BLE advertiser non-complete 128-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseNonComplete128BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var nonComplete128BitUUIDs = reverseBytes(data);
  advertiserData.nonComplete128BitUUIDs = nonComplete128BitUUIDs;
}

/**
 * Parse BLE advertiser complete 128-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseComplete128BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var complete128BitUUIDs = reverseBytes(data);
  advertiserData.complete128BitUUIDs = complete128BitUUIDs;
}

/**
 * Parse BLE advertiser data non-complete shortened local name.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseShortenedLocalName(payload, cursor, advertiserData) {
  var hexName = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var result = "";
  for(var cChar = 0; cChar < hexName.length; cChar += 2)
    result += String.fromCharCode(parseInt(hexName.substr(cChar,2),16));
  advertiserData.shortenedLocalName = result;
}

/**
 * Parse BLE advertiser data complete local name.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseCompleteLocalName(payload, cursor, advertiserData) {
  var hexName = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var result = "";
  for(var cChar = 0; cChar < hexName.length; cChar += 2)
    result += String.fromCharCode(parseInt(hexName.substr(cChar,2),16));
  advertiserData.completeLocalName = result;
}

/**
 * Parse BLE advertiser TX power.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseTxPower(payload, cursor, advertiserData) {
  advertiserData.txPower = convertTxPower(payload.substr(cursor+4,2));
}

/**
 * Parse BLE advertiser data slave connection interval range.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseSlaveConnectionIntervalRange(payload, cursor, advertiserData) {
  var result = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  advertiserData.slaveConnectionIntervalRange = result;
}

/**
 * Parse BLE advertiser data service solicitation 16-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseSolicitation16BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var solicitation16BitUUIDs = reverseBytes(data);
  advertiserData.solicitation16BitUUIDs = solicitation16BitUUIDs;
}

/**
 * Parse BLE advertiser data service solicitation 128-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseSolicitation128BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var solicitation128BitUUIDs = reverseBytes(data);
  advertiserData.solicitation128BitUUIDs = solicitation128BitUUIDs;
}

/**
 * Parse BLE advertiser data service data.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseServiceData(payload, cursor, advertiserData) {
  var serviceData = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var uuid = serviceData.substr(2,2) + serviceData.substr(0,2);
  var data = serviceData.substr(4);
  advertiserData.serviceData = { uuid: uuid, data: data };
}

/**
 * Parse BLE advertiser data manufacturer specific data.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseManufacturerSpecificData(payload, cursor, advertiserData) {
  var companyIdentifierCode = payload.substr(cursor+6, 2);
  companyIdentifierCode += payload.substr(cursor+4, 2);
  var data = payload.substr(cursor+8, getTagDataLength(payload, cursor)-4);
  advertiserData.manufacturerSpecificData = {
                                 companyIdentifierCode: companyIdentifierCode,
                                 data: data };
  var isApple = (companyIdentifierCode == "004c");
  var isIBeacon = (data.substr(0,4) == "0215");
  if(isApple && isIBeacon) {
    var iBeacon = {};
    iBeacon.uuid = data.substr(4,32);
    iBeacon.major = data.substr(36,4);
    iBeacon.minor = data.substr(40,4);
    iBeacon.txPower = convertTxPower(data.substr(44,2));
    advertiserData.manufacturerSpecificData.iBeacon = iBeacon;
  }
}

/**
 * Calculate the length of the data (flag excluded) of a BLE tag.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 */
function getTagDataLength(payload, cursor) {
  return (parseInt(payload.substr(cursor,2),16) - 1) * 2;
}

/**
 * Convert raw txPower to dBm.
 * @param {string} rawTxPower The raw txPower as a hexadecimal-string.
 */
function convertTxPower(rawTxPower) {
  var txPower = parseInt(rawTxPower,16);
  if(txPower > 127) {
    txPower -= 256;
  }
  return txPower + "dBm";
}

/**
 * Reverse the order of the bytes in the data.
 * @param {string} data The data as a hexadecimal-string.
 */
function reverseBytes(data) {
  var result = "";
  for(var cChar = (data.length - 2); cChar >= 0; cChar -= 2) {
    result += data.substr(cChar,2);
  }
  return result;
}

module.exports.process = process;
<commit_msg>Added support for all Advertising Data Types<commit_after>/**
 * Copyright reelyActive 2014
 * We believe in an open Internet of Things
 */

var identifier = require('../../common/util/identifier');

/**
 * Process a raw Bluetooth Low Energy radio payload into semantically
 * meaningful information.
 * address.  Note that the payload is LSB first.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {AdvA48} Advertiser address identifier.
 */
function process(payload) {
  var advA48 = toAdvertiserAddress(payload.substr(4,12));
  advA48.advHeader = toAdvertiserHeader(payload.substr(0,4));
  advA48.advData = toAdvertiserData(payload.substr(16));
  return advA48;
}

/**
 * Convert a raw Bluetooth Low Energy advertiser address.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {Object} 48-bit advertiser address.
 */
function toAdvertiserAddress(payload) {
  var advAString = payload.substr(10,2);
  advAString += payload.substr(8,2);
  advAString += payload.substr(6,2);
  advAString += payload.substr(4,2);
  advAString += payload.substr(2,2);
  advAString += payload.substr(0,2);
  return new identifier(identifier.ADVA48, advAString);
}

/**
 * Convert a raw Bluetooth Low Energy advertiser header into its meaningful
 * parts.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {Object} Semantically meaningful advertiser header.
 */
function toAdvertiserHeader(payload) {
  var addressType = parseInt(payload.substr(0,1),16);
  var typeCode = payload.substr(1,1);
  var length = parseInt(payload.substr(2,2),16) % 64;
  var rxAdd = "public";
  var txAdd = "public";
  if(addressType & 0x8) {
    rxAdd = "random";
  }
  if(addressType & 0x4) {
    txAdd = "random";
  }
  var type;
  switch(typeCode) {
    case("0"):
      type = "ADV_IND";
      break;
    case("1"):
      type = "ADV_DIRECT_IND";
      break;
    case("2"):
      type = "ADV_NONCONNECT_IND";
      break;
    case("3"):
      type = "SCAN_REQ";
      break;
    case("4"):
      type = "SCAN_RSP";
      break;
    case("5"):
      type = "CONNECT_REQ";
      break;
    case("6"):
      type = "ADV_DISCOVER_IND";
      break;
  }
  return { type: type,
           length: length,
           txAdd: txAdd,
           rxAdd: rxAdd
  };
}

/**
 * Convert a raw Bluetooth Low Energy advertiser header into its meaningful
 * parts.
 * https://www.bluetooth.org/en-us/specification/assigned-numbers/generic-access-profile
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @return {Object} Semantically meaningful advertiser header.
 */
function toAdvertiserData(payload) {
  var advertiserDataLength = payload.length;
  var cursor = 0;
  var advertiserData = {};
  while(cursor < advertiserDataLength) {
    var length = (parseInt(payload.substr(cursor,2),16) + 1) * 2;
    var tag = payload.substr(cursor+2,2);
    switch(tag) {
      case("01"):
        parseFlags(payload, cursor, advertiserData);
        break;
      case("02"):
        parseNonComplete16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("03"):
        parseComplete16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("04"):
        parseGenericData(payload, cursor, advertiserData, "nonComplete32BitUUIDs");
        break;
      case("05"):
        parseGenericData(payload, cursor, advertiserData, "complete32BitUUIDs");
        break;
      case("06"):
        parseNonComplete128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("07"):
        parseComplete128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("08"):
        parseShortenedLocalName(payload, cursor, advertiserData);
        break;
      case("09"):
        parseCompleteLocalName(payload, cursor, advertiserData);
        break;
      case("0a"):
        parseTxPower(payload, cursor, advertiserData);
        break;
      case("0d"):
        parseGenericData(payload, cursor, advertiserData, "classOfDevice");
        break;
      case("0e"):
        parseGenericData(payload, cursor, advertiserData, "simplePairingHashC");
        break;
      case("0f"):
        parseGenericData(payload, cursor, advertiserData,
                         "simplePairingRandomizerR");
        break;
      case("10"):
        parseGenericData(payload, cursor, advertiserData,
                         "securityManagerTKValue");
        break;
      case("11"):
        parseGenericData(payload, cursor, advertiserData,
                         "securityManagerOOBFlags");
        break;
      case("12"):
        parseSlaveConnectionIntervalRange(payload, cursor, advertiserData);
        break;
      case("14"):
        parseSolicitation16BitUUIDs(payload, cursor, advertiserData);
        break;
      case("15"):
        parseSolicitation128BitUUIDs(payload, cursor, advertiserData);
        break;
      case("16"):
        parseServiceData(payload, cursor, advertiserData);
        break;
      case("17"):
        parseGenericData(payload, cursor, advertiserData,
                         "publicTargetAddress");
        break;
      case("18"):
        parseGenericData(payload, cursor, advertiserData,
                         "randomTargetAddress");
        break;
      case("19"):
        parseGenericData(payload, cursor, advertiserData, "appearance");
        break;
      case("1a"):
        parseGenericData(payload, cursor, advertiserData,
                         "advertisingInterval");
        break;
      case("1b"):
        parseGenericData(payload, cursor, advertiserData,
                         "leBluetoothDeviceAddress");
        break;
      case("1c"):
        parseGenericData(payload, cursor, advertiserData, "leRole");
        break;
      case("1d"):
        parseGenericData(payload, cursor, advertiserData,
                         "simplePairingHashC256");
        break;
      case("1e"):
        parseGenericData(payload, cursor, advertiserData,
                         "simplePairingRandomizerR256");
        break;
      case("1f"):
        parseGenericData(payload, cursor, advertiserData,
                         "solicitation32BitUUIDs");
        break;
      case("3d"):
        parseGenericData(payload, cursor, advertiserData,
                         "informationData3D");
        break;
      case("ff"):
        parseManufacturerSpecificData(payload, cursor, advertiserData);
        break;
      default:
        //console.log("Unhandled BLE tag " + tag);
    }
    cursor += length;
  }
  return advertiserData;
}

/**
 * Parse BLE advertiser data flags.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseFlags(payload, cursor, advertiserData) {
  var flags = parseInt(payload.substr(cursor+4,2),16);
  var result = [];
  if(flags & 0x01) {
    result.push("LE Limited Discoverable Mode");
  }
  if(flags & 0x02) {
    result.push("LE General Discoverable Mode");
  }
  if(flags & 0x04) {
    result.push("BR/EDR Not Supported");
  }
  if(flags & 0x08) {
    result.push("Simultaneous LE and BR/EDR to Same Device Capable (Controller)");
  }
  if(flags & 0x10) {
    result.push("Simultaneous LE and BR/EDR to Same Device Capable (Host)");
  }
  advertiserData.flags = result;
}

/**
 * Parse BLE advertiser non-complete 16-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseNonComplete16BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var nonComplete16BitUUIDs = reverseBytes(data);
  advertiserData.nonComplete16BitUUIDs = nonComplete16BitUUIDs;
}

/**
 * Parse BLE advertiser complete 16-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseComplete16BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var complete16BitUUIDs = reverseBytes(data);
  advertiserData.complete16BitUUIDs = complete16BitUUIDs;
}

/**
 * Parse BLE advertiser non-complete 128-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseNonComplete128BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var nonComplete128BitUUIDs = reverseBytes(data);
  advertiserData.nonComplete128BitUUIDs = nonComplete128BitUUIDs;
}

/**
 * Parse BLE advertiser complete 128-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseComplete128BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var complete128BitUUIDs = reverseBytes(data);
  advertiserData.complete128BitUUIDs = complete128BitUUIDs;
}

/**
 * Parse BLE advertiser data non-complete shortened local name.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseShortenedLocalName(payload, cursor, advertiserData) {
  var hexName = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var result = "";
  for(var cChar = 0; cChar < hexName.length; cChar += 2)
    result += String.fromCharCode(parseInt(hexName.substr(cChar,2),16));
  advertiserData.shortenedLocalName = result;
}

/**
 * Parse BLE advertiser data complete local name.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseCompleteLocalName(payload, cursor, advertiserData) {
  var hexName = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var result = "";
  for(var cChar = 0; cChar < hexName.length; cChar += 2)
    result += String.fromCharCode(parseInt(hexName.substr(cChar,2),16));
  advertiserData.completeLocalName = result;
}

/**
 * Parse BLE advertiser TX power.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseTxPower(payload, cursor, advertiserData) {
  advertiserData.txPower = convertTxPower(payload.substr(cursor+4,2));
}

/**
 * Parse BLE advertiser data slave connection interval range.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseSlaveConnectionIntervalRange(payload, cursor, advertiserData) {
  var result = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  advertiserData.slaveConnectionIntervalRange = result;
}

/**
 * Parse BLE advertiser data service solicitation 16-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseSolicitation16BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var solicitation16BitUUIDs = reverseBytes(data);
  advertiserData.solicitation16BitUUIDs = solicitation16BitUUIDs;
}

/**
 * Parse BLE advertiser data service solicitation 128-bit UUIDs.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseSolicitation128BitUUIDs(payload, cursor, advertiserData) {
  var data = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var solicitation128BitUUIDs = reverseBytes(data);
  advertiserData.solicitation128BitUUIDs = solicitation128BitUUIDs;
}

/**
 * Parse BLE advertiser data service data.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseServiceData(payload, cursor, advertiserData) {
  var serviceData = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  var uuid = serviceData.substr(2,2) + serviceData.substr(0,2);
  var data = serviceData.substr(4);
  advertiserData.serviceData = { uuid: uuid, data: data };
}

/**
 * Parse generic data.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseGenericData(payload, cursor, advertiserData, adType) {
  var genericData = payload.substr(cursor+4, getTagDataLength(payload, cursor));
  advertiserData[adType] = genericData;
}

/**
 * Parse BLE advertiser data manufacturer specific data.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 * @param {Object} advertiserData The object containing all parsed data.
 */
function parseManufacturerSpecificData(payload, cursor, advertiserData) {
  var companyIdentifierCode = payload.substr(cursor+6, 2);
  companyIdentifierCode += payload.substr(cursor+4, 2);
  var data = payload.substr(cursor+8, getTagDataLength(payload, cursor)-4);
  advertiserData.manufacturerSpecificData = {
                                 companyIdentifierCode: companyIdentifierCode,
                                 data: data };
  var isApple = (companyIdentifierCode == "004c");
  var isIBeacon = (data.substr(0,4) == "0215");
  if(isApple && isIBeacon) {
    var iBeacon = {};
    iBeacon.uuid = data.substr(4,32);
    iBeacon.major = data.substr(36,4);
    iBeacon.minor = data.substr(40,4);
    iBeacon.txPower = convertTxPower(data.substr(44,2));
    advertiserData.manufacturerSpecificData.iBeacon = iBeacon;
  }
}

/**
 * Calculate the length of the data (flag excluded) of a BLE tag.
 * @param {string} payload The raw payload as a hexadecimal-string.
 * @param {number} cursor The start index within the payload.
 */
function getTagDataLength(payload, cursor) {
  return (parseInt(payload.substr(cursor,2),16) - 1) * 2;
}

/**
 * Convert raw txPower to dBm.
 * @param {string} rawTxPower The raw txPower as a hexadecimal-string.
 */
function convertTxPower(rawTxPower) {
  var txPower = parseInt(rawTxPower,16);
  if(txPower > 127) {
    txPower -= 256;
  }
  return txPower + "dBm";
}

/**
 * Reverse the order of the bytes in the data.
 * @param {string} data The data as a hexadecimal-string.
 */
function reverseBytes(data) {
  var result = "";
  for(var cChar = (data.length - 2); cChar >= 0; cChar -= 2) {
    result += data.substr(cChar,2);
  }
  return result;
}

module.exports.process = process;
<|endoftext|>


<commit_before>HTMLElement.prototype.addClass = function (addMe) {
    if (this.hasClass(addMe)) {
        return;
    }
    this.className = this.className + " " + addMe;
};

HTMLElement.prototype.removeClass = function (removeMe) {
    var newClassName = "", i, classes = this.className.split(/\s+/);
    for (i = 0; i < classes.length; i++) {
        if (classes[i] !== removeMe) {
            newClassName += classes[i] + " ";
        }
    }
    this.className = newClassName;
};

HTMLElement.prototype.hasClass = function (hasMe) {
    var classes = this.className.split(/\s+/), i;
    for (i = 0; i < classes.length; i++) {
        if (classes[i] === hasMe) {
            return true;
        }
    }
    return false;
};

HTMLElement.prototype.toggleClass = function (toggleMe) {
    if (this.hasClass(toggleMe)) {
        this.removeClass(toggleMe);
    } else {
        this.addClass(toggleMe);
    }
};

document.on = function (eventName, selector, handler) {
    document.addEventListener(eventName, function (event) {
        for (var target = event.target || event.srcElement; target && target !== this; target = target.parentNode) {
            // loop parent nodes from the target to the delegation node
            if (target.matches(selector)) {
                handler.call(target, event);
                break;
            }
        }
    }, false);
};
<commit_msg>fix(assets): support `document.on('click')` for touch devices [ci skip]<commit_after>HTMLElement.prototype.addClass = function (addMe) {
    if (this.hasClass(addMe)) {
        return;
    }
    this.className = this.className + " " + addMe;
};

HTMLElement.prototype.removeClass = function (removeMe) {
    var newClassName = "", i, classes = this.className.split(/\s+/);
    for (i = 0; i < classes.length; i++) {
        if (classes[i] !== removeMe) {
            newClassName += classes[i] + " ";
        }
    }
    this.className = newClassName;
};

HTMLElement.prototype.hasClass = function (hasMe) {
    var classes = this.className.split(/\s+/), i;
    for (i = 0; i < classes.length; i++) {
        if (classes[i] === hasMe) {
            return true;
        }
    }
    return false;
};

HTMLElement.prototype.toggleClass = function (toggleMe) {
    if (this.hasClass(toggleMe)) {
        this.removeClass(toggleMe);
    } else {
        this.addClass(toggleMe);
    }
};

document.on = function (eventName, selector, handler) {
    var click = 'ontouchstart' in document.documentElement ? 'touchend' : 'click';
    eventName === 'click' && (eventName = click);
    document.addEventListener(eventName, function (event) {
        for (var target = event.target || event.srcElement; target && target !== this; target = target.parentNode) {
            // loop parent nodes from the target to the delegation node
            if (target.matches(selector)) {
                handler.call(target, event);
                break;
            }
        }
    }, false);
};
<|endoftext|>


<commit_before>macommune.MapSelector = function( nav ) {
    this.nav = nav;
    this.requestCounter = 0;
    this.map;

    var mapSelector = this;

    this.init = function() {
        // Initialise the main map object
        mapSelector.map = L.map( 'mapselector' ).setView( [ 46.85, 2 ], 6 );

        //Add a base layer, which contains no labels
        L.tileLayer( 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}.png', {
            attribution: '©OpenStreetMap, ©CARTO'
        } ).addTo( mapSelector.map );

        //Add in a separated pane a layer containing only labels
        mapSelector.map.createPane('labels');
        mapSelector.map.getPane('labels').style.zIndex = 650;
        mapSelector.map.getPane('labels').style.pointerEvents = 'none';

        L.tileLayer( 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}.png', {
            attribution: '©OpenStreetMap, ©CARTO',
            pane: 'labels',
        }).addTo( mapSelector.map );

        //Add an empty geoJson layer group in between
        mapSelector.geoJsonGroup = L.geoJSON( { "type": "FeatureCollection", "features": [] }, {
            onEachFeature: mapSelector.onEachFeature,
            style: mapSelector.colorizeGeoJson,
        } );
        mapSelector.geoJsonGroup.addTo( mapSelector.map );

        //Create a control which will contain little help messages
        mapSelector.helpControl = L.control();
        mapSelector.helpControl.onAdd = function ( map ) {
            this._div = L.DomUtil.create( 'div', 'help-control' );
            this.update();
            return this._div;
        };
        mapSelector.helpControl.update = function ( title ) {
            this._div.innerHTML = '
Aide
' +  ( title ?
                'Cliquez pour voir ' + title + ' en détail.' :
                ( mapSelector.geoJsonGroup !== undefined && mapSelector.geoJsonGroup.getLayers().length > 0 ? 'Cliquez sur une ville.' : 'Zoomez vers votre ville.' ));
        };
        mapSelector.helpControl.addTo( mapSelector.map );

        //Create the legend message
        mapSelector.legendControl = L.control( { position: 'bottomright' } );
        mapSelector.legendControl.onAdd = function ( map ) {
            this._div = L.DomUtil.create('div', 'help-control info legend'),
                grades = [ 0, 20, 40, 60, 80, 100 ],
                labels = [ 'peu avancé', '', '', '', '', 'très complet' ];

            // loop through our density intervals and generate a label with a colored square for each interval
            for (var i = 0; i < grades.length; i++) {
                this._div.innerHTML +=
                    ' ' +
                    labels[ i ] + ( grades[ i + 1 ] ? '
' : '' );
            }

            $( this._div ).hide();

            return this._div;
        };
        mapSelector.legendControl.disabled = function ( shouldDisable ) {
            if ( shouldDisable ) {
                $( this._div ).hide();
            }
            else {
                $( this._div ).show();
            }
        };
        mapSelector.legendControl.addTo( mapSelector.map );

        //Refresh the displayed geoJson polygons each times the map is moved
        mapSelector.map.on( 'moveend', mapSelector.onMapMove );
    };

    //Add some events handlers to all new geojson polygon
    this.onEachFeature = function( feature, layer ) {
        if ( feature.properties.qid === mapSelector.nav.qid ) {
            mapSelector.selectedLayer = layer;
        }
        layer.on( {
            mouseover: function() {
                mapSelector.helpControl.update( feature.properties.title );
            },
            mouseout: function() {
                mapSelector.helpControl.update();
            },
            click: function() {
                navigation.loadPage( feature.properties.qid, feature.properties.title, true );
            }
        } );
    };

    this.colorizeGeoJson = function( feature ) {
        if ( feature.properties.qid === mapSelector.nav.qid ) {
            return {
                fillColor: '#f2aa2e',
                color: '#f2aa2e',
                weight: 6,
            };
        }
        rgb = mapSelector.pickGradientColor( feature.properties.avg );

        return {
            fillColor: 'rgb(' + rgb[ 0 ] + ', ' + rgb[ 1 ] + ', ' + rgb[ 2 ] + ')',
            color: '#336699',
        };
    }

    this.pickGradientColor = function( value ) {
        const color1 = [ 33, 150, 243 ],
              color2 = [ 244, 67, 54 ]
        var w = ( value / 100 ) * 2 - 1,
            w1 = ( w * 1 + 1 ) / 2,
            w2 = 1 - w1;
        return [ Math.round( ( color1[ 0 ] * w1 ) + ( color2[ 0 ] * w2 ) ),
                    Math.round( ( color1[ 1 ] * w1 ) + ( color2[ 1 ] * w2 ) ),
                    Math.round( ( color1[ 2 ] * w1 ) + ( color2[ 2 ] * w2 ) ) ];
    }

    //Clear properly all the geoJson layers
    this.clearGeoJson = function() {
        if ( mapSelector.geoJsonGroup !== undefined ) {
            mapSelector.geoJsonGroup.eachLayer( function ( layer ) {
                layer.off();
            } );
            mapSelector.geoJsonGroup.clearLayers();
            mapSelector.geoJsonGroup.remove();
            delete mapSelector.geoJsonGroup;
        }
    };

    this.onMapMove = function() {
        if ( mapSelector.map.getZoom() >= 7 ) {
            var bounds = mapSelector.map.getBounds();
            var requestID = ++mapSelector.requestCounter;
            $.getJSON( '/api/geoshape/' + bounds.getSouth()
                       + '/' + bounds.getNorth()
                       + '/' + bounds.getWest()
                       + '/' + bounds.getEast() )
            .then( function( data ) {
                if ( requestID === mapSelector.requestCounter ) {
                    mapSelector.selectedLayer = undefined;
                    mapSelector.clearGeoJson();
                    if ( data.features !== undefined && data.features.length > 0 ) {
                        mapSelector.geoJsonGroup = L.geoJSON( data, {
                            onEachFeature: mapSelector.onEachFeature,
                            style: mapSelector.colorizeGeoJson,
                        } );
                        mapSelector.geoJsonGroup.addTo( mapSelector.map );
                        if ( mapSelector.selectedLayer !== undefined ) {
                            mapSelector.selectedLayer.bringToFront();
                        }
                        mapSelector.legendControl.disabled( false );
                    }
                    else {
                        mapSelector.legendControl.disabled( true );
                    }
                }
                mapSelector.helpControl.update();
                delete data;
            } );
        }
        else {
            mapSelector.clearGeoJson();
            mapSelector.helpControl.update();
            mapSelector.legendControl.disabled( true );
        }
    };

    this.move = function( lat, lng ) {
        mapSelector.map.flyTo( [ lat, lng ], 12 );
        //mapSelector.map.setView( [ lat, lng ], 12 );
    };

    return this.init();
};

<commit_msg>[Frontend] Add transparency to the gradient in the tooltip. (Fixes #13)<commit_after>macommune.MapSelector = function( nav ) {
    this.nav = nav;
    this.requestCounter = 0;
    this.map;

    var mapSelector = this;

    this.init = function() {
        // Initialise the main map object
        mapSelector.map = L.map( 'mapselector' ).setView( [ 46.85, 2 ], 6 );

        //Add a base layer, which contains no labels
        L.tileLayer( 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_nolabels/{z}/{x}/{y}.png', {
            attribution: '©OpenStreetMap, ©CARTO'
        } ).addTo( mapSelector.map );

        //Add in a separated pane a layer containing only labels
        mapSelector.map.createPane('labels');
        mapSelector.map.getPane('labels').style.zIndex = 650;
        mapSelector.map.getPane('labels').style.pointerEvents = 'none';

        L.tileLayer( 'https://cartodb-basemaps-{s}.global.ssl.fastly.net/light_only_labels/{z}/{x}/{y}.png', {
            attribution: '©OpenStreetMap, ©CARTO',
            pane: 'labels',
        }).addTo( mapSelector.map );

        //Add an empty geoJson layer group in between
        mapSelector.geoJsonGroup = L.geoJSON( { "type": "FeatureCollection", "features": [] }, {
            onEachFeature: mapSelector.onEachFeature,
            style: mapSelector.colorizeGeoJson,
        } );
        mapSelector.geoJsonGroup.addTo( mapSelector.map );

        //Create a control which will contain little help messages
        mapSelector.helpControl = L.control();
        mapSelector.helpControl.onAdd = function ( map ) {
            this._div = L.DomUtil.create( 'div', 'help-control' );
            this.update();
            return this._div;
        };
        mapSelector.helpControl.update = function ( title ) {
            this._div.innerHTML = '
Aide
' +  ( title ?
                'Cliquez pour voir ' + title + ' en détail.' :
                ( mapSelector.geoJsonGroup !== undefined && mapSelector.geoJsonGroup.getLayers().length > 0 ? 'Cliquez sur une ville.' : 'Zoomez vers votre ville.' ));
        };
        mapSelector.helpControl.addTo( mapSelector.map );

        //Create the legend message
        mapSelector.legendControl = L.control( { position: 'bottomright' } );
        mapSelector.legendControl.onAdd = function ( map ) {
            this._div = L.DomUtil.create('div', 'help-control info legend'),
                grades = [ 0, 20, 40, 60, 80, 100 ],
                labels = [ 'peu avancé', '', '', '', '', 'très complet' ];

            // loop through our density intervals and generate a label with a colored square for each interval
            for (var i = 0; i < grades.length; i++) {
                var gradientColor = mapSelector.pickGradientColor( grades[ i ] ).join(',')
                this._div.innerHTML +=
                    ' ' +
                    labels[ i ] + ( grades[ i + 1 ] ? '
' : '' );
            }

            $( this._div ).hide();

            return this._div;
        };
        mapSelector.legendControl.disabled = function ( shouldDisable ) {
            if ( shouldDisable ) {
                $( this._div ).hide();
            }
            else {
                $( this._div ).show();
            }
        };
        mapSelector.legendControl.addTo( mapSelector.map );

        //Refresh the displayed geoJson polygons each times the map is moved
        mapSelector.map.on( 'moveend', mapSelector.onMapMove );
    };

    //Add some events handlers to all new geojson polygon
    this.onEachFeature = function( feature, layer ) {
        if ( feature.properties.qid === mapSelector.nav.qid ) {
            mapSelector.selectedLayer = layer;
        }
        layer.on( {
            mouseover: function() {
                mapSelector.helpControl.update( feature.properties.title );
            },
            mouseout: function() {
                mapSelector.helpControl.update();
            },
            click: function() {
                navigation.loadPage( feature.properties.qid, feature.properties.title, true );
            }
        } );
    };

    this.colorizeGeoJson = function( feature ) {
        if ( feature.properties.qid === mapSelector.nav.qid ) {
            return {
                fillColor: '#f2aa2e',
                color: '#f2aa2e',
                weight: 6,
            };
        }
        rgb = mapSelector.pickGradientColor( feature.properties.avg );

        return {
            fillColor: 'rgb(' + rgb[ 0 ] + ', ' + rgb[ 1 ] + ', ' + rgb[ 2 ] + ')',
            color: '#336699',
        };
    }

    this.pickGradientColor = function( value ) {
        const color1 = [ 33, 150, 243 ],
              color2 = [ 244, 67, 54 ]
        var w = ( value / 100 ) * 2 - 1,
            w1 = ( w * 1 + 1 ) / 2,
            w2 = 1 - w1;
        return [ Math.round( ( color1[ 0 ] * w1 ) + ( color2[ 0 ] * w2 ) ),
                    Math.round( ( color1[ 1 ] * w1 ) + ( color2[ 1 ] * w2 ) ),
                    Math.round( ( color1[ 2 ] * w1 ) + ( color2[ 2 ] * w2 ) ) ];
    }

    //Clear properly all the geoJson layers
    this.clearGeoJson = function() {
        if ( mapSelector.geoJsonGroup !== undefined ) {
            mapSelector.geoJsonGroup.eachLayer( function ( layer ) {
                layer.off();
            } );
            mapSelector.geoJsonGroup.clearLayers();
            mapSelector.geoJsonGroup.remove();
            delete mapSelector.geoJsonGroup;
        }
    };

    this.onMapMove = function() {
        if ( mapSelector.map.getZoom() >= 7 ) {
            var bounds = mapSelector.map.getBounds();
            var requestID = ++mapSelector.requestCounter;
            $.getJSON( '/api/geoshape/' + bounds.getSouth()
                       + '/' + bounds.getNorth()
                       + '/' + bounds.getWest()
                       + '/' + bounds.getEast() )
            .then( function( data ) {
                if ( requestID === mapSelector.requestCounter ) {
                    mapSelector.selectedLayer = undefined;
                    mapSelector.clearGeoJson();
                    if ( data.features !== undefined && data.features.length > 0 ) {
                        mapSelector.geoJsonGroup = L.geoJSON( data, {
                            onEachFeature: mapSelector.onEachFeature,
                            style: mapSelector.colorizeGeoJson,
                        } );
                        mapSelector.geoJsonGroup.addTo( mapSelector.map );
                        if ( mapSelector.selectedLayer !== undefined ) {
                            mapSelector.selectedLayer.bringToFront();
                        }
                        mapSelector.legendControl.disabled( false );
                    }
                    else {
                        mapSelector.legendControl.disabled( true );
                    }
                }
                mapSelector.helpControl.update();
                delete data;
            } );
        }
        else {
            mapSelector.clearGeoJson();
            mapSelector.helpControl.update();
            mapSelector.legendControl.disabled( true );
        }
    };

    this.move = function( lat, lng ) {
        mapSelector.map.flyTo( [ lat, lng ], 12 );
        //mapSelector.map.setView( [ lat, lng ], 12 );
    };

    return this.init();
};

<|endoftext|>


<commit_before>/**
 * @file Defines the provider settings.
 *
 * Will set the path to Mongo, and applications id
 * Most of the configuration can be done using system environment variables.
 */

// Load environment variables from .env file
var dotenv = require('dotenv');
dotenv.load();

// node_env can either be "development" or "production"
var node_env = process.env.NODE_ENV || "development";

// Port to run the app on. 8000 for development
// (Vagrant syncs this port)
// 80 for production
var default_port = 8000;
if(node_env === "production") {
  default_port = 80;
}

// Exports configuration for use by app.js
module.exports = {
  env: node_env,
  port: process.env.PORT || default_port,
  mongoUrl: process.env.MONGO_URL || process.env.MONGOLAB_URI,
  redisUrl: process.env.REDIS_URL || process.env.REDISCLOUD_URL,

  googleId: process.env.GCONTACTS_API_ID,
  googleSecret: process.env.GCONTACTS_API_SECRET,

  appId: process.env.ANYFETCH_API_ID,
  appSecret: process.env.ANYFETCH_API_SECRET,

  providerUrl: process.env.PROVIDER_URL,

  testRefreshToken: process.env.GCONTACTS_TEST_REFRESH_TOKEN
};
<commit_msg>Add opbeat tokens to config<commit_after>/**
 * @file Defines the provider settings.
 *
 * Will set the path to Mongo, and applications id
 * Most of the configuration can be done using system environment variables.
 */

// Load environment variables from .env file
var dotenv = require('dotenv');
dotenv.load();

// node_env can either be "development" or "production"
var node_env = process.env.NODE_ENV || "development";

// Port to run the app on. 8000 for development
// (Vagrant syncs this port)
// 80 for production
var default_port = 8000;
if(node_env === "production") {
  default_port = 80;
}

// Exports configuration for use by app.js
module.exports = {
  env: node_env,
  port: process.env.PORT || default_port,
  mongoUrl: process.env.MONGO_URL || process.env.MONGOLAB_URI,
  redisUrl: process.env.REDIS_URL || process.env.REDISCLOUD_URL,

  googleId: process.env.GCONTACTS_API_ID,
  googleSecret: process.env.GCONTACTS_API_SECRET,

  appId: process.env.ANYFETCH_API_ID,
  appSecret: process.env.ANYFETCH_API_SECRET,

  providerUrl: process.env.PROVIDER_URL,

  testRefreshToken: process.env.GCONTACTS_TEST_REFRESH_TOKEN,

  opbeat: {
    organization_id: process.env.OPBEAT_ORGANIZATION_ID,
    app_id: process.env.OPBEAT_APP_ID,
    secret_token: process.env.OPBEAT_SECRET_TOKEN,
    silent: true
  }
};
<|endoftext|>