<commit_before>"use strict";

var RoadIntersection = function(mapX, mapZ) {
  var edges = [];

  var indexOfEdge = function(mapX, mapZ) {
    var i;

    for (i = 0; i < edges.length; i++) {
      if (edges[i][0] === mapX && edges[i][1] === mapZ) {
        return i;
      }
    }

    return -1;
  };

  var roadIntersection = {};

  roadIntersection.addEdge = function(mapX, mapZ) {
    if (indexOfEdge(mapX, mapZ) === -1) {
      edges.push([mapX, mapZ]);
    }
  };

  roadIntersection.removeEdge = function(mapX, mapZ) {
    var index = indexOfEdge(mapX, mapZ);

    if (index !== -1) {
      edges.splice(index, 1);
    }
  };

  roadIntersection.hasPathTo = function(mapX, mapZ) {
    return indexOfEdge(mapX, mapZ) > -1;
  };

  roadIntersection.isEmpty = function() {
    return edges.length === 0;
  };

  return roadIntersection;
};

var RoadNetwork = function(minColumn, maxColumn, minRow, maxRow) {
  var network = [];

  var init = function() {
    var x, z, roadIntersection;

    for (x = minColumn; x <= maxColumn; x++) {
      for (z = minRow; z <= maxRow; z++) {
        roadIntersection = new RoadIntersection(x, z);

        if (x > minColumn) {
          roadIntersection.addEdge(x - 1, z);
        }
        if (x < maxColumn) {
          roadIntersection.addEdge(x + 1, z);
        }
        if (z > minRow) {
          roadIntersection.addEdge(x, z - 1);
        }
        if (z < maxRow) {
          roadIntersection.addEdge(x, z + 1);
        }

        network[[x, z]] = roadIntersection;
      }
    }
  };

  init();

  var removeEdge = function(mapX1, mapZ1, mapX2, mapZ2) {
    var roadIntersection;

    roadIntersection = network[[mapX1, mapZ1]]
    if (roadIntersection) {
      roadIntersection.removeEdge(mapX2, mapZ2);

      if (roadIntersection.isEmpty()) {
        network[[mapX1, mapZ1]] = null;
      }
    }

    roadIntersection = network[[mapX2, mapZ2]];
    if (roadIntersection) {
      roadIntersection.removeEdge(mapX1, mapZ1);

      if (roadIntersection.isEmpty()) {
        network[[mapX2, mapZ2]] = null;
      }
    }
  };

  var roadNetwork = {};

  roadNetwork.hasIntersection = function(mapX, mapZ) {
    return network[[mapX, mapZ]] != null;
  };

  roadNetwork.intersectionAt = function(mapX, mapZ) {
    return network[[mapX, mapZ]];
  };

  roadNetwork.hasEdgeBetween = function(mapX1, mapZ1, mapX2, mapZ2) {
    var roadIntersection1, roadIntersection2;

    roadIntersection1 = network[[mapX1, mapZ1]];
    roadIntersection2 = network[[mapX2, mapZ2]];

    return roadIntersection1.hasPathTo(mapX2, mapZ2) && roadIntersection2.hasPathTo(mapX1, mapZ1);
  };

  roadNetwork.removeEdge = removeEdge;

  roadNetwork.pruneSteepEdges = function(terrain) {
    var mapX, mapZ;
    var roadIntersection;
    var heightAtPoint1, heightAtPoint2, angle;
    var MAX_STEEPNESS = 0.3587;

    for (mapX = minColumn; mapX <= maxColumn; mapX++) {
      for (mapZ = minRow; mapZ <= maxRow; mapZ++) {
        heightAtPoint1 = terrain.heightAtCoordinates(mapX, mapZ);
        heightAtPoint2 = terrain.heightAtCoordinates(mapX, mapZ - 1);
        angle = Math.atan2((heightAtPoint1 - heightAtPoint2), CityConfig.BLOCK_DEPTH);
        if (Math.abs(angle) > MAX_STEEPNESS) {
          removeEdge(mapX, mapZ, mapX, mapZ - 1);
        }

        heightAtPoint1 = terrain.heightAtCoordinates(mapX, mapZ);
        heightAtPoint2 = terrain.heightAtCoordinates(mapX, mapZ + 1);
        angle = Math.atan2((heightAtPoint1 - heightAtPoint2), CityConfig.BLOCK_DEPTH);
        if (Math.abs(angle) > MAX_STEEPNESS) {
          removeEdge(mapX, mapZ, mapX, mapZ + 1);
        }

        heightAtPoint1 = terrain.heightAtCoordinates(mapX, mapZ);
        heightAtPoint2 = terrain.heightAtCoordinates(mapX - 1, mapZ);
        angle = Math.atan2((heightAtPoint1 - heightAtPoint2), CityConfig.BLOCK_DEPTH);
        if (Math.abs(angle) > MAX_STEEPNESS) {
          removeEdge(mapX, mapZ, mapX - 1, mapZ);
        }

        heightAtPoint1 = terrain.heightAtCoordinates(mapX, mapZ);
        heightAtPoint2 = terrain.heightAtCoordinates(mapX + 1, mapZ);
        angle = Math.atan2((heightAtPoint1 - heightAtPoint2), CityConfig.BLOCK_DEPTH);
        if (Math.abs(angle) > MAX_STEEPNESS) {
          removeEdge(mapX, mapZ, mapX + 1, mapZ);
        }
      }
    }
  };

  roadNetwork.pruneHorizontalEdgesWithNoBuildings = function(buildings) {
    var mapX, mapZ;
    var edgeHasBuildings;
    var blockAbove, blockBelow;
    var blockAboveHasBuildings, blockBelowHasBuildings;
    var roadIntersection;

    for (mapZ = minRow; mapZ <= maxRow; mapZ++) {
      mapX = minColumn;
      edgeHasBuildings = false;

      while (mapX < maxColumn && !edgeHasBuildings) {
        blockAboveHasBuildings = false;
        if (mapZ > minRow) {
          blockAbove = buildings.blockAtCoordinates(mapX, mapZ - 1);
          blockAbove.forEach(function(building) {
            if (building.bottom === 1.0) {
              blockAboveHasBuildings = true;
            }
          });
        }

        blockBelowHasBuildings = false;
        if (mapZ < maxRow) {
          blockBelow = buildings.blockAtCoordinates(mapX, mapZ);
          blockBelow.forEach(function(building) {
            if (building.top === 0.0) {
              blockBelowHasBuildings = true;
            }
          });
        }

        edgeHasBuildings = blockAboveHasBuildings || blockBelowHasBuildings;

        if (!edgeHasBuildings) {
          removeEdge(mapX, mapZ, mapX + 1, mapZ);
        }

        mapX += 1;
      }

      mapX = maxColumn;
      edgeHasBuildings = false;

      while (mapX > minColumn && !edgeHasBuildings) {
        blockAboveHasBuildings = false;
        if (mapZ > minRow) {
          blockAbove = buildings.blockAtCoordinates(mapX - 1, mapZ - 1);
          blockAbove.forEach(function(building) {
            if (building.bottom === 1.0) {
              blockAboveHasBuildings = true;
            }
          });
        }

        blockBelowHasBuildings = false;
        if (mapZ < maxRow) {
          blockBelow = buildings.blockAtCoordinates(mapX - 1, mapZ);
          blockBelow.forEach(function(building) {
            if (building.top === 0.0) {
              blockBelowHasBuildings = true;
            }
          });
        }

        edgeHasBuildings = blockAboveHasBuildings || blockBelowHasBuildings;

        if (!edgeHasBuildings) {
          removeEdge(mapX, mapZ, mapX - 1, mapZ);
        }

        mapX -= 1;
      }
    }
  };

  roadNetwork.pruneVerticalEdgesWithNoBuildings = function(buildings) {
    var mapX, mapZ;
    var edgeHasBuildings;
    var blockLeft, blockRight;
    var blockLeftHasBuildings, blockRightHasBuildings;
    var roadIntersection;

    for (mapX = minColumn; mapX <= maxColumn; mapX++) {
      mapZ = minRow;
      edgeHasBuildings = false;

      while (mapZ < maxRow && !edgeHasBuildings) {
        blockLeftHasBuildings = false;
        if (mapX > minColumn) {
          blockLeft = buildings.blockAtCoordinates(mapX - 1, mapZ);
          blockLeft.forEach(function(building) {
            if (building.right === 1.0) {
              blockLeftHasBuildings = true;
            }
          });
        }

        blockRightHasBuildings = false;
        if (mapX < maxColumn) {
          blockRight = buildings.blockAtCoordinates(mapX, mapZ);
          blockRight.forEach(function(building) {
            if (building.left === 0.0) {
              blockRightHasBuildings = true;
            }
          });
        }

        edgeHasBuildings = blockLeftHasBuildings || blockRightHasBuildings;

        if (!edgeHasBuildings) {
          removeEdge(mapX, mapZ, mapX, mapZ + 1);
        }

        mapZ += 1;
      }

      mapZ = maxRow - 1;
      edgeHasBuildings = false;

      while (mapZ > minRow && !edgeHasBuildings) {
        blockLeftHasBuildings = false;
        if (mapX > minColumn) {
          blockLeft = buildings.blockAtCoordinates(mapX - 1, mapZ);
          blockLeft.forEach(function(building) {
            if (building.right === 1.0) {
              blockLeftHasBuildings = true;
            }
          });
        }

        blockRightHasBuildings = false;
        if (mapX < maxColumn) {
          blockRight = buildings.blockAtCoordinates(mapX, mapZ);
          blockRight.forEach(function(building) {
            if (building.left === 0.0) {
              blockRightHasBuildings = true;
            }
          });
        }

        edgeHasBuildings = blockLeftHasBuildings || blockRightHasBuildings;

        if (!edgeHasBuildings) {
          removeEdge(mapX, mapZ, mapX, mapZ + 1);
        }

        mapZ -= 1;
      }
    }
  };

  return roadNetwork;
};
<commit_msg>Increasing max allowed steepness for roads<commit_after>"use strict";

var RoadIntersection = function(mapX, mapZ) {
  var edges = [];

  var indexOfEdge = function(mapX, mapZ) {
    var i;

    for (i = 0; i < edges.length; i++) {
      if (edges[i][0] === mapX && edges[i][1] === mapZ) {
        return i;
      }
    }

    return -1;
  };

  var roadIntersection = {};

  roadIntersection.addEdge = function(mapX, mapZ) {
    if (indexOfEdge(mapX, mapZ) === -1) {
      edges.push([mapX, mapZ]);
    }
  };

  roadIntersection.removeEdge = function(mapX, mapZ) {
    var index = indexOfEdge(mapX, mapZ);

    if (index !== -1) {
      edges.splice(index, 1);
    }
  };

  roadIntersection.hasPathTo = function(mapX, mapZ) {
    return indexOfEdge(mapX, mapZ) > -1;
  };

  roadIntersection.isEmpty = function() {
    return edges.length === 0;
  };

  return roadIntersection;
};

var RoadNetwork = function(minColumn, maxColumn, minRow, maxRow) {
  var network = [];

  var init = function() {
    var x, z, roadIntersection;

    for (x = minColumn; x <= maxColumn; x++) {
      for (z = minRow; z <= maxRow; z++) {
        roadIntersection = new RoadIntersection(x, z);

        if (x > minColumn) {
          roadIntersection.addEdge(x - 1, z);
        }
        if (x < maxColumn) {
          roadIntersection.addEdge(x + 1, z);
        }
        if (z > minRow) {
          roadIntersection.addEdge(x, z - 1);
        }
        if (z < maxRow) {
          roadIntersection.addEdge(x, z + 1);
        }

        network[[x, z]] = roadIntersection;
      }
    }
  };

  init();

  var removeEdge = function(mapX1, mapZ1, mapX2, mapZ2) {
    var roadIntersection;

    roadIntersection = network[[mapX1, mapZ1]]
    if (roadIntersection) {
      roadIntersection.removeEdge(mapX2, mapZ2);

      if (roadIntersection.isEmpty()) {
        network[[mapX1, mapZ1]] = null;
      }
    }

    roadIntersection = network[[mapX2, mapZ2]];
    if (roadIntersection) {
      roadIntersection.removeEdge(mapX1, mapZ1);

      if (roadIntersection.isEmpty()) {
        network[[mapX2, mapZ2]] = null;
      }
    }
  };

  var roadNetwork = {};

  roadNetwork.hasIntersection = function(mapX, mapZ) {
    return network[[mapX, mapZ]] != null;
  };

  roadNetwork.intersectionAt = function(mapX, mapZ) {
    return network[[mapX, mapZ]];
  };

  roadNetwork.hasEdgeBetween = function(mapX1, mapZ1, mapX2, mapZ2) {
    var roadIntersection1, roadIntersection2;

    roadIntersection1 = network[[mapX1, mapZ1]];
    roadIntersection2 = network[[mapX2, mapZ2]];

    return roadIntersection1.hasPathTo(mapX2, mapZ2) && roadIntersection2.hasPathTo(mapX1, mapZ1);
  };

  roadNetwork.removeEdge = removeEdge;

  roadNetwork.pruneSteepEdges = function(terrain) {
    var mapX, mapZ;
    var roadIntersection;
    var heightAtPoint1, heightAtPoint2, angle;
    var MAX_STEEPNESS = Math.PI / 4;

    for (mapX = minColumn; mapX <= maxColumn; mapX++) {
      for (mapZ = minRow; mapZ <= maxRow; mapZ++) {
        heightAtPoint1 = terrain.heightAtCoordinates(mapX, mapZ);
        heightAtPoint2 = terrain.heightAtCoordinates(mapX, mapZ - 1);
        angle = Math.atan2((heightAtPoint1 - heightAtPoint2), CityConfig.BLOCK_DEPTH);
        if (Math.abs(angle) > MAX_STEEPNESS) {
          removeEdge(mapX, mapZ, mapX, mapZ - 1);
        }

        heightAtPoint1 = terrain.heightAtCoordinates(mapX, mapZ);
        heightAtPoint2 = terrain.heightAtCoordinates(mapX, mapZ + 1);
        angle = Math.atan2((heightAtPoint1 - heightAtPoint2), CityConfig.BLOCK_DEPTH);
        if (Math.abs(angle) > MAX_STEEPNESS) {
          removeEdge(mapX, mapZ, mapX, mapZ + 1);
        }

        heightAtPoint1 = terrain.heightAtCoordinates(mapX, mapZ);
        heightAtPoint2 = terrain.heightAtCoordinates(mapX - 1, mapZ);
        angle = Math.atan2((heightAtPoint1 - heightAtPoint2), CityConfig.BLOCK_DEPTH);
        if (Math.abs(angle) > MAX_STEEPNESS) {
          removeEdge(mapX, mapZ, mapX - 1, mapZ);
        }

        heightAtPoint1 = terrain.heightAtCoordinates(mapX, mapZ);
        heightAtPoint2 = terrain.heightAtCoordinates(mapX + 1, mapZ);
        angle = Math.atan2((heightAtPoint1 - heightAtPoint2), CityConfig.BLOCK_DEPTH);
        if (Math.abs(angle) > MAX_STEEPNESS) {
          removeEdge(mapX, mapZ, mapX + 1, mapZ);
        }
      }
    }
  };

  roadNetwork.pruneHorizontalEdgesWithNoBuildings = function(buildings) {
    var mapX, mapZ;
    var edgeHasBuildings;
    var blockAbove, blockBelow;
    var blockAboveHasBuildings, blockBelowHasBuildings;
    var roadIntersection;

    for (mapZ = minRow; mapZ <= maxRow; mapZ++) {
      mapX = minColumn;
      edgeHasBuildings = false;

      while (mapX < maxColumn && !edgeHasBuildings) {
        blockAboveHasBuildings = false;
        if (mapZ > minRow) {
          blockAbove = buildings.blockAtCoordinates(mapX, mapZ - 1);
          blockAbove.forEach(function(building) {
            if (building.bottom === 1.0) {
              blockAboveHasBuildings = true;
            }
          });
        }

        blockBelowHasBuildings = false;
        if (mapZ < maxRow) {
          blockBelow = buildings.blockAtCoordinates(mapX, mapZ);
          blockBelow.forEach(function(building) {
            if (building.top === 0.0) {
              blockBelowHasBuildings = true;
            }
          });
        }

        edgeHasBuildings = blockAboveHasBuildings || blockBelowHasBuildings;

        if (!edgeHasBuildings) {
          removeEdge(mapX, mapZ, mapX + 1, mapZ);
        }

        mapX += 1;
      }

      mapX = maxColumn;
      edgeHasBuildings = false;

      while (mapX > minColumn && !edgeHasBuildings) {
        blockAboveHasBuildings = false;
        if (mapZ > minRow) {
          blockAbove = buildings.blockAtCoordinates(mapX - 1, mapZ - 1);
          blockAbove.forEach(function(building) {
            if (building.bottom === 1.0) {
              blockAboveHasBuildings = true;
            }
          });
        }

        blockBelowHasBuildings = false;
        if (mapZ < maxRow) {
          blockBelow = buildings.blockAtCoordinates(mapX - 1, mapZ);
          blockBelow.forEach(function(building) {
            if (building.top === 0.0) {
              blockBelowHasBuildings = true;
            }
          });
        }

        edgeHasBuildings = blockAboveHasBuildings || blockBelowHasBuildings;

        if (!edgeHasBuildings) {
          removeEdge(mapX, mapZ, mapX - 1, mapZ);
        }

        mapX -= 1;
      }
    }
  };

  roadNetwork.pruneVerticalEdgesWithNoBuildings = function(buildings) {
    var mapX, mapZ;
    var edgeHasBuildings;
    var blockLeft, blockRight;
    var blockLeftHasBuildings, blockRightHasBuildings;
    var roadIntersection;

    for (mapX = minColumn; mapX <= maxColumn; mapX++) {
      mapZ = minRow;
      edgeHasBuildings = false;

      while (mapZ < maxRow && !edgeHasBuildings) {
        blockLeftHasBuildings = false;
        if (mapX > minColumn) {
          blockLeft = buildings.blockAtCoordinates(mapX - 1, mapZ);
          blockLeft.forEach(function(building) {
            if (building.right === 1.0) {
              blockLeftHasBuildings = true;
            }
          });
        }

        blockRightHasBuildings = false;
        if (mapX < maxColumn) {
          blockRight = buildings.blockAtCoordinates(mapX, mapZ);
          blockRight.forEach(function(building) {
            if (building.left === 0.0) {
              blockRightHasBuildings = true;
            }
          });
        }

        edgeHasBuildings = blockLeftHasBuildings || blockRightHasBuildings;

        if (!edgeHasBuildings) {
          removeEdge(mapX, mapZ, mapX, mapZ + 1);
        }

        mapZ += 1;
      }

      mapZ = maxRow - 1;
      edgeHasBuildings = false;

      while (mapZ > minRow && !edgeHasBuildings) {
        blockLeftHasBuildings = false;
        if (mapX > minColumn) {
          blockLeft = buildings.blockAtCoordinates(mapX - 1, mapZ);
          blockLeft.forEach(function(building) {
            if (building.right === 1.0) {
              blockLeftHasBuildings = true;
            }
          });
        }

        blockRightHasBuildings = false;
        if (mapX < maxColumn) {
          blockRight = buildings.blockAtCoordinates(mapX, mapZ);
          blockRight.forEach(function(building) {
            if (building.left === 0.0) {
              blockRightHasBuildings = true;
            }
          });
        }

        edgeHasBuildings = blockLeftHasBuildings || blockRightHasBuildings;

        if (!edgeHasBuildings) {
          removeEdge(mapX, mapZ, mapX, mapZ + 1);
        }

        mapZ -= 1;
      }
    }
  };

  return roadNetwork;
};
<|endoftext|>


<commit_before>(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory();
    } else {
        root.t = factory();
    }
}(this, function () {

    "use strict";

    // --------------------------------------------------------------
    // Utils
    // --------------------------------------------------------------

    var failed = false;

    function fail(message) {
        if (!failed) {
            debugger;
        }
        failed = true;
        throw new Error(message);
    }

    function assert(guard, message) {
        if (guard !== true) {
            fail(message || 'assert failed');
        }
    }

    function freeze(obj_or_arr, unless) {
        if (unless !== true && Object.freeze) {
            Object.freeze(obj_or_arr);
        }
        return obj_or_arr;
    }

    function mixin(x, y, overwrite) {
        for (var k in y) {
            if (y.hasOwnProperty(k)) {
                if (!overwrite) {
                    assert(!x.hasOwnProperty(k), 'mixin(): cannot overwrite property ' + k);
                }
                x[k] = y[k];
            }
        }
        return x;
    }

    // --------------------------------------------------------------
    // array manipulation
    // --------------------------------------------------------------

    function is_valid_index(index, from, to) {
        return Num.is(index) && index >= from && index <= to;
    }

    function append(arr, element) {
        assert(Arr.is(arr), 'append(): bad array');
        var ret = arr.slice();
        ret.push(element);
        return ret;
    }

    function prepend(arr, element) {
        assert(Arr.is(arr), 'prepend(): bad array');
        var ret = arr.slice();
        ret.unshift(element);
        return ret;
    }

    function update(arr, index, element) {
        assert(Arr.is(arr), 'update(): bad array');
        assert(is_valid_index(index, 0, arr.length - 1), 'update(): bad index');
        var ret = arr.slice();
        ret[index] = element;
        return ret;
    }

    function remove(arr, index) {
        assert(Arr.is(arr), 'remove(): bad array');
        assert(is_valid_index(index, 0, arr.length - 1), 'remove(): bad index');
        var ret = arr.slice();
        ret.splice(index, 1);
        return ret;
    }

    function move(arr, from, to) {
        assert(Arr.is(arr), 'move(): bad array');
        assert(is_valid_index(from, 0, arr.length - 1), 'move(): bad from');
        assert(is_valid_index(to, 0, arr.length - 1), 'move(): bad to');
        var ret = arr.slice();
        if (from === to) {
            return ret;
        }
        var element = ret.splice(from, 1)[0];
        ret.splice(to, 0, element);
        return ret;
    }

    // --------------------------------------------------------------
    // primitive
    // --------------------------------------------------------------

    function primitive(name, is) {

        function Primitive(values) {
            assert(Primitive.is(values), 'bad ' + name);
            return values;
        }

        Primitive.meta = {
            kind: 'primitive',
            name: name
        };

        Primitive.is = is;

        return Primitive;
    }

    var Nil = primitive('Nil', function (x) {
        return x === null || x === undefined;
    });

    var Str = primitive('Str', function (x) {
        return typeof x === 'string';
    });

    var Num = primitive('Num', function (x) {
        return typeof x === 'number' && isFinite(x) && !isNaN(x);
    });

    var Bool = primitive('Bool', function (x) {
        return x === true || x === false;
    });

    var Arr = primitive('Arr', function (x) {
        return x instanceof Array;
    });

    var Obj = primitive('Obj', function (x) {
        return !Nil.is(x) && x.constructor === Object && !Arr.is(x);
    });

    var Func = primitive('Func', function (x) {
        return typeof x === 'function';
    });

    var Err = primitive('Err', function (x) {
        return x instanceof Error;
    });

    // --------------------------------------------------------------
    // struct
    // --------------------------------------------------------------

    function struct(props, name) {

        function Struct(values, mut) {

            assert(Obj.is(values), 'bad ' + (name || 'struct'));
            assert(maybe(Bool).is(mut), 'bad mut');

            for (var prop in props) {
                if (props.hasOwnProperty(prop)) {
                    var Type = props[prop],
                        value = values[prop];
                    this[prop] = Type.is(value) ? value : new Type(value, mut);
                }
            }

            freeze(this, mut);
        }

        Struct.meta = {
            kind: 'struct',
            props: props,
            name: name
        };

        Struct.is = function (x) {
            return x instanceof Struct;
        };

        Struct.update = function (instance, updates, mut) {

            assert(Struct.is(instance));
            assert(Obj.is(updates));

            var v = {};
            for (var prop in props) {
                if (props.hasOwnProperty(prop)) {
                    v[prop] = updates.hasOwnProperty(prop) ? updates[prop]
                            : instance[prop];
                }
            }
            return new Struct(v, mut);
        };

        return Struct;
    }

    // --------------------------------------------------------------
    // union
    // --------------------------------------------------------------

    function union(types, name) {

        function Union(values, mut) {
            var Type = Union.dispatch(values);
            return new Type(values, mut);
        }

        Union.meta = {
            kind: 'union',
            types: types,
            name: name
        };

        Union.is = function (x) {
            return types.some(function (type) {
                return type.is(x);
            });
        };

        return Union;
    }

    // --------------------------------------------------------------
    // maybe
    // --------------------------------------------------------------

    function maybe(Type, name) {

        function Maybe(values, mut) {
            return Nil.is(values) ? null : new Type(values, mut);
        }

        Maybe.meta = {
            kind: 'maybe',
            type: Type,
            name: name
        };

        Maybe.is = function (x) {
            return Nil.is(x) || Type.is(x);
        };

        return Maybe;
    }

    // --------------------------------------------------------------
    // enums
    // --------------------------------------------------------------

    function enums(map, name) {

        function Enums(x) {
            assert(Enums.is(x), 'bad ' + (name || 'enum'));
            return x;
        }

        Enums.meta = {
            kind: 'enums',
            map: map,
            name: name
        };

        Enums.is = function (x) {
            return Str.is(x) && map.hasOwnProperty(x);
        };

        return Enums;
    }

    // --------------------------------------------------------------
    // tuple
    // --------------------------------------------------------------

    function tuple(types, name) {

        var len = types.length;

        function Tuple(values, mut) {

            assert(Arr.is(values), 'bad ' + (name || 'tuple'));

            var arr = [];
            for (var i = 0 ; i < len ; i++) {
                var Type = types[i];
                var value = values[i];
                arr.push(Type.is(value) ? value : new Type(value, mut));
            }

            return freeze(arr, mut);
        }

        Tuple.meta = {
            kind: 'tuple',
            types: types,
            name: name
        };

        Tuple.is = function (x) {
            return Arr.is(x) && x.length === len &&
                types.every(function (type, i) {
                    return type.is(x[i]);
                });
        };

        Tuple.update = function (instance, index, element, mut) {
            var Type = types[index],
                value = Type.is(element) ? element : new Type(element, mut),
                arr = update(instance, index, value);
            return freeze(arr, mut);
        };

        return Tuple;
    }

    // --------------------------------------------------------------
    // subtype
    // --------------------------------------------------------------

    function subtype(Type, predicate, name) {

        function Subtype(values, mut) {
            var x = new Type(values, mut);
            assert(predicate(x), 'bad ' + (name || 'subtype'));
            return x;
        }

        Subtype.meta = {
            kind: 'subtype',
            type: Type,
            predicate: predicate,
            name: name
        };

        Subtype.is = function (x) {
            return Type.is(x) && predicate(x);
        };

        return Subtype;
    }

    // --------------------------------------------------------------
    // list
    // --------------------------------------------------------------

    function list(Type, name) {

        function List(values, mut) {

            assert(Arr.is(values), 'bad ' + (name || 'list'));

            var arr = [];
            for (var i = 0, len = values.length ; i < len ; i++ ) {
                var value = values[i];
                arr.push(Type.is(value) ? value : new Type(value, mut));
            }

            return freeze(arr, mut);
        }

        List.meta = {
            kind: 'list',
            type: Type,
            name: name
        };

        List.is = function (x) {
            return Arr.is(x) && x.every(Type.is);
        };

        List.append = function (instance, element, mut) {
            var value = Type.is(element) ? element : new Type(element, mut),
                arr = append(instance, value);
            return freeze(arr, mut);
        };

        List.prepend = function (instance, element, mut) {
            var value = Type.is(element) ? element : new Type(element, mut),
                arr = prepend(instance, value);
            return freeze(arr, mut);
        };

        List.update = function (instance, index, element, mut) {
            var value = Type.is(element) ? element : new Type(element, mut),
                arr = update(instance, index, value);
            return freeze(arr, mut);
        };

        List.remove = function (instance, index, mut) {
            var arr = remove(instance, index);
            return freeze(arr, mut);
        };

        List.move = function (instance, from, to, mut) {
            var arr = move(instance, from, to);
            return freeze(arr, mut);
        };

        return List;
    }

    // --------------------------------------------------------------
    // func (experimental)
    // --------------------------------------------------------------

    var func = function (Arguments, f, Return, name) {

        function func() {
            var args = Array.prototype.slice.call(arguments);
            if (args.length < f.length) args.length = f.length; // handle optional arguments

            args = Arguments.is(args) ? args : new Arguments(args);

            var r = f.apply(this, new Arguments(args));

            if (Return) {
                r = Return.is(r) ? r : new Return(r);
            }

            return r;
        }

        func.is = function (x) { return x === func; };

        func.meta = {
            kind: 'func',
            Arguments: Arguments,
            f: f,
            Return: Return,
            name: name
        };

        return func;
    };

    return {
        fail: fail,
        assert: assert,
        freeze: freeze,
        mixin: mixin,
        append: append,
        prepend: prepend,
        update: update,
        remove: remove,
        move: move,

        Nil: Nil,
        Str: Str,
        Num: Num,
        Bool: Bool,
        Arr: Arr,
        Obj: Obj,
        Func: Func,
        Err: Err,

        primitive: primitive,
        struct: struct,
        enums: enums,
        union: union,
        maybe: maybe,
        tuple: tuple,
        subtype: subtype,
        list: list,
        func: func
    };
}));
<commit_msg>license<commit_after>//     tcomb.js 0.0.4
//     https://github.com/gcanti/tcomb
//     (c) 2014 Giulio Canti
//     tcomb.js may be freely distributed under the MIT license.

(function (root, factory) {
    if (typeof define === 'function' && define.amd) {
        define([], factory);
    } else if (typeof exports === 'object') {
        module.exports = factory();
    } else {
        root.t = factory();
    }
}(this, function () {

    "use strict";

    // --------------------------------------------------------------
    // Utils
    // --------------------------------------------------------------

    var failed = false;

    function fail(message) {
        if (!failed) {
            debugger;
        }
        failed = true;
        throw new Error(message);
    }

    function assert(guard, message) {
        if (guard !== true) {
            fail(message || 'assert failed');
        }
    }

    function freeze(obj_or_arr, unless) {
        if (unless !== true && Object.freeze) {
            Object.freeze(obj_or_arr);
        }
        return obj_or_arr;
    }

    function mixin(x, y, overwrite) {
        for (var k in y) {
            if (y.hasOwnProperty(k)) {
                if (!overwrite) {
                    assert(!x.hasOwnProperty(k), 'mixin(): cannot overwrite property ' + k);
                }
                x[k] = y[k];
            }
        }
        return x;
    }

    // --------------------------------------------------------------
    // array manipulation
    // --------------------------------------------------------------

    function is_valid_index(index, from, to) {
        return Num.is(index) && index >= from && index <= to;
    }

    function append(arr, element) {
        assert(Arr.is(arr), 'append(): bad array');
        var ret = arr.slice();
        ret.push(element);
        return ret;
    }

    function prepend(arr, element) {
        assert(Arr.is(arr), 'prepend(): bad array');
        var ret = arr.slice();
        ret.unshift(element);
        return ret;
    }

    function update(arr, index, element) {
        assert(Arr.is(arr), 'update(): bad array');
        assert(is_valid_index(index, 0, arr.length - 1), 'update(): bad index');
        var ret = arr.slice();
        ret[index] = element;
        return ret;
    }

    function remove(arr, index) {
        assert(Arr.is(arr), 'remove(): bad array');
        assert(is_valid_index(index, 0, arr.length - 1), 'remove(): bad index');
        var ret = arr.slice();
        ret.splice(index, 1);
        return ret;
    }

    function move(arr, from, to) {
        assert(Arr.is(arr), 'move(): bad array');
        assert(is_valid_index(from, 0, arr.length - 1), 'move(): bad from');
        assert(is_valid_index(to, 0, arr.length - 1), 'move(): bad to');
        var ret = arr.slice();
        if (from === to) {
            return ret;
        }
        var element = ret.splice(from, 1)[0];
        ret.splice(to, 0, element);
        return ret;
    }

    // --------------------------------------------------------------
    // primitive
    // --------------------------------------------------------------

    function primitive(name, is) {

        function Primitive(values) {
            assert(Primitive.is(values), 'bad ' + name);
            return values;
        }

        Primitive.meta = {
            kind: 'primitive',
            name: name
        };

        Primitive.is = is;

        return Primitive;
    }

    var Nil = primitive('Nil', function (x) {
        return x === null || x === undefined;
    });

    var Str = primitive('Str', function (x) {
        return typeof x === 'string';
    });

    var Num = primitive('Num', function (x) {
        return typeof x === 'number' && isFinite(x) && !isNaN(x);
    });

    var Bool = primitive('Bool', function (x) {
        return x === true || x === false;
    });

    var Arr = primitive('Arr', function (x) {
        return x instanceof Array;
    });

    var Obj = primitive('Obj', function (x) {
        return !Nil.is(x) && x.constructor === Object && !Arr.is(x);
    });

    var Func = primitive('Func', function (x) {
        return typeof x === 'function';
    });

    var Err = primitive('Err', function (x) {
        return x instanceof Error;
    });

    // --------------------------------------------------------------
    // struct
    // --------------------------------------------------------------

    function struct(props, name) {

        function Struct(values, mut) {

            assert(Obj.is(values), 'bad ' + (name || 'struct'));
            assert(maybe(Bool).is(mut), 'bad mut');

            for (var prop in props) {
                if (props.hasOwnProperty(prop)) {
                    var Type = props[prop],
                        value = values[prop];
                    this[prop] = Type.is(value) ? value : new Type(value, mut);
                }
            }

            freeze(this, mut);
        }

        Struct.meta = {
            kind: 'struct',
            props: props,
            name: name
        };

        Struct.is = function (x) {
            return x instanceof Struct;
        };

        Struct.update = function (instance, updates, mut) {

            assert(Struct.is(instance));
            assert(Obj.is(updates));

            var v = {};
            for (var prop in props) {
                if (props.hasOwnProperty(prop)) {
                    v[prop] = updates.hasOwnProperty(prop) ? updates[prop]
                            : instance[prop];
                }
            }
            return new Struct(v, mut);
        };

        return Struct;
    }

    // --------------------------------------------------------------
    // union
    // --------------------------------------------------------------

    function union(types, name) {

        function Union(values, mut) {
            var Type = Union.dispatch(values);
            return new Type(values, mut);
        }

        Union.meta = {
            kind: 'union',
            types: types,
            name: name
        };

        Union.is = function (x) {
            return types.some(function (type) {
                return type.is(x);
            });
        };

        return Union;
    }

    // --------------------------------------------------------------
    // maybe
    // --------------------------------------------------------------

    function maybe(Type, name) {

        function Maybe(values, mut) {
            return Nil.is(values) ? null : new Type(values, mut);
        }

        Maybe.meta = {
            kind: 'maybe',
            type: Type,
            name: name
        };

        Maybe.is = function (x) {
            return Nil.is(x) || Type.is(x);
        };

        return Maybe;
    }

    // --------------------------------------------------------------
    // enums
    // --------------------------------------------------------------

    function enums(map, name) {

        function Enums(x) {
            assert(Enums.is(x), 'bad ' + (name || 'enum'));
            return x;
        }

        Enums.meta = {
            kind: 'enums',
            map: map,
            name: name
        };

        Enums.is = function (x) {
            return Str.is(x) && map.hasOwnProperty(x);
        };

        return Enums;
    }

    // --------------------------------------------------------------
    // tuple
    // --------------------------------------------------------------

    function tuple(types, name) {

        var len = types.length;

        function Tuple(values, mut) {

            assert(Arr.is(values), 'bad ' + (name || 'tuple'));

            var arr = [];
            for (var i = 0 ; i < len ; i++) {
                var Type = types[i];
                var value = values[i];
                arr.push(Type.is(value) ? value : new Type(value, mut));
            }

            return freeze(arr, mut);
        }

        Tuple.meta = {
            kind: 'tuple',
            types: types,
            name: name
        };

        Tuple.is = function (x) {
            return Arr.is(x) && x.length === len &&
                types.every(function (type, i) {
                    return type.is(x[i]);
                });
        };

        Tuple.update = function (instance, index, element, mut) {
            var Type = types[index],
                value = Type.is(element) ? element : new Type(element, mut),
                arr = update(instance, index, value);
            return freeze(arr, mut);
        };

        return Tuple;
    }

    // --------------------------------------------------------------
    // subtype
    // --------------------------------------------------------------

    function subtype(Type, predicate, name) {

        function Subtype(values, mut) {
            var x = new Type(values, mut);
            assert(predicate(x), 'bad ' + (name || 'subtype'));
            return x;
        }

        Subtype.meta = {
            kind: 'subtype',
            type: Type,
            predicate: predicate,
            name: name
        };

        Subtype.is = function (x) {
            return Type.is(x) && predicate(x);
        };

        return Subtype;
    }

    // --------------------------------------------------------------
    // list
    // --------------------------------------------------------------

    function list(Type, name) {

        function List(values, mut) {

            assert(Arr.is(values), 'bad ' + (name || 'list'));

            var arr = [];
            for (var i = 0, len = values.length ; i < len ; i++ ) {
                var value = values[i];
                arr.push(Type.is(value) ? value : new Type(value, mut));
            }

            return freeze(arr, mut);
        }

        List.meta = {
            kind: 'list',
            type: Type,
            name: name
        };

        List.is = function (x) {
            return Arr.is(x) && x.every(Type.is);
        };

        List.append = function (instance, element, mut) {
            var value = Type.is(element) ? element : new Type(element, mut),
                arr = append(instance, value);
            return freeze(arr, mut);
        };

        List.prepend = function (instance, element, mut) {
            var value = Type.is(element) ? element : new Type(element, mut),
                arr = prepend(instance, value);
            return freeze(arr, mut);
        };

        List.update = function (instance, index, element, mut) {
            var value = Type.is(element) ? element : new Type(element, mut),
                arr = update(instance, index, value);
            return freeze(arr, mut);
        };

        List.remove = function (instance, index, mut) {
            var arr = remove(instance, index);
            return freeze(arr, mut);
        };

        List.move = function (instance, from, to, mut) {
            var arr = move(instance, from, to);
            return freeze(arr, mut);
        };

        return List;
    }

    // --------------------------------------------------------------
    // func (experimental)
    // --------------------------------------------------------------

    var func = function (Arguments, f, Return, name) {

        function func() {
            var args = Array.prototype.slice.call(arguments);
            if (args.length < f.length) args.length = f.length; // handle optional arguments

            args = Arguments.is(args) ? args : new Arguments(args);

            var r = f.apply(this, new Arguments(args));

            if (Return) {
                r = Return.is(r) ? r : new Return(r);
            }

            return r;
        }

        func.is = function (x) { return x === func; };

        func.meta = {
            kind: 'func',
            Arguments: Arguments,
            f: f,
            Return: Return,
            name: name
        };

        return func;
    };

    return {
        fail: fail,
        assert: assert,
        freeze: freeze,
        mixin: mixin,
        append: append,
        prepend: prepend,
        update: update,
        remove: remove,
        move: move,

        Nil: Nil,
        Str: Str,
        Num: Num,
        Bool: Bool,
        Arr: Arr,
        Obj: Obj,
        Func: Func,
        Err: Err,

        primitive: primitive,
        struct: struct,
        enums: enums,
        union: union,
        maybe: maybe,
        tuple: tuple,
        subtype: subtype,
        list: list,
        func: func
    };
}));
<|endoftext|>


<commit_before>define(
	['./segments', '../events', 'jquery'],
	function(routeSegments, events, jQuery) {

		var urlSeparator = '/';

		// ### Route object
		//
		// Routes are used to match URLs. Routes represent
		// the path taken for which an action has to be taken
		// (registered in the router with an associated callback
		// function).
		//
		// A route have a `segments` array. Some segments are optional and other mandatory.
		// A route match a URL if the segments matches the route segments.
		//
		// The strategy to match route against a URL is to match it against the segments
		// and then all combinations optional parameters. An array with all optional
		// sequences is calculated when route is created.
		//
		// _Note:_: Avvoid large number of optionals since it will consume memory
		// and slow down matching. You can sse query parameters instead.
		//
		var route = function(spec, my) {
			if(Object.prototype.toString.call(spec) === '[object String]') {
				var routePattern = spec;
				var routeSpec = my;
				return routeFactory(routePattern, routeSpec);
			}

			spec = spec || {};
			my = my || {};

			// Segments to match
			var segments = spec.segments;

			// Array with all optional sequences, ie. all combinations
			// of optional perameters. Array must be orderd to match URL:s
			// left to right.
			var optionalSequences = [];

			// Pre-caluclate optional sequences.
			ensureOptionalSequences();

			var that = {};

			// Mixin events
			jQuery.extend(that, events.eventhandler());

			my.getSegments = function() {
				return segments;
			};

			that.matchUrl = function(url) {
				// Match URL segments against route segments
				var urlSegments = url.getSegments();

				var matchingSegments = findMatch(urlSegments);
				if(!matchingSegments) {
					return routeNoMatchResult();
				}

				// Get values for each matching parameter and return as result
				var parameters = getParameters(matchingSegments, urlSegments);
				var result = routeMatchResult({route: that, url: url, parameters: parameters});

				that.trigger('matched', result);

				return result;
			};

			that.expand = function(params) {
				params = params || {};

				// Try to expand route into URL
				var urlSegments = [];
				segments.forEach(function(routeSegment) {
					var urlSegment;
					if(routeSegment.isParameter()) {
						// Use supplied value for parameters
						urlSegment = params[routeSegment.getName()];
					} else {
						// name/value for segments
						urlSegment = routeSegment.getValue();
					}

					// Skip if no match and optional
					if(urlSegment === undefined &&
						routeSegment.isOptional()) {
						return;
					}

					// Validate segment
					if (!routeSegment.match(urlSegment)) {
						throw new Error('Could not generate a valid URL');
					}

					urlSegments.push(urlSegment);
				});

				return urlSegments.join('/');
			};

			that.hasParameter = function(name) {
				return segments.some(function(segment) {
					return segment.isParameter() && segment.getName() === name;
				});
			};

			that.toString = function() {
				return 'route(' + segments.join('/') + ')';
			};

			function isMatch(urlSegments, sequence) {
				sequence = sequence || segments;

				// Can not match if different sizes
				if(urlSegments.length != sequence.length) {
					return false;
				}

				// TODO: rtewrite
				for(var segmentIndex = 0; segmentIndex < sequence.length; segmentIndex++) {
					var urlSegment = urlSegments[segmentIndex];
					var routeSegment = sequence[segmentIndex];
					if(urlSegment === undefined || !routeSegment.match(urlSegment)) {
						return false;
					}
				}

				return true;
			};

			function findMatch(urlSegments) {
				if(isMatch(urlSegments)) {
					return segments;
				}

				for(var i = 0; i < optionalSequences.length; i++) {
					if(isMatch(urlSegments, optionalSequences[i])) {
						return optionalSequences[i];
					}
				}

				return null;
			}

			function ensureOptionalSequences () {
				// Find positions for optionals
				var optionalPositions = [];
				segments.forEach(function(segment, index) {
					if(segment.isOptional()) {
						optionalPositions.push(index);
					}
				});

				// Create arrays that we can use to pick all combinations of
				// optional positions.
				// 3 => [[2], [1], [0], [1,2], [0,1], [0,2], [0,1,2]]
				var generator = rangeSubsets(optionalPositions.length)

				// Generate all possible combinations of optional
				generator.forEach(function(indexSequence) {
					// Clone segments array and remove optionals matching
					// indexes in index sequence
					var permutation = segments.slice();
					indexSequence.forEach(function(index, numRemoved) {
						// Use the generator index to look up the real
						// position of the optional using the optionalPositions array
						var optionalIndex = optionalPositions[index];

						// Remove optional but take in to account that we have already
						// removed {numRemoved} from permutation.
						permutation.splice(optionalIndex - numRemoved, 1);
					});

					optionalSequences.push(permutation);
				});
			};

			function getParameters(segmentPath, urlSegments) {
				var parameters = {};

				// Fill with matched parameter values
				segmentPath.forEach(function(routeSegment, index) {
					if(routeSegment.isParameter()) {
						parameters[routeSegment.getName()] = routeSegment.getValue(urlSegments[index]);
					}
				});

				// Fill unmatched parameters
				segments.forEach(function(routeSegment) {
					if(routeSegment.isParameter() && segmentPath.indexOf(routeSegment) === -1){ //!segmentPath.contains(routeSegment)) {
						parameters[routeSegment.getName()] = routeSegment.getValue(); // should return default
					}
				});

				return parameters;
			}

			return that;
		};

		// ### Route Factory
		//
		// Create route from pattern. A pattern can look like:
		//
		//	`/foo/#bar/?baz`
		//
		// See valid [segments](segments.html)
		//
		var routeFactory = function(routePattern, spec) {
			var segmentStrings = routePattern.split(urlSeparator);

			var nonEmptySegmentStrings = segmentStrings
				.map(Function.prototype.call, String.prototype.trim)
				.filter(Boolean);

			var segmentArray = nonEmptySegmentStrings.map(function(segmentString) {
				return routeSegments.segmentFactory(segmentString, spec);
			});

			return route({
				segments: segmentArray
			});
		};

		// Generates a range of a given size. Eg. 3 => [1,2,3]
		// Then generates all subsets of the range (with same internal order)
		// Returned subsets are ordered in right to left order.
		// Examples:
		// 1 => [[0]]
		// 2 => [[0], [0,1]]
		// 3 => [[2], [1], [0], [1,2], [0,1], [0,2], [0,1,2]]
		function rangeSubsets(size) {
			var queue = [];
			var anArray = [];
			for(var i = 0; i < size; i++) {
				anArray.push(i);
				queue.push([i]);
			}

			anArray.slice().map(function(item) {
				return [item];
			});

			var permutations = [];
			while (queue.length > 0) {
				var seqence = queue.pop();
				permutations.push(seqence);

				var indexLastItem = seqence[seqence.length - 1];
				var remaining = anArray.slice(indexLastItem + 1);

				remaining.forEach(function(item) {
					var newSequence = seqence.concat(item);
					queue.unshift(newSequence);
				});
			}

			return permutations;
		}

		// ### Route result
		//
		// Route match result are used as the answer of matching
		// a url for a route.
		//
		// Parameters is a hash with matched segment names as keys
		// and matching url segment values.
		//
		var routeMatchResult = function(spec) {
			spec = spec || {};

			var url = spec.url;
			var route = spec.route;
			var parameters = spec.parameters || {};

			var that = {};

			that.getParameters = function() { return parameters; };

			that.getKeys = function() {
				return Object.keys(parameters);
			};

			that.getValues = function() {
				return that.getKeys().map(function(v) {
					return parameters[v];
				});
			};

			that.getRoute = function () {
				return route;
			};

			that.getUrl = function () {
				return url;
			};

			that.matched = function() {
				return true;
			};

			that.getCallbackArguments = function () {
				var callbackArguments = [];
				callbackArguments = callbackArguments.concat(that.getValues());
				callbackArguments.push(url.getQuery());
				return callbackArguments;
			};

			return that;
		};

		var routeNoMatchResult = function() {
			var that = routeMatchResult();

			that.matched = function() {
				return false;
			};

			return that;
		};

		return route;
	}
);<commit_msg>Refactored route for readability<commit_after>define(
	['./segments', '../events', 'jquery'],
	function(routeSegments, events, jQuery) {

		var urlSeparator = '/';

		// ### Route object
		//
		// Routes are used to match URLs. Routes represent
		// the path taken for which an action has to be taken
		// (registered in the router with an associated callback
		// function).
		//
		// A route have a `segments` array. Some segments are optional and other mandatory.
		// A route match a URL if the segments matches the route segments.
		//
		// The strategy to match route against a URL is to match it against the segments
		// and then all combinations optional parameters. An array with all optional
		// sequences is calculated when route is created.
		//
		// _Note:_: Avvoid large number of optionals since it will consume memory
		// and slow down matching. You can sse query parameters instead.
		//
		var route = function(spec, my) {
			if(Object.prototype.toString.call(spec) === '[object String]') {
				var routePattern = spec;
				var routeSpec = my;
				return routeFactory(routePattern, routeSpec);
			}

			spec = spec || {};
			my = my || {};

			// Segments to match
			var segments = spec.segments;

			// Array with all optional sequences, ie. all combinations
			// of optional perameters. Array must be orderd to match URL:s
			// left to right.
			var optionalSequences = [];

			// Pre-caluclate optional sequences.
			ensureOptionalSequences();

			var that = {};

			// Mixin events
			jQuery.extend(that, events.eventhandler());

			my.getSegments = function() {
				return segments;
			};

			that.matchUrl = function(url) {
				var match = findMatch(url);
				if(!match) {
					return routeNoMatchResult();
				}

				var result = createMatchResult(match, url);

				that.trigger('matched', result);

				return result;
			};

			that.expand = function(params) {
				params = params || {};

				// Try to expand route into URL
				var urlSegments = [];
				segments.forEach(function(routeSegment) {
					var urlSegment;
					if(routeSegment.isParameter()) {
						// Use supplied value for parameters
						urlSegment = params[routeSegment.getName()];
					} else {
						// name/value for segments
						urlSegment = routeSegment.getValue();
					}

					// Skip if no match and optional
					if(urlSegment === undefined &&
						routeSegment.isOptional()) {
						return;
					}

					// Validate segment
					if (!routeSegment.match(urlSegment)) {
						throw new Error('Could not generate a valid URL');
					}

					urlSegments.push(urlSegment);
				});

				return urlSegments.join('/');
			};

			that.hasParameter = function(name) {
				return segments.some(function(segment) {
					return segment.isParameter() && segment.getName() === name;
				});
			};

			that.toString = function() {
				return 'route(' + segments.join('/') + ')';
			};

			function isMatch(urlSegments, sequence) {
				sequence = sequence || segments;

				// Can not match if different sizes
				if(urlSegments.length != sequence.length) {
					return false;
				}

				// All routeSegments much match coresponding URL segment
				return sequence.every(function(routeSegment, index) {
					var urlSegment = urlSegments[index];
					return urlSegment !== undefined && routeSegment.match(urlSegment);
				});
			}

			function findMatch(url) {
				// Match URL segments against route segments
				var urlSegments = url.getSegments();

				// Try match orignial segements
				if(isMatch(urlSegments)) {
					return segments;
				}

				// then optionals
				for(var i = 0; i < optionalSequences.length; i++) {
					if(isMatch(urlSegments, optionalSequences[i])) {
						return optionalSequences[i];
					}
				}

				return null;
			}

			function ensureOptionalSequences () {
				// Find positions for optionals
				var optionalPositions = [];
				segments.forEach(function(segment, index) {
					if(segment.isOptional()) {
						optionalPositions.push(index);
					}
				});

				// Generate possible sequences
				var possibleOptionalSequences = orderedSubsets(optionalPositions);

				possibleOptionalSequences.forEach(function(sequence) {
					// Clone segments array and remove optionals matching
					// indexes in index sequence
					var optionalSequence = segments.slice();
					sequence.forEach(function(optionalIndex, numRemoved) {
						// Remove optional but take in to account that we have already
						// removed {numRemoved} from permutation.
						optionalSequence.splice(optionalIndex - numRemoved, 1);
					});

					optionalSequences.push(optionalSequence);
				});
			}

			function createMatchResult(match, url) {
				var urlSegments = url.getSegments();

				var parameters = {};

				// Fill with matched parameter values
				match.forEach(function(routeSegment, index) {
					if(routeSegment.isParameter()) {
						parameters[routeSegment.getName()] = routeSegment.getValue(urlSegments[index]);
					}
				});

				// Fill with default values for not matched parameters
				segments.forEach(function(routeSegment) {
					if(routeSegment.isParameter() && match.indexOf(routeSegment) === -1) {
						parameters[routeSegment.getName()] = routeSegment.getValue();
					}
				});

				return routeMatchResult({route: that, url: url, parameters: parameters});
			}

			return that;
		};

		// ### Route Factory
		//
		// Create route from pattern. A pattern can look like:
		//
		//	`/foo/#bar/?baz`
		//
		// See valid [segments](segments.html)
		//
		var routeFactory = function(routePattern, spec) {
			var segmentStrings = routePattern.split(urlSeparator);

			var nonEmptySegmentStrings = segmentStrings
				.map(Function.prototype.call, String.prototype.trim)
				.filter(Boolean);

			var segmentArray = nonEmptySegmentStrings.map(function(segmentString) {
				return routeSegments.segmentFactory(segmentString, spec);
			});

			return route({
				segments: segmentArray
			});
		};

		// Generates all subsets of aray with same internal order
		// Returned subsets are ordered in right to left order.
		// Examples:
		// [1,2,3] => [1,2,3],[2,3],[1,3],[3],[1,2],[2],[1])
		var orderedSubsets = function(input) {
			var results = [], result, mask,
				total = Math.pow(2, input.length);

			for (mask = 1; mask < total; mask++) {
				result = [];
				i = input.length - 1;
				do {
					if ((mask & (1 << i)) !== 0) {
						result.unshift(input[i]);
					}
				} while (i--);
				results.unshift(result);
			}

			return results;
		};

		// ### Route result
		//
		// Route match result are used as the answer of matching
		// a url for a route.
		//
		// Parameters is a hash with matched segment names as keys
		// and matching url segment values.
		//
		var routeMatchResult = function(spec) {
			spec = spec || {};

			var url = spec.url;
			var route = spec.route;
			var parameters = spec.parameters || {};

			var that = {};

			that.getParameters = function() { return parameters; };

			that.getKeys = function() {
				return Object.keys(parameters);
			};

			that.getValues = function() {
				return that.getKeys().map(function(v) {
					return parameters[v];
				});
			};

			that.getRoute = function () {
				return route;
			};

			that.getUrl = function () {
				return url;
			};

			that.matched = function() {
				return true;
			};

			that.getCallbackArguments = function () {
				var callbackArguments = [];
				callbackArguments = callbackArguments.concat(that.getValues());
				callbackArguments.push(url.getQuery());
				return callbackArguments;
			};

			return that;
		};

		var routeNoMatchResult = function() {
			var that = routeMatchResult();

			that.matched = function() {
				return false;
			};

			return that;
		};

		return route;
	}
);<|endoftext|>


<commit_before>'use strict'

class Empire {
  get dossier () {
    if (!this._dossier) {
      this._dossier = new qlib.Dossier()
    }
    return this._dossier
  }

  get terminals () {
    if (!this._terminals) {
      this._terminals = []
      for (const city of this.cities) {
        if (Game.rooms[city] && Game.rooms[city].terminal) {
          this._terminals.push(Game.rooms[city].terminal)
        }
      }
    }
    return this._terminals
  }

  get cities () {
    if (!this._cities) {
      this._cities = Room.getCities()
    }
    return this._cities
  }
}

module.exports = Empire
<commit_msg>Only add own terminals to Empire._terminals<commit_after>'use strict'

class Empire {
  get dossier () {
    if (!this._dossier) {
      this._dossier = new qlib.Dossier()
    }
    return this._dossier
  }

  get terminals () {
    if (!this._terminals) {
      this._terminals = []
      for (const city of this.cities) {
        if (Game.rooms[city] && Game.rooms[city].terminal && Game.rooms[city].terminal.my) {
          this._terminals.push(Game.rooms[city].terminal)
        }
      }
    }
    return this._terminals
  }

  get cities () {
    if (!this._cities) {
      this._cities = Room.getCities()
    }
    return this._cities
  }
}

module.exports = Empire
<|endoftext|>


<commit_before>import React from 'react'
import { Route, IndexRoute, Redirect } from 'react-router'
import { routerActions } from 'react-router-redux'

// NOTE: here we're making use of the `resolve.root` configuration
// option in webpack, which allows us to specify import paths as if
// they were from the root of the ~/src directory. This makes it
// very easy to navigate to files regardless of how deeply nested
// your current file is.
// import CoreLayout from 'layouts/CoreLayout/CoreLayout'
// import HomeView from 'views/HomeView/HomeView'
import HomeView from 'views/HomeView/HomeView'
import BingoView from 'views/BingoView/BingoView'
import SearchPictogramsView from 'views/SearchPictogramsView'
import ShowPictogramsView from 'views/ShowPictogramsView'
import PictogramView from 'views/PictogramView'
import PictogramsGridView from 'views/PictogramsGridView/PictogramsGridView'
import RegisterView from 'views/RegisterView'
import AppConfView from 'views/AppConfView'
import SigninView from 'views/SigninView'
import SignoutView from 'views/SignoutView'
// import SearchView from 'views/SearchView'
// import ApiView from 'views/ApiView'
// import BingoView from 'views/BingoView'
// import TableroView from 'views/TableroView'
// import DominoView from 'views/DominoView'
// import NotFoundView from 'views/NotFoundView/NotFoundView'
// import Header from 'layouts/Header'
import Master from 'layouts/master'
import { UserAuthWrapper } from 'redux-auth-wrapper'
// import auth from 'components/auth'

/* function requireAuth(nextState, replace) {
  if (!auth.loggedIn()) {
    replace({
      pathname: '/signin',
      state: { nextPathname: nextState.location.pathname }
    })
  }
}
*/

const UserIsAuthenticated = UserAuthWrapper({
  authSelector: state => state.user,
  redirectAction: routerActions.replace,
  wrapperDisplayName: 'UserIsAuthenticated'
})
const UserIsAdmin = UserAuthWrapper({
  authSelector: state => state.user,
  redirectAction: routerActions.replace,
  wrapperDisplayName: 'UserIsAdmin',
  predicate: user => user.isAdmin
})

const UserIsTranslator = UserAuthWrapper({
  authSelector: state => state.user,
  redirectAction: routerActions.replace,
  wrapperDisplayName: 'UserIsTranslator',
  predicate: user => (user.isTranslator || user.isAdmin)
})

export default store => (
  <Route path='/' component={Master}>
    <IndexRoute component={HomeView}/>
    <Route path='home' component={HomeView} />
    <Route path='saac' component={HomeView} />
    <Route path='premios' component={HomeView} />
    <Route path='descargas' component={HomeView} />
    <Route path='condiciones-de-uso' component={HomeView} />
    <Redirect from='pictograms' to='/pictograms/search' />
    <Route path='pictograms'>
      <Route path='search' component={SearchPictogramsView}>
        <Route path='/pictograms/search/:searchText' component={ShowPictogramsView} />
        <Route path='/pictograms/id/:id' component={PictogramView} />
      </Route>
      <Route path='api' component={HomeView} />
      <Route path='catalogs' component={HomeView} />
    </Route>
    <Route path='materials' component={HomeView} />

    <Redirect from='onlinetools' to='/onlinetools/bingos-creator' />
    <Route path='onlinetools'>
      <Route path='animations-maker' component={HomeView} />
      <Route path='symbols-creator' component={HomeView} />
      <Route path='schedule-generator' component={HomeView} />
      <Route path='calendar-generator' component={HomeView} />
      <Route path='bingos-creator' component={BingoView} />
      <Route path='snakes-and-ladders' component={HomeView} />
      <Route path='dominos' component={HomeView} />
      <Route path='dominos-encadenados' component={HomeView} />
    </Route>
    // User Routes
    <Route path='signin' component={SigninView} />
    <Route path='register' component={RegisterView} />
    <Route path='configuration' component={AppConfView} />
    <Route path='profile' component={UserIsAuthenticated(HomeView)}  />
    <Route path='usermaterial' component={UserIsAuthenticated(HomeView)}  />
    <Route path='upload' component={UserIsAdmin(HomeView)} />
    <Route path='translate' component={UserIsTranslator(PictogramsGridView)} />
    <Route path='signout' component={SignoutView} />
  </Route>
)
<commit_msg>default login for auth wrapper is signin<commit_after>import { Route, IndexRoute, Redirect } from 'react-router'
import { routerActions } from 'react-router-redux'
import React from 'react'

// NOTE: here we're making use of the `resolve.root` configuration
// option in webpack, which allows us to specify import paths as if
// they were from the root of the ~/src directory. This makes it
// very easy to navigate to files regardless of how deeply nested
// your current file is.
// import CoreLayout from 'layouts/CoreLayout/CoreLayout'
// import HomeView from 'views/HomeView/HomeView'
import HomeView from 'views/HomeView/HomeView'
import BingoView from 'views/BingoView/BingoView'
import SearchPictogramsView from 'views/SearchPictogramsView'
import ShowPictogramsView from 'views/ShowPictogramsView'
import PictogramView from 'views/PictogramView'
import PictogramsGridView from 'views/PictogramsGridView/PictogramsGridView'
import RegisterView from 'views/RegisterView'
import AppConfView from 'views/AppConfView'
import SigninView from 'views/SigninView'
import SignoutView from 'views/SignoutView'
// import SearchView from 'views/SearchView'
// import ApiView from 'views/ApiView'
// import BingoView from 'views/BingoView'
// import TableroView from 'views/TableroView'
// import DominoView from 'views/DominoView'
// import NotFoundView from 'views/NotFoundView/NotFoundView'
// import Header from 'layouts/Header'
import Master from 'layouts/master'
import { UserAuthWrapper } from 'redux-auth-wrapper'
// import auth from 'components/auth'

/* function requireAuth(nextState, replace) {
  if (!auth.loggedIn()) {
    replace({
      pathname: '/signin',
      state: { nextPathname: nextState.location.pathname }
    })
  }
}
*/

const UserIsAuthenticated = UserAuthWrapper({
  authSelector: state => state.auth.isAuthenticated,
  failureRedirectPath: 'signin',
  redirectAction: routerActions.replace,
  wrapperDisplayName: 'UserIsAuthenticated'
})
const UserIsAdmin = UserAuthWrapper({
  authSelector: state => state.user,
  failureRedirectPath: 'signin',
  redirectAction: routerActions.replace,
  wrapperDisplayName: 'UserIsAdmin',
  predicate: user => user.isAdmin
})

const UserIsTranslator = UserAuthWrapper({
  authSelector: state => state.user,
  failureRedirectPath: 'signin',
  redirectAction: routerActions.replace,
  wrapperDisplayName: 'UserIsTranslator',
  predicate: user => (user.isTranslator || user.isAdmin)
})

export default store => (
  <Route path='/' component={Master}>
    <IndexRoute component={HomeView} />
    <Route path='home' component={HomeView} />
    <Route path='saac' component={HomeView} />
    <Route path='premios' component={HomeView} />
    <Route path='descargas' component={HomeView} />
    <Route path='condiciones-de-uso' component={HomeView} />
    <Redirect from='pictograms' to='/pictograms/search' />
    <Route path='pictograms'>
      <Route path='search' component={SearchPictogramsView}>
        <Route path='/pictograms/search/:searchText' component={ShowPictogramsView} />
        <Route path='/pictograms/id/:id' component={PictogramView} />
      </Route>
      <Route path='api' component={HomeView} />
      <Route path='catalogs' component={HomeView} />
    </Route>
    <Route path='materials' component={HomeView} />

    <Redirect from='onlinetools' to='/onlinetools/bingos-creator' />
    <Route path='onlinetools'>
      <Route path='animations-maker' component={HomeView} />
      <Route path='symbols-creator' component={HomeView} />
      <Route path='schedule-generator' component={HomeView} />
      <Route path='calendar-generator' component={HomeView} />
      <Route path='bingos-creator' component={BingoView} />
      <Route path='snakes-and-ladders' component={HomeView} />
      <Route path='dominos' component={HomeView} />
      <Route path='dominos-encadenados' component={HomeView} />
    </Route>
    // User Routes
    <Route path='signin' component={SigninView} />
    <Route path='register' component={RegisterView} />
    <Route path='configuration' component={AppConfView} />
    <Route path='profile' component={UserIsAuthenticated(HomeView)} />
    <Route path='usermaterial' component={UserIsAuthenticated(HomeView)} />
    <Route path='upload' component={UserIsAdmin(HomeView)} />
    <Route path='translate' component={UserIsTranslator(PictogramsGridView)} />
    <Route path='signout' component={SignoutView} />
  </Route>
)
<|endoftext|>


<commit_before>const express = require('express');
const router = express.Router();
const moment = require('moment');
const utils = require('../modules/utils');
const Order = require('../models/orders');
const User = require('../models/users');
const debug = require('debug')('route:order');
const responder = require('../modules/responder');

router.get('/order', (req, res, next) => {
  let {user, id} = req.query;
  let query = {};
  if (!user && !id)
    query = { dateFinished: { $gte: moment() } };
  else if (user)
    query = { user: user };
  else if (id)
    query = { _id: id };
  Order.find(query).sort({dateCreated: 'desc'}).populate('user').populate('location').exec()
  .then(r => {
    return res.json(r);
  })
  .catch(e => {
    debug(e);
    if (e.name === 'CastError')
      return res.status(400).json(responder(400, 1, 'Invalid id'));
    else
      return res.status(400).json(responder(400, 1, e));
  })
});

router.post('/order', (req, res, next) => {
  let {user, location, deliveryLocation, items, total, dateFinished} = req.body;
  if (!(user && items && location && total && dateFinished))
    return res.status(400).json(responder(400, 1, 'All fields are required!'));

  if (user.length !== 24)
    return res.status(400).json(responder(400, 2, 'Invalid id!'));

  if (!utils.valTime(dateFinished))
    return res.status(400).json(responder(400, 3, 'Invalid dateFinished!'));

  if (deliveryLocation)
    if (!utils.isNumeric(deliveryLocation.zipCode))
      return res.status(400).json(responder(400, 4, 'Zip code must be a number!'));

  User.count({ _id: user }).exec()
  .then(c => {
    if (c < 1) throw `User ${user} does not exist!`;

    let model = new Order ({
                user: user
              , location: location
              , deliveryLocation: deliveryLocation
              , items: items
              , total: total
              , dateFinished: moment(dateFinished, "HH:mm", true)
    });
    model.save(e => {
      if (e) throw e;
      return res.json(responder(200, 0, 'Order has been created!'));
    })
  })
  .catch(e => {
    return res.json(responder(400, 5, e));
  })
});

module.exports = router;
<commit_msg>Return all orders, no matter of the date finished<commit_after>const express = require('express');
const router = express.Router();
const moment = require('moment');
const utils = require('../modules/utils');
const Order = require('../models/orders');
const User = require('../models/users');
const debug = require('debug')('route:order');
const responder = require('../modules/responder');

router.get('/order', (req, res, next) => {
  let {user, id} = req.query;
  let query = {};
  if (!user && !id) {
    query = {};
  } else if (user)
    query = { user: user };
  else if (id)
    query = { _id: id };
  Order.find(query).sort({dateCreated: 'desc'}).populate('user').populate('location').exec()
  .then(r => {
    return res.json(r);
  })
  .catch(e => {
    debug(e);
    if (e.name === 'CastError')
      return res.status(400).json(responder(400, 1, 'Invalid id'));
    else
      return res.status(400).json(responder(400, 1, e));
  })
});

router.post('/order', (req, res, next) => {
  let {user, location, deliveryLocation, items, total, dateFinished} = req.body;
  if (!(user && items && location && total && dateFinished))
    return res.status(400).json(responder(400, 1, 'All fields are required!'));

  if (user.length !== 24)
    return res.status(400).json(responder(400, 2, 'Invalid id!'));

  if (!utils.valTime(dateFinished))
    return res.status(400).json(responder(400, 3, 'Invalid dateFinished!'));

  if (deliveryLocation)
    if (!utils.isNumeric(deliveryLocation.zipCode))
      return res.status(400).json(responder(400, 4, 'Zip code must be a number!'));

  User.count({ _id: user }).exec()
  .then(c => {
    if (c < 1) throw `User ${user} does not exist!`;

    let model = new Order ({
                user: user
              , location: location
              , deliveryLocation: deliveryLocation
              , items: items
              , total: total
              , dateFinished: moment(dateFinished, "HH:mm", true)
    });
    model.save(e => {
      if (e) throw e;
      return res.json(responder(200, 0, 'Order has been created!'));
    })
  })
  .catch(e => {
    return res.json(responder(400, 5, e));
  })
});

module.exports = router;
<|endoftext|>


<commit_before>define([
  './util',
  './webfinger',
  './wireClient',
  './sync',
  './store',
  './schedule',
  './baseClient',
  './platform',
  './getputdelete',
  './widget/default'
], function(util, webfinger, wireClient, sync, store, schedule, BaseClient,
            platform, getputdelete, defaultView) {

  // Namespace: widget
  //
  // The remoteStorage widget.
  //
  // See <remoteStorage.displayWidget>
  //
  //
  // Event: ready
  //   Fired when the user has connected and the initial synchronization
  //   has been performed. After this has fired your app can query and
  //   display data.
  //
  // Event: disconnect
  //   Fired when the user has clicked the 'disconnect' button and all
  //   locally cached data has been removed. At this point your app should
  //   remove all visible user-owned data from the screen and return to
  //   it's initial state.
  //
  // Event: state
  //   Fired whenever the internal state of the widget changes.
  //   Apps usually don't need to use this event, it is only included
  //   for debugging purposes and legacy support.
  //
  //   Parameters:
  //     state - A String. The new state the widget moved into.
  //

  "use strict";

  var settings = util.getSettingStore('remotestorage_widget');
  var events = util.getEventEmitter('ready', 'disconnect', 'state');
  var logger = util.getLogger('widget');

  var maxTimeout = 45000;
  var timeoutAdjustmentFactor = 1.5;

  // the view.
  var view = defaultView;
  // options passed to displayWidget
  var widgetOptions = {};
  // passed to display() to avoid circular deps
  var remoteStorage;

  var reconnectInterval = 10000;

  var offlineTimer = null;

  var viewState;

  var stateActions = {
    offline: function() {
      if(! offlineTimer) {
        offlineTimer = setTimeout(function() {
          offlineTimer = null;
          sync.fullSync().
            then(function() {
              schedule.enable();
            });
        }, reconnectInterval);
      }
    },
    connected: function() {
      if(offlineTimer) {
        clearTimeout(offlineTimer);
        offlineTimer = null;
      }
    }
  };

  function setState(state) {
    if(state === viewState) {
      return;
    }
    viewState = state;
    var action = stateActions[state];
    if(action) {
      action.apply(null, arguments);
    }
    view.setState.apply(view, arguments);
    events.emit('state', state);
  }

  function requestToken(authEndpoint) {
    logger.info('requestToken', authEndpoint);
    var redirectUri = (widgetOptions.redirectUri || view.getLocation());
    var state;
    var md = redirectUri.match(/^(.+)#(.*)$/);
    if(md) {
      redirectUri = md[1];
      state = md[2];
    }
    var clientId = util.hostNameFromUri(redirectUri);
    authEndpoint += authEndpoint.indexOf('?') > 0 ? '&' : '?';
    var params = [
      ['redirect_uri', redirectUri],
      ['client_id', clientId],
      ['scope', remoteStorage.access.scopeParameter],
      ['response_type', 'token']
    ];
    if(typeof(state) === 'string' && state.length > 0) {
      params.push(['state', state]);
    }
    authEndpoint += params.map(function(kv) {
      return kv[0] + '=' + encodeURIComponent(kv[1]);
    }).join('&');
    console.log('redirecting to', authEndpoint);
    return view.redirectTo(authEndpoint);
  }

  function connectStorage(userAddress) {
    settings.set('userAddress', userAddress);
    setState('authing');
    return webfinger.getStorageInfo(userAddress).
      then(wireClient.setStorageInfo, function(error) {
        if(error === 'timeout') {
          adjustTimeout();
        }
        setState((typeof(error) === 'string') ? 'typing' : 'error', error);
      }).
      then(function(storageInfo) {
        remoteStorage.access.setStorageType(storageInfo.type);
        return requestToken(storageInfo.properties['auth-endpoint']);
      }).
      then(schedule.enable, util.curry(setState, 'error'));
  }

  function reconnectStorage() {
    connectStorage(settings.get('userAddress'));
  }

  function disconnectStorage() {
    schedule.disable();
    remoteStorage.flushLocal();
    events.emit('state', 'disconnected');
    events.emit('disconnect');
  }

  // destructively parse query string from URI fragment
  function parseParams() {
    var md = view.getLocation().match(/^(.*?)#(.*)$/);
    var result = {};
    if(md) {
      var hash = md[2];
      hash.split('&').forEach(function(param) {
        var kv = param.split('=');
        if(kv[1]) {
          result[kv[0]] = decodeURIComponent(kv[1]);
        }
      });
      if(Object.keys(result).length > 0) {
        view.setLocation(md[1] + '#');
      }
    }
    return result;
  }

  function processParams() {
    var params = parseParams();

    // Query parameter: access_token
    if(params.access_token) {
      wireClient.setBearerToken(params.access_token);
    }
    // Query parameter: remotestorage
    if(params.remotestorage) {
      view.setUserAddress(params.remotestorage);
    } else {
      var userAddress = settings.get('userAddress');
      if(userAddress) {
        view.setUserAddress(userAddress);
      }
    }
    // Query parameter: state
    if(params.state) {
      view.setLocation(view.getLocation().split('#')[0] + '#' + params.state);
    }
  }

  function handleSyncError(error) {
    if(error.message === 'unauthorized') {
      setState('unauthorized');
    } else if(error.message === 'network error') {
      setState('offline', error);
    } else {
      setState('error', error);
    }
  }

  function adjustTimeout() {
    var t = getputdelete.getTimeout();
    if(t < maxTimeout) {
      t *= timeoutAdjustmentFactor;
      webfinger.setTimeout(t);
      getputdelete.setTimeout(t);
    }
  }

  function handleSyncTimeout() {
    adjustTimeout();
    schedule.disable();
    setState('offline');
  }

  function initialSync() {
    if(settings.get('initialSyncDone')) {
      store.fireInitialEvents();
    } else {
      setState('busy', true);
      sync.fullSync().then(function() {
        schedule.enable();
        settings.set('initialSyncDone', true);
        events.emit('ready');
      }, handleSyncError);
    }
  }

  function display(_remoteStorage, element, options) {
    remoteStorage = _remoteStorage;
    widgetOptions = options;
    if(! options) {
      options = {};
    }

    options.getLastSyncAt = function() {
      return sync.lastSyncAt && sync.lastSyncAt.getTime();
    };

    schedule.watch('/', 30000);

    view.display(element, options);

    view.on('sync', sync.forceSync);
    view.on('connect', connectStorage);
    view.on('disconnect', disconnectStorage);
    view.on('reconnect', reconnectStorage);

    sync.on('busy', util.curry(setState, 'busy'));
    sync.on('ready', util.curry(setState, 'connected'));
    wireClient.on('connected', function() {
      setState('connected');
      initialSync();
    });
    wireClient.on('disconnected', util.curry(setState, 'initial'));

    BaseClient.on('error', util.curry(setState, 'error'));
    sync.on('error', handleSyncError);
    sync.on('timeout', handleSyncTimeout);

    processParams();

    wireClient.calcState();
  }

  return util.extend({
    display : display,

    clearSettings: settings.clear,

    setView: function(_view) {
      view = _view;
    }
  }, events);
});
<commit_msg>widget: fire "ready" after firing initial events<commit_after>define([
  './util',
  './webfinger',
  './wireClient',
  './sync',
  './store',
  './schedule',
  './baseClient',
  './platform',
  './getputdelete',
  './widget/default'
], function(util, webfinger, wireClient, sync, store, schedule, BaseClient,
            platform, getputdelete, defaultView) {

  // Namespace: widget
  //
  // The remoteStorage widget.
  //
  // See <remoteStorage.displayWidget>
  //
  //
  // Event: ready
  //   Fired when the user has connected and the initial synchronization
  //   has been performed. After this has fired your app can query and
  //   display data.
  //
  // Event: disconnect
  //   Fired when the user has clicked the 'disconnect' button and all
  //   locally cached data has been removed. At this point your app should
  //   remove all visible user-owned data from the screen and return to
  //   it's initial state.
  //
  // Event: state
  //   Fired whenever the internal state of the widget changes.
  //   Apps usually don't need to use this event, it is only included
  //   for debugging purposes and legacy support.
  //
  //   Parameters:
  //     state - A String. The new state the widget moved into.
  //

  "use strict";

  var settings = util.getSettingStore('remotestorage_widget');
  var events = util.getEventEmitter('ready', 'disconnect', 'state');
  var logger = util.getLogger('widget');

  var maxTimeout = 45000;
  var timeoutAdjustmentFactor = 1.5;

  // the view.
  var view = defaultView;
  // options passed to displayWidget
  var widgetOptions = {};
  // passed to display() to avoid circular deps
  var remoteStorage;

  var reconnectInterval = 10000;

  var offlineTimer = null;

  var viewState;

  var stateActions = {
    offline: function() {
      if(! offlineTimer) {
        offlineTimer = setTimeout(function() {
          offlineTimer = null;
          sync.fullSync().
            then(function() {
              schedule.enable();
            });
        }, reconnectInterval);
      }
    },
    connected: function() {
      if(offlineTimer) {
        clearTimeout(offlineTimer);
        offlineTimer = null;
      }
    }
  };

  function setState(state) {
    if(state === viewState) {
      return;
    }
    viewState = state;
    var action = stateActions[state];
    if(action) {
      action.apply(null, arguments);
    }
    view.setState.apply(view, arguments);
    events.emit('state', state);
  }

  function requestToken(authEndpoint) {
    logger.info('requestToken', authEndpoint);
    var redirectUri = (widgetOptions.redirectUri || view.getLocation());
    var state;
    var md = redirectUri.match(/^(.+)#(.*)$/);
    if(md) {
      redirectUri = md[1];
      state = md[2];
    }
    var clientId = util.hostNameFromUri(redirectUri);
    authEndpoint += authEndpoint.indexOf('?') > 0 ? '&' : '?';
    var params = [
      ['redirect_uri', redirectUri],
      ['client_id', clientId],
      ['scope', remoteStorage.access.scopeParameter],
      ['response_type', 'token']
    ];
    if(typeof(state) === 'string' && state.length > 0) {
      params.push(['state', state]);
    }
    authEndpoint += params.map(function(kv) {
      return kv[0] + '=' + encodeURIComponent(kv[1]);
    }).join('&');
    console.log('redirecting to', authEndpoint);
    return view.redirectTo(authEndpoint);
  }

  function connectStorage(userAddress) {
    settings.set('userAddress', userAddress);
    setState('authing');
    return webfinger.getStorageInfo(userAddress).
      then(wireClient.setStorageInfo, function(error) {
        if(error === 'timeout') {
          adjustTimeout();
        }
        setState((typeof(error) === 'string') ? 'typing' : 'error', error);
      }).
      then(function(storageInfo) {
        remoteStorage.access.setStorageType(storageInfo.type);
        return requestToken(storageInfo.properties['auth-endpoint']);
      }).
      then(schedule.enable, util.curry(setState, 'error'));
  }

  function reconnectStorage() {
    connectStorage(settings.get('userAddress'));
  }

  function disconnectStorage() {
    schedule.disable();
    remoteStorage.flushLocal();
    events.emit('state', 'disconnected');
    events.emit('disconnect');
  }

  // destructively parse query string from URI fragment
  function parseParams() {
    var md = view.getLocation().match(/^(.*?)#(.*)$/);
    var result = {};
    if(md) {
      var hash = md[2];
      hash.split('&').forEach(function(param) {
        var kv = param.split('=');
        if(kv[1]) {
          result[kv[0]] = decodeURIComponent(kv[1]);
        }
      });
      if(Object.keys(result).length > 0) {
        view.setLocation(md[1] + '#');
      }
    }
    return result;
  }

  function processParams() {
    var params = parseParams();

    // Query parameter: access_token
    if(params.access_token) {
      wireClient.setBearerToken(params.access_token);
    }
    // Query parameter: remotestorage
    if(params.remotestorage) {
      view.setUserAddress(params.remotestorage);
    } else {
      var userAddress = settings.get('userAddress');
      if(userAddress) {
        view.setUserAddress(userAddress);
      }
    }
    // Query parameter: state
    if(params.state) {
      view.setLocation(view.getLocation().split('#')[0] + '#' + params.state);
    }
  }

  function handleSyncError(error) {
    if(error.message === 'unauthorized') {
      setState('unauthorized');
    } else if(error.message === 'network error') {
      setState('offline', error);
    } else {
      setState('error', error);
    }
  }

  function adjustTimeout() {
    var t = getputdelete.getTimeout();
    if(t < maxTimeout) {
      t *= timeoutAdjustmentFactor;
      webfinger.setTimeout(t);
      getputdelete.setTimeout(t);
    }
  }

  function handleSyncTimeout() {
    adjustTimeout();
    schedule.disable();
    setState('offline');
  }

  function initialSync() {
    if(settings.get('initialSyncDone')) {
      store.fireInitialEvents().then(util.curry(events.emit, 'ready'));
    } else {
      setState('busy', true);
      sync.fullSync().then(function() {
        schedule.enable();
        settings.set('initialSyncDone', true);
        events.emit('ready');
      }, handleSyncError);
    }
  }

  function display(_remoteStorage, element, options) {
    remoteStorage = _remoteStorage;
    widgetOptions = options;
    if(! options) {
      options = {};
    }

    options.getLastSyncAt = function() {
      return sync.lastSyncAt && sync.lastSyncAt.getTime();
    };

    schedule.watch('/', 30000);

    view.display(element, options);

    view.on('sync', sync.forceSync);
    view.on('connect', connectStorage);
    view.on('disconnect', disconnectStorage);
    view.on('reconnect', reconnectStorage);

    sync.on('busy', util.curry(setState, 'busy'));
    sync.on('ready', util.curry(setState, 'connected'));
    wireClient.on('connected', function() {
      setState('connected');
      initialSync();
    });
    wireClient.on('disconnected', util.curry(setState, 'initial'));

    BaseClient.on('error', util.curry(setState, 'error'));
    sync.on('error', handleSyncError);
    sync.on('timeout', handleSyncTimeout);

    processParams();

    wireClient.calcState();
  }

  return util.extend({
    display : display,

    clearSettings: settings.clear,

    setView: function(_view) {
      view = _view;
    }
  }, events);
});
<|endoftext|>