<commit_before>import _ from 'lodash';
import moment from 'moment';
import ajax from '../ajax';

const id = 'helsinki';
const name = 'Helsinki';
const latitude = 60.200763;
const longitude = 24.936219;
const apiUrl = 'http://dev.hsl.fi/siriaccess/vm/json';

function fetch() {
    return ajax(apiUrl).then(_transform);
}

function _transform(data) {
    const vehicles = data.Siri.ServiceDelivery.VehicleMonitoringDelivery[0].VehicleActivity;
    return _.map(vehicles, _transformVehicle.bind(this, data));
}

function _transformVehicle(data, vehicle) {
    var journey = vehicle.MonitoredVehicleJourney;

    // XXX: The vehicle type information is already in the lines json
    var routeInfo = _interpretJore(journey.LineRef.value);
    var lineName = routeInfo[2];
    var vehicleType = routeInfo[0].toLowerCase();

    return {
        id: journey.VehicleRef.value,
        type: vehicleType,
        line: lineName,
        latitude: journey.VehicleLocation.Latitude,
        longitude: journey.VehicleLocation.Longitude,
        rotation: journey.Bearing || 0,
        responseTime: moment(data.Siri.ServiceDelivery.ResponseTimestamp).toISOString()
    };
}

/*eslint-disable */
// jscs:disable
// From http://dev.hsl.fi/:
// (Values of lineRef are "JORE codes" and can be converted to passenger-friendly line numbers using the interpret_jore example code.)
// (lineRef query parameter can be added to limit the response to that single "JORE code".)
// This function was automatically converted from coffeescript to js, from
// https://github.com/HSLdevcom/navigator-proto/blob/master/src/routing.coffee
function _interpretJore(routeId) {
    var mode, ref, ref1, ref2, ref3, ref4, ref5, ref6, route, routeType;

    if (routeId != null ? routeId.match(/^1019/) : void 0) {
        ref1 = ["FERRY", 4, "Ferry"], mode = ref1[0], routeType = ref1[1], route = ref1[2];
    } else if (routeId != null ? routeId.match(/^1300/) : void 0) {
        ref2 = ["SUBWAY", 1, routeId.substring(4, 5)], mode = ref2[0], routeType = ref2[1], route = ref2[2];
    } else if (routeId != null ? routeId.match(/^300/) : void 0) {
        ref3 = ["TRAIN", 2, routeId.substring(4, 5)], mode = ref3[0], routeType = ref3[1], route = ref3[2];
    } else if (routeId != null ? routeId.match(/^10(0|10)/) : void 0) {
        ref4 = ["TRAM", 0, routeId.replace(/^.0*/, "")], mode = ref4[0], routeType = ref4[1], route = ref4[2];
    } else if (routeId != null ? routeId.match(/^(1|2|4).../) : void 0) {
        ref5 = ["BUS", 3, routeId.replace(/^.0*/, "")], mode = ref5[0], routeType = ref5[1], route = ref5[2];
    } else {
        ref6 = ["BUS", 3, routeId], mode = ref6[0], routeType = ref6[1], route = ref6[2];
    }
    return [mode, routeType, route];
}
// jscs:enable
/*eslint-enable */

export {
    id,
    name,
    latitude,
    longitude,
    fetch
};
<commit_msg>Convert manchester line names to more sensible names<commit_after>import _ from 'lodash';
import moment from 'moment';
import ajax from '../ajax';

const id = 'helsinki';
const name = 'Helsinki';
const latitude = 60.200763;
const longitude = 24.936219;
const apiUrl = 'http://dev.hsl.fi/siriaccess/vm/json';

function fetch() {
    return ajax(apiUrl).then(_transform);
}

function _transform(data) {
    const vehicles = data.Siri.ServiceDelivery.VehicleMonitoringDelivery[0].VehicleActivity;
    return _.map(vehicles, _transformVehicle.bind(this, data));
}

function _transformVehicle(data, vehicle) {
    var journey = vehicle.MonitoredVehicleJourney;

    // XXX: The vehicle type information is already in the lines json
    var routeInfo = _interpretJore(journey.LineRef.value);
    var lineName = routeInfo[2];
    if (_.startsWith(lineName, 'GMN:')) {
        // Helsinki data contains some Manchester vehicles also,
        // they are prefixed with a special name.
        // Try to make the line name sensible
        const parts = lineName.split('GMN:');
        if (parts.length > 1) {
            lineName = parts[1].replace(/:/g, '').trim();
        }
    }

    var vehicleType = routeInfo[0].toLowerCase();

    return {
        id: journey.VehicleRef.value,
        type: vehicleType || 'bus',
        line: lineName,
        latitude: journey.VehicleLocation.Latitude,
        longitude: journey.VehicleLocation.Longitude,
        rotation: journey.Bearing || 0,
        responseTime: moment(data.Siri.ServiceDelivery.ResponseTimestamp).toISOString()
    };
}

/*eslint-disable */
// jscs:disable
// From http://dev.hsl.fi/:
// (Values of lineRef are "JORE codes" and can be converted to passenger-friendly line numbers using the interpret_jore example code.)
// (lineRef query parameter can be added to limit the response to that single "JORE code".)
// This function was automatically converted from coffeescript to js, from
// https://github.com/HSLdevcom/navigator-proto/blob/master/src/routing.coffee
function _interpretJore(routeId) {
    var mode, ref, ref1, ref2, ref3, ref4, ref5, ref6, route, routeType;

    if (routeId != null ? routeId.match(/^1019/) : void 0) {
        ref1 = ["FERRY", 4, "Ferry"], mode = ref1[0], routeType = ref1[1], route = ref1[2];
    } else if (routeId != null ? routeId.match(/^1300/) : void 0) {
        ref2 = ["SUBWAY", 1, routeId.substring(4, 5)], mode = ref2[0], routeType = ref2[1], route = ref2[2];
    } else if (routeId != null ? routeId.match(/^300/) : void 0) {
        ref3 = ["TRAIN", 2, routeId.substring(4, 5)], mode = ref3[0], routeType = ref3[1], route = ref3[2];
    } else if (routeId != null ? routeId.match(/^10(0|10)/) : void 0) {
        ref4 = ["TRAM", 0, routeId.replace(/^.0*/, "")], mode = ref4[0], routeType = ref4[1], route = ref4[2];
    } else if (routeId != null ? routeId.match(/^(1|2|4).../) : void 0) {
        ref5 = ["BUS", 3, routeId.replace(/^.0*/, "")], mode = ref5[0], routeType = ref5[1], route = ref5[2];
    } else {
        ref6 = ["BUS", 3, routeId], mode = ref6[0], routeType = ref6[1], route = ref6[2];
    }
    return [mode, routeType, route];
}
// jscs:enable
/*eslint-enable */

export {
    id,
    name,
    latitude,
    longitude,
    fetch
};
<|endoftext|>


<commit_before>angular.module('breadcrumb').factory('Trail', function ($http) {
  const submitTrail = (trail, crumbs) => {
    const length = trail.length.replace(/\D/g, '');
    if (!trail.transport) {
      trail.transport = 'WALKING';
      if (length > 5) {
        trail.transport = 'BICYCLING';
      } else if (length > 20) {
        trail.transport = 'DRIVING';
      }
    }
    if (!trail.difficulty) {
      trail.difficulty = 1;
      if (length > 5) {
        trail.difficulty = 2;
      } else if (length > 20) {
        trail.difficulty = 3;
      }
    }
    return $http({
      method: 'POST',
      url: 'http://54.203.104.113/trails',
      // url: 'http://192.168.99.100/trails',
      data: trail,
      json: true,
    })
    .then((response) => {
      const trailId = response.data.data[0].id;
      crumbs.forEach((crumb, index) => {
        crumb.trail_id = trailId;
        crumb.order_number = index + 1;
        return $http({
          method: 'POST',
          url: 'http://54.203.104.113/crumbs',
          // url: 'http://192.168.99.100/crumbs',
          data: crumb,
          json: true,
        });
      });
    })
    .catch(err => err);
  };
  return {
    submit: submitTrail,
  };
});
<commit_msg>(feature) refactor trail submission to take a full trail and submit all the crumbs, to the server side<commit_after>angular.module('breadcrumb').factory('Trail', function ($http) {
  const submitTrail = (trail, crumbs) => {
    const length = trail.length.replace(/\D/g, '');
    if (!trail.transport) {
      trail.transport = 'WALKING';
      if (length > 5) {
        trail.transport = 'BICYCLING';
      } else if (length > 20) {
        trail.transport = 'DRIVING';
      }
    }
    if (!trail.difficulty) {
      trail.difficulty = 1;
      if (length > 5) {
        trail.difficulty = 2;
      } else if (length > 20) {
        trail.difficulty = 3;
      }
    }
    trail.crumbs = crumbs;
    return $http({
      method: 'POST',
      // url: 'http://54.203.104.113/trails',
      // url: 'http://192.168.99.100:3000/trails',
      url: 'http://192.168.99.100:3000/trails',
      header: {
        'Access-Control-Allow-Origin': '*',
      },
      data: trail,
      json: true,
    })
    .then(response => response)
    .catch(err => err);
  };
  return {
    submit: submitTrail,
  };
});
<|endoftext|>


<commit_before>'use strict';
exports.base64 = false;
exports.binary = false;
exports.dir = false;
exports.createFolders = false;
exports.date = null;
exports.compression = null;
exports.compressionOptions = null;
exports.comment = null;
exports.unixPermissions = null;
exports.dosPermissions = null;
<commit_msg>Delete defaults.js<commit_after><|endoftext|>


<commit_before>/**
 * The Searchbox module.
 *
 * @return searchbox class (extends Backbone.View).
 */
define([
  'underscore',
  'backbone',
  'handlebars',
  'map/views/Widget',
  'map/presenters/SearchboxPresenter',
  'text!map/templates/searchbox.handlebars'
], function(_, Backbone, Handlebars, Widget, Presenter, tpl) {

  'use strict';

  var Searchbox = Widget.extend({

    className: 'widget widget-searchbox',

    template: Handlebars.compile(tpl),

    initialize: function() {
      _.bindAll(this, 'setAutocomplete', 'onPlaceSelected');
      this.presenter = new Presenter(this);
      Searchbox.__super__.initialize.apply(this);
      this.setAutocomplete();
    },

    setAutocomplete: function() {
      this.autocomplete = new google.maps.places.SearchBox(this.$el.find('input')[0]);
      google.maps.event.addListener(this.autocomplete, 'places_changed', this.onPlaceSelected);
    },

    onPlaceSelected: function() {
      var place = this.autocomplete.getPlaces();
      if (place.length == 1) {
        place = place[0];
        if (place && place.geometry && place.geometry.viewport) {
          this.presenter.fitBounds(place.geometry.viewport);
        }
        // TODO: When there isn't viewport, and there is location...
        if (place && place.geometry && place.geometry.location && !place.geometry.viewport) {
          console.log(place.geometry.location);
          this.presenter.setCenter(place.geometry.location.k,place.geometry.location.B);
        }
      };
      ga('send', 'event', 'Map', 'Searchbox', 'Find location');
    }

  });

  return Searchbox;
});
<commit_msg>Change autocomplete plugin to search box plugin, it allows us to use the enter key<commit_after>/**
 * The Searchbox module.
 *
 * @return searchbox class (extends Backbone.View).
 */
define([
  'underscore',
  'backbone',
  'handlebars',
  'map/views/Widget',
  'map/presenters/SearchboxPresenter',
  'text!map/templates/searchbox.handlebars'
], function(_, Backbone, Handlebars, Widget, Presenter, tpl) {

  'use strict';

  var Searchbox = Widget.extend({

    className: 'widget widget-searchbox',

    template: Handlebars.compile(tpl),

    initialize: function() {
      _.bindAll(this, 'setAutocomplete', 'onPlaceSelected');
      this.presenter = new Presenter(this);
      Searchbox.__super__.initialize.apply(this);
      this.setAutocomplete();
    },

    setAutocomplete: function() {
      this.autocomplete = new google.maps.places.SearchBox(this.$el.find('input')[0]);
      google.maps.event.addListener(this.autocomplete, 'places_changed', this.onPlaceSelected);
    },

    onPlaceSelected: function() {
      var place = this.autocomplete.getPlaces();
      if (place.length == 1) {
        place = place[0];
        if (place && place.geometry && place.geometry.viewport) {
          this.presenter.fitBounds(place.geometry.viewport);
        }
        // TODO: When there isn't viewport, and there is location...
        if (place && place.geometry && place.geometry.location && !place.geometry.viewport) {
          this.presenter.setCenter(place.geometry.location.k,place.geometry.location.B);
        }
      };
      ga('send', 'event', 'Map', 'Searchbox', 'Find location');
    }

  });

  return Searchbox;
});
<|endoftext|>


<commit_before>(function() {

  var noop = function() {};
  var objectFreeze = (Object.freeze) ? Object.freeze : noop;
  var objectDefineProperty = Object.defineProperty;
  var isString = angular.isString;
  var isFunction = angular.isFunction;
  var isDefined = angular.isDefined;
  var isArray = angular.isArray;
  // ie8 wat
  if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(elt /*, from*/) {
      var len = this.length >>> 0;
      var from = Number(arguments[1]) || 0;
      from = (from < 0) ? Math.ceil(from) : Math.floor(from);
      if (from < 0) {
        from += len;
      }

      for (; from < len; from++) {
        if (from in this && this[from] === elt) { return from; }
      }
      return -1;
    };
  }

  $WebSocketProvider.$inject = ['$rootScope', '$q', '$timeout', '$websocketBackend'];
  function $WebSocketProvider($rootScope, $q, $timeout, $websocketBackend) {

    function safeDigest(autoApply) {
      if (autoApply && !$rootScope.$$phase) {
        $rootScope.$apply();
      }
    }

    function $WebSocket(url, options) {
      // var bits = url.split('/');

      var protocols = options && options.protocols;
      if (isString(options) || isArray(options)) {
        protocols = options;
      }

      this.protocols = protocols || 'Sec-WebSocket-Protocol';
      this.url = url || 'Missing URL';
      this.ssl = /(wss)/i.test(this.url);

      // this.binaryType = '';
      // this.extensions = '';
      // this.bufferedAmount = 0;
      // this.trasnmitting = false;
      // this.buffer = [];

      this._reconnectAttempts = 0;
      this.initialTimeout = 500; // 500ms
      this.maxTimeout = 5 * 60 * 1000; // 5 minutes
      this.sendQueue = [];
      this.onOpenCallbacks = [];
      this.onMessageCallbacks = [];
      this.onErrorCallbacks = [];
      this.onCloseCallbacks = [];

      objectFreeze(this._readyStateConstants);

      if (url) {
        this._connect();
      } else {
        this._setInternalState(0);
      }
    }

    $WebSocket.prototype._readyStateConstants = {
      'CONNECTING': 0,
      'OPEN': 1,
      'CLOSING': 2,
      'CLOSED': 3,
      'RECONNECT_ABORTED': 4
    };

    $WebSocket.prototype._reconnectableStatusCodes = [
      4000
    ];

    $WebSocket.prototype.close = function (force) {
      if (force || !this.socket.bufferedAmount) {
        this.socket.close();
      }
      return this;
    };

    $WebSocket.prototype._connect = function (force) {
      if (force || !this.socket || this.socket.readyState !== this._readyStateConstants.OPEN) {
        this.socket = $websocketBackend.createWebSocketBackend(this.url, this.protocol);
        this.socket.onopen = this._onOpenHandler.bind(this);
        this.socket.onmessage = this._onMessageHandler.bind(this);
        this.socket.onerror = this._onErrorHandler.bind(this);
        this.socket.onclose = this._onCloseHandler.bind(this);
      }
    };

    $WebSocket.prototype.fireQueue = function () {
      while (this.sendQueue.length && this.socket.readyState === this._readyStateConstants.OPEN) {
        var data = this.sendQueue.shift();

        this.socket.send(
          isString(data.message) ? data.message : JSON.stringify(data.message)
        );
        data.deferred.resolve();
      }
    };

    $WebSocket.prototype.notifyOpenCallbacks = function () {
      for (var i = 0; i < this.onOpenCallbacks.length; i++) {
        this.onOpenCallbacks[i].call(this);
      }
    };

    $WebSocket.prototype.notifyCloseCallbacks = function (event) {
      for (var i = 0; i < this.onCloseCallbacks.length; i++) {
        this.onCloseCallbacks[i].call(this, event);
      }
    };
    $WebSocket.prototype.notifyErrorCallbacks = function (event) {
      for (var i = 0; i < this.onErrorCallbacks.length; i++) {
        this.onErrorCallbacks[i].call(this, event);
      }
    };

    $WebSocket.prototype.onOpen = function (cb) {
      this.onOpenCallbacks.push(cb);
      return this;
    };

    $WebSocket.prototype.onClose = function (cb) {
      this.onCloseCallbacks.push(cb);
      return this;
    };

    $WebSocket.prototype.onError = function (cb) {
      this.onErrorCallbacks.push(cb);
      return this;
    };

    $WebSocket.prototype.onMessage = function (callback, options) {
      if (!isFunction(callback)) {
        throw new Error('Callback must be a function');
      }

      if (options && isDefined(options.filter) && !isString(options.filter) && !(options.filter instanceof RegExp)) {
        throw new Error('Pattern must be a string or regular expression');
      }

      this.onMessageCallbacks.push({
        fn: callback,
        pattern: options ? options.filter : undefined,
        autoApply: options ? options.autoApply : true
      });
      return this;
    };

    $WebSocket.prototype._onOpenHandler = function () {
      this._reconnectAttempts = 0;
      this.notifyOpenCallbacks();
      this.fireQueue();
    };

    $WebSocket.prototype._onCloseHandler = function (event) {
      this.notifyCloseCallbacks(event);
      if (this._reconnectableStatusCodes.indexOf(event.code) > -1) {
        this.reconnect();
      }
    };

    $WebSocket.prototype._onErrorHandler = function (event) {
      this.notifyErrorCallbacks(event);
    };

    $WebSocket.prototype._onMessageHandler = function (message) {
      var pattern;
      var socket = this;
      var currentCallback;
      for (var i = 0; i < socket.onMessageCallbacks.length; i++) {
        currentCallback = socket.onMessageCallbacks[i];
        pattern = currentCallback.pattern;
        if (pattern) {
          if (isString(pattern) && message.data === pattern) {
            currentCallback.fn.call(this, message);
            safeDigest(currentCallback.autoApply);
          }
          else if (pattern instanceof RegExp && pattern.exec(message.data)) {
            currentCallback.fn.call(this, message);
            safeDigest(currentCallback.autoApply);
          }
        }
        else {
          currentCallback.fn.call(this, message);
          safeDigest(currentCallback.autoApply);
        }
      }
    };

    $WebSocket.prototype.send = function (data) {
      var deferred = $q.defer();
      var socket = this;
      var promise = cancelableify(deferred.promise);

      if (socket.readyState === socket._readyStateConstants.RECONNECT_ABORTED) {
        deferred.reject('Socket connection has been closed');
      }
      else {
        this.sendQueue.push({
          message: data,
          deferred: deferred
        });
        this.fireQueue();
      }

      // Credit goes to @btford
      function cancelableify(promise) {
        promise.cancel = cancel;
        var then = promise.then;
        promise.then = function() {
          var newPromise = then.apply(this, arguments);
          return cancelableify(newPromise);
        };
        return promise;
      }

      function cancel(reason) {
        socket.sendQueue.splice(socket.sendQueue.indexOf(data), 1);
        deferred.reject(reason);
        return this;
      }

      return promise;
    };

    $WebSocket.prototype.reconnect = function () {
      this.close();
      $timeout(angular.bind(this, function() {
        this._connect();
      }), this._getBackoffDelay(++this._reconnectAttempts), true);

      return this;
    };
    // Exponential Backoff Formula by Prof. Douglas Thain
    // http://dthain.blogspot.co.uk/2009/02/exponential-backoff-in-distributed.html
    $WebSocket.prototype._getBackoffDelay = function(attempt) {
      var R = Math.random() + 1;
      var T = this.initialTimeout;
      var F = 2;
      var N = attempt;
      var M = this.maxTimeout;

      return Math.floor(Math.min(R * T * Math.pow(F, N), M));
    };

    $WebSocket.prototype._setInternalState = function(state) {
      if (Math.floor(state) !== state || state < 0 || state > 4) {
        throw new Error('state must be an integer between 0 and 4, got: ' + state);
      }

      // ie8 wat
      if (!objectDefineProperty) {
        this.readyState = state || this.socket.readyState;
      }
      this._internalConnectionState = state;

      angular.forEach(this.sendQueue, function(pending) {
        pending.deferred.reject('Message cancelled due to closed socket connection');
      });
    };

    if (objectDefineProperty) {
      objectDefineProperty($WebSocket.prototype, 'readyState', {
        get: function() {
          return this._internalConnectionState || this.socket.readyState;
        },
        set: function() {
          throw new Error('The readyState property is read-only');
        }
      });
    }

    return function(url, protocols) {
      return new $WebSocket(url, protocols);
    };
  }

  $WebSocketBackend.$inject = ['$window'];
  function $WebSocketBackend($window) {
    this.createWebSocketBackend = function (url, protocols) {
      var match = /wss?:\/\//.exec(url);
      var Socket, ws;
      if (!match) {
        throw new Error('Invalid url provided');
      }
      if (typeof exports === 'object' && require) {
        try {
          ws = require('ws');
          Socket = (ws.Client || ws.client || ws);
        } catch(e) {}
      }
      Socket = Socket || $window.WebSocket || $window.MozWebSocket;

      if (protocols) {
        return new Socket(url, protocols);
      }
      return new Socket(url);
    };
  }

  angular.module('ngWebSocket', [])
  .factory('$websocket', $WebSocketProvider)
  .factory('WebSocket',  $WebSocketProvider)
  .service('$websocketBackend', $WebSocketBackend)
  .service('WebSocketBackend', $WebSocketBackend);

  angular.module('angular-websocket', ['ngWebSocket']);

}());
<commit_msg>perf: use strict<commit_after>(function() {
  'use strict';

  var noop = function() {};
  var objectFreeze = (Object.freeze) ? Object.freeze : noop;
  var objectDefineProperty = Object.defineProperty;
  var isString = angular.isString;
  var isFunction = angular.isFunction;
  var isDefined = angular.isDefined;
  var isArray = angular.isArray;
  // ie8 wat
  if (!Array.prototype.indexOf) {
    Array.prototype.indexOf = function(elt /*, from*/) {
      var len = this.length >>> 0;
      var from = Number(arguments[1]) || 0;
      from = (from < 0) ? Math.ceil(from) : Math.floor(from);
      if (from < 0) {
        from += len;
      }

      for (; from < len; from++) {
        if (from in this && this[from] === elt) { return from; }
      }
      return -1;
    };
  }

  $WebSocketProvider.$inject = ['$rootScope', '$q', '$timeout', '$websocketBackend'];
  function $WebSocketProvider($rootScope, $q, $timeout, $websocketBackend) {

    function safeDigest(autoApply) {
      if (autoApply && !$rootScope.$$phase) {
        $rootScope.$apply();
      }
    }

    function $WebSocket(url, options) {
      // var bits = url.split('/');

      var protocols = options && options.protocols;
      if (isString(options) || isArray(options)) {
        protocols = options;
      }

      this.protocols = protocols || 'Sec-WebSocket-Protocol';
      this.url = url || 'Missing URL';
      this.ssl = /(wss)/i.test(this.url);

      // this.binaryType = '';
      // this.extensions = '';
      // this.bufferedAmount = 0;
      // this.trasnmitting = false;
      // this.buffer = [];

      this._reconnectAttempts = 0;
      this.initialTimeout = 500; // 500ms
      this.maxTimeout = 5 * 60 * 1000; // 5 minutes
      this.sendQueue = [];
      this.onOpenCallbacks = [];
      this.onMessageCallbacks = [];
      this.onErrorCallbacks = [];
      this.onCloseCallbacks = [];

      objectFreeze(this._readyStateConstants);

      if (url) {
        this._connect();
      } else {
        this._setInternalState(0);
      }
    }

    $WebSocket.prototype._readyStateConstants = {
      'CONNECTING': 0,
      'OPEN': 1,
      'CLOSING': 2,
      'CLOSED': 3,
      'RECONNECT_ABORTED': 4
    };

    $WebSocket.prototype._reconnectableStatusCodes = [
      4000
    ];

    $WebSocket.prototype.close = function (force) {
      if (force || !this.socket.bufferedAmount) {
        this.socket.close();
      }
      return this;
    };

    $WebSocket.prototype._connect = function (force) {
      if (force || !this.socket || this.socket.readyState !== this._readyStateConstants.OPEN) {
        this.socket = $websocketBackend.createWebSocketBackend(this.url, this.protocol);
        this.socket.onopen = this._onOpenHandler.bind(this);
        this.socket.onmessage = this._onMessageHandler.bind(this);
        this.socket.onerror = this._onErrorHandler.bind(this);
        this.socket.onclose = this._onCloseHandler.bind(this);
      }
    };

    $WebSocket.prototype.fireQueue = function () {
      while (this.sendQueue.length && this.socket.readyState === this._readyStateConstants.OPEN) {
        var data = this.sendQueue.shift();

        this.socket.send(
          isString(data.message) ? data.message : JSON.stringify(data.message)
        );
        data.deferred.resolve();
      }
    };

    $WebSocket.prototype.notifyOpenCallbacks = function () {
      for (var i = 0; i < this.onOpenCallbacks.length; i++) {
        this.onOpenCallbacks[i].call(this);
      }
    };

    $WebSocket.prototype.notifyCloseCallbacks = function (event) {
      for (var i = 0; i < this.onCloseCallbacks.length; i++) {
        this.onCloseCallbacks[i].call(this, event);
      }
    };
    $WebSocket.prototype.notifyErrorCallbacks = function (event) {
      for (var i = 0; i < this.onErrorCallbacks.length; i++) {
        this.onErrorCallbacks[i].call(this, event);
      }
    };

    $WebSocket.prototype.onOpen = function (cb) {
      this.onOpenCallbacks.push(cb);
      return this;
    };

    $WebSocket.prototype.onClose = function (cb) {
      this.onCloseCallbacks.push(cb);
      return this;
    };

    $WebSocket.prototype.onError = function (cb) {
      this.onErrorCallbacks.push(cb);
      return this;
    };

    $WebSocket.prototype.onMessage = function (callback, options) {
      if (!isFunction(callback)) {
        throw new Error('Callback must be a function');
      }

      if (options && isDefined(options.filter) && !isString(options.filter) && !(options.filter instanceof RegExp)) {
        throw new Error('Pattern must be a string or regular expression');
      }

      this.onMessageCallbacks.push({
        fn: callback,
        pattern: options ? options.filter : undefined,
        autoApply: options ? options.autoApply : true
      });
      return this;
    };

    $WebSocket.prototype._onOpenHandler = function () {
      this._reconnectAttempts = 0;
      this.notifyOpenCallbacks();
      this.fireQueue();
    };

    $WebSocket.prototype._onCloseHandler = function (event) {
      this.notifyCloseCallbacks(event);
      if (this._reconnectableStatusCodes.indexOf(event.code) > -1) {
        this.reconnect();
      }
    };

    $WebSocket.prototype._onErrorHandler = function (event) {
      this.notifyErrorCallbacks(event);
    };

    $WebSocket.prototype._onMessageHandler = function (message) {
      var pattern;
      var socket = this;
      var currentCallback;
      for (var i = 0; i < socket.onMessageCallbacks.length; i++) {
        currentCallback = socket.onMessageCallbacks[i];
        pattern = currentCallback.pattern;
        if (pattern) {
          if (isString(pattern) && message.data === pattern) {
            currentCallback.fn.call(this, message);
            safeDigest(currentCallback.autoApply);
          }
          else if (pattern instanceof RegExp && pattern.exec(message.data)) {
            currentCallback.fn.call(this, message);
            safeDigest(currentCallback.autoApply);
          }
        }
        else {
          currentCallback.fn.call(this, message);
          safeDigest(currentCallback.autoApply);
        }
      }
    };

    $WebSocket.prototype.send = function (data) {
      var deferred = $q.defer();
      var socket = this;
      var promise = cancelableify(deferred.promise);

      if (socket.readyState === socket._readyStateConstants.RECONNECT_ABORTED) {
        deferred.reject('Socket connection has been closed');
      }
      else {
        this.sendQueue.push({
          message: data,
          deferred: deferred
        });
        this.fireQueue();
      }

      // Credit goes to @btford
      function cancelableify(promise) {
        promise.cancel = cancel;
        var then = promise.then;
        promise.then = function() {
          var newPromise = then.apply(this, arguments);
          return cancelableify(newPromise);
        };
        return promise;
      }

      function cancel(reason) {
        socket.sendQueue.splice(socket.sendQueue.indexOf(data), 1);
        deferred.reject(reason);
        return this;
      }

      return promise;
    };

    $WebSocket.prototype.reconnect = function () {
      this.close();
      $timeout(angular.bind(this, function() {
        this._connect();
      }), this._getBackoffDelay(++this._reconnectAttempts), true);

      return this;
    };
    // Exponential Backoff Formula by Prof. Douglas Thain
    // http://dthain.blogspot.co.uk/2009/02/exponential-backoff-in-distributed.html
    $WebSocket.prototype._getBackoffDelay = function(attempt) {
      var R = Math.random() + 1;
      var T = this.initialTimeout;
      var F = 2;
      var N = attempt;
      var M = this.maxTimeout;

      return Math.floor(Math.min(R * T * Math.pow(F, N), M));
    };

    $WebSocket.prototype._setInternalState = function(state) {
      if (Math.floor(state) !== state || state < 0 || state > 4) {
        throw new Error('state must be an integer between 0 and 4, got: ' + state);
      }

      // ie8 wat
      if (!objectDefineProperty) {
        this.readyState = state || this.socket.readyState;
      }
      this._internalConnectionState = state;

      angular.forEach(this.sendQueue, function(pending) {
        pending.deferred.reject('Message cancelled due to closed socket connection');
      });
    };

    if (objectDefineProperty) {
      objectDefineProperty($WebSocket.prototype, 'readyState', {
        get: function() {
          return this._internalConnectionState || this.socket.readyState;
        },
        set: function() {
          throw new Error('The readyState property is read-only');
        }
      });
    }

    return function(url, protocols) {
      return new $WebSocket(url, protocols);
    };
  }

  $WebSocketBackend.$inject = ['$window'];
  function $WebSocketBackend($window) {
    this.createWebSocketBackend = function (url, protocols) {
      var match = /wss?:\/\//.exec(url);
      var Socket, ws;
      if (!match) {
        throw new Error('Invalid url provided');
      }
      if (typeof exports === 'object' && require) {
        try {
          ws = require('ws');
          Socket = (ws.Client || ws.client || ws);
        } catch(e) {}
      }
      Socket = Socket || $window.WebSocket || $window.MozWebSocket;

      if (protocols) {
        return new Socket(url, protocols);
      }
      return new Socket(url);
    };
  }

  angular.module('ngWebSocket', [])
  .factory('$websocket', $WebSocketProvider)
  .factory('WebSocket',  $WebSocketProvider)
  .service('$websocketBackend', $WebSocketBackend)
  .service('WebSocketBackend', $WebSocketBackend);

  angular.module('angular-websocket', ['ngWebSocket']);

}());
<|endoftext|>


<commit_before>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2015  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import difference from 'lodash/difference';
import uniq from 'lodash/uniq';

export const User = {
  registration: {
    username: [
      'required',
      'maxLength:31',
      { rule: (val) => {
        if (!val.match(/^(?!.*\.{2})[a-z0-9\-\_\'\.]+$/i)) {
          throw new Error("Username can contain letters a-z, numbers 0-9, dashes (-), underscores (_), apostrophes (\'), and periods (.)");
        }
      }
      }],
    password: [
      'required',
      {
        rule: (val) => {
          if (!val.match(/^[\x20-\x7E]{8,}$/)) {
            throw new Error("Password is min. 8 characters. Password can only have ascii characters.");
          }
        }
      }
    ],
    firstName: [
    ],
    lastName: [
    ],
    // email: ['email', 'required']
    email: [
      'required',
      {
        rule: (val) => {
          if (!val.match(/^[a-z0-9!#$%&"'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i)) {
            throw new Error('The email must be a valid email address');
          }
        }
      }
    ]
  },

  settings: {
    base: {
    },
    more: {
      summary: ['string'],
      bio: ['string'],
      roles: ['array'],
      first_login: ['boolean'],
      avatar: ['plainObject'],
      head_pic: ['plainObject'],
      mute_all_posts: ['boolean'],
      firstName: ['string'],
      lastName: ['string']
    }
  }
};

export const School = {
  more: {
    head_pic: ['plainObject'],
    last_editor: ['string']
  }
};

export const Geotag = {
  more: {
    description: ['string'],
    last_editor: ['string']
  }
};

export const Hashtag = {
  more: {
    description: ['string'],
    head_pic: ['plainObject'],
    last_editor: ['string']
  }
};

export const UserMessage = {
  text: ['string', 'minLength:1', 'required']
};

export const ProfilePost = {
  text: ['string', 'maxLength:200'],
  html: ['string'],
  type: [
    'required',
    val => {
      if (!ProfilePost.TYPES.includes(val)) {
        throw new Error('Invalid post type');
      }
    }
  ],
  user_id: ['required', 'uuid'],
  more: ['plainObject']
};

ProfilePost.TYPES = [
  'text',
  'head_pic',
  'avatar'
];

const SEARCH_RESULT_TYPES = [
  'hashtags',
  'locations',
  'posts',
  'people',
  'schools'
];
const SORTING_TYPES = [
  '-q',
  '-updated_at'
];
export const SearchQuery = {
  limit: ['integer', 'natural'],
  offset: ['integer', 'natural'],
  show: {
    rule: val => {
      if (val) {
        if (typeof val === 'string') {
          if (val !== 'all' && !SEARCH_RESULT_TYPES.includes(val)) {
            throw new Error('Unsupported type of search result');
          }
        } else if (Array.isArray(val)) {
          const unique = uniq(val);
          if (unique.length > 1 && unique.includes('all')) {
            throw new Error('Ambiguity between "all" and explicit type of search results');
          } else if (difference(unique, SEARCH_RESULT_TYPES).length > 0) {
            throw new Error('At least one of presented search result types is unsupported');
          }
        } else {
          throw new Error('Type of search results is expected to be a string or an array');
        }
      }
    }
  },
  sort: ['string', val => {
    if (val && !SORTING_TYPES.includes(val)) {
      throw new Error('Invalid search sorting type');
    }
  }],
  q: ['string']
};
<commit_msg>Support setting user's 'more.lang' property<commit_after>/*
 This file is a part of libertysoil.org website
 Copyright (C) 2015  Loki Education (Social Enterprise)

 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU Affero General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU Affero General Public License for more details.

 You should have received a copy of the GNU Affero General Public License
 along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
import difference from 'lodash/difference';
import uniq from 'lodash/uniq';

const SUPPORTED_LOCALES = Object.keys(
  require('../../consts/localization').SUPPORTED_LOCALES
);

export const User = {
  registration: {
    username: [
      'required',
      'maxLength:31',
      { rule: (val) => {
        if (!val.match(/^(?!.*\.{2})[a-z0-9\-\_\'\.]+$/i)) {
          throw new Error("Username can contain letters a-z, numbers 0-9, dashes (-), underscores (_), apostrophes (\'), and periods (.)");
        }
      }
      }],
    password: [
      'required',
      {
        rule: (val) => {
          if (!val.match(/^[\x20-\x7E]{8,}$/)) {
            throw new Error("Password is min. 8 characters. Password can only have ascii characters.");
          }
        }
      }
    ],
    firstName: [
    ],
    lastName: [
    ],
    // email: ['email', 'required']
    email: [
      'required',
      {
        rule: (val) => {
          if (!val.match(/^[a-z0-9!#$%&"'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/i)) {
            throw new Error('The email must be a valid email address');
          }
        }
      }
    ]
  },

  settings: {
    base: {
    },
    more: {
      summary: ['string'],
      bio: ['string'],
      roles: ['array'],
      first_login: ['boolean'],
      avatar: ['plainObject'],
      head_pic: ['plainObject'],
      mute_all_posts: ['boolean'],
      firstName: ['string'],
      lastName: ['string'],
      lang: {
        rule: val => {
          if (val) {
            if (!SUPPORTED_LOCALES.includes(val)) {
              throw new Error('Locale isn\'t supported');
            }
          }
        }
      }
    }
  }
};

export const School = {
  more: {
    head_pic: ['plainObject'],
    last_editor: ['string']
  }
};

export const Geotag = {
  more: {
    description: ['string'],
    last_editor: ['string']
  }
};

export const Hashtag = {
  more: {
    description: ['string'],
    head_pic: ['plainObject'],
    last_editor: ['string']
  }
};

export const UserMessage = {
  text: ['string', 'minLength:1', 'required']
};

export const ProfilePost = {
  text: ['string', 'maxLength:200'],
  html: ['string'],
  type: [
    'required',
    val => {
      if (!ProfilePost.TYPES.includes(val)) {
        throw new Error('Invalid post type');
      }
    }
  ],
  user_id: ['required', 'uuid'],
  more: ['plainObject']
};

ProfilePost.TYPES = [
  'text',
  'head_pic',
  'avatar'
];

const SEARCH_RESULT_TYPES = [
  'hashtags',
  'locations',
  'posts',
  'people',
  'schools'
];
const SORTING_TYPES = [
  '-q',
  '-updated_at'
];
export const SearchQuery = {
  limit: ['integer', 'natural'],
  offset: ['integer', 'natural'],
  show: {
    rule: val => {
      if (val) {
        if (typeof val === 'string') {
          if (val !== 'all' && !SEARCH_RESULT_TYPES.includes(val)) {
            throw new Error('Unsupported type of search result');
          }
        } else if (Array.isArray(val)) {
          const unique = uniq(val);
          if (unique.length > 1 && unique.includes('all')) {
            throw new Error('Ambiguity between "all" and explicit type of search results');
          } else if (difference(unique, SEARCH_RESULT_TYPES).length > 0) {
            throw new Error('At least one of presented search result types is unsupported');
          }
        } else {
          throw new Error('Type of search results is expected to be a string or an array');
        }
      }
    }
  },
  sort: ['string', val => {
    if (val && !SORTING_TYPES.includes(val)) {
      throw new Error('Invalid search sorting type');
    }
  }],
  q: ['string']
};
<|endoftext|>


<commit_before><commit_msg>Adds date test case<commit_after><|endoftext|>


<commit_before>/*****************************************************************************/
/* SaleEdit: Event Handlers */
/*****************************************************************************/
Template.SaleEdit.events({
  'click .remove-item': function() {
    console.log(this);
    var sale = Blaze.getData().sale;
    console.log(sale);
    Sales.update({_id: sale._id}, {$pullAll: {items: [this]}}, function(err, result){console.log(err); console.log(result);});
  }
});

/*****************************************************************************/
/* SaleEdit: Helpers */
/*****************************************************************************/
Template.SaleEdit.helpers({
  productName: function(productId) {
    var product = Products.findOne(productId);
    return product.name;
  },

  asDollars: function(amt) {
    return '$' + amt.toFixed(2);
  },

  asPercent: function(pct) {
    return pct * 100 + '%'
  }
});

/*****************************************************************************/
/* SaleEdit: Lifecycle Hooks */
/*****************************************************************************/
Template.SaleEdit.created = function () {
};

Template.SaleEdit.rendered = function () {
};

Template.SaleEdit.destroyed = function () {
};
<commit_msg>updating eachPrice when new product is selected<commit_after>/*****************************************************************************/
/* SaleEdit: Event Handlers */
/*****************************************************************************/
Template.SaleEdit.events({
  'change .product-select': function(event, template) {
    console.log(event.target.value);
    var product = Products.findOne(event.target.value);
    $("input[name=eachPrice]").val(product.listPrice);
  },

  'click .remove-item': function(event, template) {
    console.log(this);
    var sale = template.data.sale;
    console.log(sale);
    Sales.update({_id: sale._id}, {$pullAll: {items: [this]}}, function(err, result){console.log(err); console.log(result);});
  }
});

/*****************************************************************************/
/* SaleEdit: Helpers */
/*****************************************************************************/
Template.SaleEdit.helpers({
  productName: function(productId) {
    var product = Products.findOne(productId);
    return product.name;
  },

  asDollars: function(amt) {
    return '$' + amt.toFixed(2);
  },

  asPercent: function(pct) {
    return pct * 100 + '%'
  }
});

/*****************************************************************************/
/* SaleEdit: Lifecycle Hooks */
/*****************************************************************************/
Template.SaleEdit.created = function () {
};

Template.SaleEdit.rendered = function () {
};

Template.SaleEdit.destroyed = function () {
};
<|endoftext|>


<commit_before>$(document).ready(function() {
    $(function(){
    Backbone.Model.prototype.idAttribute = "_id";
    var Rooms = Backbone.Collection.extend({
        model: Room,
        url: 'rooms.json',
        parse: function(response) {
            _.each(response, function(room) {
                room.id = room._id;
            })
            return response;
        },
        colorForEvent: function(event) {
            return this.get(event.get('location_id')).get('color');
        }
    });

    var Room = Backbone.Model.extend({
        defaults: {
            name: null,
            color: null
        }
    });

    var Event = Backbone.Model.extend({
        defaults: {
            dtstart: null,
            start: null,
            dtend: null,
            end: null,
            title: null,
            location_id: null
        },
        url: function() {
            return '/events' + (this.isNew() ? '' : ('/' + this.id));
        },
        initialize: function() {
            _.bindAll(this, 'setColor', 'setDtstart', 'setDtend');
            if(this.get('location_id')) this.setColor();
            if(this.get('start')) this.setDtstart();
            if(this.get('end')) this.setDtend();
            this.bind('change:location_id', this.setColor);
            this.bind('change:start', this.setDtstart);
            this.bind('change:end', this.setDtend);
        },

        // keep sync between location and it's associate color'
        setColor: function() {
            this.set({color: rooms.colorForEvent(this)});
        },
        // keep sync between full calendar date and server side
        setDtstart: function() {
            this.set({dtstart: this.get('start')});
        },
        // keep sync between full calendar date and server side
        setDtend: function() {
            this.set({dtend: this.get('end')});
        },
        toCallendar: function() {
            var format = this.toJSON();
            format.id = this.id;
            var organizer = this.get('organizer')
            format.title = this.get('title') + (organizer ? (' | ' + organizer.email) : '');

            return format;
        }

    });

    var Events = Backbone.Collection.extend({
        model: Event,
        url: '/events',
        parse: function(response) {
            _.each(response, function(event) {
                event.start = event.dtstart;
                event.end = event.dtend;
            });
            return response;
        },
        toCallendar: function() {
            var t = this.map(function(event) {return event.toCallendar()});
            return t;
        }
    });

    var LegendView = Backbone.View.extend({
        el: $('#legend'),
        initialize: function() {
            _.bindAll(this);
            this.collection.bind('reset', this.render);
        },
        render: function() {
            var html = "";
            rooms.each(function(room) {
                //html += '
' + room.get('name') + '
';
                html += '

' + room.get('name') + '

';
            });
            this.el.html(html);
            return this;
        }
    });

    var EventsView = Backbone.View.extend({
        initialize: function(){
            _.bindAll(this);

            this.collection.bind('reset', this.addAll);
            this.collection.bind('add', this.addOne);
            this.collection.bind('change', this.change);
            this.collection.bind('destroy', this.destroy);

            this.eventView = new EventView();
            this.legendView = new LegendView({collection: rooms});
        },
        render: function() {
            this.el.fullCalendar({
                header: {
                    left: 'prev,next today',
                    center: 'title',
                    right: 'month,agendaWeek,agendaDay'
                },
                buttonText: {
                    prev:     ' ◄ ',  // left triangle
                    next:     ' ► ',  // right triangle
                    prevYear: ' << ', // <<
                    nextYear: ' >> ', // >>
                    today:    'aujourd\'hui',
                    month:    'mois',
                    week:     'semaine',
                    day:      'jour'
                },
                titleFormat: {
                    month: 'MMMM yyyy',
                    week: "d MMMM[ yyyy]{ - d [ MMMM] yyyy}",
                    agendaDay: 'dddd d MMM yyyy'
                },
                columnFormat: {
                    month: 'ddd',
                    week: 'ddd d/M',
                    day: 'dddd d/M'
                },
                monthNames: ['Janvier', 'Fevrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet',
                 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Decembre'],
                monthNamesShort: ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jui', 'Jui',
                 'Aou', 'Sep', 'Oct', 'Nov', 'Dec'],
                dayNames: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
                dayNamesShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
                firstDay: 1,
                timeFormat: 'H:mm{ - H:mm}',
                axisFormat: 'H:mm',
                defaultView: 'agendaWeek',
                minTime: 8,
                maxTime: 21,
                allDayDefault: false,
                allDaySlot: false,
                selectable: true,
                selectHelper: true,
                editable: true,
                ignoreTimezone: false,
                select: this.select,
                eventClick: this.eventClick,
                eventDrop: this.eventDropOrResize,
                eventResize: this.eventDropOrResize
            });
        },
        addAll: function() {
            this.el.fullCalendar('addEventSource', this.collection.toCallendar());
        },
        addOne: function(event) {
            this.el.fullCalendar('renderEvent', event.toCallendar());
        },
        select: function(startDate, endDate) {
            this.eventView.collection = this.collection;
            this.eventView.model = new Event({
                start: $.fullCalendar.formatDate(startDate, 'u'),
                end: $.fullCalendar.formatDate(endDate, 'u')
            });
            this.eventView.render();
        },
        eventClick: function(fcEvent) {
            this.eventView.model = this.collection.get(fcEvent.id);
            this.eventView.render();
        },
        change: function(event) {
            // Look up the underlying event in the calendar and update its details from the model
            var fcEvent = this.el.fullCalendar('clientEvents', event.id)[0];
            var organizer = event.get('organizer')
            fcEvent.title = event.get('title') + (organizer ? (' | ' + organizer.email) : '');
            fcEvent.color = event.get('color');
            fcEvent.start = event.get('start');
            fcEvent.end = event.get('end');
            this.el.fullCalendar('updateEvent', fcEvent);
        },
        eventDropOrResize: function(fcEvent) {
            // Lookup the model that has the ID of the event and update its attributes
            var event = this.collection.get(fcEvent.id)
              , values = {start: fcEvent.start, end: fcEvent.end}
              , el = this.el;
            $.when(
                event.clone().save(values))
                .done(function() {event.set(values, {silent: true})})
                .fail(function() {
                    $.gritter.add({title: Wording.error, text: Wording.update.unauthorized});
                })
                .fail(function() {
                    var fcEvent = el.fullCalendar('clientEvents', event.id)[0];
                    fcEvent.start = event.get('start');
                    fcEvent.end = event.get('end');
                    el.fullCalendar('updateEvent', fcEvent);
                });
        },
        destroy: function(event) {
            this.el.fullCalendar('removeEvents', event.id);
        }
    });

    var EventView = Backbone.View.extend({
        el: $('#eventDialog'),
        initialize: function() {
            _.bindAll(this);
        },
        render: function() {
            var buttons = {'Ok': this.save};
            if (!this.model.isNew()) {
                _.extend(buttons, {'Delete': this.destroy});
            }
            _.extend(buttons, {'Cancel': this.close});

            this.el.dialog({
                modal: true,
                title: (this.model.isNew() ? 'New' : 'Edit') + ' Event',
                buttons: buttons,
                open: this.open
            });

            return this;
        },
        open: function() {
            this.$('#title').val(this.model.get('title'));
            var begin = $.fullCalendar.parseDate(this.model.get('start'));
            this.$('#begin').val($.fullCalendar.formatDate(begin, 'HH:mm'));
            this.$('#begin').timepicker();
            var end = $.fullCalendar.parseDate(this.model.get('end'));
            this.$('#end').val($.fullCalendar.formatDate(end, 'HH:mm'));
            this.$('#end').timepicker();
            var room = new RoomsSelectView({el: this.$('#rooms')});
            room.render({selected: this.model.get('location_id')});
        },
        save: function() {
            var start = $.fullCalendar.parseDate(this.model.get('start'))
              , newStart = $.fullCalendar.formatDate(start, "yyyy-MM-dd'T"+this.$('#begin').val()+"':ssTZO")
              , end = $.fullCalendar.parseDate(this.model.get('start'))
              , newEnd = $.fullCalendar.formatDate(end, "yyyy-MM-dd'T"+this.$('#end').val()+"':ssTZO");
            var values = {
                'title': this.$('#title').val(),
                'start': newStart,
                'end': newEnd,
                'location_id': this.$('select[name="rooms"] :selected').val()};

            if (this.model.isNew()) {
                this.model.set(values);
                $.when(
                    this.collection.create(this.model))
                    .then(this.close);
            } else {
                var model = this.model;
                $.when(
                    model.clone().save(values))
                    .done(function() {model.set(values)})
                    .then(this.close)
                    .fail(function(){$.gritter.add({title: Wording.error, text: Wording.update.unauthorized})});
            }
        },
        close: function() {
            this.el.dialog('close');
        },
        destroy: function() {
            $.when(
                this.model.destroy())
                .then(this.close)
                .fail(function(){$.gritter.add({title: Wording.error, text: Wording.delete.unauthorized})});
        }
    });

    var RoomsSelectView = Backbone.View.extend({
        render: function(options) {
            var select = $('<select name="rooms"/>');
            rooms.each(function(room) {
                var selected = options.selected == room.id;
                select.append('<option value="'+room.id +'"'+ (selected  ? ' selected' : '') +'>'+room.get('name')+'</option>');
            });
            this.el.html(select);
        }
    });

    var events = new Events();
    var rooms = new Rooms();
    new EventsView({el: $("#main"), collection: events}).render();
    rooms.fetch().done(events.fetch); // need rooms config before displaying.
});

});

var Wording = {
    'error': 'Erreur',
    'update': {
        unauthorized: 'Vous n\'êtes pas autorisé à modifier cet événement.'
    },
    'delete': {
        unauthorized: 'Vous n\'êtes pas autorisé à supprimer cet événement.'
    }
};
<commit_msg>fix events not loaded<commit_after>$(document).ready(function() {
    $(function(){
    Backbone.Model.prototype.idAttribute = "_id";
    var Rooms = Backbone.Collection.extend({
        model: Room,
        url: 'rooms.json',
        parse: function(response) {
            _.each(response, function(room) {
                room.id = room._id;
            })
            return response;
        },
        colorForEvent: function(event) {
            return this.get(event.get('location_id')).get('color');
        }
    });

    var Room = Backbone.Model.extend({
        defaults: {
            name: null,
            color: null
        }
    });

    var Event = Backbone.Model.extend({
        defaults: {
            dtstart: null,
            start: null,
            dtend: null,
            end: null,
            title: null,
            location_id: null
        },
        url: function() {
            return '/events' + (this.isNew() ? '' : ('/' + this.id));
        },
        initialize: function() {
            _.bindAll(this, 'setColor', 'setDtstart', 'setDtend');
            if(this.get('location_id')) this.setColor();
            if(this.get('start')) this.setDtstart();
            if(this.get('end')) this.setDtend();
            this.bind('change:location_id', this.setColor);
            this.bind('change:start', this.setDtstart);
            this.bind('change:end', this.setDtend);
        },

        // keep sync between location and it's associate color'
        setColor: function() {
            this.set({color: rooms.colorForEvent(this)});
        },
        // keep sync between full calendar date and server side
        setDtstart: function() {
            this.set({dtstart: this.get('start')});
        },
        // keep sync between full calendar date and server side
        setDtend: function() {
            this.set({dtend: this.get('end')});
        },
        toCallendar: function() {
            var format = this.toJSON();
            format.id = this.id;
            var organizer = this.get('organizer')
            format.title = this.get('title') + (organizer ? (' | ' + organizer.email) : '');

            return format;
        }

    });

    var Events = Backbone.Collection.extend({
        model: Event,
        url: '/events',
        parse: function(response) {
            _.each(response, function(event) {
                event.start = event.dtstart;
                event.end = event.dtend;
            });
            return response;
        },
        toCallendar: function() {
            var t = this.map(function(event) {return event.toCallendar()});
            return t;
        }
    });

    var LegendView = Backbone.View.extend({
        el: $('#legend'),
        initialize: function() {
            _.bindAll(this);
            this.collection.bind('reset', this.render);
        },
        render: function() {
            var html = "";
            rooms.each(function(room) {
                //html += '
' + room.get('name') + '
';
                html += '

' + room.get('name') + '

';
            });
            this.el.html(html);
            return this;
        }
    });

    var EventsView = Backbone.View.extend({
        initialize: function(){
            _.bindAll(this);

            this.collection.bind('reset', this.addAll);
            this.collection.bind('add', this.addOne);
            this.collection.bind('change', this.change);
            this.collection.bind('destroy', this.destroy);

            this.eventView = new EventView();
            this.legendView = new LegendView({collection: rooms});
        },
        render: function() {
            this.el.fullCalendar({
                header: {
                    left: 'prev,next today',
                    center: 'title',
                    right: 'month,agendaWeek,agendaDay'
                },
                buttonText: {
                    prev:     ' ◄ ',  // left triangle
                    next:     ' ► ',  // right triangle
                    prevYear: ' << ', // <<
                    nextYear: ' >> ', // >>
                    today:    'aujourd\'hui',
                    month:    'mois',
                    week:     'semaine',
                    day:      'jour'
                },
                titleFormat: {
                    month: 'MMMM yyyy',
                    week: "d MMMM[ yyyy]{ - d [ MMMM] yyyy}",
                    agendaDay: 'dddd d MMM yyyy'
                },
                columnFormat: {
                    month: 'ddd',
                    week: 'ddd d/M',
                    day: 'dddd d/M'
                },
                monthNames: ['Janvier', 'Fevrier', 'Mars', 'Avril', 'Mai', 'Juin', 'Juillet',
                 'Aout', 'Septembre', 'Octobre', 'Novembre', 'Decembre'],
                monthNamesShort: ['Jan', 'Fev', 'Mar', 'Avr', 'Mai', 'Jui', 'Jui',
                 'Aou', 'Sep', 'Oct', 'Nov', 'Dec'],
                dayNames: ['Dimanche', 'Lundi', 'Mardi', 'Mercredi', 'Jeudi', 'Vendredi', 'Samedi'],
                dayNamesShort: ['Dim', 'Lun', 'Mar', 'Mer', 'Jeu', 'Ven', 'Sam'],
                firstDay: 1,
                timeFormat: 'H:mm{ - H:mm}',
                axisFormat: 'H:mm',
                defaultView: 'agendaWeek',
                minTime: 8,
                maxTime: 21,
                allDayDefault: false,
                allDaySlot: false,
                selectable: true,
                selectHelper: true,
                editable: true,
                ignoreTimezone: false,
                select: this.select,
                eventClick: this.eventClick,
                eventDrop: this.eventDropOrResize,
                eventResize: this.eventDropOrResize
            });
        },
        addAll: function() {
            this.el.fullCalendar('addEventSource', this.collection.toCallendar());
        },
        addOne: function(event) {
            this.el.fullCalendar('renderEvent', event.toCallendar());
        },
        select: function(startDate, endDate) {
            this.eventView.collection = this.collection;
            this.eventView.model = new Event({
                start: $.fullCalendar.formatDate(startDate, 'u'),
                end: $.fullCalendar.formatDate(endDate, 'u')
            });
            this.eventView.render();
        },
        eventClick: function(fcEvent) {
            this.eventView.model = this.collection.get(fcEvent.id);
            this.eventView.render();
        },
        change: function(event) {
            // Look up the underlying event in the calendar and update its details from the model
            var fcEvent = this.el.fullCalendar('clientEvents', event.id)[0];
            var organizer = event.get('organizer')
            fcEvent.title = event.get('title') + (organizer ? (' | ' + organizer.email) : '');
            fcEvent.color = event.get('color');
            fcEvent.start = event.get('start');
            fcEvent.end = event.get('end');
            this.el.fullCalendar('updateEvent', fcEvent);
        },
        eventDropOrResize: function(fcEvent) {
            // Lookup the model that has the ID of the event and update its attributes
            var event = this.collection.get(fcEvent.id)
              , values = {start: fcEvent.start, end: fcEvent.end}
              , el = this.el;
            $.when(
                event.clone().save(values))
                .done(function() {event.set(values, {silent: true})})
                .fail(function() {
                    $.gritter.add({title: Wording.error, text: Wording.update.unauthorized});
                })
                .fail(function() {
                    var fcEvent = el.fullCalendar('clientEvents', event.id)[0];
                    fcEvent.start = event.get('start');
                    fcEvent.end = event.get('end');
                    el.fullCalendar('updateEvent', fcEvent);
                });
        },
        destroy: function(event) {
            this.el.fullCalendar('removeEvents', event.id);
        }
    });

    var EventView = Backbone.View.extend({
        el: $('#eventDialog'),
        initialize: function() {
            _.bindAll(this);
        },
        render: function() {
            var buttons = {'Ok': this.save};
            if (!this.model.isNew()) {
                _.extend(buttons, {'Delete': this.destroy});
            }
            _.extend(buttons, {'Cancel': this.close});

            this.el.dialog({
                modal: true,
                title: (this.model.isNew() ? 'New' : 'Edit') + ' Event',
                buttons: buttons,
                open: this.open
            });

            return this;
        },
        open: function() {
            this.$('#title').val(this.model.get('title'));
            var begin = $.fullCalendar.parseDate(this.model.get('start'));
            this.$('#begin').val($.fullCalendar.formatDate(begin, 'HH:mm'));
            this.$('#begin').timepicker();
            var end = $.fullCalendar.parseDate(this.model.get('end'));
            this.$('#end').val($.fullCalendar.formatDate(end, 'HH:mm'));
            this.$('#end').timepicker();
            var room = new RoomsSelectView({el: this.$('#rooms')});
            room.render({selected: this.model.get('location_id')});
        },
        save: function() {
            var start = $.fullCalendar.parseDate(this.model.get('start'))
              , newStart = $.fullCalendar.formatDate(start, "yyyy-MM-dd'T"+this.$('#begin').val()+"':ssTZO")
              , end = $.fullCalendar.parseDate(this.model.get('start'))
              , newEnd = $.fullCalendar.formatDate(end, "yyyy-MM-dd'T"+this.$('#end').val()+"':ssTZO");
            var values = {
                'title': this.$('#title').val(),
                'start': newStart,
                'end': newEnd,
                'location_id': this.$('select[name="rooms"] :selected').val()};

            if (this.model.isNew()) {
                this.model.set(values);
                $.when(
                    this.collection.create(this.model))
                    .then(this.close);
            } else {
                var model = this.model;
                $.when(
                    model.clone().save(values))
                    .done(function() {model.set(values)})
                    .then(this.close)
                    .fail(function(){$.gritter.add({title: Wording.error, text: Wording.update.unauthorized})});
            }
        },
        close: function() {
            this.el.dialog('close');
        },
        destroy: function() {
            $.when(
                this.model.destroy())
                .then(this.close)
                .fail(function(){$.gritter.add({title: Wording.error, text: Wording.delete.unauthorized})});
        }
    });

    var RoomsSelectView = Backbone.View.extend({
        render: function(options) {
            var select = $('<select name="rooms"/>');
            rooms.each(function(room) {
                var selected = options.selected == room.id;
                select.append('<option value="'+room.id +'"'+ (selected  ? ' selected' : '') +'>'+room.get('name')+'</option>');
            });
            this.el.html(select);
        }
    });

    var events = new Events();
    var rooms = new Rooms();
    new EventsView({el: $("#main"), collection: events}).render();
    rooms.fetch().done(_.bind(events.fetch, events)); // need rooms config before displaying.
});

});

var Wording = {
    'error': 'Erreur',
    'update': {
        unauthorized: 'Vous n\'êtes pas autorisé à modifier cet événement.'
    },
    'delete': {
        unauthorized: 'Vous n\'êtes pas autorisé à supprimer cet événement.'
    }
};
<|endoftext|>


<commit_before>var map;
var markers;
var popup;

OpenLayers._getScriptLocation = function () {
   return "/openlayers/";
}

function createMap(divName) {
   map = new OpenLayers.Map(divName, {
      controls: [
         new OpenLayers.Control.ArgParser(),
         new OpenLayers.Control.Attribution(),
         new OpenLayers.Control.LayerSwitcher(),
         new OpenLayers.Control.Navigation(),
         new OpenLayers.Control.PanZoomBar()
      ]
   });

   var mapnik = new OpenLayers.Layer.OSM.Mapnik("Mapnik", {
      displayOutsideMaxExtent: true,
      wrapDateLine: true
   });
   map.addLayer(mapnik);

   var osmarender = new OpenLayers.Layer.OSM.Osmarender("Osmarender", {
      displayOutsideMaxExtent: true,
      wrapDateLine: true
   });
   map.addLayer(osmarender);

   var mapnik_local = new OpenLayers.Layer.OSM("Mapnik (Local)", "http://bericote.uk.cyberscience.com/tiles/", {
      displayOutsideMaxExtent: true,
      wrapDateLine: true
   });
   map.addLayer(mapnik_local);

   var maplint = new OpenLayers.Layer.OSM.Maplint("Maplint", {
      displayOutsideMaxExtent: true,
      wrapDateLine: true
   });
   map.addLayer(maplint);

   var numZoomLevels = Math.max(mapnik.numZoomLevels, osmarender.numZoomLevels);
   markers = new OpenLayers.Layer.Markers("Markers", {
      displayInLayerSwitcher: false,
      numZoomLevels: numZoomLevels,
      maxExtent: new OpenLayers.Bounds(-20037508,-20037508,20037508,20037508),
      maxResolution: 156543,
      units: "m",
      projection: "EPSG:900913"
   });
   map.addLayer(markers);

   return map;
}

function getArrowIcon() {
   var size = new OpenLayers.Size(25, 22);
   var offset = new OpenLayers.Pixel(-30, -27);
   var icon = new OpenLayers.Icon("/images/arrow.png", size, offset);

   return icon;
}

function addMarkerToMap(position, icon, description) {
   var marker = new OpenLayers.Marker(position, icon);

   markers.addMarker(marker);

   if (description) {
      marker.events.register("click", marker, function() { openMapPopup(marker, description) });
   }

   return marker;
}

function openMapPopup(marker, description) {
   closeMapPopup();

   popup = new OpenLayers.Popup.AnchoredBubble("popup", marker.lonlat,
                                               sizeMapPopup(description),
                                               "
" + description + "
",
                                               marker.icon, true);
   popup.setBackgroundColor("#E3FFC5");
   map.addPopup(popup);

   return popup;
}

function closeMapPopup() {
   if (popup) {
      map.removePopup(popup);
      delete popup;
   }
}

function sizeMapPopup(text) {
   var box = document.createElement("div");

   box.innerHTML = text;
   box.style.visibility = "hidden";
   box.style.position = "absolute";
   box.style.top = "0px";
   box.style.left = "0px";
   box.style.width = "200px";
   box.style.height = "auto";

   document.body.appendChild(box);

   var width = box.offsetWidth;
   var height = box.offsetHeight;

   document.body.removeChild(box);

   return new OpenLayers.Size(width + 30, height + 24);
}

function removeMarkerFromMap(marker){
   markers.removeMarker(marker);
}

function getMapLayers() {
   var layers = "";

   for (var i=0; i< this.map.layers.length; i++) {
      var layer = this.map.layers[i];

      if (layer.isBaseLayer) {
         layers += (layer == this.map.baseLayer) ? "B" : "0";
      } else {
         layers += (layer.getVisibility()) ? "T" : "F";
      }
   }

   return layers;
}

function setMapLayers(layers) {
   for (var i=0; i < layers.length; i++) {
      var layer = map.layers[i];
      var c = layers.charAt(i);

      if (c == "B") {
         map.setBaseLayer(layer);
      } else if ( (c == "T") || (c == "F") ) {
         layer.setVisibility(c == "T");
      }
   }
}

function mercatorToLonLat(merc) {
   var lon = (merc.lon / 20037508.34) * 180;
   var lat = (merc.lat / 20037508.34) * 180;

   lat = 180/Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);

   return new OpenLayers.LonLat(lon, lat);
}

function lonLatToMercator(ll) {
   var lon = ll.lon * 20037508.34 / 180;
   var lat = Math.log(Math.tan((90 + ll.lat) * Math.PI / 360)) / (Math.PI / 180);

   lat = lat * 20037508.34 / 180;

   return new OpenLayers.LonLat(lon, lat);
}

function scaleToZoom(scale) {
   return Math.log(360.0/(scale * 512.0)) / Math.log(2.0);
}
<commit_msg>Remove accidental commit of my local mapnik layer.<commit_after>var map;
var markers;
var popup;

OpenLayers._getScriptLocation = function () {
   return "/openlayers/";
}

function createMap(divName) {
   map = new OpenLayers.Map(divName, {
      controls: [
         new OpenLayers.Control.ArgParser(),
         new OpenLayers.Control.Attribution(),
         new OpenLayers.Control.LayerSwitcher(),
         new OpenLayers.Control.Navigation(),
         new OpenLayers.Control.PanZoomBar()
      ]
   });

   var mapnik = new OpenLayers.Layer.OSM.Mapnik("Mapnik", {
      displayOutsideMaxExtent: true,
      wrapDateLine: true
   });
   map.addLayer(mapnik);

   var osmarender = new OpenLayers.Layer.OSM.Osmarender("Osmarender", {
      displayOutsideMaxExtent: true,
      wrapDateLine: true
   });
   map.addLayer(osmarender);

   var mapnik_local = new OpenLayers.Layer.OSM("Mapnik (Local)", "http://bericote.uk.cyberscience.com/tiles/", {
      displayOutsideMaxExtent: true,
      wrapDateLine: true
   });
   map.addLayer(mapnik_local);

   var numZoomLevels = Math.max(mapnik.numZoomLevels, osmarender.numZoomLevels);
   markers = new OpenLayers.Layer.Markers("Markers", {
      displayInLayerSwitcher: false,
      numZoomLevels: numZoomLevels,
      maxExtent: new OpenLayers.Bounds(-20037508,-20037508,20037508,20037508),
      maxResolution: 156543,
      units: "m",
      projection: "EPSG:900913"
   });
   map.addLayer(markers);

   return map;
}

function getArrowIcon() {
   var size = new OpenLayers.Size(25, 22);
   var offset = new OpenLayers.Pixel(-30, -27);
   var icon = new OpenLayers.Icon("/images/arrow.png", size, offset);

   return icon;
}

function addMarkerToMap(position, icon, description) {
   var marker = new OpenLayers.Marker(position, icon);

   markers.addMarker(marker);

   if (description) {
      marker.events.register("click", marker, function() { openMapPopup(marker, description) });
   }

   return marker;
}

function openMapPopup(marker, description) {
   closeMapPopup();

   popup = new OpenLayers.Popup.AnchoredBubble("popup", marker.lonlat,
                                               sizeMapPopup(description),
                                               "
" + description + "
",
                                               marker.icon, true);
   popup.setBackgroundColor("#E3FFC5");
   map.addPopup(popup);

   return popup;
}

function closeMapPopup() {
   if (popup) {
      map.removePopup(popup);
      delete popup;
   }
}

function sizeMapPopup(text) {
   var box = document.createElement("div");

   box.innerHTML = text;
   box.style.visibility = "hidden";
   box.style.position = "absolute";
   box.style.top = "0px";
   box.style.left = "0px";
   box.style.width = "200px";
   box.style.height = "auto";

   document.body.appendChild(box);

   var width = box.offsetWidth;
   var height = box.offsetHeight;

   document.body.removeChild(box);

   return new OpenLayers.Size(width + 30, height + 24);
}

function removeMarkerFromMap(marker){
   markers.removeMarker(marker);
}

function getMapLayers() {
   var layers = "";

   for (var i=0; i< this.map.layers.length; i++) {
      var layer = this.map.layers[i];

      if (layer.isBaseLayer) {
         layers += (layer == this.map.baseLayer) ? "B" : "0";
      } else {
         layers += (layer.getVisibility()) ? "T" : "F";
      }
   }

   return layers;
}

function setMapLayers(layers) {
   for (var i=0; i < layers.length; i++) {
      var layer = map.layers[i];
      var c = layers.charAt(i);

      if (c == "B") {
         map.setBaseLayer(layer);
      } else if ( (c == "T") || (c == "F") ) {
         layer.setVisibility(c == "T");
      }
   }
}

function mercatorToLonLat(merc) {
   var lon = (merc.lon / 20037508.34) * 180;
   var lat = (merc.lat / 20037508.34) * 180;

   lat = 180/Math.PI * (2 * Math.atan(Math.exp(lat * Math.PI / 180)) - Math.PI / 2);

   return new OpenLayers.LonLat(lon, lat);
}

function lonLatToMercator(ll) {
   var lon = ll.lon * 20037508.34 / 180;
   var lat = Math.log(Math.tan((90 + ll.lat) * Math.PI / 360)) / (Math.PI / 180);

   lat = lat * 20037508.34 / 180;

   return new OpenLayers.LonLat(lon, lat);
}

function scaleToZoom(scale) {
   return Math.log(360.0/(scale * 512.0)) / Math.log(2.0);
}
<|endoftext|>


<commit_before>// Mapping functions

var map;
var markers;
var otherMarkers;
var stopsById = new Array();
var proj = new OpenLayers.Projection("EPSG:4326");
var selectControl;
var openPopup;
var openHover;
var segmentStyle =
{
  strokeColor: "#CC0000",
  strokeOpacity: 0.2,
  strokeWidth: 4
};

var segmentSelectedStyle =
{
  strokeColor: "#000000",
  strokeOpacity: 0.5,
  strokeWidth: 4
};

// Handle a click on a marker - go to its URL
function markerClick(evt) {
  if (evt.feature.attributes.url) {
    window.location =  evt.feature.attributes.url;
  }
  OpenLayers.Event.stop(evt);
}

function area_init() {
  // Vector layers must be added onload for IE
  if ($.browser.msie) {
      $(window).load(createAreaMap);
  } else {
      createAreaMap();
  }
}

function createAreaMap(){
  createMap('map');
  bounds = new OpenLayers.Bounds();

  // layers for markers that are the current focus (markers), other markers in the area
  // (otherMarkers) and other markers that are significant (highlightedMarkers)
  // - e.g. locations with problem reports, rather than without when browsing
  // an area.
  markers = new OpenLayers.Layer.Vector( "Markers" );
  otherMarkers = new OpenLayers.Layer.Vector( "Other Markers" );
  highlightedMarkers = new OpenLayers.Layer.Vector( "Highlighted Markers" );
  map.addLayer(otherMarkers);
  map.addLayer(highlightedMarkers);
  map.addLayer(markers);

  // All markers should handle to a click event
  markers.events.register( 'featureselected', markers, markerClick );
  otherMarkers.events.register( 'featureselected', otherMarkers, markerClick );
  highlightedMarkers.events.register( 'featureselected', highlightedMarkers, markerClick );
  var select = new OpenLayers.Control.SelectFeature( [markers, otherMarkers, highlightedMarkers] );
  map.addControl( select );
  select.activate();

  // Load the main markers, and the background markers (in either the highlighted or other layer)
  addMarkerList(areaStops, markers, false, null);
  addMarkerList(otherAreaStops, otherMarkers, true, highlightedMarkers);

  centerCoords =  new OpenLayers.LonLat(lon, lat);
  centerCoords.transform(proj, map.getProjectionObject());
  map.setCenter(centerCoords, zoom);

  if (findOtherLocations == true) {
    map.events.register('moveend', map, updateLocations);
  }

  // if we're constrained to less than the expected map dimensions, try
  // to make sure the markers are all shown
  if (($('#map').width() < mapWidth || $('#map').height() < mapHeight) && (areaStops.length > 0)) {
    map.zoomToExtent(bounds, false);
  }

  // Enforce some zoom constraints
  map.events.register("zoomend", map, function() {
      // World zoom resets map
      if (map.getZoom() == 0) {
        map.setCenter(centerCoords, zoom);
        return;
      }
      if (map.getZoom() < minZoom) map.zoomTo(minZoom);
      if (map.getZoom() > maxZoom) map.zoomTo(maxZoom);
  });

}

function updateLocations(event) {
  var currentZoom = map.getZoom();
  if (currentZoom >= minZoomForOtherMarkers){
    if ($('#map-zoom-notice').length > 0) {
      $('#map-zoom-notice').fadeOut(500);
    }
    // Show other, non-highlighted markers
    otherMarkers.setVisibility(true)
  }else{
    if ($('#map-zoom-notice').length > 0) {
      $('#map-zoom-notice').fadeIn(500);
    }
    // Hide other, non-highlighted markers
    otherMarkers.setVisibility(false)
  }
  // Request and load markers by ajax
  if (currentZoom >= minZoomForOtherMarkers || highlight == 'has_content'){
    center = map.getCenter();
    center = center.transform(map.getProjectionObject(), proj);
    url = "/locations/" + map.getZoom() + "/" + Math.round(center.lat*1000)/1000 + "/" + Math.round(center.lon*1000)/1000 + "/" + linkType;
    params = "?height=" + $('#map').height() + "&width=" + $('#map').width();
    params = params + "&highlight=" + highlight;
    $.ajax({
      url: url + params,
      dataType: 'json',
      success: loadNewMarkers,
      failure: markerFail})
  }

}

function loadNewMarkers(markerData) {
  newMarkers = markerData['locations'];
  // load new background markers
  addMarkerList(newMarkers, otherMarkers, true, highlightedMarkers);
  // update any associated list of issues
  newContent = markerData['issue_content'];
  if ($('#issues-in-area').length > 0){
    $('#issues-in-area').html(newContent);
  }
}

function markerFail(){
// do nothing
}

function addMarkerList(list, layer, others, highlightedLayer) {
  for (var i=0; i < list.length; i++){
    var item = list[i];
    // an element in the list may be an individual marker or an array
    // of markers representing a route
    if (item instanceof Array){
      for (var j=0; j < item.length; j++){
        addMarker(item[j], bounds, layer, others, highlightedLayer);
      }
    }else{
      addMarker(item, bounds, layer, others, highlightedLayer);
    }
  }
}

function addMarker(current, bounds, layer, other, highlightedLayer){
  stopCoords = pointCoords(current.lon, current.lat);

  if (stopsById[current.id] == undefined) {
    bounds.extend(stopCoords);
    var marker = new OpenLayers.Feature.Vector(stopCoords, {
      url: current.url,
      name: current.description,
      id: current.id,
      highlight: current.highlight
    },
    {externalGraphic: current.icon + ".png",
        graphicTitle: current.description,
        graphicWidth: current.width,
        graphicHeight: current.height,
        graphicOpacity: 1,
        graphicXOffset: -( current.width/2),
        graphicYOffset: -current.height,
        cursor: 'pointer'
    });

    stopsById[current.id] = marker;
    if (current.highlight == true && other == true) {
      highlightedLayer.addFeatures( marker );
    }else{
      layer.addFeatures( marker );
    }
  }

}

function pointCoords(lon, lat) {
  return new OpenLayers.Geometry.Point(lon, lat).transform(proj, map.getProjectionObject());
}

function route_init(map_element, routeSegments) {

  createMap(map_element);
  bounds = new OpenLayers.Bounds();

  var vectorLayer = new OpenLayers.Layer.Vector("Vector Layer",{projection: proj});
  map.addLayer(vectorLayer);

  addSelectedHandler(vectorLayer);
  for (var i=0; i < routeSegments.length; i++){
     var coords = routeSegments[i];
     var fromCoords = pointCoords(coords[0].lon, coords[0].lat);
     var toCoords = pointCoords(coords[1].lon, coords[1].lat);
     var points = [];
     points.push(fromCoords);
     points.push(toCoords)
     bounds.extend(fromCoords);
     bounds.extend(toCoords);
     lineString = new OpenLayers.Geometry.LineString(points);
     lineFeature = new OpenLayers.Feature.Vector(lineString, {projection: proj}, segmentStyle);
     lineFeature.segment_id = coords[2];
     vectorLayer.addFeatures([lineFeature]);
   }
   map.zoomToExtent(bounds, false);

}

function addSelectedHandler(vectorLayer) {
  vectorLayer.events.on({
      'featureselected': segmentSelected,
      'featureunselected': segmentUnselected
  });
  selectControl = new OpenLayers.Control.SelectFeature(vectorLayer, {multiple: false,
                                                                     toggleKey: "ctrlKey",
                                                                     multipleKey: "shiftKey"});
  map.addControl(selectControl);
  selectControl.activate();
}

function segmentSelected(event) {
  segment = event.feature;
  segment.style = segmentSelectedStyle;
  this.drawFeature(segment);
  var row = $("#route_segment_" + segment.segment_id);
  row.toggleClass("selected");
  row.find(".check-route-segment").attr('checked', 'true');
}

function segmentUnselected(event) {
  segment = event.feature;
  segment.style = segmentStyle;
  this.drawFeature(segment);
  $("#route_segment_" + segment.segment_id).toggleClass("selected");

}

function jsPath(filename) {
    var r = new RegExp("(^|(.*?\\/))("+filename+"\.js)(\\?|$)"),
        s = document.getElementsByTagName('script'),
        src, m, l = "";
    for(var i=0, len=s.length; i<len; i++) {
        src = s[i].getAttribute('src');
        if(src) {
            var m = src.match(r);
            if(m) {
                l = m[1];
                break;
            }
        }
    }
    return l;
}

function createMap(map_element) {
  // handle both cached and uncached js files in calculating image path
  var javascriptPath = jsPath('OpenLayers');
  if (javascriptPath == ''){
    javascriptPath = jsPath('libraries')
  }
  if (javascriptPath == ''){
    javascriptPath = jsPath('admin_libraries')
  }
  OpenLayers.ImgPath = javascriptPath + 'img/';
  var options = {
        'projection': new OpenLayers.Projection("EPSG:900913"),
        'units': "m",
        'numZoomLevels': 18,
        'maxResolution': 156543.0339,
        'theme': null,
        'maxExtent': new OpenLayers.Bounds(-20037508.34, -20037508.34,
                                          20037508.34, 20037508.34)
      };
  $('.static-map-element').hide();
  map = new OpenLayers.Map(map_element, options);
  var layer = new OpenLayers.Layer.Google("Google Streets",{'sphericalMercator': true,
                                                           'maxExtent': new OpenLayers.Bounds(-20037508.34, -20037508.34,
                                                                                20037508.34, 20037508.34)});
  map.addLayer(layer);
}

<commit_msg>On dragging the issue map, update the URL with the new permalink<commit_after>// Mapping functions

var map;
var markers;
var otherMarkers;
var stopsById = new Array();
var proj = new OpenLayers.Projection("EPSG:4326");
var selectControl;
var openPopup;
var openHover;
var segmentStyle =
{
  strokeColor: "#CC0000",
  strokeOpacity: 0.2,
  strokeWidth: 4
};

var segmentSelectedStyle =
{
  strokeColor: "#000000",
  strokeOpacity: 0.5,
  strokeWidth: 4
};

// Handle a click on a marker - go to its URL
function markerClick(evt) {
  if (evt.feature.attributes.url) {
    window.location =  evt.feature.attributes.url;
  }
  OpenLayers.Event.stop(evt);
}

function area_init() {
  // Vector layers must be added onload for IE
  if ($.browser.msie) {
      $(window).load(createAreaMap);
  } else {
      createAreaMap();
  }
}

function createAreaMap(){
  createMap('map');
  bounds = new OpenLayers.Bounds();

  // layers for markers that are the current focus (markers), other markers in the area
  // (otherMarkers) and other markers that are significant (highlightedMarkers)
  // - e.g. locations with problem reports, rather than without when browsing
  // an area.
  markers = new OpenLayers.Layer.Vector( "Markers" );
  otherMarkers = new OpenLayers.Layer.Vector( "Other Markers" );
  highlightedMarkers = new OpenLayers.Layer.Vector( "Highlighted Markers" );
  map.addLayer(otherMarkers);
  map.addLayer(highlightedMarkers);
  map.addLayer(markers);

  // All markers should handle to a click event
  markers.events.register( 'featureselected', markers, markerClick );
  otherMarkers.events.register( 'featureselected', otherMarkers, markerClick );
  highlightedMarkers.events.register( 'featureselected', highlightedMarkers, markerClick );
  var select = new OpenLayers.Control.SelectFeature( [markers, otherMarkers, highlightedMarkers] );
  map.addControl( select );
  select.activate();

  // Load the main markers, and the background markers (in either the highlighted or other layer)
  addMarkerList(areaStops, markers, false, null);
  addMarkerList(otherAreaStops, otherMarkers, true, highlightedMarkers);

  centerCoords =  new OpenLayers.LonLat(lon, lat);
  centerCoords.transform(proj, map.getProjectionObject());
  map.setCenter(centerCoords, zoom);

  if (findOtherLocations == true) {
    map.events.register('moveend', map, updateLocations);
  }

  // if we're constrained to less than the expected map dimensions, try
  // to make sure the markers are all shown
  if (($('#map').width() < mapWidth || $('#map').height() < mapHeight) && (areaStops.length > 0)) {
    map.zoomToExtent(bounds, false);
  }

  // Enforce some zoom constraints
  map.events.register("zoomend", map, function() {
      // World zoom resets map
      if (map.getZoom() == 0) {
        map.setCenter(centerCoords, zoom);
        return;
      }
      if (map.getZoom() < minZoom) map.zoomTo(minZoom);
      if (map.getZoom() > maxZoom) map.zoomTo(maxZoom);
  });

}

function getQueryStringParametersMap() {
  // Based on: http://stackoverflow.com/a/3855394/223092
  var result = {}, i, value;
  var keyValuePairs = window.location.search.substr(1).split('&');
  if (!keyValuePairs)
    return result;
  for (i = 0; i < keyValuePairs.length; ++i)
  {
    var parts = keyValuePairs[i].split('=');
    // Skip over any malformed parts with multiple = signs:
    if (parts.length != 2)
      continue;
    value = decodeURIComponent(parts[1].replace(/\+/g, " "));
    result[parts[0]] = value;
  }
  return result;
}

function updateLocations(eevent) {
  var currentZoom = map.getZoom(), newLat, newLon, newPath;
  var parameters, key;
  var positionKeys = {'lon': true,
                      'lat': true,
                      'zoom': true};
  if (currentZoom >= minZoomForOtherMarkers){
    if ($('#map-zoom-notice').length > 0) {
      $('#map-zoom-notice').fadeOut(500);
    }
    // Show other, non-highlighted markers
    otherMarkers.setVisibility(true)
  }else{
    if ($('#map-zoom-notice').length > 0) {
      $('#map-zoom-notice').fadeIn(500);
    }
    // Hide other, non-highlighted markers
    otherMarkers.setVisibility(false)
  }
  // Request and load markers by ajax
  if (currentZoom >= minZoomForOtherMarkers || highlight == 'has_content'){
    center = map.getCenter();
    center = center.transform(map.getProjectionObject(), proj);
    newLat = Math.round(center.lat*1000)/1000;
    newLon = Math.round(center.lon*1000)/1000;
    url = "/locations/" + map.getZoom() + "/" + newLat + "/" + newLon + "/" + linkType;
    params = "?height=" + $('#map').height() + "&width=" + $('#map').width();
    params = params + "&highlight=" + highlight;
    $.ajax({
      url: url + params,
      dataType: 'json',
      success: loadNewMarkers,
      failure: markerFail});
    // If we're able to replace the URL with history.replaceState,
    // update it to give a permalink to the new map position:
    if (history.replaceState) {
      newPath = window.location.pathname + '?';
      parameters = getQueryStringParametersMap();
      for (key in parameters) {
        if (parameters.hasOwnProperty(key)) {
          if (!positionKeys[key]) {
            newPath += key + '=' + encodeURIComponent(parameters[key]) + '&';
          }
        }
      }
      newPath += 'lon='+newLon+'&lat='+newLat+'&zoom='+currentZoom;
      history.replaceState(null, "New Map Position", newPath);
    }
  }

}

function loadNewMarkers(markerData) {
  newMarkers = markerData['locations'];
  // load new background markers
  addMarkerList(newMarkers, otherMarkers, true, highlightedMarkers);
  // update any associated list of issues
  newContent = markerData['issue_content'];
  if ($('#issues-in-area').length > 0){
    $('#issues-in-area').html(newContent);
  }
}

function markerFail(){
// do nothing
}

function addMarkerList(list, layer, others, highlightedLayer) {
  for (var i=0; i < list.length; i++){
    var item = list[i];
    // an element in the list may be an individual marker or an array
    // of markers representing a route
    if (item instanceof Array){
      for (var j=0; j < item.length; j++){
        addMarker(item[j], bounds, layer, others, highlightedLayer);
      }
    }else{
      addMarker(item, bounds, layer, others, highlightedLayer);
    }
  }
}

function addMarker(current, bounds, layer, other, highlightedLayer){
  stopCoords = pointCoords(current.lon, current.lat);

  if (stopsById[current.id] == undefined) {
    bounds.extend(stopCoords);
    var marker = new OpenLayers.Feature.Vector(stopCoords, {
      url: current.url,
      name: current.description,
      id: current.id,
      highlight: current.highlight
    },
    {externalGraphic: current.icon + ".png",
        graphicTitle: current.description,
        graphicWidth: current.width,
        graphicHeight: current.height,
        graphicOpacity: 1,
        graphicXOffset: -( current.width/2),
        graphicYOffset: -current.height,
        cursor: 'pointer'
    });

    stopsById[current.id] = marker;
    if (current.highlight == true && other == true) {
      highlightedLayer.addFeatures( marker );
    }else{
      layer.addFeatures( marker );
    }
  }

}

function pointCoords(lon, lat) {
  return new OpenLayers.Geometry.Point(lon, lat).transform(proj, map.getProjectionObject());
}

function route_init(map_element, routeSegments) {

  createMap(map_element);
  bounds = new OpenLayers.Bounds();

  var vectorLayer = new OpenLayers.Layer.Vector("Vector Layer",{projection: proj});
  map.addLayer(vectorLayer);

  addSelectedHandler(vectorLayer);
  for (var i=0; i < routeSegments.length; i++){
     var coords = routeSegments[i];
     var fromCoords = pointCoords(coords[0].lon, coords[0].lat);
     var toCoords = pointCoords(coords[1].lon, coords[1].lat);
     var points = [];
     points.push(fromCoords);
     points.push(toCoords)
     bounds.extend(fromCoords);
     bounds.extend(toCoords);
     lineString = new OpenLayers.Geometry.LineString(points);
     lineFeature = new OpenLayers.Feature.Vector(lineString, {projection: proj}, segmentStyle);
     lineFeature.segment_id = coords[2];
     vectorLayer.addFeatures([lineFeature]);
   }
   map.zoomToExtent(bounds, false);

}

function addSelectedHandler(vectorLayer) {
  vectorLayer.events.on({
      'featureselected': segmentSelected,
      'featureunselected': segmentUnselected
  });
  selectControl = new OpenLayers.Control.SelectFeature(vectorLayer, {multiple: false,
                                                                     toggleKey: "ctrlKey",
                                                                     multipleKey: "shiftKey"});
  map.addControl(selectControl);
  selectControl.activate();
}

function segmentSelected(event) {
  segment = event.feature;
  segment.style = segmentSelectedStyle;
  this.drawFeature(segment);
  var row = $("#route_segment_" + segment.segment_id);
  row.toggleClass("selected");
  row.find(".check-route-segment").attr('checked', 'true');
}

function segmentUnselected(event) {
  segment = event.feature;
  segment.style = segmentStyle;
  this.drawFeature(segment);
  $("#route_segment_" + segment.segment_id).toggleClass("selected");

}

function jsPath(filename) {
    var r = new RegExp("(^|(.*?\\/))("+filename+"\.js)(\\?|$)"),
        s = document.getElementsByTagName('script'),
        src, m, l = "";
    for(var i=0, len=s.length; i<len; i++) {
        src = s[i].getAttribute('src');
        if(src) {
            var m = src.match(r);
            if(m) {
                l = m[1];
                break;
            }
        }
    }
    return l;
}

function createMap(map_element) {
  // handle both cached and uncached js files in calculating image path
  var javascriptPath = jsPath('OpenLayers');
  if (javascriptPath == ''){
    javascriptPath = jsPath('libraries')
  }
  if (javascriptPath == ''){
    javascriptPath = jsPath('admin_libraries')
  }
  OpenLayers.ImgPath = javascriptPath + 'img/';
  var options = {
        'projection': new OpenLayers.Projection("EPSG:900913"),
        'units': "m",
        'numZoomLevels': 18,
        'maxResolution': 156543.0339,
        'theme': null,
        'maxExtent': new OpenLayers.Bounds(-20037508.34, -20037508.34,
                                          20037508.34, 20037508.34)
      };
  $('.static-map-element').hide();
  map = new OpenLayers.Map(map_element, options);
  var layer = new OpenLayers.Layer.Google("Google Streets",{'sphericalMercator': true,
                                                           'maxExtent': new OpenLayers.Bounds(-20037508.34, -20037508.34,
                                                                                20037508.34, 20037508.34)});
  map.addLayer(layer);
}

<|endoftext|>