<commit_before>/*global System*/
// import config from "../config.js";
import { obj, arr, num } from "lively.lang";
import { Rectangle, Color, pt } from "lively.graphics";
import { connect, signal, disconnect } from "lively.bindings"; // for makeInputLine
import { Text } from "../text/morph.js"
import { Range } from "./range.js";

export default class InputLine extends Text {

/*

  This represents a single text input line, useful in places where users
  should be prompted for short strings

  The input line provides two connection points: input and inputChanged
  input is signaled when a user accepts the text content, e.g. by pressing enter,
  inputChanged is signaled when the input text changes, basically on every keystroke

  props:
    clearOnInput: Boolean to indicate to remove the users input when input is signaled
    historyId: id to use for providing an input history, the history stores
       the users accepted inputs and when a InputLine is subsequently created with
       the same id those inputs become available again via Alt-H (browse history) or
       Arrow Up/Down (traverse history)
    label: a string that prefixes the text content that is static, i.e. it
      cannot be selected or changed by the user and will not be part of "input"
    input: string to pre-fill the input text

  Example:

    InputLine.getHistory("name query");

    var input = Text.makeInputLine({
      fill: Color.green,
      historyId: "name query",
      label: "What is your name? ",
      placeholder: "type your name in here",
      width: 300
    }).openInWorld();

    connect(input, 'inputAccepted', input, 'remove');
    connect(input, 'inputAccepted', input.world(), 'setStatusMessage');
    input.focus()

*/

  static getHistoryFromLocalSorage(id) {
    if (typeof localStorage === "undefined") return null;
    try {
      var hist = localStorage.getItem("lively.morphic-inputline-" + id);
      return hist ? JSON.parse(hist) : null;
    } catch (e) {
      return null
    }
  }

  static addHistoryToLocalStorage(id, hist) {
    if (typeof localStorage === "undefined") return;
    try {
      localStorage.setItem("lively.morphic-inputline-" + id, JSON.stringify(hist));
    } catch (e) { console.error(e); }
  }

  static get histories() {
    if (!this._histories) this._histories = new Map();
    return this._histories;
  }

  static getHistory(id) {
    var hist = this.histories.get(id);
    if (hist) return hist;
    hist = this.getHistoryFromLocalSorage(id) || {items: [], max: 50, index: 0};
    this.histories.set(id, hist);
    return hist;
  }

  static setHistory(id, hist = {items: [], max: 50, index: 0}) {
    this.histories.set(id, hist);
    this.addHistoryToLocalStorage(id, hist);
    return hist;
  }

  static get properties() {

    return {
      fixedWidth:   {defaultValue: true},
      fixedHeight:  {defaultValue: true},
      extent:       {defaultValue: pt(100, 20)},
      padding:      {defaultValue: Rectangle.inset(2,4)},
      clipMode:     {defaultValue: "hidden"},
      lineWrapping: {defaultValue: false},
      historyId:    {defaultValue: null},
      clearOnInput: {defaultValue: false},

      haloShadow: {
        defaultValue: {
          blur: 6,
          color: Color.rgb(52,152,219),
          distance: 0,
          rotation: 45
        }
      },
      highlightWhenFocused: {defaultValue: false},

      height: {
        after: ["padding", "textAndAttributes"],
        initialize() {
          this.height = this.defaultLineHeight + this.padding.top() + this.padding.bottom();
        }
      },

      label: {
        after: ["textAndAttributes", "extent", "padding", "submorphs"], defaultValue: "",
        set(value) {
          this.setProperty("label", value);
          if (this.textString.startsWith(value)) return;
          disconnect(this, 'textChange', this, 'onInputChanged');
          this.textString = value + this.input;
          connect(this, 'textChange', this, 'onInputChanged');
        }
      },

      input: {
        after: ["label"], derived: true,
        get() {
          return this.textString.slice(this.label.length);
        },
        set(val) {
          this.textString = this.label + (val ? String(val) : "");
          this.updatePlaceholder();
        }
      },

      placeholder: {
        after: ["submorphs", "label", "defaultTextStyle"], dervied: true,
        get() {
          let placeholder = this.getSubmorphNamed("placeholder");
          return placeholder ? placeholder.value : null;
        },
        set(val) {
          let placeholder = this.getSubmorphNamed("placeholder");
          if (!val) {
            if (placeholder) {
              placeholder.remove();
              placeholder = null;
            }
          } else {
            if (!placeholder) {
              placeholder = this.addMorph(Text.makeLabel(val, {
                ...this.defaultTextStyle,
                name: "placeholder",
                reactsToPointer: false,
                fontColor: Color.gray
              }));
            } else {
              placeholder.defaultTextStyle = {...this.defaultTextStyle, fontColor: Color.gray};
              placeholder.value = val;
            }
          }
          this.updatePlaceholder();
        }
      }
    }
  }

  constructor(props = {}) {
    super(props);
    connect(this, 'textChange', this, 'onInputChanged');
    connect(this, 'selectionChange', this, 'fixCursor');
    this.updatePlaceholder();
  }

  get isInputLine() { return true; }

  get allowDuplicatesInHistory() { return false }

  resetHistory() { this.inputHistory = {items: [], max: 50, index: 0}; }

  get inputHistory() {
    if (this._inputHistory) return this._inputHistory;
    return this._inputHistory = this.historyId ?
      this.constructor.getHistory(this.historyId) :
      {items: [], max: 30, index: 0};
  }

  set inputHistory(hist) {
    this._inputHistory = hist;
    this.historyId && this.constructor.setHistory(this.historyId, this._inputHistory);
  }

  clear() {
    this.input = "";
  }

  focus() {
    this.fixCursor();
    return super.focus();
  }

  updatePlaceholder() {
    let placeholder = this.getSubmorphNamed("placeholder");
    if (!placeholder) return;
    if (this.input.length) {
      placeholder.visible = false;
      return;
    }
    placeholder.visible = true;
    placeholder.height = this.height;
    placeholder.padding = this.padding;
    placeholder.defaultTextStyle = this.defaultTextStyle;
    if (this.label.length)
      placeholder.leftCenter = this.textBounds().rightCenter().addXY(0, this.borderWidth);
    else
      placeholder.leftCenter = pt(0, this.height/2);
  }

  fixCursor() {
    if (!this.label) return
    var leadIndex = this.positionToIndex(this.selection.lead);
    if (leadIndex < this.label.length)
      this.selection.lead = this.indexToPosition(this.label.length)
    var anchorIndex = this.positionToIndex(this.selection.anchor);
    if (anchorIndex < this.label.length)
      this.selection.anchor = this.indexToPosition(this.label.length)
  }

  acceptInput() { var i = this.input; this.onInput(i); return i; }
  onInput(input) {
    if (this.input.length > 0) this.addInputToHistory(this.input);
    this.clearOnInput && this.clear();
    signal(this, "inputAccepted", input);
  }
  onInputChanged(change) {
    signal(this, "inputChanged", change);
    this.updatePlaceholder();
  }

  deleteText(range) {
    range = range.isRange ? range : new Range(range);
    if (range.isEmpty()) return;
    range = range.subtract({
      start: {row: 0, column: 0},
      end: {row: 0, column: this.label.length}
    })[0];
    return super.deleteText(range);
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // history

  addInputToHistory(input) {
    var hist = this.inputHistory,
        items = hist.items;
    if (arr.last(items) === input) return;
    items.push(input);
    if (items.length > hist.max) {
      hist.items = items = items.slice(-hist.max);
    }
    hist.index = items.length - 1;
    if (!this.allowDuplicatesInHistory) {
      for (var i = hist.items.length-1; i--; )
        if (hist.items[i] === input) {
          hist.items.splice(i, 1); hist.index--; }
    }
    this.historyId && this.constructor.addHistoryToLocalStorage(this.historyId, hist);
  }

  async browseHistory() {
    var items = this.inputHistory.items.map((item, i) =>
          ({isListItem: true, string: item, value: i})).reverse(),
        {selected: [item]} = await this.world().filterableListPrompt(
                                "Choose item:", items, {commands: [this.histEditCommandForHistBrowse()]});
    typeof item === "number" && this.setAndShowHistItem(item);
    this.focus();
  }

  histEditCommandForHistBrowse() {
    return {
      name: "edit history " + this.historyId,
      exec: async prompt => {
        var items = this.inputHistory.items.slice().reverse()
        var {status, list: values} = await prompt.world().editListPrompt(
                                        "edit history " + this.historyId, items)
        if ("canceled" === status) return true;
        items = items.filter(ea => ea.isListItem ? values.includes(ea.value) : values.includes(ea));
        this.inputHistory = {...this.inputHistory, items};
        prompt.get("list").items = items.map((item, i) =>
          ({isListItem: true, string: item, value: i})).reverse()
        return true;
      }
    }
  }

  setAndShowHistItem(idx) {
    var hist = this.inputHistory, items = hist.items, len = items.length-1, i = idx;
    if (!num.between(i, 0, len+1)) hist.index = i = len;
    else hist.index = i;
    if (this.input !== items[i] && typeof items[i] !== 'undefined') this.input = items[i];
  }

  showHistItem(dir) {
    dir = dir || 'next';
    var hist = this.inputHistory, items = hist.items, len = items.length-1, i = hist.index;
    if (!num.between(i, 0, len+1)) hist.index = i = len;
    if (this.input !== items[i] && typeof items[i] !== 'undefined') { this.input = items[i]; return; }
    if (dir === 'next') {
      if (i > len) return;
      i = ++hist.index;
    } else {
      if (i <= 0) return;
      i = --hist.index;
    }
    this.input = items[i] || '';
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // ui events
  get commands() {
    return [
      {name: "accept input", exec: () => { this.acceptInput(); return true; }},
      {name: "show previous input from history", exec: () => { this.showHistItem('prev'); return true; }},
      {name: "show next input from history", exec: () => { this.showHistItem('next'); return true; }},
      {name: "browse history", exec: () => { this.browseHistory(); return true; }},
      {
        name: "remove items from history",
        exec: async inputLine => {
          var hist = inputLine.inputHistory,
              items = hist.items.map((item, i) =>
                ({isListItem: true, string: item, value: i})).reverse(),
              {selected} = await inputLine.world().filterableListPrompt(
                            "Choose items to delete:", items, {multiSelect: true});

          arr.sort(selected).reverse().forEach(index => {
            if (index < hist.index) hist.index--;
            hist.items.splice(index, 1);
          });
          inputLine.inputHistory = hist;
          return true;
        }
      }
    ].concat(super.commands);
  }

  get keybindings() {
    return super.keybindings.concat([
      {keys: "Enter", command: "accept input"},
      {keys: {mac: "Meta-S", win: "Ctrl-S"}, command: "accept input"},
      {keys: "Up|Ctrl-Up|Alt-P", command: "show previous input from history"},
      {keys: "Down|Ctrl-Down|Alt-N", command: "show next input from history"},
      {keys: "Alt-H", command: "browse history"},
      {keys: "Alt-Shift-H", command: "remove items from history"}
    ]);
  }

  onFocus() {
     this.highlightWhenFocused && this.animate({
      dropShadow: this.haloShadow,
      duration: 200
    });
  }

  onBlur() {
   this.highlightWhenFocused && this.animate({
     dropShadow: false,
     duration: 200
   });
  }
}

import { HTMLMorph } from "../html-morph.js"

// var i = new PasswordInputLine().openInWorld();
// i.remove();

export class PasswordInputLine extends HTMLMorph {

  static get properties() {
    return {
      extent: {defaultValue: pt(100,20)},
      html: {
        initialize() {
          this.html = `<input style="height: calc(100% - 6px); width: calc(100% - 6px);" type="password" value="">`;
        }
      },

      inputNode: {
        readOnly: true, derived: true, after: ["domNode"],
        get() { return this.domNode.childNodes[0]; }
      },

      input: {
        derived: true, after: ["inputNode"],
        get() { return this.inputNode.value || ""; },
        set(val) { this.ensureInputNode().then(n => n.value = val); }
      },

      placeholder: {
        derived: true, after: ["inputNode"],
        get() { return this.inputNode.placeholder; },
        set(val) { this.ensureInputNode().then(n => n.placeholder = val); }
      }

    }
  }

  constructor(opts = {}) {
    super(opts);
    this.onLoad();
  }

  onLoad() {
    this.updateHtml();
    // hmm key events aren't dispatched by default...
    this.ensureInputNode().then(node => {
      node.onkeydown = evt => this.env.eventDispatcher.dispatchDOMEvent(evt, this, "onKeyDown");
      node.onkeyup = evt => this.env.eventDispatcher.dispatchDOMEvent(evt, this, "onKeyUp");
    });
  }

  ensureInputNode() { return this.whenRendered().then(() => this.inputNode); }

  static get properties() {
    return {
      fontSize: {
        defaultValue: 12,
        set(value) {
          this.setProperty("fontSize", value);
          this.updateHtml();
        }
      }
    }
  }

  onKeyDown(evt) {
    super.onKeyDown(evt);
    // at that point in time the input has not changed to the most recent value yet
    if (this.input != this.lastInput) {
      this.onInputChanged(this.input);
    }
    this.lastInput = this.input
  }

  get inputNode() { return this.domNode.childNodes[0]; }
  ensureInputNode() { return this.whenRendered().then(() => this.inputNode); }

  get input() { return this.inputNode.value || ""; }
  set input(val) {
    this.ensureInputNode().then(n => n.value = val);
  }

  get placeholder() { return this.inputNode.placeholder; }
  set placeholder(val) { this.ensureInputNode().then(n => n.placeholder = val); }

  focus() { this.ensureInputNode().then(n => n.focus()); }

  acceptInput() { var i = this.input; signal(this, "inputAccepted", i); return i; }
  onInputChanged(change) { signal(this, "inputChanged", change); }

  updateHtml() {
    this.html = `<input style="height: calc(100% - 6px); width: calc(100% - 6px); font-size: ${this.fontSize}pt" type="password" value="${this.input}">`;
  }

  get commands() {
    return [
      {name: "accept input", exec: () => { this.acceptInput(); return true; }}
    ].concat(super.commands);
  }

  get keybindings() {
    return super.keybindings.concat([
      {keys: "Enter", command: "accept input"},
      {keys: {mac: "Meta-S", win: "Ctrl-S"}, command: "accept input"}
    ]);
  }
}
<commit_msg>fix input line focus / blur<commit_after>/*global System*/
// import config from "../config.js";
import { obj, arr, num } from "lively.lang";
import { Rectangle, Color, pt } from "lively.graphics";
import { connect, signal, disconnect } from "lively.bindings"; // for makeInputLine
import { Text } from "../text/morph.js"
import { Range } from "./range.js";

export default class InputLine extends Text {

/*

  This represents a single text input line, useful in places where users
  should be prompted for short strings

  The input line provides two connection points: input and inputChanged
  input is signaled when a user accepts the text content, e.g. by pressing enter,
  inputChanged is signaled when the input text changes, basically on every keystroke

  props:
    clearOnInput: Boolean to indicate to remove the users input when input is signaled
    historyId: id to use for providing an input history, the history stores
       the users accepted inputs and when a InputLine is subsequently created with
       the same id those inputs become available again via Alt-H (browse history) or
       Arrow Up/Down (traverse history)
    label: a string that prefixes the text content that is static, i.e. it
      cannot be selected or changed by the user and will not be part of "input"
    input: string to pre-fill the input text

  Example:

    InputLine.getHistory("name query");

    var input = Text.makeInputLine({
      fill: Color.green,
      historyId: "name query",
      label: "What is your name? ",
      placeholder: "type your name in here",
      width: 300
    }).openInWorld();

    connect(input, 'inputAccepted', input, 'remove');
    connect(input, 'inputAccepted', input.world(), 'setStatusMessage');
    input.focus()

*/

  static getHistoryFromLocalSorage(id) {
    if (typeof localStorage === "undefined") return null;
    try {
      var hist = localStorage.getItem("lively.morphic-inputline-" + id);
      return hist ? JSON.parse(hist) : null;
    } catch (e) {
      return null
    }
  }

  static addHistoryToLocalStorage(id, hist) {
    if (typeof localStorage === "undefined") return;
    try {
      localStorage.setItem("lively.morphic-inputline-" + id, JSON.stringify(hist));
    } catch (e) { console.error(e); }
  }

  static get histories() {
    if (!this._histories) this._histories = new Map();
    return this._histories;
  }

  static getHistory(id) {
    var hist = this.histories.get(id);
    if (hist) return hist;
    hist = this.getHistoryFromLocalSorage(id) || {items: [], max: 50, index: 0};
    this.histories.set(id, hist);
    return hist;
  }

  static setHistory(id, hist = {items: [], max: 50, index: 0}) {
    this.histories.set(id, hist);
    this.addHistoryToLocalStorage(id, hist);
    return hist;
  }

  static get properties() {

    return {
      fixedWidth:   {defaultValue: true},
      fixedHeight:  {defaultValue: true},
      extent:       {defaultValue: pt(100, 20)},
      padding:      {defaultValue: Rectangle.inset(2,4)},
      clipMode:     {defaultValue: "hidden"},
      lineWrapping: {defaultValue: false},
      historyId:    {defaultValue: null},
      clearOnInput: {defaultValue: false},

      haloShadow: {
        defaultValue: {
          blur: 6,
          color: Color.rgb(52,152,219),
          distance: 0,
          rotation: 45
        }
      },
      highlightWhenFocused: {defaultValue: false},

      height: {
        after: ["padding", "textAndAttributes"],
        initialize() {
          this.height = this.defaultLineHeight + this.padding.top() + this.padding.bottom();
        }
      },

      label: {
        after: ["textAndAttributes", "extent", "padding", "submorphs"], defaultValue: "",
        set(value) {
          this.setProperty("label", value);
          if (this.textString.startsWith(value)) return;
          disconnect(this, 'textChange', this, 'onInputChanged');
          this.textString = value + this.input;
          connect(this, 'textChange', this, 'onInputChanged');
        }
      },

      input: {
        after: ["label"], derived: true,
        get() {
          return this.textString.slice(this.label.length);
        },
        set(val) {
          this.textString = this.label + (val ? String(val) : "");
          this.updatePlaceholder();
        }
      },

      placeholder: {
        after: ["submorphs", "label", "defaultTextStyle"], dervied: true,
        get() {
          let placeholder = this.getSubmorphNamed("placeholder");
          return placeholder ? placeholder.value : null;
        },
        set(val) {
          let placeholder = this.getSubmorphNamed("placeholder");
          if (!val) {
            if (placeholder) {
              placeholder.remove();
              placeholder = null;
            }
          } else {
            if (!placeholder) {
              placeholder = this.addMorph(Text.makeLabel(val, {
                ...this.defaultTextStyle,
                name: "placeholder",
                reactsToPointer: false,
                fontColor: Color.gray
              }));
            } else {
              placeholder.defaultTextStyle = {...this.defaultTextStyle, fontColor: Color.gray};
              placeholder.value = val;
            }
          }
          this.updatePlaceholder();
        }
      }
    }
  }

  constructor(props = {}) {
    super(props);
    connect(this, 'textChange', this, 'onInputChanged');
    connect(this, 'selectionChange', this, 'fixCursor');
    this.updatePlaceholder();
  }

  get isInputLine() { return true; }

  get allowDuplicatesInHistory() { return false }

  resetHistory() { this.inputHistory = {items: [], max: 50, index: 0}; }

  get inputHistory() {
    if (this._inputHistory) return this._inputHistory;
    return this._inputHistory = this.historyId ?
      this.constructor.getHistory(this.historyId) :
      {items: [], max: 30, index: 0};
  }

  set inputHistory(hist) {
    this._inputHistory = hist;
    this.historyId && this.constructor.setHistory(this.historyId, this._inputHistory);
  }

  clear() {
    this.input = "";
  }

  focus() {
    this.fixCursor();
    return super.focus();
  }

  updatePlaceholder() {
    let placeholder = this.getSubmorphNamed("placeholder");
    if (!placeholder) return;
    if (this.input.length) {
      placeholder.visible = false;
      return;
    }
    placeholder.visible = true;
    placeholder.height = this.height;
    placeholder.padding = this.padding;
    placeholder.defaultTextStyle = this.defaultTextStyle;
    if (this.label.length)
      placeholder.leftCenter = this.textBounds().rightCenter().addXY(0, this.borderWidth);
    else
      placeholder.leftCenter = pt(0, this.height/2);
  }

  fixCursor() {
    if (!this.label) return
    var leadIndex = this.positionToIndex(this.selection.lead);
    if (leadIndex < this.label.length)
      this.selection.lead = this.indexToPosition(this.label.length)
    var anchorIndex = this.positionToIndex(this.selection.anchor);
    if (anchorIndex < this.label.length)
      this.selection.anchor = this.indexToPosition(this.label.length)
  }

  acceptInput() { var i = this.input; this.onInput(i); return i; }
  onInput(input) {
    if (this.input.length > 0) this.addInputToHistory(this.input);
    this.clearOnInput && this.clear();
    signal(this, "inputAccepted", input);
  }
  onInputChanged(change) {
    signal(this, "inputChanged", change);
    this.updatePlaceholder();
  }

  deleteText(range) {
    range = range.isRange ? range : new Range(range);
    if (range.isEmpty()) return;
    range = range.subtract({
      start: {row: 0, column: 0},
      end: {row: 0, column: this.label.length}
    })[0];
    return super.deleteText(range);
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // history

  addInputToHistory(input) {
    var hist = this.inputHistory,
        items = hist.items;
    if (arr.last(items) === input) return;
    items.push(input);
    if (items.length > hist.max) {
      hist.items = items = items.slice(-hist.max);
    }
    hist.index = items.length - 1;
    if (!this.allowDuplicatesInHistory) {
      for (var i = hist.items.length-1; i--; )
        if (hist.items[i] === input) {
          hist.items.splice(i, 1); hist.index--; }
    }
    this.historyId && this.constructor.addHistoryToLocalStorage(this.historyId, hist);
  }

  async browseHistory() {
    var items = this.inputHistory.items.map((item, i) =>
          ({isListItem: true, string: item, value: i})).reverse(),
        {selected: [item]} = await this.world().filterableListPrompt(
                                "Choose item:", items, {commands: [this.histEditCommandForHistBrowse()]});
    typeof item === "number" && this.setAndShowHistItem(item);
    this.focus();
  }

  histEditCommandForHistBrowse() {
    return {
      name: "edit history " + this.historyId,
      exec: async prompt => {
        var items = this.inputHistory.items.slice().reverse()
        var {status, list: values} = await prompt.world().editListPrompt(
                                        "edit history " + this.historyId, items)
        if ("canceled" === status) return true;
        items = items.filter(ea => ea.isListItem ? values.includes(ea.value) : values.includes(ea));
        this.inputHistory = {...this.inputHistory, items};
        prompt.get("list").items = items.map((item, i) =>
          ({isListItem: true, string: item, value: i})).reverse()
        return true;
      }
    }
  }

  setAndShowHistItem(idx) {
    var hist = this.inputHistory, items = hist.items, len = items.length-1, i = idx;
    if (!num.between(i, 0, len+1)) hist.index = i = len;
    else hist.index = i;
    if (this.input !== items[i] && typeof items[i] !== 'undefined') this.input = items[i];
  }

  showHistItem(dir) {
    dir = dir || 'next';
    var hist = this.inputHistory, items = hist.items, len = items.length-1, i = hist.index;
    if (!num.between(i, 0, len+1)) hist.index = i = len;
    if (this.input !== items[i] && typeof items[i] !== 'undefined') { this.input = items[i]; return; }
    if (dir === 'next') {
      if (i > len) return;
      i = ++hist.index;
    } else {
      if (i <= 0) return;
      i = --hist.index;
    }
    this.input = items[i] || '';
  }

  // -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
  // ui events
  get commands() {
    return [
      {name: "accept input", exec: () => { this.acceptInput(); return true; }},
      {name: "show previous input from history", exec: () => { this.showHistItem('prev'); return true; }},
      {name: "show next input from history", exec: () => { this.showHistItem('next'); return true; }},
      {name: "browse history", exec: () => { this.browseHistory(); return true; }},
      {
        name: "remove items from history",
        exec: async inputLine => {
          var hist = inputLine.inputHistory,
              items = hist.items.map((item, i) =>
                ({isListItem: true, string: item, value: i})).reverse(),
              {selected} = await inputLine.world().filterableListPrompt(
                            "Choose items to delete:", items, {multiSelect: true});

          arr.sort(selected).reverse().forEach(index => {
            if (index < hist.index) hist.index--;
            hist.items.splice(index, 1);
          });
          inputLine.inputHistory = hist;
          return true;
        }
      }
    ].concat(super.commands);
  }

  get keybindings() {
    return super.keybindings.concat([
      {keys: "Enter", command: "accept input"},
      {keys: {mac: "Meta-S", win: "Ctrl-S"}, command: "accept input"},
      {keys: "Up|Ctrl-Up|Alt-P", command: "show previous input from history"},
      {keys: "Down|Ctrl-Down|Alt-N", command: "show next input from history"},
      {keys: "Alt-H", command: "browse history"},
      {keys: "Alt-Shift-H", command: "remove items from history"}
    ]);
  }

  onFocus(evt) {
    super.onFocus(evt);
    this.highlightWhenFocused && this.animate({
      dropShadow: this.haloShadow,
      duration: 200
    });
  }

  onBlur(evt) {
    super.onBlur(evt);
    this.highlightWhenFocused && this.animate({
      dropShadow: false,
      duration: 200
    });
  }
}

import { HTMLMorph } from "../html-morph.js"

// var i = new PasswordInputLine().openInWorld();
// i.remove();

export class PasswordInputLine extends HTMLMorph {

  static get properties() {
    return {
      extent: {defaultValue: pt(100,20)},
      html: {
        initialize() {
          this.html = `<input style="height: calc(100% - 6px); width: calc(100% - 6px);" type="password" value="">`;
        }
      },

      inputNode: {
        readOnly: true, derived: true, after: ["domNode"],
        get() { return this.domNode.childNodes[0]; }
      },

      input: {
        derived: true, after: ["inputNode"],
        get() { return this.inputNode.value || ""; },
        set(val) { this.ensureInputNode().then(n => n.value = val); }
      },

      placeholder: {
        derived: true, after: ["inputNode"],
        get() { return this.inputNode.placeholder; },
        set(val) { this.ensureInputNode().then(n => n.placeholder = val); }
      }

    }
  }

  constructor(opts = {}) {
    super(opts);
    this.onLoad();
  }

  onLoad() {
    this.updateHtml();
    // hmm key events aren't dispatched by default...
    this.ensureInputNode().then(node => {
      node.onkeydown = evt => this.env.eventDispatcher.dispatchDOMEvent(evt, this, "onKeyDown");
      node.onkeyup = evt => this.env.eventDispatcher.dispatchDOMEvent(evt, this, "onKeyUp");
    });
  }

  ensureInputNode() { return this.whenRendered().then(() => this.inputNode); }

  static get properties() {
    return {
      fontSize: {
        defaultValue: 12,
        set(value) {
          this.setProperty("fontSize", value);
          this.updateHtml();
        }
      }
    }
  }

  onKeyDown(evt) {
    super.onKeyDown(evt);
    // at that point in time the input has not changed to the most recent value yet
    if (this.input != this.lastInput) {
      this.onInputChanged(this.input);
    }
    this.lastInput = this.input
  }

  get inputNode() { return this.domNode.childNodes[0]; }
  ensureInputNode() { return this.whenRendered().then(() => this.inputNode); }

  get input() { return this.inputNode.value || ""; }
  set input(val) {
    this.ensureInputNode().then(n => n.value = val);
  }

  get placeholder() { return this.inputNode.placeholder; }
  set placeholder(val) { this.ensureInputNode().then(n => n.placeholder = val); }

  focus() { this.ensureInputNode().then(n => n.focus()); }

  acceptInput() { var i = this.input; signal(this, "inputAccepted", i); return i; }
  onInputChanged(change) { signal(this, "inputChanged", change); }

  updateHtml() {
    this.html = `<input style="height: calc(100% - 6px); width: calc(100% - 6px); font-size: ${this.fontSize}pt" type="password" value="${this.input}">`;
  }

  get commands() {
    return [
      {name: "accept input", exec: () => { this.acceptInput(); return true; }}
    ].concat(super.commands);
  }

  get keybindings() {
    return super.keybindings.concat([
      {keys: "Enter", command: "accept input"},
      {keys: {mac: "Meta-S", win: "Ctrl-S"}, command: "accept input"}
    ]);
  }
}
<|endoftext|>


<commit_before>/*
 * Laden von Styledaten einer HTML-Seite vom Browser
 * für Vergleich von HTML-Styles für Regressions- und Back-to-Back-Tests
 *
 * casperjs load-page-styles.js --configFile="../config/default.js" --pageKey="google-form-phantomjs"
 *
 * additional argument to overwrite config settings:
 * --url=http://www.google.de --selector="form" --subdir=google-form --hover="#submit" --blacklist="adserv,doubleclick" --whitelist="trustedhostname.de"
 *
 * (c) Uwe Gerdes, entwicklung@uwegerdes.de
 */
'use strict';

/* globals document, XPathResult */

var casper = require('casper').create(),
	x = require('casper').selectXPath,
	fs = require('fs');

var config = null,
	pageKey = '',
	subdir = 'frontend.local',
	url = 'http://frontend.local/',
	domain = 'frontend.local',
	selector = ['form'],
	hover = '',
	whitelist = '',
	blacklist = '',
	uname = '',
	pval = '';

var verbose = false;

var results = {};

// evaluating arguments
if (casper.cli.options.configFile && casper.cli.options.pageKey) {
	var configFile = fs.absolute(casper.cli.options.configFile);
	pageKey = casper.cli.options.pageKey;
	console.log("config file: " + configFile);
	try {
		config = require(configFile);
	}
	catch (err) {
		throw("FAIL: could not read file " + configFile);
	}
	subdir = 'results/' + config.destDir + '/' + pageKey;
	console.log('subdir: ' + subdir);
	if (config.pages[pageKey]) {
		var page = config.pages[pageKey];
		url = page.url;
		domain = url.replace(/^https?:\/\/([^\/]+).*/, '$1');
		var selectorList;
		if (page.selectorList) {
			selectorList = page.selectorList;
		} else {
			selectorList = page.selector.split(/,/);
		}
		if (page.hover) {
			hover = page.hover;
		}
		if (page.credentials && page.credentials.length == 2) {
			uname = page.credentials[0];
			pval = page.credentials[1];
		}
	} else {
		throw("FAIL: pageKey not found: " + pageKey);
	}
	if (config.blacklist && config.blacklist.length > 0) {
		blacklist = config.blacklist;
	}
	if (config.whitelist && config.whitelist.length > 0) {
		whitelist = config.whitelist;
	}
}
casper.echo('loading: ' + url + ', selector: "' + selectorList.join(',') + (hover !== '' ? '", hover:"' + hover : '' ) + '", saving in "' + subdir + '"', 'INFO');
try {
	if (fs.stat(subdir) && !fs.stat(subdir).isDirectory()) {
		console.log('can\'t create ' + subdir);
	}
} catch(err) {
	fs.makeTree(subdir);
	console.log('created ' + subdir);
}

// event handling
casper.on('remote.message', function(msg) {
	this.echo('BROWSER: ' + msg.trim(), 'INFO');
});
casper.on('error', function(msg, backtrace) {
	this.echo('ERROR: ' + msg, 'WARNING');
	this.echo(JSON.stringify(backtrace, null, 4));
});
casper.on('http.status.404', function(resource) {
	this.echo('ERROR 404: ' + resource.url, 'ERROR');
});

// block external resources
casper.options.onResourceRequested = function(C, requestData, request) {
	if ( requestData.url.match(/https?:\/\//) && ( ! whitelistOk(requestData) || blacklistHit(requestData) ) ) {
//		casper.echo('skipped: ' + requestData.url, 'WARNING');
		request.abort();
	} else {
		if (verbose) {
			casper.echo('loading: ' + requestData.url, 'INFO');
		}
	}
};

function whitelistOk(requestData) {
	var result = false;
	if (requestData.url.indexOf(domain) > -1) {
		result = true;
	} else {
		whitelist.split(/,/).forEach(function(whitedomain){
			if (requestData.url.indexOf(whitedomain) > -1) {
				result = true;
			}
		});
	}
	return result;
}

function blacklistHit(requestData) {
	var result = false;
	if (blacklist.length > 0) {
		blacklist.split(/,/).forEach(function(blackdomain){
			if (requestData.url.indexOf(blackdomain) > -1) {
				result = true;
			}
		});
	}
	return result;
}

// evaluated in browser
function _getStyles(selector, hover) {
	var getStyles = function(element, pseudo) {
		var styles = {};
		var y = document.defaultView.getComputedStyle(element, pseudo);
		for (var i=0; i < y.length; i++) {
			styles[y[i]] = y.getPropertyValue(y[i]);
		}
		return styles;
	};
	var getElementInfo = function(element) {
		var children = [];
		if (document.defaultView.getComputedStyle(element,':before').getPropertyValue('content')) {
			children.push({
				tagName: element.tagName.toLowerCase() + ':before',
				textContent: document.defaultView.getComputedStyle(element,':before').getPropertyValue('content'),
				elementId: '',
				cssclass: '',
				type: '',
				name: '',
				value: '',
				style: getStyles(element, ':before'),
				_childElementCount : 0,
				_childElementInfo : []
			});
		}
		for (var j=0; j<element.childElementCount; j++) {
			children.push(getElementInfo(element.children[j]));
		}
		var elementInfo = {
			tagName: element.tagName.toLowerCase(),
			elementId: element.getAttribute('id') || '',
			cssclass: element.getAttribute('class') || '',
			type: element.getAttribute('type') || '',
			name: element.getAttribute('name') || '',
			value: element.getAttribute('value') || '',
			textContent: element.textContent.trim().replace(/[ \n\t]+/g, " "),
			style: getStyles(element, null),
			_childElementCount : element.childElementCount,
			_childElementInfo : children
		};
		if (document.defaultView.getComputedStyle(element,':after').getPropertyValue('content')) {
			children.push({
				tagName: element.tagName.toLowerCase() + ':after',
				textContent: document.defaultView.getComputedStyle(element,':after').getPropertyValue('content'),
				elementId: '',
				cssclass: '',
				type: '',
				name: '',
				value: '',
				style: getStyles(element, ':after'),
				_childElementCount : 0,
				_childElementInfo : []
			});
		}
		return elementInfo;
	};
	var elements = [];
	var children = [];
	if (selector.indexOf('/') === 0) {
		children = document.evaluate(selector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
	} else {
		children = document.querySelectorAll(selector);
	}
	for (var i=0; i<children.length; i++) {
		var elementInfo = getElementInfo(children[i]);
		elements.push(elementInfo);
	}
	return elements;
}

// set test class for element
function _setTestClass(selector) {
	document.querySelector(selector || 'body').classList.add('test');
}

casper.start();

casper.viewport(1024, 768);

if (uname.length > 0 && pval.length > 0) {
	casper.setHttpAuth(uname, pval);
}

selectorList.forEach(function(sel) {
	casper.thenOpen(url, function() {
		this.echo('searching for "' + sel + '"', 'INFO');
		casper.evaluate(_setTestClass);
	})
	.then(function() {
		if (hover !== '') {
			this.echo('hover to "' + hover + '"', 'INFO');
			casper.mouse.move(hover);
		}
	})
	.then(function() {
		results[sel] = this.evaluate(_getStyles, sel, hover); // evaluate in browser
	});
});
casper.then(function() {
	Object.keys(results).forEach(function(selector) {
		var result = results[selector];
		var name = safeFilename(selector);
		if (result && result.length > 0) {
			var html;
			if (selector.indexOf('/') === 0) {
				html = casper.getHTML(x(selector), true);
			} else {
				html = casper.getHTML(selector, true);
			}
			fs.write(subdir + '/' + name + '.html', html);
			casper.echo(subdir + '/' + name + '.html' + ' saved', 'INFO');
			fs.write(subdir + '/' + name + '.json', JSON.stringify(result, undefined, 4), 0);
			casper.echo(subdir + '/' + name + '.json' + ' saved', 'INFO');
			if (selector.indexOf('/') === 0) {
				casper.captureSelector(subdir + '/' + name + '.png', x(selector), { format: 'png' });
			} else {
				casper.captureSelector(subdir + '/' + name + '.png', selector, { format: 'png' });
			}
			casper.echo(subdir + '/' + name + '.png' + ' saved', 'INFO');
		} else {
			casper.echo('element not found: "' + selector + '"', 'ERROR');
		}
	});
	fs.write(subdir + '/page.html', casper.getHTML(), 0);
	casper.echo(subdir + '/page.html' + ' saved', 'INFO');
	casper.capture(subdir + '/page.png', undefined, { format: 'png' });
	casper.echo(subdir + '/page.png' + ' saved', 'INFO');
});
casper.run(function() {
	this.exit();
});

function safeFilename(name) {
	return name.replace(/[ ?#/:\(\)<>|\\]/g, "_").trim();
}
<commit_msg>linting and better defaults for load-page-styles.js<commit_after>/*
 * Laden von Styledaten einer HTML-Seite vom Browser
 * für Vergleich von HTML-Styles für Regressions- und Back-to-Back-Tests
 *
 * casperjs load-page-styles.js --configFile="../config/default.js" --pageKey="google-form-phantomjs"
 *
 * additional argument to overwrite config settings:
 * --url=http://www.google.de --selector="form" --subdir=google-form --hover="#submit" --blacklist="adserv,doubleclick" --whitelist="trustedhostname.de"
 *
 * (c) Uwe Gerdes, entwicklung@uwegerdes.de
 */
'use strict';

/* globals document, XPathResult */

var casper = require('casper').create(),
	x = require('casper').selectXPath,
	fs = require('fs');

var config = null,
	pageKey = '',
	subdir = 'localhost',
	url = 'http://localhost/',
	domain = 'localhost',
	hover = '',
	whitelist = '',
	blacklist = '',
	uname = '',
	pval = '';

var verbose = false;

var results = {};

// evaluating arguments
if (casper.cli.options.configFile && casper.cli.options.pageKey) {
	var configFile = fs.absolute(casper.cli.options.configFile);
	pageKey = casper.cli.options.pageKey;
	console.log("config file: " + configFile);
	try {
		config = require(configFile);
	}
	catch (err) {
		throw("FAIL: could not read file " + configFile);
	}
	subdir = 'results/' + config.destDir + '/' + pageKey;
	console.log('subdir: ' + subdir);
	if (config.pages[pageKey]) {
		var page = config.pages[pageKey];
		url = page.url;
		domain = url.replace(/^https?:\/\/([^\/]+).*/, '$1');
		var selectorList;
		if (page.selectorList) {
			selectorList = page.selectorList;
		} else {
			selectorList = page.selector.split(/,/);
		}
		if (page.hover) {
			hover = page.hover;
		}
		if (page.credentials && page.credentials.length == 2) {
			uname = page.credentials[0];
			pval = page.credentials[1];
		}
	} else {
		throw("FAIL: pageKey not found: " + pageKey);
	}
	if (config.blacklist && config.blacklist.length > 0) {
		blacklist = config.blacklist;
	}
	if (config.whitelist && config.whitelist.length > 0) {
		whitelist = config.whitelist;
	}
}
casper.echo('loading: ' + url + ', selector: "' + selectorList.join(',') + (hover !== '' ? '", hover:"' + hover : '' ) + '", saving in "' + subdir + '"', 'INFO');
try {
	if (fs.stat(subdir) && !fs.stat(subdir).isDirectory()) {
		console.log('can\'t create ' + subdir);
	}
} catch(err) {
	fs.makeTree(subdir);
	console.log('created ' + subdir);
}

// event handling
casper.on('remote.message', function(msg) {
	this.echo('BROWSER: ' + msg.trim(), 'INFO');
});
casper.on('error', function(msg, backtrace) {
	this.echo('ERROR: ' + msg, 'WARNING');
	this.echo(JSON.stringify(backtrace, null, 4));
});
casper.on('http.status.404', function(resource) {
	this.echo('ERROR 404: ' + resource.url, 'ERROR');
});

// block external resources
casper.options.onResourceRequested = function(C, requestData, request) {
	if ( requestData.url.match(/https?:\/\//) && ( ! whitelistOk(requestData) || blacklistHit(requestData) ) ) {
//		casper.echo('skipped: ' + requestData.url, 'WARNING');
		request.abort();
	} else {
		if (verbose) {
			casper.echo('loading: ' + requestData.url, 'INFO');
		}
	}
};

function whitelistOk(requestData) {
	var result = false;
	if (requestData.url.indexOf(domain) > -1) {
		result = true;
	} else {
		whitelist.split(/,/).forEach(function(whitedomain){
			if (requestData.url.indexOf(whitedomain) > -1) {
				result = true;
			}
		});
	}
	return result;
}

function blacklistHit(requestData) {
	var result = false;
	if (blacklist.length > 0) {
		blacklist.split(/,/).forEach(function(blackdomain){
			if (requestData.url.indexOf(blackdomain) > -1) {
				result = true;
			}
		});
	}
	return result;
}

// evaluated in browser
function _getStyles(selector, hover) {
	var getStyles = function(element, pseudo) {
		var styles = {};
		var y = document.defaultView.getComputedStyle(element, pseudo);
		for (var i=0; i < y.length; i++) {
			styles[y[i]] = y.getPropertyValue(y[i]);
		}
		return styles;
	};
	var getElementInfo = function(element) {
		var children = [];
		if (document.defaultView.getComputedStyle(element,':before').getPropertyValue('content')) {
			children.push({
				tagName: element.tagName.toLowerCase() + ':before',
				textContent: document.defaultView.getComputedStyle(element,':before').getPropertyValue('content'),
				elementId: '',
				cssclass: '',
				type: '',
				name: '',
				value: '',
				style: getStyles(element, ':before'),
				_childElementCount : 0,
				_childElementInfo : []
			});
		}
		for (var j=0; j<element.childElementCount; j++) {
			children.push(getElementInfo(element.children[j]));
		}
		var elementInfo = {
			tagName: element.tagName.toLowerCase(),
			elementId: element.getAttribute('id') || '',
			cssclass: element.getAttribute('class') || '',
			type: element.getAttribute('type') || '',
			name: element.getAttribute('name') || '',
			value: element.getAttribute('value') || '',
			textContent: element.textContent.trim().replace(/[ \n\t]+/g, " "),
			style: getStyles(element, null),
			_childElementCount : element.childElementCount,
			_childElementInfo : children
		};
		if (document.defaultView.getComputedStyle(element,':after').getPropertyValue('content')) {
			children.push({
				tagName: element.tagName.toLowerCase() + ':after',
				textContent: document.defaultView.getComputedStyle(element,':after').getPropertyValue('content'),
				elementId: '',
				cssclass: '',
				type: '',
				name: '',
				value: '',
				style: getStyles(element, ':after'),
				_childElementCount : 0,
				_childElementInfo : []
			});
		}
		return elementInfo;
	};
	var elements = [];
	var children = [];
	if (selector.indexOf('/') === 0) {
		children = document.evaluate(selector, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
	} else {
		children = document.querySelectorAll(selector);
	}
	for (var i=0; i<children.length; i++) {
		var elementInfo = getElementInfo(children[i]);
		elements.push(elementInfo);
	}
	return elements;
}

// set test class for element
function _setTestClass(selector) {
	document.querySelector(selector || 'body').classList.add('test');
}

casper.start();

casper.viewport(1024, 768);

if (uname.length > 0 && pval.length > 0) {
	casper.setHttpAuth(uname, pval);
}

selectorList.forEach(function(sel) {
	casper.thenOpen(url, function() {
		this.echo('searching for "' + sel + '"', 'INFO');
		casper.evaluate(_setTestClass);
	})
	.then(function() {
		if (hover !== '') {
			this.echo('hover to "' + hover + '"', 'INFO');
			casper.mouse.move(hover);
		}
	})
	.then(function() {
		results[sel] = this.evaluate(_getStyles, sel, hover); // evaluate in browser
	});
});
casper.then(function() {
	Object.keys(results).forEach(function(selector) {
		var result = results[selector];
		var name = safeFilename(selector);
		if (result && result.length > 0) {
			var html;
			if (selector.indexOf('/') === 0) {
				html = casper.getHTML(x(selector), true);
			} else {
				html = casper.getHTML(selector, true);
			}
			fs.write(subdir + '/' + name + '.html', html);
			casper.echo(subdir + '/' + name + '.html' + ' saved', 'INFO');
			fs.write(subdir + '/' + name + '.json', JSON.stringify(result, undefined, 4), 0);
			casper.echo(subdir + '/' + name + '.json' + ' saved', 'INFO');
			if (selector.indexOf('/') === 0) {
				casper.captureSelector(subdir + '/' + name + '.png', x(selector), { format: 'png' });
			} else {
				casper.captureSelector(subdir + '/' + name + '.png', selector, { format: 'png' });
			}
			casper.echo(subdir + '/' + name + '.png' + ' saved', 'INFO');
		} else {
			casper.echo('element not found: "' + selector + '"', 'ERROR');
		}
	});
	fs.write(subdir + '/page.html', casper.getHTML(), 0);
	casper.echo(subdir + '/page.html' + ' saved', 'INFO');
	casper.capture(subdir + '/page.png', undefined, { format: 'png' });
	casper.echo(subdir + '/page.png' + ' saved', 'INFO');
});
casper.run(function() {
	this.exit();
});

function safeFilename(name) {
	return name.replace(/[ ?#/:\(\)<>|\\]/g, "_").trim();
}
<|endoftext|>


<commit_before>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure and Initial Settings and functions.

var version = '2.1 Beta';
var SCBversion = '3.03'; //Last SandCastle Builder version tested

//Declare and Initialize Variables
var incoming_ONG = 0;
var NinjaAutoClickStatus = 1;
var Time_to_ONG = 1800000;
var lootBoxes = ['boosts', 'ninj', 'cyb', 'hpt', 'bean', 'chron', 'badges', 'badgesav'];

//BB Options Variables
var AudioAlertsStatus = 3;
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume = 1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume = 1;
var BorderAlertStatus = 1;
var description = "Error";
var refreshRate = 1000;
var RKAlertFrequency = 8;
var RKAutoClickStatus = 1;
var RKPlayAudio = 1;

//RK Variables
var start = -1;
var content = "empty";
var LCAutoClickStatus = 0;
var LCSolution = 'blank';
var len = 0;
var Logicat = 0;
var oldRKLocation = -1;
var oldRC = Molpy.redactedClicks - 1;
var oldLC = Molpy.Boosts['Logicat'].power - 1;
var RKLevel = '-1';
var RKLocation = '123';
var RKNew = 1;
var RKNewAudio = 1;
var RKTimer = Molpy.redactedToggle - Molpy.redactedCountup;

function BClick(){
	if (Molpy.Got('Temporal Rift') && Molpy.Boosts['Temporal Rift'].countdown < 5){
		//Do Nothing to avoid temporal rift
	}
	else {
		Molpy.ClickBeach();
	}
}

function Ninja() {
    if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0 && BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
				BClick();
				Molpy.Notify('Ninja Auto Click', 1);
				if (BorderAlertStatus == 1) {
					$("#beach").css("border","2px solid green");
				}
            }
            else if (BorderAlertStatus == 1) {
            $("#beach").css("border","4px solid blue");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
		if (BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid yellow");
		}
        if (incoming_ONG == 0 && (AudioAlertsStatus == 3 || AudioAlertsStatus == 4)) {
			audio_Chime.play();
			incoming_ONG = 1;
        }
    }
    else if (BorderAlertStatus == 1) {
        $("#beach").css("border","2px solid green");
	}
}

function ToggleMenus(wantOpen) {
	for (i=0, len = lootBoxes.length; i < len; i++) {
		if (lootBoxes[i] == wantOpen) {
			if (Molpy.options.showhide[lootBoxes[i]] != 1) {
				showhideToggle(lootBoxes[i]);
			}
		}
		else {
			if (Molpy.options.showhide[lootBoxes[i]]) {
				showhideToggle(lootBoxes[i]);
			}
		}
	}
}

function FindRK() {
	/*
	RV of 1 is Sand Tools
	RV of 2 is Castle Tools
	RV of 3 is Boosts Main Page
	RV of 4 is Boosts Menus, Hill People Tech, etc.
	RV of 5 is Badges Earned
	RV of 6 is Badges Available
	*/

	//Determines RK location
	RKLocation = '123';
	if (Molpy.redactedVisible == 6) {
		RKLocation = 'badgesav';
	}
	else if (Molpy.redactedVisible > 3) {
		RKLocation = Molpy.redactedGr;
	}

	//Opens RK location
	ToggleMenus(RKLocation);

	//Resets old RK variables
	oldRKLocation = Molpy.redactedVisible;
	oldRC = Molpy.redactedClicks;
	oldLC = Molpy.Boosts['Logicat'].power;
}

function ManualRKClick() {

}

function RedundaKitty() {
	//Refresh Timer Variable
	RKTimer = Molpy.redactedToggle - Molpy.redactedCountup;

	//If a RedundaKitty is active
	if (Molpy.redactedVisible > 0) {

		//Checks if RK is new
		if (RKNew == 1 || Molpy.redactedVisible != oldRKLocation || Molpy.redactedClicks > oldRC || Molpy.Boosts['Logicat'].power != oldLC) {
			RKNewAudio = 1;
			RKNew = 0;
			//Finds RK if appropriate BeachBall option enabled
			if (RKAutoClickStatus > 0) {
				FindRK();
			}
		}

		//Determines if it is an RK or LC
		//If RK is visible
		if ($('#redacteditem').length) {
			$('#redacteditem').css("border","2px solid red"); //Highlights RK
			content = $('#redacteditem').html();
			//If RK contains word statement, it is a LC.
			if (content.indexOf("statement") !== -1) {
				Logicat = 1;
			}
			//Otherwise it is an RK
			else {
				Logicat = 0;
				if (content.indexOf("Show") != -1) {
					start = content.indexOf("Show");
					content = content.substring(start+15,start+38);
					content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
					len = content.length;
					RKLevel = content.substring(18,len);
					Molpy.Notify('RedundaKitty Level is: ' + RKLevel, 1);
				}
				else {
					start = content.indexOf("iframe src=");
					content = content.substring(start-39,start-16);
					Molpy.Notify(content, 1);
					content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
					Molpy.Notify(content, 1);
					len = content.length;
					RKLevel = content.substring(18,len);
					Molpy.Notify('YT RedundaKitty Level is: ' + RKLevel, 1);
				}
			}
		}
		//If RK not visible, LC to 99.
		else {
			Logicat = 99;
		}

		//Clicks RK if AutoClick Enabled
		if (RKAutoClickStatus == 2 && Logicat == 0 ) {
			Molpy.ClickRedacted(RKLevel);
			RKNew = 1;
			RKLocation = '123';
			ToggleMenus('123');
		}
		//Solves LC if AutoClick enabled
		else if (Logicat == 1 && LCAutoClickStatus == 1) {
			SolveLogicat();
			RKNew = 1;
			RKLocation = '123';
			ToggleMenus('123');
		}

		//Redundakitty Notifications for Manual Clicking (Title Bar, Audio)
		else {
			document.title = "! kitten !";
			//If RK Audio Alerts Enabled
			if (AudioAlertsStatus == 1 || AudioAlertsStatus == 3) {
				//If proper mNP and hasn't yet played this mNP
				if (Math.floor(RKTimer % RKAlertFrequency) == 0 && RKPlayAudio == 1) {
					audio_Bell.play();
					RKPlayAudio = 0;
				}
				else {
					RKPlayAudio = 1;
				}
			}
		}
	}
	//If no RK active, update title Timer. Reset some variables.
	else {
		document.title = RKTimer;
		oldRKLocation = -1;
		RKNew = 1;
		RKPlayAudio = 0;
	}
}

function SolveLogicat() {
	var i = 65;
	do
		{LCSolution = String.fromCharCode(i);
		i++;}
	while (Molpy.redactedPuzzleTarget != Molpy.redactedSGen.StatementValue(LCSolution));
	Molpy.ClickRedactedPuzzle(LCSolution);
}

function SwitchOption(option) {
	switch (option) {
		case 'RKAutoClick':
			RKAutoClickStatus++;
			if (RKAutoClickStatus > 2) {RKAutoClickStatus = 0;}
			status = RKAutoClickStatus;
			break;
		case 'LCAutoClick':
			LCAutoClickStatus++;
			if (LCAutoClickStatus > 1) {LCAutoClickStatus = 0;}
			status = LCAutoClickStatus;
			break;
		case 'NinjaAutoClick':
			NinjaAutoClickStatus++;
			if (NinjaAutoClickStatus > 1) {NinjaAutoClickStatus = 0;}
			status = NinjaAutoClickStatus;
			break;
		case 'BorderAlert':
			BorderAlertStatus++;
			if (BorderAlertStatus > 1) {
				BorderAlertStatus = 0;
				$("#beach").css("border","1px solid white");
			}
			status = BorderAlertStatus;
			break;
		case 'AudioAlerts':
			AudioAlertsStatus++;
			if (AudioAlertsStatus > 3) {AudioAlertsStatus = 0;}
			status = AudioAlertsStatus;
			break;
		case 'RefreshRate':
			newRate = parseInt(prompt('Please enter your desired BeachBall refresh rate in milliseconds (500 - 3600):', refreshRate));
			if (newRate < 500 || newRate > 3600 || isNaN(newRate)){
				Molpy.Notify('Invalid Refresh Rate', 1);
			}
			else {
				refreshRate = newRate;
			}
			break;
	}
	DisplayDescription(option, status);
}

function DisplayDescription(option, status) {
	error = 0;
	if (option == 'LCAutoClick' || option == 'NinjaAutoClick' || option == 'BorderAlert') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'On';}
		else {Molpy.Notify('Display Description Error', 1);}
	}
	else if (option == 'AudioAlerts') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'RK Only'; RKPlayAudio = 1;}
		else if (status == 2) {description = 'ONG Only';}
		else if (status == 3) {description = 'RK and ONG'; RKPlayAudio = 1;}
		else {Molpy.Notify('Display Description Error - Audio Alerts: ' + status, 1);}
	}
	else if (option == 'RKAutoClick') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'Find RK Only'; RKNew = 1;}
		else if (status == 2) {description = 'On'; RKNew = 1;}
		else {Molpy.Notify('Display Description Error - RKAutoClick: ' + status, 1);}
	}
	else if (option == 'RefreshRate') {
		description = refreshRate;
	}
	else {
		Molpy.Notify(option + ' is not a valid option.', 1);
		error = 1;
	}

	if (error == 0) {g(option + 'Desc').innerHTML = '
' + description;}
}

//Beach Ball Startup
//Set Settings
if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

//Create Menu
$('#optionsItems').append('

BeachBall Settings

v ' + version + '

');
$('#optionsItems').append('

Redundakitty Auto Click

');
$('#optionsItems').append('

Logicat Auto Click

');
$('#optionsItems').append('

Ninja Auto Click

');
$('#optionsItems').append('

Ninja Visual Alert

');
$('#optionsItems').append('

Audio Alerts

');
$('#optionsItems').append('

Refresh Rate

');
$('#optionsItems').append('

Spawn RK

');
$('#optionsItems').append('

Extend RK

');
DisplayDescription('RKAutoClick', RKAutoClickStatus);
DisplayDescription('LCAutoClick', LCAutoClickStatus);
DisplayDescription('NinjaAutoClick', NinjaAutoClickStatus);
DisplayDescription('BorderAlert', BorderAlertStatus);
DisplayDescription('AudioAlerts', AudioAlertsStatus);
DisplayDescription('RefreshRate', refreshRate);

Molpy.Notify('BeachBall version ' + version + ' loaded for SandCastle Builder version ' + SCBversion, 1);
Loop();

function MainProgram() {
	Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
	RedundaKitty();
	Ninja();
	Loop();
}

function Loop() {
	setTimeout(MainProgram, refreshRate);
}

function SpawnRK() {
	Molpy.redactedCountup = Molpy.redactedToggle - 1;
}

function ExtendRK() {
	Molpy.redactedCountup = 1;
}

<commit_msg>RKAutoClick fixed for YT and Multi-Level RKs. Final Testing<commit_after>//v1.0 as Starting Point
//v2.0 Cleaning up code. Setting up Menu Structure and Initial Settings and functions.

var version = '2.1 Beta';
var SCBversion = '3.03'; //Last SandCastle Builder version tested

//Declare and Initialize Variables
var incoming_ONG = 0;
var NinjaAutoClickStatus = 1;
var Time_to_ONG = 1800000;
var lootBoxes = ['boosts', 'ninj', 'cyb', 'hpt', 'bean', 'chron', 'badges', 'badgesav'];

//BB Options Variables
var AudioAlertsStatus = 3;
var audio_Bell = new Audio("http://xenko.comxa.com/Ship_Bell.mp3");
	audio_Bell.volume = 1;
var audio_Chime = new Audio("http://xenko.comxa.com/Chime.mp3");
	audio_Chime.volume = 1;
var BorderAlertStatus = 1;
var description = "Error";
var refreshRate = 1000;
var RKAlertFrequency = 8;
var RKAutoClickStatus = 1;
var RKPlayAudio = 1;

//RK Variables
var start = -1;
var content = "empty";
var LCAutoClickStatus = 0;
var LCSolution = 'blank';
var len = 0;
var Logicat = 0;
var oldRKLocation = -1;
var oldRC = Molpy.redactedClicks - 1;
var oldLC = Molpy.Boosts['Logicat'].power - 1;
var RKLevel = '-1';
var RKLocation = '123';
var RKNew = 1;
var RKNewAudio = 1;
var RKTimer = Molpy.redactedToggle - Molpy.redactedCountup;

function BClick(){
	if (Molpy.Got('Temporal Rift') && Molpy.Boosts['Temporal Rift'].countdown < 5){
		//Do Nothing to avoid temporal rift
	}
	else {
		Molpy.ClickBeach();
	}
}

function Ninja() {
    if (Molpy.ninjad == 0) {
        if (Molpy.npbONG == 0 && BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid red");
        }
        else {
            incoming_ONG = 0;
            if (NinjaAutoClickStatus == 1) {
				BClick();
				Molpy.Notify('Ninja Auto Click', 1);
				if (BorderAlertStatus == 1) {
					$("#beach").css("border","2px solid green");
				}
            }
            else if (BorderAlertStatus == 1) {
            $("#beach").css("border","4px solid blue");
            }

        }
	}
    else if (Time_to_ONG <= 15000) {
		if (BorderAlertStatus == 1) {
			$("#beach").css("border","4px solid yellow");
		}
        if (incoming_ONG == 0 && (AudioAlertsStatus == 3 || AudioAlertsStatus == 4)) {
			audio_Chime.play();
			incoming_ONG = 1;
        }
    }
    else if (BorderAlertStatus == 1) {
        $("#beach").css("border","2px solid green");
	}
}

function ToggleMenus(wantOpen) {
	for (i=0, len = lootBoxes.length; i < len; i++) {
		if (lootBoxes[i] == wantOpen) {
			if (Molpy.options.showhide[lootBoxes[i]] != 1) {
				showhideToggle(lootBoxes[i]);
			}
		}
		else {
			if (Molpy.options.showhide[lootBoxes[i]]) {
				showhideToggle(lootBoxes[i]);
			}
		}
	}
}

function FindRK() {
	/*
	RV of 1 is Sand Tools
	RV of 2 is Castle Tools
	RV of 3 is Boosts Main Page
	RV of 4 is Boosts Menus, Hill People Tech, etc.
	RV of 5 is Badges Earned
	RV of 6 is Badges Available
	*/

	//Determines RK location
	RKLocation = '123';
	if (Molpy.redactedVisible == 6) {
		RKLocation = 'badgesav';
	}
	else if (Molpy.redactedVisible > 3) {
		RKLocation = Molpy.redactedGr;
	}

	//Opens RK location
	ToggleMenus(RKLocation);

	//Resets old RK variables
	oldRKLocation = Molpy.redactedVisible;
	oldRC = Molpy.redactedClicks;
	oldLC = Molpy.Boosts['Logicat'].power;
}

function ManualRKClick() {

}

function RedundaKitty() {
	//Refresh Timer Variable
	RKTimer = Molpy.redactedToggle - Molpy.redactedCountup;

	//If a RedundaKitty is active
	if (Molpy.redactedVisible > 0) {

		//Checks if RK is new
		if (RKNew == 1 || Molpy.redactedVisible != oldRKLocation || Molpy.redactedClicks > oldRC || Molpy.Boosts['Logicat'].power != oldLC) {
			RKNewAudio = 1;
			RKNew = 0;
			//Finds RK if appropriate BeachBall option enabled
			if (RKAutoClickStatus > 0) {
				FindRK();
			}
		}

		//Determines if it is an RK or LC
		//If RK is visible
		if ($('#redacteditem').length) {
			$('#redacteditem').css("border","2px solid red"); //Highlights RK
			content = $('#redacteditem').html();
			//If RK contains word statement, it is a LC.
			if (content.indexOf("statement") !== -1) {
				Logicat = 1;
			}
			//Otherwise it is an RK
			else {
				Logicat = 0;
				if (content.indexOf("Show") != -1) {
					start = content.indexOf("Show");
					content = content.substring(start+15,start+38);
					content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
					len = content.length;
					RKLevel = content.substring(18,len);
					Molpy.Notify('RedundaKitty Level is: ' + RKLevel, 1);
				}
				else {
					start = content.indexOf("iframe src=");
					content = content.substring(start-39,start-16);
					Molpy.Notify(content, 1);
					content = content.replace(/[&\/\\#,+()$~%.'":*?<>{}]/g,'');
					Molpy.Notify(content, 1);
					len = content.length;
					RKLevel = content.substring(18,len);
					Molpy.Notify('YT RedundaKitty Level is: ' + RKLevel, 1);
				}
			}
		}
		//If RK not visible, LC to 99.
		else {
			Logicat = 99;
		}

		//Clicks RK if AutoClick Enabled
		if (RKAutoClickStatus == 2 && Logicat == 0 ) {
			Molpy.ClickRedacted(RKLevel);
			RKNew = 1;
			RKLocation = '123';
			ToggleMenus('123');
		}
		//Solves LC if AutoClick enabled
		else if (Logicat == 1 && LCAutoClickStatus == 1) {
			SolveLogicat();
			RKNew = 1;
			RKLocation = '123';
			ToggleMenus('123');
		}

		//Redundakitty Notifications for Manual Clicking (Title Bar, Audio)
		else {
			document.title = "! kitten !";
			//If RK Audio Alerts Enabled
			if (AudioAlertsStatus == 1 || AudioAlertsStatus == 3) {
				//If proper mNP and hasn't yet played this mNP
				if (Math.floor(RKTimer % RKAlertFrequency) == 0 && RKPlayAudio == 1) {
					audio_Bell.play();
					RKPlayAudio = 0;
				}
				else {
					RKPlayAudio = 1;
				}
			}
		}
	}
	//If no RK active, update title Timer. Reset some variables.
	else {
		SpawnRK();
		document.title = RKTimer;
		oldRKLocation = -1;
		RKNew = 1;
		RKPlayAudio = 0;
	}
}

function SolveLogicat() {
	var i = 65;
	do
		{LCSolution = String.fromCharCode(i);
		i++;}
	while (Molpy.redactedPuzzleTarget != Molpy.redactedSGen.StatementValue(LCSolution));
	Molpy.ClickRedactedPuzzle(LCSolution);
}

function SwitchOption(option) {
	switch (option) {
		case 'RKAutoClick':
			RKAutoClickStatus++;
			if (RKAutoClickStatus > 2) {RKAutoClickStatus = 0;}
			status = RKAutoClickStatus;
			break;
		case 'LCAutoClick':
			LCAutoClickStatus++;
			if (LCAutoClickStatus > 1) {LCAutoClickStatus = 0;}
			status = LCAutoClickStatus;
			break;
		case 'NinjaAutoClick':
			NinjaAutoClickStatus++;
			if (NinjaAutoClickStatus > 1) {NinjaAutoClickStatus = 0;}
			status = NinjaAutoClickStatus;
			break;
		case 'BorderAlert':
			BorderAlertStatus++;
			if (BorderAlertStatus > 1) {
				BorderAlertStatus = 0;
				$("#beach").css("border","1px solid white");
			}
			status = BorderAlertStatus;
			break;
		case 'AudioAlerts':
			AudioAlertsStatus++;
			if (AudioAlertsStatus > 3) {AudioAlertsStatus = 0;}
			status = AudioAlertsStatus;
			break;
		case 'RefreshRate':
			newRate = parseInt(prompt('Please enter your desired BeachBall refresh rate in milliseconds (500 - 3600):', refreshRate));
			if (newRate < 500 || newRate > 3600 || isNaN(newRate)){
				Molpy.Notify('Invalid Refresh Rate', 1);
			}
			else {
				refreshRate = newRate;
			}
			break;
	}
	DisplayDescription(option, status);
}

function DisplayDescription(option, status) {
	error = 0;
	if (option == 'LCAutoClick' || option == 'NinjaAutoClick' || option == 'BorderAlert') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'On';}
		else {Molpy.Notify('Display Description Error', 1);}
	}
	else if (option == 'AudioAlerts') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'RK Only'; RKPlayAudio = 1;}
		else if (status == 2) {description = 'ONG Only';}
		else if (status == 3) {description = 'RK and ONG'; RKPlayAudio = 1;}
		else {Molpy.Notify('Display Description Error - Audio Alerts: ' + status, 1);}
	}
	else if (option == 'RKAutoClick') {
		if (status == 0) {description = 'Off';}
		else if (status == 1) {description = 'Find RK Only'; RKNew = 1;}
		else if (status == 2) {description = 'On'; RKNew = 1;}
		else {Molpy.Notify('Display Description Error - RKAutoClick: ' + status, 1);}
	}
	else if (option == 'RefreshRate') {
		description = refreshRate;
	}
	else {
		Molpy.Notify(option + ' is not a valid option.', 1);
		error = 1;
	}

	if (error == 0) {g(option + 'Desc').innerHTML = '
' + description;}
}

//Beach Ball Startup
//Set Settings
if (Molpy.Got('Kitnip') == 1){RKAlertFrequency = 10;}

//Create Menu
$('#optionsItems').append('

BeachBall Settings

v ' + version + '

');
$('#optionsItems').append('

Redundakitty Auto Click

');
$('#optionsItems').append('

Logicat Auto Click

');
$('#optionsItems').append('

Ninja Auto Click

');
$('#optionsItems').append('

Ninja Visual Alert

');
$('#optionsItems').append('

Audio Alerts

');
$('#optionsItems').append('

Refresh Rate

');
$('#optionsItems').append('

Spawn RK

');
$('#optionsItems').append('

Extend RK

');
DisplayDescription('RKAutoClick', RKAutoClickStatus);
DisplayDescription('LCAutoClick', LCAutoClickStatus);
DisplayDescription('NinjaAutoClick', NinjaAutoClickStatus);
DisplayDescription('BorderAlert', BorderAlertStatus);
DisplayDescription('AudioAlerts', AudioAlertsStatus);
DisplayDescription('RefreshRate', refreshRate);

Molpy.Notify('BeachBall version ' + version + ' loaded for SandCastle Builder version ' + SCBversion, 1);
Loop();

function MainProgram() {
	Time_to_ONG = (Molpy.NPlength * 1000) - Molpy.ONGelapsed;
	RedundaKitty();
	Ninja();
	Loop();
}

function Loop() {
	setTimeout(MainProgram, refreshRate);
}

function SpawnRK() {
	Molpy.redactedCountup = Molpy.redactedToggle - 1;
}

function ExtendRK() {
	Molpy.redactedCountup = 1;
}

<|endoftext|>


<commit_before>require('source-map-support').install()
const isAbsolute = require('./_isAbsolute')
const path = require('path')
const cp = require('child_process')

let testFile = process.argv[2]
if (!isAbsolute(testFile)) {
  testFile = path.join(process.cwd(), testFile)
}
let tapSpecFile = require.resolve('../dist/tap-spec.js')

let testRunner = cp.fork(testFile, {
  stdio: ['ignore', 'pipe', 2, 'ipc']
})
let tapeSepc = cp.fork(tapSpecFile, {
  stdio: ['pipe', 1, 2, 'ipc']
})

testRunner.stdout.pipe(tapeSepc.stdin)
<commit_msg>Let test script exit with error code.<commit_after>require('source-map-support').install()
const isAbsolute = require('./_isAbsolute')
const path = require('path')
const cp = require('child_process')

let testFile = process.argv[2]
if (!isAbsolute(testFile)) {
  testFile = path.join(process.cwd(), testFile)
}
let tapSpecFile = require.resolve('../dist/tap-spec.js')

let testRunner = cp.fork(testFile, {
  stdio: ['ignore', 'pipe', 2, 'ipc']
})
let tapeSepc = cp.fork(tapSpecFile, {
  stdio: ['pipe', 1, 2, 'ipc']
})

testRunner.on('error', function(error) {
  console.error(error)
  process.exit(1)
})

testRunner.on('close', function(exitCode) {
  process.exit(exitCode)
})

testRunner.stdout.pipe(tapeSepc.stdin)
<|endoftext|>


<commit_before>#! /usr/bin/env node

const shell = require("shelljs");

shell.echo("running eslint");
shell.exec("eslint --color index.js src/* test/*.js");

shell.echo("\nrunning tests");
shell.exec("browserify -t [ babelify --presets [ es2015 ] ] test/*.js | tape-run --render='faucet'");
// shell.exec("babel-node ./node_modules/istanbul/lib/cli.js cover test/*.js -v");
// shell.exec("browserify test/*.js -t bubleify -o build/test.js && cat build/test.js | tape-run");

// shell.exec("browserify test/*.js -d -t [ babelify --presets [ es2015 ] ] -p tape-istanbul/plugin | tape-run | tape-istanbul");
// shell.exec("istanbul help report");
// shell.exec("istanbul report text");
// shell.exec("browserify test/*.js -d -t [ babelify --sourceMapsAbsolute --presets [ es2015 ] ] -t browserify-istanbul | tape-run");
// shell.exec("browserify -t [ bubleify --transforms module ] test/*.js | tape-run");
// shell.exec("cat build/test-old.js | tape-run");
// shell.exec("rollup test/*.js | tape-run");

// const browserify = require("browserify");
// // const tapeIstanbul = require("tape-istanbul");
// const tapeRun = require("tape-run");
// const glob = require("glob");
//
// browserify({debug: true})
//   .add(glob("test/*.js"))
//   .transform("bubleify", {
//     sourceMap: false,
//     transforms: {
//       modules: true
//     }
//   })
//   // .plugin("tape-istanbul/plugin")
//   .bundle()
//   // .pipe(tapeRun());
//   // .pipe(tapeIstanbul());

// const buble = require("rollup-plugin-buble"),
//       commonjs = require("rollup-plugin-commonjs"),
//       deps = require("rollup-plugin-node-resolve"),
//       inject = require("rollup-plugin-inject"),
//       rollup = require("rollup");
//
// const entry = {
//   entry: "test/accessor.js",
//   plugins: [
//     // commonjs({ignoreGlobal: true, exclude: ["node_modules/buble/**"]}),
//     commonjs(),
//     deps({preferBuiltins: true, jsnext: true, main: true, browser: true}),
//     inject({require: "require"}),
//     buble()
//   ],
//   // onwarn: () => {},
//   onwarn: err => console.log(err),
//   sourceMap: true
// };
//
// const config = {
//   dest: "build/test.js",
//   format: "cjs",
//   moduleName: "test",
//   sourceMap: "inline"
// };
//
// shell.mkdir("-p", "build");
// rollup.rollup(entry).catch(err => console.log(err)).then(bundle => {
//   bundle.write(config).then(() => {
//     // shell.exec("browserify build/test.js -o build/test.browserify.js");
//     // shell.exec("browserify build/test.js -p tape-istanbul/plugin -o build/test.instanbul.js");
//     // shell.exec("cat build/test.browserify.js | tape-run");
//     // shell.exec("cat build/test.js | tape-run");
//     // shell.exec("browserify build/test.js -p tape-istanbul/plugin | tape-run | tape-istanbul");
//     // shell.exec("istanbul ---only report");
//   });
// });
<commit_msg>removes unused comments from bin/test<commit_after>#! /usr/bin/env node

const shell = require("shelljs");

shell.echo("running eslint");
shell.exec("eslint --color index.js src/* test/*.js");

shell.echo("\nrunning tests");
shell.exec("browserify -t [ babelify --presets [ es2015 ] ] test/*.js | tape-run --render='faucet'");
<|endoftext|>


<commit_before>/**
 * Binds a CodeMirror widget to a <textarea> element.
 */
angular.module('ui.codemirror', [])
  .constant('uiCodemirrorConfig', {})
  .directive('uiCodemirror', ['uiCodemirrorConfig', function (uiCodemirrorConfig) {
    'use strict';

    return {
      restrict: 'EA',
      require: '?ngModel',
      compile: function compile(tElement, tAttrs, transclude) {

        // Require CodeMirror
        if (angular.isUndefined(window.CodeMirror)) {
          throw new Error('ui-codemirror need CodeMirror to work... (o rly?)');
        }

        // Create a codemirror instance with
        // - the function that will to place the editor into the document.
        // - the initial content of the editor.
        //   see http://codemirror.net/doc/manual.html#api_constructor
        var value = tElement.text();
        var codeMirror = new CodeMirror(function (cm_el) {

          angular.forEach(tElement.prop("attributes"), function (a) {
            if (a.name === "ui-codemirror") {
              cm_el.setAttribute("ui-codemirror-opts", a.textContent);
            } else {
              cm_el.setAttribute(a.name, a.textContent);
            }
          });

          // FIX replaceWith throw not parent Error !
          if (tElement.parent().length <= 0) {
            tElement.wrap("<div>");
          }

          tElement.replaceWith(cm_el);
        }, {value: value});

        return  function postLink(scope, iElement, iAttrs, ngModel) {

          var options, opts, onChange;

          options = uiCodemirrorConfig.codemirror || {};
          opts = angular.extend({}, options, scope.$eval(iAttrs.uiCodemirror), scope.$eval(iAttrs.uiCodemirrorOpts));

          function updateOptions(newValues) {
            for (var key in newValues) {
              if (newValues.hasOwnProperty(key)) {
                codeMirror.setOption(key, newValues[key]);
              }
            }
          }

          updateOptions(opts);

          if (angular.isDefined(scope[iAttrs.uiCodemirror])) {
            scope.$watch(iAttrs.uiCodemirror, updateOptions, true);
          }
          // Specialize change event
          codeMirror.on("change", function (instance, changeObj) {
            var newValue = instance.getValue();
            if (ngModel && newValue !== ngModel.$viewValue) {
              ngModel.$setViewValue(newValue);
            }
            if (!scope.$$phase) {
              scope.$apply();
            }
          });

          if (ngModel) {
            // CodeMirror expects a string, so make sure it gets one.
            // This does not change the model.
            ngModel.$formatters.push(function (value) {
              if (angular.isUndefined(value) || value === null) {
                return '';
              }
              else if (angular.isObject(value) || angular.isArray(value)) {
                throw new Error('ui-codemirror cannot use an object or an array as a model');
              }
              return value;
            });

            // Override the ngModelController $render method, which is what gets called when the model is updated.
            // This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else.
            ngModel.$render = function () {
              //Code mirror expects a string so make sure it gets one
              //Although the formatter have already done this, it can be possible that another formatter returns undefined (for example the required directive)
              var safeViewValue = ngModel.$viewValue || '';
              codeMirror.setValue(safeViewValue);
            };
          }

          // Watch ui-refresh and refresh the directive
          if (iAttrs.uiRefresh) {
            scope.$watch(iAttrs.uiRefresh, function (newVal, oldVal) {
              // Skip the initial watch firing
              if (newVal !== oldVal) {
                  codeMirror.refresh();
              }
            });
          }

          // onLoad callback
          if (angular.isFunction(opts.onLoad)) {
            opts.onLoad(codeMirror);
          }
        };
      }
    };
  }]);
<commit_msg>bug fix: cannot watch a dotted property<commit_after>/**
 * Binds a CodeMirror widget to a <textarea> element.
 */
angular.module('ui.codemirror', [])
  .constant('uiCodemirrorConfig', {})
  .directive('uiCodemirror', ['uiCodemirrorConfig', function (uiCodemirrorConfig) {
    'use strict';

    return {
      restrict: 'EA',
      require: '?ngModel',
      compile: function compile(tElement, tAttrs, transclude) {

        // Require CodeMirror
        if (angular.isUndefined(window.CodeMirror)) {
          throw new Error('ui-codemirror need CodeMirror to work... (o rly?)');
        }

        // Create a codemirror instance with
        // - the function that will to place the editor into the document.
        // - the initial content of the editor.
        //   see http://codemirror.net/doc/manual.html#api_constructor
        var value = tElement.text();
        var codeMirror = new CodeMirror(function (cm_el) {

          angular.forEach(tElement.prop("attributes"), function (a) {
            if (a.name === "ui-codemirror") {
              cm_el.setAttribute("ui-codemirror-opts", a.textContent);
            } else {
              cm_el.setAttribute(a.name, a.textContent);
            }
          });

          // FIX replaceWith throw not parent Error !
          if (tElement.parent().length <= 0) {
            tElement.wrap("<div>");
          }

          tElement.replaceWith(cm_el);
        }, {value: value});

        return  function postLink(scope, iElement, iAttrs, ngModel) {

          var options, opts, onChange;

          options = uiCodemirrorConfig.codemirror || {};
          opts = angular.extend({}, options, scope.$eval(iAttrs.uiCodemirror), scope.$eval(iAttrs.uiCodemirrorOpts));

          function updateOptions(newValues) {
            for (var key in newValues) {
              if (newValues.hasOwnProperty(key)) {
                codeMirror.setOption(key, newValues[key]);
              }
            }
          }

          updateOptions(opts);

          if (angular.isDefined(scope.$eval(iAttrs.uiCodemirror))) {
            scope.$watch(iAttrs.uiCodemirror, updateOptions, true);
          }
          // Specialize change event
          codeMirror.on("change", function (instance, changeObj) {
            var newValue = instance.getValue();
            if (ngModel && newValue !== ngModel.$viewValue) {
              ngModel.$setViewValue(newValue);
            }
            if (!scope.$$phase) {
              scope.$apply();
            }
          });

          if (ngModel) {
            // CodeMirror expects a string, so make sure it gets one.
            // This does not change the model.
            ngModel.$formatters.push(function (value) {
              if (angular.isUndefined(value) || value === null) {
                return '';
              }
              else if (angular.isObject(value) || angular.isArray(value)) {
                throw new Error('ui-codemirror cannot use an object or an array as a model');
              }
              return value;
            });

            // Override the ngModelController $render method, which is what gets called when the model is updated.
            // This takes care of the synchronizing the codeMirror element with the underlying model, in the case that it is changed by something else.
            ngModel.$render = function () {
              //Code mirror expects a string so make sure it gets one
              //Although the formatter have already done this, it can be possible that another formatter returns undefined (for example the required directive)
              var safeViewValue = ngModel.$viewValue || '';
              codeMirror.setValue(safeViewValue);
            };
          }

          // Watch ui-refresh and refresh the directive
          if (iAttrs.uiRefresh) {
            scope.$watch(iAttrs.uiRefresh, function (newVal, oldVal) {
              // Skip the initial watch firing
              if (newVal !== oldVal) {
                  codeMirror.refresh();
              }
            });
          }

          // onLoad callback
          if (angular.isFunction(opts.onLoad)) {
            opts.onLoad(codeMirror);
          }
        };
      }
    };
  }]);
<|endoftext|>


<commit_before>#!/usr/bin/env node

var path = require('path');
var fs   = require('fs');
var ytdl = require('..');
require('colors');

var info = JSON.parse(
    fs.readFileSync(path.resolve(__dirname, '..', 'package.json')));

var opts = require('nomnom')
  .option('version', {
    abbr: 'v'
  , flag: true
  , callback: function() {
      console.log('v' + info.version);
      process.exit();
    }
  , help: 'Print program version.'
  })
  .option('url', {
    position: 0
  , required: true
  , help: 'URL to the video.'
  })
  .option('quality', {
    abbr: 'q'
  , metavar: 'ITAG'
  , help: 'Video quality to download. Default: `highest`'
  })
  .option('start', {
    abbr: 's'
  , metavar: 'TIME'
  , help: 'Where to begin the video. ie 1m3s, 45s, 2300.'
  })
  .option('output', {
    abbr: 'o'
  , metavar: 'FILE'
  , help: 'Where to save the file. Default: stdout'
  })
  .option('filterContainer', {
    full: 'filter-container'
  , metavar: 'REGEXP'
  , help: 'Filter in format container.'
  })
  .option('unfilterContainer', {
    full: 'unfilter-container'
  , metavar: 'REGEXP'
  , help: 'Filter out format container.'
  })
  .option('filterResolution', {
    full: 'filter-resolution'
  , metavar: 'REGEXP'
  , help: 'Filter in format resolution.'
  })
  .option('unfilterResolution', {
    full: 'unfilter-resolution'
  , metavar: 'REGEXP'
  , help: 'Filter out format resolution.'
  })
  .option('filterEncoding', {
    full: 'filter-encoding'
  , metavar: 'REGEXP'
  , help: 'Filter in format encoding.'
  })
  .option('unfilterEncoding', {
    full: 'unfilter-encoding'
  , metavar: 'REGEXP'
  , help: 'Filter out format encoding.'
  })
  .script('ytdl')
  .colors()
  .parse()
  ;

var output = opts.output;
var writeStream = output ? fs.createWriteStream(output) : process.stdout;

var ytdlOptions = {};
ytdlOptions.quality = opts.quality;
ytdlOptions.begin = opts.begin;

// Create filters.
var filters = [];

/**
 * @param {String} field
 * @param {String} regexpStr
 * @param {Boolean|null} negated
 */
function createFilter(field, regexpStr, negated) {
  try {
    var regexp = new RegExp(regexpStr, 'i');
  } catch (err) {
    console.error(err.message);
    process.exit(1);
  }

  filters.push(function(format) {
    return negated !== regexp.test(format[field]);
  });
}

['container', 'resolution', 'encoding'].forEach(function(field) {
  var key = 'filter' + field[0].toUpperCase() + field.slice(1);
  if (opts[key]) {
    createFilter(field, opts[key], false);
  }

  key = 'un' + key;
  if (opts[key]) {
    createFilter(field, opts[key], true);
  }
});

ytdlOptions.filter = function(format) {
  return filters.every(function(filter) {
    return filter(format);
  });
};

var readStream = ytdl(opts.url, ytdlOptions);
readStream.pipe(writeStream);

// Converst bytes to human readable unit.
// Thank you Amir from StackOverflow.
var units = ' KMGTPEZYXWVU';
function toHumanSize(bytes) {
  if (bytes <= 0) { return 0; }
  var t2 = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), 12);
  return (Math.round(bytes * 100 / Math.pow(1024, t2)) / 100) +
          units.charAt(t2).replace(' ', '') + 'B';
}

// Print progress bar and some video info if not streaming to stdout.
if (output) {
  readStream.on('info', function(info, format) {

    console.log();
    console.log('title: '.bold + info.title);
    console.log('author: '.bold + info.author);
    console.log('container: '.bold + format.container);
    console.log('resolution: '.bold + format.resolution);
    console.log('encoding: '.bold + format.encoding);
    console.log('size: '.bold + toHumanSize(format.size));
    console.log('output: '.bold + output);
    console.log();

    // Create progress bar.
    var bar = require('progress-bar').create(process.stdout, 50);
    bar.format = '$bar; $percentage;%';

    // Keep track of progress.
    var dataRead = 0;
    readStream.on('data', function(data) {
      dataRead += data.length;
      var percent = dataRead / format.size;
      bar.update(percent);
    });

  });
}

readStream.on('end', function() {
  console.log();
  process.exit();
});

process.on('SIGINT', function() {
  console.log();
  process.exit();
});
<commit_msg>exit method<commit_after>#!/usr/bin/env node

var path = require('path');
var fs   = require('fs');
var ytdl = require('..');
require('colors');

var info = JSON.parse(
    fs.readFileSync(path.resolve(__dirname, '..', 'package.json')));

var opts = require('nomnom')
  .option('version', {
    abbr: 'v'
  , flag: true
  , callback: function() {
      console.log('v' + info.version);
      process.exit();
    }
  , help: 'Print program version.'
  })
  .option('url', {
    position: 0
  , required: true
  , help: 'URL to the video.'
  })
  .option('quality', {
    abbr: 'q'
  , metavar: 'ITAG'
  , help: 'Video quality to download. Default: `highest`'
  })
  .option('start', {
    abbr: 's'
  , metavar: 'TIME'
  , help: 'Where to begin the video. ie 1m3s, 45s, 2300.'
  })
  .option('output', {
    abbr: 'o'
  , metavar: 'FILE'
  , help: 'Where to save the file. Default: stdout'
  })
  .option('filterContainer', {
    full: 'filter-container'
  , metavar: 'REGEXP'
  , help: 'Filter in format container.'
  })
  .option('unfilterContainer', {
    full: 'unfilter-container'
  , metavar: 'REGEXP'
  , help: 'Filter out format container.'
  })
  .option('filterResolution', {
    full: 'filter-resolution'
  , metavar: 'REGEXP'
  , help: 'Filter in format resolution.'
  })
  .option('unfilterResolution', {
    full: 'unfilter-resolution'
  , metavar: 'REGEXP'
  , help: 'Filter out format resolution.'
  })
  .option('filterEncoding', {
    full: 'filter-encoding'
  , metavar: 'REGEXP'
  , help: 'Filter in format encoding.'
  })
  .option('unfilterEncoding', {
    full: 'unfilter-encoding'
  , metavar: 'REGEXP'
  , help: 'Filter out format encoding.'
  })
  .script('ytdl')
  .colors()
  .parse()
  ;

var output = opts.output;
var writeStream = output ? fs.createWriteStream(output) : process.stdout;

var ytdlOptions = {};
ytdlOptions.quality = opts.quality;
ytdlOptions.begin = opts.begin;

// Create filters.
var filters = [];

/**
 * @param {String} field
 * @param {String} regexpStr
 * @param {Boolean|null} negated
 */
function createFilter(field, regexpStr, negated) {
  try {
    var regexp = new RegExp(regexpStr, 'i');
  } catch (err) {
    console.error(err.message);
    process.exit(1);
  }

  filters.push(function(format) {
    return negated !== regexp.test(format[field]);
  });
}

['container', 'resolution', 'encoding'].forEach(function(field) {
  var key = 'filter' + field[0].toUpperCase() + field.slice(1);
  if (opts[key]) {
    createFilter(field, opts[key], false);
  }

  key = 'un' + key;
  if (opts[key]) {
    createFilter(field, opts[key], true);
  }
});

ytdlOptions.filter = function(format) {
  return filters.every(function(filter) {
    return filter(format);
  });
};

var readStream = ytdl(opts.url, ytdlOptions);
readStream.pipe(writeStream);

// Converst bytes to human readable unit.
// Thank you Amir from StackOverflow.
var units = ' KMGTPEZYXWVU';
function toHumanSize(bytes) {
  if (bytes <= 0) { return 0; }
  var t2 = Math.min(Math.floor(Math.log(bytes) / Math.log(1024)), 12);
  return (Math.round(bytes * 100 / Math.pow(1024, t2)) / 100) +
          units.charAt(t2).replace(' ', '') + 'B';
}

// Print progress bar and some video info if not streaming to stdout.
if (output) {
  readStream.on('info', function(info, format) {

    console.log();
    console.log('title: '.bold + info.title);
    console.log('author: '.bold + info.author);
    console.log('container: '.bold + format.container);
    console.log('resolution: '.bold + format.resolution);
    console.log('encoding: '.bold + format.encoding);
    console.log('size: '.bold + toHumanSize(format.size));
    console.log('output: '.bold + output);
    console.log();

    // Create progress bar.
    var bar = require('progress-bar').create(process.stdout, 50);
    bar.format = '$bar; $percentage;%';

    // Keep track of progress.
    var dataRead = 0;
    readStream.on('data', function(data) {
      dataRead += data.length;
      var percent = dataRead / format.size;
      bar.update(percent);
    });

  });
}

function exit() {
  console.log();
  process.exit();
}

readStream.on('end', exit);
process.on('SIGINT', exit);
<|endoftext|>


<commit_before>import {get} from 'lodash/object'
import {partialRight} from 'lodash/function'
import log from 'npmlog'
import fs from 'fs'
import errorBuffer from 'contentful-batch-libs/utils/error-buffer'

export default function dumpErrorBuffer (params, message = 'Additional errors were found') {
  const {destinationSpace, sourceSpace, errorLogFile} = params
  const loggedErrors = errorBuffer.drain()
  if (loggedErrors.length > 0) {
    const errors = loggedErrors.reduce(partialRight(logErrorsWithAppLinks, sourceSpace, destinationSpace), '')
    fs.writeFileSync(errorLogFile, errors)
    log.warn(message)
    log.warn(`Check ${errorLogFile} for details.`)
  }
}

function logErrorsWithAppLinks (accumulatedErrorStr, err, idx, loggedErrors, sourceSpace, destinationSpace) {
  const requestUri = get(err, 'request.uri')
  if (requestUri) {
    return accumulatedErrorStr +
      `Error ${idx + 1}:\n` +
      'in ' + parseEntityUrl(sourceSpace, destinationSpace, requestUri) + '\n' +
      JSON.stringify(err, null, '  ') +
      '\n\n'
  } else {
    return accumulatedErrorStr +
      `Error ${idx + 1}:\n` +
      JSON.stringify(err, null, '  ') +
      '\n\n'
  }
}

function parseEntityUrl (sourceSpace, destinationSpace, url) {
  return url.replace(/api.contentful/, 'app.contentful')
            .replace(/:443/, '')
            .replace(destinationSpace, sourceSpace)
            .split('/').splice(0, 7).join('/')
}
<commit_msg>fix(buildfix): Trigger a release after semantic-release setup changes<commit_after>import {get} from 'lodash/object'
import {partialRight} from 'lodash/function'
import log from 'npmlog'
import fs from 'fs'
import errorBuffer from 'contentful-batch-libs/utils/error-buffer'

export default function dumpErrorBuffer (params, message = 'Additional errors were found') {
  const {destinationSpace, sourceSpace, errorLogFile} = params
  const loggedErrors = errorBuffer.drain()
  if (loggedErrors.length > 0) {
    const errors = loggedErrors.reduce(partialRight(logErrorsWithAppLinks, sourceSpace, destinationSpace), '')
    fs.writeFileSync(errorLogFile, errors)
    log.warn(message)
    log.warn(`Check ${errorLogFile} for details.`)
  }
}

function logErrorsWithAppLinks (accumulatedErrorStr, err, idx, loggedErrors, sourceSpace, destinationSpace) {
  const requestUri = get(err, 'request.uri')
  if (requestUri) {
    return accumulatedErrorStr +
      `Error ${idx + 1}:\n` +
      'in ' + parseEntityUrl(sourceSpace, destinationSpace, requestUri) +
      '\n' +
      JSON.stringify(err, null, '  ') +
      '\n\n'
  } else {
    return accumulatedErrorStr +
      `Error ${idx + 1}:\n` +
      JSON.stringify(err, null, '  ') +
      '\n\n'
  }
}

function parseEntityUrl (sourceSpace, destinationSpace, url) {
  return url.replace(/api.contentful/, 'app.contentful')
            .replace(/:443/, '')
            .replace(destinationSpace, sourceSpace)
            .split('/').splice(0, 7).join('/')
}
<|endoftext|>


<commit_before>'use strict';
const symbol = require('../utilities/symbol');
const getCallerFile = require('get-caller-file');
const deprecate = require('../utilities/deprecate');

let experiments = {
  MODULE_UNIFICATION: symbol('module-unification'),
  get INSTRUMENTATION() {
    deprecate(`\`experiments.INSTRUMENTATION\` is deprecated now that the feature has landed, use \`instrumentation\` method on your addon instead. Required from: \n${getCallerFile()}`, true);

    return 'instrumentation';
  },
};

Object.freeze(experiments);

module.exports = experiments;
<commit_msg>Disable experiments<commit_after>'use strict';

let experiments = {
};

Object.freeze(experiments);

module.exports = experiments;
<|endoftext|>