<commit_before>var XiangqiViewer = {}
window.XiangqiViewer = XiangqiViewer;

XiangqiViewer.BoardRenderer = function(element, cellSize, strokeWidth) {
  var BOARD_WIDTH = 8;
  var BOARD_HEIGHT = 9;
  var BOARD_COLOR = 'rgb(6, 120, 155)';
  var MARGIN = cellSize * 0.70;
  var XSIZE = 2;
  var PIECE_SIZE = 0.85 * cellSize;

  var width = BOARD_WIDTH * cellSize + 2*MARGIN;
  var height = BOARD_HEIGHT * cellSize + 2*MARGIN;

  var leftBorder = MARGIN;
  var rightBorder = BOARD_WIDTH * cellSize + MARGIN;
  var topBorder = MARGIN;
  var botBorder = BOARD_HEIGHT * cellSize + MARGIN;
  var riverTop = Math.floor(BOARD_HEIGHT/2) * cellSize + MARGIN;
  var riverBot = Math.ceil(BOARD_HEIGHT/2) * cellSize + MARGIN;
  var dotDistance = 2 * strokeWidth;

  var root = SVG('xiangqi-example').width(width).height(height);

  this.draw = function(selector) {
    // horizontal
    drawHorizontalLines();
    drawVerticalLines(topBorder, riverTop);
    drawVerticalLines(riverBot, botBorder);
    drawBorder(leftBorder);
    drawBorder(rightBorder);
    drawX(topBorder);
    drawX(botBorder - XSIZE * cellSize);

    for (var i = 2; i < 10; i += 2) {
      drawDots(i, 3, false);
      drawDots(i, 6, false);
    }

    for (var i = 0; i < 8; i += 2) {
      drawDots(i, 3, true);
      drawDots(i, 6, true);
    }
  };

  var drawBoardLine = function(x1, y1, x2, y2) {
    root.line(x1, y1, x2, y2).stroke({
      color: BOARD_COLOR,
      width: strokeWidth
    });
  };

  var drawHorizontalLines = function() {
    for (var j = 0; j < BOARD_HEIGHT + 1; j++) {
      var height = MARGIN + j * cellSize;

      drawBoardLine(leftBorder, height, rightBorder, height);
    };
  }

  var drawVerticalLines = function(top, bottom) {
    for (var j = 1; j < BOARD_WIDTH; j++) {
      var offset = MARGIN + j * cellSize;

      drawBoardLine(offset, top, offset, bottom);
    };
  }

  var drawBorder = function(x) {
    drawBoardLine(x, topBorder - strokeWidth/2, x, botBorder + strokeWidth/2);
  };

  var drawX = function(top) {
    var leftEdge = leftBorder + 3 * cellSize;
    var rightEdge = leftBorder + (3 + XSIZE) * cellSize;
    var bot = top + XSIZE * cellSize;
    drawBoardLine(leftEdge, top, rightEdge, bot);
    drawBoardLine(leftEdge, bot, rightEdge, top);
  };

  // x and y are from 0 to (n - 1)
  var drawDots = function(x, y, isRight) {
    var directionMultipler = isRight ? -1 : 1;
    root.rect(strokeWidth, strokeWidth).move(
      MARGIN + x * cellSize - dotDistance * directionMultipler - (strokeWidth / 2),
      MARGIN + y * cellSize - dotDistance - (strokeWidth / 2)
    ).attr({fill: BOARD_COLOR});

    root.rect(strokeWidth, strokeWidth).move(
      MARGIN + x * cellSize - dotDistance * directionMultipler - (strokeWidth / 2),
      MARGIN + y * cellSize + dotDistance - (strokeWidth / 2)
    ).attr({fill: BOARD_COLOR});
  };

  var getX = function(file) {
    return MARGIN + cellSize * file - (PIECE_SIZE / 2);
  };

  var getY = function(rank) {
    return MARGIN + cellSize * rank - (PIECE_SIZE / 2);
  };

  this.putPiece = function(file, rank, piece) {
    return root.image(piece.spriteUrl())
      .move(getX(file), getY(rank))
      .attr({
        width: PIECE_SIZE,
        height: PIECE_SIZE
      });
  }

  this.movePiece = function(file, rank, piece) {
    piece.rendered.move(getX(file), getY(rank));
  };

  return this;
};

XiangqiViewer.Board = function(selector, cellSize, strokeWidth) {
  var renderer = new XiangqiViewer.BoardRenderer(selector, cellSize, strokeWidth);
  renderer.draw();

  var WIDTH = 9
  var HEIGHT = 10
  var matrix;
  var history;

  var initialize = function() {
    history = [];
    matrix = []; // rank, file; x, y

    for(var i = 0; i < WIDTH; i++) {
      matrix[i] = [];
      for(var j = 0; j < HEIGHT; j++) {
        matrix[i][j] = null;
      }
    }
  };

  var get = function(file, rank) {
    return matrix[file][rank];
  };

  var place = function(x, y, piece) {
    if (x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT) {
      throw "x, y coords are out of bounds";
    }

    matrix[x][y] = piece;
    piece.rendered = renderer.putPiece(x, y, piece);
  };

  var searchForward = function(pieceCode, red) {
    for (var i = 0; i < WIDTH; i++) {
      for (var j = 0; j < HEIGHT; j++) {
        var piece = get(i, j);
        if (piece && piece.code == pieceCode) {
          return {piece: piece, position: {file: i, rank: j}};
        }
      }
    }
  };

  var searchBackward = function(pieceCode, red) {
    for (var i = 0; i < WIDTH; i++) {
      for (var j = HEIGHT - 1; j >= 0; j--) {
        var piece = get(i, j);
        if (piece && piece.code == pieceCode && piece.red == red) {
          return {piece: piece, position: {file: i, rank: j}};
        }
      }
    }
  };

  var getPositionedPiece = function(instruction, red) {
    if (instruction[0] == 'f' && red) {
      return searchBackward(instruction[1], red);
    } else if (instruction[0] == 'b' && red) {
      return searchForward(instruction[1], red);
    } else if (instruction[0] == 'f' && !red) {
      return searchBackward(instruction[1], red);
    } else if (instruction[0] == 'b' && !red) {
      return searchForward(instruction[1], red);
    } else {
      var instructionPiece = instruction[0];
      var file;
      if (red) {
        file = 9 - parseInt(instruction[1]);
      } else {
        file = parseInt(instruction[1]) - 1;
      }

      for (var i = 0; i < HEIGHT; i++) {
        var piece = get(file, i);
        if (piece && piece.code == instructionPiece && piece.red == red) {
          return {piece: piece, position: {file: file, rank: i}};
        }
      }

      throw "no piece on this file";
    }
  }

  this.runMove = function(instruction, red) {
    if (instruction.length != 4) {
      throw "illegal instruction format";
    }
    instruction = instruction.toLowerCase();

    var positionedPiece = getPositionedPiece(instruction, red);
    var move = positionedPiece.piece.getMove(positionedPiece.position, instruction);

    // remove captured piece
    var capturedPiece = get(move.to.file, move.to.rank);
    if (capturedPiece) {
      matrix[move.to.file][move.to.rank] = null;
      capturedPiece.rendered.remove();
    }

    // push to history
    history.push({from: move.from, to: move.to, capturedPiece: capturedPiece});

    // update matrix
    matrix[move.from.file][move.from.rank] = null;
    matrix[move.to.file][move.to.rank] = positionedPiece.piece;

    //rerender
    renderer.movePiece(move.to.file, move.to.rank, positionedPiece.piece);
  }

  this.defaultSetup = function() {
    place(0, 0, new XiangqiViewer.Chariot(false));
    place(1, 0, new XiangqiViewer.Horse(false));
    place(2, 0, new XiangqiViewer.Elephant(false));
    place(3, 0, new XiangqiViewer.Advisor(false));
    place(4, 0, new XiangqiViewer.General(false));
    place(5, 0, new XiangqiViewer.Advisor(false));
    place(6, 0, new XiangqiViewer.Elephant(false));
    place(7, 0, new XiangqiViewer.Horse(false));
    place(8, 0, new XiangqiViewer.Chariot(false));
    place(0, 3, new XiangqiViewer.Pawn(false));
    place(2, 3, new XiangqiViewer.Pawn(false));
    place(4, 3, new XiangqiViewer.Pawn(false));
    place(6, 3, new XiangqiViewer.Pawn(false));
    place(8, 3, new XiangqiViewer.Pawn(false));
    place(1, 2, new XiangqiViewer.Cannon(false));
    place(7, 2, new XiangqiViewer.Cannon(false));

    place(0, 9, new XiangqiViewer.Chariot(true));
    place(1, 9, new XiangqiViewer.Horse(true));
    place(2, 9, new XiangqiViewer.Elephant(true));
    place(3, 9, new XiangqiViewer.Advisor(true));
    place(4, 9, new XiangqiViewer.General(true));
    place(5, 9, new XiangqiViewer.Advisor(true));
    place(6, 9, new XiangqiViewer.Elephant(true));
    place(7, 9, new XiangqiViewer.Horse(true));
    place(8, 9, new XiangqiViewer.Chariot(true));
    place(0, 6, new XiangqiViewer.Pawn(true));
    place(2, 6, new XiangqiViewer.Pawn(true));
    place(4, 6, new XiangqiViewer.Pawn(true));
    place(6, 6, new XiangqiViewer.Pawn(true));
    place(8, 6, new XiangqiViewer.Pawn(true));
    place(1, 7, new XiangqiViewer.Cannon(true));
    place(7, 7, new XiangqiViewer.Cannon(true));
  };

  initialize();
}

XiangqiViewer.StraightMover = function() {
  this.getMove = function(position, instruction) {
    var operator = instruction[2];
    var destination = parseInt(instruction[3]);
    var to = $.extend(true, {}, position);

    if (operator === '-') {
      if (this.red) {
        to.rank += destination;
      } else {
        to.rank -= destination;
      }
    } else if (operator === '+') {
      if (this.red) {
        to.rank -= destination;
      } else {
        to.rank += destination;
      }
    } else {
      if (this.red) {
        to.file = 9 - destination;
      } else {
        to = destination - 1;
      }
    }

    return {from: position, to: to};
  };
};

XiangqiViewer.Chariot = function(red) {
  var me = new XiangqiViewer.StraightMover();
  me.code = 'r';
  me.red = red;
  me.spriteUrl = function() {
    if (red) {
      return "images/chariot_red.svg";
    } else {
      return "images/chariot_black.svg";
    }
  };

  return me;
};
XiangqiViewer.Chariot.prototype = XiangqiViewer.StraightMover;

XiangqiViewer.Horse = function(red) {
  this.code = 'h';
  this.red = red;
  this.spriteUrl = function() {
    if (red) {
      return "images/horse_red.svg";
    } else {
      return "images/horse_black.svg";
    }
  };

  this.getMove = function(position, instruction) {
  };
};

XiangqiViewer.Elephant = function(red) {
  this.code = 'e';
  this.red = red;
  this.spriteUrl = function() {
    if (red) {
      return "images/elephant_red.svg";
    } else {
      return "images/elephant_black.svg";
    }
  };

  this.getMove = function(position, instruction) {
  };
};

XiangqiViewer.Advisor = function(red) {
  this.code = 'a';
  this.red = red;
  this.spriteUrl = function() {
    if (red) {
      return "images/adviser_red.svg";
    } else {
      return "images/adviser_black.svg";
    }
  };

  this.getMove = function(position, instruction) {
  };
};

XiangqiViewer.General = function(red) {
  var me = new XiangqiViewer.StraightMover();
  me.code = 'g';
  me.red = red;
  me.spriteUrl = function() {
    if (red) {
      return "images/general_red.svg";
    } else {
      return "images/general_black.svg";
    }
  };

  return me;
};

XiangqiViewer.Pawn = function(red) {
  var me = new XiangqiViewer.StraightMover();
  me.code = 'p';
  me.red = red;
  me.spriteUrl = function() {
    if (red) {
      return "images/pawn_red.svg";
    } else {
      return "images/pawn_black.svg";
    }
  };

  return me;
};

XiangqiViewer.Cannon = function(red) {
  var me = new XiangqiViewer.StraightMover();
  me.code = 'c';
  me.red = red;
  me.spriteUrl = function() {
    if (red) {
      return "images/cannon_red.svg";
    } else {
      return "images/cannon_black.svg";
    }
  };

  return me;
};
<commit_msg>remove unused line<commit_after>var XiangqiViewer = {}
window.XiangqiViewer = XiangqiViewer;

XiangqiViewer.BoardRenderer = function(element, cellSize, strokeWidth) {
  var BOARD_WIDTH = 8;
  var BOARD_HEIGHT = 9;
  var BOARD_COLOR = 'rgb(6, 120, 155)';
  var MARGIN = cellSize * 0.70;
  var XSIZE = 2;
  var PIECE_SIZE = 0.85 * cellSize;

  var width = BOARD_WIDTH * cellSize + 2*MARGIN;
  var height = BOARD_HEIGHT * cellSize + 2*MARGIN;

  var leftBorder = MARGIN;
  var rightBorder = BOARD_WIDTH * cellSize + MARGIN;
  var topBorder = MARGIN;
  var botBorder = BOARD_HEIGHT * cellSize + MARGIN;
  var riverTop = Math.floor(BOARD_HEIGHT/2) * cellSize + MARGIN;
  var riverBot = Math.ceil(BOARD_HEIGHT/2) * cellSize + MARGIN;
  var dotDistance = 2 * strokeWidth;

  var root = SVG('xiangqi-example').width(width).height(height);

  this.draw = function(selector) {
    // horizontal
    drawHorizontalLines();
    drawVerticalLines(topBorder, riverTop);
    drawVerticalLines(riverBot, botBorder);
    drawBorder(leftBorder);
    drawBorder(rightBorder);
    drawX(topBorder);
    drawX(botBorder - XSIZE * cellSize);

    for (var i = 2; i < 10; i += 2) {
      drawDots(i, 3, false);
      drawDots(i, 6, false);
    }

    for (var i = 0; i < 8; i += 2) {
      drawDots(i, 3, true);
      drawDots(i, 6, true);
    }
  };

  var drawBoardLine = function(x1, y1, x2, y2) {
    root.line(x1, y1, x2, y2).stroke({
      color: BOARD_COLOR,
      width: strokeWidth
    });
  };

  var drawHorizontalLines = function() {
    for (var j = 0; j < BOARD_HEIGHT + 1; j++) {
      var height = MARGIN + j * cellSize;

      drawBoardLine(leftBorder, height, rightBorder, height);
    };
  }

  var drawVerticalLines = function(top, bottom) {
    for (var j = 1; j < BOARD_WIDTH; j++) {
      var offset = MARGIN + j * cellSize;

      drawBoardLine(offset, top, offset, bottom);
    };
  }

  var drawBorder = function(x) {
    drawBoardLine(x, topBorder - strokeWidth/2, x, botBorder + strokeWidth/2);
  };

  var drawX = function(top) {
    var leftEdge = leftBorder + 3 * cellSize;
    var rightEdge = leftBorder + (3 + XSIZE) * cellSize;
    var bot = top + XSIZE * cellSize;
    drawBoardLine(leftEdge, top, rightEdge, bot);
    drawBoardLine(leftEdge, bot, rightEdge, top);
  };

  // x and y are from 0 to (n - 1)
  var drawDots = function(x, y, isRight) {
    var directionMultipler = isRight ? -1 : 1;
    root.rect(strokeWidth, strokeWidth).move(
      MARGIN + x * cellSize - dotDistance * directionMultipler - (strokeWidth / 2),
      MARGIN + y * cellSize - dotDistance - (strokeWidth / 2)
    ).attr({fill: BOARD_COLOR});

    root.rect(strokeWidth, strokeWidth).move(
      MARGIN + x * cellSize - dotDistance * directionMultipler - (strokeWidth / 2),
      MARGIN + y * cellSize + dotDistance - (strokeWidth / 2)
    ).attr({fill: BOARD_COLOR});
  };

  var getX = function(file) {
    return MARGIN + cellSize * file - (PIECE_SIZE / 2);
  };

  var getY = function(rank) {
    return MARGIN + cellSize * rank - (PIECE_SIZE / 2);
  };

  this.putPiece = function(file, rank, piece) {
    return root.image(piece.spriteUrl())
      .move(getX(file), getY(rank))
      .attr({
        width: PIECE_SIZE,
        height: PIECE_SIZE
      });
  }

  this.movePiece = function(file, rank, piece) {
    piece.rendered.move(getX(file), getY(rank));
  };

  return this;
};

XiangqiViewer.Board = function(selector, cellSize, strokeWidth) {
  var renderer = new XiangqiViewer.BoardRenderer(selector, cellSize, strokeWidth);
  renderer.draw();

  var WIDTH = 9
  var HEIGHT = 10
  var matrix;
  var history;

  var initialize = function() {
    history = [];
    matrix = []; // rank, file; x, y

    for(var i = 0; i < WIDTH; i++) {
      matrix[i] = [];
      for(var j = 0; j < HEIGHT; j++) {
        matrix[i][j] = null;
      }
    }
  };

  var get = function(file, rank) {
    return matrix[file][rank];
  };

  var place = function(x, y, piece) {
    if (x < 0 || y < 0 || x >= WIDTH || y >= HEIGHT) {
      throw "x, y coords are out of bounds";
    }

    matrix[x][y] = piece;
    piece.rendered = renderer.putPiece(x, y, piece);
  };

  var searchForward = function(pieceCode, red) {
    for (var i = 0; i < WIDTH; i++) {
      for (var j = 0; j < HEIGHT; j++) {
        var piece = get(i, j);
        if (piece && piece.code == pieceCode) {
          return {piece: piece, position: {file: i, rank: j}};
        }
      }
    }
  };

  var searchBackward = function(pieceCode, red) {
    for (var i = 0; i < WIDTH; i++) {
      for (var j = HEIGHT - 1; j >= 0; j--) {
        var piece = get(i, j);
        if (piece && piece.code == pieceCode && piece.red == red) {
          return {piece: piece, position: {file: i, rank: j}};
        }
      }
    }
  };

  var getPositionedPiece = function(instruction, red) {
    if (instruction[0] == 'f' && red) {
      return searchBackward(instruction[1], red);
    } else if (instruction[0] == 'b' && red) {
      return searchForward(instruction[1], red);
    } else if (instruction[0] == 'f' && !red) {
      return searchBackward(instruction[1], red);
    } else if (instruction[0] == 'b' && !red) {
      return searchForward(instruction[1], red);
    } else {
      var instructionPiece = instruction[0];
      var file;
      if (red) {
        file = 9 - parseInt(instruction[1]);
      } else {
        file = parseInt(instruction[1]) - 1;
      }

      for (var i = 0; i < HEIGHT; i++) {
        var piece = get(file, i);
        if (piece && piece.code == instructionPiece && piece.red == red) {
          return {piece: piece, position: {file: file, rank: i}};
        }
      }

      throw "no piece on this file";
    }
  }

  this.runMove = function(instruction, red) {
    if (instruction.length != 4) {
      throw "illegal instruction format";
    }
    instruction = instruction.toLowerCase();

    var positionedPiece = getPositionedPiece(instruction, red);
    var move = positionedPiece.piece.getMove(positionedPiece.position, instruction);

    // remove captured piece
    var capturedPiece = get(move.to.file, move.to.rank);
    if (capturedPiece) {
      matrix[move.to.file][move.to.rank] = null;
      capturedPiece.rendered.remove();
    }

    // push to history
    history.push({from: move.from, to: move.to, capturedPiece: capturedPiece});

    // update matrix
    matrix[move.from.file][move.from.rank] = null;
    matrix[move.to.file][move.to.rank] = positionedPiece.piece;

    //rerender
    renderer.movePiece(move.to.file, move.to.rank, positionedPiece.piece);
  }

  this.defaultSetup = function() {
    place(0, 0, new XiangqiViewer.Chariot(false));
    place(1, 0, new XiangqiViewer.Horse(false));
    place(2, 0, new XiangqiViewer.Elephant(false));
    place(3, 0, new XiangqiViewer.Advisor(false));
    place(4, 0, new XiangqiViewer.General(false));
    place(5, 0, new XiangqiViewer.Advisor(false));
    place(6, 0, new XiangqiViewer.Elephant(false));
    place(7, 0, new XiangqiViewer.Horse(false));
    place(8, 0, new XiangqiViewer.Chariot(false));
    place(0, 3, new XiangqiViewer.Pawn(false));
    place(2, 3, new XiangqiViewer.Pawn(false));
    place(4, 3, new XiangqiViewer.Pawn(false));
    place(6, 3, new XiangqiViewer.Pawn(false));
    place(8, 3, new XiangqiViewer.Pawn(false));
    place(1, 2, new XiangqiViewer.Cannon(false));
    place(7, 2, new XiangqiViewer.Cannon(false));

    place(0, 9, new XiangqiViewer.Chariot(true));
    place(1, 9, new XiangqiViewer.Horse(true));
    place(2, 9, new XiangqiViewer.Elephant(true));
    place(3, 9, new XiangqiViewer.Advisor(true));
    place(4, 9, new XiangqiViewer.General(true));
    place(5, 9, new XiangqiViewer.Advisor(true));
    place(6, 9, new XiangqiViewer.Elephant(true));
    place(7, 9, new XiangqiViewer.Horse(true));
    place(8, 9, new XiangqiViewer.Chariot(true));
    place(0, 6, new XiangqiViewer.Pawn(true));
    place(2, 6, new XiangqiViewer.Pawn(true));
    place(4, 6, new XiangqiViewer.Pawn(true));
    place(6, 6, new XiangqiViewer.Pawn(true));
    place(8, 6, new XiangqiViewer.Pawn(true));
    place(1, 7, new XiangqiViewer.Cannon(true));
    place(7, 7, new XiangqiViewer.Cannon(true));
  };

  initialize();
}

XiangqiViewer.StraightMover = function() {
  this.getMove = function(position, instruction) {
    var operator = instruction[2];
    var destination = parseInt(instruction[3]);
    var to = $.extend(true, {}, position);

    if (operator === '-') {
      if (this.red) {
        to.rank += destination;
      } else {
        to.rank -= destination;
      }
    } else if (operator === '+') {
      if (this.red) {
        to.rank -= destination;
      } else {
        to.rank += destination;
      }
    } else {
      if (this.red) {
        to.file = 9 - destination;
      } else {
        to = destination - 1;
      }
    }

    return {from: position, to: to};
  };
};

XiangqiViewer.Chariot = function(red) {
  var me = new XiangqiViewer.StraightMover();
  me.code = 'r';
  me.red = red;
  me.spriteUrl = function() {
    if (red) {
      return "images/chariot_red.svg";
    } else {
      return "images/chariot_black.svg";
    }
  };

  return me;
};

XiangqiViewer.Horse = function(red) {
  this.code = 'h';
  this.red = red;
  this.spriteUrl = function() {
    if (red) {
      return "images/horse_red.svg";
    } else {
      return "images/horse_black.svg";
    }
  };

  this.getMove = function(position, instruction) {
  };
};

XiangqiViewer.Elephant = function(red) {
  this.code = 'e';
  this.red = red;
  this.spriteUrl = function() {
    if (red) {
      return "images/elephant_red.svg";
    } else {
      return "images/elephant_black.svg";
    }
  };

  this.getMove = function(position, instruction) {
  };
};

XiangqiViewer.Advisor = function(red) {
  this.code = 'a';
  this.red = red;
  this.spriteUrl = function() {
    if (red) {
      return "images/adviser_red.svg";
    } else {
      return "images/adviser_black.svg";
    }
  };

  this.getMove = function(position, instruction) {
  };
};

XiangqiViewer.General = function(red) {
  var me = new XiangqiViewer.StraightMover();
  me.code = 'g';
  me.red = red;
  me.spriteUrl = function() {
    if (red) {
      return "images/general_red.svg";
    } else {
      return "images/general_black.svg";
    }
  };

  return me;
};

XiangqiViewer.Pawn = function(red) {
  var me = new XiangqiViewer.StraightMover();
  me.code = 'p';
  me.red = red;
  me.spriteUrl = function() {
    if (red) {
      return "images/pawn_red.svg";
    } else {
      return "images/pawn_black.svg";
    }
  };

  return me;
};

XiangqiViewer.Cannon = function(red) {
  var me = new XiangqiViewer.StraightMover();
  me.code = 'c';
  me.red = red;
  me.spriteUrl = function() {
    if (red) {
      return "images/cannon_red.svg";
    } else {
      return "images/cannon_black.svg";
    }
  };

  return me;
};
<|endoftext|>


<commit_before>function objectIsEmpty(obj) {
  for(var x in obj)
    return false;

  return true;
}

function generateGraph(groups, jobs, resources) {
  var cGraph = new dagreD3.graphlib.Graph({
    compound: true
  }).setGraph({
    rankDir: "LR"
  });

  populateGroupNodes(cGraph, groups);
  populateResourceNodes(cGraph, resources);
  populateJobNodesAndEdges(cGraph, jobs);
  inlineNodesIntoCommonGroup(cGraph);

  if (!objectIsEmpty(groups)) {
    removeUnconnectedGroupMembers(groups, cGraph);
    removeOrphanedNodes(cGraph);
  }

  return cGraph;
}

function populateGroupNodes(cGraph, groups) {
  for (var name in groups) {
    cGraph.setNode(groupNode(name), {});
  }
}

function populateResourceNodes(cGraph, resources) {
  for (var i in resources) {
    var resource = resources[i];
    var id = resourceNode(resource.name);

    var classes = ["resource"];

    if (resource.failing) {
      classes.push("failing");
    }

    if (resource.checking) {
      classes.push("checking")
    }

    cGraph.setNode(id, {
      class: classes.join(" "),
      label: "
" + resource.name + "
",
      labelType: "html",
      groups: resource.groups
    });

    if (resource.groups.length == 1) {
      cGraph.setParent(id, groupNode(resource.groups[0]));
    }
  }
}

function populateJobNodesAndEdges(cGraph, jobs) {
  // populate all job nodes first, so that they can be interconnected
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    var classes = ["job"];

    var status = "normal";
    var url = job.url;
    if (job.next_build) {
      status = job.next_build.status;
      url = job.next_build.url;
    } else if (job.finished_build) {
      status = job.finished_build.status;
      url = job.finished_build.url;
    }

    classes.push(status);

    cGraph.setNode(id, {
      class: classes.join(" "),
      status: status,
      label: "
" + job.name + "
",
      labelType: "html",
      groups: job.groups,
      totalInputs: job.inputs.length
    });

    if (job.groups.length == 1) {
      cGraph.setParent(id, groupNode(job.groups[0]));
    }
  }

  // populate job input and output edges
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    for (var j in job.inputs) {
      var input = job.inputs[j];

      if (input.hidden) {
        continue;
      }

      if (!input.passed || input.passed.length == 0) {
        edgeFromResource(cGraph, input.resource, id);
      } else if (input.passed.length == 1) {
        edgeFromJob(cGraph, input.passed[0], id, input.resource);
      } else {
        edgeFromGateway(cGraph, input.passed, id, input.resource);
      }
    }

    for (var j in job.outputs) {
      var output = job.outputs[j];
      var destinationNode = resourceNode(output.resource);

      edgeFromJob(cGraph, job.name, destinationNode);
    }
  }
}

function edgeFromResource(graph, resourceName, destinationNode) {
  var sourceNode = resourceNode(resourceName);

  graph.setEdge(sourceNode, destinationNode, {
    "id": "resource-"+sourceNode+"-to-"+destinationNode,
    "status": "normal",
    "arrowhead": "status",
  });
}

function edgeFromJob(graph, sourceJobName, destinationNode, resourceName) {
  var sourceNode = jobNode(sourceJobName);
  var sourceJob = graph.node(sourceNode);

  var existingEdge = graph.edge(sourceNode, destinationNode);
  if (existingEdge && resourceName && sourceJob.totalInputs > 1) {
    existingEdge.label += "\n" + resourceName
  } else {
    var label;
    if (sourceJob.totalInputs > 1) {
      label = resourceName;
    }

    graph.setEdge(sourceNode, destinationNode, {
      "id": "job-"+sourceNode+"-to-"+destinationNode,
      "label": label,
      "status": sourceJob.status,
      "arrowhead": "status",
    });
  }
}

function edgeFromGateway(graph, gatewayJobNames, destinationNode, label) {
  var sourceNode = gatewayNode(gatewayJobNames);

  graph.setNode(sourceNode, {
    label: "",
    gateway: true,
    class: "gateway"
  });

  for (var i in gatewayJobNames) {
    edgeFromJob(graph, gatewayJobNames[i], sourceNode, label);
  }

  graph.setEdge(sourceNode, destinationNode, {
    "id": "gateway-"+sourceNode+"-to-"+destinationNode,
    "status": "normal",
    "arrowhead": "status"
  });
}

function inlineNodesIntoCommonGroup(cGraph) {
  cGraph.nodes().forEach(function(v) {
    var commonGroup;

    var outE = cGraph.outEdges(v);
    for(var o in outE) {
      var edge = outE[o];
      var parent = cGraph.parent(edge.w);

      if (!commonGroup) {
        commonGroup = parent;
      }

      if (commonGroup != parent) {
        return
      }
    }

    var inE = cGraph.inEdges(v);
    for(var i in inE) {
      var edge = inE[i];
      var parent = cGraph.parent(edge.v);

      if (!commonGroup) {
        commonGroup = parent;
      }

      if (commonGroup != parent) {
        return
      }
    }

    if (commonGroup) {
      cGraph.setParent(v, commonGroup);
    }
  });
}

function removeUnconnectedGroupMembers(groups, digraph) {
  for (var group in groups) {
    var enabled = groups[group];
    if (enabled) {
      continue;
    }

    var id = groupNode(group);

    digraph.children(id).forEach(function(v) {
      var outE = digraph.outEdges(v);
      for(var o in outE) {
        var edge = outE[o];

        var targetValue = digraph.node(edge.w);
        if (nodeIsInGroups(groups, targetValue)) {
          return;
        }
      }

      var inE = digraph.inEdges(v);
      for(var i in inE) {
        var edge = inE[i];

        var sourceValue = digraph.node(edge.v);
        if (nodeIsInGroups(groups, sourceValue)) {
          return;
        }
      }

      digraph.removeNode(v);
    });
  }
}

function removeOrphanedNodes(digraph) {
  digraph.nodes().forEach(function(v) {
    if (dagreD3.util.isSubgraph(digraph, v)) {
      return;
    }

    if (digraph.parent(v)) {
      return;
    }

    if (digraph.nodeEdges(v).length == 0) {
      digraph.removeNode(v);
    }
  });
}

function nodeIsInGroups(groups, value) {
  if (!value.groups) {
    return false;
  }

  for(var i in value.groups) {
    if (groups[value.groups[i]]) {
      return true;
    }
  }

  return false;
}

function groupNode(name) {
  return "group-"+name;
}

function resourceNode(name) {
  return "resource-"+name;
}

function jobNode(name) {
  return "job-"+name;
}

function gatewayNode(jobNames) {
  return "gateway-"+jobNames.sort().join("-")
}
<commit_msg>improve gateway rendering<commit_after>function objectIsEmpty(obj) {
  for(var x in obj)
    return false;

  return true;
}

function generateGraph(groups, jobs, resources) {
  var cGraph = new dagreD3.graphlib.Graph({
    compound: true
  }).setGraph({
    rankDir: "LR"
  });

  populateGroupNodes(cGraph, groups);
  populateResourceNodes(cGraph, resources);
  populateJobNodesAndEdges(cGraph, jobs);
  inlineNodesIntoCommonGroup(cGraph);

  if (!objectIsEmpty(groups)) {
    removeUnconnectedGroupMembers(groups, cGraph);
    removeOrphanedNodes(cGraph);
  }

  return cGraph;
}

function populateGroupNodes(cGraph, groups) {
  for (var name in groups) {
    cGraph.setNode(groupNode(name), {});
  }
}

function populateResourceNodes(cGraph, resources) {
  for (var i in resources) {
    var resource = resources[i];
    var id = resourceNode(resource.name);

    var classes = ["resource"];

    if (resource.failing) {
      classes.push("failing");
    }

    if (resource.checking) {
      classes.push("checking")
    }

    cGraph.setNode(id, {
      resource: resource.name,
      class: classes.join(" "),
      label: "
" + resource.name + "
",
      labelType: "html",
      groups: resource.groups
    });

    if (resource.groups.length == 1) {
      cGraph.setParent(id, groupNode(resource.groups[0]));
    }
  }
}

function populateJobNodesAndEdges(cGraph, jobs) {
  // populate all job nodes first, so that they can be interconnected
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    var classes = ["job"];

    var status = "normal";
    var url = job.url;
    if (job.next_build) {
      status = job.next_build.status;
      url = job.next_build.url;
    } else if (job.finished_build) {
      status = job.finished_build.status;
      url = job.finished_build.url;
    }

    classes.push(status);

    cGraph.setNode(id, {
      job: job.name,
      class: classes.join(" "),
      status: status,
      label: "
" + job.name + "
",
      labelType: "html",
      groups: job.groups,
      totalInputs: job.inputs.length
    });

    if (job.groups.length == 1) {
      cGraph.setParent(id, groupNode(job.groups[0]));
    }
  }

  // populate job input and output edges
  for (var i in jobs) {
    var job = jobs[i];
    var id = jobNode(job.name);

    for (var j in job.inputs) {
      var input = job.inputs[j];

      if (input.hidden) {
        continue;
      }

      if (!input.passed || input.passed.length == 0) {
        edgeFromResource(cGraph, input.resource, id);
      } else if (input.passed.length == 1) {
        edgeFromJob(cGraph, input.passed[0], id, input.resource);
      } else {
        edgeFromGateway(cGraph, input.passed, id, input.resource);
      }
    }

    for (var j in job.outputs) {
      var output = job.outputs[j];
      var destinationNode = resourceNode(output.resource);

      edgeFromJob(cGraph, job.name, destinationNode);
    }
  }
}

function edgeFromResource(graph, resourceName, destinationNode) {
  var sourceNode = resourceNode(resourceName);

  graph.setEdge(sourceNode, destinationNode, {
    "id": "resource-"+sourceNode+"-to-"+destinationNode,
    "status": "normal",
    "arrowhead": "status",
  });
}

function edgeFromJob(graph, sourceJobName, destinationNode, resourceName) {
  var sourceNode = jobNode(sourceJobName);
  var sourceJob = graph.node(sourceNode);

  var existingEdge = graph.edge(sourceNode, destinationNode);
  if (existingEdge && resourceName && sourceJob.totalInputs > 1) {
    existingEdge.label += "\n" + resourceName
  } else {
    var label;
    if (sourceJob.totalInputs > 1) {
      label = resourceName;
    }

    graph.setEdge(sourceNode, destinationNode, {
      "id": "job-"+sourceNode+"-to-"+destinationNode,
      "label": label,
      "status": sourceJob.status,
      "arrowhead": "status",
    });
  }
}

function edgeFromGateway(graph, gatewayJobNames, destinationNode, label) {
  var sourceNode = gatewayNode(gatewayJobNames);

  graph.setNode(sourceNode, {
    label: "",
    gateway: true,
    class: "gateway"
  });

  for (var i in gatewayJobNames) {
    edgeFromJob(graph, gatewayJobNames[i], sourceNode, label);
  }

  graph.setEdge(sourceNode, destinationNode, {
    "id": "gateway-"+sourceNode+"-to-"+destinationNode,
    "status": "normal",
    "arrowhead": "status"
  });
}

function _nodes(graph, v, edgesFn, edgeAttr) {
  var nodes = {};

  var edges = graph[edgesFn](v);
  for (var i in edges) {
    var edge = edges[i];
    var edgePoint = edge[edgeAttr];

    var node = graph.node(edgePoint);
    if (node.gateway) {
      var gatewayNodes = _nodes(graph, edgePoint, edgesFn, edgeAttr);
      for (var n in gatewayNodes) {
        nodes[n] = gatewayNodes[n]
      }
    } else {
      nodes[edgePoint] = node
    }
  }

  return nodes;
}

function upstreamNodes(graph, v) {
  return _nodes(graph, v, "inEdges", "v");
}

function downstreamNodes(graph, v) {
  return _nodes(graph, v, "outEdges", "w");
}

function inlineNodesIntoCommonGroup(cGraph) {
  cGraph.nodes().forEach(function(v) {
    var commonGroup;

    var outNodes = downstreamNodes(cGraph, v);
    for(var o in outNodes) {
      var parent = cGraph.parent(o);

      if (!commonGroup) {
        commonGroup = parent;
      }

      if (commonGroup != parent) {
        return
      }
    }

    var inNodes = upstreamNodes(cGraph, v);
    for(var i in inNodes) {
      var parent = cGraph.parent(i);

      if (!commonGroup) {
        commonGroup = parent;
      }

      if (commonGroup != parent) {
        return
      }
    }

    if (commonGroup) {
      cGraph.setParent(v, commonGroup);
    }
  });
}

function removeUnconnectedGroupMembers(groups, digraph) {
  digraph.nodes().forEach(function(v) {
    if (dagreD3.util.isSubgraph(digraph, v)) {
      return;
    }

    var value = digraph.node(v);
    if (value.gateway && connectedUpstreamAndDownstream(digraph, groups, v)) {
      return;
    }

    if (connectedUpstreamOrDownstream(digraph, groups, v)) {
      return;
    }

    if (!nodeIsInGroups(groups, value)) {
      digraph.removeNode(v);
    }
  });
}

function connectedUpstreamAndDownstream(digraph, groups, v) {
  var hasUpstreamNode, hasDownstreamNode;

  var inNodes = upstreamNodes(digraph, v);
  for(var i in inNodes) {
    if (nodeIsInGroups(groups, inNodes[i])) {
      hasUpstreamNode = true;
      break;
    }
  }

  var outNodes = downstreamNodes(digraph, v);
  for(var o in outNodes) {
    if (nodeIsInGroups(groups, outNodes[o])) {
      hasDownstreamNode = true;
      break;
    }
  }

  return hasUpstreamNode && hasDownstreamNode;
}

function connectedUpstreamOrDownstream(digraph, groups, v) {
  var value = digraph.node(v);

  var inNodes = upstreamNodes(digraph, v);
  for(var i in inNodes) {
    if (nodeIsInGroups(groups, inNodes[i])) {
      return true;
    }
  }

  var outNodes = downstreamNodes(digraph, v);
  for(var o in outNodes) {
    if (nodeIsInGroups(groups, outNodes[o])) {
      return true;
    }
  }

  return false;
}

function removeOrphanedNodes(digraph) {
  digraph.nodes().forEach(function(v) {
    if (dagreD3.util.isSubgraph(digraph, v)) {
      return;
    }

    if (!digraph.node(v).gateway && digraph.parent(v)) {
      return;
    }

    if (digraph.nodeEdges(v).length == 0) {
      digraph.removeNode(v);
    }
  });
}

function nodeIsInGroups(groups, value) {
  if (!value.groups) {
    return false;
  }

  for(var i in value.groups) {
    if (groups[value.groups[i]]) {
      return true;
    }
  }

  return false;
}

function groupNode(name) {
  return "group-"+name;
}

function resourceNode(name) {
  return "resource-"+name;
}

function jobNode(name) {
  return "job-"+name;
}

function gatewayNode(jobNames) {
  return "gateway-"+jobNames.sort().join("-")
}
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
// Creator:   Dominik
// Date:      29.11.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

m_require('core/foundation/object.js');

/**
 * @class
 *
 * M.I18N defines a prototype for for internationalisation and localisation within
 * The M-Project. It is used to set and get the application's language and to
 * localize any string within an application.
 *
 * @extends M.Object
 */
M.I18N = M.Object.extend(
/** @scope M.I18N.prototype */ {

    /**
     * The type of this object.
     *
     * @type String
     */
    type: 'M.I18N',

    /**
     * The system's default language.
     *
     * @type String
     */
    defaultLanguage: 'en_us',

    /**
     * This property is used to map the navigator's language to an ISO standard
     * if necessary. E.g. 'de' will be mapped to 'de_de'. Currently we only provide
     * support for english and german. More languages are about to come or can be
     * added by overwriting this property.
     *
     * @type Object
     */
    languageMapper: {
        de: 'de_de',
        en: 'en_us'
    },

    /**
     * This method returns the localized string for the given key based on
     * the current language.
     *
     * @param {String} key The key to the localized string.
     * @returns {String} The localizes string based on the current application language.
     */
    l: function(key) {
        return this.localize(key);
    },

    /**
     * This method returns the localized string for the given key based on
     * the current language. It is internally used as a wrapper for l() for
     * a better readability.
     *
     * @private
     * @param {String} key The key to the localized string.
     * @returns {String} The localizes string based on the current application language.
     */
    localize: function(key) {
        if(!M.Application.currentLanguage) {
            M.Application.currentLanguage = this.getLanguage();
        }

        if(this[M.Application.currentLanguage] && this[M.Application.currentLanguage][key]) {
            return this[M.Application.currentLanguage][key];
        } else if(this[M.Application.defaultLanguage] && this[M.Application.defaultLanguage][key]) {
            M.Logger.log('Key \'' + key + '\' not defined for language \'' + M.Application.currentLanguage + '\', switched to default language \'' + M.Application.defaultLanguage + '\'', M.WARN);
            return this[M.Application.defaultLanguage][key];
        }  else if(this[this.defaultLanguage] && this[this.defaultLanguage][key]) {
            M.Logger.log('Key \'' + key + '\' not defined for language \'' + M.Application.currentLanguage + '\', switched to system\'s default language \'' + this.defaultLanguage + '\'', M.WARN);
            return this[this.defaultLanguage][key];
        } else {
            M.Logger.log('Key \'' + key + '\' not defined for both language \'' + M.Application.currentLanguage + '\' and the system\'s default language \'' + this.defaultLanguage + '\'', M.WARN);
        }

    },

    /**
     * This method sets the applications current language and forces it to reload.
     *
     * @param {String} language The language to be set.
     */
    setLanguage: function(language) {
        if(!this.isLanguageAvailable(language)) {
            M.Logger.log('There is no language \'' + language + '\' specified!', M.WARN);
            return;
        } else if(language && language === M.Application.currentLanguage) {
            M.Logger.log('Language \'' + language + '\' already selected', M.INFO);
            return;
        }

        if(localStorage) {
            localStorage.setItem('$' + M.Application.name + '_lang$', language);
            location.hash = '';
            location.reload();
        }
    },

    /**
     * This method is used to get a language for the current user. This process is divided
     * into three steps. If one step leads to a language, this on is returned. The steps are
     * prioritized as follows:
     *
     * - get the user's language by checking his navigator
     * - use the application's default language
     * - use the systems's default language
     *
     * @returns {String} The user's language.
     */
    getLanguage: function() {
        var language = null;

        if(localStorage) {
            language = localStorage.getItem('$' + M.Application.name + '_lang$');
        }

        if(language) {
            return language;
        } else if(navigator) {
            var regexResult = /([a-zA-Z]{2,3})[\s_.-]?([a-zA-Z]{2,3})?/.exec(navigator.language);
            if(regexResult && this[regexResult[0]]) {
                return regexResult[0].toLowerCase();
            } else if(regexResult && regexResult[1] && this.languageMapper[regexResult[1]]) {
                var language = this.languageMapper[regexResult[1]];
                return language.toLowerCase();
            } else if(M.Application.defaultLanguage) {
                return M.Application.defaultLanguage.toLowerCase();
            }
        } else {
            return this.defaultLanguage;
        }
    },

    /**
     * This method checks if the passed language is available within the app or not.
     *
     * @param {String} language The language to be checked.
     * @returns {Boolean} Indicates whether the requested language is available or not.
     */
    isLanguageAvailable: function(language) {
        if(this[language] && typeof(this[language]) === 'object') {
            return true;
        } else {
            M.Logger.log('no language \'' + language + '\' specified.', M.WARN);
            return false;
        }
    }

});<commit_msg>refactoring: do not reset the location's hash since this should be part of the application, not part and job of the framework.<commit_after>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
// Creator:   Dominik
// Date:      29.11.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

m_require('core/foundation/object.js');

/**
 * @class
 *
 * M.I18N defines a prototype for for internationalisation and localisation within
 * The M-Project. It is used to set and get the application's language and to
 * localize any string within an application.
 *
 * @extends M.Object
 */
M.I18N = M.Object.extend(
/** @scope M.I18N.prototype */ {

    /**
     * The type of this object.
     *
     * @type String
     */
    type: 'M.I18N',

    /**
     * The system's default language.
     *
     * @type String
     */
    defaultLanguage: 'en_us',

    /**
     * This property is used to map the navigator's language to an ISO standard
     * if necessary. E.g. 'de' will be mapped to 'de_de'. Currently we only provide
     * support for english and german. More languages are about to come or can be
     * added by overwriting this property.
     *
     * @type Object
     */
    languageMapper: {
        de: 'de_de',
        en: 'en_us'
    },

    /**
     * This method returns the localized string for the given key based on
     * the current language.
     *
     * @param {String} key The key to the localized string.
     * @returns {String} The localizes string based on the current application language.
     */
    l: function(key) {
        return this.localize(key);
    },

    /**
     * This method returns the localized string for the given key based on
     * the current language. It is internally used as a wrapper for l() for
     * a better readability.
     *
     * @private
     * @param {String} key The key to the localized string.
     * @returns {String} The localizes string based on the current application language.
     */
    localize: function(key) {
        if(!M.Application.currentLanguage) {
            M.Application.currentLanguage = this.getLanguage();
        }

        if(this[M.Application.currentLanguage] && this[M.Application.currentLanguage][key]) {
            return this[M.Application.currentLanguage][key];
        } else if(this[M.Application.defaultLanguage] && this[M.Application.defaultLanguage][key]) {
            M.Logger.log('Key \'' + key + '\' not defined for language \'' + M.Application.currentLanguage + '\', switched to default language \'' + M.Application.defaultLanguage + '\'', M.WARN);
            return this[M.Application.defaultLanguage][key];
        }  else if(this[this.defaultLanguage] && this[this.defaultLanguage][key]) {
            M.Logger.log('Key \'' + key + '\' not defined for language \'' + M.Application.currentLanguage + '\', switched to system\'s default language \'' + this.defaultLanguage + '\'', M.WARN);
            return this[this.defaultLanguage][key];
        } else {
            M.Logger.log('Key \'' + key + '\' not defined for both language \'' + M.Application.currentLanguage + '\' and the system\'s default language \'' + this.defaultLanguage + '\'', M.WARN);
        }

    },

    /**
     * This method sets the applications current language and forces it to reload.
     *
     * @param {String} language The language to be set.
     */
    setLanguage: function(language) {
        if(!this.isLanguageAvailable(language)) {
            M.Logger.log('There is no language \'' + language + '\' specified!', M.WARN);
            return;
        } else if(language && language === M.Application.currentLanguage) {
            M.Logger.log('Language \'' + language + '\' already selected', M.INFO);
            return;
        }

        if(localStorage) {
            localStorage.setItem('$' + M.Application.name + '_lang$', language);
            location.reload();
        }
    },

    /**
     * This method is used to get a language for the current user. This process is divided
     * into three steps. If one step leads to a language, this on is returned. The steps are
     * prioritized as follows:
     *
     * - get the user's language by checking his navigator
     * - use the application's default language
     * - use the systems's default language
     *
     * @returns {String} The user's language.
     */
    getLanguage: function() {
        var language = null;

        if(localStorage) {
            language = localStorage.getItem('$' + M.Application.name + '_lang$');
        }

        if(language) {
            return language;
        } else if(navigator) {
            var regexResult = /([a-zA-Z]{2,3})[\s_.-]?([a-zA-Z]{2,3})?/.exec(navigator.language);
            if(regexResult && this[regexResult[0]]) {
                return regexResult[0].toLowerCase();
            } else if(regexResult && regexResult[1] && this.languageMapper[regexResult[1]]) {
                var language = this.languageMapper[regexResult[1]];
                return language.toLowerCase();
            } else if(M.Application.defaultLanguage) {
                return M.Application.defaultLanguage.toLowerCase();
            }
        } else {
            return this.defaultLanguage;
        }
    },

    /**
     * This method checks if the passed language is available within the app or not.
     *
     * @param {String} language The language to be checked.
     * @returns {Boolean} Indicates whether the requested language is available or not.
     */
    isLanguageAvailable: function(language) {
        if(this[language] && typeof(this[language]) === 'object') {
            return true;
        } else {
            M.Logger.log('no language \'' + language + '\' specified.', M.WARN);
            return false;
        }
    }

});<|endoftext|>


<commit_before>const { Command } = require('discord.js-commando');

const Currency = require('../../structures/currency/Currency');
const Inventory = require('../../structures/currency/Inventory');
const ItemGroup = require('../../structures/currency/ItemGroup');

module.exports = class ItemTradeCommand extends Command {
	constructor(client) {
		super(client, {
			name: 'item-trade',
			aliases: ['trade-items', 'trade-item', 'items-trade'],
			group: 'item',
			memberName: 'trade',
			description: `Trade items with another user.`,
			guildOnly: true,
			throttling: {
				usages: 2,
				duration: 3
			},

			args: [
				{
					key: 'member',
					prompt: 'what user would you like to give your item(s)?\n',
					type: 'member'
				},
				{
					key: 'offerItem',
					prompt: 'what item would you like to trade?\n',
					type: 'string'
				},
				{
					key: 'offerAmount',
					label: 'amount of items to give',
					prompt: 'how many would you like to trade?\n',
					type: 'integer',
					min: 1
				},
				{
					key: 'receiveItem',
					prompt: 'what item would you like to receive?\n',
					type: 'string'
				},
				{
					key: 'receiveAmount',
					label: 'amount of items to receive',
					prompt: 'how many items would you like to receive?\n',
					type: 'integer',
					min: 1
				}
			]
		});
	}

	async run(msg, args) {
		const { user, offerAmount, receiveAmount } = args;
		const offerItem = this.isDonuts(args.offerItem, offerAmount);
		const receiveItem = this.isDonuts(args.receiveItem, receiveAmount);

		if (user.id === msg.author.id) return msg.reply('what are you trying to achieve by trading with yourself?');
		if (user.user.bot) return msg.reply('bots got nothing to trade, man.');
		if (!offerItem && !receiveItem) return msg.reply("you can't trade donuts for donuts.");

		const offerBalance = Currency.getBalance(msg.author.id);
		const receiveBalance = Currency.getBalance(user.id);
		const offerInv = Inventory.fetchInventory(msg.author.id);
		const receiveInv = Inventory.fetchInventory(user.id);
		const offerItemBalance = offerInv.content[offerItem] ? offerInv.content[offerItem].amount : null;
		const receiveItemBalance = receiveInv.content[receiveItem] ? receiveInv.content[receiveItem].amount : null;
		const offerValidation = this.validate(offerItem, offerAmount, offerBalance, offerItemBalance, 'you');
		const receiveValidation = this.validate(receiveItem, receiveAmount, receiveBalance, receiveItemBalance, user.displayName); // eslint-disable-line max-len
		if (offerValidation) return msg.reply(offerValidation);
		if (receiveValidation) return msg.reply(receiveValidation);

		const embed = {
			title: `${msg.member.displayName} < -- > ${user.displayName}`,
			description: 'Type `accept` within the next 30 seconds to accept this trade.',
			fields: [
				{
					name: msg.member.displayName,
					value: offerItem
						? `${offerAmount} ${offerItem}`
						: Currency.convert(offerAmount)
				},
				{
					name: user.displayName,
					value: receiveItem
						? `${receiveAmount} ${receiveItem}`
						: Currency.convert(receiveAmount)
				}
			]
		};

		if (!await this.response(msg, user, embed)) return msg.reply(`${user.displayName} declined or failed to respond.`);
		if (!offerItem) this.sendDonuts(msg.author, user, offerAmount);
		else this.sendItems(offerInv, receiveInv, offerItem, offerAmount);
		if (!receiveItem) this.sendDonuts(user, msg.author, receiveAmount);
		else this.sendItems(receiveInv, offerInv, receiveItem, receiveAmount);
		return msg.say('Trade successful.');
	}

	validate(item, amount, balance, itemBalance, user) {
		const person = user === 'you';
		if (item) {
			if (!itemBalance) return `${user} ${person ? "don't" : "doesn't"} have any ${item}s.`;
			if (amount > itemBalance) return `${user} ${person ? 'have' : 'has'} only ${itemBalance} ${item}s.`;
		} else if (amount > balance) {
			return `${user} ${person ? 'have' : 'has'} only ${Currency.convert(balance)}`;
		}

		return null;
	}

	isDonuts(item, amount) {
		if (/donuts?/.test(item)) return null;

		return ItemGroup.convert(item, amount);
	}

	sendItems(fromInventory, toInventory, item, amount) {
		const itemGroup = new ItemGroup(item, amount);
		fromInventory.removeItems(itemGroup);
		toInventory.addItems(itemGroup);
	}

	sendDonuts(fromUser, toUser, amount) {
		Currency.removeBalance(fromUser, amount);
		Currency.addBalance(toUser, amount);
	}

	async response(msg, user, embed) {
		msg.say(`${user}, ${msg.member.displayName} wants to trade with you.`);
		msg.embed(embed);
		const responses = await msg.channel.awaitMessages(response =>
			response.author.id === user.id && response.content.toLowerCase() === 'accept',
			{
				maxMatches: 1,
				time: 30e3
			});

		if (responses.size === 0) return false;
		return true;
	}
};
<commit_msg>Fix trade.js also<commit_after>const { Command } = require('discord.js-commando');

const Currency = require('../../structures/currency/Currency');
const Inventory = require('../../structures/currency/Inventory');
const ItemGroup = require('../../structures/currency/ItemGroup');

module.exports = class ItemTradeCommand extends Command {
	constructor(client) {
		super(client, {
			name: 'item-trade',
			aliases: ['trade-items', 'trade-item', 'items-trade'],
			group: 'item',
			memberName: 'trade',
			description: `Trade items with another user.`,
			guildOnly: true,
			throttling: {
				usages: 2,
				duration: 3
			},

			args: [
				{
					key: 'member',
					prompt: 'what user would you like to give your item(s)?\n',
					type: 'member'
				},
				{
					key: 'offerItem',
					prompt: 'what item would you like to trade?\n',
					type: 'string'
				},
				{
					key: 'offerAmount',
					label: 'amount of items to give',
					prompt: 'how many would you like to trade?\n',
					type: 'integer',
					min: 1
				},
				{
					key: 'receiveItem',
					prompt: 'what item would you like to receive?\n',
					type: 'string'
				},
				{
					key: 'receiveAmount',
					label: 'amount of items to receive',
					prompt: 'how many items would you like to receive?\n',
					type: 'integer',
					min: 1
				}
			]
		});
	}

	async run(msg, args) {
		const { member, offerAmount, receiveAmount } = args;
		const offerItem = this.isDonuts(args.offerItem, offerAmount);
		const receiveItem = this.isDonuts(args.receiveItem, receiveAmount);

		if (member.id === msg.author.id) return msg.reply('what are you trying to achieve by trading with yourself?');
		if (member.user.bot) return msg.reply('bots got nothing to trade, man.');
		if (!offerItem && !receiveItem) return msg.reply("you can't trade donuts for donuts.");

		const offerBalance = Currency.getBalance(msg.author.id);
		const receiveBalance = Currency.getBalance(member.id);
		const offerInv = Inventory.fetchInventory(msg.author.id);
		const receiveInv = Inventory.fetchInventory(member.id);
		const offerItemBalance = offerInv.content[offerItem] ? offerInv.content[offerItem].amount : null;
		const receiveItemBalance = receiveInv.content[receiveItem] ? receiveInv.content[receiveItem].amount : null;
		const offerValidation = this.validate(offerItem, offerAmount, offerBalance, offerItemBalance, 'you');
		const receiveValidation = this.validate(receiveItem, receiveAmount, receiveBalance, receiveItemBalance, member.displayName); // eslint-disable-line max-len
		if (offerValidation) return msg.reply(offerValidation);
		if (receiveValidation) return msg.reply(receiveValidation);

		const embed = {
			title: `${msg.member.displayName} < -- > ${member.displayName}`,
			description: 'Type `accept` within the next 30 seconds to accept this trade.',
			fields: [
				{
					name: msg.member.displayName,
					value: offerItem
						? `${offerAmount} ${offerItem}`
						: Currency.convert(offerAmount)
				},
				{
					name: member.displayName,
					value: receiveItem
						? `${receiveAmount} ${receiveItem}`
						: Currency.convert(receiveAmount)
				}
			]
		};

		if (!await this.response(msg, member, embed)) return msg.reply(`${member.displayName} declined or failed to respond.`); // eslint-disable-line
		if (!offerItem) this.sendDonuts(msg.author, member, offerAmount);
		else this.sendItems(offerInv, receiveInv, offerItem, offerAmount);
		if (!receiveItem) this.sendDonuts(member, msg.author, receiveAmount);
		else this.sendItems(receiveInv, offerInv, receiveItem, receiveAmount);
		return msg.say('Trade successful.');
	}

	validate(item, amount, balance, itemBalance, user) {
		const person = user === 'you';
		if (item) {
			if (!itemBalance) return `${user} ${person ? "don't" : "doesn't"} have any ${item}s.`;
			if (amount > itemBalance) return `${user} ${person ? 'have' : 'has'} only ${itemBalance} ${item}s.`;
		} else if (amount > balance) {
			return `${user} ${person ? 'have' : 'has'} only ${Currency.convert(balance)}`;
		}

		return null;
	}

	isDonuts(item, amount) {
		if (/donuts?/.test(item)) return null;

		return ItemGroup.convert(item, amount);
	}

	sendItems(fromInventory, toInventory, item, amount) {
		const itemGroup = new ItemGroup(item, amount);
		fromInventory.removeItems(itemGroup);
		toInventory.addItems(itemGroup);
	}

	sendDonuts(fromUser, toUser, amount) {
		Currency.removeBalance(fromUser, amount);
		Currency.addBalance(toUser, amount);
	}

	async response(msg, user, embed) {
		msg.say(`${user}, ${msg.member.displayName} wants to trade with you.`);
		msg.embed(embed);
		const responses = await msg.channel.awaitMessages(response =>
			response.author.id === user.id && response.content.toLowerCase() === 'accept',
			{
				maxMatches: 1,
				time: 30e3
			});

		if (responses.size === 0) return false;
		return true;
	}
};
<|endoftext|>


<commit_before>var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Essay Better Maker' });
});

router.post('/essayParse', function(req, res, next){
  console.log(req.body.inputTxt)
  res.render('essayOutput', { title: 'Essay Better Maker' , output: 'testOutput'})
})

module.exports = router;
<commit_msg>Crucial fix<commit_after>var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Essay Better Maker' });
});

router.post('/essayParse', function(req, res, next){
  console.log(req.body.inputTxt)
  res.render('essayOutput', { title: 'Essay Better Maker' , output: req.body.inputTxt})
})

module.exports = router;
<|endoftext|>


<commit_before>/**
 * Created by andreas on 18.05.14.
 */

avnav.provide('avnav.map.NavLayer');
var NavCompute=require('../nav/navcompute');
var navobjects=require('../nav/navobjects');
var NavData=require('../nav/navobjects');

/**
 * a cover for the layer that contaisn the booat, the current wp and the route between them
 * @param {avnav.map.MapHolder} mapholder
 * @param {NavData} navobject
 * @constructor
 */
avnav.map.NavLayer=function(mapholder,navobject){
    var self=this;
    /**
     * @private
     * @type {avnav.map.MapHolder}
     */
    this.mapholder=mapholder;
    /**
     * @private
     * @type {NavData}
     */
    this.navobject=navobject;

    var self=this;
    /**
     * the last boat course
     * @private
     * @type {number}
     */
    this.lastBoatCourse=0;
    /**
     * the initial course must be different from 0 to create a style...
     * @private
     * @type {ol.style.Style}
     */
    this.boatStyle={
        anchor: [15, 72],
        size: [30,120],
        src: 'images/Boat2.png',
        rotation: 20/180*Math.PI,
        rotateWithView: true,
        image: new Image()
    };
    this.boatStyle.image.src=this.boatStyle.src;

    /**
     * @private
     * @type {ol.style.Style}
     */
    this.circleStyle={};
    this.setStyle();

    /**
     * the properties for the center marker
     * @private
     * @type {{anchor: number[], size: number[], anchorXUnits: string, anchorYUnits: string, opacity: number, src: string, image:Image}}
     */
    this.centerStyle={
        anchor: [20, 20],
        size: [40, 40],
        src: 'images/Marker2.png',
        image: new Image()
    };
    this.centerStyle.image.src=this.centerStyle.src;

    /**
     * the boat position in map coordinates
     * @type {ol.Coordinate}
     */
    this.boatPosition=[0,0];

    $(document).on(avnav.util.PropertyChangeEvent.EVENT_TYPE, function(ev,evdata){
        self.propertyChange(evdata);
    });
};

/**
 * set the style(s)
 * @private
 */
avnav.map.NavLayer.prototype.setStyle=function() {
    this.circleStyle={
            color: this.mapholder.properties.getProperties().navCircleColor,
            width: this.mapholder.properties.getProperties().navCircleWidth
    };
};

/**
 * draw the marker and course
 * we rely on the move end to really set the marker pos to the cookie and to the navobject
 * @param {ol.Coordinate} center in map coordinates
 * @param {avnav.map.Drawing} drawing
 */
avnav.map.NavLayer.prototype.onPostCompose=function(center,drawing){
    var prop=this.mapholder.getProperties().getProperties();
    if (prop.layers.boat) {
        drawing.drawImageToContext(this.boatPosition, this.boatStyle.image, this.boatStyle);
        var pos = this.boatPosition;
        var other;
        if (prop.navCircle1Radius > 10) {
            other = this.computeTarget(pos, this.lastBoatCourse, prop.navCircle1Radius);
            drawing.drawCircleToContext(pos, other, this.circleStyle);
        }
        if (prop.navCircle2Radius > 10 && prop.navCircle2Radius > prop.navCircle1Radius) {
            other = this.computeTarget(pos, this.lastBoatCourse, prop.navCircle2Radius);
            drawing.drawCircleToContext(pos, other, this.circleStyle);
        }
        if (prop.navCircle3Radius > 10 && prop.navCircle3Radius > prop.navCircle2Radius && prop.navCircle3Radius > prop.navCircle2Radius) {
            other = this.computeTarget(pos, this.lastBoatCourse, prop.navCircle3Radius);
            drawing.drawCircleToContext(pos, other, this.circleStyle);
        }
    }
    if (!this.mapholder.getGpsLock()) {
        drawing.drawImageToContext(center, this.centerStyle.image, this.centerStyle);
    }

};

/**
 * compute a target point in map units from a given point
 * for drawing the circles
 * assumes "flatted" area around the point
 * @param {ol.Coordinate} pos in map coordinates
 * @param {number} course in degrees
 * @param {number} dist in m
 */
avnav.map.NavLayer.prototype.computeTarget=function(pos,course,dist){
    var point=new navobjects.Point();
    point.fromCoord(this.mapholder.transformFromMap(pos));
    var tp=NavCompute.computeTarget(point,course,dist);
    var tpmap=this.mapholder.transformToMap(tp.toCoord());
    return tpmap;
};

/**
 * set the boat position
 * @param {ol.Coordinate} pos
 * @param {number} course
 */
avnav.map.NavLayer.prototype.setBoatPosition=function(pos,course) {
    this.boatStyle.rotation=course*Math.PI/180;
    this.lastBoatCourse=course;
    this.boatPosition = this.mapholder.transformToMap(pos);
};

avnav.map.NavLayer.prototype.propertyChange=function(evdata){
    this.setStyle();
};
<commit_msg>closes #29: correctly check circle radiuses<commit_after>/**
 * Created by andreas on 18.05.14.
 */

avnav.provide('avnav.map.NavLayer');
var NavCompute=require('../nav/navcompute');
var navobjects=require('../nav/navobjects');
var NavData=require('../nav/navobjects');

/**
 * a cover for the layer that contaisn the booat, the current wp and the route between them
 * @param {avnav.map.MapHolder} mapholder
 * @param {NavData} navobject
 * @constructor
 */
avnav.map.NavLayer=function(mapholder,navobject){
    var self=this;
    /**
     * @private
     * @type {avnav.map.MapHolder}
     */
    this.mapholder=mapholder;
    /**
     * @private
     * @type {NavData}
     */
    this.navobject=navobject;

    var self=this;
    /**
     * the last boat course
     * @private
     * @type {number}
     */
    this.lastBoatCourse=0;
    /**
     * the initial course must be different from 0 to create a style...
     * @private
     * @type {ol.style.Style}
     */
    this.boatStyle={
        anchor: [15, 72],
        size: [30,120],
        src: 'images/Boat2.png',
        rotation: 20/180*Math.PI,
        rotateWithView: true,
        image: new Image()
    };
    this.boatStyle.image.src=this.boatStyle.src;

    /**
     * @private
     * @type {ol.style.Style}
     */
    this.circleStyle={};
    this.setStyle();

    /**
     * the properties for the center marker
     * @private
     * @type {{anchor: number[], size: number[], anchorXUnits: string, anchorYUnits: string, opacity: number, src: string, image:Image}}
     */
    this.centerStyle={
        anchor: [20, 20],
        size: [40, 40],
        src: 'images/Marker2.png',
        image: new Image()
    };
    this.centerStyle.image.src=this.centerStyle.src;

    /**
     * the boat position in map coordinates
     * @type {ol.Coordinate}
     */
    this.boatPosition=[0,0];

    $(document).on(avnav.util.PropertyChangeEvent.EVENT_TYPE, function(ev,evdata){
        self.propertyChange(evdata);
    });
};

/**
 * set the style(s)
 * @private
 */
avnav.map.NavLayer.prototype.setStyle=function() {
    this.circleStyle={
            color: this.mapholder.properties.getProperties().navCircleColor,
            width: this.mapholder.properties.getProperties().navCircleWidth
    };
};

/**
 * draw the marker and course
 * we rely on the move end to really set the marker pos to the cookie and to the navobject
 * @param {ol.Coordinate} center in map coordinates
 * @param {avnav.map.Drawing} drawing
 */
avnav.map.NavLayer.prototype.onPostCompose=function(center,drawing){
    var prop=this.mapholder.getProperties().getProperties();
    if (prop.layers.boat) {
        drawing.drawImageToContext(this.boatPosition, this.boatStyle.image, this.boatStyle);
        var pos = this.boatPosition;
        var other;
        var radius1=parseInt(prop.navCircle1Radius);
        if (radius1 > 10) {
            other = this.computeTarget(pos, this.lastBoatCourse, radius1);
            drawing.drawCircleToContext(pos, other, this.circleStyle);
        }
        var radius2=parseInt(prop.navCircle2Radius);
        if (radius2 > 10 && radius2 > radius1) {
            other = this.computeTarget(pos, this.lastBoatCourse, radius2);
            drawing.drawCircleToContext(pos, other, this.circleStyle);
        }
        var radius3=parseInt(prop.navCircle3Radius);
        if (radius3 > 10 && radius3 > radius2 && radius3 > radius1) {
            other = this.computeTarget(pos, this.lastBoatCourse, radius3);
            drawing.drawCircleToContext(pos, other, this.circleStyle);
        }
    }
    if (!this.mapholder.getGpsLock()) {
        drawing.drawImageToContext(center, this.centerStyle.image, this.centerStyle);
    }

};

/**
 * compute a target point in map units from a given point
 * for drawing the circles
 * assumes "flatted" area around the point
 * @param {ol.Coordinate} pos in map coordinates
 * @param {number} course in degrees
 * @param {number} dist in m
 */
avnav.map.NavLayer.prototype.computeTarget=function(pos,course,dist){
    var point=new navobjects.Point();
    point.fromCoord(this.mapholder.transformFromMap(pos));
    var tp=NavCompute.computeTarget(point,course,dist);
    var tpmap=this.mapholder.transformToMap(tp.toCoord());
    return tpmap;
};

/**
 * set the boat position
 * @param {ol.Coordinate} pos
 * @param {number} course
 */
avnav.map.NavLayer.prototype.setBoatPosition=function(pos,course) {
    this.boatStyle.rotation=course*Math.PI/180;
    this.lastBoatCourse=course;
    this.boatPosition = this.mapholder.transformToMap(pos);
};

avnav.map.NavLayer.prototype.propertyChange=function(evdata){
    this.setStyle();
};
<|endoftext|>


<commit_before>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
// Creator:   Dominik
// Date:      29.11.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

m_require('core/foundation/object.js');

/**
 * @class
 *
 * M.I18N defines a prototype for for internationalisation and localisation within
 * The M-Project. It is used to set and get the application's language and to
 * localize any string within an application.
 *
 * @extends M.Object
 */
M.I18N = M.Object.extend(
/** @scope M.I18N.prototype */ {

    /**
     * The type of this object.
     *
     * @type String
     */
    type: 'M.I18N',

    /**
     * The system's default language.
     *
     * @type String
     */
    defaultLanguage: 'en_us',

    /**
     * This property is used to map the navigator's language to an ISO standard
     * if necessary. E.g. 'de' will be mapped to 'de_de'. Currently we only provide
     * support for english and german. More languages are about to come or can be
     * added by overwriting this property.
     *
     * @type Object
     */
    languageMapper: {
        de: 'de_de',
        en: 'en_us'
    },

    /**
     * This method returns the localized string for the given key based on
     * the current language.
     *
     * @param {String} key The key to the localized string.
     * @returns {String} The localizes string based on the current application language.
     */
    l: function(key) {
        return this.localize(key);
    },

    /**
     * This method returns the localized string for the given key based on
     * the current language. It is internally used as a wrapper for l() for
     * a better readability.
     *
     * @private
     * @param {String} key The key to the localized string.
     * @returns {String} The localizes string based on the current application language.
     */
    localize: function(key) {
        if(!M.Application.currentLanguage) {
            M.Application.currentLanguage = this.getLanguage();
        }

        if(this[M.Application.currentLanguage] && this[M.Application.currentLanguage][key]) {
            return this[M.Application.currentLanguage][key];
        } else if(this[M.Application.defaultLanguage] && this[M.Application.defaultLanguage][key]) {
            M.Logger.log('Key \'' + key + '\' not defined for language \'' + M.Application.currentLanguage + '\', switched to default language \'' + M.Application.defaultLanguage + '\'', M.WARN);
            return this[M.Application.defaultLanguage][key];
        }  else if(this[this.defaultLanguage] && this[this.defaultLanguage][key]) {
            M.Logger.log('Key \'' + key + '\' not defined for language \'' + M.Application.currentLanguage + '\', switched to system\'s default language \'' + this.defaultLanguage + '\'', M.WARN);
            return this[this.defaultLanguage][key];
        } else {
            M.Logger.log('Key \'' + key + '\' not defined for both language \'' + M.Application.currentLanguage + '\' and the system\'s default language \'' + this.defaultLanguage + '\'', M.WARN);
        }

    },

    /**
     * This method sets the applications current language and forces it to reload.
     *
     * @param {String} language The language to be set.
     */
    setLanguage: function(language) {
        if(!this.isLanguageAvailable(language)) {
            M.Logger.log('There is no language \'' + language + '\' specified!', M.WARN);
            return;
        } else if(language && language === M.Application.currentLanguage) {
            M.Logger.log('Language \'' + language + '\' already selected', M.INFO);
            return;
        }

        if(localStorage) {
            localStorage.setItem('$' + M.Application.name + '_lang$', language);
            location.href = location.protocol + '//' + location.pathname;
        }
    },

    /**
     * This method is used to get a language for the current user. This process is divided
     * into three steps. If one step leads to a language, this on is returned. The steps are
     * prioritized as follows:
     *
     * - get the user's language by checking his navigator
     * - use the application's default language
     * - use the systems's default language
     *
     * @returns {String} The user's language.
     */
    getLanguage: function() {
        var language = null;

        if(localStorage) {
            language = localStorage.getItem('$' + M.Application.name + '_lang$');
        }

        if(language) {
            return language;
        } else if(navigator) {
            var regexResult = /([a-zA-Z]{2,3})[\s_.-]?([a-zA-Z]{2,3})?/.exec(navigator.language);
            if(regexResult && this[regexResult[0]]) {
                return regexResult[0].toLowerCase();
            } else if(regexResult && regexResult[1] && this.languageMapper[regexResult[1]]) {
                var language = this.languageMapper[regexResult[1]];
                return language.toLowerCase();
            } else if(M.Application.defaultLanguage) {
                return M.Application.defaultLanguage.toLowerCase();
            }
        } else {
            return this.defaultLanguage;
        }
    },

    /**
     * This method checks if the passed language is available within the app or not.
     *
     * @param {String} language The language to be checked.
     * @returns {Boolean} Indicates whether the requested language is available or not.
     */
    isLanguageAvailable: function(language) {
        if(this[language] && typeof(this[language]) === 'object') {
            return true;
        } else {
            M.Logger.log('no language \'' + language + '\' specified.', M.WARN);
            return false;
        }
    }

});<commit_msg>Fixed setLanguage to reload the correct url (location.host was missing)<commit_after>// ==========================================================================
// Project:   The M-Project - Mobile HTML5 Application Framework
// Copyright: (c) 2010 M-Way Solutions GmbH. All rights reserved.
// Creator:   Dominik
// Date:      29.11.2010
// License:   Dual licensed under the MIT or GPL Version 2 licenses.
//            http://github.com/mwaylabs/The-M-Project/blob/master/MIT-LICENSE
//            http://github.com/mwaylabs/The-M-Project/blob/master/GPL-LICENSE
// ==========================================================================

m_require('core/foundation/object.js');

/**
 * @class
 *
 * M.I18N defines a prototype for for internationalisation and localisation within
 * The M-Project. It is used to set and get the application's language and to
 * localize any string within an application.
 *
 * @extends M.Object
 */
M.I18N = M.Object.extend(
/** @scope M.I18N.prototype */ {

    /**
     * The type of this object.
     *
     * @type String
     */
    type: 'M.I18N',

    /**
     * The system's default language.
     *
     * @type String
     */
    defaultLanguage: 'en_us',

    /**
     * This property is used to map the navigator's language to an ISO standard
     * if necessary. E.g. 'de' will be mapped to 'de_de'. Currently we only provide
     * support for english and german. More languages are about to come or can be
     * added by overwriting this property.
     *
     * @type Object
     */
    languageMapper: {
        de: 'de_de',
        en: 'en_us'
    },

    /**
     * This method returns the localized string for the given key based on
     * the current language.
     *
     * @param {String} key The key to the localized string.
     * @returns {String} The localizes string based on the current application language.
     */
    l: function(key) {
        return this.localize(key);
    },

    /**
     * This method returns the localized string for the given key based on
     * the current language. It is internally used as a wrapper for l() for
     * a better readability.
     *
     * @private
     * @param {String} key The key to the localized string.
     * @returns {String} The localizes string based on the current application language.
     */
    localize: function(key) {
        if(!M.Application.currentLanguage) {
            M.Application.currentLanguage = this.getLanguage();
        }

        if(this[M.Application.currentLanguage] && this[M.Application.currentLanguage][key]) {
            return this[M.Application.currentLanguage][key];
        } else if(this[M.Application.defaultLanguage] && this[M.Application.defaultLanguage][key]) {
            M.Logger.log('Key \'' + key + '\' not defined for language \'' + M.Application.currentLanguage + '\', switched to default language \'' + M.Application.defaultLanguage + '\'', M.WARN);
            return this[M.Application.defaultLanguage][key];
        }  else if(this[this.defaultLanguage] && this[this.defaultLanguage][key]) {
            M.Logger.log('Key \'' + key + '\' not defined for language \'' + M.Application.currentLanguage + '\', switched to system\'s default language \'' + this.defaultLanguage + '\'', M.WARN);
            return this[this.defaultLanguage][key];
        } else {
            M.Logger.log('Key \'' + key + '\' not defined for both language \'' + M.Application.currentLanguage + '\' and the system\'s default language \'' + this.defaultLanguage + '\'', M.WARN);
        }

    },

    /**
     * This method sets the applications current language and forces it to reload.
     *
     * @param {String} language The language to be set.
     */
    setLanguage: function(language) {
        if(!this.isLanguageAvailable(language)) {
            M.Logger.log('There is no language \'' + language + '\' specified!', M.WARN);
            return;
        } else if(language && language === M.Application.currentLanguage) {
            M.Logger.log('Language \'' + language + '\' already selected', M.INFO);
            return;
        }

        if(localStorage) {
            localStorage.setItem('$' + M.Application.name + '_lang$', language);
            location.href = location.protocol + '//' + location.host + location.pathname;
        }
    },

    /**
     * This method is used to get a language for the current user. This process is divided
     * into three steps. If one step leads to a language, this on is returned. The steps are
     * prioritized as follows:
     *
     * - get the user's language by checking his navigator
     * - use the application's default language
     * - use the systems's default language
     *
     * @returns {String} The user's language.
     */
    getLanguage: function() {
        var language = null;

        if(localStorage) {
            language = localStorage.getItem('$' + M.Application.name + '_lang$');
        }

        if(language) {
            return language;
        } else if(navigator) {
            var regexResult = /([a-zA-Z]{2,3})[\s_.-]?([a-zA-Z]{2,3})?/.exec(navigator.language);
            if(regexResult && this[regexResult[0]]) {
                return regexResult[0].toLowerCase();
            } else if(regexResult && regexResult[1] && this.languageMapper[regexResult[1]]) {
                var language = this.languageMapper[regexResult[1]];
                return language.toLowerCase();
            } else if(M.Application.defaultLanguage) {
                return M.Application.defaultLanguage.toLowerCase();
            }
        } else {
            return this.defaultLanguage;
        }
    },

    /**
     * This method checks if the passed language is available within the app or not.
     *
     * @param {String} language The language to be checked.
     * @returns {Boolean} Indicates whether the requested language is available or not.
     */
    isLanguageAvailable: function(language) {
        if(this[language] && typeof(this[language]) === 'object') {
            return true;
        } else {
            M.Logger.log('no language \'' + language + '\' specified.', M.WARN);
            return false;
        }
    }

});<|endoftext|>


<commit_before>$(document).ready(function () {
    var t = window.openerp.test_support;
    function context_(c) {
        return _.extend({ lang: 'en_US', tz: 'UTC', uid: 87539319 }, c);
    }

    t.module('Dataset shortcuts', 'data');
    t.test('read_index', function (openerp) {
        var ds = new openerp.web.DataSet(
            {session: openerp.connection}, 'some.model');
        ds.ids = [10, 20, 30, 40, 50];
        ds.index = 2;
        t.expect(ds.read_index(['a', 'b', 'c']), function (result) {
            strictEqual(result.method, 'read');
            strictEqual(result.model, 'some.model');

            strictEqual(result.args.length, 3);
            deepEqual(result.args[0], [30]);
            deepEqual(result.args[1], ['a', 'b', 'c']);
            deepEqual(result.args[2], context_());

            ok(_.isEmpty(result.kwargs));
        });
    });
    t.test('default_get', function (openerp) {
        var ds = new openerp.web.DataSet(
            {session: openerp.connection}, 'some.model', {foo: 'bar'});
        t.expect(ds.default_get(['a', 'b', 'c']), function (result) {
            strictEqual(result.method, 'default_get');
            strictEqual(result.model, 'some.model');

            strictEqual(result.args.length, 2);
            deepEqual(result.args[0], ['a', 'b', 'c']);
            console.log(result.args[1]);
            console.log(context_({foo: 'bar'}));
            deepEqual(result.args[1], context_({foo: 'bar'}));

            ok(_.isEmpty(result.kwargs));
        });
    });
    t.test('create', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'some.model');
        t.expect(ds.create({foo: 1, bar: 2}), function (r) {
            strictEqual(r.method, 'create');

            strictEqual(r.args.length, 2);
            deepEqual(r.args[0], {foo: 1, bar: 2});
            deepEqual(r.args[1], context_());

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('write', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.write(42, {foo: 1}), function (r) {
            strictEqual(r.method, 'write');

            strictEqual(r.args.length, 3);
            deepEqual(r.args[0], [42]);
            deepEqual(r.args[1], {foo: 1});
            deepEqual(r.args[2], context_());

            ok(_.isEmpty(r.kwargs));
        });
        // FIXME: can't run multiple sessions in the same test(), fucks everything up
//        t.expect(ds.write(42, {foo: 1}, { context: {lang: 'bob'} }), function (r) {
//            strictEqual(r.args.length, 3);
//            strictEqual(r.args[2].lang, 'bob');
//        });
    });
    t.test('unlink', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.unlink([42]), function (r) {
            strictEqual(r.method, 'unlink');

            strictEqual(r.args.length, 2);
            deepEqual(r.args[0], [42]);
            deepEqual(r.args[1], context_());

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('call', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.call('frob', ['a', 'b', 42]), function (r) {
            strictEqual(r.method, 'frob');

            strictEqual(r.args.length, 3);
            deepEqual(r.args, ['a', 'b', 42]);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_get', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.name_get([1, 2], null), function (r) {
            strictEqual(r.method, 'name_get');

            strictEqual(r.args.length, 2);
            deepEqual(r.args[0], [1, 2]);
            deepEqual(r.args[1], context_());

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_search, name', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.name_search('bob'), function (r) {
            strictEqual(r.method, 'name_search');

            strictEqual(r.args.length, 5);
            strictEqual(r.args[0], 'bob');
            // domain
            deepEqual(r.args[1], []);
            strictEqual(r.args[2], 'ilike');
            deepEqual(r.args[3], context_());
            strictEqual(r.args[4], 0);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_search, domain & operator', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.name_search(0, [['foo', '=', 3]], 'someop'), function (r) {
            strictEqual(r.method, 'name_search');

            strictEqual(r.args.length, 5);
            strictEqual(r.args[0], '');
            // domain
            deepEqual(r.args[1], [['foo', '=', 3]]);
            strictEqual(r.args[2], 'someop');
            deepEqual(r.args[3], context_());
            // limit
            strictEqual(r.args[4], 0);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('exec_workflow', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.exec_workflow(42, 'foo'), function (r) {
            strictEqual(r['service'], 'object');
            strictEqual(r.method, 'exec_workflow');

            // db, id, password, model, method, id
            strictEqual(r.args.length, 6);
            strictEqual(r.args[4], 'foo');
            strictEqual(r.args[5], 42);
        });
    });

    t.test('DataSetSearch#read_slice', function (openerp) {
        var ds = new openerp.web.DataSetSearch({session: openerp.connection}, 'mod');
        t.expect(ds.read_slice(['foo', 'bar'], {
            domain: [['foo', '>', 42], ['qux', '=', 'grault']],
            context: {peewee: 'herman'},
            offset: 160,
            limit: 80
        }), function (r) {
            strictEqual(r.method, 'search');

            strictEqual(r.args.length, 5);
            deepEqual(r.args[0], [['foo', '>', 42], ['qux', '=', 'grault']]);
            strictEqual(r.args[1], 160);
            strictEqual(r.args[2], 80);
            strictEqual(r.args[3], false);
            strictEqual(r.args[4].peewee, 'herman');

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('DataSetSearch#read_slice sorted', function (openerp) {
        var ds = new openerp.web.DataSetSearch({session: openerp.connection}, 'mod');
        ds.sort('foo');
        ds.sort('foo');
        ds.sort('bar');
        t.expect(ds.read_slice(['foo', 'bar'], { }), function (r) {
            strictEqual(r.method, 'search');

            strictEqual(r.args.length, 5);
            deepEqual(r.args[0], []);
            strictEqual(r.args[1], 0);
            strictEqual(r.args[2], false);
            strictEqual(r.args[3], 'bar ASC, foo DESC');
            deepEqual(r.args[4], context_());

            ok(_.isEmpty(r.kwargs));
        });
    });

    t.module('Nonliterals', 'data', {
        domains: [
            "[('model_id', '=', parent.model)]",
            "[('product_id','=',product_id)]"
        ],
        contexts: ['{a: b > c}']
    });
    t.test('Dataset', function (openerp) {
        var ds = new openerp.web.DataSetSearch(
            {session: openerp.connection}, 'mod');
        var c = new openerp.web.CompoundContext(
            {a: 'foo', b: 3, c: 5}, openerp.contexts[0]);
        t.expect(ds.read_slice(['foo', 'bar'], {
            context: c
        }), function (r) {
            strictEqual(r.method, 'search');

            deepEqual(r.args[4], context_({
                foo: false,
                a: 'foo',
                b: 3,
                c: 5
            }));

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_search', function (openerp) {
        var eval_context = {
            active_id: 42,
            active_ids: [42],
            active_model: 'mod',
            parent: {model: 'qux'}
        };
        var ds = new openerp.web.DataSet(
            {session: openerp.connection}, 'mod',
             new openerp.web.CompoundContext({})
                 .set_eval_context(eval_context));
        var domain = new openerp.web.CompoundDomain(openerp.domains[0])
                .set_eval_context(eval_context);
        t.expect(ds.name_search('foo', domain, 'ilike', 0), function (r) {
            strictEqual(r.method, 'name_search');

            strictEqual(r.args.length, 5);
            strictEqual(r.args[0], 'foo');
            deepEqual(r.args[1], [['model_id', '=', 'qux']]);
            strictEqual(r.args[2], 'ilike');
            deepEqual(r.args[3], context_());
            strictEqual(r.args[4], 0);

            ok(_.isEmpty(r.kwargs));
        });
    });
    // TODO: non-literal domains and contexts basics
    // TODO: call_and_eval
    // TODO: name_search, non-literal domains

});
<commit_msg>[REM] todos<commit_after>$(document).ready(function () {
    var t = window.openerp.test_support;
    function context_(c) {
        return _.extend({ lang: 'en_US', tz: 'UTC', uid: 87539319 }, c);
    }

    t.module('Dataset shortcuts', 'data');
    t.test('read_index', function (openerp) {
        var ds = new openerp.web.DataSet(
            {session: openerp.connection}, 'some.model');
        ds.ids = [10, 20, 30, 40, 50];
        ds.index = 2;
        t.expect(ds.read_index(['a', 'b', 'c']), function (result) {
            strictEqual(result.method, 'read');
            strictEqual(result.model, 'some.model');

            strictEqual(result.args.length, 3);
            deepEqual(result.args[0], [30]);
            deepEqual(result.args[1], ['a', 'b', 'c']);
            deepEqual(result.args[2], context_());

            ok(_.isEmpty(result.kwargs));
        });
    });
    t.test('default_get', function (openerp) {
        var ds = new openerp.web.DataSet(
            {session: openerp.connection}, 'some.model', {foo: 'bar'});
        t.expect(ds.default_get(['a', 'b', 'c']), function (result) {
            strictEqual(result.method, 'default_get');
            strictEqual(result.model, 'some.model');

            strictEqual(result.args.length, 2);
            deepEqual(result.args[0], ['a', 'b', 'c']);
            console.log(result.args[1]);
            console.log(context_({foo: 'bar'}));
            deepEqual(result.args[1], context_({foo: 'bar'}));

            ok(_.isEmpty(result.kwargs));
        });
    });
    t.test('create', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'some.model');
        t.expect(ds.create({foo: 1, bar: 2}), function (r) {
            strictEqual(r.method, 'create');

            strictEqual(r.args.length, 2);
            deepEqual(r.args[0], {foo: 1, bar: 2});
            deepEqual(r.args[1], context_());

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('write', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.write(42, {foo: 1}), function (r) {
            strictEqual(r.method, 'write');

            strictEqual(r.args.length, 3);
            deepEqual(r.args[0], [42]);
            deepEqual(r.args[1], {foo: 1});
            deepEqual(r.args[2], context_());

            ok(_.isEmpty(r.kwargs));
        });
        // FIXME: can't run multiple sessions in the same test(), fucks everything up
//        t.expect(ds.write(42, {foo: 1}, { context: {lang: 'bob'} }), function (r) {
//            strictEqual(r.args.length, 3);
//            strictEqual(r.args[2].lang, 'bob');
//        });
    });
    t.test('unlink', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.unlink([42]), function (r) {
            strictEqual(r.method, 'unlink');

            strictEqual(r.args.length, 2);
            deepEqual(r.args[0], [42]);
            deepEqual(r.args[1], context_());

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('call', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.call('frob', ['a', 'b', 42]), function (r) {
            strictEqual(r.method, 'frob');

            strictEqual(r.args.length, 3);
            deepEqual(r.args, ['a', 'b', 42]);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_get', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.name_get([1, 2], null), function (r) {
            strictEqual(r.method, 'name_get');

            strictEqual(r.args.length, 2);
            deepEqual(r.args[0], [1, 2]);
            deepEqual(r.args[1], context_());

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_search, name', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.name_search('bob'), function (r) {
            strictEqual(r.method, 'name_search');

            strictEqual(r.args.length, 5);
            strictEqual(r.args[0], 'bob');
            // domain
            deepEqual(r.args[1], []);
            strictEqual(r.args[2], 'ilike');
            deepEqual(r.args[3], context_());
            strictEqual(r.args[4], 0);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_search, domain & operator', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.name_search(0, [['foo', '=', 3]], 'someop'), function (r) {
            strictEqual(r.method, 'name_search');

            strictEqual(r.args.length, 5);
            strictEqual(r.args[0], '');
            // domain
            deepEqual(r.args[1], [['foo', '=', 3]]);
            strictEqual(r.args[2], 'someop');
            deepEqual(r.args[3], context_());
            // limit
            strictEqual(r.args[4], 0);

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('exec_workflow', function (openerp) {
        var ds = new openerp.web.DataSet({session: openerp.connection}, 'mod');
        t.expect(ds.exec_workflow(42, 'foo'), function (r) {
            strictEqual(r['service'], 'object');
            strictEqual(r.method, 'exec_workflow');

            // db, id, password, model, method, id
            strictEqual(r.args.length, 6);
            strictEqual(r.args[4], 'foo');
            strictEqual(r.args[5], 42);
        });
    });

    t.test('DataSetSearch#read_slice', function (openerp) {
        var ds = new openerp.web.DataSetSearch({session: openerp.connection}, 'mod');
        t.expect(ds.read_slice(['foo', 'bar'], {
            domain: [['foo', '>', 42], ['qux', '=', 'grault']],
            context: {peewee: 'herman'},
            offset: 160,
            limit: 80
        }), function (r) {
            strictEqual(r.method, 'search');

            strictEqual(r.args.length, 5);
            deepEqual(r.args[0], [['foo', '>', 42], ['qux', '=', 'grault']]);
            strictEqual(r.args[1], 160);
            strictEqual(r.args[2], 80);
            strictEqual(r.args[3], false);
            strictEqual(r.args[4].peewee, 'herman');

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('DataSetSearch#read_slice sorted', function (openerp) {
        var ds = new openerp.web.DataSetSearch({session: openerp.connection}, 'mod');
        ds.sort('foo');
        ds.sort('foo');
        ds.sort('bar');
        t.expect(ds.read_slice(['foo', 'bar'], { }), function (r) {
            strictEqual(r.method, 'search');

            strictEqual(r.args.length, 5);
            deepEqual(r.args[0], []);
            strictEqual(r.args[1], 0);
            strictEqual(r.args[2], false);
            strictEqual(r.args[3], 'bar ASC, foo DESC');
            deepEqual(r.args[4], context_());

            ok(_.isEmpty(r.kwargs));
        });
    });

    t.module('Nonliterals', 'data', {
        domains: [
            "[('model_id', '=', parent.model)]",
            "[('product_id','=',product_id)]"
        ],
        contexts: ['{a: b > c}']
    });
    t.test('Dataset', function (openerp) {
        var ds = new openerp.web.DataSetSearch(
            {session: openerp.connection}, 'mod');
        var c = new openerp.web.CompoundContext(
            {a: 'foo', b: 3, c: 5}, openerp.contexts[0]);
        t.expect(ds.read_slice(['foo', 'bar'], {
            context: c
        }), function (r) {
            strictEqual(r.method, 'search');

            deepEqual(r.args[4], context_({
                foo: false,
                a: 'foo',
                b: 3,
                c: 5
            }));

            ok(_.isEmpty(r.kwargs));
        });
    });
    t.test('name_search', function (openerp) {
        var eval_context = {
            active_id: 42,
            active_ids: [42],
            active_model: 'mod',
            parent: {model: 'qux'}
        };
        var ds = new openerp.web.DataSet(
            {session: openerp.connection}, 'mod',
             new openerp.web.CompoundContext({})
                 .set_eval_context(eval_context));
        var domain = new openerp.web.CompoundDomain(openerp.domains[0])
                .set_eval_context(eval_context);
        t.expect(ds.name_search('foo', domain, 'ilike', 0), function (r) {
            strictEqual(r.method, 'name_search');

            strictEqual(r.args.length, 5);
            strictEqual(r.args[0], 'foo');
            deepEqual(r.args[1], [['model_id', '=', 'qux']]);
            strictEqual(r.args[2], 'ilike');
            deepEqual(r.args[3], context_());
            strictEqual(r.args[4], 0);

            ok(_.isEmpty(r.kwargs));
        });
    });
});
<|endoftext|>