<commit_before>
/**
 * Module dependencies.
 */
var sh = require('../../supershell')
  , fs = require('fs')
  , dns = require('dns')
  , path = require('path')
  , parsers = require('./parsers');

/**
 * This context aim to make easier git commands manipulation. It provides some
 * wrappers on useful git commands and store several informations on the
 * current git repository.
 */
var git = new sh.Context();

/**
 * Initialize the context.
 */
git.ordered = true;
git.scope = {
  remote: 'origin',
  branches: {
    current: 'master',
    local: ['master'],
    remote: []
  },
  commits: {
    local: [],
    remote: [],
    common: []
  },
  maxCommits: 20,
  folder: '',
  url: '',
  connected: false
};
git._initCommands = ['clone', 'open'];
git._localCommands = [
  'open', 'init', 'resolve', 'commit', 'files', 'file', 'rebasing',
  'stage', 'unstage', 'stageall', 'unstageall', 'checkout', 'branches',
  'commits'
];

/**
 * Helper to run git commands in the right repository.
 *
 * @param {EventEmitter} e the event emitter to handle success and fail
 * @param {Array} args the arguments of the git command
 * @param {Function} fn an optional function to call on success
 * @param {Boolean} events defines events that should be emitted
 */
function _exec(e, args, fn, events) {
  if (fn && fn.constructor.name !== 'Function') {
    events = fn;
    fn = undefined;
  }
  events = events !== undefined ? events : ['success', 'fail'];
  events = events.constructor.name === 'Array' ? events : [events];
  fn = fn || function () {};
  return sh('git', [
        "--git-dir=" + path.join(git.scope.folder, ".git"),
        "--work-tree=" + git.scope.folder
      ].concat(args))
    .on('success', function (output) {
      fn.call(git, output);
      if (events.indexOf('success') !== -1)
        e.emit('success', output);
    })
    .on('fail', function (output) {
      if (events.indexOf('fail') !== -1)
        e.emit('fail', output);
    });
}

/**
 * Validators.
 */
function _isInit(e, name) {
  if (!git.scope.folder.length) {
    e.emit('fail', 'you should init, clone or open a repo to use it.');
    return false;
  }
  return true;
}

function _isConnected(e, name) {
  if (!git.scope.connected) {
    e.emit('fail', 'command ' + name + ' requires an internet connection.');
    return false;
  }
  return true;
}

function _isClear(e, name) {
  if (git.scope.files.staged.length || git.scope.files.unstaged.length) {
    e.emit('fail', 'please commit or stage your changes.');
    return false;
  }
  return true;
}

function _isNotRebasing(e, name) {
  if (git.scope.rebasing) {
    e.emit('fail', 'cannot execute ' + name + ' during a rebase.');
    return false;
  }
  return true;
}

/**
 * Clone a git repository.
 *
 * @param {String} url the url of the repository
 * @param {String} folder the destination πath
 */
git.command('clone', function (e, url, folder) {
  sh('git', ['clone', url, folder])
    .pipe(sh.parsers.trim())
    .on('success', function (output) {
      git.scope.url = url;
      git.scope.folder = folder;
      e.emit('success', output);
    })
    .on('fail', function (output) { e.emit('fail', output); });
});

/**
 * Open a local repository and check whether the given path is a valid
 * git repository.
 *
 * @param {String} folder the path of the repository to open
 */
git.command('open', function (e, folder) {
  fs.exists(folder, function (exists) {
    if (!exists)
      e.emit('fail', 'folder ' + folder + ' doesnt exists.');
    else {
      fs.exists(path.join(folder, '.git'), function (exists) {
        if (!exists)
          e.emit('fail', 'folder ' + folder + ' isn\'t a valid git repo.');
        else {
          git.scope.folder = folder;
          var args = ['config', 'remote.' + git.scope.remote + '.url'];
          _exec(e, args, function (output) {
            git.scope.url = output;
          });
        }
      });
    }
  });
});

/**
 * Init a new git repository.
 */
git.command('init', function (e) {
  e.emit('fail', 'TODO: init');
});

/**
 * Check if the repository url is reachable.
 */
git.command('resolve', function (e) {
  dns.resolve(git.scope.url, function (err) {
    git.scope.connected = !err;
    if (err)
      e.emit('fail', 'could not resolve url ' + git.scope.url + '.');
    else
      e.emit('success');
  });
}, [_isInit]);

/**
 * Check whether the current repository is in rebase mode.
 */
git.command('rebasing', function (e) {
  var rebaseFolder = path.join(git.scope.folder, '.git', 'rebase-apply');
  fs.exists(rebaseFolder, function (exists) {
    git.scope.rebasing = exists;
    e.emit('success', git.scope.rebasing);
  });
}, [_isInit]);

/**
 * List the commits of the repository.
 */
git.command('commits', function (e) {
  var br = git.scope.remote + '/' + git.scope.branches.current
    , count = git.scope.maxCommits
    , args = function (n, br) {
      return [
        'log', '--format="%aN|%B|%cr|%H"',
        '--max-count=' + n
      ].concat([br]);
    };

  // remote commits
  if (git.scope.connected) {
    _exec(e, args(count, 'HEAD..' + br), function (output) {
      git.scope.commits.remote = output;
      count -= output.length;
    }, 'fail').pipe(parsers.log()).pipe(sh.parsers.trim());
  }

  // local commits
  _exec(e, args(count, br + '..HEAD'), function (output) {
    git.scope.commits.local = output;
    count -= output.length;
  }, 'fail').pipe(parsers.log()).pipe(sh.parsers.trim())

  // common commits
    .on('success', function () {
      var a = args(count, '--skip=' + git.scope.commits.local.length);
      _exec(e, a, function (output) {
        git.scope.commits.common = output;
        e.emit('success', git.scope.commits);
      }, 'fail').pipe(parsers.log()).pipe(sh.parsers.trim());
    });

}, [_isInit, _isNotRebasing]);

/**
 * List the status of the current files on the repository.
 */
git.command('files', function (e) {
  var args = ['status', '--porcelain', '--untracked-files=all'];
  _exec(e, args, function (files) {
    git.scope.files = files;
  }).pipe(parsers.files()).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * List the branches of the repository.
 */
git.command('branches', function (e) {
  _exec(e, ['branch', '-a'], function (branches) {
    git.scope.branches = branches;
  }).pipe(parsers.branches());
}, [_isInit, _isNotRebasing]);

/**
 * Commit all the files staged into the repository.
 *
 * @param {String} message the message of the commit
 * @param {String} amend true to merge this commit with the previous one
 */
git.command('commit', function (e, message, amend) {
  var args = ['commit ', (amend ? '--amend ' : ''), '-m', '"' + message + '"'];
  _exec(e, args).pipe(sh.parsers.trim());
}, [_isInit, _isNotRebasing]);

/**
 * Get the git diff output of the given file.
 */
git.command('file', function (e, file) {
  _exec(e, ['diff', 'HEAD', '--', file])
    .pipe(sh.parsers.trim())
    .pipe(parsers.diff());
}, [_isInit]);

/**
 * Stage the given file.
 */
git.command('stage', function (e, file) {
  _exec(e, ['add', '--all', '--', file]).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Stage all files of the repository.
 */
git.command('stageall', function (e) {
  _exec(e, ['add', '--all', '.']).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Unstage the given file.
 */
git.command('unstage', function (e, file) {
  _exec(e, ['reset', '--', file]).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Unstage all the staged files.
 */
git.command('unstageall', function (e) {
  _exec(e, ['reset', '.']).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Checkout files, branches or commits.
 */
git.command('checkout', function (e, sep, name) {
  if (name === undefined)
    name = sep;
  _exec(e, ['checkout', name]).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Clean the repository.
 */
git.command('clean', function (e) {
  _exec(e, ['clean', '-fdx']).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Fetch changes from the repository.
 */
git.command('fetch', function (e) {
  _exec(e, ['fetch']);
}, [_isInit, _isConnected, _isNotRebasing]);

/**
 * Rebase changes from the current branch. If already rebasing, you can specify
 * and option to do something related to the current rebase.
 *
 * @param {String} option the rebase option (skip, continue or abort)
 */
git.command('rebase', function (e, arg) {
  if (git.scope.rebasing === true) {
    if (['skip', 'abort', 'continue'].indexOf(arg) === -1)
      e.emit('fail', 'unknown rebase option --' + arg);
    else
      _exec(e, ['rebase', '--' + arg]);
  }
  else {
    if (arg === undefined)
      _exec(e, [
        'pull', '--rebase', git.config.remote, git.config.branches.current
      ]);
    else
      _exec(e, ['rebase', arg]);
  }
}, [_isInit, _isConnected]);

/**
 * Push changes to current branch's remote.
 */
git.command('push', function (e, force) {
  _exec(e, [
    'push', force ? '--force' : '',
    git.config.remote, git.config.branches.current
  ]);
}, [_isInit, _isConnected, _isNotRebasing]);

/**
 * Update the current branch from the given one.
 */
// git.task('update', function (task, branch) {
//   var br = this.scope.branches.current;
//   task.step(function () { git.exec('rebase'); })
//       .step(function () { git.exec('checkout', branch); })
//       .step(function () { git.exec('rebase'); })
//       .step(function () { git.exec('checkout', br); })
//       .step(function () { git.exec('rebase', branch); })
//       .step(function () { git.exec('push', true); });
// }, [_isNotRebasing, sh.Context.taskNotRunning('update')]);

// git.command('update', function (e, branch) {
//   var br = git.scope.branches.current;
//   git.exec('rebase');
//   git.exec('checkout', branch);
//   git.exec('rebase');
//   git.exec('checkout', br);
//   git.exec('rebase', branch);
//   git.exec('push', true);
// });

// git.task('sync').exec();

// git.command('sync', function (e, target, source) {
  // TODO (find a way to resume commands)
  // git pull --rebase origin br
  // git checkout develop
  // git pull --rebase origin develop
  // git checkout br
  // git rebase develop
  // git push --force origin br
// }, [_isInit, _isConnected, _isNotRebasing]);

// TODO
// create
// rebase
// push
// sync
// publish
// finish
// git diff --name-status HEAD 84879b69266db6d93be4ea7aef06c2e737f8ec95

/**
 * Export this context as a singleton.
 */
module.exports = git;
<commit_msg>Fix remote parsing and replace config by scope.<commit_after>
/**
 * Module dependencies.
 */
var sh = require('../../supershell')
  , fs = require('fs')
  , dns = require('dns')
  , path = require('path')
  , parsers = require('./parsers');

/**
 * This context aim to make easier git commands manipulation. It provides some
 * wrappers on useful git commands and store several informations on the
 * current git repository.
 */
var git = new sh.Context();

/**
 * Initialize the context.
 */
git.ordered = true;
git.scope = {
  remote: 'origin',
  branches: {
    current: 'master',
    local: ['master'],
    remote: []
  },
  commits: {
    local: [],
    remote: [],
    common: []
  },
  maxCommits: 20,
  folder: '',
  url: '',
  connected: false
};
git._initCommands = ['clone', 'open'];
git._localCommands = [
  'open', 'init', 'resolve', 'commit', 'files', 'file', 'rebasing',
  'stage', 'unstage', 'stageall', 'unstageall', 'checkout', 'branches',
  'commits'
];

/**
 * Helper to run git commands in the right repository.
 *
 * @param {EventEmitter} e the event emitter to handle success and fail
 * @param {Array} args the arguments of the git command
 * @param {Function} fn an optional function to call on success
 * @param {Boolean} events defines events that should be emitted
 */
function _exec(e, args, fn, events) {
  if (fn && fn.constructor.name !== 'Function') {
    events = fn;
    fn = undefined;
  }
  events = events !== undefined ? events : ['success', 'fail'];
  events = events.constructor.name === 'Array' ? events : [events];
  fn = fn || function () {};
  return sh('git', [
        "--git-dir=" + path.join(git.scope.folder, ".git"),
        "--work-tree=" + git.scope.folder
      ].concat(args))
    .on('success', function (output) {
      fn.call(git, output);
      if (events.indexOf('success') !== -1)
        e.emit('success', output);
    })
    .on('fail', function (output) {
      if (events.indexOf('fail') !== -1)
        e.emit('fail', output);
    });
}

/**
 * Parse git remote url.
 */
function parseRemote(input) {
  var match = null;

  // ssh
  if (/git@([\w\.]+\.\w{2,4})\:(\w+)\/(\w+)\.git/.test(input)) {
    match = /git@([\w\.]+\.\w{2,4})\:(\w+)\/(\w+)\.git/.exec(input);
  }

  // https
  else if (/https:\/\/([\w\.]+\.\w{2,4})\/(\w+)\/(\w+)\.git/.test(input)) {
    match = /https:\/\/([\w\.]+\.\w{2,4})\/(\w+)\/(\w+)\.git/.exec(input);
  }

  return match ? { host: match[1], user: match[2], repo: match[3] } : null;
}

/**
 * Validators.
 */
function _isInit(e, name) {
  if (!git.scope.folder.length) {
    e.emit('fail', 'you should init, clone or open a repo to use it.');
    return false;
  }
  return true;
}

function _isConnected(e, name) {
  if (!git.scope.connected) {
    e.emit('fail', 'command ' + name + ' requires an internet connection.');
    return false;
  }
  return true;
}

function _isClear(e, name) {
  if (git.scope.files.staged.length || git.scope.files.unstaged.length) {
    e.emit('fail', 'please commit or stage your changes.');
    return false;
  }
  return true;
}

function _isNotRebasing(e, name) {
  if (git.scope.rebasing) {
    e.emit('fail', 'cannot execute ' + name + ' during a rebase.');
    return false;
  }
  return true;
}

/**
 * Clone a git repository.
 *
 * @param {String} url the url of the repository
 * @param {String} folder the destination πath
 */
git.command('clone', function (e, url, folder) {
  sh('git', ['clone', url, folder])
    .pipe(sh.parsers.trim())
    .on('success', function (output) {
      git.scope.url = url;
      git.scope.folder = folder;
      e.emit('success', output);
    })
    .on('fail', function (output) { e.emit('fail', output); });
});

/**
 * Open a local repository and check whether the given path is a valid
 * git repository.
 *
 * @param {String} folder the path of the repository to open
 */
git.command('open', function (e, folder) {
  fs.exists(folder, function (exists) {
    if (!exists)
      e.emit('fail', 'folder ' + folder + ' doesnt exists.');
    else {
      fs.exists(path.join(folder, '.git'), function (exists) {
        if (!exists)
          e.emit('fail', 'folder ' + folder + ' isn\'t a valid git repo.');
        else {
          git.scope.folder = folder;
          var args = ['config', 'remote.' + git.scope.remote + '.url'];
          _exec(e, args, function (output) {
            git.scope.url = output;
          });
        }
      });
    }
  });
});

/**
 * Init a new git repository.
 */
git.command('init', function (e) {
  e.emit('fail', 'TODO: init');
});

/**
 * Check if the repository url is reachable.
 */
git.command('resolve', function (e) {
  var remote = parseRemote(git.scope.url);
  if (remote === null)
    e.emit('fail', 'invalid remote url ' + git.scope.url + '.');
  else {
    dns.resolve(remote['host'], function (err) {
      git.scope.connected = !err;
      if (err)
        e.emit('fail', 'could not resolve url ' + git.scope.url + '.');
      else
        e.emit('success');
    });
  }
}, [_isInit]);

/**
 * Check whether the current repository is in rebase mode.
 */
git.command('rebasing', function (e) {
  var rebaseFolder = path.join(git.scope.folder, '.git', 'rebase-apply');
  fs.exists(rebaseFolder, function (exists) {
    git.scope.rebasing = exists;
    e.emit('success', git.scope.rebasing);
  });
}, [_isInit]);

/**
 * List the commits of the repository.
 */
git.command('commits', function (e) {
  var br = git.scope.remote + '/' + git.scope.branches.current
    , count = git.scope.maxCommits
    , args = function (n, br) {
      return [
        'log', '--format="%aN|%B|%cr|%H"',
        '--max-count=' + n
      ].concat([br]);
    };

  // remote commits
  if (git.scope.connected) {
    _exec(e, args(count, 'HEAD..' + br), function (output) {
      git.scope.commits.remote = output;
      count -= output.length;
    }, 'fail').pipe(parsers.log()).pipe(sh.parsers.trim());
  }

  // local commits
  _exec(e, args(count, br + '..HEAD'), function (output) {
    git.scope.commits.local = output;
    count -= output.length;
  }, 'fail').pipe(parsers.log()).pipe(sh.parsers.trim())

  // common commits
    .on('success', function () {
      var a = args(count, '--skip=' + git.scope.commits.local.length);
      _exec(e, a, function (output) {
        git.scope.commits.common = output;
        e.emit('success', git.scope.commits);
      }, 'fail').pipe(parsers.log()).pipe(sh.parsers.trim());
    });

}, [_isInit, _isNotRebasing]);

/**
 * List the status of the current files on the repository.
 */
git.command('files', function (e) {
  var args = ['status', '--porcelain', '--untracked-files=all'];
  _exec(e, args, function (files) {
    git.scope.files = files;
  }).pipe(parsers.files()).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * List the branches of the repository.
 */
git.command('branches', function (e) {
  _exec(e, ['branch', '-a'], function (branches) {
    git.scope.branches = branches;
  }).pipe(parsers.branches());
}, [_isInit, _isNotRebasing]);

/**
 * Commit all the files staged into the repository.
 *
 * @param {String} message the message of the commit
 * @param {String} amend true to merge this commit with the previous one
 */
git.command('commit', function (e, message, amend) {
  var args = ['commit ', (amend ? '--amend ' : ''), '-m', '"' + message + '"'];
  _exec(e, args).pipe(sh.parsers.trim());
}, [_isInit, _isNotRebasing]);

/**
 * Get the git diff output of the given file.
 */
git.command('file', function (e, file) {
  _exec(e, ['diff', 'HEAD', '--', file])
    .pipe(sh.parsers.trim())
    .pipe(parsers.diff());
}, [_isInit]);

/**
 * Stage the given file.
 */
git.command('stage', function (e, file) {
  _exec(e, ['add', '--all', '--', file]).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Stage all files of the repository.
 */
git.command('stageall', function (e) {
  _exec(e, ['add', '--all', '.']).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Unstage the given file.
 */
git.command('unstage', function (e, file) {
  _exec(e, ['reset', '--', file]).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Unstage all the staged files.
 */
git.command('unstageall', function (e) {
  _exec(e, ['reset', '.']).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Checkout files, branches or commits.
 */
git.command('checkout', function (e, sep, name) {
  if (name === undefined)
    name = sep;
  _exec(e, ['checkout', name]).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Clean the repository.
 */
git.command('clean', function (e) {
  _exec(e, ['clean', '-fdx']).pipe(sh.parsers.trim());
}, [_isInit]);

/**
 * Fetch changes from the repository.
 */
git.command('fetch', function (e) {
  _exec(e, ['fetch']);
}, [_isInit, _isConnected, _isNotRebasing]);

/**
 * Rebase changes from the current branch. If already rebasing, you can specify
 * and option to do something related to the current rebase.
 *
 * @param {String} option the rebase option (skip, continue or abort)
 */
git.command('rebase', function (e, arg) {
  if (git.scope.rebasing === true) {
    if (['skip', 'abort', 'continue'].indexOf(arg) === -1)
      e.emit('fail', 'unknown rebase option --' + arg);
    else
      _exec(e, ['rebase', '--' + arg]);
  }
  else {
    if (arg === undefined)
      _exec(e, [
        'pull', '--rebase', git.scope.remote, git.scope.branches.current
      ]);
    else
      _exec(e, ['rebase', arg]);
  }
}, [_isInit, _isConnected]);

/**
 * Push changes to current branch's remote.
 */
git.command('push', function (e, force) {
  _exec(e, [
    'push', force ? '--force' : '',
    git.scope.remote, git.scope.branches.current
  ]);
}, [_isInit, _isConnected, _isNotRebasing]);

/**
 * Update the current branch from the given one.
 */
// git.task('update', function (task, branch) {
//   var br = this.scope.branches.current;
//   task.step(function () { git.exec('rebase'); })
//       .step(function () { git.exec('checkout', branch); })
//       .step(function () { git.exec('rebase'); })
//       .step(function () { git.exec('checkout', br); })
//       .step(function () { git.exec('rebase', branch); })
//       .step(function () { git.exec('push', true); });
// }, [_isNotRebasing, sh.Context.taskNotRunning('update')]);

// git.command('update', function (e, branch) {
//   var br = git.scope.branches.current;
//   git.exec('rebase');
//   git.exec('checkout', branch);
//   git.exec('rebase');
//   git.exec('checkout', br);
//   git.exec('rebase', branch);
//   git.exec('push', true);
// });

// git.task('sync').exec();

// git.command('sync', function (e, target, source) {
  // TODO (find a way to resume commands)
  // git pull --rebase origin br
  // git checkout develop
  // git pull --rebase origin develop
  // git checkout br
  // git rebase develop
  // git push --force origin br
// }, [_isInit, _isConnected, _isNotRebasing]);

// TODO
// create
// rebase
// push
// sync
// publish
// finish
// git diff --name-status HEAD 84879b69266db6d93be4ea7aef06c2e737f8ec95

/**
 * Export this context as a singleton.
 */
module.exports = git;
<|endoftext|>


<commit_before>'use strict';

function SnapService() {
  var snapper, draggerElement, drawerSlidingDisabled = false;

  return {
    createSnapper: function(element) {
      if (snapper) {
        snapper.settings({element: element});
        snapper.enable();
      } else {
        snapper = new Snap({
          element: element,
          disable: 'right',  // use left only
          transitionSpeed: 0.2,
          minDragDistance: 0,
          addBodyClasses: false,
        });
        if (draggerElement) {
          snapper.settings({dragger: draggerElement});
        }
      }
    },
    deleteSnapper: function() {
      if (snapper) {
        snapper.disable();
      }
    },
    setDraggerElement: function(element) {
      draggerElement = element;
      if (snapper) {
        snapper.settings({dragger: draggerElement});
      }
    },
    toggle: function() {
      // https://github.com/jakiestfu/Snap.js/#--how-do-i-make-a-toggle-button
      if(snapper.state().state == 'left') {
        snapper.close();
      } else {
        snapper.open('left');
      }
    },
    registerAnimatedCallback: function(callback) {
      // http://stackoverflow.com/a/3458612
      snapper.on('animated', snapAnimated);
      function snapAnimated() {
        callback(snapper.state().state);
      }
    },
    registerCloseCallback: function(callback) {
      snapper.on('close', callback);
    },
    registerEndCallback: function(callback) {
      snapper.on('end', paneReleased);
      function paneReleased() {
        callback(snapper);
      }
    },
    disableSliding: function() {
      if (!drawerSlidingDisabled) {
        snapper.disable();
        drawerSlidingDisabled = true;
      }
    },
    enableSliding: function() {
      if (drawerSlidingDisabled) {
        snapper.enable();
        drawerSlidingDisabled = false;
      }
    }
  };
}
angular.module('common').factory('SnapService', SnapService);
<commit_msg>Increased Snap.js drawer transition speed.<commit_after>'use strict';

function SnapService() {
  var snapper, draggerElement, drawerSlidingDisabled = false;

  return {
    createSnapper: function(element) {
      if (snapper) {
        snapper.settings({element: element});
        snapper.enable();
      } else {
        snapper = new Snap({
          element: element,
          disable: 'right',  // use left only
          transitionSpeed: 0.1,
          minDragDistance: 0,
          addBodyClasses: false,
        });
        if (draggerElement) {
          snapper.settings({dragger: draggerElement});
        }
      }
    },
    deleteSnapper: function() {
      if (snapper) {
        snapper.disable();
      }
    },
    setDraggerElement: function(element) {
      draggerElement = element;
      if (snapper) {
        snapper.settings({dragger: draggerElement});
      }
    },
    toggle: function() {
      // https://github.com/jakiestfu/Snap.js/#--how-do-i-make-a-toggle-button
      if(snapper.state().state == 'left') {
        snapper.close();
      } else {
        snapper.open('left');
      }
    },
    registerAnimatedCallback: function(callback) {
      // http://stackoverflow.com/a/3458612
      snapper.on('animated', snapAnimated);
      function snapAnimated() {
        callback(snapper.state().state);
      }
    },
    registerCloseCallback: function(callback) {
      snapper.on('close', callback);
    },
    registerEndCallback: function(callback) {
      snapper.on('end', paneReleased);
      function paneReleased() {
        callback(snapper);
      }
    },
    disableSliding: function() {
      if (!drawerSlidingDisabled) {
        snapper.disable();
        drawerSlidingDisabled = true;
      }
    },
    enableSliding: function() {
      if (drawerSlidingDisabled) {
        snapper.enable();
        drawerSlidingDisabled = false;
      }
    }
  };
}
angular.module('common').factory('SnapService', SnapService);
<|endoftext|>


<commit_before>'use strict'
var split = require('split2')
var net = require('net')
var safeStringify = require('fast-safe-stringify')

/**
 * Constructor called by logagent, when the config file contains tis entry:
 * input
 *  tcp:
 *    module: megastef/logagent-input-tcp
 *    port: 4545
 *    bindAddress: 0.0.0.0
 *    returnResult: false
 *
 * @config cli arguments and config.configFile entries
 * @eventEmitter logent eventEmitter object
 */
function InputTCP (config, eventEmitter) {
  this.config = config.configFile.input.tcp
  this.config.maxInputRate = config.configFile.input.tcp.maxInputRate || config.maxInputRate
  this.eventEmitter = eventEmitter
}
module.exports = InputTCP
/**
 * Plugin start function, called after constructor
 *
 */
InputTCP.prototype.start = function () {
  if (!this.started) {
    this.createServer()
    this.started = true
  }
}

/**
 * Plugin stop function, called when logagent terminates
 * we close the server socket here.
 */
InputTCP.prototype.stop = function (cb) {
  this.server.close(cb)
}

InputTCP.prototype.createServer = function () {
  var self = this
  this.server = net.createServer(function (socket) {
    // Context object, the source name is used to identify patterns
    var context = { name: 'input.tcp', sourceName: self.config.sourceName || socket.remoteAddress + ':' + socket.remotePort }
    socket.pipe(Throttle(self.config.maxInputRate)).pipe(split()).on('data', function emitLine (data) {
      // emit a 'data.raw' event for each line we receive
      self.eventEmitter.emit('data.raw', data, context)
      if (self.config.debug) {
        console.log(data + '\n', context)
      }
    }).on('error', console.error)
    if (self.config.returnResults) {
      // Retun parsed objects in LD_JSON format to the client
      // We have to register for "data.parsed" events
      self.eventEmitter.on('data.parsed', function (data, context) {
        socket.write(safeStringify(data) + '\n')
        if (self.config.debug) {
          console.log(data, context)
        }
      })
    }
  })
  var port = this.config.port || 4545
  var address = this.config.bindAddress || '0. 0.0.0'
  this.server.listen(port, address)
  console.log('listening to ' + address + ':' + port)
}

// helper  to throttle bandwidth
var StreamThrottle = require('stream-throttle').Throttle
function Throttle (maxRate) {
  var inputRate = maxRate || 1024 * 1024 * 100
  var chunkSize = inputRate / 10
  if (chunkSize < 1) {
    chunkSize = 1
  }
  return new StreamThrottle({
    chunksize: chunkSize,
    rate: inputRate || 1024 * 1024 * 100
  })
}
<commit_msg>standard format<commit_after>'use strict'
var split = require('split2')
var net = require('net')
var safeStringify = require('fast-safe-stringify')

/**
 * Constructor called by logagent, when the config file contains tis entry:
 * input
 *  tcp:
 *    module: megastef/logagent-input-tcp
 *    port: 4545
 *    bindAddress: 0.0.0.0
 *    returnResult: false
 *
 * @config cli arguments and config.configFile entries
 * @eventEmitter logent eventEmitter object
 */
function InputTCP (config, eventEmitter) {
  this.config = config.configFile.input.tcp
  this.config.maxInputRate = config.configFile.input.tcp.maxInputRate || config.maxInputRate
  this.eventEmitter = eventEmitter
}
module.exports = InputTCP
/**
 * Plugin start function, called after constructor
 *
 */
InputTCP.prototype.start = function () {
  if (!this.started) {
    this.createServer()
    this.started = true
  }
}

/**
 * Plugin stop function, called when logagent terminates
 * we close the server socket here.
 */
InputTCP.prototype.stop = function (cb) {
  this.server.close(cb)
}

InputTCP.prototype.createServer = function () {
  var self = this
  this.server = net.createServer(function (socket) {
    // Context object, the source name is used to identify patterns
    var context = { name: 'input.tcp', sourceName: self.config.sourceName || socket.remoteAddress + ':' + socket.remotePort }
    socket.pipe(Throttle(self.config.maxInputRate)).pipe(split()).on('data', function emitLine (data) {
      // emit a 'data.raw' event for each line we receive
      self.eventEmitter.emit('data.raw', data, context)
      if (self.config.debug) {
        console.log(data + '\n', context)
      }
    }).on('error', console.error)
    if (self.config.returnResults) {
      // Retun parsed objects in LD_JSON format to the client
      // We have to register for "data.parsed" events
      self.eventEmitter.on('data.parsed', function (data, context) {
        socket.write(safeStringify(data) + '\n')
        if (self.config.debug) {
          console.log(data, context)
        }
      })
    }
  })
  var port = this.config.port || 4545
  var address = this.config.bindAddress || '0. 0.0.0'
  this.server.listen(port, address)
  console.log('listening to ' + address + ':' + port)
}

// helper  to throttle bandwidth
var StreamThrottle = require('stream-throttle').Throttle
function Throttle (maxRate) {
  var inputRate = maxRate || 1024 * 1024 * 100
  var chunkSize = inputRate / 10
  if (chunkSize < 1) {
    chunkSize = 1
  }
  return new StreamThrottle({
    chunksize: chunkSize,
    rate: inputRate || 1024 * 1024 * 100
  })
}
<|endoftext|>


<commit_before>const KEYCODES = {
    'enter': 13,
    'tab': 9,
    'escape': 27
};

export function init() {
    $('.header').each((i, elem) => {
        new SrfHeader(
            elem,
            (isOpen) => {/* Header is now open or closed */}
        );
    });
}

export class SrfHeader {

    constructor(element, onMenuToggle) {
        this.$element = $(element);
        this.menuToggleCallback  = this.checkFunctionParam(onMenuToggle);

        this.$menuButton = this.$element.find('.js-menu-button');
        this.menuIsOpen = false;
        this.isInTransition = false;

        // A11Y
        this.$navigation = this.$element.find('.js-header-navigation');
        this.$A11YElements = $('body > div, body > section, body > footer').not('.header');
        this.setA11YProperties(this.menuIsOpen);

        this.registerListeners();

        // Set initial state
        this.$navigation.hide();
    }

    /**
     * Make sure a parameter is actually a function - return an empty function if it's not.
     *
     * @param param any
     * @return {function()}
     */
    checkFunctionParam(param) {
        return param && typeof param == 'function' ? param : () => {};
    }

    registerListeners() {
        this.$menuButton.on('click', event => this.onMenuButtonClicked(event) );
        this.$menuButton.on('keydown', event => this.onMenuButtonKeyPressed(event));

        $(document).on('touchstart click', event => this.onDocumentClicked(event) );

        $(document).on('keydown.header', event => this.onKeyPressed(event));

        // A11Y Helper: when tabbing out of the menu, the first element is and will always be a breadcrumb. --> on focus, close the menu.
        $('.breadcrumb__link').first().on('focus', event => this.closeIfOpen());
        $('.footer-bottom__link').last().on('focus', event => this.closeIfOpen());
    }

    /**
     * Click on any element outside of the header or on the root element of the header itself should close the menu if
     * it's open.
     *
     * @param e {jQuery.Event}
     */
    onDocumentClicked(e) {
        if (this.menuIsOpen && !$.contains(this.$element[0], e.target)) {
            this.close();
        }
    }

    closeIfOpen() {
        if (this.menuIsOpen) {
            this.close();
        }
    }

    onMenuButtonClicked(e) {
        typeof e !== 'undefined' ? e.preventDefault() : null;

        if (!this.isInTransition) {
            this.changeMenuState(!this.menuIsOpen);
            return false;
        }
    }

    /**
     * Keypress on menu is slightly different than a click.
     * Only the enter key is relevant here and it should set the focus to the
     * appropriate element inside (see SetMenuFocus())
     *
     * @param e {jQuery.event}
     * @return {boolean}
     */
    onMenuButtonKeyPressed(e) {
        if (e.keyCode === KEYCODES.enter && !this.isInTransition) {
            typeof e !== 'undefined' ? e.preventDefault() : null;

            this.changeMenuState(!this.menuIsOpen, true);

            if( this.menuIsOpen) {
                this.setInnerFocus();
            }
            return false;
        }
    }

    /**
     * Core functionality: Open or close the menu.
     * The menu wrapper is hidden when not opened and has to be animated, so opening it
     * consists of showing and then animating it (by setting a class). Hiding means
     * removing the class and then hiding it ('then' = on transition end).
     *
     * Additionally, other elements in the body will be hidden from screenreaders.
     *
     * @param newState {boolean}
     */
    changeMenuState(newState) {
        this.menuIsOpen = newState;

        // Show then animate via class OR animate via class then hide
        if (this.menuIsOpen) {
            this.$menuButton.attr('aria-expanded', 'true').attr('aria-haspopup', 'true');
            this.$navigation.show();

            this.$element.addClass('header--open');

            this.isInTransition = true;
            this.$navigation.one('transitionend', () => {
                $('html').toggleClass('menu--opened', this.menuIsOpen);
                this.isInTransition = false;
            });
        } else {
            this.$menuButton.attr('aria-expanded', 'false').attr('aria-haspopup', 'false');
            $('html').toggleClass('menu--opened', this.menuIsOpen);

            window.scrollTo(0, 0);

            this.$element.removeClass('header--open');

            this.isInTransition = true;
            this.$navigation.one('transitionend', () => {
                this.$navigation.hide();
                this.isInTransition = false;
            });
        }

        this.setA11YProperties(this.menuIsOpen);
        this.menuToggleCallback(this.menuIsOpen);
    }

    /**
     * The following key events concern us:
     * - Escape if the menu's open --> close it
     *
     * @param e {jQuery.Event}
     */
    onKeyPressed(e) {
        if (e.keyCode === KEYCODES.escape) {
            this.closeIfOpen();
        }
    }

    close() {
        this.changeMenuState(false);
    }

    /**
     * When the menu is open, make the navigation accessible to screenreaders and
     * hide the rest of the page from them.
     *
     * @param menuIsOpened {boolean}
     */
    setA11YProperties(menuIsOpened) {
        this.$navigation.attr({
            'aria-hidden': !menuIsOpened,
            'role': menuIsOpened ? '' : 'presentation'
        });

        this.$A11YElements.attr({
            'aria-hidden': menuIsOpened,
            'role': menuIsOpened ? 'presentation': ''
        });
    }

    /**
     * don't stay on the same element when opening the menu - focus on the first element inside of the menu.
     * Which element that is depends on the screen width as the inner search field is hidden on 720px+
     */
    setInnerFocus() {
        if ($(window).width() > 720) {
            this.$navigation.find('.navigation-link').first().attr('tabindex', -1).on('blur focusout', function () {
                $(this).removeAttr('tabindex');
            }).focus();
        } else {
            this.$navigation.find('.js-search-input').first().attr('tabindex', -1).on('blur focusout', function () {
                $(this).removeAttr('tabindex');
            }).focus();
        }
    }
}
<commit_msg>set focus after transition ended<commit_after>const KEYCODES = {
    'enter': 13,
    'tab': 9,
    'escape': 27
};

export function init() {
    $('.header').each((i, elem) => {
        new SrfHeader(
            elem,
            (isOpen) => {/* Header is now open or closed */}
        );
    });
}

export class SrfHeader {

    constructor(element, onMenuToggle) {
        this.$element = $(element);
        this.menuToggleCallback  = this.checkFunctionParam(onMenuToggle);

        this.$menuButton = this.$element.find('.js-menu-button');
        this.menuIsOpen = false;
        this.isInTransition = false;

        // A11Y
        this.$navigation = this.$element.find('.js-header-navigation');
        this.$A11YElements = $('body > div, body > section, body > footer').not('.header');
        this.setA11YProperties(this.menuIsOpen);

        this.registerListeners();

        // Set initial state
        this.$navigation.hide();
    }

    /**
     * Make sure a parameter is actually a function - return an empty function if it's not.
     *
     * @param param any
     * @return {function()}
     */
    checkFunctionParam(param) {
        return param && typeof param == 'function' ? param : () => {};
    }

    registerListeners() {
        this.$menuButton.on('click', event => this.onMenuButtonClicked(event) );
        this.$menuButton.on('keydown', event => this.onMenuButtonKeyPressed(event));

        $(document).on('touchstart click', event => this.onDocumentClicked(event) );

        $(document).on('keydown.header', event => this.onKeyPressed(event));

        // A11Y Helper: when tabbing out of the menu, the first element is and will always be a breadcrumb. --> on focus, close the menu.
        $('.breadcrumb__link').first().on('focus', event => this.closeIfOpen());
        $('.footer-bottom__link').last().on('focus', event => this.closeIfOpen());
    }

    /**
     * Click on any element outside of the header or on the root element of the header itself should close the menu if
     * it's open.
     *
     * @param e {jQuery.Event}
     */
    onDocumentClicked(e) {
        if (this.menuIsOpen && !$.contains(this.$element[0], e.target)) {
            this.close();
        }
    }

    closeIfOpen() {
        if (this.menuIsOpen) {
            this.close();
        }
    }

    onMenuButtonClicked(e) {
        typeof e !== 'undefined' ? e.preventDefault() : null;

        if (!this.isInTransition) {
            this.changeMenuState(!this.menuIsOpen);
            return false;
        }
    }

    /**
     * Keypress on menu is slightly different than a click.
     * Only the enter key is relevant here and it should set the focus to the
     * appropriate element inside (see SetMenuFocus())
     *
     * @param e {jQuery.event}
     * @return {boolean}
     */
    onMenuButtonKeyPressed(e) {
        if (e.keyCode === KEYCODES.enter && !this.isInTransition) {
            typeof e !== 'undefined' ? e.preventDefault() : null;

            this.changeMenuState(!this.menuIsOpen, true);

            return false;
        }
    }

    /**
     * Core functionality: Open or close the menu.
     * The menu wrapper is hidden when not opened and has to be animated, so opening it
     * consists of showing and then animating it (by setting a class). Hiding means
     * removing the class and then hiding it ('then' = on transition end).
     *
     * Additionally, other elements in the body will be hidden from screenreaders.
     *
     * @param newState {boolean}
     */
    changeMenuState(newState) {
        this.menuIsOpen = newState;

        // Show then animate via class OR animate via class then hide
        if (this.menuIsOpen) {
            this.$menuButton.attr('aria-expanded', 'true').attr('aria-haspopup', 'true');
            this.$navigation.show();

            this.$element.addClass('header--open');

            this.isInTransition = true;
            this.$navigation.one('transitionend', () => {
                $('html').toggleClass('menu--opened', this.menuIsOpen);
                this.setInnerFocus();
                this.isInTransition = false;
            });
        } else {
            this.$menuButton.attr('aria-expanded', 'false').attr('aria-haspopup', 'false');
            $('html').toggleClass('menu--opened', this.menuIsOpen);

            window.scrollTo(0, 0);

            this.$element.removeClass('header--open');

            this.isInTransition = true;
            this.$navigation.one('transitionend', () => {
                this.$navigation.hide();
                this.isInTransition = false;
            });
        }

        this.setA11YProperties(this.menuIsOpen);
        this.menuToggleCallback(this.menuIsOpen);
    }

    /**
     * The following key events concern us:
     * - Escape if the menu's open --> close it
     *
     * @param e {jQuery.Event}
     */
    onKeyPressed(e) {
        if (e.keyCode === KEYCODES.escape) {
            this.closeIfOpen();
        }
    }

    close() {
        this.changeMenuState(false);
    }

    /**
     * When the menu is open, make the navigation accessible to screenreaders and
     * hide the rest of the page from them.
     *
     * @param menuIsOpened {boolean}
     */
    setA11YProperties(menuIsOpened) {
        this.$navigation.attr({
            'aria-hidden': !menuIsOpened,
            'role': menuIsOpened ? '' : 'presentation'
        });

        this.$A11YElements.attr({
            'aria-hidden': menuIsOpened,
            'role': menuIsOpened ? 'presentation': ''
        });
    }

    /**
     * don't stay on the same element when opening the menu - focus on the first element inside of the menu.
     * Which element that is depends on the screen width as the inner search field is hidden on 720px+
     */
    setInnerFocus() {
        if ($(window).width() > 720) {
            this.$navigation.find('.navigation-link').first().attr('tabindex', -1).on('blur focusout', function () {
                $(this).removeAttr('tabindex');
            }).focus();
        } else {
            this.$navigation.find('.js-search-input').first().attr('tabindex', -1).on('blur focusout', function () {
                $(this).removeAttr('tabindex');
            }).focus();
        }
    }
}
<|endoftext|>


<commit_before>import React from 'react'

/* Displays the 2D floor map.
   Currently not implemented */
class FloorMap extends React.Component {
	render() {
		return(

			)
	}
}

export default FloorMap<commit_msg>Started trying to make the map image displayed dependent on a select dropdown.<commit_after>import React, { PropTypes } from 'react'
import {connect} from 'react-redux'

/* Displays the 2D floor map.
   Currently not implemented */
class FloorMap extends React.Component {
	static propTypes ={
		floor: PropTypes.string.isRequired,
	}
	render() {
		return(

				<select value={this.props.optionsState}>
					<option value='floor_1'>Floor 1</option>
					<option value='floor_1-5'>Floor 1.5</option>
					<option value='floor_2'>Floor 2</option>
					<option value='floor_3'>Floor 3</option>
					<option value='floor_4'>Floor 4</option>
				</select>

			)
	}
}

function mapStateToProps(optionsState) {
	return {
		floor: optionsState,
	}
}

export default connect(mapStateToProps)(FloorMap)<|endoftext|>


<commit_before>import hull from 'hull.js'
import {DBSCAN} from 'density-clustering'

import Graphic from './Graphic'
import {fipsColor, fipsToPostal} from '../utils'
import hexagonGrid from '../HexagonGrid'
import {
  devicePixelRatio,
  selectedTileBorderColor,
  hoveredTileBorderColor,
} from '../constants'

export default class GridGraphic extends Graphic {
  constructor() {
    super()
    this.originalTilesLength = 0
    this._highlightId = null
    this._makingMarqueeSelection = false
    this._draggingMultiSelect = false
    this._selectedTiles = []
    this._mouseAt = {x: 0, y: 0}
    document.body.onkeydown = this.onkeydown.bind(this)
  }

  onMouseDown(event) {
    event.preventDefault()
    const position = hexagonGrid.rectToHexPosition(event.offsetX, event.offsetY)
    const tile = this._findTile(position)
    if (tile == null || this._selectedTiles.includes(tile)) {
      this._onMarqueeMouseDown(event)
    } else {
      this._onArrowMouseDown(event)
    }
  }

  _onArrowMouseDown(event) {
    if (this._tiles) {
      const position = hexagonGrid.rectToHexPosition(
        event.offsetX,
        event.offsetY
      )
      const tile = this._findTile(position)
      // Deselect if clicking on null tile, otherwise select tile and/or allow drag
      if (tile == null) {
        this._selectedTiles.length = 0
        return
      }
      if (this._selectedTiles[0] !== tile) {
        this._selectedTiles.length = 0
        this._draggingMultiSelect = true
        this._draggingMultiSelectOrigin = {
          x: event.offsetX,
          y: event.offsetY,
        }
        this._selectedTiles.push(tile)
      }
    }
  }

  _onMarqueeMouseDown(event) {
    if (this._tiles) {
      let createMarquee = true;
      // check if mouse on currently selected marquee tiles
      if (this._selectedTiles.length > 0) {
        const position = hexagonGrid.rectToHexPosition(event.offsetX, event.offsetY)
        const tile = this._findTile(position)
        if (this._selectedTiles.includes(tile)) {
          createMarquee = false
          this._draggingMultiSelect = true
          this._draggingMultiSelectOrigin = {
            x: event.offsetX,
            y: event.offsetY,
          }
        }
      }

      // else start a marquee
      if (createMarquee) {
        this._marqueeStart = this._mouseAt
        this._makingMarqueeSelection = true
      }
    }
  }

  onMouseUp(event) {
    if (this._makingMarqueeSelection) {
      const marqueeBounds = {
        x1: Math.min(this._marqueeStart.x, this._mouseAt.x),
        x2: Math.max(this._marqueeStart.x, this._mouseAt.x),
        y1: Math.min(this._marqueeStart.y, this._mouseAt.y),
        y2: Math.max(this._marqueeStart.y, this._mouseAt.y),
      }
      this._selectedTiles = this._tiles.filter((tile) => {
        const center = hexagonGrid.tileCenterPoint(tile.position)
        center.x /= devicePixelRatio
        center.y /= devicePixelRatio
        return center.x > marqueeBounds.x1 && center.x < marqueeBounds.x2 &&
          center.y > marqueeBounds.y1 && center.y < marqueeBounds.y2
      })
      this._makingMarqueeSelection = false
    } else if (this._draggingMultiSelect) {
      // we need to offset each selected tile by the amount the mouse moved,
      // we cannot just put each tile where the mouse is as some will not be under the mouse
      // when the mouse went down. calculated to where the mouse went down when
      // dragging started.
      const offset = {
        x: event.offsetX - this._draggingMultiSelectOrigin.x,
        y: event.offsetY - this._draggingMultiSelectOrigin.y,
      }

      // special case for new tiles, there is only one of them at a time
      // so they can just go where the mouse is
      if (this._selectedTiles[0] === this._newTile) {
        offset.x = event.offsetX
        offset.y = event.offsetY
      }

      // determine where each tile is going to be moved to
      const overlaps = this._selectedTiles.some((tile) => {
        // figure out where in XY space this tile currently is
        const tileXY = hexagonGrid.tileCenterPoint(tile.position)
        // add in the offset of the moved mouse (accounting for DPI)
        tileXY.x = (tileXY.x / devicePixelRatio) + offset.x
        tileXY.y = (tileXY.y / devicePixelRatio) + offset.y
        // convert back to hex coordinates
        tile.newPosition = hexagonGrid.rectToHexPosition(tileXY.x, tileXY.y)
        // check to see if a tile exists at that place
        const overlappingTile = this._findTile(tile.newPosition)
        // if there is an overlapping tile
        if (overlappingTile) {
          // and it's not currently selected
          if (!this._selectedTiles.includes(overlappingTile)) {
            // bail, we are moving a tile to where a tile already exists.
            return true
          }
        }
        return false
      })

      // nothing is overlapping
      if (!overlaps) {
        // actually update the tile positions
        this._selectedTiles.forEach((tile) => {
          tile.position = tile.newPosition
          delete tile.newPosition
        })
      } else if (this._selectedTiles[0] === this._newTile) {
        // there exists overlaps on a new tile, remove new tile
        this._newTile = null
        this._selectedTiles.length = 0
      }

      if (this._selectedTiles[0] === this._newTile) {
        // add new tile to list of tiles
        this._tiles.push(this._newTile)
        this.updateUi()
        this._newTile = null
      }
      this._draggingMultiSelect = false
    }
  }

  bodyOnMouseUp() {
    if (this._makingMarqueeSelection) {
      this._makingMarqueeSelection = false
    } else if (this._draggingMultiSelect) {
      this._draggingMultiSelect = false
    }
  }

  onMouseMove(event) {
    if (this._tiles) {
      this._mouseAt = {
        x: event.offsetX,
        y: event.offsetY,
      }
      const position = hexagonGrid.rectToHexPosition(
        this._mouseAt.x,
        this._mouseAt.y
      )
      const tile = this._findTile(position)
      if (!tile) {
        this._highlightId = null
        return
      }
      this._highlightId = tile.id
    }
  }

  onDoubleClick(event) {
    if (this._tiles) {
      const position = hexagonGrid.rectToHexPosition(
        event.offsetX,
        event.offsetY
      )
      const tile = this._findTile(position)
      if (tile) {
        this._deselectTile()
        this._selectedTiles = this._tiles.filter((t) => {
          return t.id === tile.id
        })
      }
    }
  }

  onHighlightGeo(id) {
    this._highlightId = id
  }

  resetHighlightedGeo() {
    this._highlightId = null
  }

  onkeydown(event) {
    const key = event.keyCode || event.charCode
    if (key === 8 || key === 46) {
      this._selectedTiles.forEach((tile) => {
        this._deleteTile(tile)
      })
      this._selectedTiles.length = 0
      this.updateUi()
    }
  }

  onChange(callback) {
    this._onChangeCallback = callback
  }

  _deselectTile() {
    if (this._selectedTiles.length !== 0) {
      this._selectedTiles.length = 0
      this._makingMarqueeSelection = false
      this._draggingMultiSelect = false
    }
  }

  onAddTileMouseDown(id) {
    this._deselectTile()
    this._newTile = {
      id,
      position: {
        x: null,
        y: null,
      },
    }
    this._draggingMultiSelect = true
    this._draggingMultiSelectOrigin = {
      x: this._mouseAt.x,
      y: this._mouseAt.y,
    }
    this._selectedTiles.length = 0
    this._selectedTiles.push(this._newTile)
  }

  _deleteTile(selected) {
    this._tiles = this._tiles.filter((tile) => {
      return tile.position.x !== selected.position.x || tile.position.y !== selected.position.y
    })
  }

  /** Populate tiles based on given TopoJSON-backed map graphic */
  populateTiles(mapGraphic) {
    this._tiles = []
    this._deselectTile()
    hexagonGrid.forEachTilePosition((x, y) => {
      const point = hexagonGrid.tileCenterPoint({x, y})
      const feature = mapGraphic.getFeatureAtPoint(point)
      if (feature) {
        this._tiles.push({
          id: feature.id,
          position: {x, y},
        })
      }
    })
    // save tiles length so the stats does not have a moving target
    this.originalTilesLength = this._tiles.length
    this.updateUi()
    return this._tiles
  }

  importTiles(tiles) {
    const maxX = Math.max(...tiles.map(tile => tile.position.x))
    const maxY = Math.max(...tiles.map(tile => tile.position.y))
    hexagonGrid.setTileEdgeFromMax(maxX, maxY)
    this._tiles = tiles
  }

  getTiles() {
    return this._tiles
  }

  getOriginalTilesLength() {
    return this.originalTilesLength
  }

  _findTile(position) {
    return this._tiles.find(tile => {
      return tile.position.x === position.x && tile.position.y === position.y
    })
  }

  render(ctx) {
    this._ctx = ctx
    this._tiles.forEach(tile => {
      let color = fipsColor(tile.id)
      if (this._selectedTiles.includes(tile)) {
        color = '#cccccc'
      }
      this._drawTile(tile.position, color)
    })

    if (this._highlightId) {
      this._drawGeoBorder(this._highlightId)
    }

    if (this._selectedTiles.length > 0) {
      this._selectedTiles.forEach((tile) => {
        let position = tile.position
        if (this._draggingMultiSelect) {
          const offset = {
            x: this._mouseAt.x - this._draggingMultiSelectOrigin.x,
            y: this._mouseAt.y - this._draggingMultiSelectOrigin.y,
          }
          if (this._selectedTiles[0] === this._newTile) {
            offset.x = this._mouseAt.x
            offset.y = this._mouseAt.y
          }

          const tileXY = hexagonGrid.tileCenterPoint(position)
          tileXY.x = (tileXY.x / devicePixelRatio) + offset.x
          tileXY.y = (tileXY.y / devicePixelRatio) + offset.y
          position = hexagonGrid.rectToHexPosition(tileXY.x, tileXY.y)
        }
        this._drawTile(
          position,
          fipsColor(tile.id),
          true
        )
      })
    }
    if (this._makingMarqueeSelection) {
      this._drawMarqueeSelection()
    }

    if (this._highlightId) {
      this._ctx.fillStyle = 'black'
      this._ctx.font = `${12.0 * devicePixelRatio}px Arial`
      this._ctx.fillText(fipsToPostal(this._highlightId), 20, 40)
    }
  }

  _drawMarqueeSelection() {
    const rect = [
      this._marqueeStart.x * devicePixelRatio,
      this._marqueeStart.y * devicePixelRatio,
      (this._mouseAt.x * devicePixelRatio) -
        (this._marqueeStart.x * devicePixelRatio),
      (this._mouseAt.y * devicePixelRatio) -
        (this._marqueeStart.y * devicePixelRatio)
    ]

    // stroke
    this._ctx.strokeStyle = '#333'
    this._ctx.lineWidth = 0.5
    this._ctx.strokeRect(...rect)

    // fill
    this._ctx.globalAlpha = 0.1
    this._ctx.fillStyle = '#666'
    this._ctx.fillRect(...rect)
    this._ctx.globalAlpha = 1.0
  }

  /** http://www.redblobgames.com/hexagonGrids/hexagons/#basics */
  _drawTile(position, fill, superstroke) {
    const center = hexagonGrid.tileCenterPoint(position)
    this._ctx.beginPath()
    this._ctx.moveTo(...hexagonGrid.getUpperLeftPoint(center))
    this._ctx.lineTo(...hexagonGrid.getUpperRightPoint(center))
    this._ctx.lineTo(...hexagonGrid.getRightPoint(center))
    this._ctx.lineTo(...hexagonGrid.getLowerRightPoint(center))
    this._ctx.lineTo(...hexagonGrid.getLowerLeftPoint(center))
    this._ctx.lineTo(...hexagonGrid.getLeftPoint(center))
    this._ctx.closePath()
    if (fill) {
      this._ctx.fillStyle = fill
      this._ctx.fill()
    }
    if (superstroke) {
      this._ctx.strokeStyle = selectedTileBorderColor
      this._ctx.lineWidth = 1.5
      this._ctx.stroke()
    }
  }

  /** Draw border around geo using convex hull algorithm */
  _drawGeoBorder(id) {
    const tiles = this._getTilesById(id)
    const paths = this._computeOutlinePaths(tiles)
    paths.forEach(path => {
      this._ctx.beginPath()
      path.forEach((point, index) => {
        const command = (index === 0) ? 'moveTo' : 'lineTo'
        this._ctx[command](...point)
      })
      this._ctx.closePath()
      this._ctx.globalAlpha = 0.75
      this._ctx.strokeStyle = hoveredTileBorderColor
      this._ctx.lineWidth = 2.0
      this._ctx.stroke()
      this._ctx.globalAlpha = 1.0
    })
  }

  /** Compute contiguous outline (convex hull) of given tiles */
  _computeOutlinePaths(tiles) {
    // collect unique points for tiles
    const points = []
    tiles.forEach(tile => {
      const center = hexagonGrid.tileCenterPoint(tile.position)
      const hexagonPoints = [
        hexagonGrid.getUpperLeftPoint(center),
        hexagonGrid.getUpperRightPoint(center),
        hexagonGrid.getRightPoint(center),
        hexagonGrid.getLowerRightPoint(center),
        hexagonGrid.getLowerLeftPoint(center),
        hexagonGrid.getLeftPoint(center),
      ]
      hexagonPoints.forEach(point => {
        if (points.indexOf(point) === -1) {
          points.push(point)
        }
      })
    })

    // cluster points
    const dbscan = new DBSCAN()
    const clusters = dbscan.run(
      points,
      hexagonGrid.getTileEdge(),  // neighborhood radius
      2                           // min points per cluster
    )

    // return paths
    return clusters.map(clusterIndices => {
      const clusterPoints = clusterIndices.map(index => points[index])
      return hull(
        clusterPoints,
        hexagonGrid.getTileEdge() // 'concavity', a.k.a. max edge length
      )
    })
  }

  updateUi() {
    if (this._onChangeCallback) {
      this._onChangeCallback()
    }
  }

  _getTilesById(id) {
    return this._tiles.filter((tile) => tile.id === id)
  }
}
<commit_msg>new tile stays under mouse<commit_after>import hull from 'hull.js'
import {DBSCAN} from 'density-clustering'

import Graphic from './Graphic'
import {fipsColor, fipsToPostal} from '../utils'
import hexagonGrid from '../HexagonGrid'
import {
  devicePixelRatio,
  selectedTileBorderColor,
  hoveredTileBorderColor,
} from '../constants'

export default class GridGraphic extends Graphic {
  constructor() {
    super()
    this.originalTilesLength = 0
    this._highlightId = null
    this._makingMarqueeSelection = false
    this._draggingMultiSelect = false
    this._selectedTiles = []
    this._mouseAt = {x: 0, y: 0}
    document.body.onkeydown = this.onkeydown.bind(this)
  }

  onMouseDown(event) {
    event.preventDefault()
    const position = hexagonGrid.rectToHexPosition(event.offsetX, event.offsetY)
    const tile = this._findTile(position)
    if (tile == null || this._selectedTiles.includes(tile)) {
      this._onMarqueeMouseDown(event)
    } else {
      this._onArrowMouseDown(event)
    }
  }

  _onArrowMouseDown(event) {
    if (this._tiles) {
      const position = hexagonGrid.rectToHexPosition(
        event.offsetX,
        event.offsetY
      )
      const tile = this._findTile(position)
      // Deselect if clicking on null tile, otherwise select tile and/or allow drag
      if (tile == null) {
        this._selectedTiles.length = 0
        return
      }
      if (this._selectedTiles[0] !== tile) {
        this._selectedTiles.length = 0
        this._draggingMultiSelect = true
        this._draggingMultiSelectOrigin = {
          x: event.offsetX,
          y: event.offsetY,
        }
        this._selectedTiles.push(tile)
      }
    }
  }

  _onMarqueeMouseDown(event) {
    if (this._tiles) {
      let createMarquee = true;
      // check if mouse on currently selected marquee tiles
      if (this._selectedTiles.length > 0) {
        const position = hexagonGrid.rectToHexPosition(event.offsetX, event.offsetY)
        const tile = this._findTile(position)
        if (this._selectedTiles.includes(tile)) {
          createMarquee = false
          this._draggingMultiSelect = true
          this._draggingMultiSelectOrigin = {
            x: event.offsetX,
            y: event.offsetY,
          }
        }
      }

      // else start a marquee
      if (createMarquee) {
        this._marqueeStart = this._mouseAt
        this._makingMarqueeSelection = true
      }
    }
  }

  onMouseUp(event) {
    if (this._makingMarqueeSelection) {
      const marqueeBounds = {
        x1: Math.min(this._marqueeStart.x, this._mouseAt.x),
        x2: Math.max(this._marqueeStart.x, this._mouseAt.x),
        y1: Math.min(this._marqueeStart.y, this._mouseAt.y),
        y2: Math.max(this._marqueeStart.y, this._mouseAt.y),
      }
      this._selectedTiles = this._tiles.filter((tile) => {
        const center = hexagonGrid.tileCenterPoint(tile.position)
        center.x /= devicePixelRatio
        center.y /= devicePixelRatio
        return center.x > marqueeBounds.x1 && center.x < marqueeBounds.x2 &&
          center.y > marqueeBounds.y1 && center.y < marqueeBounds.y2
      })
      this._makingMarqueeSelection = false
    } else if (this._draggingMultiSelect) {
      // we need to offset each selected tile by the amount the mouse moved,
      // we cannot just put each tile where the mouse is as some will not be under the mouse
      // when the mouse went down. calculated to where the mouse went down when
      // dragging started.
      const offset = {
        x: event.offsetX - this._draggingMultiSelectOrigin.x,
        y: event.offsetY - this._draggingMultiSelectOrigin.y,
      }

      // special case for new tiles, there is only one of them at a time
      // so they can just go where the mouse is
      if (this._selectedTiles[0] === this._newTile) {
        offset.x = event.offsetX
        offset.y = event.offsetY
      }

      // determine where each tile is going to be moved to
      const overlaps = this._selectedTiles.some((tile) => {
        // figure out where in XY space this tile currently is
        const tileXY = hexagonGrid.tileCenterPoint(tile.position)
        // add in the offset of the moved mouse (accounting for DPI)
        tileXY.x = (tileXY.x / devicePixelRatio) + offset.x
        tileXY.y = (tileXY.y / devicePixelRatio) + offset.y
        // convert back to hex coordinates
        tile.newPosition = hexagonGrid.rectToHexPosition(tileXY.x, tileXY.y)
        // check to see if a tile exists at that place
        const overlappingTile = this._findTile(tile.newPosition)
        // if there is an overlapping tile
        if (overlappingTile) {
          // and it's not currently selected
          if (!this._selectedTiles.includes(overlappingTile)) {
            // bail, we are moving a tile to where a tile already exists.
            return true
          }
        }
        return false
      })

      // nothing is overlapping
      if (!overlaps) {
        // actually update the tile positions
        this._selectedTiles.forEach((tile) => {
          tile.position = tile.newPosition
          delete tile.newPosition
        })
      } else if (this._selectedTiles[0] === this._newTile) {
        // there exists overlaps on a new tile, remove new tile
        this._newTile = null
        this._selectedTiles.length = 0
      }

      if (this._selectedTiles[0] === this._newTile) {
        // add new tile to list of tiles
        this._tiles.push(this._newTile)
        this.updateUi()
        this._newTile = null
      }
      this._draggingMultiSelect = false
    }
  }

  bodyOnMouseUp() {
    if (this._makingMarqueeSelection) {
      this._makingMarqueeSelection = false
    } else if (this._draggingMultiSelect) {
      this._draggingMultiSelect = false
    }
  }

  onMouseMove(event) {
    if (this._tiles) {
      this._mouseAt = {
        x: event.offsetX,
        y: event.offsetY,
      }
      const position = hexagonGrid.rectToHexPosition(
        this._mouseAt.x,
        this._mouseAt.y
      )
      const tile = this._findTile(position)
      if (!tile) {
        this._highlightId = null
        return
      }
      this._highlightId = tile.id
    }
  }

  onDoubleClick(event) {
    if (this._tiles) {
      const position = hexagonGrid.rectToHexPosition(
        event.offsetX,
        event.offsetY
      )
      const tile = this._findTile(position)
      if (tile) {
        this._deselectTile()
        this._selectedTiles = this._tiles.filter((t) => {
          return t.id === tile.id
        })
      }
    }
  }

  onHighlightGeo(id) {
    this._highlightId = id
  }

  resetHighlightedGeo() {
    this._highlightId = null
  }

  onkeydown(event) {
    const key = event.keyCode || event.charCode
    if (key === 8 || key === 46) {
      this._selectedTiles.forEach((tile) => {
        this._deleteTile(tile)
      })
      this._selectedTiles.length = 0
      this.updateUi()
    }
  }

  onChange(callback) {
    this._onChangeCallback = callback
  }

  _deselectTile() {
    if (this._selectedTiles.length !== 0) {
      this._selectedTiles.length = 0
      this._makingMarqueeSelection = false
      this._draggingMultiSelect = false
    }
  }

  onAddTileMouseDown(id) {
    this._deselectTile()
    this._newTile = {
      id,
      position: {
        x: -1,
        y: -1,
      },
    }
    this._draggingMultiSelect = true
    this._draggingMultiSelectOrigin = {
      x: this._mouseAt.x,
      y: this._mouseAt.y,
    }
    this._selectedTiles.length = 0
    this._selectedTiles.push(this._newTile)
  }

  _deleteTile(selected) {
    this._tiles = this._tiles.filter((tile) => {
      return tile.position.x !== selected.position.x || tile.position.y !== selected.position.y
    })
  }

  /** Populate tiles based on given TopoJSON-backed map graphic */
  populateTiles(mapGraphic) {
    this._tiles = []
    this._deselectTile()
    hexagonGrid.forEachTilePosition((x, y) => {
      const point = hexagonGrid.tileCenterPoint({x, y})
      const feature = mapGraphic.getFeatureAtPoint(point)
      if (feature) {
        this._tiles.push({
          id: feature.id,
          position: {x, y},
        })
      }
    })
    // save tiles length so the stats does not have a moving target
    this.originalTilesLength = this._tiles.length
    this.updateUi()
    return this._tiles
  }

  importTiles(tiles) {
    const maxX = Math.max(...tiles.map(tile => tile.position.x))
    const maxY = Math.max(...tiles.map(tile => tile.position.y))
    hexagonGrid.setTileEdgeFromMax(maxX, maxY)
    this._tiles = tiles
  }

  getTiles() {
    return this._tiles
  }

  getOriginalTilesLength() {
    return this.originalTilesLength
  }

  _findTile(position) {
    return this._tiles.find(tile => {
      return tile.position.x === position.x && tile.position.y === position.y
    })
  }

  render(ctx) {
    this._ctx = ctx
    this._tiles.forEach(tile => {
      let color = fipsColor(tile.id)
      if (this._selectedTiles.includes(tile)) {
        color = '#cccccc'
      }
      this._drawTile(tile.position, color)
    })

    if (this._highlightId) {
      this._drawGeoBorder(this._highlightId)
    }

    if (this._selectedTiles.length > 0) {
      this._selectedTiles.forEach((tile) => {
        let position = tile.position
        if (this._draggingMultiSelect) {
          const offset = {
            x: this._mouseAt.x - this._draggingMultiSelectOrigin.x,
            y: this._mouseAt.y - this._draggingMultiSelectOrigin.y,
          }
          if (this._selectedTiles[0] === this._newTile) {
            offset.x = this._mouseAt.x
            offset.y = this._mouseAt.y
          }

          const tileXY = hexagonGrid.tileCenterPoint(position)
          tileXY.x = (tileXY.x / devicePixelRatio) + offset.x
          tileXY.y = (tileXY.y / devicePixelRatio) + offset.y
          position = hexagonGrid.rectToHexPosition(tileXY.x, tileXY.y)
        }
        this._drawTile(
          position,
          fipsColor(tile.id),
          true
        )
      })
    }
    if (this._makingMarqueeSelection) {
      this._drawMarqueeSelection()
    }

    if (this._highlightId) {
      this._ctx.fillStyle = 'black'
      this._ctx.font = `${12.0 * devicePixelRatio}px Arial`
      this._ctx.fillText(fipsToPostal(this._highlightId), 20, 40)
    }
  }

  _drawMarqueeSelection() {
    const rect = [
      this._marqueeStart.x * devicePixelRatio,
      this._marqueeStart.y * devicePixelRatio,
      (this._mouseAt.x * devicePixelRatio) -
        (this._marqueeStart.x * devicePixelRatio),
      (this._mouseAt.y * devicePixelRatio) -
        (this._marqueeStart.y * devicePixelRatio)
    ]

    // stroke
    this._ctx.strokeStyle = '#333'
    this._ctx.lineWidth = 0.5
    this._ctx.strokeRect(...rect)

    // fill
    this._ctx.globalAlpha = 0.1
    this._ctx.fillStyle = '#666'
    this._ctx.fillRect(...rect)
    this._ctx.globalAlpha = 1.0
  }

  /** http://www.redblobgames.com/hexagonGrids/hexagons/#basics */
  _drawTile(position, fill, superstroke) {
    const center = hexagonGrid.tileCenterPoint(position)
    this._ctx.beginPath()
    this._ctx.moveTo(...hexagonGrid.getUpperLeftPoint(center))
    this._ctx.lineTo(...hexagonGrid.getUpperRightPoint(center))
    this._ctx.lineTo(...hexagonGrid.getRightPoint(center))
    this._ctx.lineTo(...hexagonGrid.getLowerRightPoint(center))
    this._ctx.lineTo(...hexagonGrid.getLowerLeftPoint(center))
    this._ctx.lineTo(...hexagonGrid.getLeftPoint(center))
    this._ctx.closePath()
    if (fill) {
      this._ctx.fillStyle = fill
      this._ctx.fill()
    }
    if (superstroke) {
      this._ctx.strokeStyle = selectedTileBorderColor
      this._ctx.lineWidth = 1.5
      this._ctx.stroke()
    }
  }

  /** Draw border around geo using convex hull algorithm */
  _drawGeoBorder(id) {
    const tiles = this._getTilesById(id)
    const paths = this._computeOutlinePaths(tiles)
    paths.forEach(path => {
      this._ctx.beginPath()
      path.forEach((point, index) => {
        const command = (index === 0) ? 'moveTo' : 'lineTo'
        this._ctx[command](...point)
      })
      this._ctx.closePath()
      this._ctx.globalAlpha = 0.75
      this._ctx.strokeStyle = hoveredTileBorderColor
      this._ctx.lineWidth = 2.0
      this._ctx.stroke()
      this._ctx.globalAlpha = 1.0
    })
  }

  /** Compute contiguous outline (convex hull) of given tiles */
  _computeOutlinePaths(tiles) {
    // collect unique points for tiles
    const points = []
    tiles.forEach(tile => {
      const center = hexagonGrid.tileCenterPoint(tile.position)
      const hexagonPoints = [
        hexagonGrid.getUpperLeftPoint(center),
        hexagonGrid.getUpperRightPoint(center),
        hexagonGrid.getRightPoint(center),
        hexagonGrid.getLowerRightPoint(center),
        hexagonGrid.getLowerLeftPoint(center),
        hexagonGrid.getLeftPoint(center),
      ]
      hexagonPoints.forEach(point => {
        if (points.indexOf(point) === -1) {
          points.push(point)
        }
      })
    })

    // cluster points
    const dbscan = new DBSCAN()
    const clusters = dbscan.run(
      points,
      hexagonGrid.getTileEdge(),  // neighborhood radius
      2                           // min points per cluster
    )

    // return paths
    return clusters.map(clusterIndices => {
      const clusterPoints = clusterIndices.map(index => points[index])
      return hull(
        clusterPoints,
        hexagonGrid.getTileEdge() // 'concavity', a.k.a. max edge length
      )
    })
  }

  updateUi() {
    if (this._onChangeCallback) {
      this._onChangeCallback()
    }
  }

  _getTilesById(id) {
    return this._tiles.filter((tile) => tile.id === id)
  }
}
<|endoftext|>


<commit_before>import * as THREE from 'three';
import { MeshBVH, acceleratedRaycast, computeBoundsTree, disposeBoundsTree, CENTER, SAH, AVERAGE } from '../src/index.js';
import fs from 'fs';
import path from 'path';

const dataPath = path.resolve( __dirname, './data/points.bin' );
const buffer = fs.readFileSync( dataPath );
const points = new Float32Array( buffer.buffer, buffer.byteOffset, buffer.byteLength / 4 );

THREE.Mesh.prototype.raycast = acceleratedRaycast;
THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;

describe( 'AVERAGE Points Raycast', () => {

	let geometry = null;
	let mesh = null;
	let raycaster = null;
	beforeEach( () => {

		geometry = new THREE.BufferGeometry();
		geometry.addAttribute( 'position', new THREE.BufferAttribute( points.slice(), 3 ) );
		geometry.computeVertexNormals();

		mesh = new THREE.Mesh( geometry );

		geometry.computeBoundingBox();
		const center = new THREE.Vector3();
		geometry.boundingBox.getCenter( center );

		const x = 101086.2438562272 - center.x;
		const y = 99421.40510391879 - center.y;

		geometry.center();

		raycaster = new THREE.Raycaster();
		raycaster.firstHitOnly = true;
		raycaster.set( new THREE.Vector3( x, y, - 1000 ), new THREE.Vector3( 0, 0, 1 ) );

	} );

	it( 'should collide against the geometry with CENTER split', () => {

		geometry.boundsTree = new MeshBVH( geometry, {
			strategy: CENTER,
			maxDepth: 64,
			maxLeafTris: 16
		} );

		const res1 = raycaster.intersectObject( mesh );

		geometry.boundsTree = null;
		const res2 = raycaster.intersectObject( mesh );

		expect( res1 ).toEqual( res2 );

	} );

	it( 'should collide against the geometry with SAH split', () => {

		geometry.boundsTree = new MeshBVH( geometry, {
			strategy: SAH,
			maxDepth: 64,
			maxLeafTris: 16
		} );

		const res1 = raycaster.intersectObject( mesh );

		geometry.boundsTree = null;
		const res2 = raycaster.intersectObject( mesh );

		expect( res1 ).toEqual( res2 );

	} );

	it( 'should collide against the geometry with AVERAGE split', () => {

		geometry.boundsTree = new MeshBVH( geometry, {
			strategy: AVERAGE,
			maxDepth: 64,
			maxLeafTris: 16
		} );

		const res1 = raycaster.intersectObject( mesh );

		geometry.boundsTree = null;
		const res2 = raycaster.intersectObject( mesh );

		res1.length && delete res1[ 0 ].object;
		res2.length && delete res2[ 0 ].object;

		expect( res1 ).toEqual( res2 );

	} );

} );
<commit_msg>Update AverageSplit.test.js<commit_after>import * as THREE from 'three';
import { MeshBVH, acceleratedRaycast, computeBoundsTree, disposeBoundsTree, CENTER, SAH, AVERAGE } from '../src/index.js';
import fs from 'fs';
import path from 'path';

const dataPath = path.resolve( __dirname, './data/points.bin' );
const buffer = fs.readFileSync( dataPath );
const points = new Float32Array( buffer.buffer, buffer.byteOffset, buffer.byteLength / 4 );

THREE.Mesh.prototype.raycast = acceleratedRaycast;
THREE.BufferGeometry.prototype.computeBoundsTree = computeBoundsTree;
THREE.BufferGeometry.prototype.disposeBoundsTree = disposeBoundsTree;

describe( 'AVERAGE Points Raycast', () => {

	let geometry = null;
	let mesh = null;
	let raycaster = null;
	beforeEach( () => {

		geometry = new THREE.BufferGeometry();
		geometry.addAttribute( 'position', new THREE.BufferAttribute( points.slice(), 3 ) );
		geometry.computeVertexNormals();

		mesh = new THREE.Mesh( geometry );

		// NOTE: If the geometry is not centered then the AVERAGE split strategy
		// case fails.
		geometry.computeBoundingBox();
		const center = new THREE.Vector3();
		geometry.boundingBox.getCenter( center );

		const x = 101086.2438562272 - center.x;
		const y = 99421.40510391879 - center.y;

		geometry.center();

		raycaster = new THREE.Raycaster();
		raycaster.firstHitOnly = true;
		raycaster.set( new THREE.Vector3( x, y, - 1000 ), new THREE.Vector3( 0, 0, 1 ) );

	} );

	it( 'should collide against the geometry with CENTER split', () => {

		geometry.boundsTree = new MeshBVH( geometry, {
			strategy: CENTER,
			maxDepth: 64,
			maxLeafTris: 16
		} );

		const res1 = raycaster.intersectObject( mesh );

		geometry.boundsTree = null;
		const res2 = raycaster.intersectObject( mesh );

		expect( res1 ).toEqual( res2 );

	} );

	it( 'should collide against the geometry with SAH split', () => {

		geometry.boundsTree = new MeshBVH( geometry, {
			strategy: SAH,
			maxDepth: 64,
			maxLeafTris: 16
		} );

		const res1 = raycaster.intersectObject( mesh );

		geometry.boundsTree = null;
		const res2 = raycaster.intersectObject( mesh );

		expect( res1 ).toEqual( res2 );

	} );

	it( 'should collide against the geometry with AVERAGE split', () => {

		geometry.boundsTree = new MeshBVH( geometry, {
			strategy: AVERAGE,
			maxDepth: 64,
			maxLeafTris: 16
		} );

		const res1 = raycaster.intersectObject( mesh );

		geometry.boundsTree = null;
		const res2 = raycaster.intersectObject( mesh );

		res1.length && delete res1[ 0 ].object;
		res2.length && delete res2[ 0 ].object;

		expect( res1 ).toEqual( res2 );

	} );

} );
<|endoftext|>


<commit_before>/**
 * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 */
	var basePath = CKEDITOR.basePath;
	basePath = basePath.substr(0, basePath.indexOf("ndf/"));
 	//function to add External plugins
	 (function() {
	   CKEDITOR.plugins.addExternal('addImage',basePath+'ndf/js/ckPlugins/addImage/','plugin.js');
	   CKEDITOR.plugins.addExternal('closebtn',basePath+'ndf/js/ckPlugins/closebtn/','plugin.js');

	})();

CKEDITOR.editorConfig = function( config ) {
	// Define changes to default configuration here.
	// For complete reference see:
	// http://docs.ckeditor.com/#!/api/CKEDITOR.config

	// The toolbar groups arrangement, optimized for two toolbar rows.
	config.toolbar_GeneralToolbar =
	[

		{ name: 'basicstyles', items : [ 'Bold','Italic','Strike','-','RemoveFormat' ] },
		{ name: 'editing', items : [ 'Find','Replace','-','SelectAll','-','Scayt' ] },
		{ name: 'insert', items : [ 'Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak','Iframe','video' ] },
		{ name: 'styles', items : [ 'Styles','Format' ] },
		{ name: 'paragraph', items : [ 'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote' ] },
		{ name: 'links', items : [ 'Link','Unlink' ] },
		{ name: 'tools', items : [ 'addImage', 'Source','Maximize','-'] },
	];

	config.toolbar_BasicToolbar =
	[
		{ name: 'basicstyles', items : [ 'Bold','Italic','Strike','-','RemoveFormat' ] },
		{ name: 'paragraph', items : [ 'NumberedList','BulletedList','-' ] },
		{ name: 'links', items : [ 'Link','Unlink'] },
		{ name: 'insert', items : [ 'Image','Flash','Iframe' ] },
		{ name: 'tools', items : [ 'addImage','Source', 'Maximize','-','closebtn'] },
	];

	// ];
	// config.toolbarGroups = [
	// 	{ name: 'clipboard',   groups: [ 'clipboard', 'undo' ] },
	// 	{ name: 'editing',     groups: [ 'find', 'selection', 'spellchecker' ] },
	// 	{ name: 'links' },
	// 	{ name: 'insert' },
	// 	{ name: 'forms' },
	// 	{ name: 'tools' },
	// 	{ name: 'document',	   groups: [ 'mode', 'document', 'doctools' ] },
	// 	{ name: 'others' },
	// 	'/',
	// 	{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
	// 	{ name: 'paragraph',   groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
	// 	{ name: 'styles' },
	// 	{ name: 'colors' },
	// 	{ name: 'addImage'}

	// ];

	// Remove some buttons provided by the standard plugins, which are
	// not needed in the Standard(s) toolbar.
	config.removeButtons = 'Underline,Subscript,Superscript';

	// Set the most common block elements.
	config.format_tags = 'p;h1;h2;h3;pre';

	// Simplify the dialog windows.
	config.removeDialogTabs = 'image:advanced;link:advanced';
	config.extraPlugins = 'addImage,closebtn';
	config.allowedContent = true;
};
<commit_msg>tab spacing enabled and ASCII code support ex.'' &<commit_after>/**
 * @license Copyright (c) 2003-2015, CKSource - Frederico Knabben. All rights reserved.
 * For licensing, see LICENSE.md or http://ckeditor.com/license
 */
	var basePath = CKEDITOR.basePath;
	basePath = basePath.substr(0, basePath.indexOf("ndf/"));
 	//function to add External plugins
	 (function() {
	   CKEDITOR.plugins.addExternal('addImage',basePath+'ndf/js/ckPlugins/addImage/','plugin.js');
	   CKEDITOR.plugins.addExternal('closebtn',basePath+'ndf/js/ckPlugins/closebtn/','plugin.js');

	})();

CKEDITOR.editorConfig = function( config ) {
	// Define changes to default configuration here.
	// For complete reference see:
	// http://docs.ckeditor.com/#!/api/CKEDITOR.config

	// The toolbar groups arrangement, optimized for two toolbar rows.
	config.toolbar_GeneralToolbar =
	[

		{ name: 'basicstyles', items : [ 'Bold','Italic','Strike','-','RemoveFormat' ] },
		{ name: 'editing', items : [ 'Find','Replace','-','SelectAll','-','Scayt' ] },
		{ name: 'insert', items : [ 'Image','Flash','Table','HorizontalRule','Smiley','SpecialChar','PageBreak','Iframe','video' ] },
		{ name: 'styles', items : [ 'Styles','Format' ] },
		{ name: 'paragraph', items : [ 'NumberedList','BulletedList','-','Outdent','Indent','-','Blockquote' ] },
		{ name: 'links', items : [ 'Link','Unlink' ] },
		{ name: 'tools', items : [ 'addImage', 'Source','Maximize','-'] },
	];

	config.toolbar_BasicToolbar =
	[
		{ name: 'basicstyles', items : [ 'Bold','Italic','Strike','-','RemoveFormat' ] },
		{ name: 'paragraph', items : [ 'NumberedList','BulletedList','-' ] },
		{ name: 'links', items : [ 'Link','Unlink'] },
		{ name: 'insert', items : [ 'Image','Flash','Iframe' ] },
		{ name: 'tools', items : [ 'addImage','Source', 'Maximize','-','closebtn'] },
	];

	// ];
	// config.toolbarGroups = [
	// 	{ name: 'clipboard',   groups: [ 'clipboard', 'undo' ] },
	// 	{ name: 'editing',     groups: [ 'find', 'selection', 'spellchecker' ] },
	// 	{ name: 'links' },
	// 	{ name: 'insert' },
	// 	{ name: 'forms' },
	// 	{ name: 'tools' },
	// 	{ name: 'document',	   groups: [ 'mode', 'document', 'doctools' ] },
	// 	{ name: 'others' },
	// 	'/',
	// 	{ name: 'basicstyles', groups: [ 'basicstyles', 'cleanup' ] },
	// 	{ name: 'paragraph',   groups: [ 'list', 'indent', 'blocks', 'align', 'bidi' ] },
	// 	{ name: 'styles' },
	// 	{ name: 'colors' },
	// 	{ name: 'addImage'}

	// ];

	// Remove some buttons provided by the standard plugins, which are
	// not needed in the Standard(s) toolbar.
	config.removeButtons = 'Underline,Subscript,Superscript';

	// Set the most common block elements.
	config.format_tags = 'p;h1;h2;h3;pre';
	config.entities = false; //set false to work with  ASCII such as  "" & '' in source code
	config.tabSpaces = 4; // for tab spacing

	// Simplify the dialog windows.
	config.removeDialogTabs = 'image:advanced;link:advanced';
	config.extraPlugins = 'addImage,closebtn';
	config.allowedContent = true;
};
<|endoftext|>


<commit_before>var assert = require('chai').assert;
var nodemock = require('nodemock');
var Deferred = require('jsdeferred').Deferred;

var utils = require('../test-utils');

var express = require('express');
var httpAdapter = require('../../lib/adapter/http');
var command = require('../../lib/adapter/command');
var restAPI = require('../../lib/adapter/api/rest');
var droongaAPI = require('../../lib/adapter/api/droonga');
var groongaAPI = require('../../lib/adapter/api/groonga');

suite('HTTP Adapter', function() {
  test('registeration of plugin commands', function() {
    var basePlugin = {
      getCommand: new command.HTTPCommand({
        path: '/get'
      }),
      putCommand: new command.HTTPCommand({
        method: 'PUT',
        path: '/put'
      }),
      postCommand: new command.HTTPCommand({
        method: 'POST',
        path: '/post'
      }),
      deleteCommand: new command.HTTPCommand({
        method: 'DELETE',
        path: '/delete'
      }),
      ignored: new command.SocketCommand()
    };
    var overridingPlugin = {
      postCommand: new command.HTTPCommand({
        method: 'POST',
        path: '/post/overridden'
      }),
      deleteCommand: new command.HTTPCommand({
        method: 'DELETE',
        path: '/delete/overridden'
      })
    };

    var application = express();
    var registeredCommands = httpAdapter.register(application, {
      prefix:     '',
      connection: utils.createStubbedBackendConnection(),
      plugins: [
        basePlugin,
        overridingPlugin
      ]
    });

    registeredCommands = registeredCommands.map(function(command) {
      return {
        name:       command.name,
        definition: command.definition
      };
    });
    assert.deepEqual(registeredCommands,
                     [{ name:       'search',
                        definition: restAPI.search },
                      { name:       'droonga',
                        definition: droongaAPI.droonga },
                      { name:       'groonga',
                        definition: groongaAPI.groonga },
                      { name:       'getCommand',
                        definition: basePlugin.getCommand },
                      { name:       'putCommand',
                        definition: basePlugin.putCommand },
                      { name:       'postCommand',
                        definition: overridingPlugin.postCommand },
                      { name:       'deleteCommand',
                        definition: overridingPlugin.deleteCommand }]);
  });

  suite('registeration', function() {
    var testPlugin = {
      adapter: new command.HTTPCommand({
        path: '/path/to/adapter',
        onRequest: function(request, connection) {
          connection.emit('adapter', 'adapter requested');
        },
        onResponse: function(data, response) {
          response.jsonp('adapter OK', 200);
        }
      })
    };

    var connection;
    var application;
    var server;
    var backend;

    setup(function(done) {
      utils.setupApplication()
        .next(function(result) {
          backend = result.backend;
          server = result.server;
          connection = result.connection;
          application = result.application;
          done();
        });
    });

    teardown(function() {
      utils.teardownApplication({ backend:    backend,
                                  server:     server,
                                  connection: connection });
    });

    test('to the document root', function(done) {
      httpAdapter.register(application, {
        prefix:     '',
        connection: connection,
        plugins:    [testPlugin]
      });

      backend.reserveResponse(function(request) {
        return utils.createReplyPacket(request,
                                       {
                                         statusCode: 200,
                                         body:       'Adapter response',
                                       });
      });

      utils.get('/path/to/adapter')
        .next(function(response) {
          backend.assertReceived([{ type: 'adapter',
                                    body: 'adapter requested' }]);
          assert.deepEqual(response,
                          { statusCode: 200,
                            body:       JSON.stringify('adapter OK') });
          done();
        })
        .error(function(error) {
          done(error);
        });
    });

    test('under specified path', function(done) {
      httpAdapter.register(application, {
        prefix:     '/path/to/droonga',
        connection: connection,
        plugins:    [testPlugin]
      });

      backend.reserveResponse(function(request) {
        return utils.createReplyPacket(request,
                                       {
                                         statusCode: 200,
                                         body:       'Adapter response',
                                       });
      });

      utils.get('/path/to/droonga/path/to/adapter')
        .next(function(response) {
          backend.assertReceived([{ type: 'adapter',
                                    body: 'adapter requested' }]);
          assert.deepEqual(response,
                          { statusCode: 200,
                            body:       JSON.stringify('adapter OK') });
          done();
        })
        .error(function(error) {
          done(error);
        });
    });
  });

  suite('default commands', function() {
    var server;

    teardown(function() {
      if (server) {
        server.close();
        server = undefined;
      }
    });

    test('search', function(done) {
      var receiverCallback = {};
      var connection = utils.createStubbedBackendConnection();
      var application = express();
      httpAdapter.register(application, {
        prefix:     '',
        connection: connection
      });
      utils.setupServer(application)
        .next(function(newServer) {
          server = newServer;
          utils.get('/tables/foo?query=bar');
        })
        .wait(0.1)
        .next(function() {
          assert.equal(1, connection.emitMessageCalledArguments.length);
          var args = connection.emitMessageCalledArguments[0];
          assert.equal(args.type, 'search');

          var expected = {
            queries: {
              result: {
                source: 'foo',
                condition: {
                  query: 'bar'
                },
                output: {
                  elements: utils.allElements,
                  attributes: []
                }
              }
            }
          };
          assert.equalJSON(args.message, expected);

          done();
        })
        .error(function(error) {
          done(error);
        });
    });
  });
});

<commit_msg>Add test for builtin droonga command<commit_after>var assert = require('chai').assert;
var nodemock = require('nodemock');
var Deferred = require('jsdeferred').Deferred;

var utils = require('../test-utils');

var express = require('express');
var httpAdapter = require('../../lib/adapter/http');
var command = require('../../lib/adapter/command');
var restAPI = require('../../lib/adapter/api/rest');
var droongaAPI = require('../../lib/adapter/api/droonga');
var groongaAPI = require('../../lib/adapter/api/groonga');

suite('HTTP Adapter', function() {
  test('registeration of plugin commands', function() {
    var basePlugin = {
      getCommand: new command.HTTPCommand({
        path: '/get'
      }),
      putCommand: new command.HTTPCommand({
        method: 'PUT',
        path: '/put'
      }),
      postCommand: new command.HTTPCommand({
        method: 'POST',
        path: '/post'
      }),
      deleteCommand: new command.HTTPCommand({
        method: 'DELETE',
        path: '/delete'
      }),
      ignored: new command.SocketCommand()
    };
    var overridingPlugin = {
      postCommand: new command.HTTPCommand({
        method: 'POST',
        path: '/post/overridden'
      }),
      deleteCommand: new command.HTTPCommand({
        method: 'DELETE',
        path: '/delete/overridden'
      })
    };

    var application = express();
    var registeredCommands = httpAdapter.register(application, {
      prefix:     '',
      connection: utils.createStubbedBackendConnection(),
      plugins: [
        basePlugin,
        overridingPlugin
      ]
    });

    registeredCommands = registeredCommands.map(function(command) {
      return {
        name:       command.name,
        definition: command.definition
      };
    });
    assert.deepEqual(registeredCommands,
                     [{ name:       'search',
                        definition: restAPI.search },
                      { name:       'droonga',
                        definition: droongaAPI.droonga },
                      { name:       'groonga',
                        definition: groongaAPI.groonga },
                      { name:       'getCommand',
                        definition: basePlugin.getCommand },
                      { name:       'putCommand',
                        definition: basePlugin.putCommand },
                      { name:       'postCommand',
                        definition: overridingPlugin.postCommand },
                      { name:       'deleteCommand',
                        definition: overridingPlugin.deleteCommand }]);
  });

  suite('registeration', function() {
    var testPlugin = {
      adapter: new command.HTTPCommand({
        path: '/path/to/adapter',
        onRequest: function(request, connection) {
          connection.emit('adapter', 'adapter requested');
        },
        onResponse: function(data, response) {
          response.jsonp('adapter OK', 200);
        }
      })
    };

    var connection;
    var application;
    var server;
    var backend;

    setup(function(done) {
      utils.setupApplication()
        .next(function(result) {
          backend = result.backend;
          server = result.server;
          connection = result.connection;
          application = result.application;
          done();
        });
    });

    teardown(function() {
      utils.teardownApplication({ backend:    backend,
                                  server:     server,
                                  connection: connection });
    });

    test('to the document root', function(done) {
      httpAdapter.register(application, {
        prefix:     '',
        connection: connection,
        plugins:    [testPlugin]
      });

      backend.reserveResponse(function(request) {
        return utils.createReplyPacket(request,
                                       {
                                         statusCode: 200,
                                         body:       'Adapter response',
                                       });
      });

      utils.get('/path/to/adapter')
        .next(function(response) {
          backend.assertReceived([{ type: 'adapter',
                                    body: 'adapter requested' }]);
          assert.deepEqual(response,
                          { statusCode: 200,
                            body:       JSON.stringify('adapter OK') });
          done();
        })
        .error(function(error) {
          done(error);
        });
    });

    test('under specified path', function(done) {
      httpAdapter.register(application, {
        prefix:     '/path/to/droonga',
        connection: connection,
        plugins:    [testPlugin]
      });

      backend.reserveResponse(function(request) {
        return utils.createReplyPacket(request,
                                       {
                                         statusCode: 200,
                                         body:       'Adapter response',
                                       });
      });

      utils.get('/path/to/droonga/path/to/adapter')
        .next(function(response) {
          backend.assertReceived([{ type: 'adapter',
                                    body: 'adapter requested' }]);
          assert.deepEqual(response,
                          { statusCode: 200,
                            body:       JSON.stringify('adapter OK') });
          done();
        })
        .error(function(error) {
          done(error);
        });
    });
  });

  suite('default commands', function() {
    var server;

    teardown(function() {
      if (server) {
        server.close();
        server = undefined;
      }
    });

    test('search', function(done) {
      var receiverCallback = {};
      var connection = utils.createStubbedBackendConnection();
      var application = express();
      httpAdapter.register(application, {
        prefix:     '',
        connection: connection
      });
      utils.setupServer(application)
        .next(function(newServer) {
          server = newServer;
          utils.get('/tables/foo?query=bar');
        })
        .wait(0.1)
        .next(function() {
          assert.equal(1, connection.emitMessageCalledArguments.length);
          var args = connection.emitMessageCalledArguments[0];
          assert.equal(args.type, 'search');

          var expected = {
            queries: {
              result: {
                source: 'foo',
                condition: {
                  query: 'bar'
                },
                output: {
                  elements: utils.allElements,
                  attributes: []
                }
              }
            }
          };
          assert.equalJSON(args.message, expected);

          done();
        })
        .error(function(error) {
          done(error);
        });
    });

    test('droonga', function(done) {
      var receiverCallback = {};
      var connection = utils.createStubbedBackendConnection();
      var application = express();
      application.use(express.bodyParser());
      httpAdapter.register(application, {
        prefix:     '',
        connection: connection
      });
      var searchQueries = {
        source: 'table'
      };
      utils.setupServer(application)
        .next(function(newServer) {
          server = newServer;
          utils.post('/droonga/search', JSON.stringify({ queries: searchQueries }));
        })
        .wait(0.1)
        .next(function() {
          assert.equal(1, connection.emitMessageCalledArguments.length);
          var args = connection.emitMessageCalledArguments[0];
          assert.equal(args.type, 'search');

          var expected = {
            queries: searchQueries,
            timeout: 1000,
            type:    'droonga-search'
          };
          assert.equalJSON(args.message, expected);

          done();
        })
        .error(function(error) {
          done(error);
        });
    });
  });
});

<|endoftext|>