<commit_before>import { PixelRatio } from "react-native";

import variable from "./../variables/platform";

export default (variables = variable) => {
  const platformStyle = variables.platformStyle;
  const platform = variables.platform;

  const headerTheme = {
    ".span": {
      height: 128,
      "NativeBase.Left": {
        alignSelf: "flex-start",
      },
      "NativeBase.Body": {
        alignSelf: "flex-end",
        alignItems: "flex-start",
        justifyContent: "center",
        paddingBottom: 26,
      },
      "NativeBase.Right": {
        alignSelf: "flex-start",
      },
    },
    ".hasSubtitle": {
      "NativeBase.Body": {
        "NativeBase.Title": {
          fontSize: variables.titleFontSize - 2,
          fontFamily: variables.titleFontfamily,
          textAlign: "center",
        },
        "NativeBase.Subtitle": {
          fontSize: variables.subTitleFontSize,
          fontFamily: variables.titleFontfamily,
          color: variables.subtitleColor,
          textAlign: "center",
        },
      },
    },
    ".noShadow": {
      elevation: 0,
      shadowColor: null,
      shadowOffset: null,
      shadowRadius: null,
      shadowOpacity: null,
    },
    ".hasTabs": {
      elevation: 0,
      shadowColor: null,
      shadowOffset: null,
      shadowRadius: null,
      shadowOpacity: null,
      borderBottomWidth: null,
    },
    ".hasSegment": {
      elevation: 0,
      shadowColor: null,
      shadowOffset: null,
      shadowRadius: null,
      shadowOpacity: null,
      borderBottomWidth: null,
      "NativeBase.Left": {
        flex: 0.3,
      },
      "NativeBase.Right": {
        flex: 0.3,
      },
      "NativeBase.Body": {
        flex: 1,
        "NativeBase.Segment": {
          marginRight: 0,
          alignSelf: 'center',
          "NativeBase.Button": {
            paddingLeft: 0,
            paddingRight: 0
          }
        },
      },
    },
    "NativeBase.Button": {
      justifyContent: "center",
      alignSelf: "center",
      alignItems: "center",
      ".transparent": {
        "NativeBase.Text": {
          color: variables.toolbarBtnTextColor,
          fontWeight: "600",
        },
        "NativeBase.Icon": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.IconNB": {
          color: variables.toolbarBtnColor,
        },
        paddingHorizontal: variables.buttonPadding,
      },
      paddingHorizontal: 15,
    },
    ".searchBar": {
      "NativeBase.Item": {
        "NativeBase.Icon": {
          backgroundColor: "transparent",
          color: variables.dropdownLinkColor,
          fontSize: variables.toolbarSearchIconSize,
          alignItems: "center",
          marginTop: 2,
          paddingRight: 10,
          paddingLeft: 10,
        },
        "NativeBase.IconNB": {
          backgroundColor: "transparent",
          color: null,
          alignSelf: "center",
        },
        "NativeBase.Input": {
          alignSelf: "center",
          lineHeight: 24,
          height: variables.searchBarInputHeight,
        },
        alignSelf: "center",
        alignItems: "center",
        justifyContent: "flex-start",
        flex: 1,
        height: variables.searchBarHeight,
        borderColor: "transparent",
        backgroundColor: variables.toolbarInputColor,
      },
      "NativeBase.Button": {
        ".transparent": {
          "NativeBase.Text": {
            fontWeight: "500",
          },
          paddingHorizontal: null,
          paddingLeft: platform === "ios" ? 10 : null,
        },
        paddingHorizontal: platform === "ios" ? undefined : null,
        width: platform === "ios" ? undefined : 0,
        height: platform === "ios" ? undefined : 0,
      },
    },
    ".rounded": {
      "NativeBase.Item": {
        borderRadius: platform === "ios" && platformStyle !== "material" ? 25 : 3,
      },
    },
    "NativeBase.Left": {
      "NativeBase.Button": {
        ".hasText": {
          marginLeft: -10,
          height: 30,
          "NativeBase.Icon": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize,
            marginTop: 2,
            marginRight: 5,
            marginLeft: 2,
          },
          "NativeBase.Text": {
            color: variables.toolbarBtnTextColor,
            fontSize: 17,
            marginLeft: 2,
            lineHeight: 21,
          },
          "NativeBase.IconNB": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize,
            marginTop: 2,
            marginRight: 5,
            marginLeft: 2,
          },
        },
        ".transparent": {
          marginLeft: -3,
          "NativeBase.Icon": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize,
            marginTop: 2,
            marginRight: 2,
            marginLeft: 2,
          },
          "NativeBase.IconNB": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize,
            marginTop: 2,
            marginRight: 2,
            marginLeft: 2,
          },
          "NativeBase.Text": {
            color: variables.toolbarBtnTextColor,
            fontSize: 17,
            top: platform === "ios" ? undefined : -1.5,
          },
          backgroundColor: "transparent",
          borderColor: null,
          elevation: 0,
          shadowColor: null,
          shadowOffset: null,
          shadowRadius: null,
          shadowOpacity: null,
        },
        "NativeBase.Icon": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.IconNB": {
          color: variables.toolbarBtnColor,
        },
        alignSelf: null,
        paddingHorizontal: variables.buttonPadding,
      },
      flex: platform === "ios" && platformStyle !== "material" ? 1 : 0.5,
      alignSelf: "center",
      alignItems: "flex-start",
    },
    "NativeBase.Body": {
      flex: 1,
      alignItems: platform === "ios" && platformStyle !== "material" ? "center" : "flex-start",
      alignSelf: "center",
      "NativeBase.Segment": {
        borderWidth: 0,
        alignSelf: "flex-end",
        marginRight: platform === "ios" ? -40 : -55,
      },
      "NativeBase.Button": {
        alignSelf: "center",
        ".transparent": {
          backgroundColor: "transparent",
        },
        "NativeBase.Icon": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.IconNB": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.Text": {
          color: variables.inverseTextColor,
          backgroundColor: "transparent",
        },
      },
    },
    "NativeBase.Right": {
      "NativeBase.Button": {
        ".hasText": {
          height: 30,
          "NativeBase.Icon": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize - 2,
            marginTop: 2,
            marginRight: 2,
            marginLeft: 5,
          },
          "NativeBase.Text": {
            color: variables.toolbarBtnTextColor,
            fontSize: 17,
            lineHeight: 21,
          },
          "NativeBase.IconNB": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize - 2,
            marginTop: 2,
            marginRight: 2,
            marginLeft: 5,
          },
        },
        ".transparent": {
          marginRight: -8,
          paddingHorizontal: 15,
          borderRadius: 50,
          "NativeBase.Icon": {
            color: variables.toolbarBtnColor,
            fontSize:
              platform === "ios" ? variables.iconHeaderSize - 6 : variables.iconHeaderSize - 2,
            marginTop: 2,
            marginLeft: 2,
            marginRight: 2,
          },
          "NativeBase.IconNB": {
            color: variables.toolbarBtnColor,
            fontSize:
              platform === "ios" ? variables.iconHeaderSize - 6 : variables.iconHeaderSize - 2,
            marginTop: 2,
            marginLeft: 2,
            marginRight: 2,
          },
          "NativeBase.Text": {
            color: variables.toolbarBtnTextColor,
            fontSize: 17,
            top: platform === "ios" ? undefined : -1.5,
          },
          backgroundColor: "transparent",
          borderColor: null,
          elevation: 0,
          shadowColor: null,
          shadowOffset: null,
          shadowRadius: null,
          shadowOpacity: null,
        },
        "NativeBase.Icon": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.IconNB": {
          color: variables.toolbarBtnColor,
        },
        alignSelf: null,
        paddingHorizontal: variables.buttonPadding,
      },
      flex: 1,
      alignSelf: "center",
      alignItems: "flex-end",
      flexDirection: "row",
      justifyContent: "flex-end",
    },
    backgroundColor: variables.toolbarDefaultBg,
    flexDirection: "row",
    paddingHorizontal: 10,
    justifyContent: "center",
    paddingTop: platform === "ios" ? (variables.isIphoneX ? 39 : 15) : 0,
    borderBottomWidth: platform === "ios" ? 1 / PixelRatio.getPixelSizeForLayoutSize(1) : 0,
    borderBottomColor: variables.toolbarDefaultBorder,
    height: variables.toolbarHeight,
    elevation: 3,
    shadowColor: platformStyle === "material" ? "#000" : undefined,
    shadowOffset: platformStyle === "material" ? { width: 0, height: 2 } : undefined,
    shadowOpacity: platformStyle === "material" ? 0.2 : undefined,
    shadowRadius: platformStyle === "material" ? 1.2 : undefined,
    top: 0,
    left: 0,
    right: 0,
  };

  return headerTheme;
};
<commit_msg>Added StatusBar height to Header height for platformStyle 'material' on iOS device<commit_after>import { PixelRatio, StatusBar } from "react-native";

import variable from "./../variables/platform";

export default (variables = variable) => {
  const platformStyle = variables.platformStyle;
  const platform = variables.platform;

  const headerTheme = {
    ".span": {
      height: 128,
      "NativeBase.Left": {
        alignSelf: "flex-start",
      },
      "NativeBase.Body": {
        alignSelf: "flex-end",
        alignItems: "flex-start",
        justifyContent: "center",
        paddingBottom: 26,
      },
      "NativeBase.Right": {
        alignSelf: "flex-start",
      },
    },
    ".hasSubtitle": {
      "NativeBase.Body": {
        "NativeBase.Title": {
          fontSize: variables.titleFontSize - 2,
          fontFamily: variables.titleFontfamily,
          textAlign: "center",
        },
        "NativeBase.Subtitle": {
          fontSize: variables.subTitleFontSize,
          fontFamily: variables.titleFontfamily,
          color: variables.subtitleColor,
          textAlign: "center",
        },
      },
    },
    ".noShadow": {
      elevation: 0,
      shadowColor: null,
      shadowOffset: null,
      shadowRadius: null,
      shadowOpacity: null,
    },
    ".hasTabs": {
      elevation: 0,
      shadowColor: null,
      shadowOffset: null,
      shadowRadius: null,
      shadowOpacity: null,
      borderBottomWidth: null,
    },
    ".hasSegment": {
      elevation: 0,
      shadowColor: null,
      shadowOffset: null,
      shadowRadius: null,
      shadowOpacity: null,
      borderBottomWidth: null,
      "NativeBase.Left": {
        flex: 0.3,
      },
      "NativeBase.Right": {
        flex: 0.3,
      },
      "NativeBase.Body": {
        flex: 1,
        "NativeBase.Segment": {
          marginRight: 0,
          alignSelf: 'center',
          "NativeBase.Button": {
            paddingLeft: 0,
            paddingRight: 0
          }
        },
      },
    },
    "NativeBase.Button": {
      justifyContent: "center",
      alignSelf: "center",
      alignItems: "center",
      ".transparent": {
        "NativeBase.Text": {
          color: variables.toolbarBtnTextColor,
          fontWeight: "600",
        },
        "NativeBase.Icon": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.IconNB": {
          color: variables.toolbarBtnColor,
        },
        paddingHorizontal: variables.buttonPadding,
      },
      paddingHorizontal: 15,
    },
    ".searchBar": {
      "NativeBase.Item": {
        "NativeBase.Icon": {
          backgroundColor: "transparent",
          color: variables.dropdownLinkColor,
          fontSize: variables.toolbarSearchIconSize,
          alignItems: "center",
          marginTop: 2,
          paddingRight: 10,
          paddingLeft: 10,
        },
        "NativeBase.IconNB": {
          backgroundColor: "transparent",
          color: null,
          alignSelf: "center",
        },
        "NativeBase.Input": {
          alignSelf: "center",
          lineHeight: 24,
          height: variables.searchBarInputHeight,
        },
        alignSelf: "center",
        alignItems: "center",
        justifyContent: "flex-start",
        flex: 1,
        height: variables.searchBarHeight,
        borderColor: "transparent",
        backgroundColor: variables.toolbarInputColor,
      },
      "NativeBase.Button": {
        ".transparent": {
          "NativeBase.Text": {
            fontWeight: "500",
          },
          paddingHorizontal: null,
          paddingLeft: platform === "ios" ? 10 : null,
        },
        paddingHorizontal: platform === "ios" ? undefined : null,
        width: platform === "ios" ? undefined : 0,
        height: platform === "ios" ? undefined : 0,
      },
    },
    ".rounded": {
      "NativeBase.Item": {
        borderRadius: platform === "ios" && platformStyle !== "material" ? 25 : 3,
      },
    },
    "NativeBase.Left": {
      "NativeBase.Button": {
        ".hasText": {
          marginLeft: -10,
          height: 30,
          "NativeBase.Icon": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize,
            marginTop: 2,
            marginRight: 5,
            marginLeft: 2,
          },
          "NativeBase.Text": {
            color: variables.toolbarBtnTextColor,
            fontSize: 17,
            marginLeft: 2,
            lineHeight: 21,
          },
          "NativeBase.IconNB": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize,
            marginTop: 2,
            marginRight: 5,
            marginLeft: 2,
          },
        },
        ".transparent": {
          marginLeft: -3,
          "NativeBase.Icon": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize,
            marginTop: 2,
            marginRight: 2,
            marginLeft: 2,
          },
          "NativeBase.IconNB": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize,
            marginTop: 2,
            marginRight: 2,
            marginLeft: 2,
          },
          "NativeBase.Text": {
            color: variables.toolbarBtnTextColor,
            fontSize: 17,
            top: platform === "ios" ? undefined : -1.5,
          },
          backgroundColor: "transparent",
          borderColor: null,
          elevation: 0,
          shadowColor: null,
          shadowOffset: null,
          shadowRadius: null,
          shadowOpacity: null,
        },
        "NativeBase.Icon": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.IconNB": {
          color: variables.toolbarBtnColor,
        },
        alignSelf: null,
        paddingHorizontal: variables.buttonPadding,
      },
      flex: platform === "ios" && platformStyle !== "material" ? 1 : 0.5,
      alignSelf: "center",
      alignItems: "flex-start",
    },
    "NativeBase.Body": {
      flex: 1,
      alignItems: platform === "ios" && platformStyle !== "material" ? "center" : "flex-start",
      alignSelf: "center",
      "NativeBase.Segment": {
        borderWidth: 0,
        alignSelf: "flex-end",
        marginRight: platform === "ios" ? -40 : -55,
      },
      "NativeBase.Button": {
        alignSelf: "center",
        ".transparent": {
          backgroundColor: "transparent",
        },
        "NativeBase.Icon": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.IconNB": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.Text": {
          color: variables.inverseTextColor,
          backgroundColor: "transparent",
        },
      },
    },
    "NativeBase.Right": {
      "NativeBase.Button": {
        ".hasText": {
          height: 30,
          "NativeBase.Icon": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize - 2,
            marginTop: 2,
            marginRight: 2,
            marginLeft: 5,
          },
          "NativeBase.Text": {
            color: variables.toolbarBtnTextColor,
            fontSize: 17,
            lineHeight: 21,
          },
          "NativeBase.IconNB": {
            color: variables.toolbarBtnColor,
            fontSize: variables.iconHeaderSize - 2,
            marginTop: 2,
            marginRight: 2,
            marginLeft: 5,
          },
        },
        ".transparent": {
          marginRight: -8,
          paddingHorizontal: 15,
          borderRadius: 50,
          "NativeBase.Icon": {
            color: variables.toolbarBtnColor,
            fontSize:
              platform === "ios" ? variables.iconHeaderSize - 6 : variables.iconHeaderSize - 2,
            marginTop: 2,
            marginLeft: 2,
            marginRight: 2,
          },
          "NativeBase.IconNB": {
            color: variables.toolbarBtnColor,
            fontSize:
              platform === "ios" ? variables.iconHeaderSize - 6 : variables.iconHeaderSize - 2,
            marginTop: 2,
            marginLeft: 2,
            marginRight: 2,
          },
          "NativeBase.Text": {
            color: variables.toolbarBtnTextColor,
            fontSize: 17,
            top: platform === "ios" ? undefined : -1.5,
          },
          backgroundColor: "transparent",
          borderColor: null,
          elevation: 0,
          shadowColor: null,
          shadowOffset: null,
          shadowRadius: null,
          shadowOpacity: null,
        },
        "NativeBase.Icon": {
          color: variables.toolbarBtnColor,
        },
        "NativeBase.IconNB": {
          color: variables.toolbarBtnColor,
        },
        alignSelf: null,
        paddingHorizontal: variables.buttonPadding,
      },
      flex: 1,
      alignSelf: "center",
      alignItems: "flex-end",
      flexDirection: "row",
      justifyContent: "flex-end",
    },
    backgroundColor: variables.toolbarDefaultBg,
    flexDirection: "row",
    paddingHorizontal: 10,
    justifyContent: "center",
    paddingTop: platform === "ios" ? (variables.isIphoneX ? 39 : 15) : 0,
    borderBottomWidth: platform === "ios" ? 1 / PixelRatio.getPixelSizeForLayoutSize(1) : 0,
    borderBottomColor: variables.toolbarDefaultBorder,
    height: variables.platform === "ios" && variables.platformStyle === "material" ? (variables.toolbarHeight + StatusBar.height) : variables.toolbarHeight,
    elevation: 3,
    shadowColor: platformStyle === "material" ? "#000" : undefined,
    shadowOffset: platformStyle === "material" ? { width: 0, height: 2 } : undefined,
    shadowOpacity: platformStyle === "material" ? 0.2 : undefined,
    shadowRadius: platformStyle === "material" ? 1.2 : undefined,
    top: 0,
    left: 0,
    right: 0,
  };

  return headerTheme;
};
<|endoftext|>


<commit_before>var GridList = function(items, options) {
  /**
   * A GridList manages a sorted list of rectangular items with variable width
   * and height.
   *
   * The GridList has two functions:
   *
   *  - Generate the 2d position based on the sorting index
   *  - TODO: Generate the sorting index based on the 2d position
   *
   * The 2d position is generated under the constraints of N rows and M columns
   * per Group (see below). The virtual grid space will expand horizontally to
   * any size required to fit all the items provided.
   *
   * A Group is a positional subset of the Grid, of variable columns. These are
   * the specs of the positioning algorithm:
   *
   *  - Groups are displayed one after another, from left to right.
   *  - Items withing a group are displayed left-to-right, top-to-bottom
   *  - If an item doesn't fit to the right of the previous item within the
   *    current Group, it will go under it
   *  - If an item doesn't fit under the previous item within the current
   *    Group, it will start a new Group, where it will be placed top-left
   *
   *  _G1____   _G2____
   * |_1_|_2_| |_5_|_6_|
   * |_3_|_4_| |_7_|
   *
   * The width and height of an item is expressed using the number of cols and
   * rows it takes un within the grid.
   *
   * Each item has a numerical sorting "index" and a 2d "position" within the
   * grid. At least one of them is required. One can be calculated from the
   * other.
   *
   * An item is an object of structure:
   * {
   *   cols: 3,
   *   rows: 1,
   *   index: 3,
   *   position: [0, 2]
   * }
   */
  this.options = _.extend({}, this.defaults, options || {});
  this.items = items;
};

GridList.prototype.defaults = {
  rows: 5,
  colsPerGroup: 5
};

GridList.prototype.generatePositionsFromIndex = function() {
  this.groups = [];
  this._startNewGroup();
  for (var i = 0; i < this.items.length; i++) {
    this._generateItemPositionFromIndex(this.items[i]);
  }
  return this.items;
};

GridList.prototype.generateIndexesFromPosition = function(item) {
  this._sortItemsByPosition();
  if (typeof(this.options.sortItemsByPosition) == 'function') {
    this.options.sortItemsByPosition.call(this, this.items);
  }
  return this.items;
};

GridList.prototype._sortItemsByPosition = function() {
  var _this = this;
  this.items.sort(function(item1, item2) {
    var groupItem1 = Math.floor(item1.position.col / _this.options.colsPerGroup);
    var groupItem2 = Math.floor(item2.position.col / _this.options.colsPerGroup);
    // It's simple for items in different groups, the smaller the group number,
    // the smaller the sorting index
    if (groupItem1 != groupItem2) {
      return groupItem1 - groupItem2;
    }
    // Rows preced cols when it comes to sorting, so if an item is from a
    // smaller row, its sorting index will be smaller
    if (item1.position.row != item2.position.row) {
      return item1.position.row - item2.position.row;
    }
    // An earlier column means a smaller index
    if (item1.position.col != item2.position.col) {
      return item1.position.col - item2.position.col;
    }
    return 0;
  });
};

GridList.prototype._generateItemPositionFromIndex = function(item) {
  var currentGroup = this._getCurrentGroup();
  var itemPosition = currentGroup.findPositionForItem(item);
  // Warning: Any item must fit in a new blank group, otherwise this will go
  // into an infinite recursion loop
  if (itemPosition) {
    item.position = itemPosition;
    currentGroup.markItemCells(item);
    // Apply offset of cols based on the number of Groups before the current
    // one, to make the item position relative to the entire grid
    item.position.col += this.groups.indexOf(currentGroup) *
                         this.options.colsPerGroup;
  } else {
    this._startNewGroup();
    this._generateItemPositionFromIndex(item);
  }
};

GridList.prototype._startNewGroup = function() {
  this.groups.push(new GridGroup(this.options.colsPerGroup, this.options.rows));
};

GridList.prototype._getCurrentGroup = function() {
  return this.groups[this.groups.length - 1];
};

GridList.prototype._getIndexOfItem = function(item) {
  return this.items.indexOf(item);
};

var GridGroup = function(cols, rows) {
  this.colsPerRow = cols;
  // Create empty rows
  for (var i = 0; i < rows; i++) {
    this.push(new GridRow(cols));
  }
};

// Extend the Array prototype
GridGroup.prototype = [];

GridGroup.prototype.findPositionForItem = function(item) {
  var x, y, row, position;
  for (y = 0; y < this.length; y++) {
    row = this[y];
    for (x = 0; x < row.length; x++) {
      position = {
        col: x,
        row: y
      };
      if (this.itemFitsAtPosition(item, position)) {
        return position;
      }
    }
  }
  // There isn't available space for item in this Group
  return null;
};

GridGroup.prototype.itemFitsAtPosition = function(item, position) {
  /**
   * Check that an item wouldn't overlap with another one if placed at a
   * certain position within the grid
   */
  var x, y, row;
  // Make sure the item doesn't go outside of the grid bounds
  if ((position.col + item.cols > this.colsPerRow) ||
      (position.row + item.rows > this.length)) {
    return false;
  }
  // Make sure the item doesn't overlap with an already positioned item, or
  // isn't placed before a previous item
  for (y = position.row; y < this.length; y++) {
    row = this[y];
    for (x = position.col; x < row.length; x++) {
      if (row[x]) {
        return false;
      }
    }
  }
  return true;
};

GridGroup.prototype.markItemCells = function(item) {
  /**
   * Mark the grid cells that are occupied by an item. This prevents items
   * from overlapping in the grid
   */
  var x, y, row;
  for (y = item.position.row; y < item.position.row + item.rows; y++) {
    row = this[y];
    for (x = item.position.col; x < item.position.col + item.cols; x++) {
      row[x] = item;
    }
  }
};

var GridRow = function(cols) {
  for (var i = 0; i < cols; i++) {
    this.push(null);
  }
};

// Extend the Array prototype
GridRow.prototype = [];
<commit_msg>Fix row placement inside Group<commit_after>var GridList = function(items, options) {
  /**
   * A GridList manages a sorted list of rectangular items with variable width
   * and height.
   *
   * The GridList has two functions:
   *
   *  - Generate the 2d position based on the sorting index
   *  - TODO: Generate the sorting index based on the 2d position
   *
   * The 2d position is generated under the constraints of N rows and M columns
   * per Group (see below). The virtual grid space will expand horizontally to
   * any size required to fit all the items provided.
   *
   * A Group is a positional subset of the Grid, of variable columns. These are
   * the specs of the positioning algorithm:
   *
   *  - Groups are displayed one after another, from left to right.
   *  - Items withing a group are displayed left-to-right, top-to-bottom
   *  - If an item doesn't fit to the right of the previous item within the
   *    current Group, it will go under it
   *  - If an item doesn't fit under the previous item within the current
   *    Group, it will start a new Group, where it will be placed top-left
   *
   *  _G1____   _G2____
   * |_1_|_2_| |_5_|_6_|
   * |_3_|_4_| |_7_|
   *
   * The width and height of an item is expressed using the number of cols and
   * rows it takes un within the grid.
   *
   * Each item has a numerical sorting "index" and a 2d "position" within the
   * grid. At least one of them is required. One can be calculated from the
   * other.
   *
   * An item is an object of structure:
   * {
   *   cols: 3,
   *   rows: 1,
   *   index: 3,
   *   position: [0, 2]
   * }
   */
  this.options = _.extend({}, this.defaults, options || {});
  this.items = items;
};

GridList.prototype.defaults = {
  rows: 5,
  colsPerGroup: 5
};

GridList.prototype.generatePositionsFromIndex = function() {
  this.groups = [];
  this._startNewGroup();
  for (var i = 0; i < this.items.length; i++) {
    this._generateItemPositionFromIndex(this.items[i]);
  }
  return this.items;
};

GridList.prototype.generateIndexesFromPosition = function(item) {
  this._sortItemsByPosition();
  if (typeof(this.options.sortItemsByPosition) == 'function') {
    this.options.sortItemsByPosition.call(this, this.items);
  }
  return this.items;
};

GridList.prototype._sortItemsByPosition = function() {
  var _this = this;
  this.items.sort(function(item1, item2) {
    var groupItem1 = Math.floor(item1.position.col / _this.options.colsPerGroup);
    var groupItem2 = Math.floor(item2.position.col / _this.options.colsPerGroup);
    // It's simple for items in different groups, the smaller the group number,
    // the smaller the sorting index
    if (groupItem1 != groupItem2) {
      return groupItem1 - groupItem2;
    }
    // Rows preced cols when it comes to sorting, so if an item is from a
    // smaller row, its sorting index will be smaller
    if (item1.position.row != item2.position.row) {
      return item1.position.row - item2.position.row;
    }
    // An earlier column means a smaller index
    if (item1.position.col != item2.position.col) {
      return item1.position.col - item2.position.col;
    }
    return 0;
  });
};

GridList.prototype._generateItemPositionFromIndex = function(item) {
  var currentGroup = this._getCurrentGroup();
  var itemPosition = currentGroup.findPositionForItem(item);
  // Warning: Any item must fit in a new blank group, otherwise this will go
  // into an infinite recursion loop
  if (itemPosition) {
    item.position = itemPosition;
    currentGroup.markItemCells(item);
    // Since we position items one row at a time inside a Group, we must always
    // start positioning the next element on the row the prev one was placed
    currentGroup.currentRow = item.position.row;
    // Apply offset of cols based on the number of Groups before the current
    // one, to make the item position relative to the entire grid
    item.position.col += this.groups.indexOf(currentGroup) *
                         this.options.colsPerGroup;
  } else {
    this._startNewGroup();
    this._generateItemPositionFromIndex(item);
  }
};

GridList.prototype._startNewGroup = function() {
  this.groups.push(new GridGroup(this.options.colsPerGroup, this.options.rows));
};

GridList.prototype._getCurrentGroup = function() {
  return this.groups[this.groups.length - 1];
};

GridList.prototype._getIndexOfItem = function(item) {
  return this.items.indexOf(item);
};

var GridGroup = function(cols, rows) {
  this.colsPerRow = cols;
  this.currentRow = 0;
  // Create empty rows
  for (var i = 0; i < rows; i++) {
    this.push(new GridRow(cols));
  }
};

// Extend the Array prototype
GridGroup.prototype = [];

GridGroup.prototype.findPositionForItem = function(item) {
  var x, y, row, position;
  for (y = this.currentRow; y < this.length; y++) {
    row = this[y];
    for (x = 0; x < row.length; x++) {
      position = {
        col: x,
        row: y
      };
      if (this.itemFitsAtPosition(item, position)) {
        return position;
      }
    }
  }
  // There isn't available space for item in this Group
  return null;
};

GridGroup.prototype.itemFitsAtPosition = function(item, position) {
  /**
   * Check that an item wouldn't overlap with another one if placed at a
   * certain position within the grid
   */
  var x, y, row;
  // Make sure the item doesn't go outside of the grid bounds
  if ((position.col + item.cols > this.colsPerRow) ||
      (position.row + item.rows > this.length)) {
    return false;
  }
  // Make sure the item doesn't overlap with an already positioned item, or
  // isn't placed before a previous item
  for (y = position.row; y < this.length; y++) {
    row = this[y];
    for (x = position.col; x < row.length; x++) {
      if (row[x]) {
        return false;
      }
    }
  }
  return true;
};

GridGroup.prototype.markItemCells = function(item) {
  /**
   * Mark the grid cells that are occupied by an item. This prevents items
   * from overlapping in the grid
   */
  var x, y, row;
  for (y = item.position.row; y < item.position.row + item.rows; y++) {
    row = this[y];
    for (x = item.position.col; x < item.position.col + item.cols; x++) {
      row[x] = item;
    }
  }
};

var GridRow = function(cols) {
  for (var i = 0; i < cols; i++) {
    this.push(null);
  }
};

// Extend the Array prototype
GridRow.prototype = [];
<|endoftext|>


<commit_before>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');

//  ---------------------------------------------------------------------------

module.exports = class independentreserve extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'independentreserve',
            'name': 'Independent Reserve',
            'countries': [ 'AU', 'NZ' ], // Australia, New Zealand
            'rateLimit': 1000,
            'has': {
                'CORS': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/30521662-cf3f477c-9bcb-11e7-89bc-d1ac85012eda.jpg',
                'api': {
                    'public': 'https://api.independentreserve.com/Public',
                    'private': 'https://api.independentreserve.com/Private',
                },
                'www': 'https://www.independentreserve.com',
                'doc': 'https://www.independentreserve.com/API',
            },
            'api': {
                'public': {
                    'get': [
                        'GetValidPrimaryCurrencyCodes',
                        'GetValidSecondaryCurrencyCodes',
                        'GetValidLimitOrderTypes',
                        'GetValidMarketOrderTypes',
                        'GetValidOrderTypes',
                        'GetValidTransactionTypes',
                        'GetMarketSummary',
                        'GetOrderBook',
                        'GetTradeHistorySummary',
                        'GetRecentTrades',
                        'GetFxRates',
                    ],
                },
                'private': {
                    'post': [
                        'PlaceLimitOrder',
                        'PlaceMarketOrder',
                        'CancelOrder',
                        'GetOpenOrders',
                        'GetClosedOrders',
                        'GetClosedFilledOrders',
                        'GetOrderDetails',
                        'GetAccounts',
                        'GetTransactions',
                        'GetDigitalCurrencyDepositAddress',
                        'GetDigitalCurrencyDepositAddresses',
                        'SynchDigitalCurrencyDepositAddressWithBlockchain',
                        'WithdrawDigitalCurrency',
                        'RequestFiatWithdrawal',
                        'GetTrades',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'taker': 0.5 / 100,
                    'maker': 0.5 / 100,
                    'percentage': true,
                    'tierBased': false,
                },
            },
        });
    }

    async fetchMarkets () {
        let baseCurrencies = await this.publicGetGetValidPrimaryCurrencyCodes ();
        let quoteCurrencies = await this.publicGetGetValidSecondaryCurrencyCodes ();
        let result = [];
        for (let i = 0; i < baseCurrencies.length; i++) {
            let baseId = baseCurrencies[i];
            let baseIdUppercase = baseId.toUpperCase ();
            let base = this.commonCurrencyCode (baseIdUppercase);
            for (let j = 0; j < quoteCurrencies.length; j++) {
                let quoteId = quoteCurrencies[j];
                let quoteIdUppercase = quoteId.toUpperCase ();
                let quote = this.commonCurrencyCode (quoteIdUppercase);
                let id = baseId + '/' + quoteId;
                let symbol = base + '/' + quote;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': id,
                });
            }
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let balances = await this.privatePostGetAccounts ();
        let result = { 'info': balances };
        for (let i = 0; i < balances.length; i++) {
            let balance = balances[i];
            let currencyCode = balance['CurrencyCode'];
            let uppercase = currencyCode.toUpperCase ();
            let currency = this.commonCurrencyCode (uppercase);
            let account = this.account ();
            account['free'] = balance['AvailableBalance'];
            account['total'] = balance['TotalBalance'];
            account['used'] = account['total'] - account['free'];
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetGetOrderBook (this.extend ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
        }, params));
        let timestamp = this.parse8601 (response['CreatedTimestampUtc']);
        return this.parseOrderBook (response, timestamp, 'BuyOrders', 'SellOrders', 'Price', 'Volume');
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.parse8601 (ticker['CreatedTimestampUtc']);
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        let last = ticker['LastPrice'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': ticker['DayHighestPrice'],
            'low': ticker['DayLowestPrice'],
            'bid': ticker['CurrentHighestBidPrice'],
            'bidVolume': undefined,
            'ask': ticker['CurrentLowestOfferPrice'],
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': ticker['DayAvgPrice'],
            'baseVolume': ticker['DayVolumeXbtInSecondaryCurrrency'],
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetGetMarketSummary (this.extend ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
        }, params));
        return this.parseTicker (response, market);
    }

    parseOrder (order, market = undefined) {
        let symbol = undefined;
        if (typeof market === 'undefined') {
            symbol = market['symbol'];
        } else {
            market = this.findMarket (order['PrimaryCurrencyCode'] + '/' + order['SecondaryCurrencyCode']);
        }
        let orderType = this.safeValue (order, 'Type');
        if (orderType.indexOf ('Market') >= 0)
            orderType = 'market';
        else if (orderType.indexOf ('Limit') >= 0)
            orderType = 'limit';
        let side = undefined;
        if (orderType.indexOf ('Bid') >= 0)
            side = 'buy';
        else if (orderType.indexOf ('Offer') >= 0)
            side = 'sell';
        let timestamp = this.parse8601 (order['CreatedTimestampUtc']);
        let amount = this.safeFloat (order, 'VolumeOrdered');
        if (typeof amount === 'undefined')
            amount = this.safeFloat (order, 'Volume');
        let filled = this.safeFloat (order, 'VolumeFilled');
        let remaining = undefined;
        let feeRate = this.safeFloat (order, 'FeePercent');
        let feeCost = undefined;
        if (typeof amount !== 'undefined') {
            if (typeof filled !== 'undefined') {
                remaining = amount - filled;
                if (typeof feeRate !== 'undefined')
                    feeCost = feeRate * filled;
            }
        }
        let feeCurrency = undefined;
        if (typeof market !== 'undefined') {
            symbol = market['symbol'];
            feeCurrency = market['base'];
        }
        let fee = {
            'rate': feeRate,
            'cost': feeCost,
            'currency': feeCurrency,
        };
        let id = order['OrderGuid'];
        let status = this.parseOrderStatus (order['Status']);
        let cost = this.safeFloat (order, 'Value');
        let average = this.safeFloat (order, 'AvgPrice');
        let price = this.safeFloat (order, 'Price', average);
        return {
            'info': order,
            'id': id,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'type': orderType,
            'side': side,
            'price': price,
            'cost': cost,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
        };
    }

    parseOrderStatus (status) {
        let statuses = {
            'Open': 'open',
            'PartiallyFilled': 'open',
            'Filled': 'closed',
            'PartiallyFilledAndCancelled': 'canceled',
            'Cancelled': 'canceled',
            'PartiallyFilledAndExpired': 'canceled',
            'Expired': 'canceled',
        };
        if (status in statuses)
            return statuses[status];
        return status;
    }

    async fetchOrder (id, symbol = undefined, params) {
        let request = {
            'orderGuid': id,
        };
        const response = await this.privatePostGetOrderDetails (this.extend (request, params));
        return this.parseOrder (response);
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        const request = {
            'pageIndex': params['pageIndex'] || 1, // todo: rework this crap
            'pageSize': 50,
        };
        let market = undefined;
        if (typeof symbol !== 'undefined') {
            market = this.market (symbol);
        }
        const response = await this.privatePostGetTrades (this.extend (request, params));
        return this.parseTrades (response['Data'], market, since, limit);
    }

    parseTrade (trade, market) {
        let timestamp = this.parse8601 (trade['TradeTimestampUtc']);
        return {
            'id': trade['TradeGuid'],
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': market['symbol'],
            'order': trade['OrderGuid'],
            'type': undefined,
            'side': /Bid/.exec (trade['OrderType']) ? 'buy' : 'sell',
            'price': trade['Price'],
            'amount': trade['VolumeTraded'],
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetGetRecentTrades (this.extend ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
            'numberOfRecentTradesToRetrieve': 50, // max = 50
        }, params));
        return this.parseTrades (response['Trades'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let capitalizedOrderType = this.capitalize (type);
        let method = 'privatePostPlace' + capitalizedOrderType + 'Order';
        let orderType = capitalizedOrderType;
        orderType += (side === 'sell') ? 'Offer' : 'Bid';
        let order = this.ordered ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
            'orderType': orderType,
        });
        if (type === 'limit')
            order['price'] = price;
        order['volume'] = amount;
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['OrderGuid'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privatePostCancelOrder ({ 'orderGuid': id });
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api] + '/' + path;
        if (api === 'public') {
            if (Object.keys (params).length)
                url += '?' + this.urlencode (params);
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ();
            let auth = [
                url,
                'apiKey=' + this.apiKey,
                'nonce=' + nonce.toString (),
            ];
            // remove this crap
            let keys = this.apiSpecificKeySort (Object.keys (params));
            const sortedParams = [];
            for (let i = 0; i < keys.length; i++) {
                let key = keys[i];
                sortedParams.push (key + '=' + params[key]);
            }
            let message = auth.concat (sortedParams).join (',');
            let signature = this.hmac (this.encode (message), this.encode (this.secret));
            let query = this.extend ({
                'apiKey': this.apiKey,
                'nonce': nonce,
                'signature': signature,
            }, this.apiSpecificKeySort (params));
            body = this.json (query);
            headers = { 'Content-Type': 'application/json' };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    apiSpecificKeySort (params, out = {}) {
        // todo rework this crap
        const keyOrder = [
            'amount',
            'withdrawalAddress',
            'comment',
            'depositAddress',
            'primaryCurrencyCode',
            'secondaryCurrencyCode',
            'withdrawalAmount',
            'withdrawalBankAccountName',
            'orderType',
            'price',
            'volume',
            'orderGuid',
            'accountGuid',
            'fromTimestampUtc',
            'toTimestampUtc',
            'txTypes',
            'pageIndex',
            'pageSize',
        ];
        if (params.constructor === Object) {
            for (const k of keyOrder) {
                if (params[k]) {
                    out[k] = params[k];
                }
            }
        } else if (params.constructor === Array) {
            out = [];
            for (const k of keyOrder) {
                const key = params.find (p => p === k);
                if (key) {
                    out.push (key);
                }
            }
        }
        return out;
    }

    async request (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let response = await this.fetch2 (path, api, method, params, headers, body);
        // todo error handling
        return response;
    }
};
<commit_msg>removed deprecated request() from independentreserve #2410<commit_after>'use strict';

//  ---------------------------------------------------------------------------

const Exchange = require ('./base/Exchange');

//  ---------------------------------------------------------------------------

module.exports = class independentreserve extends Exchange {
    describe () {
        return this.deepExtend (super.describe (), {
            'id': 'independentreserve',
            'name': 'Independent Reserve',
            'countries': [ 'AU', 'NZ' ], // Australia, New Zealand
            'rateLimit': 1000,
            'has': {
                'CORS': false,
            },
            'urls': {
                'logo': 'https://user-images.githubusercontent.com/1294454/30521662-cf3f477c-9bcb-11e7-89bc-d1ac85012eda.jpg',
                'api': {
                    'public': 'https://api.independentreserve.com/Public',
                    'private': 'https://api.independentreserve.com/Private',
                },
                'www': 'https://www.independentreserve.com',
                'doc': 'https://www.independentreserve.com/API',
            },
            'api': {
                'public': {
                    'get': [
                        'GetValidPrimaryCurrencyCodes',
                        'GetValidSecondaryCurrencyCodes',
                        'GetValidLimitOrderTypes',
                        'GetValidMarketOrderTypes',
                        'GetValidOrderTypes',
                        'GetValidTransactionTypes',
                        'GetMarketSummary',
                        'GetOrderBook',
                        'GetTradeHistorySummary',
                        'GetRecentTrades',
                        'GetFxRates',
                    ],
                },
                'private': {
                    'post': [
                        'PlaceLimitOrder',
                        'PlaceMarketOrder',
                        'CancelOrder',
                        'GetOpenOrders',
                        'GetClosedOrders',
                        'GetClosedFilledOrders',
                        'GetOrderDetails',
                        'GetAccounts',
                        'GetTransactions',
                        'GetDigitalCurrencyDepositAddress',
                        'GetDigitalCurrencyDepositAddresses',
                        'SynchDigitalCurrencyDepositAddressWithBlockchain',
                        'WithdrawDigitalCurrency',
                        'RequestFiatWithdrawal',
                        'GetTrades',
                    ],
                },
            },
            'fees': {
                'trading': {
                    'taker': 0.5 / 100,
                    'maker': 0.5 / 100,
                    'percentage': true,
                    'tierBased': false,
                },
            },
        });
    }

    async fetchMarkets () {
        let baseCurrencies = await this.publicGetGetValidPrimaryCurrencyCodes ();
        let quoteCurrencies = await this.publicGetGetValidSecondaryCurrencyCodes ();
        let result = [];
        for (let i = 0; i < baseCurrencies.length; i++) {
            let baseId = baseCurrencies[i];
            let baseIdUppercase = baseId.toUpperCase ();
            let base = this.commonCurrencyCode (baseIdUppercase);
            for (let j = 0; j < quoteCurrencies.length; j++) {
                let quoteId = quoteCurrencies[j];
                let quoteIdUppercase = quoteId.toUpperCase ();
                let quote = this.commonCurrencyCode (quoteIdUppercase);
                let id = baseId + '/' + quoteId;
                let symbol = base + '/' + quote;
                result.push ({
                    'id': id,
                    'symbol': symbol,
                    'base': base,
                    'quote': quote,
                    'baseId': baseId,
                    'quoteId': quoteId,
                    'info': id,
                });
            }
        }
        return result;
    }

    async fetchBalance (params = {}) {
        await this.loadMarkets ();
        let balances = await this.privatePostGetAccounts ();
        let result = { 'info': balances };
        for (let i = 0; i < balances.length; i++) {
            let balance = balances[i];
            let currencyCode = balance['CurrencyCode'];
            let uppercase = currencyCode.toUpperCase ();
            let currency = this.commonCurrencyCode (uppercase);
            let account = this.account ();
            account['free'] = balance['AvailableBalance'];
            account['total'] = balance['TotalBalance'];
            account['used'] = account['total'] - account['free'];
            result[currency] = account;
        }
        return this.parseBalance (result);
    }

    async fetchOrderBook (symbol, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetGetOrderBook (this.extend ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
        }, params));
        let timestamp = this.parse8601 (response['CreatedTimestampUtc']);
        return this.parseOrderBook (response, timestamp, 'BuyOrders', 'SellOrders', 'Price', 'Volume');
    }

    parseTicker (ticker, market = undefined) {
        let timestamp = this.parse8601 (ticker['CreatedTimestampUtc']);
        let symbol = undefined;
        if (market)
            symbol = market['symbol'];
        let last = ticker['LastPrice'];
        return {
            'symbol': symbol,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'high': ticker['DayHighestPrice'],
            'low': ticker['DayLowestPrice'],
            'bid': ticker['CurrentHighestBidPrice'],
            'bidVolume': undefined,
            'ask': ticker['CurrentLowestOfferPrice'],
            'askVolume': undefined,
            'vwap': undefined,
            'open': undefined,
            'close': last,
            'last': last,
            'previousClose': undefined,
            'change': undefined,
            'percentage': undefined,
            'average': ticker['DayAvgPrice'],
            'baseVolume': ticker['DayVolumeXbtInSecondaryCurrrency'],
            'quoteVolume': undefined,
            'info': ticker,
        };
    }

    async fetchTicker (symbol, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetGetMarketSummary (this.extend ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
        }, params));
        return this.parseTicker (response, market);
    }

    parseOrder (order, market = undefined) {
        let symbol = undefined;
        if (typeof market === 'undefined') {
            symbol = market['symbol'];
        } else {
            market = this.findMarket (order['PrimaryCurrencyCode'] + '/' + order['SecondaryCurrencyCode']);
        }
        let orderType = this.safeValue (order, 'Type');
        if (orderType.indexOf ('Market') >= 0)
            orderType = 'market';
        else if (orderType.indexOf ('Limit') >= 0)
            orderType = 'limit';
        let side = undefined;
        if (orderType.indexOf ('Bid') >= 0)
            side = 'buy';
        else if (orderType.indexOf ('Offer') >= 0)
            side = 'sell';
        let timestamp = this.parse8601 (order['CreatedTimestampUtc']);
        let amount = this.safeFloat (order, 'VolumeOrdered');
        if (typeof amount === 'undefined')
            amount = this.safeFloat (order, 'Volume');
        let filled = this.safeFloat (order, 'VolumeFilled');
        let remaining = undefined;
        let feeRate = this.safeFloat (order, 'FeePercent');
        let feeCost = undefined;
        if (typeof amount !== 'undefined') {
            if (typeof filled !== 'undefined') {
                remaining = amount - filled;
                if (typeof feeRate !== 'undefined')
                    feeCost = feeRate * filled;
            }
        }
        let feeCurrency = undefined;
        if (typeof market !== 'undefined') {
            symbol = market['symbol'];
            feeCurrency = market['base'];
        }
        let fee = {
            'rate': feeRate,
            'cost': feeCost,
            'currency': feeCurrency,
        };
        let id = order['OrderGuid'];
        let status = this.parseOrderStatus (order['Status']);
        let cost = this.safeFloat (order, 'Value');
        let average = this.safeFloat (order, 'AvgPrice');
        let price = this.safeFloat (order, 'Price', average);
        return {
            'info': order,
            'id': id,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'type': orderType,
            'side': side,
            'price': price,
            'cost': cost,
            'average': average,
            'amount': amount,
            'filled': filled,
            'remaining': remaining,
            'status': status,
            'fee': fee,
        };
    }

    parseOrderStatus (status) {
        let statuses = {
            'Open': 'open',
            'PartiallyFilled': 'open',
            'Filled': 'closed',
            'PartiallyFilledAndCancelled': 'canceled',
            'Cancelled': 'canceled',
            'PartiallyFilledAndExpired': 'canceled',
            'Expired': 'canceled',
        };
        if (status in statuses)
            return statuses[status];
        return status;
    }

    async fetchOrder (id, symbol = undefined, params) {
        let request = {
            'orderGuid': id,
        };
        const response = await this.privatePostGetOrderDetails (this.extend (request, params));
        return this.parseOrder (response);
    }

    async fetchMyTrades (symbol = undefined, since = undefined, limit = 50, params = {}) {
        await this.loadMarkets ();
        let pageIndex = this.safeInteger (params, 'pageIndex', 1);
        const request = this.ordered ({
            'pageIndex': pageIndex,
            'pageSize': limit,
        });
        const response = await this.privatePostGetTrades (this.extend (request, params));
        let market = undefined;
        if (typeof symbol !== 'undefined') {
            market = this.market (symbol);
        }
        return this.parseTrades (response['Data'], market, since, limit);
    }

    parseTrade (trade, market = undefined) {
        let timestamp = this.parse8601 (trade['TradeTimestampUtc']);
        let id = this.safeString (trade, 'TradeGuid');
        let orderId = this.safeString (trade, 'OrderGuid');
        let price = this.safeFloat (trade, 'Price');
        if (typeof price === 'undefined') {
            price = this.safeFloat (trade, 'SecondaryCurrencyTradePrice');
        }
        let amount = this.safeFloat (trade, 'VolumeTraded');
        if (typeof amount === 'undefined') {
            amount = this.safeFloat (trade, 'PrimaryCurrencyAmount');
        }
        let symbol = undefined;
        if (typeof market !== 'undefined')
            symbol = market['symbol'];
        return {
            'id': id,
            'info': trade,
            'timestamp': timestamp,
            'datetime': this.iso8601 (timestamp),
            'symbol': symbol,
            'order': orderId,
            'type': undefined,
            'side': /Bid/.exec (trade['OrderType']) ? 'buy' : 'sell',
            'price': price,
            'amount': amount,
            'fee': undefined,
        };
    }

    async fetchTrades (symbol, since = undefined, limit = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let response = await this.publicGetGetRecentTrades (this.extend ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
            'numberOfRecentTradesToRetrieve': 50, // max = 50
        }, params));
        return this.parseTrades (response['Trades'], market, since, limit);
    }

    async createOrder (symbol, type, side, amount, price = undefined, params = {}) {
        await this.loadMarkets ();
        let market = this.market (symbol);
        let capitalizedOrderType = this.capitalize (type);
        let method = 'privatePostPlace' + capitalizedOrderType + 'Order';
        let orderType = capitalizedOrderType;
        orderType += (side === 'sell') ? 'Offer' : 'Bid';
        let order = this.ordered ({
            'primaryCurrencyCode': market['baseId'],
            'secondaryCurrencyCode': market['quoteId'],
            'orderType': orderType,
        });
        if (type === 'limit')
            order['price'] = price;
        order['volume'] = amount;
        let response = await this[method] (this.extend (order, params));
        return {
            'info': response,
            'id': response['OrderGuid'],
        };
    }

    async cancelOrder (id, symbol = undefined, params = {}) {
        await this.loadMarkets ();
        return await this.privatePostCancelOrder ({ 'orderGuid': id });
    }

    sign (path, api = 'public', method = 'GET', params = {}, headers = undefined, body = undefined) {
        let url = this.urls['api'][api] + '/' + path;
        if (api === 'public') {
            if (Object.keys (params).length)
                url += '?' + this.urlencode (params);
        } else {
            this.checkRequiredCredentials ();
            let nonce = this.nonce ();
            let auth = [
                url,
                'apiKey=' + this.apiKey,
                'nonce=' + nonce.toString (),
            ];
            // remove this crap
            let keys = this.apiSpecificKeySort (Object.keys (params));
            const sortedParams = [];
            for (let i = 0; i < keys.length; i++) {
                let key = keys[i];
                sortedParams.push (key + '=' + params[key]);
            }
            let message = auth.concat (sortedParams).join (',');
            let signature = this.hmac (this.encode (message), this.encode (this.secret));
            let query = this.extend ({
                'apiKey': this.apiKey,
                'nonce': nonce,
                'signature': signature,
            }, this.apiSpecificKeySort (params));
            body = this.json (query);
            headers = { 'Content-Type': 'application/json' };
        }
        return { 'url': url, 'method': method, 'body': body, 'headers': headers };
    }

    apiSpecificKeySort (params, out = {}) {
        // todo rework this crap
        const keyOrder = [
            'amount',
            'withdrawalAddress',
            'comment',
            'depositAddress',
            'primaryCurrencyCode',
            'secondaryCurrencyCode',
            'withdrawalAmount',
            'withdrawalBankAccountName',
            'orderType',
            'price',
            'volume',
            'orderGuid',
            'accountGuid',
            'fromTimestampUtc',
            'toTimestampUtc',
            'txTypes',
            'pageIndex',
            'pageSize',
        ];
        if (params.constructor === Object) {
            for (const k of keyOrder) {
                if (params[k]) {
                    out[k] = params[k];
                }
            }
        } else if (params.constructor === Array) {
            out = [];
            for (const k of keyOrder) {
                const key = params.find (p => p === k);
                if (key) {
                    out.push (key);
                }
            }
        }
        return out;
    }
};
<|endoftext|>


<commit_before>/**
 * @fileoverview Mocha test wrapper
 * @author Ilya Volodin
 */
"use strict";
/*global describe, it*/

/*
 * This is a wrapper around mocha to allow for DRY unittests for eslint
 * Format:
 * eslintTester.add("{ruleName}", {
 *      valid: [
 *          "{code}",
 *          { code: "{code}", args: {args}, global: {globals}, globals: {globals}, settings: {settings} }
 *      ],
 *      invalid: [
 *          { code: "{code}", errors: {numErrors} },
 *          { code: "{code}", args: {args}, global: {globals}, settings: {settings} errors: [{ message: "{errorMessage}", type: "{errorNodeType}"}] }
 *      ]
 *  });
 *
 * Variables:
 * {code} - String that represents the code to be tested
 * {args} - Arguments that are passed to the configurable rules. If not
 *          supplied "1" is passed to the rule
 * {globals} - An object representing a list of variables that are
 *             registered as globals
 * {settings} - An object representing global settings for all rules
 * {numErrors} - If failing case doesn't need to check error message,
 *               this integer will specify how many errors should be
 *               received
 * {errorMessage} - Message that is returned by the rule on failure
 * {errorNodeType} - AST node type that is returned by they rule as
 *                   a cause of the failure.
 *
 * Requirements:
 * Each rule has to have at least one invalid and at least one valid check.
 * If one of them is missing, the test will be marked as failed.
 */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var assert = require("chai").assert,
    util = require("util"),
    path = require("path");

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

/**
 * Creates a new instance of ESLintTester.
 * @param {ESLint} eslint The ESLint instance to use.
 * @constructor
 */
function ESLintTester(eslint) {

    /**
     * The internal linter to use.
     * @type {ESLint}
     */
    this.eslint = eslint;
}

ESLintTester.prototype = {

    /**
     * Adds a new rule test to execute.
     * @param {string} rulePath The path to the rule to test.
     * @param {Object} test The collection of tests to run.
     * @returns {void}
     */
    addRuleTest: function(rulePath, test) {

        var eslint = this.eslint,
            result = {},
            ruleName = path.basename(rulePath);

        function runRuleForItem(ruleName, item) {
            var config = { rules: {} },
                code = item.code ? item.code : item,
                filename;

            if (item.globals || item.global) {
                config.global = item.globals ? item.globals : item.global;
            }

            if (item.filename) {
                filename = item.filename;
            }

            if (item.ecmaFeatures) {
                config.ecmaFeatures = item.ecmaFeatures;
            }

            config.settings = item.settings;

            if (item.options) {
                var options = item.options.concat();
                options.unshift(1);
                config.rules[ruleName] = options;
            } else {
                config.rules[ruleName] = item.args ? item.args : 1;
            }

            eslint.defineRule(ruleName, require(path.resolve(process.cwd(), rulePath)));
            return eslint.verify(code, config, filename);
        }

        //all valid cases go through this.
        function testValidTemplate(ruleName, item) {
            var messages = runRuleForItem(ruleName, item);

            assert.equal(messages.length, 0, util.format("Should have no errors but had %d: %s",
                        messages.length, util.inspect(messages)));
        }

        //all invalid cases go through this.
        function testInvalidTemplate(ruleName, item) {
            var messages = runRuleForItem(ruleName, item);

            if (typeof item.errors === "number") {
                assert.equal(messages.length, item.errors,  util.format("Should have %d errors but had %d: %s",
                    item.errors, messages.length, util.inspect(messages)));
            } else {
                assert.equal(messages.length, item.errors.length,
                    util.format("Should have %d errors but had %d: %s",
                    item.errors.length, messages.length, util.inspect(messages)));

                for (var i = 0, l = item.errors.length; i < l; i++) {
                    assert.ok(!("fatal" in messages[i]), "A fatal parsing error occurred: " + messages[i].message);
                    assert.equal(messages[i].ruleId, ruleName, "Error rule name should be the same as the name of the rule being tested");
                    if (item.errors[i].message) {
                        assert.equal(messages[i].message, item.errors[i].message, "Error message should be " + item.errors[i].message);
                    }
                    if (item.errors[i].type) {
                        assert.equal(messages[i].node.type, item.errors[i].type, "Error type should be " + item.errors[i].type);
                    }
                    if (item.errors[i].line) {
                        assert.equal(messages[i].line, item.errors[i].line, "Error line should be " + item.errors[i].line);
                    }
                    if (item.errors[i].column) {
                        assert.equal(messages[i].column, item.errors[i].column, "Error column should be " + item.errors[i].column);
                    }
                }
            }
        }

        //this creates a mocha test suite and pipes all supplied info
        //through one of the templates above.
        describe(ruleName, function() {
            if (!test.valid || test.valid.length < 1) {
                //we only want to run this assertion when it's going to
                //fail, so we wouldn't artificially boost test count
                it("should have at least one valid test", function() {
                    assert.equal(test.valid ? test.valid.length : 0, 1, "Each rule should have at least one valid test");
                });
            } else {
                test.valid.forEach(function(valid) {
                    it(valid.code || valid, function() {
                        testValidTemplate(ruleName, valid);
                    });
                });
            }
            if (!test.invalid || test.invalid.length < 1) {
                //we only want to run this assertion when it's going to
                //fail, so we wouldn't artificially boost test count
                it("should have at least one invalid test", function() {
                    assert.equal(test.invalid ? test.invalid.length : 0, 1, "Each rule should have at least one invalid test");
                });
            } else {
                test.invalid.forEach(function(invalid) {
                    it(invalid.code, function() {
                        testInvalidTemplate(ruleName, invalid);
                    });
                });
            }
        });

        return result.suite;
    }
};

module.exports = ESLintTester;
<commit_msg>Breaking: Look for nodeType instead of node.type<commit_after>/**
 * @fileoverview Mocha test wrapper
 * @author Ilya Volodin
 */
"use strict";
/*global describe, it*/

/*
 * This is a wrapper around mocha to allow for DRY unittests for eslint
 * Format:
 * eslintTester.add("{ruleName}", {
 *      valid: [
 *          "{code}",
 *          { code: "{code}", args: {args}, global: {globals}, globals: {globals}, settings: {settings} }
 *      ],
 *      invalid: [
 *          { code: "{code}", errors: {numErrors} },
 *          { code: "{code}", args: {args}, global: {globals}, settings: {settings} errors: [{ message: "{errorMessage}", type: "{errorNodeType}"}] }
 *      ]
 *  });
 *
 * Variables:
 * {code} - String that represents the code to be tested
 * {args} - Arguments that are passed to the configurable rules. If not
 *          supplied "1" is passed to the rule
 * {globals} - An object representing a list of variables that are
 *             registered as globals
 * {settings} - An object representing global settings for all rules
 * {numErrors} - If failing case doesn't need to check error message,
 *               this integer will specify how many errors should be
 *               received
 * {errorMessage} - Message that is returned by the rule on failure
 * {errorNodeType} - AST node type that is returned by they rule as
 *                   a cause of the failure.
 *
 * Requirements:
 * Each rule has to have at least one invalid and at least one valid check.
 * If one of them is missing, the test will be marked as failed.
 */

//------------------------------------------------------------------------------
// Requirements
//------------------------------------------------------------------------------

var assert = require("chai").assert,
    util = require("util"),
    path = require("path");

//------------------------------------------------------------------------------
// Public Interface
//------------------------------------------------------------------------------

/**
 * Creates a new instance of ESLintTester.
 * @param {ESLint} eslint The ESLint instance to use.
 * @constructor
 */
function ESLintTester(eslint) {

    /**
     * The internal linter to use.
     * @type {ESLint}
     */
    this.eslint = eslint;
}

ESLintTester.prototype = {

    /**
     * Adds a new rule test to execute.
     * @param {string} rulePath The path to the rule to test.
     * @param {Object} test The collection of tests to run.
     * @returns {void}
     */
    addRuleTest: function(rulePath, test) {

        var eslint = this.eslint,
            result = {},
            ruleName = path.basename(rulePath);

        function runRuleForItem(ruleName, item) {
            var config = { rules: {} },
                code = item.code ? item.code : item,
                filename;

            if (item.globals || item.global) {
                config.global = item.globals ? item.globals : item.global;
            }

            if (item.filename) {
                filename = item.filename;
            }

            if (item.ecmaFeatures) {
                config.ecmaFeatures = item.ecmaFeatures;
            }

            config.settings = item.settings;

            if (item.options) {
                var options = item.options.concat();
                options.unshift(1);
                config.rules[ruleName] = options;
            } else {
                config.rules[ruleName] = item.args ? item.args : 1;
            }

            eslint.defineRule(ruleName, require(path.resolve(process.cwd(), rulePath)));
            return eslint.verify(code, config, filename);
        }

        //all valid cases go through this.
        function testValidTemplate(ruleName, item) {
            var messages = runRuleForItem(ruleName, item);

            assert.equal(messages.length, 0, util.format("Should have no errors but had %d: %s",
                        messages.length, util.inspect(messages)));
        }

        //all invalid cases go through this.
        function testInvalidTemplate(ruleName, item) {
            var messages = runRuleForItem(ruleName, item);

            if (typeof item.errors === "number") {
                assert.equal(messages.length, item.errors,  util.format("Should have %d errors but had %d: %s",
                    item.errors, messages.length, util.inspect(messages)));
            } else {
                assert.equal(messages.length, item.errors.length,
                    util.format("Should have %d errors but had %d: %s",
                    item.errors.length, messages.length, util.inspect(messages)));

                for (var i = 0, l = item.errors.length; i < l; i++) {
                    assert.ok(!("fatal" in messages[i]), "A fatal parsing error occurred: " + messages[i].message);
                    assert.equal(messages[i].ruleId, ruleName, "Error rule name should be the same as the name of the rule being tested");
                    if (item.errors[i].message) {
                        assert.equal(messages[i].message, item.errors[i].message, "Error message should be " + item.errors[i].message);
                    }
                    if (item.errors[i].type) {
                        assert.equal(messages[i].nodeType, item.errors[i].type, "Error type should be " + item.errors[i].type);
                    }
                    if (item.errors[i].line) {
                        assert.equal(messages[i].line, item.errors[i].line, "Error line should be " + item.errors[i].line);
                    }
                    if (item.errors[i].column) {
                        assert.equal(messages[i].column, item.errors[i].column, "Error column should be " + item.errors[i].column);
                    }
                }
            }
        }

        //this creates a mocha test suite and pipes all supplied info
        //through one of the templates above.
        describe(ruleName, function() {
            if (!test.valid || test.valid.length < 1) {
                //we only want to run this assertion when it's going to
                //fail, so we wouldn't artificially boost test count
                it("should have at least one valid test", function() {
                    assert.equal(test.valid ? test.valid.length : 0, 1, "Each rule should have at least one valid test");
                });
            } else {
                test.valid.forEach(function(valid) {
                    it(valid.code || valid, function() {
                        testValidTemplate(ruleName, valid);
                    });
                });
            }
            if (!test.invalid || test.invalid.length < 1) {
                //we only want to run this assertion when it's going to
                //fail, so we wouldn't artificially boost test count
                it("should have at least one invalid test", function() {
                    assert.equal(test.invalid ? test.invalid.length : 0, 1, "Each rule should have at least one invalid test");
                });
            } else {
                test.invalid.forEach(function(invalid) {
                    it(invalid.code, function() {
                        testInvalidTemplate(ruleName, invalid);
                    });
                });
            }
        });

        return result.suite;
    }
};

module.exports = ESLintTester;
<|endoftext|>


<commit_before>
/**
 * Module dependencies.
 */

var Emitter = require('events').EventEmitter;
var parser = require('socket.io-parser');
var debug = require('debug')('socket.io:socket');
var hasBin = require('has-binary-data');

/**
 * Module exports.
 */

module.exports = exports = Socket;

/**
 * Blacklisted events.
 *
 * @api public
 */

exports.events = [
  'error',
  'connect',
  'disconnect',
  'newListener'
];

/**
 * Flags.
 *
 * @api public
 */

exports.flags = [
  'json',
  'volatile',
  'broadcast'
];

/**
 * `EventEmitter#emit` reference.
 */

var emit = Emitter.prototype.emit;

/**
 * Interface to a `Client` for a given `Namespace`.
 *
 * @param {Namespace} nsp
 * @param {Client} client
 * @api public
 */

function Socket(nsp, client){
  this.nsp = nsp;
  this.server = nsp.server;
  this.adapter = this.nsp.adapter;
  this.id = client.id;
  this.request = client.request;
  this.client = client;
  this.conn = client.conn;
  this.rooms = [];
  this.acks = {};
  this.connected = true;
  this.disconnected = false;
}

/**
 * Inherits from `EventEmitter`.
 */

Socket.prototype.__proto__ = Emitter.prototype;

/**
 * Apply flags from `Socket`.
 */

exports.flags.forEach(function(flag){
  Socket.prototype.__defineGetter__(flag, function(){
    this.flags = this.flags || {};
    this.flags[flag] = true;
    return this;
  });
});

/**
 * `request` engine.io shorcut.
 *
 * @api public
 */

Socket.prototype.__defineGetter__('request', function(){
  return this.conn.request;
});

/**
 * Emits to this client.
 *
 * @return {Socket} self
 * @api public
 */

Socket.prototype.emit = function(ev){
  if (~exports.events.indexOf(ev)) {
    emit.apply(this, arguments);
  } else {
    // set up packet object
    var args = Array.prototype.slice.call(arguments);

    var parserType = parser.EVENT; // default
    if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary

    var packet = { type: parserType, data: args };

    // access last argument to see if it's an ACK callback
    if ('function' == typeof args[args.length - 1]) {
      if (this._rooms || (this.flags && this.flags.broadcast)) {
        throw new Error('Callbacks are not supported when broadcasting');
      }

      debug('emitting packet with ack id %d', this.nsp.ids);
      this.acks[this.nsp.ids] = args.pop();
      packet.id = this.nsp.ids++;
    }

    if (this._rooms || (this.flags && this.flags.broadcast)) {
      this.adapter.broadcast(packet, {
        except: [this.id],
        rooms: this._rooms,
        flags: this.flags
      });
    } else {
      // dispatch packet
      this.packet(packet);
    }

    // reset flags
    delete this._rooms;
    delete this.flags;
  }
  return this;
};

/**
 * Targets a room when broadcasting.
 *
 * @param {String} name
 * @return {Socket} self
 * @api public
 */

Socket.prototype.to =
Socket.prototype.in = function(name){
  this._rooms = this._rooms || [];
  if (!~this._rooms.indexOf(name)) this._rooms.push(name);
  return this;
};

/**
 * Sends a `message` event.
 *
 * @return {Socket} self
 * @api public
 */

Socket.prototype.send =
Socket.prototype.write = function(){
  var args = Array.prototype.slice.call(arguments);
  args.unshift('message');
  this.emit.apply(this, args);
  return this;
};

/**
 * Writes a packet.
 *
 * @param {Object} packet object
 * @api private
 */

Socket.prototype.packet = function(packet, preEncoded){
  packet.nsp = this.nsp.name;
  this.client.packet(packet, preEncoded);
};

/**
 * Joins a room.
 *
 * @param {String} room
 * @param {Function} optional, callback
 * @return {Socket} self
 * @api private
 */

Socket.prototype.join = function(room, fn){
  debug('joining room %s', room);
  var self = this;
  if (~this.rooms.indexOf(room)) return this;
  this.adapter.add(this.id, room, function(err){
    if (err) return fn && fn(err);
    debug('joined room %s', room);
    self.rooms.push(room);
    fn && fn(null);
  });
  return this;
};

/**
 * Leaves a room.
 *
 * @param {String} room
 * @param {Function} optional, callback
 * @return {Socket} self
 * @api private
 */

Socket.prototype.leave = function(room, fn){
  debug('leave room %s', room);
  var self = this;
  this.adapter.del(this.id, room, function(err){
    if (err) return fn && fn(err);
    debug('left room %s', room);
    self.rooms.splice(self.rooms.indexOf(room, 1));
    fn && fn(null);
  });
  return this;
};

/**
 * Leave all rooms.
 *
 * @api private
 */

Socket.prototype.leaveAll = function(){
  this.adapter.delAll(this.id);
};

/**
 * Called by `Namespace` upon succesful
 * middleware execution (ie: authorization).
 *
 * @api private
 */

Socket.prototype.onconnect = function(){
  debug('socket connected - writing packet');
  this.join(this.id);
  this.packet({ type: parser.CONNECT });
  this.nsp.connected[this.id] = this;
};

/**
 * Called with each packet. Called by `Client`.
 *
 * @param {Object} packet
 * @api private
 */

Socket.prototype.onpacket = function(packet){
  debug('got packet %j', packet);
  switch (packet.type) {
    case parser.EVENT:
      this.onevent(packet);
      break;

    case parser.BINARY_EVENT:
      this.onevent(packet);
      break;

    case parser.ACK:
      this.onack(packet);
      break;

    case parser.DISCONNECT:
      this.ondisconnect();
      break;

    case parser.ERROR:
      this.emit('error', packet.data);
  }
};

/**
 * Called upon event packet.
 *
 * @param {Object} packet object
 * @api private
 */

Socket.prototype.onevent = function(packet){
  var args = packet.data || [];
  debug('emitting event %j', args);

  if (null != packet.id) {
    debug('attaching ack callback to event');
    args.push(this.ack(packet.id));
  }

  emit.apply(this, args);
};

/**
 * Produces an ack callback to emit with an event.
 *
 * @param {Number} packet id
 * @api private
 */

Socket.prototype.ack = function(id){
  var self = this;
  var sent = false;
  return function(){
    // prevent double callbacks
    if (sent) return;
    var args = Array.prototype.slice.call(arguments);
    debug('sending ack %j', args);
    self.packet({
      id: id,
      type: parser.ACK,
      data: args
    });
  };
};

/**
 * Called upon ack packet.
 *
 * @api private
 */

Socket.prototype.onack = function(packet){
  var ack = this.acks[packet.id];
  if ('function' == typeof ack) {
    debug('calling ack %s with %j', packet.id, packet.data);
    ack.apply(this, packet.data);
    delete this.acks[packet.id];
  } else {
    debug('bad ack %s', packet.id);
  }
};

/**
 * Called upon client disconnect packet.
 *
 * @api private
 */

Socket.prototype.ondisconnect = function(){
  debug('got disconnect packet');
  this.onclose('client namespace disconnect');
};

/**
 * Called upon closing. Called by `Client`.
 *
 * @param {String} reason
 * @api private
 */

Socket.prototype.onclose = function(reason){
  if (!this.connected) return this;
  debug('closing socket - reason %s', reason);
  this.leaveAll();
  this.nsp.remove(this);
  this.client.remove(this);
  this.connected = false;
  this.disconnected = true;
  delete this.nsp.connected[this.id];
  this.emit('disconnect', reason);
};

/**
 * Produces an `error` packet.
 *
 * @param {Object} error object
 * @api private
 */

Socket.prototype.error = function(err){
  this.packet({ type: parser.ERROR, data: err });
};

/**
 * Disconnects this client.
 *
 * @param {Boolean} if `true`, closes the underlying connection
 * @return {Socket} self
 * @api public
 */

Socket.prototype.disconnect = function(close){
  if (!this.connected) return this;
  if (close) {
    this.client.disconnect();
  } else {
    this.packet({ type: parser.DISCONNECT });
    this.onclose('server namespace disconnect');
  }
  return this;
};
<commit_msg>socket: fix `flags` `api` state<commit_after>
/**
 * Module dependencies.
 */

var Emitter = require('events').EventEmitter;
var parser = require('socket.io-parser');
var debug = require('debug')('socket.io:socket');
var hasBin = require('has-binary-data');

/**
 * Module exports.
 */

module.exports = exports = Socket;

/**
 * Blacklisted events.
 *
 * @api public
 */

exports.events = [
  'error',
  'connect',
  'disconnect',
  'newListener'
];

/**
 * Flags.
 *
 * @api private
 */

var flags = [
  'json',
  'volatile',
  'broadcast'
];

/**
 * `EventEmitter#emit` reference.
 */

var emit = Emitter.prototype.emit;

/**
 * Interface to a `Client` for a given `Namespace`.
 *
 * @param {Namespace} nsp
 * @param {Client} client
 * @api public
 */

function Socket(nsp, client){
  this.nsp = nsp;
  this.server = nsp.server;
  this.adapter = this.nsp.adapter;
  this.id = client.id;
  this.request = client.request;
  this.client = client;
  this.conn = client.conn;
  this.rooms = [];
  this.acks = {};
  this.connected = true;
  this.disconnected = false;
}

/**
 * Inherits from `EventEmitter`.
 */

Socket.prototype.__proto__ = Emitter.prototype;

/**
 * Apply flags from `Socket`.
 */

flags.forEach(function(flag){
  Socket.prototype.__defineGetter__(flag, function(){
    this.flags = this.flags || {};
    this.flags[flag] = true;
    return this;
  });
});

/**
 * `request` engine.io shorcut.
 *
 * @api public
 */

Socket.prototype.__defineGetter__('request', function(){
  return this.conn.request;
});

/**
 * Emits to this client.
 *
 * @return {Socket} self
 * @api public
 */

Socket.prototype.emit = function(ev){
  if (~exports.events.indexOf(ev)) {
    emit.apply(this, arguments);
  } else {
    // set up packet object
    var args = Array.prototype.slice.call(arguments);

    var parserType = parser.EVENT; // default
    if (hasBin(args)) { parserType = parser.BINARY_EVENT; } // binary

    var packet = { type: parserType, data: args };

    // access last argument to see if it's an ACK callback
    if ('function' == typeof args[args.length - 1]) {
      if (this._rooms || (this.flags && this.flags.broadcast)) {
        throw new Error('Callbacks are not supported when broadcasting');
      }

      debug('emitting packet with ack id %d', this.nsp.ids);
      this.acks[this.nsp.ids] = args.pop();
      packet.id = this.nsp.ids++;
    }

    if (this._rooms || (this.flags && this.flags.broadcast)) {
      this.adapter.broadcast(packet, {
        except: [this.id],
        rooms: this._rooms,
        flags: this.flags
      });
    } else {
      // dispatch packet
      this.packet(packet);
    }

    // reset flags
    delete this._rooms;
    delete this.flags;
  }
  return this;
};

/**
 * Targets a room when broadcasting.
 *
 * @param {String} name
 * @return {Socket} self
 * @api public
 */

Socket.prototype.to =
Socket.prototype.in = function(name){
  this._rooms = this._rooms || [];
  if (!~this._rooms.indexOf(name)) this._rooms.push(name);
  return this;
};

/**
 * Sends a `message` event.
 *
 * @return {Socket} self
 * @api public
 */

Socket.prototype.send =
Socket.prototype.write = function(){
  var args = Array.prototype.slice.call(arguments);
  args.unshift('message');
  this.emit.apply(this, args);
  return this;
};

/**
 * Writes a packet.
 *
 * @param {Object} packet object
 * @api private
 */

Socket.prototype.packet = function(packet, preEncoded){
  packet.nsp = this.nsp.name;
  this.client.packet(packet, preEncoded);
};

/**
 * Joins a room.
 *
 * @param {String} room
 * @param {Function} optional, callback
 * @return {Socket} self
 * @api private
 */

Socket.prototype.join = function(room, fn){
  debug('joining room %s', room);
  var self = this;
  if (~this.rooms.indexOf(room)) return this;
  this.adapter.add(this.id, room, function(err){
    if (err) return fn && fn(err);
    debug('joined room %s', room);
    self.rooms.push(room);
    fn && fn(null);
  });
  return this;
};

/**
 * Leaves a room.
 *
 * @param {String} room
 * @param {Function} optional, callback
 * @return {Socket} self
 * @api private
 */

Socket.prototype.leave = function(room, fn){
  debug('leave room %s', room);
  var self = this;
  this.adapter.del(this.id, room, function(err){
    if (err) return fn && fn(err);
    debug('left room %s', room);
    self.rooms.splice(self.rooms.indexOf(room, 1));
    fn && fn(null);
  });
  return this;
};

/**
 * Leave all rooms.
 *
 * @api private
 */

Socket.prototype.leaveAll = function(){
  this.adapter.delAll(this.id);
};

/**
 * Called by `Namespace` upon succesful
 * middleware execution (ie: authorization).
 *
 * @api private
 */

Socket.prototype.onconnect = function(){
  debug('socket connected - writing packet');
  this.join(this.id);
  this.packet({ type: parser.CONNECT });
  this.nsp.connected[this.id] = this;
};

/**
 * Called with each packet. Called by `Client`.
 *
 * @param {Object} packet
 * @api private
 */

Socket.prototype.onpacket = function(packet){
  debug('got packet %j', packet);
  switch (packet.type) {
    case parser.EVENT:
      this.onevent(packet);
      break;

    case parser.BINARY_EVENT:
      this.onevent(packet);
      break;

    case parser.ACK:
      this.onack(packet);
      break;

    case parser.DISCONNECT:
      this.ondisconnect();
      break;

    case parser.ERROR:
      this.emit('error', packet.data);
  }
};

/**
 * Called upon event packet.
 *
 * @param {Object} packet object
 * @api private
 */

Socket.prototype.onevent = function(packet){
  var args = packet.data || [];
  debug('emitting event %j', args);

  if (null != packet.id) {
    debug('attaching ack callback to event');
    args.push(this.ack(packet.id));
  }

  emit.apply(this, args);
};

/**
 * Produces an ack callback to emit with an event.
 *
 * @param {Number} packet id
 * @api private
 */

Socket.prototype.ack = function(id){
  var self = this;
  var sent = false;
  return function(){
    // prevent double callbacks
    if (sent) return;
    var args = Array.prototype.slice.call(arguments);
    debug('sending ack %j', args);
    self.packet({
      id: id,
      type: parser.ACK,
      data: args
    });
  };
};

/**
 * Called upon ack packet.
 *
 * @api private
 */

Socket.prototype.onack = function(packet){
  var ack = this.acks[packet.id];
  if ('function' == typeof ack) {
    debug('calling ack %s with %j', packet.id, packet.data);
    ack.apply(this, packet.data);
    delete this.acks[packet.id];
  } else {
    debug('bad ack %s', packet.id);
  }
};

/**
 * Called upon client disconnect packet.
 *
 * @api private
 */

Socket.prototype.ondisconnect = function(){
  debug('got disconnect packet');
  this.onclose('client namespace disconnect');
};

/**
 * Called upon closing. Called by `Client`.
 *
 * @param {String} reason
 * @api private
 */

Socket.prototype.onclose = function(reason){
  if (!this.connected) return this;
  debug('closing socket - reason %s', reason);
  this.leaveAll();
  this.nsp.remove(this);
  this.client.remove(this);
  this.connected = false;
  this.disconnected = true;
  delete this.nsp.connected[this.id];
  this.emit('disconnect', reason);
};

/**
 * Produces an `error` packet.
 *
 * @param {Object} error object
 * @api private
 */

Socket.prototype.error = function(err){
  this.packet({ type: parser.ERROR, data: err });
};

/**
 * Disconnects this client.
 *
 * @param {Boolean} if `true`, closes the underlying connection
 * @return {Socket} self
 * @api public
 */

Socket.prototype.disconnect = function(close){
  if (!this.connected) return this;
  if (close) {
    this.client.disconnect();
  } else {
    this.packet({ type: parser.DISCONNECT });
    this.onclose('server namespace disconnect');
  }
  return this;
};
<|endoftext|>


<commit_before>/*
 Originally created on 11/29/16 by KhanCS
*/
(function(context) {
    // Load the Processing.js script from CDNJS
    context.ka.loadScript('https://cdnjs.cloudflare.com/ajax/libs/processing.js/1.6.3/processing.min.js'.addEventListener('load',
     function() {
        context.document.currentScript._ka_loaded("loaded!");
    });
})(window);
<commit_msg>Add functionality to lib/khancs/webpage-pjs.js<commit_after>/*
 Originally created on 11/29/16 by KhanCS
*/
(function(context) {
    // Load the Processing.js script from CDNJS
    context.ka.loadScript('https://cdnjs.cloudflare.com/ajax/libs/processing.js/1.6.3/processing.min.js').addEventListener('load',
     function() {
        /**
         * Runs specified KA/Processing.js code within the specified canvas
         * @param canvas      The canvas in which to evaluate the code
         * @param scriptText  The text of the Khan Academy-style Processing.js code
         */
        var runPJS = function(canvas, scriptText) {
            var instance = new context.Processing(canvas, function(env) {
                env.ka = context.ka; // Add the ka library loader to the PJS environment
                with (env) {
                    context.eval(scriptText); // Evaluate the script text - MAKE SURE it is trusted code!
                }
            });
        };
        var scripts = context.document.querySelectorAll('script[type="text/ka-pjs"]');
        var selector, canvas;
        for (var i = 0; i < scripts.length; i++) {
            selector = scripts[i].getAttribute('data-canvas');
            if (typeof selector !== 'string') {
                throw 'KA/webpage/pjs: Could not find attribute "data-canvas" on element "script[type=text/ka-pjs]".';
            }
            canvas = document.querySelectorAll(selector);
            if (canvas.length === 0) {
                throw 'KA/webpage/pjs: Could not find canvas "' + selector + '" in document.';
            }
            for (var ii = 0; ii < canvas.length; ii++) {
                runPJS(canvas[ii], scripts[i].innerHTML);
            }
        }
        context.document.currentScript._ka_loaded({
            runPJS: runPJS
        });
    });
})(window);
<|endoftext|>