<commit_before>/*global System*/
import { string, obj } from "lively.lang";
import { Rectangle, Color, pt } from "lively.graphics";
import { Morph, show } from "../index.js";
import { Selection } from "./selection.js";
import DocumentRenderer from "./rendering.js";
import TextDocument from "./document.js";
import { KeyHandler } from "../events/keyhandler.js";
import Keys from "../events/Keys.js";
import { CommandHandler } from "../commands.js";

const defaultKeyHandler = new KeyHandler();

export class Text extends Morph {

  static makeLabel(string, props) {
    return new Text({
      textString: string,
      fontFamily: "Helvetica Neue, Arial",
      fontColor: Color.black,
      fontSize: 11,
      readOnly: true,
      ...props
    });
  }

  constructor(props = {}) {
    var {fontMetric, textString, selectable, selection} = props;
    props = obj.dissoc(props, ["textString","fontMetric", "selectable", "selection"])
    super({
      readOnly: false,
      draggable: false,
      clipMode: "hidden",
      fixedWidth: false, fixedHeight: false,
      padding: 0,
      fontFamily: "Sans-Serif",
      fontSize: 12,
      fontColor: Color.black,
      fontKerning: true,
      ...props
    });
    this.document = new TextDocument();
    this.renderer = new DocumentRenderer(fontMetric || this.env.fontMetric);
    this._keyhandlers = []; // defaultKeyHandler is fallback
    // this.commands = new CommandHandler();
    this._selection = new Selection(this, selection);
    this.selectable = typeof selectable !== "undefined" ? selectable : true;
    this.textString = textString || "";
    this.fit();
    this._needsFit = false;
  }

  get isText() { return true }

  onChange(change) {
    if (change.prop === "textString"
     || change.prop === "fontFamily"
     || change.prop === "fontSize"
     || change.prop === "fontColor" // FIXME
     || change.prop === "fixedWidth"
     || change.prop === "fixedHeight"
     || change.prop === "fontKerning")
       this.renderer && (this.renderer.layoutComputed = false);
    super.onChange(change);
  }

  get readOnly() { return this.getProperty("readOnly"); }
  set readOnly(value) {
    this.nativeCursor = value ? "default" : "auto";
    this.addValueChange("readOnly", value);
  }

  rejectsInput() { return this.readOnly || !this.isFocused() }

  get selectable() { return this.getProperty("selectable"); }
  set selectable(value) {
    this.addValueChange("selectable", value);
    if (!value) this.selection.collapse();
  }

  get fixedWidth() { return this.getProperty("fixedWidth") }
  set fixedWidth(value) {
    this.addValueChange("fixedWidth", value);
    this._needsFit = true;
  }

  get fixedHeight() { return this.getProperty("fixedHeight"); }
  set fixedHeight(value) {
    this.addValueChange("fixedHeight", value);
    this._needsFit = true;
  }

  get padding() { return this.getProperty("padding"); }
  set padding(value) {
    this.addValueChange("padding", typeof value === "number" ? Rectangle.inset(value) : value);
    this._needsFit = true;
  }

  get fontFamily() { return this.getProperty("fontFamily") }
  set fontFamily(value) {
    this.addValueChange("fontFamily", value);
    this._needsFit = true;
  }

  get fontSize() { return this.getProperty("fontSize") }
  set fontSize(value) {
    this.addValueChange("fontSize", value);
    this._needsFit = true;
  }

  get fontColor() { return this.getProperty("fontColor") }
  set fontColor(value) { this.addValueChange("fontColor", value); }

  get fontKerning() { return this.getProperty("fontKerning") }
  set fontKerning(value) { this.addValueChange("fontKerning", value); }

  get clipMode()  { return this.getProperty("clipMode"); }
  set clipMode(value)  {
    this.addValueChange("clipMode", value);
    this.fixedWidth = this.fixedHeight = this.isClip();
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // document changes

  get textString() { return this.document ? this.document.textString : "" }
  set textString(value) {
    this.document.textString = String(value);
    this.selection = {start: 0, end: 0};
    this.addValueChange("textString", value);
    this._needsFit = true;
  }

  getLine(row) {
    if (typeof row !== "number") this.cursorPosition.row
    var doc = this.document;
    return doc.getLine(row);
  }

  insertTextAndSelect(text, pos = null) {
    text = String(text);
    if (pos) this.selection.range = this.insertText(text, pos);
    else this.selection.text = text;
  }

  insertText(text, pos = null) {
    text = String(text);
    var range = this.document.insert(text, pos || this.selection.end);
    this._needsFit = true;
    this.addValueChange(
      "textString", this.document.textString,
      {action: "insertText", text, pos});
    return range;
  }

  deleteText(range) {
    this.document.remove(range);
    this._needsFit = true;
    this.addValueChange(
      "textString", this.document.textString,
      {action: "deleteText", range});
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // selection

  get selection() { return this._selection; }
  set selection(range) { return this._selection.range = range; }

  get cursorPosition() { return this.selection.lead; }

  selectAll() {
    this.selection.range = {start: {row: 0, column: 0}, end: this.document.endPosition};
    return this.selection;
  }

  selectLine(row) {
    if (typeof row !== "number") row = this.cursorPosition.row
    this.selection.range = {start: {row, column: 0}, end: {row, column: this.getLine(row).length}};
    return this.selection;
  }

  selectionOrLineString() {
    var {text, start} = this.selection;
    if (text) return text;
    return this.getLine(start.row);
  }

  scrollToSelection() {
    var { scroll, selection, padding, renderer } = this,
        paddedBounds = this.innerBounds().insetByRect(padding),
        charBounds =   renderer.boundsFor(this, selection.start),
        selPt =        this.addPaddingAndScroll(charBounds.bottomRight());
    if (!paddedBounds.containsPoint(selPt))
      this.scroll = scroll.addPt(selPt.subPt(paddedBounds.bottomRight()));
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // text layout related

  fit() {
    let {fixedWidth, fixedHeight} = this;
    if ((fixedHeight && fixedWidth) || !this.renderer/*not init'ed yet*/) return;
    let textBounds = this.renderer.textBounds(this);
    if (!fixedHeight && !fixedWidth) this.extent = textBounds.extent();
    else if (!fixedHeight) this.height = textBounds.height;
    else if (!fixedWidth) this.width = textBounds.width;
  }

  fitIfNeeded() {
    if (this._needsFit) { this.fit(); this._needsFit = false; }
  }

  textPositionFromPoint(point) {
    return this.renderer.textPositionFor(this, point);
  }

  paddingAndScrollOffset() {
    return this.padding.topLeft().subPt(this.scroll);
  }

  addPaddingAndScroll(point) {
    return point.addPt(this.paddingAndScrollOffset());
  }

  removePaddingAndScroll(point) {
    return point.subPt(this.paddingAndScrollOffset());
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // rendering

  aboutToRender(renderer) {
    super.aboutToRender(renderer);
    this.fitIfNeeded();
  }

  render(renderer) {
    return this.renderer.renderMorph(renderer, this);
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // mouse events

  onMouseDown(evt) {
    this.onMouseMove(evt);
  }

  onMouseMove(evt) {
    if (!evt.leftMouseButtonPressed()) return;
    var {clickedOnMorph, clickedOnPosition} = evt.state;
    if (clickedOnMorph !== this || !this.selectable) return;

    var start = this.textPositionFromPoint(this.removePaddingAndScroll(this.localize(clickedOnPosition))),
        end = this.textPositionFromPoint(this.removePaddingAndScroll(this.localize(evt.position)))

// console.log("%s => %s\n%s => %s",
//   this.localize(clickedOnPosition), JSON.stringify(start),
//   this.localize(evt.position), JSON.stringify(end));

    var from =this.selection.toString();
    this.selection.range = {start, end};

// show(`[mouse selection] ${from} -> ${this.selection}`)
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // keyboard events

  get keyhandlers() {
    return [defaultKeyHandler].concat(this._keyhandlers)
  }

  simulateKeys(keyString) {
    simulateKeys(this, keyString);
  }

  onKeyUp(evt) {
    switch (evt.keyCombo) {
      case 'Command-D': case 'Command-P': evt.stop(); break;
    }
  }

  onKeyDown(evt) {
    var keyString = evt.keyCombo(),
        key = evt.domEvt.key,
        sel = this.selection,
        handled = true,
        world = this.world();

    switch (keyString) {
      case 'Command-C': case 'Command-X': case 'Command-V':
        handled = false;
        break; // handled by onCut()/onPaste()

      case 'Command-A': this.selectAll(); break;

      case 'Command-D':
        (async () => {
          if (this.selection.isEmpty()) this.selectLine();
          var result = await lively.vm.runEval(this.selection.text, {
            System, targetModule: "lively://lively.next-prototype_2016_08_23/" + this.id});
          evt.world[result.isError ? "logError" : "setStatusMessage"](result.value);
        })();
        break;

      case 'Command-P':
        (async () => {
          if (this.selection.isEmpty()) this.selectLine();
          var result = await lively.vm.runEval(this.selection.text, {
            System, targetModule: "lively://lively.next-prototype_2016_08_23/" + this.id});
          sel.collapseToEnd();
          this.insertTextAndSelect(result.value);
        })();
        break;

      case 'Command-S': this.doSave(); break;

      case 'Backspace':
        if (this.rejectsInput()) break;
        if (sel.isEmpty()) sel.growLeft(1);
        sel.text = "";
        sel.collapse();
        break;

      case 'Delete': // forward-delete
        if (this.rejectsInput()) break;
        if (sel.isEmpty()) sel.growRight(1);
        sel.text = "";
        sel.collapse();
        break;

      case 'Left': if (sel.isEmpty()) sel.growLeft(1); sel.collapse(); break;
      case 'Right': if (sel.isEmpty()) sel.growRight(1); sel.collapseToEnd(); break;

      case 'Up':
        var {row, column} = sel.start;
        sel.start = {row: row-1, column};
        sel.collapse();
        break;

      case 'Down':
        var {row, column} = sel.start;
        sel.start = {row: row+1, column};
        sel.collapseToEnd();
        break;

      case 'Enter':
        if (!this.rejectsInput()) { sel.text = "\n"; sel.collapseToEnd(); } break;
      case 'Space':
        if (!this.rejectsInput()) { sel.text = " "; sel.collapseToEnd(); } break;
      case 'Tab':
        if (!this.rejectsInput()) { sel.text = "\t"; sel.collapseToEnd(); } break;

      default:
        handled = false;
    }

    if (handled) {
      evt.stop();
      this.selection.cursorBlinkStart();
      this.scrollToSelection();
    }

  }

  onTextInput(evt) {
    if (this.rejectsInput()) return;
    var sel = this.selection;
    sel.text = evt.data;
    sel.collapseToEnd();
    this.scrollToSelection();
    this.selection.cursorBlinkStart();
  }

  invokeKeyHandlersWithEvent(evt, opts) {
    opts = {onlyCommandOrFunctionKey: false, ...opts};
    return KeyHandler.invokeKeyHandlersWithEvent(this, evt, opts);
  }

  doSave() { /*...*/ }

  onCut(evt) {
    if (this.rejectsInput()) return;
    this.onCopy(evt);
    var sel = this.selection;
    sel.text = "";
    sel.collapse();
  }

  onCopy(evt) {
    if (!this.isFocused()) return;
    evt.stop();
    evt.domEvt.clipboardData.setData("text", this.selection.text);
  }

  onPaste(evt) {
    if (this.rejectsInput()) return;
    evt.stop();
    var sel = this.selection;
    sel.text = evt.domEvt.clipboardData.getData("text");
    sel.collapseToEnd();
  }

  onFocus(evt) {
    this.makeDirty();
    this.selection.cursorBlinkStart();
  }
  onBlur(evt) {
    this.makeDirty();
    this.selection.cursorBlinkStop();
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // debugging

  inspect() {
    return `<${this.name}>`
         + `\n  ${this.selection}`
         + "\n  " + this.renderer.lines.map(({height, width, text}, i) => {
              return `[${i}] ${width.toFixed(0)}x${height.toFixed(0)} ${obj.inspect(text)}`
            }).join("\n  ")
         + `\n</${this.name}>`;
  }

}
<commit_msg>text: allow input even when not focused<commit_after>/*global System*/
import { string, obj } from "lively.lang";
import { Rectangle, Color, pt } from "lively.graphics";
import { Morph, show } from "../index.js";
import { Selection } from "./selection.js";
import DocumentRenderer from "./rendering.js";
import TextDocument from "./document.js";
import { KeyHandler } from "../events/keyhandler.js";
import Keys from "../events/Keys.js";
import { CommandHandler } from "../commands.js";

const defaultKeyHandler = new KeyHandler();

export class Text extends Morph {

  static makeLabel(string, props) {
    return new Text({
      textString: string,
      fontFamily: "Helvetica Neue, Arial",
      fontColor: Color.black,
      fontSize: 11,
      readOnly: true,
      ...props
    });
  }

  constructor(props = {}) {
    var {fontMetric, textString, selectable, selection} = props;
    props = obj.dissoc(props, ["textString","fontMetric", "selectable", "selection"])
    super({
      readOnly: false,
      draggable: false,
      clipMode: "hidden",
      fixedWidth: false, fixedHeight: false,
      padding: 0,
      fontFamily: "Sans-Serif",
      fontSize: 12,
      fontColor: Color.black,
      fontKerning: true,
      ...props
    });
    this.document = new TextDocument();
    this.renderer = new DocumentRenderer(fontMetric || this.env.fontMetric);
    this._keyhandlers = []; // defaultKeyHandler is fallback
    // this.commands = new CommandHandler();
    this._selection = new Selection(this, selection);
    this.selectable = typeof selectable !== "undefined" ? selectable : true;
    this.textString = textString || "";
    this.fit();
    this._needsFit = false;
  }

  get isText() { return true }

  onChange(change) {
    if (change.prop === "textString"
     || change.prop === "fontFamily"
     || change.prop === "fontSize"
     || change.prop === "fontColor" // FIXME
     || change.prop === "fixedWidth"
     || change.prop === "fixedHeight"
     || change.prop === "fontKerning")
       this.renderer && (this.renderer.layoutComputed = false);
    super.onChange(change);
  }

  get readOnly() { return this.getProperty("readOnly"); }
  set readOnly(value) {
    this.nativeCursor = value ? "default" : "auto";
    this.addValueChange("readOnly", value);
  }

  rejectsInput() { return this.readOnly /*|| !this.isFocused()*/ }

  get selectable() { return this.getProperty("selectable"); }
  set selectable(value) {
    this.addValueChange("selectable", value);
    if (!value) this.selection.collapse();
  }

  get fixedWidth() { return this.getProperty("fixedWidth") }
  set fixedWidth(value) {
    this.addValueChange("fixedWidth", value);
    this._needsFit = true;
  }

  get fixedHeight() { return this.getProperty("fixedHeight"); }
  set fixedHeight(value) {
    this.addValueChange("fixedHeight", value);
    this._needsFit = true;
  }

  get padding() { return this.getProperty("padding"); }
  set padding(value) {
    this.addValueChange("padding", typeof value === "number" ? Rectangle.inset(value) : value);
    this._needsFit = true;
  }

  get fontFamily() { return this.getProperty("fontFamily") }
  set fontFamily(value) {
    this.addValueChange("fontFamily", value);
    this._needsFit = true;
  }

  get fontSize() { return this.getProperty("fontSize") }
  set fontSize(value) {
    this.addValueChange("fontSize", value);
    this._needsFit = true;
  }

  get fontColor() { return this.getProperty("fontColor") }
  set fontColor(value) { this.addValueChange("fontColor", value); }

  get fontKerning() { return this.getProperty("fontKerning") }
  set fontKerning(value) { this.addValueChange("fontKerning", value); }

  get clipMode()  { return this.getProperty("clipMode"); }
  set clipMode(value)  {
    this.addValueChange("clipMode", value);
    this.fixedWidth = this.fixedHeight = this.isClip();
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // document changes

  get textString() { return this.document ? this.document.textString : "" }
  set textString(value) {
    this.document.textString = String(value);
    this.selection = {start: 0, end: 0};
    this.addValueChange("textString", value);
    this._needsFit = true;
  }

  getLine(row) {
    if (typeof row !== "number") this.cursorPosition.row
    var doc = this.document;
    return doc.getLine(row);
  }

  insertTextAndSelect(text, pos = null) {
    text = String(text);
    if (pos) this.selection.range = this.insertText(text, pos);
    else this.selection.text = text;
  }

  insertText(text, pos = null) {
    text = String(text);
    var range = this.document.insert(text, pos || this.selection.end);
    this._needsFit = true;
    this.addValueChange(
      "textString", this.document.textString,
      {action: "insertText", text, pos});
    return range;
  }

  deleteText(range) {
    this.document.remove(range);
    this._needsFit = true;
    this.addValueChange(
      "textString", this.document.textString,
      {action: "deleteText", range});
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // selection

  get selection() { return this._selection; }
  set selection(range) { return this._selection.range = range; }

  get cursorPosition() { return this.selection.lead; }

  selectAll() {
    this.selection.range = {start: {row: 0, column: 0}, end: this.document.endPosition};
    return this.selection;
  }

  selectLine(row) {
    if (typeof row !== "number") row = this.cursorPosition.row
    this.selection.range = {start: {row, column: 0}, end: {row, column: this.getLine(row).length}};
    return this.selection;
  }

  selectionOrLineString() {
    var {text, start} = this.selection;
    if (text) return text;
    return this.getLine(start.row);
  }

  scrollToSelection() {
    var { scroll, selection, padding, renderer } = this,
        paddedBounds = this.innerBounds().insetByRect(padding),
        charBounds =   renderer.boundsFor(this, selection.start),
        selPt =        this.addPaddingAndScroll(charBounds.bottomRight());
    if (!paddedBounds.containsPoint(selPt))
      this.scroll = scroll.addPt(selPt.subPt(paddedBounds.bottomRight()));
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // text layout related

  fit() {
    let {fixedWidth, fixedHeight} = this;
    if ((fixedHeight && fixedWidth) || !this.renderer/*not init'ed yet*/) return;
    let textBounds = this.renderer.textBounds(this);
    if (!fixedHeight && !fixedWidth) this.extent = textBounds.extent();
    else if (!fixedHeight) this.height = textBounds.height;
    else if (!fixedWidth) this.width = textBounds.width;
  }

  fitIfNeeded() {
    if (this._needsFit) { this.fit(); this._needsFit = false; }
  }

  textPositionFromPoint(point) {
    return this.renderer.textPositionFor(this, point);
  }

  paddingAndScrollOffset() {
    return this.padding.topLeft().subPt(this.scroll);
  }

  addPaddingAndScroll(point) {
    return point.addPt(this.paddingAndScrollOffset());
  }

  removePaddingAndScroll(point) {
    return point.subPt(this.paddingAndScrollOffset());
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // rendering

  aboutToRender(renderer) {
    super.aboutToRender(renderer);
    this.fitIfNeeded();
  }

  render(renderer) {
    return this.renderer.renderMorph(renderer, this);
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // mouse events

  onMouseDown(evt) {
    this.onMouseMove(evt);
  }

  onMouseMove(evt) {
    if (!evt.leftMouseButtonPressed()) return;
    var {clickedOnMorph, clickedOnPosition} = evt.state;
    if (clickedOnMorph !== this || !this.selectable) return;

    var start = this.textPositionFromPoint(this.removePaddingAndScroll(this.localize(clickedOnPosition))),
        end = this.textPositionFromPoint(this.removePaddingAndScroll(this.localize(evt.position)))

// console.log("%s => %s\n%s => %s",
//   this.localize(clickedOnPosition), JSON.stringify(start),
//   this.localize(evt.position), JSON.stringify(end));

    var from =this.selection.toString();
    this.selection.range = {start, end};

// show(`[mouse selection] ${from} -> ${this.selection}`)
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // keyboard events

  get keyhandlers() {
    return [defaultKeyHandler].concat(this._keyhandlers)
  }

  simulateKeys(keyString) {
    simulateKeys(this, keyString);
  }

  onKeyUp(evt) {
    switch (evt.keyCombo) {
      case 'Command-D': case 'Command-P': evt.stop(); break;
    }
  }

  onKeyDown(evt) {
    var keyString = evt.keyCombo(),
        key = evt.domEvt.key,
        sel = this.selection,
        handled = true,
        world = this.world();

    switch (keyString) {
      case 'Command-C': case 'Command-X': case 'Command-V':
        handled = false;
        break; // handled by onCut()/onPaste()

      case 'Command-A': this.selectAll(); break;

      case 'Command-D':
        (async () => {
          if (this.selection.isEmpty()) this.selectLine();
          var result = await lively.vm.runEval(this.selection.text, {
            System, targetModule: "lively://lively.next-prototype_2016_08_23/" + this.id});
          evt.world[result.isError ? "logError" : "setStatusMessage"](result.value);
        })();
        break;

      case 'Command-P':
        (async () => {
          if (this.selection.isEmpty()) this.selectLine();
          var result = await lively.vm.runEval(this.selection.text, {
            System, targetModule: "lively://lively.next-prototype_2016_08_23/" + this.id});
          sel.collapseToEnd();
          this.insertTextAndSelect(result.value);
        })();
        break;

      case 'Command-S': this.doSave(); break;

      case 'Backspace':
        if (this.rejectsInput()) break;
        if (sel.isEmpty()) sel.growLeft(1);
        sel.text = "";
        sel.collapse();
        break;

      case 'Delete': // forward-delete
        if (this.rejectsInput()) break;
        if (sel.isEmpty()) sel.growRight(1);
        sel.text = "";
        sel.collapse();
        break;

      case 'Left': if (sel.isEmpty()) sel.growLeft(1); sel.collapse(); break;
      case 'Right': if (sel.isEmpty()) sel.growRight(1); sel.collapseToEnd(); break;

      case 'Up':
        var {row, column} = sel.start;
        sel.start = {row: row-1, column};
        sel.collapse();
        break;

      case 'Down':
        var {row, column} = sel.start;
        sel.start = {row: row+1, column};
        sel.collapseToEnd();
        break;

      case 'Enter':
        if (!this.rejectsInput()) { sel.text = "\n"; sel.collapseToEnd(); } break;
      case 'Space':
        if (!this.rejectsInput()) { sel.text = " "; sel.collapseToEnd(); } break;
      case 'Tab':
        if (!this.rejectsInput()) { sel.text = "\t"; sel.collapseToEnd(); } break;

      default:
        handled = false;
    }

    if (handled) {
      evt.stop();
      this.selection.cursorBlinkStart();
      this.scrollToSelection();
    }

  }

  onTextInput(evt) {
    if (this.rejectsInput()) return;
    var sel = this.selection;
    sel.text = evt.data;
    sel.collapseToEnd();
    this.scrollToSelection();
    this.selection.cursorBlinkStart();
  }

  invokeKeyHandlersWithEvent(evt, opts) {
    opts = {onlyCommandOrFunctionKey: false, ...opts};
    return KeyHandler.invokeKeyHandlersWithEvent(this, evt, opts);
  }

  doSave() { /*...*/ }

  onCut(evt) {
    if (this.rejectsInput()) return;
    this.onCopy(evt);
    var sel = this.selection;
    sel.text = "";
    sel.collapse();
  }

  onCopy(evt) {
    if (!this.isFocused()) return;
    evt.stop();
    evt.domEvt.clipboardData.setData("text", this.selection.text);
  }

  onPaste(evt) {
    if (this.rejectsInput()) return;
    evt.stop();
    var sel = this.selection;
    sel.text = evt.domEvt.clipboardData.getData("text");
    sel.collapseToEnd();
  }

  onFocus(evt) {
    this.makeDirty();
    this.selection.cursorBlinkStart();
  }
  onBlur(evt) {
    this.makeDirty();
    this.selection.cursorBlinkStop();
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // debugging

  inspect() {
    return `<${this.name}>`
         + `\n  ${this.selection}`
         + "\n  " + this.renderer.lines.map(({height, width, text}, i) => {
              return `[${i}] ${width.toFixed(0)}x${height.toFixed(0)} ${obj.inspect(text)}`
            }).join("\n  ")
         + `\n</${this.name}>`;
  }

}
<|endoftext|>


<commit_before>"use strict";
(function(module) {
	/** Class definition BEGIN */
	var Thread = function(method, executionFrequency) {
		this.method = method || function() {
		};
		this.executionFrequency = executionFrequency || 100;

		this.name = (+("" + Math.random()).substr(2)).toString(16);
		this.iteration = 0;
		this.timeAsleep = 0;
		this.lastExecution = 0;
		this.isSleeping = false;
		this.isAlive = false;
		this.global = typeof (global) != 'undefined' ? global : window; //asks who is the global object, in case we are running inside node.js
	};

	Thread.prototype.run = function run(self) {
		if(self.isAlive) {
			var currentExecutionTime = new Date().getTime();
			if(!self.isSleeping) {
				self.method();
				self.iteration++;
			} else {
				self.timeAsleep -= currentExecutionTime - self.lastExecution;
				if(self.timeAsleep <= 0) {
					self.isSleeping = false;
				}
			}
			self.lastExecution = currentExecutionTime;
			self.global.setTimeout(run, self.executionFrequency, self);
		}
	};

	Thread.prototype.sleep = function(millis) {
		if(this.isAlive && !this.isSleeping) {
			console.log("Putting thread " + this.name + " to sleep for " + millis + " milliseconds.");
			this.isSleeping = true;
			this.timeAsleep = millis;
		}
		return this;
	};

	Thread.prototype.wakeUp = function() {
		if(this.isAlive && this.isSleeping) {
			console.log("Forcefully waking up thread " + this.name + ".");
			this.isSleeping = false;
			this.timeAsleep = 0;
		}
		return this;
	};

	Thread.prototype.start = function() {
		if(!this.isAlive) {
			console.log("Starting thread " + this.name + ".");
			this.isAlive = true;
			this.isSleeping = false;
			this.run(this);
		}
		return this;
	};

	Thread.prototype.stop = function() {
		if(this.isAlive) {
			this.iteration = 0;
			this.timeAsleep = 0;
			this.lastExecution = 0;
			this.isSleeping = false;
			this.isAlive = false;
			console.log("Thread " + this.name + " forcefully stoped.");
		}
		return this;
	};
	/** Class definition END */

	Object.freeze(Thread);
	if(module && module.exports) {
		// if required in commonjs style, exports the Thread class
		module.exports.Thread = Thread;
	} else {
		// else, exports the class as a globa object
		window.Thread = Thread;
	}
})(typeof (module) !== "undefined" ? module : undefined);
<commit_msg>commit<commit_after>"use strict";
(function(module) {
	/** Class definition BEGIN */
	var Thread = function(method, executionFrequency) {
		this.method = method || function() {
		};
		this.executionFrequency = executionFrequency || 100;

		this.name = (+("" + Math.random()).substr(2)).toString(16);
		this.iteration = 0;
		this.timeAsleep = 0;
		this.lastExecution = 0;
		this.isSleeping = false;
		this.isAlive = false;
		this.global = typeof (global) != 'undefined' ? global : window; //asks who is the global object, in case we are running inside node.js
	};

	Thread.prototype.run = function run(self) {
		if(self.isAlive) {
			var currentExecutionTime = new Date().getTime();
			if(!self.isSleeping) {
				self.method();
				self.iteration++;
			} else {
				self.timeAsleep -= currentExecutionTime - self.lastExecution;
				if(self.timeAsleep <= 0) {
					self.isSleeping = false;
				}
			}
			self.lastExecution = currentExecutionTime;
			self.global.setTimeout(run, self.executionFrequency, self);
		}
	};

	Thread.prototype.sleep = function(millis) {
		if(this.isAlive && !this.isSleeping) {
			console.log("Putting thread " + this.name + " to sleep for " + millis + " milliseconds.");
			this.isSleeping = true;
			this.timeAsleep = millis;
		}
		return this;
	};

	Thread.prototype.wakeUp = function() {
		if(this.isAlive && this.isSleeping) {
			console.log("Forcefully waking up thread " + this.name + ".");
			this.isSleeping = false;
			this.timeAsleep = 0;
		}
		return this;
	};

	Thread.prototype.start = function() {
		if(!this.isAlive) {
			console.log("Starting thread " + this.name + ".");
			this.isAlive = true;
			this.isSleeping = false;
			this.run(this);
		}
		return this;
	};

	Thread.prototype.stop = function() {
		if(this.isAlive) {
			this.iteration = 0;
			this.timeAsleep = 0;
			this.lastExecution = 0;
			this.isSleeping = false;
			this.isAlive = false;
			console.log("Thread " + this.name + " forcefully stoped.");
		}
		return this;
	};
	/** Class definition END */

	Object.freeze(Thread);
	if(module && module.exports) {
		// if required in commonjs style, exports the Thread class
		module.exports.Thread = Thread;
	} else {
		// else, exports the class as a globa object
		window.Thread = Thread;
	}
})(typeof (module) !== "undefined" ? module : undefined);

// AMD style module export
if(typeof (define) !== "undefined") {
	define(function() {
		var Thread = window.Thread || module.exports.Thread;
		return {
			"Thread" : Thread
		};
	});
}
<|endoftext|>


<commit_before>/* DEVICE AND BROWSER DETECTION
================================================== */
if(typeof VMM != 'undefined' && typeof VMM.Browser == 'undefined') {

	VMM.Browser = {
		init: function () {
			this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
			this.version = this.searchVersion(navigator.userAgent)
				|| this.searchVersion(navigator.appVersion)
				|| "an unknown version";
			this.OS = this.searchString(this.dataOS) || "an unknown OS";
			this.device = this.searchDevice(navigator.userAgent);

		},
		searchDevice: function(d) {
			if (d.match(/Android/i) || d.match(/iPhone|iPod/i)) {
				return "mobile";
			} else if (d.match(/iPad/i)) {
				return "tablet";
			} else if (d.match(/BlackBerry/i) || d.match(/IEMobile/i)) {
				return "other mobile";
			} else {
				return "desktop";
			}
		},
		searchString: function (data) {
			for (var i=0;i<data.length;i++)	{
				var dataString = data[i].string;
				var dataProp = data[i].prop;
				this.versionSearchString = data[i].versionSearch || data[i].identity;
				if (dataString) {
					if (dataString.indexOf(data[i].subString) != -1)
						return data[i].identity;
				}
				else if (dataProp)
					return data[i].identity;
			}
		},
		searchVersion: function (dataString) {
			var index = dataString.indexOf(this.versionSearchString);
			if (index == -1) return;
			return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
		},
		dataBrowser: [
			{
				string: navigator.userAgent,
				subString: "Chrome",
				identity: "Chrome"
			},
			{ 	string: navigator.userAgent,
				subString: "OmniWeb",
				versionSearch: "OmniWeb/",
				identity: "OmniWeb"
			},
			{
				string: navigator.vendor,
				subString: "Apple",
				identity: "Safari",
				versionSearch: "Version"
			},
			{
				prop: window.opera,
				identity: "Opera",
				versionSearch: "Version"
			},
			{
				string: navigator.vendor,
				subString: "iCab",
				identity: "iCab"
			},
			{
				string: navigator.vendor,
				subString: "KDE",
				identity: "Konqueror"
			},
			{
				string: navigator.userAgent,
				subString: "Firefox",
				identity: "Firefox"
			},
			{
				string: navigator.vendor,
				subString: "Camino",
				identity: "Camino"
			},
			{		// for newer Netscapes (6+)
				string: navigator.userAgent,
				subString: "Netscape",
				identity: "Netscape"
			},
			{
				string: navigator.userAgent,
				subString: "MSIE",
				identity: "Explorer",
				versionSearch: "MSIE"
			},
			{
				string: navigator.userAgent,
				subString: "Gecko",
				identity: "Mozilla",
				versionSearch: "rv"
			},
			{ 		// for older Netscapes (4-)
				string: navigator.userAgent,
				subString: "Mozilla",
				identity: "Netscape",
				versionSearch: "Mozilla"
			}
		],
		dataOS : [
			{
				string: navigator.platform,
				subString: "Win",
				identity: "Windows"
			},
			{
				string: navigator.platform,
				subString: "Mac",
				identity: "Mac"
			},
			{
				string: navigator.userAgent,
				subString: "iPhone",
				identity: "iPhone/iPod"
		    },
			{
				string: navigator.userAgent,
				subString: "iPad",
				identity: "iPad"
		    },
			{
				string: navigator.platform,
				subString: "Linux",
				identity: "Linux"
			}
		]

	}
	VMM.Browser.init();
}<commit_msg>Added orientation<commit_after>/* DEVICE AND BROWSER DETECTION
================================================== */
if(typeof VMM != 'undefined' && typeof VMM.Browser == 'undefined') {

	VMM.Browser = {
		init: function () {
			this.browser = this.searchString(this.dataBrowser) || "An unknown browser";
			this.version = this.searchVersion(navigator.userAgent)
				|| this.searchVersion(navigator.appVersion)
				|| "an unknown version";
			this.OS = this.searchString(this.dataOS) || "an unknown OS";
			this.device = this.searchDevice(navigator.userAgent);
			this.orientation = this.searchOrientation(window.orientation);
		},
		searchOrientation: function(orientation) {
			if ( orientation == 0  || orientation == 180) {
				return "portrait";
			} else if ( orientation == 90 || orientation == -90) {
				return "landscape";
			} else {
				return "normal";
			}
		},
		searchDevice: function(d) {
			if (d.match(/Android/i) || d.match(/iPhone|iPod/i)) {
				return "mobile";
			} else if (d.match(/iPad/i)) {
				return "tablet";
			} else if (d.match(/BlackBerry/i) || d.match(/IEMobile/i)) {
				return "other mobile";
			} else {
				return "desktop";
			}
		},
		searchString: function (data) {
			for (var i=0;i<data.length;i++)	{
				var dataString = data[i].string;
				var dataProp = data[i].prop;
				this.versionSearchString = data[i].versionSearch || data[i].identity;
				if (dataString) {
					if (dataString.indexOf(data[i].subString) != -1)
						return data[i].identity;
				}
				else if (dataProp)
					return data[i].identity;
			}
		},
		searchVersion: function (dataString) {
			var index = dataString.indexOf(this.versionSearchString);
			if (index == -1) return;
			return parseFloat(dataString.substring(index+this.versionSearchString.length+1));
		},
		dataBrowser: [
			{
				string: navigator.userAgent,
				subString: "Chrome",
				identity: "Chrome"
			},
			{ 	string: navigator.userAgent,
				subString: "OmniWeb",
				versionSearch: "OmniWeb/",
				identity: "OmniWeb"
			},
			{
				string: navigator.vendor,
				subString: "Apple",
				identity: "Safari",
				versionSearch: "Version"
			},
			{
				prop: window.opera,
				identity: "Opera",
				versionSearch: "Version"
			},
			{
				string: navigator.vendor,
				subString: "iCab",
				identity: "iCab"
			},
			{
				string: navigator.vendor,
				subString: "KDE",
				identity: "Konqueror"
			},
			{
				string: navigator.userAgent,
				subString: "Firefox",
				identity: "Firefox"
			},
			{
				string: navigator.vendor,
				subString: "Camino",
				identity: "Camino"
			},
			{		// for newer Netscapes (6+)
				string: navigator.userAgent,
				subString: "Netscape",
				identity: "Netscape"
			},
			{
				string: navigator.userAgent,
				subString: "MSIE",
				identity: "Explorer",
				versionSearch: "MSIE"
			},
			{
				string: navigator.userAgent,
				subString: "Gecko",
				identity: "Mozilla",
				versionSearch: "rv"
			},
			{ 		// for older Netscapes (4-)
				string: navigator.userAgent,
				subString: "Mozilla",
				identity: "Netscape",
				versionSearch: "Mozilla"
			}
		],
		dataOS : [
			{
				string: navigator.platform,
				subString: "Win",
				identity: "Windows"
			},
			{
				string: navigator.platform,
				subString: "Mac",
				identity: "Mac"
			},
			{
				string: navigator.userAgent,
				subString: "iPhone",
				identity: "iPhone/iPod"
		    },
			{
				string: navigator.userAgent,
				subString: "iPad",
				identity: "iPad"
		    },
			{
				string: navigator.platform,
				subString: "Linux",
				identity: "Linux"
			}
		]

	}
	VMM.Browser.init();
}<|endoftext|>


<commit_before>enyo.kind({
	name: "mdlFile",
	kind: enyo.Model,
	// mergeKeys: ["day"],
	// this is a read-only example, and this flag means if _destroy_ is called on this
	// model it will only do the local routines
	readOnly: true,
	attributes: {
		title: "",
		size: "",
		// icon: "",
		ext: "",
		href: "",
		lastModified: function () {
			return new Date(this.get("mtime"));
		},
		prettySize: function() {
			return this.formatSize(this.get("size"));
		},
		prettyLastModified: function () {
			var d = this.get("lastModified");
			return this.formatDate(d);
			// var d = this.get("mtime");
			// d = d.replace(/T/, " ");
			// d = d.replace(/\..*$/, "");
			// var objDate = enyo.dateToIlib(d);
			// var objDate = new ilib.Date.GregDate({unixtime: d.getTime()});
			// var objDateFmt = new ilib.DateFmt({date: 'dmw', length: 'long'});
			// console.log("objDateFmt.format(objDate)",objDateFmt.format(objDate));
			// return objDateFmt.format(objDate);
		},
	},
	computed: {
		lastModified: [{cached: true}],
		prettySize: [{cached: true}, "size"],
		prettyLastModified: [{cached: true}, "lastModified"]
	},
	primaryKey: 'name',
	formatSize: function(size) {
		var arrSizes = ['bytes','KB','MB','GB','TB'];
		for (var i = arrSizes.length - 1; i >= 0; i--) {
			var key = 1 << (i*10);
				newSize = size >> (10 * i);
			console.log("Size:", size, i, (10*i), newSize);
			if (newSize > 0) {
				return newSize + " " + arrSizes[i];
			}
		};
		return "0 " + arrSizes[0];
	sub size : method {
		my $self = shift;
		my @Sizes = @_;
		my @Chart = qw(bytes KB MB GB TB);
		foreach my $s (@Sizes) {
			my $result = '0 bytes';
			my $count = 0;
			foreach my $c (@Chart) {
				my $key = 2 ** ($count*10);
				$s = int($s+1) if ($s != int($s));
				last if ($s < $key);
				$result = sprintf('%.2f', $s/$key);
				$result = ($result+1-1) . ' ' . $c;
				$count++;
			}
			$s = $result;
		}
		return scalar(@Sizes) > 1 ? @Sizes : $Sizes[0];
	}
	},
	formatDate: function(date) {
		var arrMonths = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
		return arrMonths[date.getMonth()] + " " + date.getDate() + " " + (1900 + date.getYear()) + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
	},
	// parse: function (data) {
		// convert the array of games data hashes into a collection
		// of game records

		// var f = this.get("file");
		// we know that if there isn't a _home_ already this is our first time receiving
		// this data so we don't want to merge, we create our school entries
		// if (!f) {
			// data = new B.File(data);
		// } else {
			// since we're just merging the data and we know its current structure
			// doesn't need to be parsed we just call this to let anything know if
			// something has changed
			// h.setObject(data.home);
			// a.setObject(data.away);
			// remove these entries from the dataset so
			// they won't override our current objects
			// delete data.home;
			// delete data.away;
		// }
		// var objNewFile = enyo.createComponent({name: "fileManual", kind: "B.File"}, data);
		// data.file = new enyo.Collection(data, {model: mdlFile, didFetch: true}); // owner: this <- causes .getId error
		// var objNewFile = new enyo.Component({name: "fileManual", kind: "B.File"}); // owner: this <- causes .getId error
		// console.log('parse.data:', data);
		// return data;
		// if (data && !(data.games instanceof enyo.Collection)) {
			// because this collection is getting its data from us here it doesn't know if
			// it needs to tell the records to parse the data when they are instanced so we
			// use the _didFetch_ flag and now the `parse` method of the Game model kind will be called
			// also note the use of the _owner_ property here that is only useful if you destroy this
			// GameDay model (the owner of the the collection we're creating) it will also destroy
			// the collection since we _own_ it
			// data = this.$.createComponent({kind: "B.File", owner: this});
			// this.set("directoryContents", data.games);
			// console.log('Creating a new games collection with %s games in it.', data.games.length);
		// }
		// return data;
		// data.file = new B.File(data);
	// 	console.log("mdlFile:Data", data);

	// 	return data;
	// }
});
<commit_msg>Removing some breaking code.<commit_after>enyo.kind({
	name: "mdlFile",
	kind: enyo.Model,
	// mergeKeys: ["day"],
	// this is a read-only example, and this flag means if _destroy_ is called on this
	// model it will only do the local routines
	readOnly: true,
	attributes: {
		title: "",
		size: "",
		// icon: "",
		ext: "",
		href: "",
		lastModified: function () {
			return new Date(this.get("mtime"));
		},
		prettySize: function() {
			return this.formatSize(this.get("size"));
		},
		prettyLastModified: function () {
			var d = this.get("lastModified");
			return this.formatDate(d);
			// var d = this.get("mtime");
			// d = d.replace(/T/, " ");
			// d = d.replace(/\..*$/, "");
			// var objDate = enyo.dateToIlib(d);
			// var objDate = new ilib.Date.GregDate({unixtime: d.getTime()});
			// var objDateFmt = new ilib.DateFmt({date: 'dmw', length: 'long'});
			// console.log("objDateFmt.format(objDate)",objDateFmt.format(objDate));
			// return objDateFmt.format(objDate);
		},
	},
	computed: {
		lastModified: [{cached: true}],
		prettySize: [{cached: true}, "size"],
		prettyLastModified: [{cached: true}, "lastModified"]
	},
	primaryKey: 'name',
	formatSize: function(size) {
		var arrSizes = ['bytes','KB','MB','GB','TB'];
		for (var i = arrSizes.length - 1; i >= 0; i--) {
			var key = 1 << (i*10);
				newSize = size >> (10 * i);
			console.log("Size:", size, i, (10*i), newSize);
			if (newSize > 0) {
				return newSize + " " + arrSizes[i];
			}
		};
		return "0 " + arrSizes[0];
	// sub size : method {
	// 	my $self = shift;
	// 	my @Sizes = @_;
	// 	my @Chart = qw(bytes KB MB GB TB);
	// 	foreach my $s (@Sizes) {
	// 		my $result = '0 bytes';
	// 		my $count = 0;
	// 		foreach my $c (@Chart) {
	// 			my $key = 2 ** ($count*10);
	// 			$s = int($s+1) if ($s != int($s));
	// 			last if ($s < $key);
	// 			$result = sprintf('%.2f', $s/$key);
	// 			$result = ($result+1-1) . ' ' . $c;
	// 			$count++;
	// 		}
	// 		$s = $result;
	// 	}
	// 	return scalar(@Sizes) > 1 ? @Sizes : $Sizes[0];
	// }
	},
	formatDate: function(date) {
		var arrMonths = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec'];
		return arrMonths[date.getMonth()] + " " + date.getDate() + " " + (1900 + date.getYear()) + " " + date.getHours() + ":" + date.getMinutes() + ":" + date.getSeconds();
	},
	// parse: function (data) {
		// convert the array of games data hashes into a collection
		// of game records

		// var f = this.get("file");
		// we know that if there isn't a _home_ already this is our first time receiving
		// this data so we don't want to merge, we create our school entries
		// if (!f) {
			// data = new B.File(data);
		// } else {
			// since we're just merging the data and we know its current structure
			// doesn't need to be parsed we just call this to let anything know if
			// something has changed
			// h.setObject(data.home);
			// a.setObject(data.away);
			// remove these entries from the dataset so
			// they won't override our current objects
			// delete data.home;
			// delete data.away;
		// }
		// var objNewFile = enyo.createComponent({name: "fileManual", kind: "B.File"}, data);
		// data.file = new enyo.Collection(data, {model: mdlFile, didFetch: true}); // owner: this <- causes .getId error
		// var objNewFile = new enyo.Component({name: "fileManual", kind: "B.File"}); // owner: this <- causes .getId error
		// console.log('parse.data:', data);
		// return data;
		// if (data && !(data.games instanceof enyo.Collection)) {
			// because this collection is getting its data from us here it doesn't know if
			// it needs to tell the records to parse the data when they are instanced so we
			// use the _didFetch_ flag and now the `parse` method of the Game model kind will be called
			// also note the use of the _owner_ property here that is only useful if you destroy this
			// GameDay model (the owner of the the collection we're creating) it will also destroy
			// the collection since we _own_ it
			// data = this.$.createComponent({kind: "B.File", owner: this});
			// this.set("directoryContents", data.games);
			// console.log('Creating a new games collection with %s games in it.', data.games.length);
		// }
		// return data;
		// data.file = new B.File(data);
	// 	console.log("mdlFile:Data", data);

	// 	return data;
	// }
});
<|endoftext|>


<commit_before>/**
enyo.Scroller is scroller suitable for use in both desktop and mobile applications.

In some mobile environments, a default scrolling solution is not implemented for dom elements. In these cases, enyo.Scroller implements
a touch based scrolling solution. This can be opted into either globally by setting the flag enyo.Scroller.touchScrolling = true;
or on a per instance basis by specifying a strategyKind of "TouchScrollStrategy."
*/
enyo.kind({
	name: "enyo.Scroller",
	published: {
		/**
			Specifies how to horizontally scroll. Acceptable values are "scroll", "auto," "hidden," and "default" The precise
			effect of the setting is determined by the scroll strategy.
		*/
		horizontal: "default",
		/**
			Specifies how to vertically scroll. Acceptable values are "scroll", "auto," "hidden," and "default" The precise
			effect of the setting is determined by the scroll strategy.
		*/
		vertical: "default",
		/**
			Sets the vertical scroll position.
		*/
		scrollTop: 0,
		/**
			Sets the horizontal scroll position.
		*/
		scrollLeft: 0,
		/**
			Sets the maximum height of the scroll content.
		*/
		maxHeight: null,
		/**
			Set to true to make this scroller select a platform appropriate touch based scrolling strategy.
			Please note that specifycing a scrollStrategy will take precedence over this setting.
		*/
		touch: false,
		/**
			Specify a type of scrolling. The enyo Scroller will attempt to automatically select
			a strategy compatbile with the runtime environment. A specific strategy can also be chosen:

			* ScrollStrategy is the default and implements no scrolling, relying instead on the environment to scroll properly.
			* TouchScrollStrategy implements a touch scrolling mechanism.
			* TranslateScrollStrategy implements a touch scrolling mechanism using translations, recommended only for Android 3 and 4 currently.
		*/
		strategyKind: "ScrollStrategy",
		//* set to true to display a scroll thumb in Touch scrollers.
		thumb: true
	},
	events: {
		onScrollStart: "",
		onScroll: "",
		onScrollStop: ""
	},
	handlers: {
		onscroll: "domScroll",
		onScrollStart: "scrollStart",
		onScroll: "scroll",
		onScrollStop: "scrollStop"
	},
	classes: "enyo-scroller",
	/**
		If true, the scroller will not propagate dragstart events that cause it to start scrolling (defaults to true)
	*/
	preventDragPropagation: true,
	/**
		If true, the scroller will not propagate scroll events.
	*/
	preventScrollPropagation: true,
	//* @protected
	statics: {
		osInfo: [
			{os: "android", version: 3},
			{os: "ios", version: 5},
			{os: "webos", version: 1e9}
		],
		//* returns true if platform should have touch events
		hasTouchScrolling: function() {
			for (var i=0, t, m; t=this.osInfo[i]; i++) {
				if (enyo.platform[t.os]) {
					return true;
				}
			}
		},
		//* returns true if the platform has native div scrollers, desktop browsers always have them
		hasNativeScrolling: function() {
			for (var i=0, t, m; t=this.osInfo[i]; i++) {
				if (enyo.platform[t.os] < t.version) {
					return false;
				}
			}
			return true;
		},
		getTouchStrategy: function() {
			return enyo.platform.android >= 3 ? "TranslateScrollStrategy" : "TouchScrollStrategy";
		}
	},
	//* @protected
	controlParentName: "strategy",
	create: function() {
		this.inherited(arguments);
		this.horizontalChanged();
		this.verticalChanged();
	},
	importProps: function(inProps) {
		this.inherited(arguments);
		// allow global overriding of strategy kind
		if (inProps && inProps.strategyKind === undefined && (enyo.Scroller.touchScrolling || this.touch)) {
			this.strategyKind = enyo.Scroller.getTouchStrategy();
		}
	},
	initComponents: function() {
		this.strategyKindChanged();
		this.inherited(arguments);
	},
	teardownChildren: function() {
		this.cacheScrollPosition();
		this.inherited(arguments);
	},
	rendered: function() {
		this.inherited(arguments);
		this.restoreScrollPosition();
	},
	strategyKindChanged: function() {
		if (this.$.strategy) {
			this.$.strategy.destroy();
			this.controlParent = null;
		}
		// note: createComponents automatically updates controlParent.
		this.createStrategy();
		if (this.hasNode()) {
			this.render();
		}
	},
	createStrategy: function() {
		this.createComponents([{name: "strategy", maxHeight: this.maxHeight, kind: this.strategyKind, thumb: this.thumb, preventDragPropagation: this.preventDragPropagation, isChrome: true}]);
	},
	getStrategy: function() {
		return this.$.strategy;
	},
	maxHeightChanged: function() {
		this.$.strategy.setMaxHeight(this.maxHeight);
	},
	showingChanged: function() {
		if (!this.showing) {
			this.cacheScrollPosition();
			this.setScrollLeft(0);
			this.setScrollTop(0);
		}
		this.inherited(arguments);
		if (this.showing) {
			this.restoreScrollPosition();
		}
	},
	thumbChanged: function() {
		this.$.strategy.setThumb(this.thumb);
	},
	cacheScrollPosition: function() {
		this.cachedPosition = {left: this.getScrollLeft(), top: this.getScrollTop()};
	},
	restoreScrollPosition: function() {
		if (this.cachedPosition) {
			this.setScrollLeft(this.cachedPosition.left);
			this.setScrollTop(this.cachedPosition.top);
			this.cachedPosition = null;
		}
	},
	horizontalChanged: function() {
		this.$.strategy.setHorizontal(this.horizontal);
	},
	verticalChanged: function() {
		this.$.strategy.setVertical(this.vertical);
	},
	// FIXME: these properties are virtual; property changed methods are fired only if
	// property value changes, not if getter changes.
	setScrollLeft: function(inLeft) {
		this.scrollLeft = inLeft;
		this.$.strategy.setScrollLeft(this.scrollLeft);
	},
	setScrollTop: function(inTop) {
		this.scrollTop = inTop;
		this.$.strategy.setScrollTop(inTop);
	},
	getScrollLeft: function() {
		return this.$.strategy.getScrollLeft();
	},
	getScrollTop: function() {
		return this.$.strategy.getScrollTop();
	},
	//* @public
	//* returns an object describing the scroll boundaries with height and width properties.
	getScrollBounds: function() {
		return this.$.strategy.getScrollBounds();
	},
	//* scrolls the given control (inControl) into view. If inAlignWithTop is true, inControl is aligned with the top of the scroller.
	scrollIntoView: function(inControl, inAlignWithTop) {
		this.$.strategy.scrollIntoView(inControl, inAlignWithTop);
	},
	//* Scroll to the position given by inX and inY in pixel units.
	scrollTo: function(inX, inY) {
		this.$.strategy.scrollTo(inX, inY);
	},
	//* ensure that the given control is visible in the scroller's viewport.  Unlike scrollIntoView which uses DOM's scrollIntoView, this only affects the current scroller.
	scrollToControl: function(inControl, inAlignWithTop) {
		this.scrollToNode(inControl.hasNode(), inAlignWithTop);
	},
	// ensure that the given node is visible in the scroller's viewport.
	scrollToNode: function(inNode, inAlignWithTop) {
		this.$.strategy.scrollToNode(inNode, inAlignWithTop);
	},
	// normalize scroll event to onScroll.
	domScroll: function(inSender, e) {
		// if a scroll event originated here, pass it to our strategy to handle
		if (this.$.strategy.domScroll && e.originator == this) {
			this.$.strategy.scroll(inSender, e);
		}
		this.doScroll(e);
		return true;
	},
	shouldStopScrollEvent: function(inEvent) {
		return (this.preventScrollPropagation && inEvent.originator.owner != this.$.strategy);
	},
	scrollStart: function(inSender, inEvent) {
		return this.shouldStopScrollEvent(inEvent);
	},
	scroll: function(inSender, inEvent) {
		// note: scroll event can be native dom or generated.
		if (inEvent.dispatchTarget) {
			// allow a dom event if it orignated with this scroller or its strategy
			return this.preventScrollPropagation && !(inEvent.originator == this || inEvent.originator.owner == this.$.strategy);
		} else {
			return this.shouldStopScrollEvent(inEvent);
		}
	},
	scrollStop: function(inSender, inEvent) {
		return this.shouldStopScrollEvent(inEvent);
	},
	scrollToTop: function() {
		this.setScrollTop(0);
	},
	scrollToBottom: function() {
		this.setScrollTop(this.getScrollBounds().maxTop);
	},
	scrollToRight: function() {
		this.setScrollTop(this.getScrollBounds().maxLeft);
	},
	scrollToLeft: function() {
		this.setScrollLeft(0);
	},
	//* Ensure scroll position is in bounds.
	stabilize: function() {
		var s = this.getStrategy();
		if (s.stabilize) {
			s.stabilize();
		}
	}
});

// provide a touch scrolling solution by default when the environment is mobile
if (enyo.Scroller.hasTouchScrolling()) {
	enyo.Scroller.prototype.strategyKind = enyo.Scroller.getTouchStrategy();
}
<commit_msg>Edits for Scroller.js comments<commit_after>/**
_enyo.Scroller_ is a scroller suitable for use in both desktop and mobile
applications.

In some mobile environments, a default scrolling solution is not implemented for
DOM elements.  In such cases, _enyo.Scroller_ implements a touch-based scrolling
solution, which may be opted into either globally (by setting the flag
_enyo.Scroller.touchScrolling = true;_) or on a per-instance basis (by
specifying a _strategyKind_ of "TouchScrollStrategy").
*/
enyo.kind({
	name: "enyo.Scroller",
	published: {
		/**
			Specifies how to horizontally scroll.  Acceptable values are
			"scroll", "auto," "hidden," and "default".  The precise
			effect of the setting is determined by the scroll strategy.
		*/
		horizontal: "default",
		/**
			Specifies how to vertically scroll.  Acceptable values are "scroll",
			"auto," "hidden," and "default".  The precise effect of the setting
			is determined by the scroll strategy.
		*/
		vertical: "default",
		/**
			Sets the vertical scroll position.
		*/
		scrollTop: 0,
		/**
			Sets the horizontal scroll position.
		*/
		scrollLeft: 0,
		/**
			Sets the maximum height of the scroll content.
		*/
		maxHeight: null,
		/**
			Set to true to make this scroller select a platform-appropriate
			touch-based scrolling strategy.  Please note that specifycing a
			scrollStrategy will take precedence over this setting.
		*/
		touch: false,
		/**
			Specifies a type of scrolling.  The Enyo Scroller will attempt to
			automatically select a strategy compatible with the runtime
			environment.  A specific strategy may also be chosen:

			* ScrollStrategy is the default
			    and implements no scrolling, relying instead on the environment
			    to scroll properly.

			* TouchScrollStrategy
				implements a touch scrolling mechanism.

			* TranslateScrollStrategy
				implements a touch scrolling mechanism using translations; it is
				currently recommended only for Android 3 and 4.
		*/
		strategyKind: "ScrollStrategy",
		//* Set to true to display a scroll thumb in Touch scrollers.
		thumb: true
	},
	events: {
		onScrollStart: "",
		onScroll: "",
		onScrollStop: ""
	},
	handlers: {
		onscroll: "domScroll",
		onScrollStart: "scrollStart",
		onScroll: "scroll",
		onScrollStop: "scrollStop"
	},
	classes: "enyo-scroller",
	/**
		If true, the scroller will not propagate _dragstart_ events that cause
		it to start scrolling.  (Defaults to true.)
	*/
	preventDragPropagation: true,
	/**
		If true, the scroller will not propagate scroll events.
	*/
	preventScrollPropagation: true,
	//* @protected
	statics: {
		osInfo: [
			{os: "android", version: 3},
			{os: "ios", version: 5},
			{os: "webos", version: 1e9}
		],
		//* Returns true if platform should have touch events.
		hasTouchScrolling: function() {
			for (var i=0, t, m; t=this.osInfo[i]; i++) {
				if (enyo.platform[t.os]) {
					return true;
				}
			}
		},
		//* Returns true if the platform has native div scrollers (desktop browsers always have them).
		hasNativeScrolling: function() {
			for (var i=0, t, m; t=this.osInfo[i]; i++) {
				if (enyo.platform[t.os] < t.version) {
					return false;
				}
			}
			return true;
		},
		getTouchStrategy: function() {
			return enyo.platform.android >= 3 ? "TranslateScrollStrategy" : "TouchScrollStrategy";
		}
	},
	//* @protected
	controlParentName: "strategy",
	create: function() {
		this.inherited(arguments);
		this.horizontalChanged();
		this.verticalChanged();
	},
	importProps: function(inProps) {
		this.inherited(arguments);
		// allow global overriding of strategy kind
		if (inProps && inProps.strategyKind === undefined && (enyo.Scroller.touchScrolling || this.touch)) {
			this.strategyKind = enyo.Scroller.getTouchStrategy();
		}
	},
	initComponents: function() {
		this.strategyKindChanged();
		this.inherited(arguments);
	},
	teardownChildren: function() {
		this.cacheScrollPosition();
		this.inherited(arguments);
	},
	rendered: function() {
		this.inherited(arguments);
		this.restoreScrollPosition();
	},
	strategyKindChanged: function() {
		if (this.$.strategy) {
			this.$.strategy.destroy();
			this.controlParent = null;
		}
		// note: createComponents automatically updates controlParent.
		this.createStrategy();
		if (this.hasNode()) {
			this.render();
		}
	},
	createStrategy: function() {
		this.createComponents([{name: "strategy", maxHeight: this.maxHeight, kind: this.strategyKind, thumb: this.thumb, preventDragPropagation: this.preventDragPropagation, isChrome: true}]);
	},
	getStrategy: function() {
		return this.$.strategy;
	},
	maxHeightChanged: function() {
		this.$.strategy.setMaxHeight(this.maxHeight);
	},
	showingChanged: function() {
		if (!this.showing) {
			this.cacheScrollPosition();
			this.setScrollLeft(0);
			this.setScrollTop(0);
		}
		this.inherited(arguments);
		if (this.showing) {
			this.restoreScrollPosition();
		}
	},
	thumbChanged: function() {
		this.$.strategy.setThumb(this.thumb);
	},
	cacheScrollPosition: function() {
		this.cachedPosition = {left: this.getScrollLeft(), top: this.getScrollTop()};
	},
	restoreScrollPosition: function() {
		if (this.cachedPosition) {
			this.setScrollLeft(this.cachedPosition.left);
			this.setScrollTop(this.cachedPosition.top);
			this.cachedPosition = null;
		}
	},
	horizontalChanged: function() {
		this.$.strategy.setHorizontal(this.horizontal);
	},
	verticalChanged: function() {
		this.$.strategy.setVertical(this.vertical);
	},
	// FIXME: these properties are virtual; property changed methods are fired only if
	// property value changes, not if getter changes.
	setScrollLeft: function(inLeft) {
		this.scrollLeft = inLeft;
		this.$.strategy.setScrollLeft(this.scrollLeft);
	},
	setScrollTop: function(inTop) {
		this.scrollTop = inTop;
		this.$.strategy.setScrollTop(inTop);
	},
	getScrollLeft: function() {
		return this.$.strategy.getScrollLeft();
	},
	getScrollTop: function() {
		return this.$.strategy.getScrollTop();
	},
	//* @public
	//* Returns an object describing the scroll boundaries with height and width properties.
	getScrollBounds: function() {
		return this.$.strategy.getScrollBounds();
	},
	//* Scrolls the given control (_inControl_) into view. If _inAlignWithTop_ is true, _inControl_ is aligned with the top of the scroller.
	scrollIntoView: function(inControl, inAlignWithTop) {
		this.$.strategy.scrollIntoView(inControl, inAlignWithTop);
	},
	//* Scrolls to the position specified by _inX_ and _inY_ in pixel units.
	scrollTo: function(inX, inY) {
		this.$.strategy.scrollTo(inX, inY);
	},
	//* Ensures that the given control is visible in the scroller's viewport.  Unlike _scrollIntoView_, which uses DOM's _scrollIntoView_, this only affects the current scroller.
	scrollToControl: function(inControl, inAlignWithTop) {
		this.scrollToNode(inControl.hasNode(), inAlignWithTop);
	},
	// Ensures that the given node is visible in the scroller's viewport.
	scrollToNode: function(inNode, inAlignWithTop) {
		this.$.strategy.scrollToNode(inNode, inAlignWithTop);
	},
	// Normalizes scroll event to onScroll.
	domScroll: function(inSender, e) {
		// if a scroll event originated here, pass it to our strategy to handle
		if (this.$.strategy.domScroll && e.originator == this) {
			this.$.strategy.scroll(inSender, e);
		}
		this.doScroll(e);
		return true;
	},
	shouldStopScrollEvent: function(inEvent) {
		return (this.preventScrollPropagation && inEvent.originator.owner != this.$.strategy);
	},
	scrollStart: function(inSender, inEvent) {
		return this.shouldStopScrollEvent(inEvent);
	},
	scroll: function(inSender, inEvent) {
		// note: scroll event can be native dom or generated.
		if (inEvent.dispatchTarget) {
			// allow a dom event if it orignated with this scroller or its strategy
			return this.preventScrollPropagation && !(inEvent.originator == this || inEvent.originator.owner == this.$.strategy);
		} else {
			return this.shouldStopScrollEvent(inEvent);
		}
	},
	scrollStop: function(inSender, inEvent) {
		return this.shouldStopScrollEvent(inEvent);
	},
	scrollToTop: function() {
		this.setScrollTop(0);
	},
	scrollToBottom: function() {
		this.setScrollTop(this.getScrollBounds().maxTop);
	},
	scrollToRight: function() {
		this.setScrollTop(this.getScrollBounds().maxLeft);
	},
	scrollToLeft: function() {
		this.setScrollLeft(0);
	},
	//* Ensures scroll position is in bounds.
	stabilize: function() {
		var s = this.getStrategy();
		if (s.stabilize) {
			s.stabilize();
		}
	}
});

// provide a touch scrolling solution by default when the environment is mobile
if (enyo.Scroller.hasTouchScrolling()) {
	enyo.Scroller.prototype.strategyKind = enyo.Scroller.getTouchStrategy();
}
<|endoftext|>


<commit_before>var webpack = require('webpack'),
	prefix = require('postcss-prefix-selector'),
	autoprefixer = require('autoprefixer'),
	prodEnv = process.env.NODE_ENV == 'production', // eslint-disable-line no-undef
	testEnv = process.env.NODE_ENV == 'test' // eslint-disable-line no-undef

var config = {
	devtool: 'cheap-module-source-map',
	entry: {
		'cout-embauche':
			(testEnv || prodEnv) ? [
				'babel-polyfill',
				'./source/entry.js'
			] : [
				'webpack-dev-server/client?http://localhost:3000/',
				'webpack/hot/only-dev-server',
				'react-hot-loader/patch',
				'babel-polyfill',
				'./source/entry.js',
			],
		'simulateur': './source/entry-iframe.js',
		'test': [
			'babel-polyfill',
			'./source/test/test.js'
		],
		'colour-chooser': [
			'babel-polyfill',
			'./source/entry-colour-chooser.js'
		]
	},
	output: {
		path: require('path').resolve('./dist/'),
		filename: '[name].js',
		publicPath: '/dist/',
	},
	module: {
		loaders: [
			{
				test: /\.css$/,
				loader: 'style!css!postcss-loader',
			}, {
				test: /\.html$/,
				loader: 'html',
			},
			{
				test: /\.js$/,
				exclude: /node_modules/,
				loader: 'babel-loader',
			},
			{
				test: /\.(jpe?g|png|gif|svg)$/i,
				loader: 'url?limit=10000!img?progressive=true',
			},
			{
				test: /\.yaml$/,
				loader: 'json!yaml',
			},
			{
				test: /\.json$/,
				loader: 'json',
			}
		]
	},
	postcss: [
		autoprefixer({
			browsers: [ '> 1% in FR', 'not ie < 10' ],
		}),
		/* prefix all style rules with the root html class
		to avoid disturbing the widget's host page */
		prefix({
			prefix: '.SGMAPembauche ',	// <--- notice the trailing space!
			exclude: [ '.SGMAPembauche' ],	// to style the prefix container itself
		}),
	],
	externals: {
		'cheerio': 'window',
		'react/addons': true,
		'react/lib/ExecutionEnvironment': true,
		'react/lib/ReactContext': true
	}
}

/* Plugins */

var
	hotReloading = (prodEnv || testEnv) ? [] : [new webpack.HotModuleReplacementPlugin()],
	fetchDefinition = [testEnv ?
		new webpack.ProvidePlugin({'fetch': 'node-fetch'}) :
		new webpack.ProvidePlugin({
			'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch',
		})],
	noError = [new webpack.NoErrorsPlugin()]

config.plugins =
	hotReloading
		.concat(fetchDefinition)
		.concat(noError)

module.exports = config
<commit_msg>Do not compile the test webpack entry outside testEnv<commit_after>var webpack = require('webpack'),
	prefix = require('postcss-prefix-selector'),
	autoprefixer = require('autoprefixer'),
	prodEnv = process.env.NODE_ENV == 'production', // eslint-disable-line no-undef
	testEnv = process.env.NODE_ENV == 'test' // eslint-disable-line no-undef

var config = {
	devtool: 'cheap-module-source-map',
	entry: (function() {
		let
			jsModule = {
				'cout-embauche':
					(testEnv || prodEnv) ? [
						'babel-polyfill',
						'./source/entry.js'
					] : [
						'webpack-dev-server/client?http://localhost:3000/',
						'webpack/hot/only-dev-server',
						'react-hot-loader/patch',
						'babel-polyfill',
						'./source/entry.js',
					]
			},
			iframeModule = {
				'simulateur': './source/entry-iframe.js'
			},
			testModule = testEnv && {
				test: [
					'babel-polyfill',
					'./source/test/test.js'
				]
			},
			colourModule = !testEnv && {
				'colour-chooser': [
					'babel-polyfill',
					'./source/entry-colour-chooser.js'
				]
			}

		return Object.assign(
			jsModule, iframeModule, testModule, colourModule
		)
	})(),
	output: {
		path: require('path').resolve('./dist/'),
		filename: '[name].js',
		publicPath: '/dist/',
	},
	module: {
		loaders: [
			{
				test: /\.css$/,
				loader: 'style!css!postcss-loader',
			}, {
				test: /\.html$/,
				loader: 'html',
			},
			{
				test: /\.js$/,
				exclude: /node_modules/,
				loader: 'babel-loader',
			},
			{
				test: /\.(jpe?g|png|gif|svg)$/i,
				loader: 'url?limit=10000!img?progressive=true',
			},
			{
				test: /\.yaml$/,
				loader: 'json!yaml',
			},
			{
				test: /\.json$/,
				loader: 'json',
			}
		]
	},
	postcss: [
		autoprefixer({
			browsers: [ '> 1% in FR', 'not ie < 10' ],
		}),
		/* prefix all style rules with the root html class
		to avoid disturbing the widget's host page */
		prefix({
			prefix: '.SGMAPembauche ',	// <--- notice the trailing space!
			exclude: [ '.SGMAPembauche' ],	// to style the prefix container itself
		}),
	],
	externals: {
		'cheerio': 'window',
		'react/addons': true,
		'react/lib/ExecutionEnvironment': true,
		'react/lib/ReactContext': true
	}
}

/* Plugins */

var
	hotReloading = (prodEnv || testEnv) ? [] : [new webpack.HotModuleReplacementPlugin()],
	fetchDefinition = [testEnv ?
		new webpack.ProvidePlugin({'fetch': 'node-fetch'}) :
		new webpack.ProvidePlugin({
			'fetch': 'imports?this=>global!exports?global.fetch!whatwg-fetch',
		})],
	noError = [new webpack.NoErrorsPlugin()]

config.plugins =
	[].concat(hotReloading)
		.concat(fetchDefinition)
		.concat(noError)

module.exports = config
<|endoftext|>


<commit_before>/*global jasmine*/
var excludes = [ // these examples has no ng-map
  "all-examples.html",
  "map_events.html",
  "map_lazy_init.html",
  "map-lazy-load.html",
  "map-lazy-load-params.html",
  "places-auto-complete.html"
];

function using(values, func){
  'use strict';
  for (var i = 0, count = values.length; i < count; i++) {
    (!Array.isArray(values[i])) && (values[i] = [values[i]]);
    func.apply(this, values[i]); //jshint ignore:line
    jasmine.currentEnv_.currentSpec.description +=
      ' (with using ' + values[i].join(', ') + ')';
  }
}

describe('testapp directory', function() {
  'use strict';
  var files = require('fs').readdirSync(__dirname + "/../../testapp");
  files = files.filter(function(filename) {
    return filename.match(/\.html$/) && excludes.indexOf(filename) === -1;
  });

  var urls = {};
  for (var i=0;i<files.length; i++) {
    var groupId = Math.floor(i/10);
    urls[groupId] = urls[groupId] || [];
    urls[groupId].push(files[i]);
  }
  console.log('urls', urls);

  for (var key in urls) {
    using(urls[key], function(url){
      it('testapp/'+url, function() {
        browser.get('testapp/'+url);

        browser.wait( function() {
          return browser.executeScript( function() {
            var el = document.querySelector("ng-map");
            var injector = angular.element(el).injector();
            var NgMap = injector.get('NgMap');
            return NgMap.getMap();
          }).then(function(map) {
            return map;
          });
        }, 5000);
      });
    });
  }

});
<commit_msg>e2e test refactoring<commit_after>/*global jasmine*/
var excludes = [ // these examples has no ng-map
  "all-examples.html",
  "map_events.html",
  "map_lazy_init.html",
  "map-lazy-load.html",
  "map-lazy-load-params.html",
  "places-auto-complete.html"
];

function using(values, func){
  'use strict';
  for (var i = 0, count = values.length; i < count; i++) {
    (!Array.isArray(values[i])) && (values[i] = [values[i]]);
    func.apply(this, values[i]); //jshint ignore:line
    jasmine.currentEnv_.currentSpec.description +=
      ' (with using ' + values[i].join(', ') + ')';
  }
}

describe('testapp directory', function() {
  'use strict';
  var files = require('fs').readdirSync(__dirname + "/../../testapp");
  files = files.filter(function(filename) {
    return filename.match(/\.html$/) && excludes.indexOf(filename) === -1;
  }).splice(0,2);

  var urls = {};
  for (var i=0;i<files.length; i++) {
    var groupId = Math.floor(i/10);
    urls[groupId] = urls[groupId] || [];
    urls[groupId].push(files[i]);
  }
  console.log('urls', urls);

  afterEach(function() {
    browser.manage().logs().get('browser').then(function(browserLog) {
      expect(browserLog.length).toEqual(0);
      browserLog.length && console.log(JSON.stringify(browserLog));
    });
  });

  for (var key in urls) {
    using(urls[key], function(url){
      it('testapp/'+url, function() {
        browser.get('testapp/'+url);

        browser.wait( function() {
          return browser.executeScript( function() {
            var el = document.querySelector("ng-map");
            var injector = angular.element(el).injector();
            var NgMap = injector.get('NgMap');
            return NgMap.getMap();
          }).then(function(map) {
            return map;
          });
        }, 5000);
      });
    });
  }

});
<|endoftext|>


<commit_before>describe("Gilded Rose", function() {
  var foo;

  beforeEach(function() {
    foo = 0;
    foo += 1;
  });

  afterEach(function() {
    foo = 0;
  });

  it("should do something", function() {

  });

  it("is a syntax example", function() {
    expect(true).toBe(true);
  });

  it("can have a negative case", function() {
    expect(false).not.toBe(true);
  });

});
<commit_msg>Add grouped specs example<commit_after>describe("Gilded Rose", function() {
  var foo;

  beforeEach(function() {
    foo = 0;
    foo += 1;
  });

  afterEach(function() {
    foo = 0;
  });

  it("should do something", function() {

  });

  it("is a syntax example", function() {
    expect(true).toBe(true);
  });

  it("can have a negative case", function() {
    expect(false).not.toBe(true);
  });

  describe("Another spec", function() {
    it("is just a function, so it can contain any code", function() {
      var foo = 0;
      foo += 1;

      expect(foo).toEqual(1);
    });
  });

});
<|endoftext|>


<commit_before>describe('suite', function () {
  // Use sessionStorage for temporarily storing dummy data
  // and not tainting existing localStorage data
  // Also allows us to avoid worrying about destroying localhost data
  var storage   = window.sessionStorage,
      stringify = JSON.stringify;

  describe('commands', function () {

    // Asserts that a passed function
    function shouldBeChainable(func) {

    }

    afterEach(function () {
      // It's not enough to rely on sessionStorage to
      // flush since it only flushes on a crash
      storage.clear();
    });

    describe('set', function () {
      it('should store a value indexed by its key', function () {
        var k = 'foo',
            v = 'bar';

        // Attempt to store
        storage.set(k, v);

        // Retrieve the value and make sure it's equal to the stored value
        // Uses getItem to avoid the dependency on the untested get()
        var val = storage.getItem(k);
        expect(val).toBe(v);
      });

      it('should auto stringify an object being stored as a value', function () {
        var k = 'foo',
            v = {"name": "Yogi Bear"},
            type;

        storage.set(k, v);

        // Check that the value stored is a string
        type = typeof storage.getItem(k);
        expect(type).toBe('string');
      });

      it('should accept objects as keys', function () {
        var k = {"name": "Yogi Bear"},
            v = 2,
            val;

        storage.set(k, v);

        val = storage.getItem(stringify(k));
        // getItem() returns string values
        expect(val).toBe(v.toString());
      });

      it('should be chainable', function () {
        // Throws a TypeError if invocation is illegal
        expect(function(){ storage.set('foo', 1).set('bar', 2); }).not.toThrow(TypeError);
      });

    }); // end set

    describe('get', function () {
      it('should retrieve a value for a key that exists', function () {
        var k = 'foo'
          , v = 2;

        // Set the data – uses the safer setItem to avoid dependency on untested set().
        storage.setItem(k, v);
        expect(storage.get(k)).toBe(v);
      });

      it('should return a string if the value is a string literal (alphabetical)', function () {
        var k = 'foo'
          , v = 'Yogi Bear';

        storage.setItem(k, v);
        expect(storage.get(k)).toBe(v);
      });

      it('should return an object for a string value that contains an object', function () {
        var k = 'foo'
          , v = {"name": "Yogi Bear"};

        storage.setItem(k, stringify(v));
        expect(storage.get(k)).toEqual(v);
      });

      it('should return a number for a string value that contains a number', function () {
        var k = 'foo'
          , v = 2
          , val;

        storage.setItem(k, v);

        val = storage.get(k);

        expect(val).toBe(v);
      });

      it('should accept an object as a key', function () {
        var k = {"name": "Yogi Bear"}
          , v = 2
          , val;

        storage.setItem(stringify(k), v);

        val = storage.get(k);

        // Get auto parses, so we don't need v.toString()
        expect(val).toBe(v);
      });
    }); // end get

    describe('mget', function () {
      it('should retrieve the values for multiple keys', function () {
        var keysVals  = ['first', 'Joel', 'last', 'Kemp'],
            results   = [],
            expectVals= [keysVals[1], keysVals[3]];

        // Store all of the data
        for (var i = 0, l = keysVals.length; i < l; i += 2) {
          storage.setItem(keysVals[i], keysVals[i + 1]);
        }

        // Check the mget('key1', 'key2') syntax
        results[0] = storage.mget(keysVals[0], keysVals[2]);

        // Check the mget(['key1', 'key2']) syntax
        results[1] = storage.mget([keysVals[0], keysVals[2]]);

        // Check that the results sets have the proper values
        expect(results[0]).toEqual(expectVals);
        expect(results[1]).toEqual(expectVals);
      });
    }); // end mget

    describe('mset', function () {
      var keysVals  = ['first', 'Joel', 'last', 'Kemp'];

      afterEach(function () {
        // Remove the dummy data to test other insertion syntaxes
        for (var i = 0, l = keysVals.length; i < l; i += 2) {
          storage.removeItem(keysVals[i]);
        }
      });

      it('should store multiple key-value pairs passed as separate parameters', function () {
        // Test the mset('k1', 'v1', 'k2', 'v2') syntax
        storage.mset(keysVals[0], keysVals[1], keysVals[2], keysVals[3]);

        // The values should be correct if the mset worked
        expect(storage.getItem(keysVals[0])).toBe(keysVals[1]);
        expect(storage.getItem(keysVals[2])).toBe(keysVals[3]);
      });

      it('should store multiple key-value pairs passed as a list', function () {
        // Test the mset(['k1', 'v1', 'k2', 'v2']) syntax
        storage.mset(keysVals);

        expect(storage.getItem(keysVals[0])).toBe(keysVals[1]);
        expect(storage.getItem(keysVals[2])).toBe(keysVals[3]);
      });

      it('should store multiple key-value pairs passed as an object', function () {
        var objForm = {};
        // Note: we can't use vars with literal notation
        objForm[keysVals[0]] = keysVals[1];
        objForm[keysVals[2]] = keysVals[3];

        // Test the mset({'k1': 'v1', 'k2': 'v2'}) syntax
        storage.mset(objForm);

        expect(storage.getItem(keysVals[0])).toBe(keysVals[1]);
        expect(storage.getItem(keysVals[2])).toBe(keysVals[3]);
      });

      it('should be chainable', function () {
        // Throws a TypeError if invocation is illegal
        expect(function(){ storage.mset(keysVals).mset(keysVals); }).not.toThrow(TypeError);
      });

      // it should be an atomic operation

    }); // end mset
  }); // end commands

});<commit_msg>Remove jasmin helper mock. Too hard to make it useful.<commit_after>describe('HTML5 Local Redis Test Suite', function () {
  // Use sessionStorage for temporarily storing dummy data
  // and not tainting existing localStorage data
  // Also allows us to avoid worrying about destroying localhost data
  var storage   = window.sessionStorage,
      stringify = JSON.stringify;

  describe('commands', function () {

    afterEach(function () {
      // It's not enough to rely on sessionStorage to
      // flush since it only flushes on a crash
      storage.clear();
    });

    describe('set', function () {
      it('should store a value indexed by its key', function () {
        var k = 'foo',
            v = 'bar';

        // Attempt to store
        storage.set(k, v);

        // Retrieve the value and make sure it's equal to the stored value
        // Uses getItem to avoid the dependency on the untested get()
        var val = storage.getItem(k);
        expect(val).toBe(v);
      });

      it('should auto stringify an object being stored as a value', function () {
        var k = 'foo',
            v = {"name": "Yogi Bear"},
            type;

        storage.set(k, v);

        // Check that the value stored is a string
        type = typeof storage.getItem(k);
        expect(type).toBe('string');
      });

      it('should accept objects as keys', function () {
        var k = {"name": "Yogi Bear"},
            v = 2,
            val;

        storage.set(k, v);

        val = storage.getItem(stringify(k));
        // getItem() returns string values
        expect(val).toBe(v.toString());
      });

      it('should be chainable', function () {
        // Throws a TypeError if invocation is illegal
        expect(function(){ storage.set('foo', 1).set('bar', 2); }).not.toThrow(TypeError);
      });

    }); // end set

    describe('get', function () {
      it('should retrieve a value for a key that exists', function () {
        var k = 'foo'
          , v = 2;

        // Set the data – uses the safer setItem to avoid dependency on untested set().
        storage.setItem(k, v);
        expect(storage.get(k)).toBe(v);
      });

      it('should return a string if the value is a string literal (alphabetical)', function () {
        var k = 'foo'
          , v = 'Yogi Bear';

        storage.setItem(k, v);
        expect(storage.get(k)).toBe(v);
      });

      it('should return an object for a string value that contains an object', function () {
        var k = 'foo'
          , v = {"name": "Yogi Bear"};

        storage.setItem(k, stringify(v));
        expect(storage.get(k)).toEqual(v);
      });

      it('should return a number for a string value that contains a number', function () {
        var k = 'foo'
          , v = 2
          , val;

        storage.setItem(k, v);

        val = storage.get(k);

        expect(val).toBe(v);
      });

      it('should accept an object as a key', function () {
        var k = {"name": "Yogi Bear"}
          , v = 2
          , val;

        storage.setItem(stringify(k), v);

        val = storage.get(k);

        // Get auto parses, so we don't need v.toString()
        expect(val).toBe(v);
      });
    }); // end get

    describe('mget', function () {
      it('should retrieve the values for multiple keys', function () {
        var keysVals  = ['first', 'Joel', 'last', 'Kemp'],
            results   = [],
            expectVals= [keysVals[1], keysVals[3]];

        // Store all of the data
        for (var i = 0, l = keysVals.length; i < l; i += 2) {
          storage.setItem(keysVals[i], keysVals[i + 1]);
        }

        // Check the mget('key1', 'key2') syntax
        results[0] = storage.mget(keysVals[0], keysVals[2]);

        // Check the mget(['key1', 'key2']) syntax
        results[1] = storage.mget([keysVals[0], keysVals[2]]);

        // Check that the results sets have the proper values
        expect(results[0]).toEqual(expectVals);
        expect(results[1]).toEqual(expectVals);
      });
    }); // end mget

    describe('mset', function () {
      var keysVals  = ['first', 'Joel', 'last', 'Kemp'];

      afterEach(function () {
        // Remove the dummy data to test other insertion syntaxes
        for (var i = 0, l = keysVals.length; i < l; i += 2) {
          storage.removeItem(keysVals[i]);
        }
      });

      it('should store multiple key-value pairs passed as separate parameters', function () {
        // Test the mset('k1', 'v1', 'k2', 'v2') syntax
        storage.mset(keysVals[0], keysVals[1], keysVals[2], keysVals[3]);

        // The values should be correct if the mset worked
        expect(storage.getItem(keysVals[0])).toBe(keysVals[1]);
        expect(storage.getItem(keysVals[2])).toBe(keysVals[3]);
      });

      it('should store multiple key-value pairs passed as a list', function () {
        // Test the mset(['k1', 'v1', 'k2', 'v2']) syntax
        storage.mset(keysVals);

        expect(storage.getItem(keysVals[0])).toBe(keysVals[1]);
        expect(storage.getItem(keysVals[2])).toBe(keysVals[3]);
      });

      it('should store multiple key-value pairs passed as an object', function () {
        var objForm = {};
        // Note: we can't use vars with literal notation
        objForm[keysVals[0]] = keysVals[1];
        objForm[keysVals[2]] = keysVals[3];

        // Test the mset({'k1': 'v1', 'k2': 'v2'}) syntax
        storage.mset(objForm);

        expect(storage.getItem(keysVals[0])).toBe(keysVals[1]);
        expect(storage.getItem(keysVals[2])).toBe(keysVals[3]);
      });

      it('should be chainable', function () {
        // Throws a TypeError if invocation is illegal
        expect(function(){ storage.mset(keysVals).mset(keysVals); }).not.toThrow(TypeError);
      });

      // it should be an atomic operation

    }); // end mset
  }); // end commands

});<|endoftext|>


<commit_before>app.controller("HomeController", [
    "$scope",
    "$location",
    "$log",
    "$localStorage",
    "Request",
    "domainBase",
    "whoisUrl",
    function ($scope, $location, $log, $localStorage, Request, domainBase, whoisUrl) {
        'use strict';
        $scope.verify = function () {
            $scope.error = false;
            $scope.warn = false;
            $scope.status = null;
            $scope.disableBtn = true;
            var data = ($.url($scope.domain)).attr();
            var requestUrl = domainBase + whoisUrl;
            // console.log($scope.domain, data);
            Request.fetch(requestUrl, data)
                .then(function (response) {
                    // body...
                    if (!response.success) {
                        $scope.warn = response.message;
                        $scope.status = response.status + ' -';
                    }
                    $localStorage.domain = response.data;

                    $location.path('/verify');

                }, function (error) {
                    // body...
                    $scope.error = error.statusText;
                    $scope.status = error.status + ' -';
                });
            $scope.disableBtn = false;

        };

        $log.info("Home Controller Initialized");
    }]);
<commit_msg>update home controller<commit_after>app.controller("HomeController", [
    "$scope",
    "$location",
    "$log",
    "$localStorage",
    "Request",
    "domainBase",
    "whoisUrl",
    function ($scope, $location, $log, $localStorage, Request, domainBase, whoisUrl) {
        'use strict';
        $scope.whois = function () {
            $scope.error = null;
            $scope.warn = null;
            $scope.status = null;
            $scope.disableBtn = true;
            var data = ($.url($scope.domain)).attr();
            var requestUrl = domainBase + whoisUrl;
            // console.log($scope.domain, data);
            Request.fetch(requestUrl, data)
                .then(function (response) {
                    // body...
                    if (!response.success) {
                        $scope.warn = response.message;
                        $scope.status = response.status + ' -';
                    }
                    $localStorage.domain = response.data;

                    $location.path('/verify');

                }, function (error) {
                    // body...
                    $scope.error = error.statusText;
                    $scope.status = error.status + ' -';
                });
            $scope.disableBtn = false;

        };

        $log.info("Home Controller Initialized");
    }]);
<|endoftext|>