<commit_before>import { defaultStyle, defaultAttributes } from "../rendering/morphic-default.js";
import { h } from "virtual-dom";
import { arr, string } from "lively.lang";
import { pt, Rectangle } from "lively.graphics";

const newline = "\n",
      newlineLength = 1; /*fixme make work for cr lf windows...*/

// TODO: Would probably be cleaner to apply padding to a div containing the "entire" selection layer...
function selectionLayerPart(startPos, endPos, padding = Rectangle.inset(0,0,0,0)) {
  return h('div.selection-layer-part', {
    style: {
      pointerEvents: "none", position: "absolute",
      left: startPos.x + padding.left() + "px", top: startPos.y + padding.top() + "px",
      width: (endPos.x-startPos.x) + "px", height: (endPos.y-startPos.y)+"px",
      backgroundColor: "#bed8f7", zIndex: -3
    }
  })
}

// TODO: Would probably be cleaner to apply padding to a div containing the "entire" selection layer...
function cursor(pos, height, padding = Rectangle.inset(0,0,0,0)) {
    return h('div.selection-layer-part', {
    style: {
      pointerEvents: "none", position: "absolute",
      left: pos.x + padding.left() + "px", top: pos.y + padding.top() + "px",
      width: "2px", height: height + "px",
      backgroundColor: "black", zIndex: -2
    }
  })
}

class RenderedChunk {

  constructor(text, config) {
    // config: {fontFamily, fontSize, fontMetric, fontColor}
    this.config = config;
    this.text = text;

    this.rendered = undefined;
    this._charBounds = undefined;
    this._width = undefined;
    this._height = undefined;
    return this;
  }

  compatibleWith(text2, fontFamily2, fontSize2, fontMetric2, fontColor2, fontKerning2) {
    var {text, config: {fontFamily, fontSize, fontMetric, fontColor, fontKerning}} = this;
    return text       === text2
        && fontFamily === fontFamily2
        && fontSize   === fontSize2
        && fontColor  === fontColor2
        && fontMetric === fontMetric2
        && fontKerning === fontKerning2;
  }

  get height() {
    if (this._height === undefined) this.computeBounds();
    return this._height;
  }

  get width() {
    if (this._width === undefined) this.computeBounds();
    return this._width;
  }

  get length() { return this.text.length; }

  get charBounds() {
    if (this._charBounds === undefined) this.computeCharBounds();
    return this._charBounds;
  }

  computeBounds() {
    let {text, config: {fontFamily, fontSize, fontMetric}} = this,
        {height, width} = fontMetric.sizeForStr(fontFamily, fontSize, text);

    this._height = height;
    this._width = width;
    return this;
  }

  computeCharBounds() {
    let {text, config: {fontFamily, fontSize, fontMetric}} = this;
    text += newline;
    let nCols = text.length,
        _charBounds = this._charBounds = new Array(nCols);
    for (let col = 0, x = 0; col < nCols; col++) {
      let {width,height} = fontMetric.sizeFor(fontFamily, fontSize, text[col]);
      _charBounds[col] = {x, y: 0, width, height};
      x += width;
    }
  }

  render() {
    if (this.rendered) return this.rendered;
    var {config: {fontSize, fontFamily, fontColor, fontKerning}, text, width, height} = this,
        textNodes = text ? fontKerning ? text
                                       : text.split("").map(c => h("span", c))
                         : h("br");
    fontColor = fontColor || "";

    return this.rendered = h("div", {
      style: {
        pointerEvents: "none",
        fontSize: fontSize + "px",
        fontFamily,
        lineHeight: "initial",
        color: fontColor.isColor ? fontColor.toString() : String(fontColor)
      }
    }, textNodes);
  }

  boundsFor(column) {
    var charBounds = this.charBounds
    return charBounds[column] || charBounds[charBounds.length-1];
  }

  xOffsetFor(column) {
    var bounds = this.boundsFor(column);
    return bounds ? bounds.x : 0;
  }

  columnForXOffset(xInPixels) {
    var {charBounds} = this,
        length = charBounds.length;
    if (!length || xInPixels < charBounds[0].x) return 0;
    if (xInPixels >= charBounds[length-1].x) return length-1;
    return charBounds.findIndex(({x, width}) => xInPixels >= x && xInPixels < x+width);
  }
}

export default class TextLayout {

  constructor(fontMetric) {
    this.layoutComputed = false;
    this.chunks = [];
    this.fontMetric = fontMetric;
  }

  updateFromMorphIfNecessary(morph) {
    if (this.layoutComputed) return;

    let {fontFamily, fontSize, fontColor, fontKerning, document} = morph,
        fontMetric = this.fontMetric,
        lines = document.lines,
        nRows = lines.length;

    // for now: 1 line = 1 chunk
    for (let row = 0; row < nRows; row++) {
      var chunk = this.chunks[row];
      if (!chunk || !chunk.compatibleWith(lines[row], fontFamily, fontSize, fontColor, fontKerning, fontMetric))
        this.chunks[row] = new RenderedChunk(lines[row], {fontFamily, fontSize, fontColor, fontKerning, fontMetric});
    }

    this.chunks.splice(nRows, this.chunks.length - nRows);
    this.layoutComputed = true;
    return this;
  }

  renderMorph(renderer, morph) {
    this.updateFromMorphIfNecessary(morph);

    return h("div", {
      ...defaultAttributes(morph),
      style: {
        ...defaultStyle(morph),
        cursor: morph.nativeCursor === "auto" ?
          (morph.readOnly ? "default" : "text") :
          morph.nativeCursor
      }
    }, this.renderSelectionLayer(morph)
        .concat(morph.debug ? this.renderDebugLayer(morph) : [])
        .concat(this.renderTextLayer(morph))
        .concat(renderer.renderSubmorphs(morph))
      );
  }

  renderSelectionLayer(morph) {
    // FIXME just hacked together... needs cleanup!!!

    var {start, end} = morph.selection,
        {padding, document} = morph;

    if (start > end) [end, start] = [start, end];

    var chunks        = this.chunks,
        startTextPos  = document.indexToPosition(start),
        endTextPos    = document.indexToPosition(end),
        startPos      = this.pixelPositionFor(morph, startTextPos),
        endPos        = this.pixelPositionFor(morph, endTextPos),
        endLineHeight = chunks[endTextPos.row].height;

    // collapsed selection -> cursor
    if (start === end) {
      if (morph.rejectsInput()) return [];
      let {fontFamily, fontSize} = morph,
          chunkAtCursor = chunks[startTextPos.row],
          h = chunkAtCursor ? chunkAtCursor.height : this.fontMetric.defaultLineHeight(fontFamily, fontSize);
      return [cursor(startPos, chunks[startTextPos.row].height, padding)];
    }

    // single line -> one rectangle
    if (startTextPos.row === endTextPos.row) {
      return [selectionLayerPart(startPos, endPos.addXY(0, endLineHeight), padding)]
    }

    var endPosLine1 = pt(morph.width, startPos.y + chunks[startTextPos.row].height),
        startPosLine2 = pt(0, endPosLine1.y);

    // two lines -> two rectangles
    if (startTextPos.row+1 === endTextPos.row) {
      return [
        selectionLayerPart(startPos, endPosLine1, padding),
        selectionLayerPart(startPosLine2, endPos.addXY(0, endLineHeight), padding)];
    }

    var endPosMiddle = pt(morph.width, endPos.y),
        startPosLast = pt(0, endPos.y);

    // 3+ lines -> three rectangles
    return [
      selectionLayerPart(startPos, endPosLine1, padding),
      selectionLayerPart(startPosLine2, endPosMiddle, padding),
      selectionLayerPart(startPosLast, endPos.addXY(0, endLineHeight), padding)];

  }

  renderTextLayer(morph) {
    let {chunks} = this,
        textWidth = 0, textHeight = 0,
        {y: visibleTop} = morph.scroll,
        visibleBottom = visibleTop + morph.height,
        {padding} = morph,
        lastVisibleLineBottom = 0,
        row = 0,
        spacerBefore,
        renderedLines = [],
        spacerAfter;

    for (;row < chunks.length; row++) {
      let {width, height} = chunks[row],
          newTextHeight = textHeight + height;
      if (newTextHeight >= visibleTop) break;
      textWidth = Math.max(width, textWidth);
      textHeight += height;
    }

    spacerBefore = h("div", {style: {height: textHeight+"px", width: textWidth+"px"}});

    for (;row < chunks.length; row++) {
      let {width, height} = chunks[row];
      if (textHeight > visibleBottom) break;
      renderedLines.push(chunks[row].render());

      textWidth = Math.max(width, textWidth);
      textHeight += height;
    }

    lastVisibleLineBottom = textHeight;

    for (;row < chunks.length; row++) {
      let {width, height} = chunks[row];
      textWidth = Math.max(width, textWidth);
      textHeight += height;
    }

    spacerAfter = h("div", {style: {height: textHeight-lastVisibleLineBottom+"px", width: textWidth+"px"}});

    return h('div.text-layer', {
      style: {
        pointerEvents: "none", whiteSpace: "pre",
        width: textWidth+"px", height: textHeight+"px",
        padding: `${padding.top()}px ${padding.right()}px ${padding.bottom()}px ${padding.left()}px`
      }
    }, [spacerBefore].concat(renderedLines).concat(spacerAfter));
  }

  renderDebugLayer(morph) {
    let {chunks} = this,
        {y: visibleTop} = morph.scroll,
        visibleBottom = visibleTop + morph.height,
        {padding} = morph,
        debugHighlights = [],
        paddingLeft = padding.left(),
        textHeight = padding.top(),
        textWidth = 0;

    for (let row = 0; row < chunks.length; row++) {
      let {width, height, charBounds} = chunks[row];
      for (let col = 0; col < charBounds.length; col++) {
        let {x, width, height} = charBounds[col];
        x += paddingLeft;
        debugHighlights.push(h("div", {
          style: {
            position: "absolute",
            left: x+"px",
            top: textHeight+"px",
            width: width+"px",
            height: height+"px",
            outline: "1px solid orange",
            pointerEvents: "none",
            zIndex: -1
          }
        }))
      }

      textHeight += height;
      textWidth = Math.max(textWidth, width);
      if (textHeight < visibleTop || textHeight > visibleBottom) continue;
    }

    debugHighlights.push(h("div", {
      style: {
        position: "absolute",
        left: padding.left()+"px",
        top: padding.top()+"px",
        width: textWidth+"px",
        height: textHeight+"px",
        outline: "1px solid red",
        pointerEvents: "none",
        zIndex: -1
      }
    }));

    return debugHighlights
  }

  pixelPositionFor(morph, pos) {
    var { x, y } = this.boundsFor(morph, pos);
    return pt(x, y);
  }

  pixelPositionForIndex(morph, index) {
    var pos = morph.document.indexToPosition(index);
    return this.pixelPositionFor(morph, pos);
  }

  textPositionFor(morph, pos) {
    this.updateFromMorphIfNecessary(morph);
    var {chunks} = this;
    if (!chunks.length) return {row: 0, column: 0};

    let {x,y: remainingHeight} = pos, line, row;
    if (remainingHeight < 0) remainingHeight = 0;
    for (row = 0; row < chunks.length; row++) {
      line = chunks[row];
      if (remainingHeight < line.height) break;
      remainingHeight -= line.height;
    }

    return {row, column: line.columnForXOffset(x)};
  }

  textIndexFor(morph, point) {
    var pos = this.textPositionFor(morph, point);
    return morph.document.positionToIndex(pos);
  }

  textBounds(morph) {
    this.updateFromMorphIfNecessary(morph);
    let textWidth = 0, textHeight = 0;
    for (let row = 0; row < this.chunks.length; row++) {
      var {width, height} = this.chunks[row];
      textWidth = Math.max(width, textWidth);
      textHeight += height;
    }
    return new Rectangle(0,0, textWidth, textHeight);
  }

  boundsFor(morph, {row, column}) {
    this.updateFromMorphIfNecessary(morph);
    let chunks = this.chunks,
        maxLength = chunks.length-1,
        safeRow = Math.max(0, Math.min(maxLength, row)),
        line = chunks[safeRow];

    if (!line) return pt(0,0);

    for (var y = 0, i = 0; i < safeRow; i++)
      y += chunks[i].height;
    let { x, width, height } = line.boundsFor(column);
    return new Rectangle(x, y, width, height);
  }

  boundsForIndex(morph, index) {
    this.updateFromMorphIfNecessary(morph);
    var pos = morph.document.indexToPosition(index);
    return this.boundsFor(morph, pos);
  }
}

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
this is going towards tokenization, we will do this later in a seperate
tokenizer:

processLine(line, x, y, fontFamily, fontSize) {
  let fontMetric = this.fontMetric,
      rendered = [],
      [text] = line,
      maxHeight = 0,
      state = text === " " ? "space" : "text";

  for (let col = 1; col < line.length; col++) {
    let newState = line[col] === " " ? "space" : "text";
    if (newState !== state) {
      let {height, width} = fontMetric.sizeForStr(fontFamily, fontSize, text);
      rendered.push({x, y, height, text})
      maxHeight = Math.max(maxHeight, height);
      x += width;
      text = line[col];
      state = newState;
    } else {
      text += line[col];
    };
  }

  if (text.length) {
    let {height, width} = fontMetric.sizeForStr(fontFamily, fontSize, text);
    rendered.push({x, y, height, text})
    maxHeight = Math.max(maxHeight, height);
  }

  return {maxHeight, rendered};
}
*/<commit_msg>text rendering: adjust char bounds for kerning<commit_after>import { defaultStyle, defaultAttributes } from "../rendering/morphic-default.js";
import { h } from "virtual-dom";
import { arr, string } from "lively.lang";
import { pt, Rectangle } from "lively.graphics";

const newline = "\n",
      newlineLength = 1; /*fixme make work for cr lf windows...*/

// TODO: Would probably be cleaner to apply padding to a div containing the "entire" selection layer...
function selectionLayerPart(startPos, endPos, padding = Rectangle.inset(0,0,0,0)) {
  return h('div.selection-layer-part', {
    style: {
      pointerEvents: "none", position: "absolute",
      left: startPos.x + padding.left() + "px", top: startPos.y + padding.top() + "px",
      width: (endPos.x-startPos.x) + "px", height: (endPos.y-startPos.y)+"px",
      backgroundColor: "#bed8f7", zIndex: -3
    }
  })
}

// TODO: Would probably be cleaner to apply padding to a div containing the "entire" selection layer...
function cursor(pos, height, padding = Rectangle.inset(0,0,0,0)) {
    return h('div.selection-layer-part', {
    style: {
      pointerEvents: "none", position: "absolute",
      left: pos.x + padding.left() + "px", top: pos.y + padding.top() + "px",
      width: "2px", height: height + "px",
      backgroundColor: "black", zIndex: -2
    }
  })
}

class RenderedChunk {

  constructor(text, config) {
    // config: {fontFamily, fontSize, fontMetric, fontColor}
    this.config = config;
    this.text = text;

    this.rendered = undefined;
    this._charBounds = undefined;
    this._width = undefined;
    this._height = undefined;
    return this;
  }

  compatibleWith(text2, fontFamily2, fontSize2, fontMetric2, fontColor2, fontKerning2) {
    var {text, config: {fontFamily, fontSize, fontMetric, fontColor, fontKerning}} = this;
    return text       === text2
        && fontFamily === fontFamily2
        && fontSize   === fontSize2
        && fontColor  === fontColor2
        && fontMetric === fontMetric2
        && fontKerning === fontKerning2;
  }

  get height() {
    if (this._height === undefined) this.computeBounds();
    return this._height;
  }

  get width() {
    if (this._width === undefined) this.computeBounds();
    return this._width;
  }

  get length() { return this.text.length; }

  get charBounds() {
    if (this._charBounds === undefined) this.computeCharBounds();
    return this._charBounds;
  }

  computeBounds() {
    let {text, config: {fontFamily, fontSize, fontMetric}} = this,
        {height, width} = fontMetric.sizeForStr(fontFamily, fontSize, text);

    this._height = height;
    this._width = width;
    return this;
  }

  computeCharBounds() {
    let {text, config: {fontFamily, fontSize, fontMetric, fontKerning}} = this;
    text += newline;
    let nCols = text.length,
        _charBounds = this._charBounds = new Array(nCols);
    for (let col = 0, x = 0; col < nCols; col++) {
      let {width,height} = fontMetric.sizeFor(fontFamily, fontSize, text[col]);
      if (fontKerning && col < nCols - 1)
        width += fontMetric.kerningFor(fontFamily, fontSize, text[col], text[col+1]);
      _charBounds[col] = {x, y: 0, width, height};
      x += width;
    }
  }

  render() {
    if (this.rendered) return this.rendered;
    var {config: {fontSize, fontFamily, fontColor, fontKerning}, text, width, height} = this,
        textNodes = text ? fontKerning ? text
                                       : text.split("").map(c => h("span", c))
                         : h("br");
    fontColor = fontColor || "";

    return this.rendered = h("div", {
      style: {
        pointerEvents: "none",
        fontSize: fontSize + "px",
        fontFamily,
        lineHeight: "initial",
        color: fontColor.isColor ? fontColor.toString() : String(fontColor)
      }
    }, textNodes);
  }

  boundsFor(column) {
    var charBounds = this.charBounds
    return charBounds[column] || charBounds[charBounds.length-1];
  }

  xOffsetFor(column) {
    var bounds = this.boundsFor(column);
    return bounds ? bounds.x : 0;
  }

  columnForXOffset(xInPixels) {
    var {charBounds} = this,
        length = charBounds.length;
    if (!length || xInPixels < charBounds[0].x) return 0;
    if (xInPixels >= charBounds[length-1].x) return length-1;
    return charBounds.findIndex(({x, width}) => xInPixels >= x && xInPixels < x+width);
  }
}

export default class TextLayout {

  constructor(fontMetric) {
    this.layoutComputed = false;
    this.chunks = [];
    this.fontMetric = fontMetric;
  }

  updateFromMorphIfNecessary(morph) {
    if (this.layoutComputed) return;

    let {fontFamily, fontSize, fontColor, fontKerning, document} = morph,
        fontMetric = this.fontMetric,
        lines = document.lines,
        nRows = lines.length;

    // for now: 1 line = 1 chunk
    for (let row = 0; row < nRows; row++) {
      var chunk = this.chunks[row];
      if (!chunk || !chunk.compatibleWith(lines[row], fontFamily, fontSize, fontColor, fontKerning, fontMetric))
        this.chunks[row] = new RenderedChunk(lines[row], {fontFamily, fontSize, fontColor, fontKerning, fontMetric});
    }

    this.chunks.splice(nRows, this.chunks.length - nRows);
    this.layoutComputed = true;
    return this;
  }

  renderMorph(renderer, morph) {
    this.updateFromMorphIfNecessary(morph);

    return h("div", {
      ...defaultAttributes(morph),
      style: {
        ...defaultStyle(morph),
        cursor: morph.nativeCursor === "auto" ?
          (morph.readOnly ? "default" : "text") :
          morph.nativeCursor
      }
    }, this.renderSelectionLayer(morph)
        .concat(morph.debug ? this.renderDebugLayer(morph) : [])
        .concat(this.renderTextLayer(morph))
        .concat(renderer.renderSubmorphs(morph))
      );
  }

  renderSelectionLayer(morph) {
    // FIXME just hacked together... needs cleanup!!!

    var {start, end} = morph.selection,
        {padding, document} = morph;

    if (start > end) [end, start] = [start, end];

    var chunks        = this.chunks,
        startTextPos  = document.indexToPosition(start),
        endTextPos    = document.indexToPosition(end),
        startPos      = this.pixelPositionFor(morph, startTextPos),
        endPos        = this.pixelPositionFor(morph, endTextPos),
        endLineHeight = chunks[endTextPos.row].height;

    // collapsed selection -> cursor
    if (start === end) {
      if (morph.rejectsInput()) return [];
      let {fontFamily, fontSize} = morph,
          chunkAtCursor = chunks[startTextPos.row],
          h = chunkAtCursor ? chunkAtCursor.height : this.fontMetric.defaultLineHeight(fontFamily, fontSize);
      return [cursor(startPos, chunks[startTextPos.row].height, padding)];
    }

    // single line -> one rectangle
    if (startTextPos.row === endTextPos.row) {
      return [selectionLayerPart(startPos, endPos.addXY(0, endLineHeight), padding)]
    }

    var endPosLine1 = pt(morph.width, startPos.y + chunks[startTextPos.row].height),
        startPosLine2 = pt(0, endPosLine1.y);

    // two lines -> two rectangles
    if (startTextPos.row+1 === endTextPos.row) {
      return [
        selectionLayerPart(startPos, endPosLine1, padding),
        selectionLayerPart(startPosLine2, endPos.addXY(0, endLineHeight), padding)];
    }

    var endPosMiddle = pt(morph.width, endPos.y),
        startPosLast = pt(0, endPos.y);

    // 3+ lines -> three rectangles
    return [
      selectionLayerPart(startPos, endPosLine1, padding),
      selectionLayerPart(startPosLine2, endPosMiddle, padding),
      selectionLayerPart(startPosLast, endPos.addXY(0, endLineHeight), padding)];

  }

  renderTextLayer(morph) {
    let {chunks} = this,
        textWidth = 0, textHeight = 0,
        {y: visibleTop} = morph.scroll,
        visibleBottom = visibleTop + morph.height,
        {padding} = morph,
        lastVisibleLineBottom = 0,
        row = 0,
        spacerBefore,
        renderedLines = [],
        spacerAfter;

    for (;row < chunks.length; row++) {
      let {width, height} = chunks[row],
          newTextHeight = textHeight + height;
      if (newTextHeight >= visibleTop) break;
      textWidth = Math.max(width, textWidth);
      textHeight += height;
    }

    spacerBefore = h("div", {style: {height: textHeight+"px", width: textWidth+"px"}});

    for (;row < chunks.length; row++) {
      let {width, height} = chunks[row];
      if (textHeight > visibleBottom) break;
      renderedLines.push(chunks[row].render());

      textWidth = Math.max(width, textWidth);
      textHeight += height;
    }

    lastVisibleLineBottom = textHeight;

    for (;row < chunks.length; row++) {
      let {width, height} = chunks[row];
      textWidth = Math.max(width, textWidth);
      textHeight += height;
    }

    spacerAfter = h("div", {style: {height: textHeight-lastVisibleLineBottom+"px", width: textWidth+"px"}});

    return h('div.text-layer', {
      style: {
        pointerEvents: "none", whiteSpace: "pre",
        width: textWidth+"px", height: textHeight+"px",
        padding: `${padding.top()}px ${padding.right()}px ${padding.bottom()}px ${padding.left()}px`
      }
    }, [spacerBefore].concat(renderedLines).concat(spacerAfter));
  }

  renderDebugLayer(morph) {
    let {chunks} = this,
        {y: visibleTop} = morph.scroll,
        visibleBottom = visibleTop + morph.height,
        {padding} = morph,
        debugHighlights = [],
        paddingLeft = padding.left(),
        textHeight = padding.top(),
        textWidth = 0;

    for (let row = 0; row < chunks.length; row++) {
      let {width, height, charBounds} = chunks[row];
      for (let col = 0; col < charBounds.length; col++) {
        let {x, width, height} = charBounds[col];
        x += paddingLeft;
        debugHighlights.push(h("div", {
          style: {
            position: "absolute",
            left: x+"px",
            top: textHeight+"px",
            width: width+"px",
            height: height+"px",
            outline: "1px solid orange",
            pointerEvents: "none",
            zIndex: -1
          }
        }))
      }

      textHeight += height;
      textWidth = Math.max(textWidth, width);
      if (textHeight < visibleTop || textHeight > visibleBottom) continue;
    }

    debugHighlights.push(h("div", {
      style: {
        position: "absolute",
        left: padding.left()+"px",
        top: padding.top()+"px",
        width: textWidth+"px",
        height: textHeight+"px",
        outline: "1px solid red",
        pointerEvents: "none",
        zIndex: -1
      }
    }));

    return debugHighlights
  }

  pixelPositionFor(morph, pos) {
    var { x, y } = this.boundsFor(morph, pos);
    return pt(x, y);
  }

  pixelPositionForIndex(morph, index) {
    var pos = morph.document.indexToPosition(index);
    return this.pixelPositionFor(morph, pos);
  }

  textPositionFor(morph, pos) {
    this.updateFromMorphIfNecessary(morph);
    var {chunks} = this;
    if (!chunks.length) return {row: 0, column: 0};

    let {x,y: remainingHeight} = pos, line, row;
    if (remainingHeight < 0) remainingHeight = 0;
    for (row = 0; row < chunks.length; row++) {
      line = chunks[row];
      if (remainingHeight < line.height) break;
      remainingHeight -= line.height;
    }

    return {row, column: line.columnForXOffset(x)};
  }

  textIndexFor(morph, point) {
    var pos = this.textPositionFor(morph, point);
    return morph.document.positionToIndex(pos);
  }

  textBounds(morph) {
    this.updateFromMorphIfNecessary(morph);
    let textWidth = 0, textHeight = 0;
    for (let row = 0; row < this.chunks.length; row++) {
      var {width, height} = this.chunks[row];
      textWidth = Math.max(width, textWidth);
      textHeight += height;
    }
    return new Rectangle(0,0, textWidth, textHeight);
  }

  boundsFor(morph, {row, column}) {
    this.updateFromMorphIfNecessary(morph);
    let chunks = this.chunks,
        maxLength = chunks.length-1,
        safeRow = Math.max(0, Math.min(maxLength, row)),
        line = chunks[safeRow];

    if (!line) return pt(0,0);

    for (var y = 0, i = 0; i < safeRow; i++)
      y += chunks[i].height;
    let { x, width, height } = line.boundsFor(column);
    return new Rectangle(x, y, width, height);
  }

  boundsForIndex(morph, index) {
    this.updateFromMorphIfNecessary(morph);
    var pos = morph.document.indexToPosition(index);
    return this.boundsFor(morph, pos);
  }
}

/* -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
this is going towards tokenization, we will do this later in a seperate
tokenizer:

processLine(line, x, y, fontFamily, fontSize) {
  let fontMetric = this.fontMetric,
      rendered = [],
      [text] = line,
      maxHeight = 0,
      state = text === " " ? "space" : "text";

  for (let col = 1; col < line.length; col++) {
    let newState = line[col] === " " ? "space" : "text";
    if (newState !== state) {
      let {height, width} = fontMetric.sizeForStr(fontFamily, fontSize, text);
      rendered.push({x, y, height, text})
      maxHeight = Math.max(maxHeight, height);
      x += width;
      text = line[col];
      state = newState;
    } else {
      text += line[col];
    };
  }

  if (text.length) {
    let {height, width} = fontMetric.sizeForStr(fontFamily, fontSize, text);
    rendered.push({x, y, height, text})
    maxHeight = Math.max(maxHeight, height);
  }

  return {maxHeight, rendered};
}
*/<|endoftext|>


<commit_before>/**
 * The KeyEvent object gives us all the constants for key codes (should it not exist in the browser).
 * @see http://stackoverflow.com/a/1465409/545137
 * @author dnelson
 * @since 1.0.0
 */

if (typeof KeyEvent == 'undefined') {
  var KeyEvent = {
    DOM_VK_CANCEL: 3,
    DOM_VK_HELP: 6,
    DOM_VK_BACK_SPACE: 8,
    DOM_VK_TAB: 9,
    DOM_VK_CLEAR: 12,
    DOM_VK_RETURN: 13,
    DOM_VK_ENTER: 14,
    DOM_VK_SHIFT: 16,
    DOM_VK_CONTROL: 17,
    DOM_VK_ALT: 18,
    DOM_VK_PAUSE: 19,
    DOM_VK_CAPS_LOCK: 20,
    DOM_VK_ESCAPE: 27,
    DOM_VK_SPACE: 32,
    DOM_VK_PAGE_UP: 33,
    DOM_VK_PAGE_DOWN: 34,
    DOM_VK_END: 35,
    DOM_VK_HOME: 36,
    DOM_VK_LEFT: 37,
    DOM_VK_UP: 38,
    DOM_VK_RIGHT: 39,
    DOM_VK_DOWN: 40,
    DOM_VK_PRINTSCREEN: 44,
    DOM_VK_INSERT: 45,
    DOM_VK_DELETE: 46,
    DOM_VK_0: 48,
    DOM_VK_1: 49,
    DOM_VK_2: 50,
    DOM_VK_3: 51,
    DOM_VK_4: 52,
    DOM_VK_5: 53,
    DOM_VK_6: 54,
    DOM_VK_7: 55,
    DOM_VK_8: 56,
    DOM_VK_9: 57,
    DOM_VK_SEMICOLON: 59,
    DOM_VK_EQUALS: 61,
    DOM_VK_A: 65,
    DOM_VK_B: 66,
    DOM_VK_C: 67,
    DOM_VK_D: 68,
    DOM_VK_E: 69,
    DOM_VK_F: 70,
    DOM_VK_G: 71,
    DOM_VK_H: 72,
    DOM_VK_I: 73,
    DOM_VK_J: 74,
    DOM_VK_K: 75,
    DOM_VK_L: 76,
    DOM_VK_M: 77,
    DOM_VK_N: 78,
    DOM_VK_O: 79,
    DOM_VK_P: 80,
    DOM_VK_Q: 81,
    DOM_VK_R: 82,
    DOM_VK_S: 83,
    DOM_VK_T: 84,
    DOM_VK_U: 85,
    DOM_VK_V: 86,
    DOM_VK_W: 87,
    DOM_VK_X: 88,
    DOM_VK_Y: 89,
    DOM_VK_Z: 90,
    DOM_VK_CONTEXT_MENU: 93,
    DOM_VK_NUMPAD0: 96,
    DOM_VK_NUMPAD1: 97,
    DOM_VK_NUMPAD2: 98,
    DOM_VK_NUMPAD3: 99,
    DOM_VK_NUMPAD4: 100,
    DOM_VK_NUMPAD5: 101,
    DOM_VK_NUMPAD6: 102,
    DOM_VK_NUMPAD7: 103,
    DOM_VK_NUMPAD8: 104,
    DOM_VK_NUMPAD9: 105,
    DOM_VK_MULTIPLY: 106,
    DOM_VK_ADD: 107,
    DOM_VK_SEPARATOR: 108,
    DOM_VK_SUBTRACT: 109,
    DOM_VK_DECIMAL: 110,
    DOM_VK_DIVIDE: 111,
    DOM_VK_F1: 112,
    DOM_VK_F2: 113,
    DOM_VK_F3: 114,
    DOM_VK_F4: 115,
    DOM_VK_F5: 116,
    DOM_VK_F6: 117,
    DOM_VK_F7: 118,
    DOM_VK_F8: 119,
    DOM_VK_F9: 120,
    DOM_VK_F10: 121,
    DOM_VK_F11: 122,
    DOM_VK_F12: 123,
    DOM_VK_F13: 124,
    DOM_VK_F14: 125,
    DOM_VK_F15: 126,
    DOM_VK_F16: 127,
    DOM_VK_F17: 128,
    DOM_VK_F18: 129,
    DOM_VK_F19: 130,
    DOM_VK_F20: 131,
    DOM_VK_F21: 132,
    DOM_VK_F22: 133,
    DOM_VK_F23: 134,
    DOM_VK_F24: 135,
    DOM_VK_NUM_LOCK: 144,
    DOM_VK_SCROLL_LOCK: 145,
    DOM_VK_COMMA: 188,
    DOM_VK_PERIOD: 190,
    DOM_VK_SLASH: 191,
    DOM_VK_BACK_QUOTE: 192,
    DOM_VK_OPEN_BRACKET: 219,
    DOM_VK_BACK_SLASH: 220,
    DOM_VK_CLOSE_BRACKET: 221,
    DOM_VK_QUOTE: 222,
    DOM_VK_META: 224
  };
}
<commit_msg>:recycle: Using exported module<commit_after>/**
 * The KeyEvent object gives us all the constants for key codes (should it not exist in the browser).
 * @see http://stackoverflow.com/a/1465409/545137
 * @author dnelson
 * @since 1.0.0
 */
const KeyEvent = {
  DOM_VK_CANCEL: 3,
  DOM_VK_HELP: 6,
  DOM_VK_BACK_SPACE: 8,
  DOM_VK_TAB: 9,
  DOM_VK_CLEAR: 12,
  DOM_VK_RETURN: 13,
  DOM_VK_ENTER: 14,
  DOM_VK_SHIFT: 16,
  DOM_VK_CONTROL: 17,
  DOM_VK_ALT: 18,
  DOM_VK_PAUSE: 19,
  DOM_VK_CAPS_LOCK: 20,
  DOM_VK_ESCAPE: 27,
  DOM_VK_SPACE: 32,
  DOM_VK_PAGE_UP: 33,
  DOM_VK_PAGE_DOWN: 34,
  DOM_VK_END: 35,
  DOM_VK_HOME: 36,
  DOM_VK_LEFT: 37,
  DOM_VK_UP: 38,
  DOM_VK_RIGHT: 39,
  DOM_VK_DOWN: 40,
  DOM_VK_PRINTSCREEN: 44,
  DOM_VK_INSERT: 45,
  DOM_VK_DELETE: 46,
  DOM_VK_0: 48,
  DOM_VK_1: 49,
  DOM_VK_2: 50,
  DOM_VK_3: 51,
  DOM_VK_4: 52,
  DOM_VK_5: 53,
  DOM_VK_6: 54,
  DOM_VK_7: 55,
  DOM_VK_8: 56,
  DOM_VK_9: 57,
  DOM_VK_SEMICOLON: 59,
  DOM_VK_EQUALS: 61,
  DOM_VK_A: 65,
  DOM_VK_B: 66,
  DOM_VK_C: 67,
  DOM_VK_D: 68,
  DOM_VK_E: 69,
  DOM_VK_F: 70,
  DOM_VK_G: 71,
  DOM_VK_H: 72,
  DOM_VK_I: 73,
  DOM_VK_J: 74,
  DOM_VK_K: 75,
  DOM_VK_L: 76,
  DOM_VK_M: 77,
  DOM_VK_N: 78,
  DOM_VK_O: 79,
  DOM_VK_P: 80,
  DOM_VK_Q: 81,
  DOM_VK_R: 82,
  DOM_VK_S: 83,
  DOM_VK_T: 84,
  DOM_VK_U: 85,
  DOM_VK_V: 86,
  DOM_VK_W: 87,
  DOM_VK_X: 88,
  DOM_VK_Y: 89,
  DOM_VK_Z: 90,
  DOM_VK_CONTEXT_MENU: 93,
  DOM_VK_NUMPAD0: 96,
  DOM_VK_NUMPAD1: 97,
  DOM_VK_NUMPAD2: 98,
  DOM_VK_NUMPAD3: 99,
  DOM_VK_NUMPAD4: 100,
  DOM_VK_NUMPAD5: 101,
  DOM_VK_NUMPAD6: 102,
  DOM_VK_NUMPAD7: 103,
  DOM_VK_NUMPAD8: 104,
  DOM_VK_NUMPAD9: 105,
  DOM_VK_MULTIPLY: 106,
  DOM_VK_ADD: 107,
  DOM_VK_SEPARATOR: 108,
  DOM_VK_SUBTRACT: 109,
  DOM_VK_DECIMAL: 110,
  DOM_VK_DIVIDE: 111,
  DOM_VK_F1: 112,
  DOM_VK_F2: 113,
  DOM_VK_F3: 114,
  DOM_VK_F4: 115,
  DOM_VK_F5: 116,
  DOM_VK_F6: 117,
  DOM_VK_F7: 118,
  DOM_VK_F8: 119,
  DOM_VK_F9: 120,
  DOM_VK_F10: 121,
  DOM_VK_F11: 122,
  DOM_VK_F12: 123,
  DOM_VK_F13: 124,
  DOM_VK_F14: 125,
  DOM_VK_F15: 126,
  DOM_VK_F16: 127,
  DOM_VK_F17: 128,
  DOM_VK_F18: 129,
  DOM_VK_F19: 130,
  DOM_VK_F20: 131,
  DOM_VK_F21: 132,
  DOM_VK_F22: 133,
  DOM_VK_F23: 134,
  DOM_VK_F24: 135,
  DOM_VK_NUM_LOCK: 144,
  DOM_VK_SCROLL_LOCK: 145,
  DOM_VK_COMMA: 188,
  DOM_VK_PERIOD: 190,
  DOM_VK_SLASH: 191,
  DOM_VK_BACK_QUOTE: 192,
  DOM_VK_OPEN_BRACKET: 219,
  DOM_VK_BACK_SLASH: 220,
  DOM_VK_CLOSE_BRACKET: 221,
  DOM_VK_QUOTE: 222,
  DOM_VK_META: 224,
};

export default KeyEvent;
<|endoftext|>


<commit_before>myApp.service('inventoryService', function($http) {
    this.setCurrentInventory = function(inv) {
        this.inventory = inv;
    };

    this.getCurrentInventory = function() {
        return this.inventory;
    };

    this.loadInventories = function() {
        var invStr = localStorage.getItem('inventories'), ret;
        if(invStr) {
            ret = new Promise(function(resolve, reject) {
                resolve(JSON.parse(invStr));
            });
        }
        else {
            ret = $http.get('inventories.json');
        }

        return ret;
    };

    this.putInventories = function(inventories) {
        localStorage.setItem('inventories', JSON.stringify(inventories));
    };
});

<commit_msg>Fix InventoryService#loadInventories return object<commit_after>myApp.service('inventoryService', function($http) {
    this.setCurrentInventory = function(inv) {
        this.inventory = inv;
    };

    this.getCurrentInventory = function() {
        return this.inventory;
    };

    this.loadInventories = function() {
        var invStr = localStorage.getItem('inventories'), ret;
        if(invStr) {
            ret = new Promise(function(resolve, reject) {
                resolve(JSON.parse(invStr));
            });
        }
        else {
            ret = $http.get('inventories.json')
                        .then(function(res) {
                           return res.data;
                        });
        }

        return ret;
    };

    this.putInventories = function(inventories) {
        localStorage.setItem('inventories', JSON.stringify(inventories));
    };
});

<|endoftext|>


<commit_before>require('core-js/modules/es6.object.assign');
require('core-js/modules/es6.object.set-prototype-of');
require('core-js/modules/es6.promise');
require('core-js/modules/es6.set');
require('core-js/modules/es6.map');
require('core-js/modules/es6.array.from');
require('core-js/modules/es6.array.iterator');
require('core-js/modules/es6.symbol');

/**
 * @namespace baqend
 *
 * @borrows baqend.EntityManager#User
 * @borrows baqend.EntityManager#Role
 * @borrows baqend.EntityManager#Device
 * @borrows baqend.EntityManager#File
 * @borrows baqend.EntityManager#[YourEntityClass: string];
 */
var EntityManagerFactory = require('./EntityManagerFactory');
var EntityManager = require('./EntityManager');

EntityManager.prototype.binding = require('./binding');
EntityManager.prototype.connector = require('./connector');
EntityManager.prototype.error = require('./error');
EntityManager.prototype.message = require('./message');
EntityManager.prototype.metamodel = require('./metamodel');
EntityManager.prototype.util = require('./util');
EntityManager.prototype.caching = require('./caching');

EntityManager.prototype.EntityManager = require('./EntityManager');
EntityManager.prototype.EntityManagerFactory = require('./EntityManagerFactory');
EntityManager.prototype.EntityTransaction = require('./EntityTransaction');
EntityManager.prototype.Query = require('./Query');
EntityManager.prototype.Acl = require('./Acl');

var emf = new EntityManagerFactory();
exports = module.exports = emf.createEntityManager(true);

/**
 * Connects the DB with the baqend server and calls the callback on success
 * @param {string} hostOrApp The host or the app name to connect with
 * @param {boolean} [secure=false] true To use a secure connection
 * @param {baqend.util.Lockable~callback=} doneCallback The callback, called when a connection is established and the
 * SDK is ready to use
 * @param {baqend.util.Lockable~callback=} failCallback When an error occurred while initializing the SDK
 * @function
 * @return {Promise<baqend.EntityManager>}
 * @alias baqend#connect
 */
exports.connect = function(hostOrApp, secure, doneCallback, failCallback) {
  if (secure instanceof Function) {
    failCallback = doneCallback;
    doneCallback = secure;
    secure = undefined;
  }

  emf.connect(hostOrApp, secure);
  return this.ready(doneCallback, failCallback);
};
<commit_msg>fix jsdoc<commit_after>require('core-js/modules/es6.object.assign');
require('core-js/modules/es6.object.set-prototype-of');
require('core-js/modules/es6.promise');
require('core-js/modules/es6.set');
require('core-js/modules/es6.map');
require('core-js/modules/es6.array.from');
require('core-js/modules/es6.array.iterator');
require('core-js/modules/es6.symbol');

/**
 * @namespace baqend
 *
 * @borrows baqend.EntityManager#User
 * @borrows baqend.EntityManager#Role
 * @borrows baqend.EntityManager#Device
 * @borrows baqend.EntityManager#File
 * @borrows baqend.EntityManager#[YourEntityClass: string]
 */
var EntityManagerFactory = require('./EntityManagerFactory');
var EntityManager = require('./EntityManager');

EntityManager.prototype.binding = require('./binding');
EntityManager.prototype.connector = require('./connector');
EntityManager.prototype.error = require('./error');
EntityManager.prototype.message = require('./message');
EntityManager.prototype.metamodel = require('./metamodel');
EntityManager.prototype.util = require('./util');
EntityManager.prototype.caching = require('./caching');

EntityManager.prototype.EntityManager = require('./EntityManager');
EntityManager.prototype.EntityManagerFactory = require('./EntityManagerFactory');
EntityManager.prototype.EntityTransaction = require('./EntityTransaction');
EntityManager.prototype.Query = require('./Query');
EntityManager.prototype.Acl = require('./Acl');

var emf = new EntityManagerFactory();
exports = module.exports = emf.createEntityManager(true);

/**
 * Connects the DB with the baqend server and calls the callback on success
 * @param {string} hostOrApp The host or the app name to connect with
 * @param {boolean} [secure=false] true To use a secure connection
 * @param {baqend.util.Lockable~callback=} doneCallback The callback, called when a connection is established and the
 * SDK is ready to use
 * @param {baqend.util.Lockable~callback=} failCallback When an error occurred while initializing the SDK
 * @function
 * @return {Promise<baqend.EntityManager>}
 * @alias baqend#connect
 */
exports.connect = function(hostOrApp, secure, doneCallback, failCallback) {
  if (secure instanceof Function) {
    failCallback = doneCallback;
    doneCallback = secure;
    secure = undefined;
  }

  emf.connect(hostOrApp, secure);
  return this.ready(doneCallback, failCallback);
};
<|endoftext|>


<commit_before>// Everything in this file uses child processes, because we're
// testing a command line utility.

var chain = require("slide").chain
var child_process = require("child_process")
var path = require("path")
  , testdir = __dirname
  , fs = require("graceful-fs")
  , npmpkg = path.dirname(testdir)
  , npmcli = path.join(__dirname, "bin", "npm-cli.js")

var temp = process.env.TMPDIR
         || process.env.TMP
         || process.env.TEMP
         || ( process.platform === "win32"
            ? "c:\\windows\\temp"
            : "/tmp" )
temp = path.resolve(temp, "npm-test-" + process.pid)

var root = path.resolve(temp, "root")

var failures = 0
  , mkdir = require("mkdirp")
  , rimraf = require("rimraf")

var pathEnvSplit = process.platform === "win32" ? ";" : ":"
  , pathEnv = process.env.PATH.split(pathEnvSplit)

pathEnv.push( process.platform === "win32" ? root
                                           : path.join(root, "bin")
            , path.join(root, "node_modules", ".bin") )

// the env for all the test installs etc.
var env = {}
Object.keys(process.env).forEach(function (i) {
  env[i] = process.env[i]
})
env.npm_config_prefix = root
env.npm_config_color = "always"
env.npm_config_global = "true"
// have to set this to false, or it'll try to test itself forever
env.npm_config_npat = "false"
env.PATH = pathEnv.join(pathEnvSplit)
env.NODE_PATH = path.join(root, "node_modules")

function cleanup (cb) {
  if (failures !== 0) return
  rimraf(root, function (er) {
    if (er) cb(er)
    mkdir(root, 0755, cb)
  })
}

function prefix (content, pref) {
  return pref + (content.trim().split(/\r?\n/).join("\n" + pref))
}

var execCount = 0
function exec (cmd, shouldFail, cb) {
  if (typeof shouldFail === "function") {
    cb = shouldFail, shouldFail = false
  }
  console.error("\n+"+cmd + (shouldFail ? " (expect failure)" : ""))

  // XXX DEBUGGING
  // cmd = "echo RUN "+cmd
  // shouldFail = false

  child_process.exec(cmd, {env: env}, function (er, stdout, stderr) {
    if (stdout) {
      console.error(prefix(stdout, " 1> "))
    }
    if (stderr) {
      console.error(prefix(stderr, " 2> "))
    }

    execCount ++
    if (!shouldFail && !er || shouldFail && er) {
      // stdout = (""+stdout).trim()
      console.log("ok " + execCount + " " + cmd)
      return cb()
    } else {
      console.log("not ok " + execCount + " " + cmd)
      cb(new Error("failed "+cmd))
    }
  })
}

function execChain (cmds, cb) {
  chain(cmds.reduce(function (l, r) {
    return l.concat(r)
  }, []).map(function (cmd) {
    return [exec, cmd]
  }), cb)
}

function flatten (arr) {
  return arr.reduce(function (l, r) {
    return l.concat(r)
  }, [])
}

function setup (cb) {
  cleanup(function (er) {
    if (er) return cb(er)
    execChain(["node \""+path.resolve(npmpkg, "bin", "npm-cli.js")
              + "\" install \""+npmpkg+"\""
              ,"npm config set package-config:foo boo"
              ], cb)
  })
}

function main (cb) {
  console.log("# testing in %s", temp)
  console.log("# global prefix = %s", root)

  failures = 0

  process.chdir(testdir)

  // get the list of packages
  var packages = fs.readdirSync(path.resolve(testdir, "packages"))
  packages = packages.filter(function (p) {
    return p && !p.match(/^\./)
  })

  installAllThenTestAll()

  function installAllThenTestAll () {
    chain
      ( [ setup
        , [ exec, "npm install "+npmpkg ]
        , [ execChain, packages.map(function (p) {
              return "npm install packages/"+p
            }) ]
        , [ execChain, packages.map(function (p) {
              return "npm test "+p
            }) ]
        , [ execChain, packages.concat("npm").map(function (p) {
              return "npm rm " + p
            }) ]
        , installAndTestEach
        ]
      , cb
      )
  }

  function installAndTestEach (cb) {
    chain
      ( [ setup
        , [ execChain, packages.map(function (p) {
              return [ "npm install packages/"+p
                     , "npm test "+p
                     , "npm rm "+p ]
            }) ]
        , [exec, "npm rm npm"]
        , publishTest
        ], cb )
  }

  function publishTest (cb) {
    if (process.env.npm_package_config_publishtest !== "true") {
      console.error("To test publishing: "+
                    "npm config set npm:publishtest true")
      return cb()
    }

    chain
      ( [ setup
        , [ execChain, packages.filter(function (p) {
              return !p.match(/private/)
            }).map(function (p) {
              return [ "npm publish packages/"+p
                     , "npm install "+p
                     , "npm unpublish "+p+" --force"
                     ]
            }) ]
        , publishPrivateTest
        ], cb )

  }

  function publishPrivateTest (cb) {
    exec("npm publish packages/npm-test-private -s", true, function (er) {
      if (er) {
        exec( "npm unpublish npm-test-private --force"
            , function (e2) {
          cb(er || e2)
        })
      }
      cleanup(cb)
    })
  }
}

main(function (er) {
  console.log("1.." + execCount)
  if (er) throw er
})
<commit_msg>Run npm tests using the installed copy, current node<commit_after>// Everything in this file uses child processes, because we're
// testing a command line utility.

var chain = require("slide").chain
var child_process = require("child_process")
var path = require("path")
  , testdir = __dirname
  , fs = require("graceful-fs")
  , npmpkg = path.dirname(testdir)
  , npmcli = path.join(__dirname, "bin", "npm-cli.js")

var temp = process.env.TMPDIR
         || process.env.TMP
         || process.env.TEMP
         || ( process.platform === "win32"
            ? "c:\\windows\\temp"
            : "/tmp" )

temp = path.resolve(temp, "npm-test-" + process.pid)

var root = path.resolve(temp, "root")

var failures = 0
  , mkdir = require("mkdirp")
  , rimraf = require("rimraf")

var pathEnvSplit = process.platform === "win32" ? ";" : ":"
  , pathEnv = process.env.PATH.split(pathEnvSplit)
  , npmPath = process.platform === "win32" ? root : path.join(root, "bin")

pathEnv.unshift(npmPath, path.join(root, "node_modules", ".bin"))

// lastly, make sure that we get the same node that is being used to do
// run this script.  That's very important, especially when running this
// test file from in the node source folder.
pathEnv.unshift(path.dirname(process.execPath))

// the env for all the test installs etc.
var env = {}
Object.keys(process.env).forEach(function (i) {
  env[i] = process.env[i]
})
env.npm_config_prefix = root
env.npm_config_color = "always"
env.npm_config_global = "true"
// have to set this to false, or it'll try to test itself forever
env.npm_config_npat = "false"
env.PATH = pathEnv.join(pathEnvSplit)
env.NODE_PATH = path.join(root, "node_modules")

function cleanup (cb) {
  if (failures !== 0) return
  rimraf(root, function (er) {
    if (er) cb(er)
    mkdir(root, 0755, cb)
  })
}

function prefix (content, pref) {
  return pref + (content.trim().split(/\r?\n/).join("\n" + pref))
}

var execCount = 0
function exec (cmd, shouldFail, cb) {
  if (typeof shouldFail === "function") {
    cb = shouldFail, shouldFail = false
  }
  console.error("\n+"+cmd + (shouldFail ? " (expect failure)" : ""))

  // special: replace 'node' with the current execPath,
  // and 'npm' with the thing we installed.
  cmd = cmd.replace(/^npm /, path.resolve(npmPath, "npm") + " ")
  cmd = cmd.replace(/^node /, process.execPath + " ")

  child_process.exec(cmd, {env: env}, function (er, stdout, stderr) {
    if (stdout) {
      console.error(prefix(stdout, " 1> "))
    }
    if (stderr) {
      console.error(prefix(stderr, " 2> "))
    }

    execCount ++
    if (!shouldFail && !er || shouldFail && er) {
      // stdout = (""+stdout).trim()
      console.log("ok " + execCount + " " + cmd)
      return cb()
    } else {
      console.log("not ok " + execCount + " " + cmd)
      cb(new Error("failed "+cmd))
    }
  })
}

function execChain (cmds, cb) {
  chain(cmds.reduce(function (l, r) {
    return l.concat(r)
  }, []).map(function (cmd) {
    return [exec, cmd]
  }), cb)
}

function flatten (arr) {
  return arr.reduce(function (l, r) {
    return l.concat(r)
  }, [])
}

function setup (cb) {
  cleanup(function (er) {
    if (er) return cb(er)
    execChain([ "node \""+path.resolve(npmpkg, "bin", "npm-cli.js")
              + "\" install \""+npmpkg+"\""
              , "npm config set package-config:foo boo"
              ], cb)
  })
}

function main (cb) {
  console.log("# testing in %s", temp)
  console.log("# global prefix = %s", root)

  failures = 0

  process.chdir(testdir)

  // get the list of packages
  var packages = fs.readdirSync(path.resolve(testdir, "packages"))
  packages = packages.filter(function (p) {
    return p && !p.match(/^\./)
  })

  installAllThenTestAll()

  function installAllThenTestAll () {
    chain
      ( [ setup
        , [ exec, "npm install "+npmpkg ]
        , [ execChain, packages.map(function (p) {
              return "npm install packages/"+p
            }) ]
        , [ execChain, packages.map(function (p) {
              return "npm test "+p
            }) ]
        , [ execChain, packages.concat("npm").map(function (p) {
              return "npm rm " + p
            }) ]
        , installAndTestEach
        ]
      , cb
      )
  }

  function installAndTestEach (cb) {
    chain
      ( [ setup
        , [ execChain, packages.map(function (p) {
              return [ "npm install packages/"+p
                     , "npm test "+p
                     , "npm rm "+p ]
            }) ]
        , [exec, "npm rm npm"]
        , publishTest
        ], cb )
  }

  function publishTest (cb) {
    if (process.env.npm_package_config_publishtest !== "true") {
      console.error("To test publishing: "+
                    "npm config set npm:publishtest true")
      return cb()
    }

    chain
      ( [ setup
        , [ execChain, packages.filter(function (p) {
              return !p.match(/private/)
            }).map(function (p) {
              return [ "npm publish packages/"+p
                     , "npm install "+p
                     , "npm unpublish "+p+" --force"
                     ]
            }) ]
        , publishPrivateTest
        ], cb )

  }

  function publishPrivateTest (cb) {
    exec("npm publish packages/npm-test-private -s", true, function (er) {
      if (er) {
        exec( "npm unpublish npm-test-private --force"
            , function (e2) {
          cb(er || e2)
        })
      }
      cleanup(cb)
    })
  }
}

main(function (er) {
  console.log("1.." + execCount)
  if (er) throw er
})
<|endoftext|>


<commit_before>'use strict';

var deepFreeze = function deepFreeze(o) {
  Object.freeze(o);

  Object.getOwnPropertyNames(o).forEach(function (prop) {
    if (o.hasOwnProperty(prop)
    && o[prop] !== null
    && (typeof o[prop] === 'object' || typeof o[prop] === 'function')
    && !Object.isFrozen(o[prop])) {
      deepFreeze(o[prop]);
    }
  });

  return o;
};

var filters = [
  {
    id: 'filterOne',
    label: 'Filter one',
    type: 'text',
    value: 'Value one'
  },
  {
    id: 'filterTwo',
    label: 'Filter two',
    type: 'text',
    value: 'Value two'
  }
];

var filter = {
  id: 'filterThree',
  label: 'Filter three',
  type: 'text'
};

var facets = filters.concat([filter]).concat({ id: 'filterFour', label: 'Filter four', type: 'text' });

var filteredBy = {
  filterOne: 'Value one',
  filterTwo: 'Value two'
};

describe('Facetly Utils', function () {

  beforeEach(module('ngFacetly'));

  var Utils;

  deepFreeze(filters);
  deepFreeze(facets);

  beforeEach(inject(function (_FacetlyUtils_) {
    Utils = _FacetlyUtils_;
  }));

  it('should set filters', function () {
    var setFilters = Utils.setFilters(filteredBy, facets);
    expect(setFilters.length).toEqual(2);
  });

  it('should remove all filters', function () {
    var emptyFilters = Utils.removeAllFilters();
    expect(emptyFilters).toEqual([]);
  });

  it('should add a filter to the list', function () {
    var filtersAfter = Utils.addFilter(filters, filter);
    expect(filtersAfter.length).toEqual(filters.length + 1);
    expect(filtersAfter).toEqual(filters.concat([filter]));
  });

  it('should remove a filter from the list', function () {
    var filtersAfter = Utils.removeFilter(filters, filters[0].id);
    expect(filtersAfter.length).toEqual(filters.length - 1);
  });

  it('should find a filter by key', function () {
    var filterFound = Utils.findFilterByKey(filters, 'id', filters[0].id);
    expect(filterFound).toEqual(0);
  });

  it('should update the model', function () {
    var model = Utils.updateModel(filters);
    expect(model).toEqual(filteredBy);
  });

  it('should flatten an array', function () {
    var hierarchy = [
      {
        id: '1',
        title: 'Level 1',
        categories: [
          {
            id: '11',
            title: 'Level 1.1',
            categories: [
              {
                id: '12',
                title: 'Level 1.2'
              }
            ]
          }
        ]
      }
    ];
    var flat = Utils.flatten(hierarchy);
    expect(flat).toEqual([
      { id: '1', title: 'Level 1' },
      { id: '11', title: 'Level 1.1' },
      { id: '12', title: 'Level 1.2' }
    ]);
  });

  it('should get ID or IDS of a set of values', function () {

  });

});
<commit_msg>Add test for getFacetIds #nochangelog<commit_after>'use strict';

var deepFreeze = function deepFreeze(o) {
  Object.freeze(o);

  Object.getOwnPropertyNames(o).forEach(function (prop) {
    if (o.hasOwnProperty(prop)
    && o[prop] !== null
    && (typeof o[prop] === 'object' || typeof o[prop] === 'function')
    && !Object.isFrozen(o[prop])) {
      deepFreeze(o[prop]);
    }
  });

  return o;
};

var filters = [
  {
    id: 'filterOne',
    label: 'Filter one',
    type: 'text',
    value: 'Value one'
  },
  {
    id: 'filterTwo',
    label: 'Filter two',
    type: 'text',
    value: 'Value two'
  }
];

var filter = {
  id: 'filterThree',
  label: 'Filter three',
  type: 'text'
};

var facets = filters.concat([filter]).concat({ id: 'filterFour', label: 'Filter four', type: 'text' });

var filteredBy = {
  filterOne: 'Value one',
  filterTwo: 'Value two'
};

describe('Facetly Utils', function () {

  beforeEach(module('ngFacetly'));

  var Utils;

  deepFreeze(filters);
  deepFreeze(facets);

  beforeEach(inject(function (_FacetlyUtils_) {
    Utils = _FacetlyUtils_;
  }));

  it('should set filters', function () {
    var setFilters = Utils.setFilters(filteredBy, facets);
    expect(setFilters.length).toEqual(2);
  });

  it('should remove all filters', function () {
    var emptyFilters = Utils.removeAllFilters();
    expect(emptyFilters).toEqual([]);
  });

  it('should add a filter to the list', function () {
    var filtersAfter = Utils.addFilter(filters, filter);
    expect(filtersAfter.length).toEqual(filters.length + 1);
    expect(filtersAfter).toEqual(filters.concat([filter]));
  });

  it('should remove a filter from the list', function () {
    var filtersAfter = Utils.removeFilter(filters, filters[0].id);
    expect(filtersAfter.length).toEqual(filters.length - 1);
  });

  it('should find a filter by key', function () {
    var filterFound = Utils.findFilterByKey(filters, 'id', filters[0].id);
    expect(filterFound).toEqual(0);
  });

  it('should update the model', function () {
    var model = Utils.updateModel(filters);
    expect(model).toEqual(filteredBy);
  });

  it('should flatten an array', function () {
    var hierarchy = [
      {
        id: '1',
        title: 'Level 1',
        categories: [
          {
            id: '11',
            title: 'Level 1.1',
            categories: [
              {
                id: '12',
                title: 'Level 1.2'
              }
            ]
          }
        ]
      }
    ];
    var flat = Utils.flatten(hierarchy);
    expect(flat).toEqual([
      { id: '1', title: 'Level 1' },
      { id: '11', title: 'Level 1.1' },
      { id: '12', title: 'Level 1.2' }
    ]);
  });

  it('should get ID or IDS of a set of values', function () {
    var available = [
      { id: '1', title: 'Level 1' },
      { id: '11', title: 'Level 1.1' },
      { id: '12', title: 'Level 1.2' }
    ];
    var singleId = Utils.getFacetIds('Level 1', available);
    var multipleIds = Utils.getFacetIds(['Level 1', 'Level 1.1'], available);

    expect(singleId).toEqual('1');
    expect(multipleIds).toEqual(['1', '11']);
  });

});
<|endoftext|>


<commit_before>import { QueryBuilder as ObjectionQueryBuilder } from 'objection'

export class QueryBuilder extends ObjectionQueryBuilder {

	subset(limit, offset = 0) {
		return this.limit(limit).offset(offset)
	}

}
<commit_msg>QueryBuilder.subset can now handle a single parameter object with limit/offset as keys<commit_after>import { QueryBuilder as ObjectionQueryBuilder } from 'objection'

export class QueryBuilder extends ObjectionQueryBuilder {

	subset(limit, offset = 0) {
		if(typeof limit === 'object') {
			offset = limit.offset || 0
			limit = limit.limit
		}

		return this.limit(limit).offset(offset)
	}

}
<|endoftext|>


<commit_before>var jsonTimetable = [
    {
        time : "04/15/2021 04:22:00",
        fasting : false
    },
    {
        time : "04/15/2021 20:01:00",
        fasting : true
    },
    {
        time : "04/16/2021 04:19:00",
        fasting : false
    },
    {
        time : "04/16/2021 20:03:00",
        fasting : true
    },
    {
        time : "04/17/2021 04:17:00",
        fasting : false
    },
    {
        time : "04/17/2021 20:04:00",
        fasting : true
    },
    {
        time : "04/18/2021 04:15:00",
        fasting : false
    },
    {
        time : "04/18/2021 20:06:00",
        fasting : true
    },
    {
        time : "04/19/2021 04:12:00",
        fasting : false
    },
    {
        time : "04/19/2021 20:08:00",
        fasting : true
    },
    {
        time : "04/20/2021 04:10:00",
        fasting : false
    },
    {
        time : "04/20/2021 20:10:00",
        fasting : true
    },
    {
        time : "04/21/2021 04:07:00",
        fasting : false
    },
    {
        time : "04/21/2021 20:11:00",
        fasting : true
    },
    {
        time : "04/22/2021 04:05:00",
        fasting : false
    },
    {
        time : "04/22/2021 20:13:00",
        fasting : true
    },
    {
        time : "04/23/2021 04:03:00",
        fasting : false
    },
    {
        time : "04/23/2021 20:15:00",
        fasting : true
    },
    {
        time : "04/24/2021 04:00:00",
        fasting : false
    },
    {
        time : "04/24/2021 20:16:00",
        fasting : true
    },
    {
        time : "04/25/2021 03:58:00",
        fasting : false
    },
    {
        time : "04/25/2021 20:18:00",
        fasting : true
    },
    {
        time : "04/26/2021 03:56:00",
        fasting : false
    },
    {
        time : "04/26/2021 20:20:00",
        fasting: true
    },
    {
        time : "04/27/2021 03:53:00",
        fasting : false
    },
    {
        time : "04/27/2021 20:22:00",
        fasting: true
    },
    {
        time : "04/28/2021 03:51:00",
        fasting : false
    },
    {
        time : "04/28/2021 20:23:00",
        fasting : true
    },
    {
        time : "04/29/2021 03:49:00",
        fasting: false
    },
    {
        time : "04/29/2021 20:25:00",
        fasting : true
    },
    {
        time : "04/30/2021 03:46:00",
        fasting: false
    },
    {
        time : "04/30/2021 20:27:00",
        fasting : true
    },
    {
        time : "05/01/2021 03:44:00",
        fasting: false
    },
    {
        time : "05/01/2021 20:28:00",
        fasting : true
    },
    {
        time : "05/02/2021 03:42:00",
        fasting: false
    },
    {
        time : "05/02/2021 20:30:00",
        fasting : true
    },
    {
        time : "05/03/2021 03:40:00",
        fasting: false
    },
    {
        time : "05/03/2021 20:32:00",
        fasting : true
    },
    {
        time : "05/04/2021 03:38:00",
        fasting: false
    },
    {
        time : "05/04/2021 20:33:00",
        fasting : true
    },
    {
        time : "05/05/2021 03:36:00",
        fasting: false
    },
    {
        time : "05/05/2021 20:35:00",
        fasting : true
    },
    {
        time : "05/06/2021 03:34:00",
        fasting: false
    },
    {
        time : "05/06/2021 20:37:00",
        fasting : true
    },
    {
        time : "05/07/2021 03:31:00",
        fasting: false
    },
    {
        time : "05/07/2021 20:38:00",
        fasting : true
    },
    {
        time : "05/08/2021 03:29:00",
        fasting: false
    },
    {
        time : "05/08/2021 20:40:00",
        fasting : true
    },
    {
        time : "05/09/2021 03:27:00",
        fasting: false
    },
    {
        time : "05/09/2021 20:42:00",
        fasting : true
    },
    {
        time : "05/10/2021 03:25:00",
        fasting: false
    },
    {
        time : "05/10/2021 20:43:00",
        fasting : true
    },
    {
        time : "05/11/2021 03:23:00",
        fasting: false
    },
    {
        time : "05/11/2021 20:45:00",
        fasting : true
    },
    {
        time : "05/12/2021 03:21:00",
        fasting: false
    },
    {
        time : "05/12/2021 20:46:00",
        fasting : true
    },
    {
        time : "05/13/2021 03:19:00",
        fasting: false
    },
    {
        time : "05/13/2021 20:48:00",
        fasting : true
    }
]
<commit_msg>2022 dyno reset<commit_after>var jsonTimetable = [
    {
        time : "04/03/2022 04:53:00",
        fasting : false
    },
    {
        time : "04/15/2021 20:01:00",
        fasting : true
    },
    {
        time : "04/16/2021 04:19:00",
        fasting : false
    },
    {
        time : "04/16/2021 20:03:00",
        fasting : true
    },
    {
        time : "04/17/2021 04:17:00",
        fasting : false
    },
    {
        time : "04/17/2021 20:04:00",
        fasting : true
    },
    {
        time : "04/18/2021 04:15:00",
        fasting : false
    },
    {
        time : "04/18/2021 20:06:00",
        fasting : true
    },
    {
        time : "04/19/2021 04:12:00",
        fasting : false
    },
    {
        time : "04/19/2021 20:08:00",
        fasting : true
    },
    {
        time : "04/20/2021 04:10:00",
        fasting : false
    },
    {
        time : "04/20/2021 20:10:00",
        fasting : true
    },
    {
        time : "04/21/2021 04:07:00",
        fasting : false
    },
    {
        time : "04/21/2021 20:11:00",
        fasting : true
    },
    {
        time : "04/22/2021 04:05:00",
        fasting : false
    },
    {
        time : "04/22/2021 20:13:00",
        fasting : true
    },
    {
        time : "04/23/2021 04:03:00",
        fasting : false
    },
    {
        time : "04/23/2021 20:15:00",
        fasting : true
    },
    {
        time : "04/24/2021 04:00:00",
        fasting : false
    },
    {
        time : "04/24/2021 20:16:00",
        fasting : true
    },
    {
        time : "04/25/2021 03:58:00",
        fasting : false
    },
    {
        time : "04/25/2021 20:18:00",
        fasting : true
    },
    {
        time : "04/26/2021 03:56:00",
        fasting : false
    },
    {
        time : "04/26/2021 20:20:00",
        fasting: true
    },
    {
        time : "04/27/2021 03:53:00",
        fasting : false
    },
    {
        time : "04/27/2021 20:22:00",
        fasting: true
    },
    {
        time : "04/28/2021 03:51:00",
        fasting : false
    },
    {
        time : "04/28/2021 20:23:00",
        fasting : true
    },
    {
        time : "04/29/2021 03:49:00",
        fasting: false
    },
    {
        time : "04/29/2021 20:25:00",
        fasting : true
    },
    {
        time : "04/30/2021 03:46:00",
        fasting: false
    },
    {
        time : "04/30/2021 20:27:00",
        fasting : true
    },
    {
        time : "05/01/2021 03:44:00",
        fasting: false
    },
    {
        time : "05/01/2021 20:28:00",
        fasting : true
    },
    {
        time : "05/02/2021 03:42:00",
        fasting: false
    },
    {
        time : "05/02/2021 20:30:00",
        fasting : true
    },
    {
        time : "05/03/2021 03:40:00",
        fasting: false
    },
    {
        time : "05/03/2021 20:32:00",
        fasting : true
    },
    {
        time : "05/04/2021 03:38:00",
        fasting: false
    },
    {
        time : "05/04/2021 20:33:00",
        fasting : true
    },
    {
        time : "05/05/2021 03:36:00",
        fasting: false
    },
    {
        time : "05/05/2021 20:35:00",
        fasting : true
    },
    {
        time : "05/06/2021 03:34:00",
        fasting: false
    },
    {
        time : "05/06/2021 20:37:00",
        fasting : true
    },
    {
        time : "05/07/2021 03:31:00",
        fasting: false
    },
    {
        time : "05/07/2021 20:38:00",
        fasting : true
    },
    {
        time : "05/08/2021 03:29:00",
        fasting: false
    },
    {
        time : "05/08/2021 20:40:00",
        fasting : true
    },
    {
        time : "05/09/2021 03:27:00",
        fasting: false
    },
    {
        time : "05/09/2021 20:42:00",
        fasting : true
    },
    {
        time : "05/10/2021 03:25:00",
        fasting: false
    },
    {
        time : "05/10/2021 20:43:00",
        fasting : true
    },
    {
        time : "05/11/2021 03:23:00",
        fasting: false
    },
    {
        time : "05/11/2021 20:45:00",
        fasting : true
    },
    {
        time : "05/12/2021 03:21:00",
        fasting: false
    },
    {
        time : "05/12/2021 20:46:00",
        fasting : true
    },
    {
        time : "05/13/2021 03:19:00",
        fasting: false
    },
    {
        time : "05/13/2021 20:48:00",
        fasting : true
    }
]
<|endoftext|>


<commit_before>function viewUsers(view) {

  getUsers(
    success = function (data) {
      //console.log(data);
      populateUsersTable(data);
    },
    error = function (jqxhr) {
      handleApiError(jqxhr);
    }
  );

  function populateUsersTable(data) {

    var usersHtml = templates.userList(data);
    var isEditor = false;
    $('.section').html(usersHtml);

    $('.collections-select-table tbody tr').click(function () {
      $('.collections-select-table tbody tr').removeClass('selected');
      $(this).addClass('selected');
      var userId = $(this).attr('data-id');
      viewUserDetails(userId);
    });

    $('.radioBtnDiv').change(function () {
      if($('input:checked').val() === 'publisher') {
        isEditor = true;
        alert(isEditor);
      } else {
        isEditor = false;
        alert(isEditor);
      }
    });

    $('.form-create-user').submit(function (e) {
      e.preventDefault();

      var username = $('#create-user-username').val();
      var email = $('#create-user-email').val();
      var password = $('#create-user-password').val();

      if (username.length < 1) {
        sweetAlert("Please enter the users name.");
        return;
      }

      if (email.length < 1) {
        sweetAlert("Please enter the users name.");
        return;
      }

      if (password.length < 1) {
        sweetAlert("Please enter the users password.");
        return;
      }
      postUser(username, email, password, isEditor);
    });
  }
}

<commit_msg>Refresh page after user creation<commit_after>function viewUsers(view) {

  getUsers(
    success = function (data) {
      //console.log(data);
      populateUsersTable(data);
    },
    error = function (jqxhr) {
      handleApiError(jqxhr);
    }
  );

  function populateUsersTable(data) {

    var usersHtml = templates.userList(data);
    var isEditor = false;
    $('.section').html(usersHtml);

    $('.collections-select-table tbody tr').click(function () {
      $('.collections-select-table tbody tr').removeClass('selected');
      $(this).addClass('selected');
      var userId = $(this).attr('data-id');
      viewUserDetails(userId);
    });

    $('.radioBtnDiv').change(function () {
      if($('input:checked').val() === 'publisher') {
        isEditor = true;
      } else {
        isEditor = false;
      }
    });

    $('.form-create-user').submit(function (e) {
      e.preventDefault();

      var username = $('#create-user-username').val();
      var email = $('#create-user-email').val();
      var password = $('#create-user-password').val();

      if (username.length < 1) {
        sweetAlert("Please enter the users name.");
        return;
      }

      if (email.length < 1) {
        sweetAlert("Please enter the users name.");
        return;
      }

      if (password.length < 1) {
        sweetAlert("Please enter the users password.");
        return;
      }
      postUser(username, email, password, isEditor);
      viewUsers();
    });
  }
}

<|endoftext|>


<commit_before>// Load in dependencies
var gui = require('nw.gui');
var GoogleMusic = require('./google-music');

// Set up app
var app = gui.App;
var googleMusic = null;

// Generate a window for Google Music
// https://github.com/rogerwang/node-webkit/wiki/Window
// TODO: Can we change window.location for the same effect?
// TODO: Probably but the reason they usually open a subwindow is so we can act on a close event instead of exiting immediately
var googleWindow = window.open('https://play.google.com/music');
var googleWebkit = gui.Window.get(googleWindow);

// TODO: Fill out package.json in full
// TODO: Add tray menu for all methods we want
// TODO: Look into icon for taskbar item https://github.com/rogerwang/node-webkit/wiki/Icons
// TODO: Add/remove tray menu items when we leave (should be reused though for memory leaksO

// Create our tray icon
// DEV: Icon via http://lorempixel.com/32/32/abstract/3/
// https://github.com/rogerwang/node-webkit/wiki/Tray
var tray = new gui.Tray({icon: 'lib/icon.png'});

// Add a menu to our tray with bindings for GoogleMusic
var trayMenu = new gui.Menu();
// TODO: Add/remove play/pause depending on if music is playing or not
trayMenu.append(new gui.MenuItem({
  label: 'Play/Pause',
  click: function () {
    // TODO: If we are not logged in, prompt for login
    if (googleMusic) {
      googleMusic.playPause();
    }
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Quit',
  click: function () {
    app.quit();
  }
}));
tray.menu = trayMenu;

// When the tray icon is clicked, toggle the window state
googleWebkit.isVisible = true;
tray.on('click', function maximizeFromTray () {
  // If it is visible, hide it
  if (googleWebkit.isVisible) {
    googleWebkit.minimize();
    googleWebkit.isVisible = false;
  // Otherwise, show it
  } else {
    googleWebkit.restore();
    googleWebkit.isVisible = true;
  }
});

// When we minimize/restore our window, remember the state
googleWebkit.on('minimize', function minimizeToTray () {
  googleWebkit.isVisible = false;
});

// When we close the googleWebkit window, close the app
// https://github.com/rogerwang/node-webkit/wiki/Window#closed
googleWebkit.on('closed', function closeApp () {
  app.quit();
});

// When our url changes
// Create a GoogleMusic instance for our window
googleWebkit.on('loaded', function () {
  // If we have arrived to a logged in state in Google Music
  var loc = googleWindow.location;
  if (loc.href.indexOf('//play.google.com/music') !== -1 && loc.hash !== '') {
    // If we need to add scripts
    if (!googleMusic) {
      // Bind to the window
      googleMusic = new GoogleMusic(googleWindow);

      // When the song changes, update the tooltip
      // Data looks like: {title, artist, album, art, duration}
      googleMusic.on('song:changed', function (data) {
        tray.tooltip = [
          'Title: ' + data.title,
          'Artist: ' + data.artist,
          'Album: ' + data.album,
        ].join('\n');
      });
    }
  // If we have left Google Music (e.g. left Google Music), clean up google music
  } else {
    delete tray.tooltip;
    if (googleMusic) {
      googleMusic.removeAllListeners();
    }
    googleMusic = null;
  }
});
<commit_msg>Added previous/next buttons<commit_after>// Load in dependencies
var gui = require('nw.gui');
var GoogleMusic = require('./google-music');

// Set up app
var app = gui.App;
var googleMusic = null;

// Generate a window for Google Music
// https://github.com/rogerwang/node-webkit/wiki/Window
// TODO: Can we change window.location for the same effect?
// TODO: Probably but the reason they usually open a subwindow is so we can act on a close event instead of exiting immediately
var googleWindow = window.open('https://play.google.com/music');
var googleWebkit = gui.Window.get(googleWindow);

// TODO: Fill out package.json in full
// TODO: Add tray menu for all methods we want
// TODO: Look into icon for taskbar item https://github.com/rogerwang/node-webkit/wiki/Icons
// TODO: Add/remove tray menu items when we leave (should be reused though for memory leaksO

// Create our tray icon
// DEV: Icon via http://lorempixel.com/32/32/abstract/3/
// https://github.com/rogerwang/node-webkit/wiki/Tray
var tray = new gui.Tray({icon: 'lib/icon.png'});

// Add a menu to our tray with bindings for GoogleMusic
var trayMenu = new gui.Menu();
// TODO: Add/remove play/pause depending on if music is playing or not
trayMenu.append(new gui.MenuItem({
  label: 'Play/Pause',
  click: function () {
    // TODO: If we are not logged in, prompt for login
    if (googleMusic) {
      googleMusic.playPause();
    }
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Next',
  click: function () {
    if (googleMusic) {
      googleMusic.forward();
    }
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Previous',
  click: function () {
    if (googleMusic) {
      googleMusic.rewind();
    }
  }
}));
trayMenu.append(new gui.MenuItem({
  label: 'Quit',
  click: function () {
    app.quit();
  }
}));
tray.menu = trayMenu;

// When the tray icon is clicked, toggle the window state
googleWebkit.isVisible = true;
tray.on('click', function maximizeFromTray () {
  // If it is visible, hide it
  if (googleWebkit.isVisible) {
    googleWebkit.minimize();
    googleWebkit.isVisible = false;
  // Otherwise, show it
  } else {
    googleWebkit.restore();
    googleWebkit.isVisible = true;
  }
});

// When we minimize/restore our window, remember the state
googleWebkit.on('minimize', function minimizeToTray () {
  googleWebkit.isVisible = false;
});

// When we close the googleWebkit window, close the app
// https://github.com/rogerwang/node-webkit/wiki/Window#closed
googleWebkit.on('closed', function closeApp () {
  app.quit();
});

// When our url changes
// Create a GoogleMusic instance for our window
googleWebkit.on('loaded', function () {
  // If we have arrived to a logged in state in Google Music
  var loc = googleWindow.location;
  if (loc.href.indexOf('//play.google.com/music') !== -1 && loc.hash !== '') {
    // If we need to add scripts
    if (!googleMusic) {
      // Bind to the window
      googleMusic = new GoogleMusic(googleWindow);

      // When the song changes, update the tooltip
      // Data looks like: {title, artist, album, art, duration}
      googleMusic.on('song:changed', function (data) {
        tray.tooltip = [
          'Title: ' + data.title,
          'Artist: ' + data.artist,
          'Album: ' + data.album,
        ].join('\n');
      });
    }
  // If we have left Google Music (e.g. left Google Music), clean up google music
  } else {
    delete tray.tooltip;
    if (googleMusic) {
      googleMusic.removeAllListeners();
    }
    googleMusic = null;
  }
});
<|endoftext|>


<commit_before>// Everything in this file uses child processes, because we're
// testing a command line utility.

var chain = require("slide").chain
var child_process = require("child_process")
var path = require("path")
  , testdir = __dirname
  , fs = require("graceful-fs")
  , npmpkg = path.dirname(testdir)
  , npmcli = path.join(__dirname, "bin", "npm-cli.js")

var temp = process.env.TMPDIR
         || process.env.TMP
         || process.env.TEMP
         || ( process.platform === "win32"
            ? "c:\\windows\\temp"
            : "/tmp" )

temp = path.resolve(temp, "npm-test-" + process.pid)

var root = path.resolve(temp, "root")

var failures = 0
  , mkdir = require("mkdirp")
  , rimraf = require("rimraf")

var pathEnvSplit = process.platform === "win32" ? ";" : ":"
  , pathEnv = process.env.PATH.split(pathEnvSplit)
  , npmPath = process.platform === "win32" ? root : path.join(root, "bin")

pathEnv.unshift(npmPath, path.join(root, "node_modules", ".bin"))

// lastly, make sure that we get the same node that is being used to do
// run this script.  That's very important, especially when running this
// test file from in the node source folder.
pathEnv.unshift(path.dirname(process.execPath))

// the env for all the test installs etc.
var env = {}
Object.keys(process.env).forEach(function (i) {
  env[i] = process.env[i]
})
env.npm_config_prefix = root
env.npm_config_color = "always"
env.npm_config_global = "true"
// have to set this to false, or it'll try to test itself forever
env.npm_config_npat = "false"
env.PATH = pathEnv.join(pathEnvSplit)
env.NODE_PATH = path.join(root, "node_modules")

function cleanup (cb) {
  if (failures !== 0) return
  rimraf(root, function (er) {
    if (er) cb(er)
    mkdir(root, 0755, cb)
  })
}

function prefix (content, pref) {
  return pref + (content.trim().split(/\r?\n/).join("\n" + pref))
}

var execCount = 0
function exec (cmd, shouldFail, cb) {
  if (typeof shouldFail === "function") {
    cb = shouldFail, shouldFail = false
  }
  console.error("\n+"+cmd + (shouldFail ? " (expect failure)" : ""))

  // special: replace 'node' with the current execPath,
  // and 'npm' with the thing we installed.
  var cmdShow = cmd
  var npmReplace = path.resolve(npmPath, "npm")
  var nodeReplace = process.execPath
  if (process.platform === "win32") {
    npmReplace = '"' + npmReplace + '"'
    nodeReplace = '"' + nodeReplace + '"'
  }
  cmd = cmd.replace(/^npm /, npmReplace + " ")
  cmd = cmd.replace(/^node /, nodeReplace + " ")

  child_process.exec(cmd, {env: env}, function (er, stdout, stderr) {
    if (stdout) {
      console.error(prefix(stdout, " 1> "))
    }
    if (stderr) {
      console.error(prefix(stderr, " 2> "))
    }

    execCount ++
    if (!shouldFail && !er || shouldFail && er) {
      // stdout = (""+stdout).trim()
      console.log("ok " + execCount + " " + cmdShow)
      return cb()
    } else {
      console.log("not ok " + execCount + " " + cmdShow)
      cb(new Error("failed "+cmdShow))
    }
  })
}

function execChain (cmds, cb) {
  chain(cmds.reduce(function (l, r) {
    return l.concat(r)
  }, []).map(function (cmd) {
    return [exec, cmd]
  }), cb)
}

function flatten (arr) {
  return arr.reduce(function (l, r) {
    return l.concat(r)
  }, [])
}

function setup (cb) {
  cleanup(function (er) {
    if (er) return cb(er)
    execChain([ "node \""+path.resolve(npmpkg, "bin", "npm-cli.js")
              + "\" install \""+npmpkg+"\""
              , "npm config set package-config:foo boo"
              ], cb)
  })
}

function main (cb) {
  console.log("# testing in %s", temp)
  console.log("# global prefix = %s", root)

  failures = 0

  process.chdir(testdir)

  // get the list of packages
  var packages = fs.readdirSync(path.resolve(testdir, "packages"))
  packages = packages.filter(function (p) {
    return p && !p.match(/^\./)
  })

  installAllThenTestAll()

  function installAllThenTestAll () {
    chain
      ( [ setup
        , [ exec, "npm install "+npmpkg ]
        , [ execChain, packages.map(function (p) {
              return "npm install packages/"+p
            }) ]
        , [ execChain, packages.map(function (p) {
              return "npm test "+p
            }) ]
        , [ execChain, packages.concat("npm").map(function (p) {
              return "npm rm " + p
            }) ]
        , installAndTestEach
        ]
      , cb
      )
  }

  function installAndTestEach (cb) {
    chain
      ( [ setup
        , [ execChain, packages.map(function (p) {
              return [ "npm install packages/"+p
                     , "npm test "+p
                     , "npm rm "+p ]
            }) ]
        , [exec, "npm rm npm"]
        , publishTest
        ], cb )
  }

  function publishTest (cb) {
    if (process.env.npm_package_config_publishtest !== "true") {
      console.error("To test publishing: "+
                    "npm config set npm:publishtest true")
      return cb()
    }

    chain
      ( [ setup
        , [ execChain, packages.filter(function (p) {
              return !p.match(/private/)
            }).map(function (p) {
              return [ "npm publish packages/"+p
                     , "npm install "+p
                     , "npm unpublish "+p+" --force"
                     ]
            }) ]
        , publishPrivateTest
        ], cb )

  }

  function publishPrivateTest (cb) {
    exec("npm publish packages/npm-test-private -s", true, function (er) {
      if (er) {
        exec( "npm unpublish npm-test-private --force"
            , function (e2) {
          cb(er || e2)
        })
      }
      cleanup(cb)
    })
  }
}

main(function (er) {
  console.log("1.." + execCount)
  if (er) throw er
})
<commit_msg>Don't "npm rm npm" on Windows.<commit_after>// Everything in this file uses child processes, because we're
// testing a command line utility.

var chain = require("slide").chain
var child_process = require("child_process")
var path = require("path")
  , testdir = __dirname
  , fs = require("graceful-fs")
  , npmpkg = path.dirname(testdir)
  , npmcli = path.join(__dirname, "bin", "npm-cli.js")

var temp = process.env.TMPDIR
         || process.env.TMP
         || process.env.TEMP
         || ( process.platform === "win32"
            ? "c:\\windows\\temp"
            : "/tmp" )

temp = path.resolve(temp, "npm-test-" + process.pid)

var root = path.resolve(temp, "root")

var failures = 0
  , mkdir = require("mkdirp")
  , rimraf = require("rimraf")

var pathEnvSplit = process.platform === "win32" ? ";" : ":"
  , pathEnv = process.env.PATH.split(pathEnvSplit)
  , npmPath = process.platform === "win32" ? root : path.join(root, "bin")

pathEnv.unshift(npmPath, path.join(root, "node_modules", ".bin"))

// lastly, make sure that we get the same node that is being used to do
// run this script.  That's very important, especially when running this
// test file from in the node source folder.
pathEnv.unshift(path.dirname(process.execPath))

// the env for all the test installs etc.
var env = {}
Object.keys(process.env).forEach(function (i) {
  env[i] = process.env[i]
})
env.npm_config_prefix = root
env.npm_config_color = "always"
env.npm_config_global = "true"
// have to set this to false, or it'll try to test itself forever
env.npm_config_npat = "false"
env.PATH = pathEnv.join(pathEnvSplit)
env.NODE_PATH = path.join(root, "node_modules")

function cleanup (cb) {
  if (failures !== 0) return
  rimraf(root, function (er) {
    if (er) cb(er)
    mkdir(root, 0755, cb)
  })
}

function prefix (content, pref) {
  return pref + (content.trim().split(/\r?\n/).join("\n" + pref))
}

var execCount = 0
function exec (cmd, shouldFail, cb) {
  if (typeof shouldFail === "function") {
    cb = shouldFail, shouldFail = false
  }
  console.error("\n+"+cmd + (shouldFail ? " (expect failure)" : ""))

  // special: replace 'node' with the current execPath,
  // and 'npm' with the thing we installed.
  var cmdShow = cmd
  var npmReplace = path.resolve(npmPath, "npm")
  var nodeReplace = process.execPath
  if (process.platform === "win32") {
    npmReplace = '"' + npmReplace + '"'
    nodeReplace = '"' + nodeReplace + '"'
  }
  cmd = cmd.replace(/^npm /, npmReplace + " ")
  cmd = cmd.replace(/^node /, nodeReplace + " ")

  child_process.exec(cmd, {env: env}, function (er, stdout, stderr) {
    if (stdout) {
      console.error(prefix(stdout, " 1> "))
    }
    if (stderr) {
      console.error(prefix(stderr, " 2> "))
    }

    execCount ++
    if (!shouldFail && !er || shouldFail && er) {
      // stdout = (""+stdout).trim()
      console.log("ok " + execCount + " " + cmdShow)
      return cb()
    } else {
      console.log("not ok " + execCount + " " + cmdShow)
      cb(new Error("failed "+cmdShow))
    }
  })
}

function execChain (cmds, cb) {
  chain(cmds.reduce(function (l, r) {
    return l.concat(r)
  }, []).map(function (cmd) {
    return [exec, cmd]
  }), cb)
}

function flatten (arr) {
  return arr.reduce(function (l, r) {
    return l.concat(r)
  }, [])
}

function setup (cb) {
  cleanup(function (er) {
    if (er) return cb(er)
    execChain([ "node \""+path.resolve(npmpkg, "bin", "npm-cli.js")
              + "\" install \""+npmpkg+"\""
              , "npm config set package-config:foo boo"
              ], cb)
  })
}

function main (cb) {
  console.log("# testing in %s", temp)
  console.log("# global prefix = %s", root)

  failures = 0

  process.chdir(testdir)

  // get the list of packages
  var packages = fs.readdirSync(path.resolve(testdir, "packages"))
  packages = packages.filter(function (p) {
    return p && !p.match(/^\./)
  })

  installAllThenTestAll()

  function installAllThenTestAll () {
    var packagesToRm = packages
    if (process.platform !== "win32") {
      // Windows can't handle npm rm npm due to file-in-use issues.
      packagesToRm.push("npm")
    }

    chain
      ( [ setup
        , [ exec, "npm install "+npmpkg ]
        , [ execChain, packages.map(function (p) {
              return "npm install packages/"+p
            }) ]
        , [ execChain, packages.map(function (p) {
              return "npm test "+p
            }) ]
        , [ execChain, packagesToRm.map(function (p) {
              return "npm rm " + p
            }) ]
        , installAndTestEach
        ]
      , cb
      )
  }

  function installAndTestEach (cb) {
    var thingsToChain = [
        setup
        , [ execChain, packages.map(function (p) {
              return [ "npm install packages/"+p
                     , "npm test "+p
                     , "npm rm "+p ]
            }) ]
      ]
    if (process.platform !== "win32") {
      // Windows can't handle npm rm npm due to file-in-use issues.
      thingsToChain.push([exec, "npm rm npm"])
    }
    thingsToChain.push(publishTest)

    chain(thingsToChain, cb)
  }

  function publishTest (cb) {
    if (process.env.npm_package_config_publishtest !== "true") {
      console.error("To test publishing: "+
                    "npm config set npm:publishtest true")
      return cb()
    }

    chain
      ( [ setup
        , [ execChain, packages.filter(function (p) {
              return !p.match(/private/)
            }).map(function (p) {
              return [ "npm publish packages/"+p
                     , "npm install "+p
                     , "npm unpublish "+p+" --force"
                     ]
            }) ]
        , publishPrivateTest
        ], cb )

  }

  function publishPrivateTest (cb) {
    exec("npm publish packages/npm-test-private -s", true, function (er) {
      if (er) {
        exec( "npm unpublish npm-test-private --force"
            , function (e2) {
          cb(er || e2)
        })
      }
      cleanup(cb)
    })
  }
}

main(function (er) {
  console.log("1.." + execCount)
  if (er) throw er
})
<|endoftext|>


<commit_before>import React, { Component } from "react";
import Card, { CardActions, CardContent, CardMedia } from 'material-ui/Card';
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';
import {dayNames} from "./utils/Strings"
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
import Divider from 'material-ui/Divider';

class Scheduleview extends Component {

  generateDayCards(){
    let cardArray = []
    for (let i = 0; i < 7; i++){
      cardArray.push(<DayCard dayName={dayNames[i]} key={i}/>)
    }
    return cardArray;
  }

  render(){
    return(

        {this.generateDayCards()}

    )
  }
}

class DayCard extends Component {
  render(){
    return (

        <Card>
          <CardContent>
            <Typography type="headline" component="h2">
              {this.props.dayName}
            </Typography>
            <Typography component="div">
              <List>
                <ListItem button>
                  <ListItemText primary="Inbox" />
                </ListItem>
                <ListItem button>
                  <ListItemText primary="Drafts" />
                </ListItem>
              </List>
            </Typography>
          </CardContent>
        </Card>

    );
  }
}

export default Scheduleview;
<commit_msg>Finalized look of schedule view<commit_after>import React, { Component } from "react";
import Card, { CardActions, CardContent, CardMedia } from 'material-ui/Card';
import Typography from 'material-ui/Typography';
import Button from 'material-ui/Button';
import {dayNames} from "./utils/Strings"
import List, { ListItem, ListItemIcon, ListItemText } from 'material-ui/List';
import Divider from 'material-ui/Divider';

class Scheduleview extends Component {

  generateDayCards(){
    let cardArray = []
    for (let i = 0; i < 7; i++){
      cardArray.push(<DayCard dayName={dayNames[i]} key={i}/>)
    }
    return cardArray;
  }

  render(){
    return(

        {this.generateDayCards()}

    )
  }
}

class DayCard extends Component {
  render(){
    return (

        <Card style={{marginTop: 9}}>
          <CardContent style={{paddingBottom: 0}}>
            <Typography type="headline" component="h2">
              {this.props.dayName}
            </Typography>
            <Divider/>
            <Typography component="div">
              <List>
                <ListItem style={{padding: 0}}>
                  <ListContent title="CSE 280" name="Sophomore Project" startTime="10:20" endTime="12:30" buildingRoom="EC 321"/>
                </ListItem>
              </List>
            </Typography>
          </CardContent>
        </Card>

    );
  }
}

const ListContent = ({title, name, startTime, endTime, buildingRoom}) => {
  console.log(title)
  return (

Course Name

{name}

Meeting Time

{`${startTime} - ${endTime}`}

Location

{buildingRoom}

  )
}

export default Scheduleview;
<|endoftext|>


<commit_before>const fs = require('fs-extra');
const inquirer = require('inquirer');
exports.command = 'clean';
exports.description = 'Remove .idyll folder';

const PATH = './.idyll';

exports.handler = async _ => {
  try {
    let isTokenExists = await checkIfTokenExists();
    if (isTokenExists) {
      let userAnswer = await confirmClean();
      if (userAnswer) {
        removeIdyll();
      }
    } else {
      removeIdyll();
    }
  } catch (e) {
    console.log(e);
  }
};

checkIfTokenExists = _ => {
  return fs.pathExists(`${PATH}/token`);
};

confirmClean = async _ => {
  const userResponse = await inquirer.prompt([
    {
      type: 'confirm',
      name: 'confirmClean',
      message: `This command will remove the idyll cache folder, including your idyll.pub token.
      Subsequent deploys to idyll.pub will receive a new URL.
      Are you sure you wish to continue?`,
      default: true
    }
  ]);
  return userResponse.confirmClean;
};

removeIdyll = _ => {
  fs.remove(PATH, err => {
    if (err) return console.error(err);
  });
};
<commit_msg>add const<commit_after>const fs = require('fs-extra');
const inquirer = require('inquirer');
exports.command = 'clean';
exports.description = 'Remove .idyll folder';

const PATH = './.idyll';

exports.handler = async _ => {
  try {
    let isTokenExists = await checkIfTokenExists();
    if (isTokenExists) {
      let userAnswer = await confirmClean();
      if (userAnswer) {
        removeIdyll();
      }
    } else {
      removeIdyll();
    }
  } catch (e) {
    console.log(e);
  }
};

const checkIfTokenExists = _ => {
  return fs.pathExists(`${PATH}/token`);
};

const confirmClean = async _ => {
  const userResponse = await inquirer.prompt([
    {
      type: 'confirm',
      name: 'confirmClean',
      message: `This command will remove the idyll cache folder, including your idyll.pub token.
      Subsequent deploys to idyll.pub will receive a new URL.
      Are you sure you wish to continue?`,
      default: true
    }
  ]);
  return userResponse.confirmClean;
};

const removeIdyll = _ => {
  fs.remove(PATH, err => {
    if (err) return console.error(err);
  });
};
<|endoftext|>