<commit_before>import angular from 'angular';
import angularMeteor from 'angular-meteor';
import template from './textView.html';
import { Rooms } from '../../api/rooms.js';

const VIEW = `

`;
const LEFT = `
 \\
  \\
   +
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   +
  /
 /
`;
const RIGHT = `
                                /
                               /
                              +
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              +
                               \\
                                \\
`;
const FORWARD = `

   +--------------------------+
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   +--------------------------+

`;
const LEFT_FORWARD_RIGHT = `

 --+
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
 --+

`;
const RIGHT_FORWARD_LEFT = `

                              +--
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              +--

`;
const FORWARD_FORWARD = `

      +--------------------+
      |                    |
      |                    |
      |                    |
      |                    |
      |                    |
      |                    |
      |                    |
      +--------------------+

`;
const FORWARD_LEFT = `

   +
   |\\
   | \\
   |  +
   |  |
   |  |
   |  |
   |  |
   |  |
   |  |
   |  |
   |  +
   | /
   |/
   +

`;
const FORWARD_RIGHT = `

                              +
                             /|
                            / |
                           +  |
                           |  |
                           |  |
                           |  |
                           |  |
                           |  |
                           |  |
                           |  |
                           +  |
                            \\ |
                             \\|
                              +

`;
const FORWARD_LEFT_FORWARD_RIGHT = `

    --+
      |
      |
      |
      |
      |
      |
      |
    --+

`;
const FORWARD_RIGHT_FORWARD_LEFT = `

                           +--
                           |
                           |
                           |
                           |
                           |
                           |
                           |
                           +--

`;

class TextViewCtrl {

  constructor($scope, $document) {
    $scope.viewModel(this);
    this.view = '';
    this.x = 0;
    this.y = 0;
    this.direction = 'N';
    this.rooms = {};
    this.subscribe('rooms', function() {
      this.createRoomMap();
      this.view = this.render();
    });
    var that = this;
    $document.bind("keydown", function(event) {
      $scope.$apply(function(){
        that.handleKeyDown(event);
      });
    });
  }

  createRoomMap() {
    let roomy = Rooms.find().fetch();
    roomy.forEach(function(room) {
      Object.keys(room.passages).forEach(function(key) {
        room.passages[key] = roomy[room.passages[key]];
      });
    });
    this.rooms = {}
    roomy.forEach(r => {
      this.rooms[r.getKey()] = r;
    });
  }

  getRoom() {
    return(this.rooms[this.x.toString() + "," + this.y.toString()]);
  }

  render() {
    let room = this.getRoom();
    if (typeof room === 'undefined') {
      return 'nada';
    }

    let t = [VIEW.split("")]
    let left_room = room.goLeft(this.direction)
    let forward_room = room.goForward(this.direction)
    let right_room = room.goRight(this.direction)
    if (typeof left_room === 'undefined') {
      t.push(LEFT.split(""))
    }
    else {
      if (typeof left_room.goForward(this.direction) === 'undefined') {
        t.push(LEFT_FORWARD_RIGHT.split(""))
      }
    }
    if (typeof forward_room === 'undefined') {
      t.push(FORWARD.split(""))
    }
    else {
      let forward_left_room = forward_room.goLeft(this.direction)
      if (typeof forward_left_room === 'undefined') {
        t.push(FORWARD_LEFT.split(""))
      }
      else {
        if (typeof forward_left_room.goForward(this.direction) === 'undefined') {
          t.push(FORWARD_LEFT_FORWARD_RIGHT.split(""))
        }
      }
      if (typeof forward_room.goForward(this.direction) === 'undefined') {
        t.push(FORWARD_FORWARD.split(""))
      }
      let forward_right_room = forward_room.goRight(this.direction)
      if (typeof forward_right_room === 'undefined') {
        t.push(FORWARD_RIGHT.split(""))
      }
      else {
        if (typeof forward_right_room.goForward(this.direction) === 'undefined') {
          t.push(FORWARD_RIGHT_FORWARD_LEFT.split(""))
        }
      }
    }
    if (typeof right_room === 'undefined') {
      t.push(RIGHT.split(""))
    }
    else {
      if (typeof right_room.goForward(this.direction) === 'undefined') {
        t.push(RIGHT_FORWARD_LEFT.split(""))
      }
    }
    let zip= rows=>rows[0].map((_,c)=>rows.map(row=>row[c]))
    let z = zip(t);
    let result = z.map(row => {return row.reduce((a,b) => {return a>b?a:b;})});
    return(result.join(""));
  }

  handleKeyDown(e) {
    switch (e.key) {
    case 'w':
      this.goForward();
      this.view = this.render();
      break;
    case 'a':
      this.goLeft();
      this.view = this.render();
      break;
    case 's':
      this.goBackward();
      this.view = this.render();
      break;
    case 'd':
      this.goRight();
      this.view = this.render();
      break;
    default:
      return;
    }
    return;
  }

  goForward() {
    let room = this.getRoom();
    if (typeof room === 'undefined') {
      return;
    }
    let forward_room = room.goForward(this.direction)
    if (typeof forward_room === 'undefined') {
      return;
    }
    this.x = forward_room.x;
    this.y = forward_room.y;
  }

  goBackward() {
  }

  goLeft() {
    switch (this.direction) {
    case 'N':
      this.direction = 'W';
      break;
    case 'E':
      this.direction = 'N';
      break;
    case 'S':
      this.direction = 'E';
      break;
    default:
      this.direction = 'S';
      break;
    }
  }

  goRight() {
    switch (this.direction) {
    case 'N':
      this.direction = 'E';
      break;
    case 'E':
      this.direction = 'S';
      break;
    case 'S':
      this.direction = 'W';
      break;
    default:
      this.direction = 'N';
      break;
    }
  }
}

export default angular.module('maze', [
  angularMeteor
])
  .component('textView', {
    templateUrl: 'imports/components/textView/textView.html',
    controller: ['$scope', '$document', TextViewCtrl]
  });
<commit_msg>add go backwards<commit_after>import angular from 'angular';
import angularMeteor from 'angular-meteor';
import template from './textView.html';
import { Rooms } from '../../api/rooms.js';

const VIEW = `

`;
const LEFT = `
 \\
  \\
   +
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   +
  /
 /
`;
const RIGHT = `
                                /
                               /
                              +
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              +
                               \\
                                \\
`;
const FORWARD = `

   +--------------------------+
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   |                          |
   +--------------------------+

`;
const LEFT_FORWARD_RIGHT = `

 --+
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
   |
 --+

`;
const RIGHT_FORWARD_LEFT = `

                              +--
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              |
                              +--

`;
const FORWARD_FORWARD = `

      +--------------------+
      |                    |
      |                    |
      |                    |
      |                    |
      |                    |
      |                    |
      |                    |
      +--------------------+

`;
const FORWARD_LEFT = `

   +
   |\\
   | \\
   |  +
   |  |
   |  |
   |  |
   |  |
   |  |
   |  |
   |  |
   |  +
   | /
   |/
   +

`;
const FORWARD_RIGHT = `

                              +
                             /|
                            / |
                           +  |
                           |  |
                           |  |
                           |  |
                           |  |
                           |  |
                           |  |
                           |  |
                           +  |
                            \\ |
                             \\|
                              +

`;
const FORWARD_LEFT_FORWARD_RIGHT = `

    --+
      |
      |
      |
      |
      |
      |
      |
    --+

`;
const FORWARD_RIGHT_FORWARD_LEFT = `

                           +--
                           |
                           |
                           |
                           |
                           |
                           |
                           |
                           +--

`;

class TextViewCtrl {

  constructor($scope, $document) {
    $scope.viewModel(this);
    this.view = '';
    this.x = 0;
    this.y = 0;
    this.direction = 'N';
    this.rooms = {};
    this.subscribe('rooms', function() {
      this.createRoomMap();
      this.view = this.render();
    });
    var that = this;
    $document.bind("keydown", function(event) {
      $scope.$apply(function(){
        that.handleKeyDown(event);
      });
    });
  }

  createRoomMap() {
    let roomy = Rooms.find().fetch();
    roomy.forEach(function(room) {
      Object.keys(room.passages).forEach(function(key) {
        room.passages[key] = roomy[room.passages[key]];
      });
    });
    this.rooms = {}
    roomy.forEach(r => {
      this.rooms[r.getKey()] = r;
    });
  }

  getRoom() {
    return(this.rooms[this.x.toString() + "," + this.y.toString()]);
  }

  render() {
    let room = this.getRoom();
    if (typeof room === 'undefined') {
      return 'nada';
    }

    let t = [VIEW.split("")]
    let left_room = room.goLeft(this.direction)
    let forward_room = room.goForward(this.direction)
    let right_room = room.goRight(this.direction)
    if (typeof left_room === 'undefined') {
      t.push(LEFT.split(""))
    }
    else {
      if (typeof left_room.goForward(this.direction) === 'undefined') {
        t.push(LEFT_FORWARD_RIGHT.split(""))
      }
    }
    if (typeof forward_room === 'undefined') {
      t.push(FORWARD.split(""))
    }
    else {
      let forward_left_room = forward_room.goLeft(this.direction)
      if (typeof forward_left_room === 'undefined') {
        t.push(FORWARD_LEFT.split(""))
      }
      else {
        if (typeof forward_left_room.goForward(this.direction) === 'undefined') {
          t.push(FORWARD_LEFT_FORWARD_RIGHT.split(""))
        }
      }
      if (typeof forward_room.goForward(this.direction) === 'undefined') {
        t.push(FORWARD_FORWARD.split(""))
      }
      let forward_right_room = forward_room.goRight(this.direction)
      if (typeof forward_right_room === 'undefined') {
        t.push(FORWARD_RIGHT.split(""))
      }
      else {
        if (typeof forward_right_room.goForward(this.direction) === 'undefined') {
          t.push(FORWARD_RIGHT_FORWARD_LEFT.split(""))
        }
      }
    }
    if (typeof right_room === 'undefined') {
      t.push(RIGHT.split(""))
    }
    else {
      if (typeof right_room.goForward(this.direction) === 'undefined') {
        t.push(RIGHT_FORWARD_LEFT.split(""))
      }
    }
    let zip= rows=>rows[0].map((_,c)=>rows.map(row=>row[c]))
    let z = zip(t);
    let result = z.map(row => {return row.reduce((a,b) => {return a>b?a:b;})});
    return(result.join(""));
  }

  handleKeyDown(e) {
    switch (e.key) {
    case 'w':
      this.goForward();
      this.view = this.render();
      break;
    case 'a':
      this.goLeft();
      this.view = this.render();
      break;
    case 's':
      this.goBackward();
      this.view = this.render();
      break;
    case 'd':
      this.goRight();
      this.view = this.render();
      break;
    default:
      return;
    }
    return;
  }

  goForward() {
    let room = this.getRoom();
    if (typeof room === 'undefined') {
      return;
    }
    let destination = room.goForward(this.direction)
    if (typeof destination === 'undefined') {
      return;
    }
    this.x = destination.x;
    this.y = destination.y;
  }

  goBackward() {
    let room = this.getRoom();
    if (typeof room === 'undefined') {
      return;
    }
    let destination = room.goBackward(this.direction)
    if (typeof destination === 'undefined') {
      return;
    }
    this.x = destination.x;
    this.y = destination.y;
  }

  goLeft() {
    switch (this.direction) {
    case 'N':
      this.direction = 'W';
      break;
    case 'E':
      this.direction = 'N';
      break;
    case 'S':
      this.direction = 'E';
      break;
    default:
      this.direction = 'S';
      break;
    }
  }

  goRight() {
    switch (this.direction) {
    case 'N':
      this.direction = 'E';
      break;
    case 'E':
      this.direction = 'S';
      break;
    case 'S':
      this.direction = 'W';
      break;
    default:
      this.direction = 'N';
      break;
    }
  }
}

export default angular.module('maze', [
  angularMeteor
])
  .component('textView', {
    templateUrl: 'imports/components/textView/textView.html',
    controller: ['$scope', '$document', TextViewCtrl]
  });
<|endoftext|>


<commit_before>/*!
 * @author John Ropas
 * @since 17/12/2016
 */

// former initBlocklyRunner

function RunController(ctx, msgCallback) {

  this.context = ctx;
  this.messageCallback = msgCallback;
  this.interpreters = [];
  this.isRunning = [];
  this.toStop = [];
  this.stopPrograms = false;

  this.waitDelay = function (callback, value, delay) {
    if (delay > 0) {
      var identifier = "wait" + this.context.curRobot + "_" + Math.random();
      var that = this;
      this.context.delayFactory.createTimeout(identifier, function () {
        that.noDelay(callback, value)
      }, delay);
    } else {
      this.noDelay(callback, value);
    }
  };

  this.noDelay = function (callback, value) {
    var primitive = undefined;
    if (value != undefined) {
      primitive = this.interpreters[context.curRobot].createPrimitive(value);
    }
    if (Math.random() < 0.1) {
      var identifier = "wait_" + Math.random();
      var that = this;
      context.delayFactory.createTimeout(identifier, function () {
        callback(primitive);
        that.runSyncBlock();
      }, 0);
    } else {
      callback(primitive);
      this.runSyncBlock();
    }
  };

  this.initInterpreter = function (interpreter, scope) {
    for (var objectName in this.context.customBlocks) {
      for (var iCategory in this.context.customBlocks[objectName]) {
        for (var iBlock in this.context.customBlocks[objectName][iCategory].blocks) {
          var blockInfo = this.context.customBlocks[objectName][iCategory].blocks[iBlock];
          var code = this.context.strings.code[blockInfo.name];
          if (typeof(code) == "undefined") {
            code = blockInfo.name;
          }
          interpreter.setProperty(scope, code, interpreter.createAsyncFunction(blockInfo.handler));
        }
      }
    }

    interpreter.setProperty(scope, "program_end", interpreter.createAsyncFunction(this.context.program_end));

    var that = this;

    function highlightBlock(id) {
      if (that.context.display) {
        that.context.blocklyHelper._workspace.highlightBlock(id);
        highlightPause = true;
      }
    }

    // Add an API function for highlighting blocks.
    var wrapper = function (id) {
      id = id ? id.toString() : '';
      return interpreter.createPrimitive(highlightBlock(id));
    };
    interpreter.setProperty(scope, 'highlightBlock', interpreter.createNativeFunction(wrapper));
  };

  this.stop = function () {
    for (var iInterpreter = 0; iInterpreter < interpreters.length; iInterpreter++) {
      if (this.isRunning[iInterpreter]) {
        this.toStop[iInterpreter] = true;
      }
    }
    context.reset();
  };

  this.runSyncBlock = function () {
    var maxIter = 20000;
    /*      if (turn > 90) {
     task.program_end(function() {
     that.stop();
     });
     return;
     }*/
    try {
      for (var iInterpreter = 0; iInterpreter < this.interpreters.length; iInterpreter += 1) {
        this.context.curRobot = iInterpreter;
        if (this.context.infos.checkEndEveryTurn) {
          this.context.infos.checkEndCondition(this.context, false);
        }
        var interpreter = this.interpreters[iInterpreter];
        while (this.context.curSteps[iInterpreter] < maxIter) {
          if (!interpreter.step() || this.toStop[iInterpreter]) {
            this.isRunning[iInterpreter] = false;
            break;
          }
          if (interpreter.paused_) {
            break;
          }
          this.context.curSteps[iInterpreter]++;
        }
        if (this.context.curSteps[iInterpreter] >= maxIter) {
          this.isRunning[iInterpreter] = false;
          throw this.context.blocklyHelper.strings.tooManyIterations;
        }
      }
    } catch (e) {
      for (var iInterpretr = 0; iInterpretr < this.interpreters.length; iInterpretr += 1) {
        this.isRunning[iInterpretr] = false;
      }

      var message = e.toString();
      if ((this.context.nbTestCases != undefined) && (this.context.nbTestCases > 1)) {
        if (this.context.success) {
          message = this.context.messagePrefixSuccess + message;
        } else {
          message = this.context.messagePrefixFailure + message;
        }
      }
      if (this.context.success) {
        message = "" + message + "";
        if (this.context.linkBack) {
          //message += "
Retour à la liste des questions";
        }
      }
      this.messageCallback(message);
    }
  };

  this.runCodes = function (codes) {
    //this.mainContext.delayFactory.stopAll(); pb: it would top existing graders
    this.interpreters = [];
    this.context.programEnded = [];
    this.context.curSteps = [];
    this.context.reset();
    for (var iInterpreter = 0; iInterpreter < codes.length; iInterpreter += 1) {
      this.context.curSteps[iInterpreter] = 0;
      this.context.programEnded[iInterpreter] = false;
      this.interpreters.push(new Interpreter(codes[iInterpreter], this.initInterpreter.bind(this)));
      this.isRunning[iInterpreter] = true;
      this.toStop[iInterpreter] = false;
    }
    this.runSyncBlock();
  };

  this.nbRunning = function () {
    var nbRunning = 0;
    for (var iInterpreter = 0; iInterpreter < this.interpreters.length; iInterpreter += 1) {
      if (this.isRunning[iInterpreter]) {
        nbRunning++;
      }
    }
    return nbRunning;
  };

  this.context.runner = this;
  this.context.callCallback = this.noDelay;
  this.context.programEnded = [];
}

CodeEditor.Controllers.RunController = RunController;
<commit_msg>javascript interpeter working<commit_after>/*!
 * @author John Ropas
 * @since 17/12/2016
 */

// former initBlocklyRunner

function RunController(ctx, msgCallback) {

  this.context = ctx;
  this.messageCallback = msgCallback;
  this.interpreters = [];
  this.isRunning = [];
  this.toStop = [];
  this.stopPrograms = false;

  this.waitDelay = function (callback, value, delay) {
    if (delay > 0) {
      var identifier = "wait" + this.context.curRobot + "_" + Math.random();
      var that = this;
      this.context.delayFactory.createTimeout(identifier, function () {
        that.noDelay(callback, value)
      }, delay);
    } else {
      this.noDelay(callback, value);
    }
  };

  this.noDelay = function (callback, value) {
    var primitive = undefined;
    if (value != undefined) {
      primitive = this.interpreters[this.context.curRobot].createPrimitive(value);
    }
    if (Math.random() < 0.1) {
      var identifier = "wait_" + Math.random();
      var that = this;
      this.context.delayFactory.createTimeout(identifier, function () {
        callback(primitive);
        that.runSyncBlock();
      }, 0);
    } else {
      callback(primitive);
      this.runSyncBlock();
    }
  };

  this.initInterpreter = function (interpreter, scope) {
    for (var objectName in this.context.customBlocks) {
      for (var iCategory in this.context.customBlocks[objectName]) {
        for (var iBlock in this.context.customBlocks[objectName][iCategory].blocks) {
          var blockInfo = this.context.customBlocks[objectName][iCategory].blocks[iBlock];
          var code = this.context.strings.code[blockInfo.name];
          if (typeof(code) == "undefined") {
            code = blockInfo.name;
          }
          interpreter.setProperty(scope, code, interpreter.createAsyncFunction(blockInfo.handler));
        }
      }
    }

    interpreter.setProperty(scope, "program_end", interpreter.createAsyncFunction(this.context.program_end));

    var that = this;

    function highlightBlock(id) {
      if (that.context.display) {
        that.context.blocklyHelper._workspace.highlightBlock(id);
        highlightPause = true;
      }
    }

    // Add an API function for highlighting blocks.
    var wrapper = function (id) {
      id = id ? id.toString() : '';
      return interpreter.createPrimitive(highlightBlock(id));
    };
    interpreter.setProperty(scope, 'highlightBlock', interpreter.createNativeFunction(wrapper));
  };

  this.stop = function () {
    for (var iInterpreter = 0; iInterpreter < interpreters.length; iInterpreter++) {
      if (this.isRunning[iInterpreter]) {
        this.toStop[iInterpreter] = true;
      }
    }
    context.reset();
  };

  this.runSyncBlock = function () {
    var maxIter = 20000;
    /*      if (turn > 90) {
     task.program_end(function() {
     that.stop();
     });
     return;
     }*/
    try {
      for (var iInterpreter = 0; iInterpreter < this.interpreters.length; iInterpreter += 1) {
        this.context.curRobot = iInterpreter;
        if (this.context.infos.checkEndEveryTurn) {
          this.context.infos.checkEndCondition(this.context, false);
        }
        var interpreter = this.interpreters[iInterpreter];
        while (this.context.curSteps[iInterpreter] < maxIter) {
          if (!interpreter.step() || this.toStop[iInterpreter]) {
            this.isRunning[iInterpreter] = false;
            break;
          }
          if (interpreter.paused_) {
            break;
          }
          this.context.curSteps[iInterpreter]++;
        }
        if (this.context.curSteps[iInterpreter] >= maxIter) {
          this.isRunning[iInterpreter] = false;
          throw this.context.blocklyHelper.strings.tooManyIterations;
        }
      }
    } catch (e) {
      for (var iInterpretr = 0; iInterpretr < this.interpreters.length; iInterpretr += 1) {
        this.isRunning[iInterpretr] = false;
      }

      var message = e.toString();
      if ((this.context.nbTestCases != undefined) && (this.context.nbTestCases > 1)) {
        if (this.context.success) {
          message = this.context.messagePrefixSuccess + message;
        } else {
          message = this.context.messagePrefixFailure + message;
        }
      }
      if (this.context.success) {
        message = "" + message + "";
        if (this.context.linkBack) {
          //message += "
Retour à la liste des questions";
        }
      }
      this.messageCallback(message);
    }
  };

  this.runCodes = function (codes) {
    //this.mainContext.delayFactory.stopAll(); pb: it would top existing graders
    this.interpreters = [];
    this.context.programEnded = [];
    this.context.curSteps = [];
    this.context.reset();
    for (var iInterpreter = 0; iInterpreter < codes.length; iInterpreter += 1) {
      this.context.curSteps[iInterpreter] = 0;
      this.context.programEnded[iInterpreter] = false;
      this.interpreters.push(new Interpreter(codes[iInterpreter], this.initInterpreter.bind(this)));
      this.isRunning[iInterpreter] = true;
      this.toStop[iInterpreter] = false;
    }
    this.runSyncBlock();
  };

  this.nbRunning = function () {
    var nbRunning = 0;
    for (var iInterpreter = 0; iInterpreter < this.interpreters.length; iInterpreter += 1) {
      if (this.isRunning[iInterpreter]) {
        nbRunning++;
      }
    }
    return nbRunning;
  };

  this.context.runner = this;
  this.context.callCallback = this.noDelay;
  this.context.programEnded = [];
}

CodeEditor.Controllers.RunController = RunController;
<|endoftext|>


<commit_before>import * as d3 from 'd3';
import _ from 'lodash';
import { services } from '../services/index';
import { geoExtent } from '../geo/index';
import { osmEntity } from '../osm/entity';

var context,
    availableLayers = {}, //A map of key=mapid, value=name
    loadedLayers = {}, //A map of key=mapid, value=layer object
    changes = {}; //A changes object set by deriveChangeset
function getNodeMapnikSource(d) {
    var source = {
            name: d.name,
            id: d.id,
            type: 'tms',
            description: d.name,
            template: window.location.protocol + '//' + window.location.hostname
                //+ Hoot.model.REST.formatNodeJsPortOrPath(iD.data.hootConfig.nodeMapnikServerPort)
                + ':8000'
                + '/?z={zoom}&x={x}&y={-y}&color='
                + encodeURIComponent(services.hoot.palette(d.color))
                + '&mapid=' + d.id,
            scaleExtent: [0,16],
            polygon: d.polygon,
            overzoom: false,
            overlay: true,
            hootlayer: true
        };
    return source;
}

export default {

    init: function(c) {
        context = c;
    },

    changes: function() {
        return changes;
    },

    changesLength: function() {
        return _.size(changes) === 0 ? 0 : changes.created.length + changes.deleted.length + changes.modified.length;
    },

    hasChanges: function() {
        return _.size(changes) > 0;
    },

    availableLayers: function(callback) {
        d3.json(this.urlroot() + '/api/0.6/map/layers', function (error, resp) {
            if (error) {
                alert('Get available layers failed!');
                    //callback(null); Do we even need to callback?
            } else {
                availableLayers = resp.layers.reduce(function(lyrs, lyr) {
                    lyrs[lyr.id] = lyr.name;
                    return lyrs;
                }, {});
                callback(resp.layers);
            }
        });
    },

    reviewStatistics: function(mapid, callback) {
        d3.json('/hoot-services/job/review/statistics?mapId=' + mapid, function (error, resp) {
            return callback(error, resp);
        });
    },

    changesToHistory: function(changes) {
        if (!this.hasChanges()) return;

        var s = [{}, {
            modified: changes.created.concat(changes.modified).map(function(d) {
                return d.id + 'v0';
            }),
            deleted: changes.deleted.map(function(d) {
                return d.id;
            }),
            imageryUsed: ["None"],
            annotation: "Hootenanny conflated."
        }];

        return JSON.stringify({
            version: 3,
            entities: changes.created.concat(changes.modified),
            baseEntities: changes.deleted,
            stack: s,
            nextIDs: osmEntity.id.next,
            index: 1
        });

    },

    deriveChangeset: function(mapid, extent, callback) {
// {
//     "input": "678",
//     "inputtype": "db",
//     "outputtype": "osc",
//     "outputname": "changeset",
//     "USER_ID": "1",
//     "TASK_BBOX": "34.040297648723,31.174810720402537,34.06423943771007,31.186143336347865",
//     "translation": "NONE"
// }
        var json = {
            input: mapid.toString(),
            inputtype: 'db',
            outputtype: 'osc',
            USER_ID: '1',
            TASK_BBOX: extent.toParam(),
            buffer: '0.0',
            translation: 'NONE',
            textstatus: 'false',
            append: 'false'
        };

        var userid = services.osm.userDetails(function(error, dets) {
            if (error) {
                console.error(error);
            } else {
                console.log(dets);
            }
        });

        d3.json('/hoot-services/job/export/execute')
            .header("Content-Type", "application/json")
            .post(JSON.stringify(json), function (error, resp) {
                if (error) {
                    alert('Derive changeset failed!');
                } else {
                    services.hoot.pollJob(resp.jobid, function() {
                        d3.xml('/hoot-services/job/export/xml/' + resp.jobid + '?ext=osc', function(data) {
                            //console.log(data);
                            changes = services.osm.parseChangeset(data);
                            //console.log(JSON.stringify(changes));
                            var conflateHistory = services.hoot.changesToHistory(changes);
                            //console.log(conflateHistory);
                            //Remove this delay and coordinate callbacks if necessary
                            if (conflateHistory) {
                                //window.setTimeout(function() {
                                    context.history().fromJSON(conflateHistory, false);
                                //}, 5000);
                            }
                        });
                    });
                }
            });

        callback();

    },

    pollJob: function(jobid, callback) {
        function jobStatus () {
            d3.json('/hoot-services/job/status/' + jobid, function (error, resp) {
                if (error) {
                    clearInterval(job);
                    alert('Job status failed!');
                }
                if (resp.status === 'complete') {
                    clearInterval(job);
                    callback(resp);
                }
                if (resp.status === 'failed') {
                    clearInterval(job);
                    alert('Job ' + jobid + ' failed!');
                }
            });
        }

        var job = setInterval(jobStatus, 2000);
    },

    loadLayer: function(mapid, source, color, callback) {

        var name = availableLayers[mapid];
        d3.json(services.hoot.urlroot() + '/api/0.6/map/tags?mapid=' + mapid, function (error, tags) {
            if (error) {
                alert('Get map tags failed!');
                //callback(null);
            } else {
                console.log(tags);
                d3.json(services.hoot.urlroot() + '/api/0.6/map/mbr?mapId=' + mapid, function (error, mbr) {
                    if (error) {
                        //The map is empty, so assume a global extent
                        mbr = {
                            "minlon":-180,
                            "minlat":-90,
                            "maxlon":180,
                            "maxlat":90,
                            "nodescount":0
                        };
                    }

                    services.hoot.reviewStatistics(mapid, function(error, stats) {
                        if (error) {
                            alert('Get review statistics failed!');
                            //callback(null);
                        } else {
                            //mbr response sample
                            //{"minlon":-77.05030495606161,"firstlon":-77.0492105,"maxlat":38.9272,"nodescount":157,"minlat":38.9137226,"firstlat":38.9266803,"maxlon":-77.0301}

                            //Zoom map to layer extent & Add node-mapnik tile layer
                            var min = [mbr.minlon, mbr.minlat],
                                max = [mbr.maxlon, mbr.maxlat];

                            var layerExtent = new geoExtent(min, max);

                            //A layer is merged (the product of conflation) if it has map tag
                            //metadata indicating the two input sources or if it has reviews
                            //This calculation will miss a layer that has been exported (and loses
                            //its map metadata tags) and never had reviews.
                            var isMerged = Boolean(tags.input1 && tags.input2) || stats.totalCount > 0;

                            //Determine if layer is merged and set colors if so
                            if (isMerged) {
                                //Add source layers to loadedLayers
                                if (tags.input1 && tags.input2) {
                                    loadedLayers[tags.input1] = {
                                        name: tags.input1Name,
                                        id: tags.input1,
                                        color: 'purple',
                                        visible: false,
                                        merged: false
                                    };
                                    loadedLayers[tags.input2] = {
                                        name: tags.input2Name,
                                        id: tags.input2,
                                        color: 'gold',
                                        visible: false,
                                        merged: false
                                    };

                                    //Get extent for source layers
                                    [tags.input1, tags.input2].forEach(function(sourceid) {
                                        d3.json(services.hoot.urlroot() + '/api/0.6/map/mbr?mapId=' + sourceid, function (error, mbr) {
                                            if (error) {
                                                //The map is empty, so assume a global extent
                                                mbr = {
                                                    "minlon":-180,
                                                    "minlat":-90,
                                                    "maxlon":180,
                                                    "maxlat":90,
                                                    "nodescount":0
                                                };
                                            } else {
                                                var min = [mbr.minlon, mbr.minlat],
                                                    max = [mbr.maxlon, mbr.maxlat];

                                                var layerExtent = new geoExtent(min, max);
                                                loadedLayers[sourceid].extent = layerExtent;
                                            }
                                        });
                                    });

                                }
                                //Add css rule to render features by hoot:status
                                // 1 = reference source (purple)
                                // 2 = secondary source (gold)
                                // 3 = merged (active color or pink)
                                services.hoot.setLayerColor(tags.input1 || 1, loadedLayers[tags.input1].color);
                                services.hoot.setLayerColor(tags.input2 || 2, loadedLayers[tags.input2].color);

                            }

                            //Store info on the loaded layer
                            loadedLayers[mapid] = {
                                name: name,
                                id: mapid,
                                polygon: [layerExtent.polygon()],
                                color: color,
                                source: source,
                                tags: tags,
                                visible: true,
                                merged: isMerged
                            };

                            //Add css rule to render features by layer mapid
                            services.hoot.setLayerColor(mapid, color);

                            //Issue callback passing extent and node-mapnik background source and
                            //possibly a new layer color (green) if loaded layer is merged
                            if (callback) callback(layerExtent, getNodeMapnikSource(loadedLayers[mapid]), color);
                        }
                    });
                });
            }
        });
    },

    removeLayer: function(mapid) {
        var ids = [mapid];
        //If merged, delete source layers
        if (loadedLayers[mapid].merged) {
            ids.push(loadedLayers[mapid].tags.input1);
            ids.push(loadedLayers[mapid].tags.input2);
        }
        //Remove internal tracking data
        ids.forEach(function(mapid) { delete loadedLayers[mapid]; });
        //Remove osm vector data
        ids.forEach(services.osm.loadedDataRemove);
        //Remove node-mapnik layer
        context.background().removeSource(mapid);
        //Remove hoot bbox layer
        var hootOverlay = context.layers().layer('hoot');
        if (hootOverlay) {
            hootOverlay.geojson(hootOverlay.geojson().filter(function(d) {
                return d.properties.mapid !== mapid;
            }));
        }

        context.flush();

    },

    toggleMergedLayer: function(mapid) {
        //If merged, swap visibility of merged and input source layers
        if (loadedLayers[mapid].merged) {
            var input1 = loadedLayers[mapid].tags.input1;
            var input2 = loadedLayers[mapid].tags.input2;
            var viz = loadedLayers[mapid].visible;

            loadedLayers[mapid].visible = !viz;
            loadedLayers[input1].visible = viz;
            loadedLayers[input2].visible = viz;

            if (viz) {
                services.osm.loadedDataRemove(mapid);
                console.log('removing ' + mapid);
            } else {
                services.osm.loadedDataRemove(input1);
                services.osm.loadedDataRemove(input2);
                console.log('removing ' + input1 + ' & ' + input2);
            }
            context.flush();  //must do this to remove cached features

            //If an input layer is osm api db, call derive changeset
            //and we're toggling to the osm api layer
            if ((input1 === '-1' || input2 === '-1') && viz) {
                context.connection().on('loaded.hootchangeset', function() {
                    console.log('loaded.hootchangeset');
                    //Use gpx task grid or secondary layer
                    var extent;
                    //If a Tasking Manager grid is present, provide this bbox to the derive changeset job
                    var gj = context.layers().layer('gpx');
                    if (gj.hasGpx()) {
                        //get the task grid feature extent
                        extent = geoExtent(d3.geoBounds(gj.geojson()));
                        console.log(extent.toParam());
                    } else {
                        var sourceid = (input1 === '-1') ? input2 : input1;
                        extent = loadedLayers[sourceid].extent;
                    }
                    services.hoot.deriveChangeset(mapid, extent, function() {
                        context.connection().on('loaded.hootchangeset', null);
                    });
                });
            }

        }

    },

    changeLayerColor: function(lyrmenu, callback) {
        var lyr = d3.values(loadedLayers).find(function(d) {
            return d.source === lyrmenu.id;
        });
        if (lyr) {
            var mapid = lyr.id;
            loadedLayers[mapid].color = lyrmenu.color;
            services.hoot.setLayerColor(mapid, lyrmenu.color);
            if (callback) {
                callback(getNodeMapnikSource(loadedLayers[mapid]));
            }
        }
    },

    decodeHootStatus: function(status) {
        if (status === 'Input1') {
            return 1;
        }
        if (status === 'Input2') {
            return 2;
        }
        if (status === 'Conflated') {
            return 3;
        }

        return parseInt(status);
    },

    loadedLayers: function() {
        return loadedLayers;
    },

    layerBySource: function(s) {
        return d3.values(loadedLayers).find(function(d) {
            return d.source === s;
        }).id;
    },

    hasLayers: function() {
        return d3.map(loadedLayers).size() > 0;
    },

    hasOsmLayer: function() {
        return loadedLayers[-1];
    },

    layerName: function(mapid) {
        return availableLayers[mapid];
    },

    palette: function(co) {
        var palette = [{
                name: 'gold',
                hex: '#ffcc00'
        }, {
                name: 'orange',
                hex: '#ff7f2a'
        }, {
                name: 'violet',
                hex: '#ff5599'
        }, {
                name: 'purple',
                hex: '#e580ff'
        }, {
                name: 'blue',
                hex: '#5fbcd3'
        }, {
                name: 'teal',
                hex: '#5fd3bc'
        }, {
                name: 'green',
                hex: '#A7C973'
        // }, {
        //         name: 'osm',
        //         hex: ''
        }];
        if (!co) return palette;
        var obj = _.find(palette, function(a) {
            return a.name === co || a.hex === co;
        });
        if (obj === undefined) {
            obj = palette[1];
            co = obj.name;
        }
        return (obj.name === co) ? obj.hex : obj.name;
    },

    setLayerColor: function(mapid, color) {
        var sheets = document.styleSheets[document.styleSheets.length - 1];

        //Delete existing rules for mapid
        for (var i = 0; i < sheets.cssRules.length; i++) {
            var rule = sheets.cssRules[i];
            if (rule.cssText.includes('tag-hoot-' + mapid))
                sheets.deleteRule(i);
        }

        //Insert new color rules for mapid
        color = this.palette(color);
        var lighter = d3.rgb(color).brighter();
        sheets.insertRule('path.stroke.tag-hoot-' + mapid + ' { stroke:' + color + '}', sheets.cssRules.length - 1);
        sheets.insertRule('path.shadow.tag-hoot-' + mapid + ' { stroke:' + lighter + '}', sheets.cssRules.length - 1);
        sheets.insertRule('path.fill.tag-hoot-' + mapid + ' { fill:' + lighter + '}', sheets.cssRules.length - 1);
        sheets.insertRule('g.point.tag-hoot-' + mapid + ' .stroke { fill:' + color + '}', sheets.cssRules.length - 1);
    },

    urlroot: function() {
        return '/hoot-services/osm';
    }
};<commit_msg>If TASK_BBOX is present in tags, use that for extent in derive changeset<commit_after>import * as d3 from 'd3';
import _ from 'lodash';
import { services } from '../services/index';
import { geoExtent } from '../geo/index';
import { osmEntity } from '../osm/entity';

var context,
    availableLayers = {}, //A map of key=mapid, value=name
    loadedLayers = {}, //A map of key=mapid, value=layer object
    changes = {}; //A changes object set by deriveChangeset
function getNodeMapnikSource(d) {
    var source = {
            name: d.name,
            id: d.id,
            type: 'tms',
            description: d.name,
            template: window.location.protocol + '//' + window.location.hostname
                //+ Hoot.model.REST.formatNodeJsPortOrPath(iD.data.hootConfig.nodeMapnikServerPort)
                + ':8000'
                + '/?z={zoom}&x={x}&y={-y}&color='
                + encodeURIComponent(services.hoot.palette(d.color))
                + '&mapid=' + d.id,
            scaleExtent: [0,16],
            polygon: d.polygon,
            overzoom: false,
            overlay: true,
            hootlayer: true
        };
    return source;
}

export default {

    init: function(c) {
        context = c;
    },

    changes: function() {
        return changes;
    },

    changesLength: function() {
        return _.size(changes) === 0 ? 0 : changes.created.length + changes.deleted.length + changes.modified.length;
    },

    hasChanges: function() {
        return _.size(changes) > 0;
    },

    availableLayers: function(callback) {
        d3.json(this.urlroot() + '/api/0.6/map/layers', function (error, resp) {
            if (error) {
                alert('Get available layers failed!');
                    //callback(null); Do we even need to callback?
            } else {
                availableLayers = resp.layers.reduce(function(lyrs, lyr) {
                    lyrs[lyr.id] = lyr.name;
                    return lyrs;
                }, {});
                callback(resp.layers);
            }
        });
    },

    reviewStatistics: function(mapid, callback) {
        d3.json('/hoot-services/job/review/statistics?mapId=' + mapid, function (error, resp) {
            return callback(error, resp);
        });
    },

    changesToHistory: function(changes) {
        if (!this.hasChanges()) return;

        var s = [{}, {
            modified: changes.created.concat(changes.modified).map(function(d) {
                return d.id + 'v0';
            }),
            deleted: changes.deleted.map(function(d) {
                return d.id;
            }),
            imageryUsed: ["None"],
            annotation: "Hootenanny conflated."
        }];

        return JSON.stringify({
            version: 3,
            entities: changes.created.concat(changes.modified),
            baseEntities: changes.deleted,
            stack: s,
            nextIDs: osmEntity.id.next,
            index: 1
        });

    },

    deriveChangeset: function(mapid, extent, callback) {
// {
//     "input": "678",
//     "inputtype": "db",
//     "outputtype": "osc",
//     "outputname": "changeset",
//     "USER_ID": "1",
//     "TASK_BBOX": "34.040297648723,31.174810720402537,34.06423943771007,31.186143336347865",
//     "translation": "NONE"
// }
        var json = {
            input: mapid.toString(),
            inputtype: 'db',
            outputtype: 'osc',
            USER_ID: '1',
            TASK_BBOX: extent.toParam(),
            buffer: '0.0',
            translation: 'NONE',
            textstatus: 'false',
            append: 'false'
        };

        var userid = services.osm.userDetails(function(error, dets) {
            if (error) {
                console.error(error);
            } else {
                console.log(dets);
            }
        });

        d3.json('/hoot-services/job/export/execute')
            .header("Content-Type", "application/json")
            .post(JSON.stringify(json), function (error, resp) {
                if (error) {
                    alert('Derive changeset failed!');
                } else {
                    services.hoot.pollJob(resp.jobid, function() {
                        d3.xml('/hoot-services/job/export/xml/' + resp.jobid + '?ext=osc', function(data) {
                            //console.log(data);
                            changes = services.osm.parseChangeset(data);
                            //console.log(JSON.stringify(changes));
                            var conflateHistory = services.hoot.changesToHistory(changes);
                            //console.log(conflateHistory);
                            //Remove this delay and coordinate callbacks if necessary
                            if (conflateHistory) {
                                //window.setTimeout(function() {
                                    context.history().fromJSON(conflateHistory, false);
                                //}, 5000);
                            }
                        });
                    });
                }
            });

        callback();

    },

    pollJob: function(jobid, callback) {
        function jobStatus () {
            d3.json('/hoot-services/job/status/' + jobid, function (error, resp) {
                if (error) {
                    clearInterval(job);
                    alert('Job status failed!');
                }
                if (resp.status === 'complete') {
                    clearInterval(job);
                    callback(resp);
                }
                if (resp.status === 'failed') {
                    clearInterval(job);
                    alert('Job ' + jobid + ' failed!');
                }
            });
        }

        var job = setInterval(jobStatus, 2000);
    },

    loadLayer: function(mapid, source, color, callback) {

        var name = availableLayers[mapid];
        d3.json(services.hoot.urlroot() + '/api/0.6/map/tags?mapid=' + mapid, function (error, tags) {
            if (error) {
                alert('Get map tags failed!');
                //callback(null);
            } else {
                console.log(tags);
                d3.json(services.hoot.urlroot() + '/api/0.6/map/mbr?mapId=' + mapid, function (error, mbr) {
                    if (error) {
                        //The map is empty, so assume a global extent
                        mbr = {
                            "minlon":-180,
                            "minlat":-90,
                            "maxlon":180,
                            "maxlat":90,
                            "nodescount":0
                        };
                    }

                    services.hoot.reviewStatistics(mapid, function(error, stats) {
                        if (error) {
                            alert('Get review statistics failed!');
                            //callback(null);
                        } else {
                            //mbr response sample
                            //{"minlon":-77.05030495606161,"firstlon":-77.0492105,"maxlat":38.9272,"nodescount":157,"minlat":38.9137226,"firstlat":38.9266803,"maxlon":-77.0301}

                            //Zoom map to layer extent & Add node-mapnik tile layer
                            var min = [mbr.minlon, mbr.minlat],
                                max = [mbr.maxlon, mbr.maxlat];

                            var layerExtent = new geoExtent(min, max);

                            //A layer is merged (the product of conflation) if it has map tag
                            //metadata indicating the two input sources or if it has reviews
                            //This calculation will miss a layer that has been exported (and loses
                            //its map metadata tags) and never had reviews.
                            var isMerged = Boolean(tags.input1 && tags.input2) || stats.totalCount > 0;

                            //Determine if layer is merged and set colors if so
                            if (isMerged) {
                                //Add source layers to loadedLayers
                                if (tags.input1 && tags.input2) {
                                    loadedLayers[tags.input1] = {
                                        name: tags.input1Name,
                                        id: tags.input1,
                                        color: 'purple',
                                        visible: false,
                                        merged: false
                                    };
                                    loadedLayers[tags.input2] = {
                                        name: tags.input2Name,
                                        id: tags.input2,
                                        color: 'gold',
                                        visible: false,
                                        merged: false
                                    };

                                    //If TASK_BBOX is present in tags, use that for extent

                                    var params = JSON.parse(tags.params.replace(/\\"/g, '"'));
                                    if (params.TASK_BBOX) {
                                        var bbox = params.TASK_BBOX.split(',');
                                        layerExtent = new geoExtent([bbox[0], bbox[1]], [bbox[2], bbox[3]]);
                                        var sourceid = (tags.input1 === '-1') ? tags.input2 : tags.input1;
                                        loadedLayers[sourceid].extent = layerExtent;
                                    } else {

                                        //Get extent for source layers
                                        [tags.input1, tags.input2].forEach(function(sourceid) {
                                            d3.json(services.hoot.urlroot() + '/api/0.6/map/mbr?mapId=' + sourceid, function (error, mbr) {
                                                if (error) {
                                                    //The map is empty, so assume a global extent
                                                    mbr = {
                                                        "minlon":-180,
                                                        "minlat":-90,
                                                        "maxlon":180,
                                                        "maxlat":90,
                                                        "nodescount":0
                                                    };
                                                } else {
                                                    var min = [mbr.minlon, mbr.minlat],
                                                        max = [mbr.maxlon, mbr.maxlat];

                                                    var layerExtent = new geoExtent(min, max);
                                                    loadedLayers[sourceid].extent = layerExtent;
                                                }
                                            });
                                        });

                                    }
                                }
                                //Add css rule to render features by hoot:status
                                // 1 = reference source (purple)
                                // 2 = secondary source (gold)
                                // 3 = merged (active color or pink)
                                services.hoot.setLayerColor(tags.input1 || 1, loadedLayers[tags.input1].color);
                                services.hoot.setLayerColor(tags.input2 || 2, loadedLayers[tags.input2].color);

                            }

                            //Store info on the loaded layer
                            loadedLayers[mapid] = {
                                name: name,
                                id: mapid,
                                polygon: [layerExtent.polygon()],
                                color: color,
                                source: source,
                                tags: tags,
                                visible: true,
                                merged: isMerged
                            };

                            //Add css rule to render features by layer mapid
                            services.hoot.setLayerColor(mapid, color);

                            //Issue callback passing extent and node-mapnik background source and
                            //possibly a new layer color (green) if loaded layer is merged
                            if (callback) callback(layerExtent, getNodeMapnikSource(loadedLayers[mapid]), color);
                        }
                    });
                });
            }
        });
    },

    removeLayer: function(mapid) {
        var ids = [mapid];
        //If merged, delete source layers
        if (loadedLayers[mapid].merged) {
            ids.push(loadedLayers[mapid].tags.input1);
            ids.push(loadedLayers[mapid].tags.input2);
        }
        //Remove internal tracking data
        ids.forEach(function(mapid) { delete loadedLayers[mapid]; });
        //Remove osm vector data
        ids.forEach(services.osm.loadedDataRemove);
        //Remove node-mapnik layer
        context.background().removeSource(mapid);
        //Remove hoot bbox layer
        var hootOverlay = context.layers().layer('hoot');
        if (hootOverlay) {
            hootOverlay.geojson(hootOverlay.geojson().filter(function(d) {
                return d.properties.mapid !== mapid;
            }));
        }

        context.flush();

    },

    toggleMergedLayer: function(mapid) {
        //If merged, swap visibility of merged and input source layers
        if (loadedLayers[mapid].merged) {
            var input1 = loadedLayers[mapid].tags.input1;
            var input2 = loadedLayers[mapid].tags.input2;
            var viz = loadedLayers[mapid].visible;

            loadedLayers[mapid].visible = !viz;
            loadedLayers[input1].visible = viz;
            loadedLayers[input2].visible = viz;

            if (viz) {
                services.osm.loadedDataRemove(mapid);
                console.log('removing ' + mapid);
            } else {
                services.osm.loadedDataRemove(input1);
                services.osm.loadedDataRemove(input2);
                console.log('removing ' + input1 + ' & ' + input2);
            }
            context.flush();  //must do this to remove cached features

            //If an input layer is osm api db, call derive changeset
            //and we're toggling to the osm api layer
            if ((input1 === '-1' || input2 === '-1') && viz) {
                context.connection().on('loaded.hootchangeset', function() {
                    console.log('loaded.hootchangeset');
                    //Use gpx task grid or secondary layer
                    var extent;
                    //If a Tasking Manager grid is present, provide this bbox to the derive changeset job
                    var gj = context.layers().layer('gpx');
                    if (gj.hasGpx()) {
                        //get the task grid feature extent
                        extent = geoExtent(d3.geoBounds(gj.geojson()));
                        console.log(extent.toParam());
                    } else {
                        var sourceid = (input1 === '-1') ? input2 : input1;
                        extent = loadedLayers[sourceid].extent;
                    }
                    services.hoot.deriveChangeset(mapid, extent, function() {
                        context.connection().on('loaded.hootchangeset', null);
                    });
                });
            }

        }

    },

    changeLayerColor: function(lyrmenu, callback) {
        var lyr = d3.values(loadedLayers).find(function(d) {
            return d.source === lyrmenu.id;
        });
        if (lyr) {
            var mapid = lyr.id;
            loadedLayers[mapid].color = lyrmenu.color;
            services.hoot.setLayerColor(mapid, lyrmenu.color);
            if (callback) {
                callback(getNodeMapnikSource(loadedLayers[mapid]));
            }
        }
    },

    decodeHootStatus: function(status) {
        if (status === 'Input1') {
            return 1;
        }
        if (status === 'Input2') {
            return 2;
        }
        if (status === 'Conflated') {
            return 3;
        }

        return parseInt(status);
    },

    loadedLayers: function() {
        return loadedLayers;
    },

    layerBySource: function(s) {
        return d3.values(loadedLayers).find(function(d) {
            return d.source === s;
        }).id;
    },

    hasLayers: function() {
        return d3.map(loadedLayers).size() > 0;
    },

    hasOsmLayer: function() {
        return loadedLayers[-1];
    },

    layerName: function(mapid) {
        return availableLayers[mapid];
    },

    palette: function(co) {
        var palette = [{
                name: 'gold',
                hex: '#ffcc00'
        }, {
                name: 'orange',
                hex: '#ff7f2a'
        }, {
                name: 'violet',
                hex: '#ff5599'
        }, {
                name: 'purple',
                hex: '#e580ff'
        }, {
                name: 'blue',
                hex: '#5fbcd3'
        }, {
                name: 'teal',
                hex: '#5fd3bc'
        }, {
                name: 'green',
                hex: '#A7C973'
        // }, {
        //         name: 'osm',
        //         hex: ''
        }];
        if (!co) return palette;
        var obj = _.find(palette, function(a) {
            return a.name === co || a.hex === co;
        });
        if (obj === undefined) {
            obj = palette[1];
            co = obj.name;
        }
        return (obj.name === co) ? obj.hex : obj.name;
    },

    setLayerColor: function(mapid, color) {
        var sheets = document.styleSheets[document.styleSheets.length - 1];

        //Delete existing rules for mapid
        for (var i = 0; i < sheets.cssRules.length; i++) {
            var rule = sheets.cssRules[i];
            if (rule.cssText.includes('tag-hoot-' + mapid))
                sheets.deleteRule(i);
        }

        //Insert new color rules for mapid
        color = this.palette(color);
        var lighter = d3.rgb(color).brighter();
        sheets.insertRule('path.stroke.tag-hoot-' + mapid + ' { stroke:' + color + '}', sheets.cssRules.length - 1);
        sheets.insertRule('path.shadow.tag-hoot-' + mapid + ' { stroke:' + lighter + '}', sheets.cssRules.length - 1);
        sheets.insertRule('path.fill.tag-hoot-' + mapid + ' { fill:' + lighter + '}', sheets.cssRules.length - 1);
        sheets.insertRule('g.point.tag-hoot-' + mapid + ' .stroke { fill:' + color + '}', sheets.cssRules.length - 1);
    },

    urlroot: function() {
        return '/hoot-services/osm';
    }
};<|endoftext|>


<commit_before>var intersection = function(a, b, c, d) {
  var X = (Equation.B(c, d) - Equation.B(a, b))/(Equation.M(a, b) - Equation.M(c, d));
  return [X, Equation.M(a, b) * X + Equation.B(a, b)];
};
var intersecting = function(a, b, c, d) {
  var p = intersection(a, b, c, d);
  return (p.x >= Math.min(a.x, b.x) && p.x <= Math.max(a.x, b.x) && p.x >= Math.min(c.x, d.x) && p.x <= Math.max(c.x, d.x));
};
var circleCollidingLine = function(a, b, c, r) {
  if(Vector2.magSq(a.sub(c)) <= r * r || Vector2.magSq(b.sub(c)) <= r * r) { return true; }
  var m = (Equation.B(a, b) - Equation.TB(PM(a, b), c))/(Equation.PM(a, b) - Equation.M(a, b));
  return m > Math.min(a.x, b.x) && m < Math.max(a.x, b.x) && Vector2d.magSq(Vector2.sub(new Vector2(m, Equation.M(a, b) * m + Equation.B(a, b)), c)) < r * r;
};
<commit_msg>Update collisiondetection.js<commit_after>const intersection = function(a, b, c, d) {
  var X = (Equation.B(c, d) - Equation.B(a, b))/(Equation.M(a, b) - Equation.M(c, d));
  return [X, Equation.M(a, b) * X + Equation.B(a, b)];
};
const intersecting = function(a, b, c, d) {
  var p = intersection(a, b, c, d);
  return (p.x >= Math.min(a.x, b.x) && p.x <= Math.max(a.x, b.x) && p.x >= Math.min(c.x, d.x) && p.x <= Math.max(c.x, d.x));
};
const circleCollidingLine = function(a, b, c, r) {
  if(Vector2.magSq(a.sub(c)) <= r * r || Vector2.magSq(b.sub(c)) <= r * r) { return true; }
  var m = (Equation.B(a, b) - Equation.TB(PM(a, b), c))/(Equation.PM(a, b) - Equation.M(a, b));
  return m > Math.min(a.x, b.x) && m < Math.max(a.x, b.x) && Vector2d.magSq(Vector2.sub(new Vector2(m, Equation.M(a, b) * m + Equation.B(a, b)), c)) < r * r;
};
<|endoftext|>


<commit_before>// Shamelessly copied from http://stackoverflow.com/a/13194087/592174
var beep = (function () {
  var ctx = new(window.audioContext || window.webkitAudioContext);
  return function (duration, type, finishedCallback) {

    duration = +duration;

    // Only 0-4 are valid types.
    type = (type % 5) || 0;

    if (typeof finishedCallback != "function") {
      finishedCallback = function () {};
    }

    var osc = ctx.createOscillator();

    osc.type = type;

    osc.connect(ctx.destination);
    osc.noteOn(0);

    setTimeout(function () {
      osc.noteOff(0);
      finishedCallback();
    }, duration);

  };
})();

var Timer = function(parent_elem, options) {
  var start, interval, alarm_playing = false, alarm_interval, running = false, id_uniq = Math.random().toString().replace(".", ""), id;
  options = options || {};
  options.delay = options.delay || 100;
  if (options.id) {
    id = options.id;
  }
  if (options.start_time) {
    start = options.start_time;
  }
  console.log(options);

  function create() {
    $(parent_elem).append("
"+
    "
"+
    "
"+
    "     "+options.name+" ---"+
    "
"+
    "
"+
    "
"+
    "
"+
    "
"+
    "
"+
    "
"+
    "   "+
    "
"+
    "
"+
    "   "+
    "
"+
    "
");
    $("#timer-"+id_uniq).stop(true).slideDown("fast");

    $("#timer-"+id_uniq+" .timer-stop").click(function() {
      delete_item();
    });
    $("#timer-"+id_uniq+" .timer-restart").click(function () {
      restart();
    });
    if (typeof id == 'undefined') {
      restart();
      $.post("/homecontroller/timer/create", {name: options.name, duration: options.duration}, function(data) {
        id = data[0].pk;
        $("#timer-"+id_uniq).addClass("timer-backend-id-"+ id);
        start = new Date(data[0].fields.start_time)
      });
    } else {
      $("#timer-"+id_uniq).addClass("timer-backend-id-"+id);
      start_item();
    }
  }
  create();

  function zeroPad(num, places) {
    var zero = places - num.toString().length + 1;
    return Array(+(zero > 0 && zero)).join("0") + num;
  }

  function update() {
    if (!running) {
      return;
    }
    var now = new Date();
    var diff = parseInt(options.duration) - (now - start) / 1000;

    var percent = Math.round((parseFloat(diff) / parseFloat(options.duration)) * 100);
    if (percent > 100) {
      percent = 100;
    }
    var prefix = "";
    if (diff < 0) {
      prefix = "-";
      diff *= -1;
    }
    if (percent > 0) {
      $("#timer-"+id_uniq+" .progress-bar").css("width", percent+"%").removeClass("progress-bar-danger").addClass("progress-bar-success");
      $("#timer-"+id_uniq).removeClass("timer-overtime");
    } else {
      $("#timer-"+id_uniq+" .progress-bar").removeClass("progress-bar-success").addClass("progress-bar-danger").css("width", "100%");
      $("#timer-"+id_uniq).addClass("timer-overtime");
      if (!alarm_playing) {
        alarm_playing = true;
        play_alarm();
        if (alarm_interval) {
          clearInterval(alarm_interval);
        }
        alarm_interval = setInterval(play_alarm, 30000);
      }

    }
    var hours = Math.floor(diff / 3600);
    diff = diff - hours * 3600;
    var minutes = Math.floor(diff / 60);
    diff = diff - minutes * 60;
    var seconds = Math.floor(diff);
    var msec = diff - seconds;
    var timer = prefix+zeroPad(hours, 2) + ":"+zeroPad(minutes, 2)+":"+zeroPad(seconds, 2)+"."+(String(Math.round(msec*10))+"00").substr(0,1);
    $("#timer-"+id_uniq+" .timer-timeleft").html(timer);
  }

  function play_alarm() {

  }

  function start_item() {
    if (interval) {
      clearInterval(interval);
    }
    if (alarm_interval) {
      clearInterval(alarm_interval);
    }
    running = true;
    interval = setInterval(update, options.delay);
    update();
    $("#timer-"+id_uniq).data("end-timestamp", (start.getTime()/1000) + options.duration);
    sort_timers();
    $.get("/homecontroller/timer/start/"+id, function(data) {
      //TODO
    });
  }

  function restart() {
    start = new Date();
    if (interval) {
      $("#timer-"+id_uniq).stop(true).effect("highlight", {color: "#006600"}, 500);
    }

    start_item();
    $.get("/homecontroller/timer/restart/"+id, function (data) {
      start = new Date(data[0].fields.start_time);
    });
  }

  function delete_item() {
    $("#timer-"+id_uniq).slideUp("fast", function () { $(this).remove(); });
    if (interval) {
      clearInterval(interval);
    }
    if (alarm_interval) {
      clearInterval(alarm_interval);
    }
    alarm_playing = false;
    $.get("/homecontroller/timer/delete/"+id, function(data) {

    });
  }

  this.delete_item = delete_item;
  this.restart = restart;
  this.start_item = start_item;
};

function refresh_timers_from_server() {
  $.getJSON("/homecontroller/timer/list", function(data) {
    $.each(data, function() {
      var id = this.pk;
      if (this.fields.duration === null) {
        // if data.duration is not null, it's timer, not countdown
      } else {
        currently_running = $("#timer-holder").find(".timer-backend-id-"+id);
        if (currently_running.length == 0) {
          var timer_run = new Timer("#timer-holder", {"name": this.fields.name, "duration": this.fields.duration, "start_time": new Date(this.fields.start_time), "running": this.fields.running, "id": this.pk});
        }
      }
    });
  });
}

function sort_timers() {
  var items = $("#timer-holder .timer-item");
  items.detach().sort(function(a,b) {
    var astts = $(a).data('end-timestamp');
    var bstts = $(b).data('end-timestamp')
    return (astts > bstts) ? (astts > bstts) ? 1 : 0 : -1;
  });
  $("#timer-holder").append(items);
}

$(document).ready(function () {
  $(".add-timer").click(function () {
    var timer_run = new Timer("#timer-holder", {"name": $(this).data("name"), "duration": $(this).data("duration")});
  });
  refresh_timers_from_server();
  setInterval(refresh_timers_from_server, 10000);

});
<commit_msg>Sync deleted timers<commit_after>// Shamelessly copied from http://stackoverflow.com/a/13194087/592174
var beep = (function () {
  var ctx = new(window.audioContext || window.webkitAudioContext);
  return function (duration, type, finishedCallback) {

    duration = +duration;

    // Only 0-4 are valid types.
    type = (type % 5) || 0;

    if (typeof finishedCallback != "function") {
      finishedCallback = function () {};
    }

    var osc = ctx.createOscillator();

    osc.type = type;

    osc.connect(ctx.destination);
    osc.noteOn(0);

    setTimeout(function () {
      osc.noteOff(0);
      finishedCallback();
    }, duration);

  };
})();

var Timer = function(parent_elem, options) {
  var start, interval, alarm_playing = false, backend_interval, alarm_interval, running = false, id_uniq = Math.random().toString().replace(".", ""), id;
  options = options || {};
  options.delay = options.delay || 100;
  if (options.id) {
    id = options.id;
  }
  if (options.start_time) {
    start = options.start_time;
  }
  console.log(options);

  function create() {
    $(parent_elem).append("
"+
    "
"+
    "
"+
    "     "+options.name+" ---"+
    "
"+
    "
"+
    "
"+
    "
"+
    "
"+
    "
"+
    "
"+
    "   "+
    "
"+
    "
"+
    "   "+
    "
"+
    "
");
    $("#timer-"+id_uniq).stop(true).slideDown("fast");

    $("#timer-"+id_uniq+" .timer-stop").click(function() {
      delete_item();
    });
    $("#timer-"+id_uniq+" .timer-restart").click(function () {
      restart();
    });
    if (typeof id == 'undefined') {
      restart();
      $.post("/homecontroller/timer/create", {name: options.name, duration: options.duration}, function(data) {
        id = data[0].pk;
        $("#timer-"+id_uniq).addClass("timer-backend-id-"+ id);
        start = new Date(data[0].fields.start_time)
      });
    } else {
      $("#timer-"+id_uniq).addClass("timer-backend-id-"+id);
      start_item();
    }
  }
  create();

  function zeroPad(num, places) {
    var zero = places - num.toString().length + 1;
    return Array(+(zero > 0 && zero)).join("0") + num;
  }

  function update() {
    if (!running) {
      return;
    }
    var now = new Date();
    var diff = parseInt(options.duration) - (now - start) / 1000;

    var percent = Math.round((parseFloat(diff) / parseFloat(options.duration)) * 100);
    if (percent > 100) {
      percent = 100;
    }
    var prefix = "";
    if (diff < 0) {
      prefix = "-";
      diff *= -1;
    }
    if (percent > 0) {
      $("#timer-"+id_uniq+" .progress-bar").css("width", percent+"%").removeClass("progress-bar-danger").addClass("progress-bar-success");
      $("#timer-"+id_uniq).removeClass("timer-overtime");
    } else {
      $("#timer-"+id_uniq+" .progress-bar").removeClass("progress-bar-success").addClass("progress-bar-danger").css("width", "100%");
      $("#timer-"+id_uniq).addClass("timer-overtime");
      if (!alarm_playing) {
        alarm_playing = true;
        play_alarm();
        if (alarm_interval) {
          clearInterval(alarm_interval);
        }
        alarm_interval = setInterval(play_alarm, 30000);
      }

    }
    var hours = Math.floor(diff / 3600);
    diff = diff - hours * 3600;
    var minutes = Math.floor(diff / 60);
    diff = diff - minutes * 60;
    var seconds = Math.floor(diff);
    var msec = diff - seconds;
    var timer = prefix+zeroPad(hours, 2) + ":"+zeroPad(minutes, 2)+":"+zeroPad(seconds, 2)+"."+(String(Math.round(msec*10))+"00").substr(0,1);
    $("#timer-"+id_uniq+" .timer-timeleft").html(timer);
  }

  function play_alarm() {

  }

  function start_item() {
    if (interval) {
      clearInterval(interval);
    }
    if (alarm_interval) {
      clearInterval(alarm_interval);
    }
    running = true;
    interval = setInterval(update, options.delay);
    backend_interval = setInterval(refresh_from_backend, 15000);
    update();
    $("#timer-"+id_uniq).data("end-timestamp", (start.getTime()/1000) + options.duration);
    sort_timers();
    $.get("/homecontroller/timer/start/"+id, function(data) {
      //TODO
    });
  }

  function refresh_from_backend() {
    $.ajax({
      url: "/homecontroller/timer/get/"+id,
      success: function (data) {
        start = data[0].fields.start_time;
        if (data[0].fields.running) {
          if (!running) {
            start();
          }
        } else {
          if (running) {
            stop();
          }
        }
      },
      statusCode: {
        404: function () {
          delete_item();
        }
      }
    });
  }

  function restart() {
    start = new Date();
    if (interval) {
      $("#timer-"+id_uniq).stop(true).effect("highlight", {color: "#006600"}, 500);
    }

    start_item();
    $.get("/homecontroller/timer/restart/"+id, function (data) {
      start = new Date(data[0].fields.start_time);
    });
  }

  function delete_item() {
    $("#timer-"+id_uniq).slideUp("fast", function () { $(this).remove(); });
    if (interval) {
      clearInterval(interval);
    }
    if (alarm_interval) {
      clearInterval(alarm_interval);
    }
    if (backend_interval) {
      clearInterval(backend_interval);
    }
    alarm_playing = false;
    $.get("/homecontroller/timer/delete/"+id, function(data) {

    });
  }

  this.delete_item = delete_item;
  this.restart = restart;
  this.start_item = start_item;
};

function refresh_timers_from_server() {
  $.getJSON("/homecontroller/timer/list", function(data) {
    $.each(data, function() {
      var id = this.pk;
      if (this.fields.duration === null) {
        // if data.duration is not null, it's timer, not countdown
      } else {
        currently_running = $("#timer-holder").find(".timer-backend-id-"+id);
        if (currently_running.length == 0) {
          var timer_run = new Timer("#timer-holder", {"name": this.fields.name, "duration": this.fields.duration, "start_time": new Date(this.fields.start_time), "running": this.fields.running, "id": this.pk});
        }
      }
    });
  });
}

function sort_timers() {
  var items = $("#timer-holder .timer-item");
  items.detach().sort(function(a,b) {
    var astts = $(a).data('end-timestamp');
    var bstts = $(b).data('end-timestamp')
    return (astts > bstts) ? (astts > bstts) ? 1 : 0 : -1;
  });
  $("#timer-holder").append(items);
}

$(document).ready(function () {
  $(".add-timer").click(function () {
    var timer_run = new Timer("#timer-holder", {"name": $(this).data("name"), "duration": $(this).data("duration")});
  });
  refresh_timers_from_server();
  setInterval(refresh_timers_from_server, 10000);

});
<|endoftext|>


<commit_before>var Twit = require('twit');
var ent = require('ent');

var t = null;
var tConf = null;
var bot = null;

var twitRegex = /^(https?\:\/\/)?(dashboard\.|www\.)?twitter\.com\/([a-zA-Z0-9_]+)(\/status(es)?\/(\d+))?/;

module.exports.init = function(b) {
  bot = b;
  bot.getConfig("twitter.json", function(err, conf) {
    tConf = conf;
    if(err) bot.say("Unable to load twitter module: " + err);
    try {
      t = new Twit(conf);
    } catch(ex) {
      bot.say("Error loading twitter library: " + ex);
    }
  });
};

module.exports.url = function(url, reply) {
  if(t === null) return reply("Unable to handle twitter url; lib not loaded");

  var m;
  if((m = twitRegex.exec(url))) {
    if(m[3] && !m[6]) {
      // User page
      t.get("/users/show", {screen_name: m[3]}, function(err, res) {
        if(err) reply("Error getting user " + m[3]);
        else reply(ent.decode(res.name) + " (@" + ent.decode(res.screen_name) + "): " + ent.decode(res.description).replace(/\n/g, "\t"));
      });
    } else {
      var uname = m[3];
      var id = m[6];
      t.get("/statuses/show/:id", {id: id}, function(err, res) {
        if(err) reply("Error getting tweet");
        else reply(ent.decode(res.user.name) + " (@" + ent.decode(res.user.screen_name) + "): " + ent.decode(res.text).replace(/\n/g, "\t\t"));
      });
    }
  }
};

module.exports.commands = ['quo', 'quoth'];

module.exports.run = function(r, parts, reply, command, from, to) {
  if(to[0] != '#' && to[0] != '&') return;

  var scrollbackModule = bot.modules['sirc-scrollback'];

  if(!scrollbackModule) return console.log("No scrollback, can't tweet");

  scrollbackModule.getFormattedScrollbackLinesFromRanges(to, r, function(err, res) {
    if(err) return reply(err);
    if(res.match(/(pls|#)noquo/)) return reply("don't be a deck, betch");

    t.post('statuses/update', {status: res}, function(err, data) {
      if(err) return reply(err);
      reply(tConf.baseUrl + 'status/' + data.id_str);
    });
  });
};
<commit_msg>twitter: ignore search "user"<commit_after>var Twit = require('twit');
var ent = require('ent');

var t = null;
var tConf = null;
var bot = null;

var twitRegex = /^(https?\:\/\/)?(dashboard\.|www\.)?twitter\.com\/([a-zA-Z0-9_]+)(\/status(es)?\/(\d+))?/;
var nonUsers = ["search"];
module.exports.init = function(b) {
  bot = b;
  bot.getConfig("twitter.json", function(err, conf) {
    tConf = conf;
    if(err) bot.say("Unable to load twitter module: " + err);
    try {
      t = new Twit(conf);
    } catch(ex) {
      bot.say("Error loading twitter library: " + ex);
    }
  });
};

module.exports.url = function(url, reply) {
  if(t === null) return reply("Unable to handle twitter url; lib not loaded");

  var m;
  if((m = twitRegex.exec(url))) {
    if(m[3] && !m[6]) {
      if(nonUsers.indexOf(m[3]) !== -1) {
        return;
      }
      // User page
      t.get("/users/show", {screen_name: m[3]}, function(err, res) {
        if(err) reply("Error getting user " + m[3]);
        else reply(ent.decode(res.name) + " (@" + ent.decode(res.screen_name) + "): " + ent.decode(res.description).replace(/\n/g, "\t"));
      });
    } else {
      var uname = m[3];
      var id = m[6];
      t.get("/statuses/show/:id", {id: id}, function(err, res) {
        if(err) reply("Error getting tweet");
        else reply(ent.decode(res.user.name) + " (@" + ent.decode(res.user.screen_name) + "): " + ent.decode(res.text).replace(/\n/g, "\t\t"));
      });
    }
  }
};

module.exports.commands = ['quo', 'quoth'];

module.exports.run = function(r, parts, reply, command, from, to) {
  if(to[0] != '#' && to[0] != '&') return;

  var scrollbackModule = bot.modules['sirc-scrollback'];

  if(!scrollbackModule) return console.log("No scrollback, can't tweet");

  scrollbackModule.getFormattedScrollbackLinesFromRanges(to, r, function(err, res) {
    if(err) return reply(err);
    if(res.match(/(pls|#)noquo/)) return reply("don't be a deck, betch");

    t.post('statuses/update', {status: res}, function(err, data) {
      if(err) return reply(err);
      reply(tConf.baseUrl + 'status/' + data.id_str);
    });
  });
};
<|endoftext|>


<commit_before>var Twit = require('twit');
var ent = require('ent');

var t = null;
var tConf = null;
var bot = null;

var twitRegex = /^(https?\:\/\/)?(www\.)?twitter\.com\/([a-zA-Z0-9_]+)(\/status(es)?\/(\d+))?/;

module.exports.init = function(b) {
  bot = b;
  bot.getConfig("twitter.json", function(err, conf) {
    tConf = conf;
    if(err) bot.say("Unable to load twitter module: " + err);
    try {
      t = new Twit(conf);
    } catch(ex) {
      bot.say("Error loading twitter library: " + ex);
    }
  });
};

module.exports.url = function(url, reply) {
  if(t === null) return reply("Unable to handle twitter url; lib not loaded");

  var m;
  if((m = twitRegex.exec(url))) {
    if(m[3] && !m[6]) {
      // User page
      t.get("/users/show", {screen_name: m[3]}, function(err, res) {
        if(err) reply("Error getting user " + m[3]);
        else reply(ent.decode(res.name) + " (@" + ent.decode(res.screen_name) + "): " + ent.decode(res.description).replace(/\n/g, "\t"));
      });
    } else {
      var uname = m[3];
      var id = m[6];
      t.get("/statuses/show/:id", {id: id}, function(err, res) {
        if(err) reply("Error getting tweet");
        else reply(ent.decode(res.user.name) + " (@" + ent.decode(res.user.screen_name) + "): " + ent.decode(res.text).replace(/\n/g, "\t\t"));
      });
    }
  }
};

module.exports.commands = ['quo', 'quoth'];

module.exports.run = function(r, parts, reply, command, from, to) {
  if(to[0] != '#' && to[0] != '&') return;

  var scrollbackModule = bot.modules['sirc-scrollback'];

  if(!scrollbackModule) return console.log("No scrollback, can't tweet");

  scrollbackModule.getFormattedScrollbackLinesFromRanges(to, r, function(err, res) {
    if(err) return reply(err);
    if(res.match(/(pls|#)noquo/)) return reply("don't be a deck, betch");

    t.post('statuses/update', {status: res}, function(err, data) {
      if(err) return reply(err);
      reply(tConf.baseUrl + 'status/' + data.id_str);
    });
  });
};
<commit_msg>Support dashboard.twitter.com links also<commit_after>var Twit = require('twit');
var ent = require('ent');

var t = null;
var tConf = null;
var bot = null;

var twitRegex = /^(https?\:\/\/)?(dashboard\.|www\.)?twitter\.com\/([a-zA-Z0-9_]+)(\/status(es)?\/(\d+))?/;

module.exports.init = function(b) {
  bot = b;
  bot.getConfig("twitter.json", function(err, conf) {
    tConf = conf;
    if(err) bot.say("Unable to load twitter module: " + err);
    try {
      t = new Twit(conf);
    } catch(ex) {
      bot.say("Error loading twitter library: " + ex);
    }
  });
};

module.exports.url = function(url, reply) {
  if(t === null) return reply("Unable to handle twitter url; lib not loaded");

  var m;
  if((m = twitRegex.exec(url))) {
    if(m[3] && !m[6]) {
      // User page
      t.get("/users/show", {screen_name: m[3]}, function(err, res) {
        if(err) reply("Error getting user " + m[3]);
        else reply(ent.decode(res.name) + " (@" + ent.decode(res.screen_name) + "): " + ent.decode(res.description).replace(/\n/g, "\t"));
      });
    } else {
      var uname = m[3];
      var id = m[6];
      t.get("/statuses/show/:id", {id: id}, function(err, res) {
        if(err) reply("Error getting tweet");
        else reply(ent.decode(res.user.name) + " (@" + ent.decode(res.user.screen_name) + "): " + ent.decode(res.text).replace(/\n/g, "\t\t"));
      });
    }
  }
};

module.exports.commands = ['quo', 'quoth'];

module.exports.run = function(r, parts, reply, command, from, to) {
  if(to[0] != '#' && to[0] != '&') return;

  var scrollbackModule = bot.modules['sirc-scrollback'];

  if(!scrollbackModule) return console.log("No scrollback, can't tweet");

  scrollbackModule.getFormattedScrollbackLinesFromRanges(to, r, function(err, res) {
    if(err) return reply(err);
    if(res.match(/(pls|#)noquo/)) return reply("don't be a deck, betch");

    t.post('statuses/update', {status: res}, function(err, data) {
      if(err) return reply(err);
      reply(tConf.baseUrl + 'status/' + data.id_str);
    });
  });
};
<|endoftext|>


<commit_before>var collectionsView = Backbone.View.extend({
  el: '#content',
  el2: '.thumbnails',

  searchTimeout: null,

  init: function () {
  },

  template: new EJS({url: '/_admin/html/js/templates/collectionsView.ejs'}),

  render: function () {
    $(this.el).html(this.template.text);
    this.setFilterValues();

    var searchOptions = this.collection.searchOptions;

    this.collection.getFiltered(searchOptions).forEach(function (arango_collection) {
      $('.thumbnails', this.el).append(new window.CollectionListItemView({model: arango_collection}).render().el);
    }, this);

    $('.thumbnails', this.el).append('
Add Collection
');
    $('#searchInput').val(searchOptions.searchPhrase);
    $('#searchInput').focus();
    var val = $('#searchInput').val();
    $('#searchInput').val('');
    $('#searchInput').val(val);

    return this;
  },
  events: {
    "click .icon-info-sign" : "details",
    //"blur #searchInput" : "restrictToSearchPhrase",
    "keypress #searchInput" : "restrictToSearchPhraseKey",
    "change #searchInput"   : "restrictToSearchPhrase",
    "click #searchSubmit"   : "restrictToSearchPhrase",
    "click #checkSystem"    : "checkSystem",
    "click #checkLoaded"    : "checkLoaded",
    "click #checkUnloaded"  : "checkUnloaded",
    "click #checkDocument"  : "checkDocument",
    "click #checkEdge"      : "checkEdge"
  },
  checkSystem: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeSystem;

    searchOptions.includeSystem = ($('#checkSystem').is(":checked") === true);

    if (oldValue != searchOptions.includeSystem) {
      this.render();
    }
  },
  checkEdge: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeEdge;

    searchOptions.includeEdge = ($('#checkEdge').is(":checked") === true);

    if (oldValue != searchOptions.includeEdge) {
      this.render();
    }
  },
  checkDocument: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeDocument;

    searchOptions.includeDocument = ($('#checkDocument').is(":checked") === true);

    if (oldValue != searchOptions.includeDocument) {
      this.render();
    }
  },
  checkLoaded: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeLoaded;

    searchOptions.includeLoaded = ($('#checkLoaded').is(":checked") === true);

    if (oldValue != searchOptions.includeLoaded) {
      this.render();
    }
  },
  checkUnloaded: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeUnloaded;

    searchOptions.includeUnloaded = ($('#checkUnloaded').is(":checked") === true);

    if (oldValue != searchOptions.includeUnloaded) {
      this.render();
    }
  },

  setFilterValues: function () {
    var searchOptions = this.collection.searchOptions;
    $('#checkLoaded').attr('checked', searchOptions.includeLoaded);
    $('#checkUnloaded').attr('checked', searchOptions.includeUnloaded);
    $('#checkSystem').attr('checked', searchOptions.includeSystem);
    $('#checkEdge').attr('checked', searchOptions.includeEdge);
    $('#checkDocument').attr('checked', searchOptions.includeDocument);
  },
  search: function () {
    var searchOptions = this.collection.searchOptions;
    var searchPhrase = $('#searchInput').val().replace(/(^\s+|\s+$)/g, '');

    if (searchPhrase === searchOptions.searchPhrase) {
      return;
    }
    searchOptions.searchPhrase = searchPhrase;

    this.render();
  },

  restrictToSearchPhraseKey: function (e) {
    // key pressed in search box
    var self = this;
    if (self.searchTimeout) {
      clearTimeout(self.searchTimeout);
      self.searchTimeout = null;
    }

    self.searchTimeout = setTimeout(function (){
      self.search();
    }, 200);
  },

  restrictToSearchPhrase: function () {
    // search executed
    this.search();
  },

  details: function () {
  }

});
<commit_msg>issue #451<commit_after>var collectionsView = Backbone.View.extend({
  el: '#content',
  el2: '.thumbnails',

  searchTimeout: null,

  init: function () {
  },

  template: new EJS({url: '/_admin/html/js/templates/collectionsView.ejs'}),

  render: function () {
    $(this.el).html(this.template.text);
    this.setFilterValues();

    var searchOptions = this.collection.searchOptions;

    this.collection.getFiltered(searchOptions).forEach(function (arango_collection) {
      $('.thumbnails', this.el).append(new window.CollectionListItemView({model: arango_collection}).render().el);
    }, this);

    $('.thumbnails', this.el).append('
Add Collection
');
    $('#searchInput').val(searchOptions.searchPhrase);
    $('#searchInput').focus();
    var val = $('#searchInput').val();
    $('#searchInput').val('');
    $('#searchInput').val(val);

    return this;
  },
  events: {
    "click .icon-info-sign" : "details",
    "keypress #searchInput" : "restrictToSearchPhraseKey",
    "change #searchInput"   : "restrictToSearchPhrase",
    "click #searchSubmit"   : "restrictToSearchPhrase",
    "click #checkSystem"    : "checkSystem",
    "click #checkLoaded"    : "checkLoaded",
    "click #checkUnloaded"  : "checkUnloaded",
    "click #checkDocument"  : "checkDocument",
    "click #checkEdge"      : "checkEdge"
  },
  checkSystem: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeSystem;

    searchOptions.includeSystem = ($('#checkSystem').is(":checked") === true);

    if (oldValue != searchOptions.includeSystem) {
      this.render();
    }
  },
  checkEdge: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeEdge;

    searchOptions.includeEdge = ($('#checkEdge').is(":checked") === true);

    if (oldValue != searchOptions.includeEdge) {
      this.render();
    }
  },
  checkDocument: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeDocument;

    searchOptions.includeDocument = ($('#checkDocument').is(":checked") === true);

    if (oldValue != searchOptions.includeDocument) {
      this.render();
    }
  },
  checkLoaded: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeLoaded;

    searchOptions.includeLoaded = ($('#checkLoaded').is(":checked") === true);

    if (oldValue != searchOptions.includeLoaded) {
      this.render();
    }
  },
  checkUnloaded: function () {
    var searchOptions = this.collection.searchOptions;
    var oldValue = searchOptions.includeUnloaded;

    searchOptions.includeUnloaded = ($('#checkUnloaded').is(":checked") === true);

    if (oldValue != searchOptions.includeUnloaded) {
      this.render();
    }
  },

  setFilterValues: function () {
    var searchOptions = this.collection.searchOptions;
    $('#checkLoaded').attr('checked', searchOptions.includeLoaded);
    $('#checkUnloaded').attr('checked', searchOptions.includeUnloaded);
    $('#checkSystem').attr('checked', searchOptions.includeSystem);
    $('#checkEdge').attr('checked', searchOptions.includeEdge);
    $('#checkDocument').attr('checked', searchOptions.includeDocument);
  },

  search: function () {
    var searchOptions = this.collection.searchOptions;
    var searchPhrase = $('#searchInput').val().replace(/(^\s+|\s+$)/g, '');

    if (searchPhrase === searchOptions.searchPhrase) {
      return;
    }
    searchOptions.searchPhrase = searchPhrase;

    this.render();
  },

  resetSearch: function () {
    if (this.searchTimeout) {
      clearTimeout(this.searchTimeout);
      this.searchTimeout = null;
    }

    var searchOptions = this.collection.searchOptions;
    searchOptions.searchPhrase = null;
  },

  restrictToSearchPhraseKey: function (e) {
    // key pressed in search box
    var self = this;

    // force a new a search
    this.resetSearch();

    self.searchTimeout = setTimeout(function (){
      self.search();
    }, 200);
  },

  restrictToSearchPhrase: function () {
    // force a new a search
    this.resetSearch();

    // search executed
    this.search();
  },

  details: function () {
  }

});
<|endoftext|>